diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index e222d9aa98..4ee2de1105 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -4777,7 +4777,6 @@ long arch_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg) { xen_pod_target_t target; struct domain *d; - struct p2m_domain *p2m; if ( copy_from_guest(&target, arg, 1) ) return -EFAULT; @@ -4812,10 +4811,7 @@ long arch_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg) } else if ( rc >= 0 ) { - p2m = p2m_get_hostp2m(d); - target.tot_pages = domain_tot_pages(d); - target.pod_cache_pages = p2m->pod.count; - target.pod_entries = p2m->pod.entry_count; + p2m_pod_get_mem_target(d, &target); if ( __copy_to_guest(arg, &target, 1) ) { diff --git a/xen/arch/x86/mm/p2m-pod.c b/xen/arch/x86/mm/p2m-pod.c index d8d1a0ce7e..a3c9d8a974 100644 --- a/xen/arch/x86/mm/p2m-pod.c +++ b/xen/arch/x86/mm/p2m-pod.c @@ -20,6 +20,7 @@ */ #include +#include #include #include #include @@ -362,7 +363,10 @@ p2m_pod_set_mem_target(struct domain *d, unsigned long target) ASSERT( pod_target >= p2m->pod.count ); - ret = p2m_pod_set_cache_target(p2m, pod_target, 1/*preemptible*/); + if ( has_arch_pdevs(d) || cache_flush_permitted(d) ) + ret = -ENOTEMPTY; + else + ret = p2m_pod_set_cache_target(p2m, pod_target, 1/*preemptible*/); out: pod_unlock(p2m); @@ -370,6 +374,23 @@ out: return ret; } +void p2m_pod_get_mem_target(const struct domain *d, xen_pod_target_t *target) +{ + struct p2m_domain *p2m = p2m_get_hostp2m(d); + + ASSERT(is_hvm_domain(d)); + + pod_lock(p2m); + lock_page_alloc(p2m); + + target->tot_pages = domain_tot_pages(d); + target->pod_cache_pages = p2m->pod.count; + target->pod_entries = p2m->pod.entry_count; + + unlock_page_alloc(p2m); + pod_unlock(p2m); +} + int p2m_pod_empty_cache(struct domain *d) { struct p2m_domain *p2m = p2m_get_hostp2m(d); @@ -1387,6 +1408,9 @@ guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn, if ( !paging_mode_translate(d) ) return -EINVAL; + if ( has_arch_pdevs(d) || cache_flush_permitted(d) ) + return -ENOTEMPTY; + do { rc = mark_populate_on_demand(d, gfn, chunk_order); @@ -1408,3 +1432,20 @@ void p2m_pod_init(struct p2m_domain *p2m) for ( i = 0; i < ARRAY_SIZE(p2m->pod.mrp.list); ++i ) p2m->pod.mrp.list[i] = gfn_x(INVALID_GFN); } + +bool p2m_pod_active(const struct domain *d) +{ + struct p2m_domain *p2m; + bool res; + + if ( !is_hvm_domain(d) ) + return false; + + p2m = p2m_get_hostp2m(d); + + pod_lock(p2m); + res = p2m->pod.entry_count | p2m->pod.count; + pod_unlock(p2m); + + return res; +} diff --git a/xen/common/vm_event.c b/xen/common/vm_event.c index 70ab3ba406..21d2f0edf7 100644 --- a/xen/common/vm_event.c +++ b/xen/common/vm_event.c @@ -639,7 +639,7 @@ int vm_event_domctl(struct domain *d, struct xen_domctl_vm_event_op *vec) rc = -EXDEV; /* Disallow paging in a PoD guest */ - if ( p2m_pod_entry_count(p2m_get_hostp2m(d)) ) + if ( p2m_pod_active(d) ) break; /* domain_pause() not required here, see XSA-99 */ diff --git a/xen/drivers/passthrough/x86/iommu.c b/xen/drivers/passthrough/x86/iommu.c index a36a6bd4b2..dc9936e169 100644 --- a/xen/drivers/passthrough/x86/iommu.c +++ b/xen/drivers/passthrough/x86/iommu.c @@ -502,11 +502,12 @@ bool arch_iommu_use_permitted(const struct domain *d) { /* * Prevent device assign if mem paging, mem sharing or log-dirty - * have been enabled for this domain. + * have been enabled for this domain, or if PoD is still in active use. */ return d == dom_io || (likely(!mem_sharing_enabled(d)) && likely(!mem_paging_enabled(d)) && + likely(!p2m_pod_active(d)) && likely(!p2m_get_hostp2m(d)->global_logdirty)); } diff --git a/xen/include/asm-x86/p2m.h b/xen/include/asm-x86/p2m.h index 357a808748..f2af7a746c 100644 --- a/xen/include/asm-x86/p2m.h +++ b/xen/include/asm-x86/p2m.h @@ -661,6 +661,12 @@ int p2m_pod_empty_cache(struct domain *d); * domain matches target */ int p2m_pod_set_mem_target(struct domain *d, unsigned long target); +/* Obtain a consistent snapshot of PoD related domain state. */ +void p2m_pod_get_mem_target(const struct domain *d, xen_pod_target_t *target); + +/* Check whether PoD is (still) active in a domain. */ +bool p2m_pod_active(const struct domain *d); + /* Scan pod cache when offline/broken page triggered */ int p2m_pod_offline_or_broken_hit(struct page_info *p); @@ -669,11 +675,6 @@ p2m_pod_offline_or_broken_hit(struct page_info *p); void p2m_pod_offline_or_broken_replace(struct page_info *p); -static inline long p2m_pod_entry_count(const struct p2m_domain *p2m) -{ - return p2m->pod.entry_count; -} - void p2m_pod_init(struct p2m_domain *p2m); #else @@ -689,6 +690,11 @@ static inline int p2m_pod_empty_cache(struct domain *d) return 0; } +static inline bool p2m_pod_active(const struct domain *d) +{ + return false; +} + static inline int p2m_pod_offline_or_broken_hit(struct page_info *p) { return 0; @@ -699,11 +705,6 @@ static inline void p2m_pod_offline_or_broken_replace(struct page_info *p) ASSERT_UNREACHABLE(); } -static inline long p2m_pod_entry_count(const struct p2m_domain *p2m) -{ - return 0; -} - static inline void p2m_pod_init(struct p2m_domain *p2m) {} #endif