[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v5 3/3] x86/ioreq: Extend ioreq server to support multiple ioreq pages



A domain with more than (PAGE_SIZE / sizeof(ioreq_t)) vCPUs needs more
than one ioreq page to hold all per-vCPU ioreq slots. In order to
support this a number of changes have been made:

1. Add nr_ioreq_pages() to compute the required number of pages, defined
   as DIV_ROUND_UP(d->max_vcpus, PAGE_SIZE / sizeof(ioreq_t))
2. ioreq_server_alloc_mfn() now allocates nr_ioreq_pages() pages for the
   non-buf case, builds an mfn_t array, and calls vmap() to map them
   contiguously. The buf path remains single-page.
3. ioreq_server_free_mfn() uses vmap_size() to determine how many pages
   to release.
4. is_ioreq_server_page() loops over all mapped ioreq pages using
   vmap_size() and vmap_to_page() with per-page offsets
5. ioreq_server_get_frame() now handles idx values in the range
   [XENMEM_resource_ioreq_server_frame_ioreq(0),
   XENMEM_resource_ioreq_server_frame_ioreq(nr_pages - 1)], returning
   the MFN via vmap_to_mfn() with the appropriate page offset.

The legacy GFN path (hvm_map_ioreq_gfn) is restricted to single-page.
Domains with more vCPUs must use XENMEM_acquire_resource!

Signed-off-by: Julian Vetter <julian.vetter@xxxxxxxxxx>
---
Changes in v5:
- Reduced complexity a lot because there is no distinction between buf
  and !buf case
- Directly use va and gfn from struct ioreq_page, dropped additional
  members in struct ioreq_server
---
 xen/arch/x86/hvm/ioreq.c |   8 +++
 xen/common/ioreq.c       | 103 +++++++++++++++++++++++++++++----------
 xen/include/xen/ioreq.h  |   6 +++
 3 files changed, 90 insertions(+), 27 deletions(-)

diff --git a/xen/arch/x86/hvm/ioreq.c b/xen/arch/x86/hvm/ioreq.c
index 145dcba5c1..872247e300 100644
--- a/xen/arch/x86/hvm/ioreq.c
+++ b/xen/arch/x86/hvm/ioreq.c
@@ -163,6 +163,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. Guests requiring
+     * more ioreq slots must use the resource mapping interface
+     * (XENMEM_acquire_resource).
+     */
+    if ( !buf && nr_ioreq_pages(d) > 1 )
+        return -EOPNOTSUPP;
+
     base_gfn = hvm_alloc_ioreq_gfn(s);
 
     if ( gfn_eq(base_gfn, INVALID_GFN) )
diff --git a/xen/common/ioreq.c b/xen/common/ioreq.c
index b22f656701..71fac2bc7b 100644
--- a/xen/common/ioreq.c
+++ b/xen/common/ioreq.c
@@ -261,8 +261,9 @@ 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_pages = buf ? 1 : nr_ioreq_pages(s->target);
+    mfn_t *mfns;
+    int rc;
 
     if ( iorp->va )
     {
@@ -277,11 +278,20 @@ static int ioreq_server_alloc_mfn(struct ioreq_server *s, 
bool buf)
         return 0;
     }
 
+    mfns = xmalloc_array(mfn_t, nr_pages);
+    if ( !mfns )
+        return -ENOMEM;
+
+    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 +300,60 @@ 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[i] = page_to_mfn(page);
     }
-    iorp->va = vmap(&mfn, 1);
+
+    iorp->va = vmap(mfns, nr_pages);
     if ( !iorp->va )
+    {
+        rc = -ENOMEM;
         goto fail;
+    }
+
+    xfree(mfns);
+
+    for ( i = 0; i < nr_pages; i++ )
+        clear_page((char *)iorp->va + i * PAGE_SIZE);
 
-    clear_page(iorp->va);
     return 0;
 
  fail:
-    put_page_alloc_ref(page);
-    put_page_and_type(page);
+    while ( i-- )
+    {
+        struct page_info *page = mfn_to_page(mfns[i]);
+
+        put_page_alloc_ref(page);
+        put_page_and_type(page);
+    }
+    xfree(mfns);
 
-    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;
 
     if ( !iorp->va )
         return;
 
+    nr_pages = vmap_size(iorp->va);
+
+    for ( i = 0; i < nr_pages; i++ )
     {
-        page = vmap_to_page(iorp->va);
-        vunmap(iorp->va);
-        iorp->va = NULL;
+        struct page_info *page = vmap_to_page(iorp->va + i * PAGE_SIZE);
 
         put_page_alloc_ref(page);
         put_page_and_type(page);
     }
+
+    vunmap(iorp->va);
+    iorp->va = NULL;
 }
 
 bool is_ioreq_server_page(struct domain *d, const struct page_info *page)
@@ -337,12 +366,28 @@ 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) )
+        if ( s->bufioreq.va && vmap_to_page(s->bufioreq.va) == page )
         {
             found = true;
             break;
         }
+
+        if ( s->ioreq.va )
+        {
+            unsigned int i;
+
+            for ( i = 0; i < vmap_size(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);
@@ -818,26 +863,30 @@ 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 = page_to_mfn(vmap_to_page(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);
+        if ( page_idx >= vmap_size(s->ioreq.va) )
+        {
+            rc = -EINVAL;
+            goto out;
+        }
 
-    case XENMEM_resource_ioreq_server_frame_ioreq(0):
-        *mfn = page_to_mfn(vmap_to_page(s->ioreq.va));
+        *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..c12480472d 100644
--- a/xen/include/xen/ioreq.h
+++ b/xen/include/xen/ioreq.h
@@ -19,6 +19,7 @@
 #ifndef __XEN_IOREQ_H__
 #define __XEN_IOREQ_H__
 
+#include <xen/macros.h>
 #include <xen/sched.h>
 
 #include <public/hvm/dm_op.h>
@@ -82,6 +83,11 @@ static inline bool ioreq_needs_completion(const ioreq_t 
*ioreq)
 #define HANDLE_BUFIOREQ(s) \
     ((s)->bufioreq_handling != HVM_IOREQSRV_BUFIOREQ_OFF)
 
+static inline unsigned int nr_ioreq_pages(const struct domain *d)
+{
+    return DIV_ROUND_UP(d->max_vcpus, PAGE_SIZE / sizeof(ioreq_t));
+}
+
 bool domain_has_ioreq_server(const struct domain *d);
 
 bool vcpu_ioreq_pending(struct vcpu *v);
-- 
2.51.0



--
Julian Vetter | Vates Hypervisor & Kernel Developer

XCP-ng & Xen Orchestra - Vates solutions

web: https://vates.tech




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.