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

[Xen-devel] [PATCH 5 of 6] [RFC] x86/mm: use wait queues for mem_paging



# HG changeset patch
# User Tim Deegan <tim@xxxxxxx>
# Date 1330013889 0
# Node ID 510d80343793227bd39b9a5d4d3b61b9a3f776e4
# Parent  4ed10bf6325a8b07a9fb490ed575965c698a0cdd
[RFC] x86/mm: use wait queues for mem_paging

Use a wait queue to put a guest vcpu to sleep while the requested gfn is
in paging state. This adds missing p2m_mem_paging_populate() calls to
some callers of the new get_gfn* variants, which would crash now
because they get an invalid mfn. It also fixes guest crashes due to
unexpected returns from do_memory_op because copy_to/from_guest ran into
a paged gfn. Now those places will always get a valid mfn.

This is based on an earlier RFC patch by Olaf Hering, but heavily
simplified (removing a per-gfn queue of waiting vcpus in favour of
a scan of all vcpus on page-in).

Signed-off-by: Olaf Hering <olaf@xxxxxxxxx>
Signed-off-by: Tim Deegan <tim@xxxxxxx>

diff -r 4ed10bf6325a -r 510d80343793 xen/arch/x86/mm/p2m.c
--- a/xen/arch/x86/mm/p2m.c     Thu Feb 23 16:15:29 2012 +0000
+++ b/xen/arch/x86/mm/p2m.c     Thu Feb 23 16:18:09 2012 +0000
@@ -160,13 +160,49 @@ mfn_t __get_gfn_type_access(struct p2m_d
     }
 
     /* For now only perform locking on hap domains */
-    if ( locked && (hap_enabled(p2m->domain)) )
+    locked = locked && hap_enabled(p2m->domain);
+
+#ifdef __x86_64__
+again:
+#endif
+    if ( locked )
         /* Grab the lock here, don't release until put_gfn */
         gfn_lock(p2m, gfn, 0);
 
     mfn = p2m->get_entry(p2m, gfn, t, a, q, page_order);
 
 #ifdef __x86_64__
