|
[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 |