From: "Teddy Astie" <teddy.astie@vates.tech>
To: "Julian Vetter" <julian.vetter@vates.tech>,
xen-devel@lists.xenproject.org
Cc: "Jan Beulich" <jbeulich@suse.com>,
"Andrew Cooper" <andrew.cooper3@citrix.com>,
"Roger Pau Monné" <roger.pau@citrix.com>,
"Anthony PERARD" <anthony.perard@vates.tech>,
"Michal Orzel" <michal.orzel@amd.com>,
"Julien Grall" <julien@xen.org>,
"Stefano Stabellini" <sstabellini@kernel.org>
Subject: Re: [PATCH v6 3/3] x86/ioreq: Extend ioreq server to support multiple ioreq pages
Date: Mon, 20 Apr 2026 12:49:53 +0000 [thread overview]
Message-ID: <cd9b42bf-5399-471e-9b86-dcf13eaad9e6@vates.tech> (raw)
In-Reply-To: <20260420093820.825969-4-julian.vetter@vates.tech>
Le 20/04/2026 à 11:41, Julian Vetter a écrit :
> As the number of vCPUs grows, a single ioreq page of 128 slots may not
> be sufficient. Add support for allocating and mapping multiple ioreq
> pages so that the ioreq region can scale with d->max_vcpus.
>
> Introduce nr_ioreq_pages() to compute the number of pages required for
> a given domain, and IOREQ_NR_PAGES_MAX as a compile-time upper bound
> (based on HVM_MAX_VCPUS).
>
> ioreq_server_alloc_mfn() is updated to allocate nr_ioreq_pages() pages
> and map them contiguously via vmap().
>
> is_ioreq_server_page() iterates over all ioreq pages when checking
> page ownership. ioreq_server_get_frame() allows callers to retrieve any
> ioreq page by index via the XENMEM_acquire_resource interface.
>
> On x86, the legacy GFN mapping path (hvm_map_ioreq_gfn) is limited to
> a single ioreq page; device models requiring more ioreq slots must use
> the resource mapping interface (XENMEM_acquire_resource).
>
> Signed-off-by: Julian Vetter <julian.vetter@vates.tech>
> ---
> Changes in v6:
> - Adapted the comment to not mention the guest, but the device model
> - Replaced the dynamic allocation for the mfns array by a static array
> - Fixed error handling in ioreq_server_alloc_mfn, using an extra
> nr_alloc variable to track the already allocated pages
> - Dropped unnecessary void casts
> ---
> xen/arch/x86/hvm/ioreq.c | 8 ++++
> xen/common/ioreq.c | 93 ++++++++++++++++++++++++++++------------
> xen/include/xen/ioreq.h | 12 ++++++
> 3 files changed, 86 insertions(+), 27 deletions(-)
>
> diff --git a/xen/arch/x86/hvm/ioreq.c b/xen/arch/x86/hvm/ioreq.c
> index 3cabec141c..ee679bdf5a 100644
> --- a/xen/arch/x86/hvm/ioreq.c
> +++ b/xen/arch/x86/hvm/ioreq.c
> @@ -166,6 +166,14 @@ static int hvm_map_ioreq_gfn(struct ioreq_server *s, bool buf)
> if ( d->is_dying )
> return -EINVAL;
>
> + /*
> + * The legacy GFN path supports only a single ioreq page. Device models
> + * requiring more ioreq slots must use the resource mapping interface
> + * (XENMEM_acquire_resource).
> + */
> + if ( !buf && nr_ioreq_pages(d) > 1 )
> + return -EOPNOTSUPP;
> +
> iorp->gfn = hvm_alloc_ioreq_gfn(s);
>
> if ( gfn_eq(iorp->gfn, INVALID_GFN) )
> diff --git a/xen/common/ioreq.c b/xen/common/ioreq.c
> index bae9b99c99..3a08e77597 100644
> --- a/xen/common/ioreq.c
> +++ b/xen/common/ioreq.c
> @@ -261,8 +261,11 @@ bool vcpu_ioreq_handle_completion(struct vcpu *v)
> static int ioreq_server_alloc_mfn(struct ioreq_server *s, bool buf)
> {
> struct ioreq_page *iorp = buf ? &s->bufioreq : &s->ioreq;
> - struct page_info *page;
> - mfn_t mfn;
> + unsigned int i, nr_alloc = 0, nr_pages = buf ? 1 : nr_ioreq_pages(s->target);
> + mfn_t mfns[IOREQ_NR_PAGES_MAX] = {};
> + int rc;
> +
> + ASSERT(nr_pages <= IOREQ_NR_PAGES_MAX);
>
> if ( iorp->va )
> {
> @@ -277,11 +280,16 @@ static int ioreq_server_alloc_mfn(struct ioreq_server *s, bool buf)
> return 0;
> }
>
> + for ( i = 0; i < nr_pages; i++ )
> {
> - page = alloc_domheap_page(s->target, MEMF_no_refcount);
> + struct page_info *page = alloc_domheap_page(s->target,
> + MEMF_no_refcount);
>
> if ( !page )
> - return -ENOMEM;
> + {
> + rc = -ENOMEM;
> + goto fail;
> + }
>
> if ( !get_page_and_type(page, s->target, PGT_writable_page) )
> {
> @@ -290,41 +298,59 @@ static int ioreq_server_alloc_mfn(struct ioreq_server *s, bool buf)
> * here is a clear indication of something fishy going on.
> */
> domain_crash(s->emulator);
> - return -ENODATA;
> + rc = -ENODATA;
> + goto fail;
> }
>
> - mfn = page_to_mfn(page);
> + mfns[nr_alloc++] = page_to_mfn(page);
> }
> - iorp->va = vmap(&mfn, 1);
> +
> + iorp->va = vmap(mfns, nr_pages);
> if ( !iorp->va )
> + {
> + rc = -ENOMEM;
> goto fail;
> + }
>
> - clear_page(iorp->va);
> + memset(iorp->va, 0, nr_pages * PAGE_SIZE);
> return 0;
>
> fail:
> - put_page_alloc_ref(page);
> - put_page_and_type(page);
> + while ( nr_alloc-- )
> + {
> + struct page_info *page = mfn_to_page(mfns[nr_alloc]);
> +
> + put_page_alloc_ref(page);
> + put_page_and_type(page);
> + }
>
> - return -ENOMEM;
> + return rc;
> }
>
> static void ioreq_server_free_mfn(struct ioreq_server *s, bool buf)
> {
> struct ioreq_page *iorp = buf ? &s->bufioreq : &s->ioreq;
> - struct page_info *page;
> + unsigned int i, nr_pages = buf ? 1 : nr_ioreq_pages(s->target);
> + struct page_info *pages[IOREQ_NR_PAGES_MAX];
> void *va;
>
> if ( !iorp->va )
> return;
>
> + ASSERT(nr_pages <= IOREQ_NR_PAGES_MAX);
> +
> + for ( i = 0; i < nr_pages; i++ )
> + pages[i] = vmap_to_page(iorp->va + i * PAGE_SIZE);
> +
> va = iorp->va;
> - page = vmap_to_page(va);
> iorp->va = NULL;
> vunmap(va);
>
> - put_page_alloc_ref(page);
> - put_page_and_type(page);
> + for ( i = 0; i < nr_pages; i++ )
> + {
> + put_page_alloc_ref(pages[i]);
> + put_page_and_type(pages[i]);
> + }
> }
>
> bool is_ioreq_server_page(struct domain *d, const struct page_info *page)
> @@ -337,12 +363,25 @@ bool is_ioreq_server_page(struct domain *d, const struct page_info *page)
>
> FOR_EACH_IOREQ_SERVER(d, id, s)
> {
> - if ( (s->ioreq.va && vmap_to_page(s->ioreq.va) == page) ||
> - (s->bufioreq.va && vmap_to_page(s->bufioreq.va) == page) )
> + unsigned int i;
> +
> + if ( s->bufioreq.va && vmap_to_page(s->bufioreq.va) == page )
> {
> found = true;
> break;
> }
> +
> + for ( i = 0; i < nr_ioreq_pages(d) && s->ioreq.va; i++ )
> + {
> + if ( vmap_to_page(s->ioreq.va + i * PAGE_SIZE) == page )
> + {
> + found = true;
> + break;
> + }
> + }
> +
> + if ( found )
> + break;
> }
>
> rspin_unlock(&d->ioreq_server.lock);
> @@ -816,26 +855,26 @@ int ioreq_server_get_frame(struct domain *d, ioservid_t id,
> if ( rc )
> goto out;
>
> - switch ( idx )
> + if ( idx == XENMEM_resource_ioreq_server_frame_bufioreq )
> {
> - case XENMEM_resource_ioreq_server_frame_bufioreq:
> rc = -ENOENT;
> if ( !HANDLE_BUFIOREQ(s) )
> goto out;
>
> *mfn = vmap_to_mfn(s->bufioreq.va);
> rc = 0;
> - break;
> + }
> + else if ( idx >= XENMEM_resource_ioreq_server_frame_ioreq(0) &&
> + idx < XENMEM_resource_ioreq_server_frame_ioreq(nr_ioreq_pages(d)) )
> + {
> + unsigned int page_idx = idx - XENMEM_resource_ioreq_server_frame_ioreq(0);
>
> - case XENMEM_resource_ioreq_server_frame_ioreq(0):
> - *mfn = vmap_to_mfn(s->ioreq.va);
> + ASSERT(page_idx < nr_ioreq_pages(d));
> + *mfn = vmap_to_mfn(s->ioreq.va + page_idx * PAGE_SIZE);
> rc = 0;
> - break;
> -
> - default:
> - rc = -EINVAL;
> - break;
> }
> + else
> + rc = -EINVAL;
>
> out:
> rspin_unlock(&d->ioreq_server.lock);
> diff --git a/xen/include/xen/ioreq.h b/xen/include/xen/ioreq.h
> index d63fa4729e..d2a08c2371 100644
> --- a/xen/include/xen/ioreq.h
> +++ b/xen/include/xen/ioreq.h
> @@ -35,6 +35,18 @@ struct ioreq_vcpu {
> bool pending;
> };
>
> +/*
> + * Maximum number of ioreq pages, based on the maximum number
> + * of vCPUs and the number of ioreq slots per page.
> + */
> +#define IOREQ_NR_PAGES_MAX \
> + DIV_ROUND_UP(HVM_MAX_VCPUS, PAGE_SIZE / sizeof(ioreq_t))
> +
> +static inline unsigned int nr_ioreq_pages(const struct domain *d)
> +{
> + return DIV_ROUND_UP(d->max_vcpus, PAGE_SIZE / sizeof(ioreq_t));
> +}
> +
> #define NR_IO_RANGE_TYPES (XEN_DMOP_IO_RANGE_PCI + 1)
> #define MAX_NR_IO_RANGES 256
>
Is there anything that would prevent the use of alloc_domheap_pages() to
allocate a configuous set of pages at once; and vmap_contig() to map it
in one go.
That also prevents the ioreq pages from being scattered around.
We would lose a few pages by aligning the size into a order, but that
probably better than the alternatives.
That way, we would only have to keep the base gfn (or first page_info)
and size of the allocation, and don't have to use a array of mfn nor
have to reverse the vmap to track it.
Teddy
--
Teddy Astie | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
next prev parent reply other threads:[~2026-04-20 12:50 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-20 9:38 [PATCH v6 0/3] Support multiple ioreq pages Julian Vetter
2026-04-20 9:38 ` [PATCH v6 1/3] ioreq: switch ioreq page allocation to vmap Julian Vetter
2026-04-20 9:38 ` [PATCH v6 2/3] ioreq: Indent ioreq_server_alloc_mfn() body one level deeper Julian Vetter
2026-04-20 9:38 ` [PATCH v6 3/3] x86/ioreq: Extend ioreq server to support multiple ioreq pages Julian Vetter
2026-04-20 12:49 ` Teddy Astie [this message]
2026-04-20 13:38 ` Jan Beulich
2026-04-20 10:05 ` [PATCH v6 0/3] Support " Jan Beulich
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=cd9b42bf-5399-471e-9b86-dcf13eaad9e6@vates.tech \
--to=teddy.astie@vates.tech \
--cc=andrew.cooper3@citrix.com \
--cc=anthony.perard@vates.tech \
--cc=jbeulich@suse.com \
--cc=julian.vetter@vates.tech \
--cc=julien@xen.org \
--cc=michal.orzel@amd.com \
--cc=roger.pau@citrix.com \
--cc=sstabellini@kernel.org \
--cc=xen-devel@lists.xenproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.