|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v5 4/4] 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 it 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>
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 | 141 +++++++++++++++++++++++++++++++++----------
xen/include/public/memory.h | 47 ++++++++++++++
2 files changed, 156 insertions(+), 32 deletions(-)
diff --git a/xen/common/memory.c b/xen/common/memory.c
index 4b2f311..34169c1 100644
--- a/xen/common/memory.c
+++ b/xen/common/memory.c
@@ -364,14 +364,14 @@ 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);
PAGE_LIST_HEAD(out_chunk_list);
unsigned long in_chunk_order, out_chunk_order;
xen_pfn_t gpfn, gmfn, mfn;
- unsigned long i, j, k = 0; /* gcc ... */
+ unsigned long i = 0, j = 0, k = 0; /* gcc ... */
unsigned int memflags = 0;
long rc = 0;
struct domain *d;
@@ -542,54 +542,72 @@ static long
memory_exchange(XEN_GUEST_HANDLE_PARAM(xen_memory_exchange_t) arg)
/* Assign each output page to the domain. */
for ( j = 0; (page = page_list_remove_head(&out_chunk_list)); ++j )
{
- if ( assign_pages(d, page, exch.out.extent_order,
- MEMF_no_refcount) )
- {
- unsigned long dec_count;
- bool_t drop_dom_ref;
-
- /*
- * Pages in in_chunk_list is stolen without
- * decreasing the tot_pages. If the domain is dying when
- * assign pages, we need decrease the count. For those pages
- * that has been assigned, it should be covered by
- * domain_relinquish_resources().
- */
- dec_count = (((1UL << exch.in.extent_order) *
- (1UL << in_chunk_order)) -
- (j * (1UL << exch.out.extent_order)));
-
- spin_lock(&d->page_alloc_lock);
- domain_adjust_tot_pages(d, -dec_count);
- drop_dom_ref = (dec_count && !d->tot_pages);
- spin_unlock(&d->page_alloc_lock);
-
- if ( drop_dom_ref )
- put_domain(d);
-
- free_domheap_pages(page, exch.out.extent_order);
- goto dying;
- }
+ unsigned long dec_count;
+ bool_t drop_dom_ref;
if ( __copy_from_guest_offset(&gpfn, exch.out.extent_start,
(i << out_chunk_order) + j, 1) )
{
rc = -EFAULT;
- continue;
+ goto extent_error;
}
mfn = page_to_mfn(page);
guest_physmap_add_page(d, gpfn, mfn, exch.out.extent_order);
+ if ( op == XENMEM_exchange_and_pin )
+ {
+ if ( guest_physmap_pin_range(d, gpfn, exch.out.extent_order) )
+ {
+ rc = -EFAULT;
+ goto extent_error_physmap;
+ }
+ }
+
if ( !paging_mode_translate(d) )
{
for ( k = 0; k < (1UL << exch.out.extent_order); k++ )
set_gpfn_from_mfn(mfn + k, gpfn + k);
+ }
+
+ rc = assign_pages(d, page, exch.out.extent_order,
MEMF_no_refcount);
+ if ( rc )
+ goto extent_error_physmap;
+
+ 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) )
rc = -EFAULT;
}
+
+ continue;
+
+extent_error_physmap:
+ guest_physmap_remove_page(d, gpfn, mfn, exch.out.extent_order);
+extent_error:
+ /*
+ * Pages in in_chunk_list is stolen without
+ * decreasing the tot_pages. If the domain is dying when
+ * assign pages, we need decrease the count. For those pages
+ * that has been assigned, it should be covered by
+ * domain_relinquish_resources().
+ */
+ dec_count = (((1UL << exch.in.extent_order) *
+ (1UL << in_chunk_order)) -
+ (j * (1UL << exch.out.extent_order)));
+
+ spin_lock(&d->page_alloc_lock);
+ domain_adjust_tot_pages(d, -dec_count);
+ drop_dom_ref = (dec_count && !d->tot_pages);
+ spin_unlock(&d->page_alloc_lock);
+
+ if ( drop_dom_ref )
+ put_domain(d);
+
+ free_domheap_pages(page, exch.out.extent_order);
+ goto dying;
}
BUG_ON( !(d->is_dying) && (j != (1UL << out_chunk_order)) );
}
@@ -627,6 +645,60 @@ static long
memory_exchange(XEN_GUEST_HANDLE_PARAM(xen_memory_exchange_t) arg)
return rc;
}
+static long unpin(XEN_GUEST_HANDLE_PARAM(xen_unpin_t) arg)
+{
+ int rc;
+ unsigned long i;
+ struct xen_unpin unpin;
+ xen_pfn_t gpfn;
+ struct domain *d;
+
+ 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) )
+ return -EFAULT;
+
+ if ( !guest_handle_okay(unpin.in.extent_start, unpin.in.nr_extents) )
+ return -EFAULT;
+
+ d = rcu_lock_domain_by_any_id(unpin.in.domid);
+ if ( d == NULL )
+ {
+ rc = -ESRCH;
+ goto fail;
+ }
+
+ for ( i = 0; i < unpin.in.nr_extents; i++ )
+ {
+ if ( unlikely(__copy_from_guest_offset(
+ &gpfn, unpin.in.extent_start, i, 1)) )
+ {
+ rc = -EFAULT;
+ goto partial;
+ }
+
+ rc = guest_physmap_unpin_range(d, gpfn, unpin.in.extent_order);
+ if ( rc )
+ goto partial;
+ }
+
+ rc = 0;
+
+partial:
+ unpin.nr_unpinned = i;
+ if ( __copy_field_to_guest(arg, &unpin, nr_unpinned) )
+ rc = -EFAULT;
+
+ fail:
+ rcu_unlock_domain(d);
+ return rc;
+}
+
long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
{
struct domain *d;
@@ -715,8 +787,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..73fdc31 100644
--- a/xen/include/public/memory.h
+++ b/xen/include/public/memory.h
@@ -105,6 +105,7 @@ 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 should contain the address mask for the new pages.
*/
struct xen_memory_reservation in;
@@ -459,6 +460,52 @@ DEFINE_XEN_GUEST_HANDLE(xen_mem_sharing_op_t);
* The zero value is appropiate.
*/
+#define XENMEM_exchange_and_pin 26
+/*
+ * 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".
+ * The content of the exchanged pages is lost.
+ * 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 DMA frame
+ * numbers of the newly-allocated memory.
+ * Returns zero on complete success, otherwise a negative error code:
+ * -ENOSYS if not implemented
+ * -EINVAL 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_unpin 27
+/*
+ * 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.
+ */
+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 exents 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 |