+    if ( p2m_is_paging(*t) && (q & P2M_ALLOC)
+         && p2m->domain == current->domain ) 
+    {
+        if ( locked )
+            gfn_unlock(p2m, gfn, 0);
+
+        /* Ping the pager */
+        if ( *t == p2m_ram_paging_out || *t == p2m_ram_paged )
+            p2m_mem_paging_populate(p2m->domain, gfn);
+
+        /* Wait until the pager finishes paging it in */
+        current->arch.mem_paging_gfn = gfn;
+        wait_event(current->arch.mem_paging_wq, ({
+                    int done;
+                    mfn = p2m->get_entry(p2m, gfn, t, a, 0, page_order);
+                    done = (*t != p2m_ram_paging_in);
+                    /* Safety catch: it _should_ be safe to wait here
+                     * but if it's not, crash the VM, not the host */
+                    if ( in_atomic() )
+                    {
+                        WARN();
+                        domain_crash(p2m->domain);
+                        done = 1;
+                    }
+                    done;
+                }));
+        goto again;
+    }
+#endif
+
+#ifdef __x86_64__
     if ( (q & P2M_UNSHARE) && p2m_is_shared(*t) )
     {
         ASSERT(!p2m_is_nestedp2m(p2m));
@@ -946,17 +982,17 @@ void p2m_mem_paging_drop_page(struct dom
  * This function needs to be called whenever gfn_to_mfn() returns any of the 
p2m
  * paging types because the gfn may not be backed by a mfn.
  *
- * The gfn can be in any of the paging states, but the pager needs only be
- * notified when the gfn is in the paging-out path (paging_out or paged).  This
- * function may be called more than once from several vcpus. If the vcpu 
belongs
- * to the guest, the vcpu must be stopped and the pager notified that the vcpu
- * was stopped. The pager needs to handle several requests for the same gfn.
+ * The gfn can be in any of the paging states, but the pager needs only
+ * be notified when the gfn is in the paging-out path (paging_out or
+ * paged).  This function may be called more than once from several
+ * vcpus.  The pager needs to handle several requests for the same gfn.
  *
- * If the gfn is not in the paging-out path and the vcpu does not belong to the
- * guest, nothing needs to be done and the function assumes that a request was
- * already sent to the pager. In this case the caller has to try again until 
the
- * gfn is fully paged in again.
+ * If the gfn is not in the paging-out path nothing needs to be done and
+ * the function assumes that a request was already sent to the pager.
+ * In this case the caller has to try again until the gfn is fully paged
+ * in again.
  */
+
 void p2m_mem_paging_populate(struct domain *d, unsigned long gfn)
 {
     struct vcpu *v = current;
@@ -965,6 +1001,7 @@ void p2m_mem_paging_populate(struct doma
     p2m_access_t a;
     mfn_t mfn;
     struct p2m_domain *p2m = p2m_get_hostp2m(d);
+    int send_request = 0;
 
     /* We're paging. There should be a ring */
     int rc = mem_event_claim_slot(d, &d->mem_event->paging);
@@ -987,19 +1024,22 @@ void p2m_mem_paging_populate(struct doma
         /* Evict will fail now, tag this request for pager */
         if ( p2mt == p2m_ram_paging_out )
             req.flags |= MEM_EVENT_FLAG_EVICT_FAIL;
-
-        set_p2m_entry(p2m, gfn, mfn, PAGE_ORDER_4K, p2m_ram_paging_in, a);
+        if ( p2mt == p2m_ram_paging_out && mfn_valid(mfn) && v->domain == d )
+            /* Short-cut back to paged-in state (but not for foreign 
+             * mappings, or the pager couldn't map it to page it out) */
+            set_p2m_entry(p2m, gfn, mfn, PAGE_ORDER_4K,
+                          paging_mode_log_dirty(d) 
+                          ? p2m_ram_logdirty : p2m_ram_rw, a);
+        else
+        {
+            set_p2m_entry(p2m, gfn, mfn, PAGE_ORDER_4K, p2m_ram_paging_in, a);
+            send_request = 1;
+        }
     }
     gfn_unlock(p2m, gfn, 0);
 
-    /* Pause domain if request came from guest and gfn has paging type */
-    if ( p2m_is_paging(p2mt) && v->domain == d )
-    {
-        vcpu_pause_nosync(v);
-        req.flags |= MEM_EVENT_FLAG_VCPU_PAUSED;
-    }
     /* No need to inform pager if the gfn is not in the page-out path */
-    else if ( p2mt != p2m_ram_paging_out && p2mt != p2m_ram_paged )
+    if ( p2mt != p2m_ram_paging_out && p2mt != p2m_ram_paged )
     {
         /* gfn is already on its way back and vcpu is not paused */
         mem_event_cancel_slot(d, &d->mem_event->paging);
@@ -1122,6 +1162,7 @@ void p2m_mem_paging_resume(struct domain
 {
     struct p2m_domain *p2m = p2m_get_hostp2m(d);
     mem_event_response_t rsp;
+    struct vcpu *v;
     p2m_type_t p2mt;
     p2m_access_t a;
     mfn_t mfn;
@@ -1147,9 +1188,10 @@ void p2m_mem_paging_resume(struct domain
             }
             gfn_unlock(p2m, rsp.gfn, 0);
         }
-        /* Unpause domain */
-        if ( rsp.flags & MEM_EVENT_FLAG_VCPU_PAUSED )
-            vcpu_unpause(d->vcpu[rsp.vcpu_id]);
+        /* Wake any vcpus that were waiting for this GFN */
+        for_each_vcpu ( d, v )
+            if ( v->arch.mem_paging_gfn == rsp.gfn )
+                wake_up_all(&v->arch.mem_paging_wq);
     }
 }
 
diff -r 4ed10bf6325a -r 510d80343793 xen/include/asm-x86/domain.h
--- a/xen/include/asm-x86/domain.h      Thu Feb 23 16:15:29 2012 +0000
+++ b/xen/include/asm-x86/domain.h      Thu Feb 23 16:18:09 2012 +0000
@@ -4,6 +4,7 @@
 #include <xen/config.h>
 #include <xen/mm.h>
 #include <xen/radix-tree.h>
+#include <xen/wait.h>
 #include <asm/hvm/vcpu.h>
 #include <asm/hvm/domain.h>
 #include <asm/e820.h>
@@ -491,6 +492,12 @@ struct arch_vcpu
     
     struct paging_vcpu paging;
 
+#ifdef CONFIG_X86_64
+    /* Mem-paging: this vcpu is waiting for a gfn to be paged in */
+    struct waitqueue_head mem_paging_wq;
+    unsigned long mem_paging_gfn;
+#endif
+
 #ifdef CONFIG_X86_32
     /* map_domain_page() mapping cache. */
     struct mapcache_vcpu mapcache;
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel

 


Rackspace

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