[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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |