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

[Xen-changelog] [xen-unstable] Transitive grant support.



# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1254898041 -3600
# Node ID 47cf6033d2cc08198c267ed9d17bdafb1fe0c490
# Parent  ef9f401b32ca43e1fc066f260a999dcbf0992010
Transitive grant support.

Signed-off-by: Steven Smith <steven.smith@xxxxxxxxxx>
---
 xen/common/grant_table.c         |  238 ++++++++++++++++++++++++++++++++-------
 xen/include/public/grant_table.h |   29 +++-
 xen/include/xen/grant_table.h    |    2 
 3 files changed, 224 insertions(+), 45 deletions(-)

diff -r ef9f401b32ca -r 47cf6033d2cc xen/common/grant_table.c
--- a/xen/common/grant_table.c  Wed Oct 07 07:46:59 2009 +0100
+++ b/xen/common/grant_table.c  Wed Oct 07 07:47:21 2009 +0100
@@ -309,11 +309,12 @@ static int _set_status_v2(domid_t  domid
 
     /* If not already pinned, check the grant domid and type. */
     if ( !act->pin &&
-         (((flags & mask) != GTF_permit_access) ||
+         ( (((flags & mask) != GTF_permit_access) &&
+            ((flags & mask) != GTF_transitive)) ||
           (id != domid)) )
         PIN_FAIL(done, GNTST_general_error,
-                 "Bad flags (%x) or dom (%d). (expected dom %d)\n",
-                 flags, id, domid);
+                 "Bad flags (%x) or dom (%d). (expected dom %d, flags %x)\n",
+                 flags, id, domid, mask);
 
     if ( readonly )
     {
@@ -338,7 +339,8 @@ static int _set_status_v2(domid_t  domid
 
     if ( !act->pin )
     {
-        if ( ((flags & mask) != GTF_permit_access) ||
+        if ( (((flags & mask) != GTF_permit_access) &&
+              ((flags & mask) != GTF_transitive)) ||
              (id != domid) ||
              (!readonly && (flags & GTF_readonly)) )
         {
@@ -1533,6 +1535,14 @@ __release_grant_for_copy(
     struct active_grant_entry *act;
     unsigned long r_frame;
     uint16_t *status;
+    domid_t trans_domid;
+    grant_ref_t trans_gref;
+    int released_read;
+    int released_write;
+    struct domain *trans_dom;
+
+    released_read = 0;
+    released_write = 0;
 
     spin_lock(&rd->grant_table->lock);
 
@@ -1541,9 +1551,19 @@ __release_grant_for_copy(
     r_frame = act->frame;
 
     if (rd->grant_table->gt_version == 1)
+    {
         status = &sha->flags;
+        trans_domid = rd->domain_id;
+        /* Shut the compiler up.  This'll never be used, because
+           trans_domid == rd->domain_id, but gcc doesn't know that. */
+        trans_gref = 0x1234567;
+    }
     else
+    {
         status = &status_entry(rd->grant_table, gref);
+        trans_domid = act->trans_dom;
+        trans_gref = act->trans_gref;
+    }
 
     if ( readonly )
     {
@@ -1555,13 +1575,51 @@ __release_grant_for_copy(
 
         act->pin -= GNTPIN_hstw_inc;
         if ( !(act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) )
+        {
+            released_write = 1;
             gnttab_clear_flag(_GTF_writing, status);
+        }
     }
 
     if ( !act->pin )
+    {
         gnttab_clear_flag(_GTF_reading, status);
+        released_read = 1;
+    }
 
     spin_unlock(&rd->grant_table->lock);
+
+    if ( trans_domid != rd->domain_id )
+    {
+        if ( released_write || released_read )
+        {
+            trans_dom = rcu_lock_domain_by_id(trans_domid);
+            if ( trans_dom != NULL )
+            {
+                /* Recursive calls, but they're tail calls, so it's
+                   okay. */
+                if ( released_write )
+                    __release_grant_for_copy(trans_dom, trans_gref, 0);
+                else if ( released_read )
+                    __release_grant_for_copy(trans_dom, trans_gref, 1);
+            }
+        }
+    }
+}
+
+/* The status for a grant indicates that we're taking more access than
+   the pin requires.  Fix up the status to match the pin.  Called
+   under the domain's grant table lock. */
+/* Only safe on transitive grants.  Even then, note that we don't
+   attempt to drop any pin on the referent grant. */
+static void __fixup_status_for_pin(struct active_grant_entry *act,
+                                   uint16_t *status)
+{
+    if ( !(act->pin & GNTPIN_hstw_mask) )
+        *status &= ~_GTF_writing;
+
+    if ( !(act->pin & GNTPIN_hstr_mask) )
+        *status &= ~_GTF_reading;
 }
 
 /* Grab a frame number from a grant entry and update the flags and pin
@@ -1570,15 +1628,27 @@ __release_grant_for_copy(
    actually valid. */
 static int
 __acquire_grant_for_copy(
-    struct domain *rd, unsigned long gref, int readonly,
-    unsigned long *frame, unsigned *page_off, unsigned *length)
+    struct domain *rd, unsigned long gref, struct domain *ld, int readonly,
+    unsigned long *frame, unsigned *page_off, unsigned *length,
+    unsigned allow_transitive, struct domain **owning_domain)
 {
     grant_entry_v1_t *sha1;
     grant_entry_v2_t *sha2;
     grant_entry_header_t *shah;
     struct active_grant_entry *act;
     grant_status_t *status;
+    uint32_t old_pin;
+    domid_t trans_domid;
+    grant_ref_t trans_gref;
+    struct domain *rrd;
+    unsigned long grant_frame;
+    unsigned trans_page_off;
+    unsigned trans_length;
+    int is_sub_page;
+    struct domain *ignore;
     s16 rc = GNTST_okay;
+
+    *owning_domain = NULL;
 
     spin_lock(&rd->grant_table->lock);
 
@@ -1607,45 +1677,127 @@ __acquire_grant_for_copy(
 
     /* If already pinned, check the active domid and avoid refcnt overflow. */
     if ( act->pin &&
-         ((act->domid != current->domain->domain_id) ||
+         ((act->domid != ld->domain_id) ||
           (act->pin & 0x80808080U) != 0) )
         PIN_FAIL(unlock_out, GNTST_general_error,
                  "Bad domain (%d != %d), or risk of counter overflow %08x\n",
-                 act->domid, current->domain->domain_id, act->pin);
-
+                 act->domid, ld->domain_id, act->pin);
+
+    old_pin = act->pin;
     if ( !act->pin ||
          (!readonly && !(act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask))) )
     {
         if ( (rc = _set_status(rd->grant_table->gt_version,
-                               current->domain->domain_id, 
-                               readonly, 0, shah, act, status) ) != GNTST_okay 
)
+                               ld->domain_id,
+                               readonly, 0, shah, act,
+                               status) ) != GNTST_okay )
              goto unlock_out;
 
+        trans_domid = ld->domain_id;
+        trans_gref = 0;
+        if ( sha2 && (shah->flags & GTF_type_mask) == GTF_transitive )
+        {
+            if ( !allow_transitive )
+                PIN_FAIL(unlock_out, GNTST_general_error,
+                         "transitive grant when transitivity not allowed\n");
+
+            trans_domid = sha2->transitive.trans_domid;
+            trans_gref = sha2->transitive.gref;
+            barrier(); /* Stop the compiler from re-loading
+                          trans_domid from shared memory */
+            if ( trans_domid == rd->domain_id )
+                PIN_FAIL(unlock_out, GNTST_general_error,
+                         "transitive grants cannot be self-referential\n");
+
+            /* We allow the trans_domid == ld->domain_id case, which
+               corresponds to a grant being issued by one domain, sent
+               to another one, and then transitively granted back to
+               the original domain.  Allowing it is easy, and means
+               that you don't need to go out of your way to avoid it
+               in the guest. */
+
+            rrd = rcu_lock_domain_by_id(trans_domid);
+            if ( rrd == NULL )
+                PIN_FAIL(unlock_out, GNTST_general_error,
+                         "transitive grant referenced bad domain %d\n",
+                         trans_domid);
+            spin_unlock(&rd->grant_table->lock);
+
+            rc = __acquire_grant_for_copy(rrd, trans_gref, rd,
+                                          readonly, &grant_frame,
+                                          &trans_page_off, &trans_length,
+                                          0, &ignore);
+
+            spin_lock(&rd->grant_table->lock);
+            if ( rc != GNTST_okay ) {
+                __fixup_status_for_pin(act, status);
+                spin_unlock(&rd->grant_table->lock);
+                return rc;
+            }
+
+            /* We dropped the lock, so we have to check that nobody
+               else tried to pin (or, for that matter, unpin) the
+               reference in *this* domain.  If they did, just give up
+               and try again. */
+            if ( act->pin != old_pin )
+            {
+                __fixup_status_for_pin(act, status);
+                spin_unlock(&rd->grant_table->lock);
+                return __acquire_grant_for_copy(rd, gref, ld, readonly,
+                                                frame, page_off, length,
+                                                allow_transitive,
+                                                owning_domain);
+            }
+
+            /* The actual remote remote grant may or may not be a
+               sub-page, but we always treat it as one because that
+               blocks mappings of transitive grants. */
+            is_sub_page = 1;
+            *owning_domain = rrd;
+            act->gfn = INVALID_GFN;
+        }
+        else if ( sha1 )
+        {
+            act->gfn = sha1->frame;
+            grant_frame = gmfn_to_mfn(rd, act->gfn);
+            is_sub_page = 0;
+            trans_page_off = 0;
+            trans_length = PAGE_SIZE;
+            *owning_domain = rd;
+        }
+        else if ( !(sha2->hdr.flags & GTF_sub_page) )
+        {
+            act->gfn = sha2->full_page.frame;
+            grant_frame = gmfn_to_mfn(rd, act->gfn);
+            is_sub_page = 0;
+            trans_page_off = 0;
+            trans_length = PAGE_SIZE;
+            *owning_domain = rd;
+        }
+        else
+        {
+            act->gfn = sha2->sub_page.frame;
+            grant_frame = gmfn_to_mfn(rd, act->gfn);
+            is_sub_page = 1;
+            trans_page_off = sha2->sub_page.page_off;
+            trans_length = sha2->sub_page.length;
+            *owning_domain = rd;
+        }
+
         if ( !act->pin )
         {
-            act->domid = current->domain->domain_id;
-            act->is_sub_page = 0;
-            act->start = 0;
-            act->length = PAGE_SIZE;
-
-            if ( sha1 )
-            {
-                act->gfn = sha1->frame;
-            }
-            else if ( shah->flags & GTF_sub_page )
-            {
-                act->start = sha2->sub_page.page_off;
-                act->length = sha2->sub_page.length;
-                act->is_sub_page = 1;
-                act->gfn = sha2->sub_page.frame;
-            }
-            else
-            {
-                act->gfn = sha2->full_page.frame;
-            }
-
-            act->frame = gmfn_to_mfn(rd, act->gfn);
-        }
+            act->domid = ld->domain_id;
+            act->is_sub_page = is_sub_page;
+            act->start = trans_page_off;
+            act->length = trans_length;
+            act->trans_dom = trans_domid;
+            act->trans_gref = trans_gref;
+            act->frame = grant_frame;
+        }
+    }
+    else
+    {
+        *owning_domain = rd;
     }
 
     act->pin += readonly ? GNTPIN_hstr_inc : GNTPIN_hstw_inc;
@@ -1664,6 +1816,7 @@ __gnttab_copy(
     struct gnttab_copy *op)
 {
     struct domain *sd = NULL, *dd = NULL;
+    struct domain *source_domain = NULL, *dest_domain = NULL;
     unsigned long s_frame, d_frame;
     char *sp, *dp;
     s16 rc = GNTST_okay;
@@ -1704,8 +1857,9 @@ __gnttab_copy(
     if ( src_is_gref )
     {
         unsigned source_off, source_len;
-        rc = __acquire_grant_for_copy(sd, op->source.u.ref, 1, &s_frame,
-                                      &source_off, &source_len);
+        rc = __acquire_grant_for_copy(sd, op->source.u.ref, current->domain, 1,
+                                      &s_frame, &source_off, &source_len, 1,
+                                      &source_domain);
         if ( rc != GNTST_okay )
             goto error_out;
         have_s_grant = 1;
@@ -1719,11 +1873,12 @@ __gnttab_copy(
     else
     {
         s_frame = gmfn_to_mfn(sd, op->source.u.gmfn);
+        source_domain = sd;
     }
     if ( unlikely(!mfn_valid(s_frame)) )
         PIN_FAIL(error_out, GNTST_general_error,
                  "source frame %lx invalid.\n", s_frame);
-    if ( !get_page(mfn_to_page(s_frame), sd) )
+    if ( !get_page(mfn_to_page(s_frame), source_domain) )
     {
         if ( !sd->is_dying )
             gdprintk(XENLOG_WARNING, "Could not get src frame %lx\n", s_frame);
@@ -1735,8 +1890,9 @@ __gnttab_copy(
     if ( dest_is_gref )
     {
         unsigned dest_off, dest_len;
-        rc = __acquire_grant_for_copy(dd, op->dest.u.ref, 0, &d_frame,
-                                      &dest_off, &dest_len);
+        rc = __acquire_grant_for_copy(dd, op->dest.u.ref, current->domain, 0,
+                                      &d_frame, &dest_off, &dest_len, 1,
+                                      &dest_domain);
         if ( rc != GNTST_okay )
             goto error_out;
         have_d_grant = 1;
@@ -1750,11 +1906,13 @@ __gnttab_copy(
     else
     {
         d_frame = gmfn_to_mfn(dd, op->dest.u.gmfn);
+        dest_domain = dd;
     }
     if ( unlikely(!mfn_valid(d_frame)) )
         PIN_FAIL(error_out, GNTST_general_error,
                  "destination frame %lx invalid.\n", d_frame);
-    if ( !get_page_and_type(mfn_to_page(d_frame), dd, PGT_writable_page) )
+    if ( !get_page_and_type(mfn_to_page(d_frame), dest_domain,
+                            PGT_writable_page) )
     {
         if ( !dd->is_dying )
             gdprintk(XENLOG_WARNING, "Could not get dst frame %lx\n", d_frame);
diff -r ef9f401b32ca -r 47cf6033d2cc xen/include/public/grant_table.h
--- a/xen/include/public/grant_table.h  Wed Oct 07 07:46:59 2009 +0100
+++ b/xen/include/public/grant_table.h  Wed Oct 07 07:47:21 2009 +0100
@@ -83,6 +83,11 @@
  * Changing a GTF_permit_access from read-only to writable:
  *  Use SMP-safe bit-setting instruction.
  */
+
+/*
+ * Reference to a grant entry in a specified domain's grant table.
+ */
+typedef uint32_t grant_ref_t;
 
 /*
  * A grant table comprises a packed array of grant entries in one or more
@@ -118,10 +123,13 @@ typedef struct grant_entry_v1 grant_entr
  *  GTF_permit_access: Allow @domid to map/access @frame.
  *  GTF_accept_transfer: Allow @domid to transfer ownership of one page frame
  *                       to this guest. Xen writes the page number to @frame.
+ *  GTF_transitive: Allow @domid to transitively access a subrange of
+ *                  @trans_grant in @trans_domid.  No mappings are allowed.
  */
 #define GTF_invalid         (0U<<0)
 #define GTF_permit_access   (1U<<0)
 #define GTF_accept_transfer (2U<<0)
+#define GTF_transitive      (3U<<0)
 #define GTF_type_mask       (3U<<0)
 
 /*
@@ -218,6 +226,22 @@ union grant_entry_v2 {
         uint64_t frame;
     } sub_page;
 
+    /*
+     * If the grant is GTF_transitive, @domid is allowed to use the
+     * grant @gref in domain @trans_domid, as if it was the local
+     * domain.  Obviously, the transitive access must be compatible
+     * with the original grant.
+     *
+     * The current version of Xen does not allow transitive grants
+     * to be mapped.
+     */
+    struct {
+        grant_entry_header_t hdr;
+        domid_t trans_domid;
+        uint16_t pad0;
+        grant_ref_t gref;
+    } transitive;
+
     uint32_t __spacer[4]; /* Pad to a power of two */
 };
 typedef union grant_entry_v2 grant_entry_v2_t;
@@ -229,11 +253,6 @@ typedef uint16_t grant_status_t;
 /***********************************
  * GRANT TABLE QUERIES AND USES
  */
-
-/*
- * Reference to a grant entry in a specified domain's grant table.
- */
-typedef uint32_t grant_ref_t;
 
 /*
  * Handle to track a mapping created via a grant reference.
diff -r ef9f401b32ca -r 47cf6033d2cc xen/include/xen/grant_table.h
--- a/xen/include/xen/grant_table.h     Wed Oct 07 07:46:59 2009 +0100
+++ b/xen/include/xen/grant_table.h     Wed Oct 07 07:47:21 2009 +0100
@@ -32,6 +32,8 @@ struct active_grant_entry {
 struct active_grant_entry {
     u32           pin;    /* Reference count information.             */
     domid_t       domid;  /* Domain being granted access.             */
+    domid_t       trans_dom;
+    uint32_t      trans_gref;
     unsigned long frame;  /* Frame being granted.                     */
     unsigned long gfn;    /* Guest's idea of the frame being granted. */
     unsigned      is_sub_page:1; /* True if this is a sub-page grant. */

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

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