[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v6 4/5] xen: introduce XENMEM_exchange_and_pin and XENMEM_unpin
Introduce two new hypercalls XENMEM_exchange_and_pin and XENMEM_unpin. XENMEM_exchange_and_pin, it's like XENMEM_exchange but also pins the new pages: their p2m mapping are guaranteed not to be changed, until XENMEM_unpin is called. XENMEM_exchange_and_pin returns the DMA frame numbers of the new pages to the caller, even if it's an autotranslate guest. The only effect of XENMEM_unpin is to "unpin" the previously pinned pages. Afterwards the p2m mappings can be transparently changed by the hypervisor as normal. The memory remains accessible from the guest. Signed-off-by: Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx> CC: tim@xxxxxxx CC: keir@xxxxxxx CC: JBeulich@xxxxxxxx Changes in v6: - do not change error paths; - crash the guest on pinning failure; - use the right op with hypercall_create_continuation; - comments in public/memory.h should precede the #define; - make unpin return int; - improve the description of the new hypercalls; - make sure to copy back the right value of nr_unpinned even on early error paths. Changes in v5: - memory_exchange: handle guest_physmap_pin_range failures; - make i an unsigned long in unpinned; - add nr_unpinned to xen_unpin to report partial success. Changes in v4: - rename XENMEM_get_dma_buf to XENMEM_exchange_and_pin; - rename XENMEM_get_dma_buf to XENMEM_unpin; - move the pinning before we copy back the mfn to the guest; - propagate errors returned by guest_physmap_pin_range; - use xen_memory_exchange_t as parameter for XENMEM_exchange_and_pin; - use an unsigned iterator in unpin; - improve the documentation of the new hypercalls; - add a note about out.address_bits for XENMEM_exchange. --- xen/common/memory.c | 97 +++++++++++++++++++++++++++++++++++++++++- xen/include/public/memory.h | 52 +++++++++++++++++++++++ 2 files changed, 146 insertions(+), 3 deletions(-) diff --git a/xen/common/memory.c b/xen/common/memory.c index c216004..31a85be 100644 --- a/xen/common/memory.c +++ b/xen/common/memory.c @@ -394,7 +394,7 @@ static void decrease_reservation(struct memop_args *a) a->nr_done = i; } -static long memory_exchange(XEN_GUEST_HANDLE_PARAM(xen_memory_exchange_t) arg) +static long memory_exchange(int op, XEN_GUEST_HANDLE_PARAM(xen_memory_exchange_t) arg) { struct xen_memory_exchange exch; PAGE_LIST_HEAD(in_chunk_list); @@ -470,6 +470,13 @@ static long memory_exchange(XEN_GUEST_HANDLE_PARAM(xen_memory_exchange_t) arg) goto fail_early; } + if ( op == XENMEM_exchange_and_pin && !is_hardware_domain(d) ) + { + rc = -EPERM; + rcu_unlock_domain(d); + goto fail_early; + } + memflags |= MEMF_bits(domain_clamp_alloc_bitsize( d, XENMEMF_get_address_bits(exch.out.mem_flags) ? : @@ -487,7 +494,7 @@ static long memory_exchange(XEN_GUEST_HANDLE_PARAM(xen_memory_exchange_t) arg) if ( __copy_field_to_guest(arg, &exch, nr_exchanged) ) return -EFAULT; return hypercall_create_continuation( - __HYPERVISOR_memory_op, "lh", XENMEM_exchange, arg); + __HYPERVISOR_memory_op, "lh", op, arg); } /* Steal a chunk's worth of input pages from the domain. */ @@ -615,6 +622,21 @@ static long memory_exchange(XEN_GUEST_HANDLE_PARAM(xen_memory_exchange_t) arg) { for ( k = 0; k < (1UL << exch.out.extent_order); k++ ) set_gpfn_from_mfn(mfn + k, gpfn + k); + } + if ( op == XENMEM_exchange_and_pin ) + { + rc = guest_physmap_pin_range(d, gpfn, exch.out.extent_order); + if ( rc ) + { + gdprintk(XENLOG_WARNING, "couldn't pin gpfn=%"PRIpaddr" order=%u\n", + gpfn, exch.out.extent_order); + rcu_unlock_domain(d); + domain_crash(d); + return -EFAULT; + } + } + if ( op == XENMEM_exchange_and_pin || !paging_mode_translate(d) ) + { if ( __copy_to_guest_offset(exch.out.extent_start, (i << out_chunk_order) + j, &mfn, 1) ) @@ -657,6 +679,70 @@ static long memory_exchange(XEN_GUEST_HANDLE_PARAM(xen_memory_exchange_t) arg) return rc; } +static int unpin(XEN_GUEST_HANDLE_PARAM(xen_unpin_t) arg) +{ + int rc; + unsigned long i = 0; + struct xen_unpin unpin; + xen_pfn_t gpfn; + struct domain *d = NULL; + + if ( copy_from_guest(&unpin, arg, 1) ) + return -EFAULT; + + /* Various sanity checks. */ + if ( /* Extent orders are sensible? */ + (unpin.in.extent_order > MAX_ORDER) || + /* Sizes of input list do not overflow a long? */ + ((~0UL >> unpin.in.extent_order) < unpin.in.nr_extents) ) + { + rc = -EFAULT; + goto fail; + } + + if ( !guest_handle_okay(unpin.in.extent_start, unpin.in.nr_extents) ) + { + rc = -EFAULT; + goto fail; + } + + d = rcu_lock_domain_by_any_id(unpin.in.domid); + if ( d == NULL ) + { + rc = -ESRCH; + goto fail; + } + + if ( !is_hardware_domain(d) ) + { + rc = -EPERM; + goto fail; + } + + for ( ; i < unpin.in.nr_extents; i++ ) + { + if ( unlikely(__copy_from_guest_offset( + &gpfn, unpin.in.extent_start, i, 1)) ) + { + rc = -EFAULT; + goto fail; + } + + rc = guest_physmap_unpin_range(d, gpfn, unpin.in.extent_order); + if ( rc ) + goto fail; + } + + rc = 0; + + fail: + unpin.nr_unpinned = i; + if ( __copy_field_to_guest(arg, &unpin, nr_unpinned) ) + rc = -EFAULT; + rcu_unlock_domain(d); + return rc; +} + long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg) { struct domain *d; @@ -745,8 +831,13 @@ long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg) break; + case XENMEM_exchange_and_pin: case XENMEM_exchange: - rc = memory_exchange(guest_handle_cast(arg, xen_memory_exchange_t)); + rc = memory_exchange(op, guest_handle_cast(arg, xen_memory_exchange_t)); + break; + + case XENMEM_unpin: + rc = unpin(guest_handle_cast(arg, xen_unpin_t)); break; case XENMEM_maximum_ram_page: diff --git a/xen/include/public/memory.h b/xen/include/public/memory.h index 7a26dee..4accbd4 100644 --- a/xen/include/public/memory.h +++ b/xen/include/public/memory.h @@ -105,6 +105,9 @@ struct xen_memory_exchange { /* * [IN] Details of memory extents to be exchanged (GMFN bases). * Note that @in.address_bits is ignored and unused. + * @out.address_bits contains the maximum number of bits addressable + * by the caller. The addresses of the newly allocated pages have to + * meet this restriction. */ struct xen_memory_reservation in; @@ -459,6 +462,55 @@ DEFINE_XEN_GUEST_HANDLE(xen_mem_sharing_op_t); * The zero value is appropiate. */ +/* + * This hypercall is similar to XENMEM_exchange: it takes the same + * struct as an argument and it exchanges the pages passed in with a new + * set of pages. The new pages are going to be "pinned": it's guaranteed + * that their p2m mapping won't be changed until explicitly "unpinned". + * Only normal guest r/w memory can be pinned: no granted pages or + * ballooned pages. + * If return code is zero then @out.extent_list provides the frame + * numbers of the newly-allocated memory. + * On X86 the frame numbers are machine frame numbers (mfns). + * On ARMv7 and ARMv8 the frame numbers are machine frame numbers (mfns). + * Returns zero on complete success, otherwise a negative error code. + * The most common error codes are: + * -ENOSYS if not implemented + * -EPERM if the domain is not privileged for this operation + * -EBUSY if the page is already pinned + * -EFAULT if an internal error occurs + * On complete success then always @nr_exchanged == @in.nr_extents. On + * partial success @nr_exchanged indicates how much work was done and a + * negative error code is returned. + */ +#define XENMEM_exchange_and_pin 26 + +/* + * XENMEM_unpin unpins a set of pages, previously pinned by + * XENMEM_exchange_and_pin. After this call the p2m mapping of the pages can + * be transparently changed by the hypervisor, as usual. The pages are + * still accessible from the guest. + */ +#define XENMEM_unpin 27 +struct xen_unpin { + /* + * [IN] Details of memory extents to be unpinned (GMFN bases). + * Note that @in.address_bits is ignored and unused. + */ + struct xen_memory_reservation in; + /* + * [OUT] Number of input extents that were successfully unpinned. + * 1. The first @nr_unpinned input extents were successfully + * unpinned. + * 2. All other input extents are untouched. + * 3. If not all input extents are unpinned then the return code of this + * command will be non-zero. + */ + xen_ulong_t nr_unpinned; +}; +typedef struct xen_unpin xen_unpin_t; +DEFINE_XEN_GUEST_HANDLE(xen_unpin_t); + #endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ #endif /* __XEN_PUBLIC_MEMORY_H__ */ -- 1.7.2.5 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |