Stefano Stabellini
2013-Jul-31 19:57 UTC
[PATCH v2 RFC 0/3] introduce XENMEM_get_dma_buf and XENMEM_put_dma_buf
Hi all,
this patch series introduces two new hypercalls to allow autotranslate
guests to allocate a contiguous buffer in machine addresses.
The XENMEM_get_dma_buf returns the mfns and makes sure to pin the pages
so that the hypervisor won''t change their p2m mappings while in use.
XENMEM_put_dma_buf simply unpins the pages.
The implementation of XENMEM_put_dma_buf is missing, as it''s actually
unused.
The page pinning is also missing from this series. I would appreciate
feedback on the best way to implement it, especially on x86.
Cheers,
Stefano
Changes in v2:
- actually don''t print the warning more than once.
Stefano Stabellini (3):
xen/arm: implement steal_page
xen: provide empty stubs for guest_physmap_(un)pin_range on arm and x86
xen: introduce XENMEM_get_dma_buf and XENMEM_put_dma_buf
xen/arch/arm/mm.c | 43 +++++++++++++++++++++++++++++
xen/common/memory.c | 21 ++++++++++++--
xen/include/asm-arm/mm.h | 12 ++++++++
xen/include/asm-x86/p2m.h | 12 ++++++++
xen/include/public/memory.h | 64 +++++++++++++++++++++++++++++++++++++++++++
5 files changed, 149 insertions(+), 3 deletions(-)
git://xenbits.xen.org/people/sstabellini/xen-unstable.git get_dma_buf
Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
---
xen/arch/arm/mm.c | 43 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 43 insertions(+), 0 deletions(-)
diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c
index f301e65..ea64c03 100644
--- a/xen/arch/arm/mm.c
+++ b/xen/arch/arm/mm.c
@@ -751,6 +751,49 @@ int donate_page(struct domain *d, struct page_info *page,
unsigned int memflags)
int steal_page(
struct domain *d, struct page_info *page, unsigned int memflags)
{
+ unsigned long x, y;
+ bool_t drop_dom_ref = 0;
+
+ spin_lock(&d->page_alloc_lock);
+
+ if ( is_xen_heap_page(page) || (page_get_owner(page) != d) )
+ goto fail;
+
+ /*
+ * We require there is just one reference (PGC_allocated). We temporarily
+ * drop this reference now so that we can safely swizzle the owner.
+ */
+ y = page->count_info;
+ do {
+ x = y;
+ if ( (x & (PGC_count_mask|PGC_allocated)) != (1 | PGC_allocated) )
+ goto fail;
+ y = cmpxchg(&page->count_info, x, x & ~PGC_count_mask);
+ } while ( y != x );
+
+ /* Swizzle the owner then reinstate the PGC_allocated reference. */
+ page_set_owner(page, NULL);
+ y = page->count_info;
+ do {
+ x = y;
+ BUG_ON((x & (PGC_count_mask|PGC_allocated)) != PGC_allocated);
+ } while ( (y = cmpxchg(&page->count_info, x, x | 1)) != x );
+
+ /* Unlink from original owner. */
+ if ( !(memflags & MEMF_no_refcount) &&
!domain_adjust_tot_pages(d, -1) )
+ drop_dom_ref = 1;
+ page_list_del(page, &d->page_list);
+
+ spin_unlock(&d->page_alloc_lock);
+ if ( unlikely(drop_dom_ref) )
+ put_domain(d);
+ return 0;
+
+ fail:
+ spin_unlock(&d->page_alloc_lock);
+ printk("Bad page %p: ed=%p(%u), sd=%p, caf=%08lx, taf=%lx\n",
+ (void *)page_to_mfn(page), d, d->domain_id,
+ page_get_owner(page), page->count_info,
page->u.inuse.type_info);
return -1;
}
--
1.7.2.5
Stefano Stabellini
2013-Jul-31 19:57 UTC
[PATCH v2 RFC 2/3] xen: provide empty stubs for guest_physmap_(un)pin_range on arm and x86
guest_physmap_pin_range pins a range of guest pages so that they p2m
mappings won''t be changed.
guest_physmap_unpin_range unpins the previously pinned pages.
Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
---
xen/include/asm-arm/mm.h | 12 ++++++++++++
xen/include/asm-x86/p2m.h | 12 ++++++++++++
2 files changed, 24 insertions(+), 0 deletions(-)
diff --git a/xen/include/asm-arm/mm.h b/xen/include/asm-arm/mm.h
index 5e7c5a3..d88fa6c 100644
--- a/xen/include/asm-arm/mm.h
+++ b/xen/include/asm-arm/mm.h
@@ -319,6 +319,18 @@ void free_init_memory(void);
int guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn,
unsigned int order);
+static inline int guest_physmap_pin_range(struct domain *d,
+ xen_pfn_t gpfn,
+ unsigned int order)
+{
+ return -ENOSYS;
+}
+static inline int guest_physmap_unpin_range(struct domain *d,
+ xen_pfn_t gpfn,
+ unsigned int order)
+{
+ return -ENOSYS;
+}
extern void put_page_type(struct page_info *page);
static inline void put_page_and_type(struct page_info *page)
diff --git a/xen/include/asm-x86/p2m.h b/xen/include/asm-x86/p2m.h
index 43583b2..afc7738 100644
--- a/xen/include/asm-x86/p2m.h
+++ b/xen/include/asm-x86/p2m.h
@@ -492,6 +492,18 @@ void guest_physmap_remove_page(struct domain *d,
/* Set a p2m range as populate-on-demand */
int guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn,
unsigned int order);
+static inline int guest_physmap_pin_range(struct domain *d,
+ xen_pfn_t gpfn,
+ unsigned int order)
+{
+ return -ENOSYS;
+}
+int guest_physmap_unpin_range(struct domain *d,
+ xen_pfn_t gpfn,
+ unsigned int order)
+{
+ return -ENOSYS;
+}
/* Change types across all p2m entries in a domain */
void p2m_change_entry_type_global(struct domain *d,
--
1.7.2.5
Stefano Stabellini
2013-Jul-31 19:57 UTC
[PATCH v2 RFC 3/3] xen: introduce XENMEM_get_dma_buf and XENMEM_put_dma_buf
Introduce two new hypercalls XENMEM_get_dma_buf and XENMEM_put_dma_buf.
XENMEM_get_dma_buf exchanges a set of pages for a new set, contiguous and
under 4G if so requested. The new pages are going to be "pinned" so
that
their p2m mapping won''t be changed, until XENMEM_put_dma_buf is called.
XENMEM_get_dma_buf returns the MFNs of the new pages to the caller.
The only effect of XENMEM_put_dma_buf 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.
XENMEM_put_dma_buf is unimplemented.
Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
---
xen/common/memory.c | 21 ++++++++++++--
xen/include/public/memory.h | 64 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 82 insertions(+), 3 deletions(-)
diff --git a/xen/common/memory.c b/xen/common/memory.c
index 50b740f..7e26d3f 100644
--- a/xen/common/memory.c
+++ b/xen/common/memory.c
@@ -279,7 +279,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);
@@ -496,7 +496,7 @@ static long
memory_exchange(XEN_GUEST_HANDLE_PARAM(xen_memory_exchange_t) arg)
mfn = page_to_mfn(page);
guest_physmap_add_page(d, gpfn, mfn, exch.out.extent_order);
- if ( !paging_mode_translate(d) )
+ if ( op == XENMEM_get_dma_buf || !paging_mode_translate(d) )
{
for ( k = 0; k < (1UL << exch.out.extent_order); k++ )
set_gpfn_from_mfn(mfn + k, gpfn + k);
@@ -505,6 +505,18 @@ static long
memory_exchange(XEN_GUEST_HANDLE_PARAM(xen_memory_exchange_t) arg)
&mfn, 1) )
rc = -EFAULT;
}
+
+ if ( op == XENMEM_get_dma_buf )
+ {
+ static int warning;
+ if ( guest_physmap_pin_range(d, gpfn, exch.out.extent_order) )
+ {
+ if (!warning) {
+ gdprintk(XENLOG_WARNING, "guest_physmap_pin_range
not implemented\n");
+ warning = 1;
+ }
+ }
+ }
}
BUG_ON( !(d->is_dying) && (j != (1UL <<
out_chunk_order)) );
}
@@ -630,8 +642,11 @@ long do_memory_op(unsigned long cmd,
XEN_GUEST_HANDLE_PARAM(void) arg)
break;
+ case XENMEM_get_dma_buf:
+ /* xen_get_dma_buf_t is identical to xen_memory_exchange_t, so
+ * just cast it and reuse memory_exchange */
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_maximum_ram_page:
diff --git a/xen/include/public/memory.h b/xen/include/public/memory.h
index 7a26dee..354b117 100644
--- a/xen/include/public/memory.h
+++ b/xen/include/public/memory.h
@@ -459,6 +459,70 @@ DEFINE_XEN_GUEST_HANDLE(xen_mem_sharing_op_t);
* The zero value is appropiate.
*/
+#define XENMEM_get_dma_buf 26
+/*
+ * This hypercall is similar to XENMEM_exchange: it exchanges the pages
+ * passed in with a new set of pages, contiguous and under 4G if so
+ * requested. The new pages are going to be "pinned": it''s
guaranteed
+ * that their p2m mapping won''t be changed until explicitly
"unpinned".
+ * If return code is zero then @out.extent_list provides the MFNs of the
+ * newly-allocated memory. Returns zero on complete success, otherwise
+ * a negative error code.
+ * On complete success then always @nr_exchanged == @in.nr_extents. On
+ * partial success @nr_exchanged indicates how much work was done.
+ */
+struct xen_get_dma_buf {
+ /*
+ * [IN] Details of memory extents to be exchanged (GMFN bases).
+ * Note that @in.address_bits is ignored and unused.
+ */
+ struct xen_memory_reservation in;
+
+ /*
+ * [IN/OUT] Details of new memory extents.
+ * We require that:
+ * 1. @in.domid == @out.domid
+ * 2. @in.nr_extents << @in.extent_order ==
+ * @out.nr_extents << @out.extent_order
+ * 3. @in.extent_start and @out.extent_start lists must not overlap
+ * 4. @out.extent_start lists GPFN bases to be populated
+ * 5. @out.extent_start is overwritten with allocated GMFN bases
+ */
+ struct xen_memory_reservation out;
+
+ /*
+ * [OUT] Number of input extents that were successfully exchanged:
+ * 1. The first @nr_exchanged input extents were successfully
+ * deallocated.
+ * 2. The corresponding first entries in the output extent list correctly
+ * indicate the GMFNs that were successfully exchanged.
+ * 3. All other input and output extents are untouched.
+ * 4. If not all input exents are exchanged then the return code of this
+ * command will be non-zero.
+ * 5. THIS FIELD MUST BE INITIALISED TO ZERO BY THE CALLER!
+ */
+ xen_ulong_t nr_exchanged;
+};
+typedef struct xen_get_dma_buf xen_get_dma_buf_t;
+DEFINE_XEN_GUEST_HANDLE(xen_get_dma_buf_t);
+
+#define XENMEM_put_dma_buf 27
+/*
+ * XENMEM_put_dma_buf unpins a set of pages, previously pinned by
+ * XENMEM_get_dma_buf. 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_put_dma_buf {
+ /*
+ * [IN] Details of memory extents to be exchanged (GMFN bases).
+ * Note that @in.address_bits is ignored and unused.
+ */
+ struct xen_memory_reservation in;
+};
+typedef struct xen_put_dma_buf xen_put_dma_buf_t;
+DEFINE_XEN_GUEST_HANDLE(xen_put_dma_buf_t);
+
#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */
#endif /* __XEN_PUBLIC_MEMORY_H__ */
--
1.7.2.5