xen-devel.lists.xenproject.org archive mirror
 help / color / mirror / Atom feed
From: Andres Lagar-Cavilla <andres@lagarcavilla.org>
To: xen-devel@lists.xensource.com
Cc: andres@gridcentric.ca, tim@xen.org, olaf@aepfle.de, adin@gridcentric.ca
Subject: [PATCH 3 of 9] x86/mm: Refactor possibly deadlocking get_gfn calls
Date: Wed, 01 Feb 2012 14:51:55 -0500	[thread overview]
Message-ID: <3de7e43b130a87d428e0.1328125915@xdev.gridcentric.ca> (raw)
In-Reply-To: <patchbomb.1328125912@xdev.gridcentric.ca>

 xen/arch/x86/hvm/emulate.c    |  35 +++++++--------
 xen/arch/x86/mm/mem_sharing.c |  28 +++++++------
 xen/include/asm-x86/p2m.h     |  91 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 122 insertions(+), 32 deletions(-)


When calling get_gfn multiple times on different gfn's in the same function, we
can easily deadlock if p2m lookups are locked. Thus, refactor these calls to
enforce simple deadlock-avoidance rules:
 - Lowest-numbered domain first
 - Lowest-numbered gfn first

Signed-off-by: Andres Lagar-Cavilla <andres@lagarcavila.org>

