* [PATCHv3] xen/gntdev: add ioctl for grant copy
@ 2015-11-27 17:17 David Vrabel
2015-11-30 21:39 ` Konrad Rzeszutek Wilk
0 siblings, 1 reply; 3+ messages in thread
From: David Vrabel @ 2015-11-27 17:17 UTC (permalink / raw)
To: xen-devel; +Cc: Boris Ostrovsky, David Vrabel
Add IOCTL_GNTDEV_GRANT_COPY to allow applications to copy between user
space buffers and grant references.
This interface is similar to the GNTTABOP_copy hypercall ABI except
the local buffers are provided using a virtual address (instead of a
GFN and offset). To avoid userspace from having to page align its
buffers the driver will use two or more ops if required.
If the ioctl returns 0, the application must check the status of each
segment with the segments status field. If the ioctl returns a -ve
error code (EINVAL or EFAULT), the status of individual ops is
undefined.
Signed-off-by: David Vrabel <david.vrabel@citrix.com>
---
v3:
- Rewrite with different API that matches the capabilities of the
hypervisor ABI and eliminates some of the size/alignment
restrictions.
---
drivers/xen/gntdev.c | 193 ++++++++++++++++++++++++++++++++++++++++++++++
include/uapi/xen/gntdev.h | 47 +++++++++++
2 files changed, 240 insertions(+)
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index 2ea0b3b..5d78c79 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -748,6 +748,196 @@ static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u)
return rc;
}
+#define GNTDEV_COPY_BATCH 16
+
+struct gntdev_copy_batch {
+ struct gnttab_copy ops[GNTDEV_COPY_BATCH];
+ struct page *pages[2 * GNTDEV_COPY_BATCH];
+ s16 __user *status[GNTDEV_COPY_BATCH];
+ unsigned int nr_ops;
+ unsigned int nr_pages;
+};
+
+static int gntdev_get_page(struct gntdev_copy_batch *batch, void __user *virt,
+ unsigned long *gfn)
+{
+ unsigned long addr = (unsigned long)virt;
+ struct page *page;
+ unsigned long xen_pfn;
+ int ret;
+
+ ret = get_user_pages_fast(addr, 1, 1, &page);
+ if (ret < 0)
+ return ret;
+
+ batch->pages[batch->nr_pages++] = page;
+
+ xen_pfn = page_to_xen_pfn(page) + XEN_PFN_DOWN(addr & ~PAGE_MASK);
+ *gfn = pfn_to_gfn(xen_pfn);
+
+ return 0;
+}
+
+static void gntdev_put_pages(struct gntdev_copy_batch *batch)
+{
+ unsigned int i;
+
+ for (i = 0; i < batch->nr_pages; i++)
+ put_page(batch->pages[i]);
+ batch->nr_pages = 0;
+}
+
+static int gntdev_copy(struct gntdev_copy_batch *batch)
+{
+ unsigned int i;
+
+ gnttab_batch_copy(batch->ops, batch->nr_ops);
+ gntdev_put_pages(batch);
+
+ /*
+ * For each completed op, update the status if the op failed
+ * and a previous failure for the segment hasn't been
+ * recorded.
+ */
+ for (i = 0; i < batch->nr_ops; i++) {
+ s16 status = batch->ops[i].status;
+ s16 old_status;
+
+ if (status == GNTST_okay)
+ continue;
+
+ if (__get_user(old_status, batch->status[i]))
+ return -EFAULT;
+
+ if (old_status != GNTST_okay)
+ continue;
+
+ if (__put_user(status, batch->status[i]))
+ return -EFAULT;
+ }
+
+ batch->nr_ops = 0;
+ return 0;
+}
+
+static int gntdev_grant_copy_seg(struct gntdev_copy_batch *batch,
+ struct gntdev_grant_copy_segment *seg,
+ s16 __user *status)
+{
+ uint16_t copied = 0;
+
+ /* Can't cross page if source/dest is a grant ref. */
+ if (seg->flags & GNTCOPY_source_gref) {
+ if (seg->source.foreign.offset + seg->len > XEN_PAGE_SIZE)
+ return -EINVAL;
+ }
+ if (seg->flags & GNTCOPY_dest_gref) {
+ if (seg->dest.foreign.offset + seg->len > XEN_PAGE_SIZE)
+ return -EINVAL;
+ }
+
+ if (put_user(GNTST_okay, status))
+ return -EFAULT;
+
+ while (copied < seg->len) {
+ struct gnttab_copy *op;
+ void __user *virt;
+ size_t len, off;
+ unsigned long gfn;
+ int ret;
+
+ if (batch->nr_ops >= GNTDEV_COPY_BATCH) {
+ ret = gntdev_copy(batch);
+ if (ret < 0)
+ return ret;
+ }
+
+ len = seg->len - copied;
+
+ op = &batch->ops[batch->nr_ops];
+ op->flags = 0;
+
+ if (seg->flags & GNTCOPY_source_gref) {
+ op->source.u.ref = seg->source.foreign.ref;
+ op->source.domid = seg->source.foreign.domid;
+ op->source.offset = seg->source.foreign.offset + copied;
+ op->flags |= GNTCOPY_source_gref;
+ } else {
+ virt = seg->source.virt + copied;
+ off = (unsigned long)virt & ~XEN_PAGE_MASK;
+ len = min(len, XEN_PAGE_SIZE - off);
+
+ ret = gntdev_get_page(batch, virt, &gfn);
+ if (ret < 0)
+ return ret;
+
+ op->source.u.gmfn = gfn;
+ op->source.domid = DOMID_SELF;
+ op->source.offset = off;
+ }
+
+ if (seg->flags & GNTCOPY_dest_gref) {
+ op->dest.u.ref = seg->dest.foreign.ref;
+ op->dest.domid = seg->dest.foreign.domid;
+ op->dest.offset = seg->dest.foreign.offset + copied;
+ op->flags |= GNTCOPY_dest_gref;
+ } else {
+ virt = seg->dest.virt + copied;
+ off = (unsigned long)virt & ~XEN_PAGE_MASK;
+ len = min(len, XEN_PAGE_SIZE - off);
+
+ ret = gntdev_get_page(batch, virt, &gfn);
+ if (ret < 0)
+ return ret;
+
+ op->dest.u.gmfn = gfn;
+ op->dest.domid = DOMID_SELF;
+ op->dest.offset = off;
+ }
+
+ op->len = len;
+ copied += len;
+
+ batch->status[batch->nr_ops] = status;
+ batch->nr_ops++;
+ }
+
+ return 0;
+}
+
+static long gntdev_ioctl_grant_copy(struct gntdev_priv *priv, void __user *u)
+{
+ struct ioctl_gntdev_grant_copy copy;
+ struct gntdev_copy_batch batch = { .nr_ops = 0, .nr_pages = 0, };
+ unsigned int i;
+ int ret = 0;
+
+ if (copy_from_user(©, u, sizeof(copy)))
+ return -EFAULT;
+
+ for (i = 0; i < copy.count; i++) {
+ struct gntdev_grant_copy_segment seg;
+
+ if (copy_from_user(&seg, ©.segments[i], sizeof(seg))) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ ret = gntdev_grant_copy_seg(&batch, &seg, ©.segments[i].status);
+ if (ret < 0)
+ goto out;
+
+ cond_resched();
+ }
+ if (batch.nr_ops)
+ ret = gntdev_copy(&batch);
+ return ret;
+
+ out:
+ gntdev_put_pages(&batch);
+ return ret;
+}
+
static long gntdev_ioctl(struct file *flip,
unsigned int cmd, unsigned long arg)
{
@@ -767,6 +957,9 @@ static long gntdev_ioctl(struct file *flip,
case IOCTL_GNTDEV_SET_UNMAP_NOTIFY:
return gntdev_ioctl_notify(priv, ptr);
+ case IOCTL_GNTDEV_GRANT_COPY:
+ return gntdev_ioctl_grant_copy(priv, ptr);
+
default:
pr_debug("priv %p, unknown cmd %x\n", priv, cmd);
return -ENOIOCTLCMD;
diff --git a/include/uapi/xen/gntdev.h b/include/uapi/xen/gntdev.h
index aa7610a..4b02343 100644
--- a/include/uapi/xen/gntdev.h
+++ b/include/uapi/xen/gntdev.h
@@ -144,6 +144,53 @@ struct ioctl_gntdev_unmap_notify {
__u32 event_channel_port;
};
+struct gntdev_grant_copy_segment {
+ union {
+ void __user *virt;
+ struct {
+ grant_ref_t ref;
+ __u16 offset;
+ domid_t domid;
+ } foreign;
+ } source, dest;
+ __u16 len;
+
+ __u16 flags; /* GNTCOPY_* */
+ __s16 status; /* GNTST_* */
+};
+
+/*
+ * Copy between grant references and local buffers.
+ *
+ * The copy is split into @count @segments, each of which can copy
+ * to/from one grant reference.
+ *
+ * Each segment is similar to struct gnttab_copy in the hypervisor ABI
+ * except the local buffer is specified using a virtual address
+ * (instead of a GFN and offset).
+ *
+ * The local buffer may cross a Xen page boundary -- the driver will
+ * split segments into multiple ops if required.
+ *
+ * Returns 0 if all segments have been processed and @status in each
+ * segment is valid. Note that one or more segments may have failed
+ * (status != GNTST_okay).
+ *
+ * If the driver had to split a segment into two or more ops, @status
+ * includes the status of the first failed op for that segment (or
+ * GNTST_okay if all ops were successful).
+ *
+ * If -1 is returned, the status of all segments is undefined.
+ *
+ * - EINVAL: a segment crosses the boundary of a foreign page.
+ */
+#define IOCTL_GNTDEV_GRANT_COPY \
+ _IOC(_IOC_NONE, 'G', 8, sizeof(struct ioctl_gntdev_grant_copy))
+struct ioctl_gntdev_grant_copy {
+ unsigned int count;
+ struct gntdev_grant_copy_segment __user *segments;
+};
+
/* Clear (set to zero) the byte specified by index */
#define UNMAP_NOTIFY_CLEAR_BYTE 0x1
/* Send an interrupt on the indicated event channel */
--
2.1.4
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCHv3] xen/gntdev: add ioctl for grant copy
2015-11-27 17:17 [PATCHv3] xen/gntdev: add ioctl for grant copy David Vrabel
@ 2015-11-30 21:39 ` Konrad Rzeszutek Wilk
2015-12-01 10:35 ` David Vrabel
0 siblings, 1 reply; 3+ messages in thread
From: Konrad Rzeszutek Wilk @ 2015-11-30 21:39 UTC (permalink / raw)
To: David Vrabel; +Cc: xen-devel, Boris Ostrovsky
On Fri, Nov 27, 2015 at 05:17:05PM +0000, David Vrabel wrote:
> Add IOCTL_GNTDEV_GRANT_COPY to allow applications to copy between user
> space buffers and grant references.
>
> This interface is similar to the GNTTABOP_copy hypercall ABI except
> the local buffers are provided using a virtual address (instead of a
> GFN and offset). To avoid userspace from having to page align its
> buffers the driver will use two or more ops if required.
>
> If the ioctl returns 0, the application must check the status of each
> segment with the segments status field. If the ioctl returns a -ve
> error code (EINVAL or EFAULT), the status of individual ops is
> undefined.
Are there any test tools that could be used for this? To make sure that
regression wise this does not get broken?
>
> Signed-off-by: David Vrabel <david.vrabel@citrix.com>
> ---
> v3:
> - Rewrite with different API that matches the capabilities of the
> hypervisor ABI and eliminates some of the size/alignment
> restrictions.
> ---
> drivers/xen/gntdev.c | 193 ++++++++++++++++++++++++++++++++++++++++++++++
> include/uapi/xen/gntdev.h | 47 +++++++++++
> 2 files changed, 240 insertions(+)
>
> diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
> index 2ea0b3b..5d78c79 100644
> --- a/drivers/xen/gntdev.c
> +++ b/drivers/xen/gntdev.c
> @@ -748,6 +748,196 @@ static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u)
> return rc;
> }
>
> +#define GNTDEV_COPY_BATCH 16
> +
> +struct gntdev_copy_batch {
> + struct gnttab_copy ops[GNTDEV_COPY_BATCH];
> + struct page *pages[2 * GNTDEV_COPY_BATCH];
> + s16 __user *status[GNTDEV_COPY_BATCH];
> + unsigned int nr_ops;
> + unsigned int nr_pages;
> +};
> +
> +static int gntdev_get_page(struct gntdev_copy_batch *batch, void __user *virt,
> + unsigned long *gfn)
> +{
> + unsigned long addr = (unsigned long)virt;
> + struct page *page;
> + unsigned long xen_pfn;
> + int ret;
> +
> + ret = get_user_pages_fast(addr, 1, 1, &page);
Could the '1,1' have a comment please?
> + if (ret < 0)
> + return ret;
> +
> + batch->pages[batch->nr_pages++] = page;
> +
> + xen_pfn = page_to_xen_pfn(page) + XEN_PFN_DOWN(addr & ~PAGE_MASK);
> + *gfn = pfn_to_gfn(xen_pfn);
> +
> + return 0;
> +}
> +
> +static void gntdev_put_pages(struct gntdev_copy_batch *batch)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < batch->nr_pages; i++)
> + put_page(batch->pages[i]);
> + batch->nr_pages = 0;
> +}
> +
> +static int gntdev_copy(struct gntdev_copy_batch *batch)
> +{
> + unsigned int i;
> +
> + gnttab_batch_copy(batch->ops, batch->nr_ops);
> + gntdev_put_pages(batch);
> +
> + /*
> + * For each completed op, update the status if the op failed
> + * and a previous failure for the segment hasn't been
> + * recorded.
How could an previous failure not be recorded? Could you mention that
in this nice comment please?
> + */
> + for (i = 0; i < batch->nr_ops; i++) {
> + s16 status = batch->ops[i].status;
> + s16 old_status;
> +
> + if (status == GNTST_okay)
> + continue;
> +
> + if (__get_user(old_status, batch->status[i]))
> + return -EFAULT;
> +
> + if (old_status != GNTST_okay)
> + continue;
> +
> + if (__put_user(status, batch->status[i]))
> + return -EFAULT;
> + }
> +
> + batch->nr_ops = 0;
> + return 0;
> +}
> +
> +static int gntdev_grant_copy_seg(struct gntdev_copy_batch *batch,
> + struct gntdev_grant_copy_segment *seg,
> + s16 __user *status)
> +{
> + uint16_t copied = 0;
> +
> + /* Can't cross page if source/dest is a grant ref. */
> + if (seg->flags & GNTCOPY_source_gref) {
> + if (seg->source.foreign.offset + seg->len > XEN_PAGE_SIZE)
> + return -EINVAL;
> + }
> + if (seg->flags & GNTCOPY_dest_gref) {
> + if (seg->dest.foreign.offset + seg->len > XEN_PAGE_SIZE)
> + return -EINVAL;
> + }
> +
> + if (put_user(GNTST_okay, status))
> + return -EFAULT;
> +
> + while (copied < seg->len) {
> + struct gnttab_copy *op;
> + void __user *virt;
> + size_t len, off;
> + unsigned long gfn;
> + int ret;
> +
> + if (batch->nr_ops >= GNTDEV_COPY_BATCH) {
> + ret = gntdev_copy(batch);
> + if (ret < 0)
> + return ret;
> + }
> +
> + len = seg->len - copied;
> +
> + op = &batch->ops[batch->nr_ops];
> + op->flags = 0;
> +
> + if (seg->flags & GNTCOPY_source_gref) {
> + op->source.u.ref = seg->source.foreign.ref;
> + op->source.domid = seg->source.foreign.domid;
> + op->source.offset = seg->source.foreign.offset + copied;
> + op->flags |= GNTCOPY_source_gref;
> + } else {
> + virt = seg->source.virt + copied;
> + off = (unsigned long)virt & ~XEN_PAGE_MASK;
> + len = min(len, XEN_PAGE_SIZE - off);
> +
> + ret = gntdev_get_page(batch, virt, &gfn);
> + if (ret < 0)
> + return ret;
> +
> + op->source.u.gmfn = gfn;
> + op->source.domid = DOMID_SELF;
> + op->source.offset = off;
> + }
> +
> + if (seg->flags & GNTCOPY_dest_gref) {
> + op->dest.u.ref = seg->dest.foreign.ref;
> + op->dest.domid = seg->dest.foreign.domid;
> + op->dest.offset = seg->dest.foreign.offset + copied;
> + op->flags |= GNTCOPY_dest_gref;
> + } else {
> + virt = seg->dest.virt + copied;
> + off = (unsigned long)virt & ~XEN_PAGE_MASK;
> + len = min(len, XEN_PAGE_SIZE - off);
> +
> + ret = gntdev_get_page(batch, virt, &gfn);
> + if (ret < 0)
> + return ret;
> +
> + op->dest.u.gmfn = gfn;
> + op->dest.domid = DOMID_SELF;
> + op->dest.offset = off;
> + }
> +
> + op->len = len;
> + copied += len;
> +
> + batch->status[batch->nr_ops] = status;
> + batch->nr_ops++;
> + }
> +
> + return 0;
> +}
> +
> +static long gntdev_ioctl_grant_copy(struct gntdev_priv *priv, void __user *u)
> +{
> + struct ioctl_gntdev_grant_copy copy;
> + struct gntdev_copy_batch batch = { .nr_ops = 0, .nr_pages = 0, };
> + unsigned int i;
> + int ret = 0;
> +
> + if (copy_from_user(©, u, sizeof(copy)))
> + return -EFAULT;
>
+
No check on the .nr_pages' ? What if it is 0xfffffffffffffffffffffffff?
Ditto for .nr_ops?
> + for (i = 0; i < copy.count; i++) {
> + struct gntdev_grant_copy_segment seg;
> +
> + if (copy_from_user(&seg, ©.segments[i], sizeof(seg))) {
> + ret = -EFAULT;
> + goto out;
> + }
> +
> + ret = gntdev_grant_copy_seg(&batch, &seg, ©.segments[i].status);
> + if (ret < 0)
> + goto out;
> +
> + cond_resched();
> + }
> + if (batch.nr_ops)
> + ret = gntdev_copy(&batch);
> + return ret;
> +
> + out:
> + gntdev_put_pages(&batch);
> + return ret;
> +}
> +
> static long gntdev_ioctl(struct file *flip,
> unsigned int cmd, unsigned long arg)
> {
> @@ -767,6 +957,9 @@ static long gntdev_ioctl(struct file *flip,
> case IOCTL_GNTDEV_SET_UNMAP_NOTIFY:
> return gntdev_ioctl_notify(priv, ptr);
>
> + case IOCTL_GNTDEV_GRANT_COPY:
> + return gntdev_ioctl_grant_copy(priv, ptr);
> +
> default:
> pr_debug("priv %p, unknown cmd %x\n", priv, cmd);
> return -ENOIOCTLCMD;
> diff --git a/include/uapi/xen/gntdev.h b/include/uapi/xen/gntdev.h
> index aa7610a..4b02343 100644
> --- a/include/uapi/xen/gntdev.h
> +++ b/include/uapi/xen/gntdev.h
> @@ -144,6 +144,53 @@ struct ioctl_gntdev_unmap_notify {
> __u32 event_channel_port;
> };
>
> +struct gntdev_grant_copy_segment {
> + union {
> + void __user *virt;
> + struct {
> + grant_ref_t ref;
> + __u16 offset;
> + domid_t domid;
> + } foreign;
> + } source, dest;
> + __u16 len;
> +
> + __u16 flags; /* GNTCOPY_* */
> + __s16 status; /* GNTST_* */
> +};
> +
> +/*
> + * Copy between grant references and local buffers.
> + *
> + * The copy is split into @count @segments, each of which can copy
> + * to/from one grant reference.
> + *
> + * Each segment is similar to struct gnttab_copy in the hypervisor ABI
> + * except the local buffer is specified using a virtual address
> + * (instead of a GFN and offset).
> + *
> + * The local buffer may cross a Xen page boundary -- the driver will
> + * split segments into multiple ops if required.
> + *
> + * Returns 0 if all segments have been processed and @status in each
> + * segment is valid. Note that one or more segments may have failed
> + * (status != GNTST_okay).
> + *
> + * If the driver had to split a segment into two or more ops, @status
> + * includes the status of the first failed op for that segment (or
> + * GNTST_okay if all ops were successful).
> + *
> + * If -1 is returned, the status of all segments is undefined.
> + *
> + * - EINVAL: a segment crosses the boundary of a foreign page.
> + */
> +#define IOCTL_GNTDEV_GRANT_COPY \
> + _IOC(_IOC_NONE, 'G', 8, sizeof(struct ioctl_gntdev_grant_copy))
> +struct ioctl_gntdev_grant_copy {
> + unsigned int count;
> + struct gntdev_grant_copy_segment __user *segments;
> +};
> +
> /* Clear (set to zero) the byte specified by index */
> #define UNMAP_NOTIFY_CLEAR_BYTE 0x1
> /* Send an interrupt on the indicated event channel */
> --
> 2.1.4
>
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCHv3] xen/gntdev: add ioctl for grant copy
2015-11-30 21:39 ` Konrad Rzeszutek Wilk
@ 2015-12-01 10:35 ` David Vrabel
0 siblings, 0 replies; 3+ messages in thread
From: David Vrabel @ 2015-12-01 10:35 UTC (permalink / raw)
To: Konrad Rzeszutek Wilk, David Vrabel; +Cc: xen-devel, Boris Ostrovsky
[-- Attachment #1: Type: text/plain, Size: 2679 bytes --]
On 30/11/15 21:39, Konrad Rzeszutek Wilk wrote:
> On Fri, Nov 27, 2015 at 05:17:05PM +0000, David Vrabel wrote:
>> Add IOCTL_GNTDEV_GRANT_COPY to allow applications to copy between user
>> space buffers and grant references.
>>
>> This interface is similar to the GNTTABOP_copy hypercall ABI except
>> the local buffers are provided using a virtual address (instead of a
>> GFN and offset). To avoid userspace from having to page align its
>> buffers the driver will use two or more ops if required.
>>
>> If the ioctl returns 0, the application must check the status of each
>> segment with the segments status field. If the ioctl returns a -ve
>> error code (EINVAL or EFAULT), the status of individual ops is
>> undefined.
>
> Are there any test tools that could be used for this? To make sure that
> regression wise this does not get broken?
See attached.
>> +static int gntdev_copy(struct gntdev_copy_batch *batch)
>> +{
>> + unsigned int i;
>> +
>> + gnttab_batch_copy(batch->ops, batch->nr_ops);
>> + gntdev_put_pages(batch);
>> +
>> + /*
>> + * For each completed op, update the status if the op failed
>> + * and a previous failure for the segment hasn't been
>> + * recorded.
>
> How could an previous failure not be recorded? Could you mention that
> in this nice comment please?
All the negatives in this sentence are confusing so I'll reword.
If we haven't recorded a failure for the previous op in the segment it's
because it succeeded. The aim here is to record the first op failure
for a segment. From the ioctl documentation:
+ * If the driver had to split a segment into two or more ops, @status
+ * includes the status of the first failed op for that segment (or
+ * GNTST_okay if all ops were successful).
>> +static long gntdev_ioctl_grant_copy(struct gntdev_priv *priv, void __user *u)
>> +{
>> + struct ioctl_gntdev_grant_copy copy;
>> + struct gntdev_copy_batch batch = { .nr_ops = 0, .nr_pages = 0, };
>> + unsigned int i;
>> + int ret = 0;
>> +
>> + if (copy_from_user(©, u, sizeof(copy)))
>> + return -EFAULT;
>>
> +
>
> No check on the .nr_pages' ? What if it is 0xfffffffffffffffffffffffff?
>
> Ditto for .nr_ops?
batch.nr_ops and batch.nr_pages are internal, not supplied by the user
and are limited by the batch size.
I guess you're really asking about the value of copy.count? This
doesn't matter because we process the segments one by one and have a
fixed batch size for the ops.
There's also a cond_resched() every segment so submitting a single ioctl
with a crazy number of segments is really no different from userspace
calling the ioctl a crazy number of times.
David
p.s. Please trim your replies when reviewing.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: gntdev-copy.c --]
[-- Type: text/x-csrc; name="gntdev-copy.c", Size: 3567 bytes --]
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <xenctrl.h>
struct gntdev_grant_copy_segment {
union {
void *virt;
struct {
grant_ref_t ref;
uint16_t offset;
domid_t domid;
} foreign;
} source, dest;
uint16_t len;
uint16_t flags; /* GNTCOPY_* */
int16_t status; /* GNTST_* */
};
#define IOCTL_GNTDEV_GRANT_COPY \
_IOC(_IOC_NONE, 'G', 8, sizeof(struct ioctl_gntdev_grant_copy))
struct ioctl_gntdev_grant_copy {
unsigned int count;
struct gntdev_grant_copy_segment *segments;
};
#define NR_REFS 15
static void random_fill(char *buf, size_t len)
{
int fd;
int r = 0;
fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
perror("open(/dev/urandom)");
exit(1);
}
while (r < len) {
int ret;
ret = read(fd, buf + r, len - r);
if (ret < 0) {
perror("read");
exit(1);
}
r += ret;
}
close(fd);
}
static void do_copy(struct ioctl_gntdev_grant_copy *copy, char *a, char *b)
{
int fd;
unsigned int i;
int ret;
random_fill(a, NR_REFS * 4096);
fd = open("/dev/xen/gntdev", O_RDWR);
if (fd < 0) {
perror("open(/dev/xen/gntdev)");
exit(1);
}
ret = ioctl(fd, IOCTL_GNTDEV_GRANT_COPY, copy);
if (ret < 0) {
perror("ioctl(IOCTL_GNTDEV_GRANT_COPY)");
exit(1);
}
close(fd);
for (i = 0; i < NR_REFS; i++) {
if (copy->segments[i].status != GNTST_okay) {
fprintf(stderr, "[%u]: bad copy status: %d\n", i, copy->segments[i].status);
exit(1);
}
}
if (memcmp(a, b, NR_REFS * 4096) != 0) {
fprintf(stderr, "a != b\n");
exit(1);
}
}
int main(void)
{
xc_gntshr *gs;
xc_gnttab *gt;
uint32_t refs[NR_REFS];
struct gntdev_grant_copy_segment seg[NR_REFS];
struct ioctl_gntdev_grant_copy copy;
char *shared;
char local[4096 * NR_REFS];
unsigned int i;
gs = xc_gntshr_open(NULL, 0);
if (!gs) {
perror("xc_gntshr_open");
exit(1);
}
gt = xc_gnttab_open(NULL, 0);
if (!gt) {
perror("xc_gnttab_open");
exit(1);
}
shared = xc_gntshr_share_pages(gs, 0, NR_REFS, refs, 1);
if (shared == NULL) {
perror("xc_gntshr_share_pages");
exit(1);
}
/*
* 1. ref -> local
*/
printf("ref -> local\n");
for (i = 0; i < NR_REFS; i++) {
seg[i].source.foreign.ref = refs[i];
seg[i].source.foreign.offset = 0;
seg[i].source.foreign.domid = 0;
seg[i].dest.virt = local + i * 4096;
seg[i].len = 4096;
seg[i].flags = GNTCOPY_source_gref;
}
copy.count = NR_REFS;
copy.segments = seg;
do_copy(©, shared, local);
printf(" ok\n");
/*
* 2. local -> ref
*/
printf("local -> ref\n");
for (i = 0; i < NR_REFS; i++) {
seg[i].source.virt = local + i * 4096;
seg[i].dest.foreign.ref = refs[i];
seg[i].dest.foreign.offset = 0;
seg[i].dest.foreign.domid = 0;
seg[i].len = 4096;
seg[i].flags = GNTCOPY_dest_gref;
}
copy.count = NR_REFS;
copy.segments = seg;
do_copy(©, local, shared);
printf(" ok\n");
return 0;
}
[-- Attachment #3: Type: text/plain, Size: 126 bytes --]
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
http://lists.xen.org/xen-devel
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2015-12-01 10:35 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-11-27 17:17 [PATCHv3] xen/gntdev: add ioctl for grant copy David Vrabel
2015-11-30 21:39 ` Konrad Rzeszutek Wilk
2015-12-01 10:35 ` David Vrabel
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.