diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c index 6450850457..99201ac615 100644 --- a/xen/arch/arm/mm.c +++ b/xen/arch/arm/mm.c @@ -1222,7 +1222,7 @@ void share_xen_page_with_guest(struct page_info *page, struct domain *d, int xenmem_add_to_physmap_one( struct domain *d, unsigned int space, - union xen_add_to_physmap_batch_extra extra, + union add_to_physmap_extra extra, unsigned long idx, gfn_t gfn) { @@ -1294,10 +1294,6 @@ int xenmem_add_to_physmap_one( break; } case XENMAPSPACE_dev_mmio: - /* extra should be 0. Reserved for future use. */ - if ( extra.res0 ) - return -EOPNOTSUPP; - rc = map_dev_mmio_region(d, gfn, 1, _mfn(idx)); return rc; diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index 4ff24de73d..5ca5c8c9a2 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -4678,7 +4678,7 @@ static int handle_iomem_range(unsigned long s, unsigned long e, void *p) int xenmem_add_to_physmap_one( struct domain *d, unsigned int space, - union xen_add_to_physmap_batch_extra extra, + union add_to_physmap_extra extra, unsigned long idx, gfn_t gpfn) { @@ -4765,9 +4765,20 @@ int xenmem_add_to_physmap_one( rc = guest_physmap_add_page(d, gpfn, mfn, PAGE_ORDER_4K); put_both: - /* In the XENMAPSPACE_gmfn case, we took a ref of the gfn at the top. */ + /* + * In the XENMAPSPACE_gmfn case, we took a ref of the gfn at the top. + * We also may need to transfer ownership of the page reference to our + * caller. + */ if ( space == XENMAPSPACE_gmfn ) + { put_gfn(d, gfn); + if ( !rc && extra.ppage ) + { + *extra.ppage = page; + page = NULL; + } + } if ( page ) put_page(page); diff --git a/xen/common/memory.c b/xen/common/memory.c index 0ae60378f4..a4cf496f24 100644 --- a/xen/common/memory.c +++ b/xen/common/memory.c @@ -811,11 +811,10 @@ int xenmem_add_to_physmap(struct domain *d, struct xen_add_to_physmap *xatp, { unsigned int done = 0; long rc = 0; - union xen_add_to_physmap_batch_extra extra; + union add_to_physmap_extra extra = {}; + struct page_info *pages[16]; - if ( xatp->space != XENMAPSPACE_gmfn_foreign ) - extra.res0 = 0; - else + if ( xatp->space == XENMAPSPACE_gmfn_foreign ) extra.foreign_domid = DOMID_INVALID; if ( xatp->space != XENMAPSPACE_gmfn_range ) @@ -831,7 +830,10 @@ int xenmem_add_to_physmap(struct domain *d, struct xen_add_to_physmap *xatp, #ifdef CONFIG_HAS_PASSTHROUGH if ( need_iommu(d) ) + { this_cpu(iommu_dont_flush_iotlb) = 1; + extra.ppage = &pages[0]; + } #endif while ( xatp->size > done ) @@ -844,8 +846,12 @@ int xenmem_add_to_physmap(struct domain *d, struct xen_add_to_physmap *xatp, xatp->idx++; xatp->gpfn++; + if ( extra.ppage ) + ++extra.ppage; + /* Check for continuation if it's not the last iteration. */ - if ( xatp->size > ++done && hypercall_preempt_check() ) + if ( (++done > ARRAY_SIZE(pages) && extra.ppage) || + (xatp->size > done && hypercall_preempt_check()) ) { rc = start + done; break; @@ -856,6 +862,7 @@ int xenmem_add_to_physmap(struct domain *d, struct xen_add_to_physmap *xatp, if ( need_iommu(d) ) { int ret; + unsigned int i; this_cpu(iommu_dont_flush_iotlb) = 0; @@ -863,6 +870,15 @@ int xenmem_add_to_physmap(struct domain *d, struct xen_add_to_physmap *xatp, if ( unlikely(ret) && rc >= 0 ) rc = ret; + /* + * Now that the IOMMU TLB flush was done for the original GFN, drop + * the page references. The 2nd flush below is fine to make later, as + * whoever removes the page again from its new GFN will have to do + * another flush anyway. + */ + for ( i = 0; i < done; ++i ) + put_page(pages[i]); + ret = iommu_iotlb_flush(d, xatp->gpfn - done, done); if ( unlikely(ret) && rc >= 0 ) rc = ret; @@ -876,6 +892,8 @@ static int xenmem_add_to_physmap_batch(struct domain *d, struct xen_add_to_physmap_batch *xatpb, unsigned int extent) { + union add_to_physmap_extra extra = {}; + if ( xatpb->size < extent ) return -EILSEQ; @@ -884,6 +902,19 @@ static int xenmem_add_to_physmap_batch(struct domain *d, !guest_handle_subrange_okay(xatpb->errs, extent, xatpb->size - 1) ) return -EFAULT; + switch ( xatpb->space ) + { + case XENMAPSPACE_dev_mmio: + /* res0 is reserved for future use. */ + if ( xatpb->u.res0 ) + return -EOPNOTSUPP; + break; + + case XENMAPSPACE_gmfn_foreign: + extra.foreign_domid = xatpb->u.foreign_domid; + break; + } + while ( xatpb->size > extent ) { xen_ulong_t idx; @@ -896,8 +927,7 @@ static int xenmem_add_to_physmap_batch(struct domain *d, extent, 1)) ) return -EFAULT; - rc = xenmem_add_to_physmap_one(d, xatpb->space, - xatpb->u, + rc = xenmem_add_to_physmap_one(d, xatpb->space, extra, idx, _gfn(gpfn)); if ( unlikely(__copy_to_guest_offset(xatpb->errs, extent, &rc, 1)) ) diff --git a/xen/include/xen/mm.h b/xen/include/xen/mm.h index 24654e8e22..38002714de 100644 --- a/xen/include/xen/mm.h +++ b/xen/include/xen/mm.h @@ -577,8 +577,22 @@ void scrub_one_page(struct page_info *); &(d)->xenpage_list : &(d)->page_list) #endif +union add_to_physmap_extra { + /* + * XENMAPSPACE_gmfn: When deferring TLB flushes, a page reference needs + * to be kept until after the flush, so the page can't get removed from + * the domain (and re-used for another purpose) beforehand. By passing + * non-NULL, the caller of xenmem_add_to_physmap_one() indicates it wants + * to have ownership of such a reference transferred in the success case. + */ + struct page_info **ppage; + + /* XENMAPSPACE_gmfn_foreign */ + domid_t foreign_domid; +}; + int xenmem_add_to_physmap_one(struct domain *d, unsigned int space, - union xen_add_to_physmap_batch_extra extra, + union add_to_physmap_extra extra, unsigned long idx, gfn_t gfn); int xenmem_add_to_physmap(struct domain *d, struct xen_add_to_physmap *xatp,