diff -r 27031a8a4eff -r 3de7e43b130a xen/arch/x86/hvm/emulate.c
--- a/xen/arch/x86/hvm/emulate.c
+++ b/xen/arch/x86/hvm/emulate.c
@@ -660,12 +660,13 @@ static int hvmemul_rep_movs(
 {
     struct hvm_emulate_ctxt *hvmemul_ctxt =
         container_of(ctxt, struct hvm_emulate_ctxt, ctxt);
-    unsigned long saddr, daddr, bytes, sgfn, dgfn;
+    unsigned long saddr, daddr, bytes;
     paddr_t sgpa, dgpa;
     uint32_t pfec = PFEC_page_present;
-    p2m_type_t p2mt;
+    p2m_type_t sp2mt, dp2mt;
     int rc, df = !!(ctxt->regs->eflags & X86_EFLAGS_DF);
     char *buf;
+    struct two_gfns *tg;
 
     rc = hvmemul_virtual_to_linear(
         src_seg, src_offset, bytes_per_rep, reps, hvm_access_read,
@@ -693,26 +694,25 @@ static int hvmemul_rep_movs(
     if ( rc != X86EMUL_OKAY )
         return rc;
 
-    /* XXX In a fine-grained p2m locking scenario, we need to sort this
-     * get_gfn's, or else we might deadlock */
-    sgfn = sgpa >> PAGE_SHIFT;
-    (void)get_gfn(current->domain, sgfn, &p2mt);
-    if ( !p2m_is_ram(p2mt) && !p2m_is_grant(p2mt) )
+    tg = get_two_gfns(current->domain, sgpa >> PAGE_SHIFT, &sp2mt, NULL, NULL,
+                      current->domain, dgpa >> PAGE_SHIFT, &dp2mt, NULL, NULL,
+                      p2m_guest);
+    if ( !tg )
+        return X86EMUL_UNHANDLEABLE;
+
+    if ( !p2m_is_ram(sp2mt) && !p2m_is_grant(sp2mt) )
     {
         rc = hvmemul_do_mmio(
             sgpa, reps, bytes_per_rep, dgpa, IOREQ_READ, df, NULL);
-        put_gfn(current->domain, sgfn);
+        put_two_gfns(&tg);
         return rc;
     }
 
-    dgfn = dgpa >> PAGE_SHIFT;
-    (void)get_gfn(current->domain, dgfn, &p2mt);
-    if ( !p2m_is_ram(p2mt) && !p2m_is_grant(p2mt) )
+    if ( !p2m_is_ram(dp2mt) && !p2m_is_grant(dp2mt) )
     {
         rc = hvmemul_do_mmio(
             dgpa, reps, bytes_per_rep, sgpa, IOREQ_WRITE, df, NULL);
-        put_gfn(current->domain, sgfn);
-        put_gfn(current->domain, dgfn);
+        put_two_gfns(&tg);
         return rc;
     }
 
@@ -730,8 +730,7 @@ static int hvmemul_rep_movs(
      */
     if ( ((dgpa + bytes_per_rep) > sgpa) && (dgpa < (sgpa + bytes)) )
     {
-        put_gfn(current->domain, sgfn);
-        put_gfn(current->domain, dgfn);
+        put_two_gfns(&tg);
         return X86EMUL_UNHANDLEABLE;
     }
 
@@ -743,8 +742,7 @@ static int hvmemul_rep_movs(
     buf = xmalloc_bytes(bytes);
     if ( buf == NULL )
     {
-        put_gfn(current->domain, sgfn);
-        put_gfn(current->domain, dgfn);
+        put_two_gfns(&tg);
         return X86EMUL_UNHANDLEABLE;
     }
 
@@ -757,8 +755,7 @@ static int hvmemul_rep_movs(
         rc = hvm_copy_to_guest_phys(dgpa, buf, bytes);
 
     xfree(buf);
-    put_gfn(current->domain, sgfn);
-    put_gfn(current->domain, dgfn);
+    put_two_gfns(&tg);
 
     if ( rc == HVMCOPY_gfn_paged_out )
         return X86EMUL_RETRY;
diff -r 27031a8a4eff -r 3de7e43b130a xen/arch/x86/mm/mem_sharing.c
--- a/xen/arch/x86/mm/mem_sharing.c
+++ b/xen/arch/x86/mm/mem_sharing.c
@@ -718,11 +718,13 @@ int mem_sharing_share_pages(struct domai
     int ret = -EINVAL;
     mfn_t smfn, cmfn;
     p2m_type_t smfn_type, cmfn_type;
+    struct two_gfns *tg;
 
-    /* XXX if sd == cd handle potential deadlock by ordering
-     * the get_ and put_gfn's */
-    smfn = get_gfn(sd, sgfn, &smfn_type);
-    cmfn = get_gfn(cd, cgfn, &cmfn_type);
+    tg = get_two_gfns(sd, sgfn, &smfn_type, NULL, &smfn,
+                      cd, cgfn, &cmfn_type, NULL, &cmfn,
+                      p2m_query);
+    if ( !tg )
+        return -ENOMEM;
 
     /* This tricky business is to avoid two callers deadlocking if 
      * grabbing pages in opposite client/source order */
@@ -819,8 +821,7 @@ int mem_sharing_share_pages(struct domai
     ret = 0;
     
 err_out:
-    put_gfn(cd, cgfn);
-    put_gfn(sd, sgfn);
+    put_two_gfns(&tg);
     return ret;
 }
 
@@ -834,11 +835,13 @@ int mem_sharing_add_to_physmap(struct do
     struct gfn_info *gfn_info;
     struct p2m_domain *p2m = p2m_get_hostp2m(cd);
     p2m_access_t a;
-    
-    /* XXX if sd == cd handle potential deadlock by ordering
-     * the get_ and put_gfn's */
-    smfn = get_gfn_query(sd, sgfn, &smfn_type);
-    cmfn = get_gfn_type_access(p2m, cgfn, &cmfn_type, &a, p2m_query, NULL);
+    struct two_gfns *tg;
+
+    tg = get_two_gfns(sd, sgfn, &smfn_type, NULL, &smfn,
+                      cd, cgfn, &cmfn_type, &a, &cmfn,
+                      p2m_query);
+    if ( !tg )
+        return -ENOMEM;
 
     /* Get the source shared page, check and lock */
     ret = XEN_DOMCTL_MEM_SHARING_S_HANDLE_INVALID;
@@ -886,8 +889,7 @@ int mem_sharing_add_to_physmap(struct do
 err_unlock:
     mem_sharing_page_unlock(spage);
 err_out:
-    put_gfn(cd, cgfn);
-    put_gfn(sd, sgfn);
+    put_two_gfns(&tg);
     return ret;
 }
 
diff -r 27031a8a4eff -r 3de7e43b130a xen/include/asm-x86/p2m.h
--- a/xen/include/asm-x86/p2m.h
+++ b/xen/include/asm-x86/p2m.h
@@ -378,6 +378,97 @@ static inline unsigned long mfn_to_gfn(s
         return mfn_x(mfn);
 }
 
+/* Deadlock-avoidance scheme when calling get_gfn on different gfn's */
+struct two_gfns {
+    struct domain  *first_domain;
+    unsigned long   first_gfn;
+    struct domain  *second_domain;
+    unsigned long   second_gfn;
+};
+
+#define assign_pointers(dest, source)                                       \
+do {                                                                        \
+    dest ## _mfn = (source ## mfn) ? (source ## mfn) : &__ ## dest ## _mfn;  \
+    dest ## _a   = (source ## a)   ? (source ## a)   : &__ ## dest ## _a;    \
+    dest ## _t   = (source ## t)   ? (source ## t)   : &__ ## dest ## _t;    \
+} while(0)
+
+/* Returns mfn, type and access for potential caller consumption, but any
+ * of those can be NULL */
+static inline struct two_gfns *get_two_gfns(struct domain *rd, unsigned long rgfn,
+        p2m_type_t *rt, p2m_access_t *ra, mfn_t *rmfn, struct domain *ld, 
+        unsigned long lgfn, p2m_type_t *lt, p2m_access_t *la, mfn_t *lmfn,
+        p2m_query_t q)
+{
+    mfn_t           *first_mfn, *second_mfn, __first_mfn, __second_mfn;
+    p2m_access_t    *first_a, *second_a, __first_a, __second_a;
+    p2m_type_t      *first_t, *second_t, __first_t, __second_t;
+    
+    struct two_gfns *rval = xmalloc(struct two_gfns);
+    if ( !rval )
+        return NULL;
+
+    if ( rd == ld )
+    {
+        rval->first_domain = rval->second_domain = rd;
+
+        /* Sort by gfn */
+        if (rgfn <= lgfn)
+        {
+            rval->first_gfn     = rgfn;
+            rval->second_gfn    = lgfn;
+            assign_pointers(first, r);
+            assign_pointers(second, l);
+        } else {
+            rval->first_gfn     = lgfn;
+            rval->second_gfn    = rgfn;
+            assign_pointers(first, l);
+            assign_pointers(second, r);
+        }        
+    } else {
+        /* Sort by domain */
+        if ( rd->domain_id <= ld->domain_id )
+        {
+            rval->first_domain  = rd;
+            rval->first_gfn     = rgfn;
+            rval->second_domain = ld;
+            rval->second_gfn    = lgfn;
+            assign_pointers(first, r);
+            assign_pointers(second, l);
+        } else {
+            rval->first_domain  = ld;
+            rval->first_gfn     = lgfn;
+            rval->second_domain = rd;
+            rval->second_gfn    = rgfn;
+            assign_pointers(first, l);
+            assign_pointers(second, r);
+        }
+    }
+
+    /* Now do the gets */
+    *first_mfn  = get_gfn_type_access(p2m_get_hostp2m(rval->first_domain), 
+                                      rval->first_gfn, first_t, first_a, q, NULL);
+    *second_mfn = get_gfn_type_access(p2m_get_hostp2m(rval->second_domain), 
+                                      rval->second_gfn, second_t, second_a, q, NULL);
+
+    return rval;
+}
+
+static inline void put_two_gfns(struct two_gfns **arg_ptr)
+{
+    struct two_gfns *arg;
+
+    if ( !arg_ptr || !(*arg_ptr) )
+        return;
+
+    arg = *arg_ptr;
+    put_gfn(arg->second_domain, arg->second_gfn);
+    put_gfn(arg->first_domain, arg->first_gfn);
+
+    xfree(arg);
+    *arg_ptr = NULL;
+}
+
 /* Init the datastructures for later use by the p2m code */
 int p2m_init(struct domain *d);

  parent reply	other threads:[~2012-02-01 19:51 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-02-01 19:51 [PATCH 0 of 9] x86/mm: Fixes to sharing, paging and p2m Andres Lagar-Cavilla
2012-02-01 19:51 ` [PATCH 1 of 9] x86/mm: Remove p2m_ram_paging_in Andres Lagar-Cavilla
2012-02-01 19:51 ` [PATCH 2 of 9] x86/mm: Don't fail to nominate for paging on type flag, rather look at type count Andres Lagar-Cavilla
2012-02-01 19:51 ` Andres Lagar-Cavilla [this message]
2012-02-02 12:39   ` [PATCH 3 of 9] x86/mm: Refactor possibly deadlocking get_gfn calls Tim Deegan
2012-02-02 13:44     ` Andres Lagar-Cavilla
2012-02-01 19:51 ` [PATCH 4 of 9] Reorder locks used by shadow code in anticipation of synchronized p2m lookups Andres Lagar-Cavilla
2012-02-01 19:51 ` [PATCH 5 of 9] x86/mm: When removing/adding a page from/to the physmap, keep in mind it could be shared Andres Lagar-Cavilla
2012-02-02 12:41   ` Tim Deegan
2012-02-02 13:46     ` Andres Lagar-Cavilla
2012-02-01 19:51 ` [PATCH 6 of 9] x86/mm: Fix balooning+sharing Andres Lagar-Cavilla
2012-02-01 19:51 ` [PATCH 7 of 9] x86/mm: Fix paging stats Andres Lagar-Cavilla
2012-02-01 19:52 ` [PATCH 8 of 9] x86/mm: Make sharing ASSERT check more accurate Andres Lagar-Cavilla
2012-02-01 19:52 ` [PATCH 9 of 9] x86/mm: Make debug_{gfn, mfn, gref} calls to sharing more useful and correct Andres Lagar-Cavilla
2012-02-02 12:32 ` [PATCH 0 of 9] x86/mm: Fixes to sharing, paging and p2m Tim Deegan

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=3de7e43b130a87d428e0.1328125915@xdev.gridcentric.ca \
    --to=andres@lagarcavilla.org \
    --cc=adin@gridcentric.ca \
    --cc=andres@gridcentric.ca \
    --cc=olaf@aepfle.de \
    --cc=tim@xen.org \
    --cc=xen-devel@lists.xensource.com \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).