[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v2] introduce grant copy for user land



This patch introduces the interface to allow user-space applications
execute grant-copy operations. This is done by sending an ioctl to the
grant device.

Signed-off-by: Thanos Makatos <thanos.makatos@xxxxxxxxxx>
---
 drivers/xen/gntdev.c      |  171 +++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/xen/gntdev.h |   69 ++++++++++++++++++
 2 files changed, 240 insertions(+)

diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index 51f4c95..7b4a8e0 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -705,6 +705,174 @@ static long gntdev_ioctl_notify(struct gntdev_priv *priv, 
void __user *u)
        return rc;
 }
 
+static int gntdev_gcopy_batch(int nr_segments, unsigned long gcopy_cb,
+               struct gntdev_grant_copy_segment __user *__segments, int dir,
+               int src, int dst) {
+
+       static const int batch_size = PAGE_SIZE / (sizeof(struct page*) +
+               sizeof(struct gnttab_copy) + sizeof(struct 
gntdev_grant_copy_segment));
+       struct page **pages = (struct page **)gcopy_cb;
+       struct gnttab_copy *batch = (struct gnttab_copy *)((unsigned long)pages 
+
+                       sizeof(struct page*) * batch_size);
+       struct gntdev_grant_copy_segment *segments =
+               (struct gntdev_grant_copy_segment *)((unsigned long)batch +
+                               sizeof(struct gnttab_copy) * batch_size);
+       unsigned int nr_pinned = 0, nr_segs2cp = 0;
+       int err = 0, i;
+       const int write = dir == GNTCOPY_IOCTL_g2s;
+
+       nr_segments = min(nr_segments, batch_size);
+
+       if (unlikely(copy_from_user(segments, __segments,
+                          sizeof(struct gntdev_grant_copy_segment) * 
nr_segments))) {
+               pr_debug("failed to copy %d segments from user", nr_segments);
+               err = -EFAULT;
+               goto out;
+       }
+
+       for (i = 0; i < nr_segments; i++) {
+
+               xen_pfn_t pgaddr;
+               unsigned long start, offset;
+               struct gntdev_grant_copy_segment *seg = &segments[i];
+
+               if (dir == GNTCOPY_IOCTL_s2g || dir == GNTCOPY_IOCTL_g2s) {
+
+                       start = (unsigned long)seg->self.iov.iov_base & 
PAGE_MASK;
+                       offset = (unsigned long)seg->self.iov.iov_base & 
~PAGE_MASK;
+                       if (unlikely(offset + seg->self.iov.iov_len > 
PAGE_SIZE)) {
+                               pr_warn("segments crossing page boundaries not 
yet "
+                                               "implemented\n");
+                               err = -ENOSYS;
+                               goto out;
+                       }
+
+                       err = get_user_pages_fast(start, 1, write, &pages[i]);
+                       if (unlikely(err != 1)) {
+                               pr_debug("failed to get user page %lu", start);
+                               err = -EFAULT;
+                               goto out;
+                       }
+
+                       nr_pinned++;
+
+                       pgaddr = pfn_to_mfn(page_to_pfn(pages[i]));
+               }
+
+               nr_segs2cp++;
+
+               switch (dir) {
+               case GNTCOPY_IOCTL_g2s: /* copy from guest */
+                       batch[i].len = seg->self.iov.iov_len;
+                       batch[i].source.u.ref = seg->self.ref;
+                       batch[i].source.domid = src;
+                       batch[i].source.offset = seg->self.offset;
+                       batch[i].dest.u.gmfn = pgaddr;
+                       batch[i].dest.domid = DOMID_SELF;
+                       batch[i].dest.offset = offset;
+                       batch[i].flags = GNTCOPY_source_gref;
+                       break;
+               case GNTCOPY_IOCTL_s2g: /* copy to guest */
+                       batch[i].len = seg->self.iov.iov_len;
+                       batch[i].source.u.gmfn = pgaddr;
+                       batch[i].source.domid = DOMID_SELF;
+                       batch[i].source.offset = offset;
+                       batch[i].dest.u.ref = seg->self.ref;
+                       batch[i].dest.domid = dst;
+                       batch[i].dest.offset = seg->self.offset;
+                       batch[i].flags = GNTCOPY_dest_gref;
+                       break;
+               case GNTCOPY_IOCTL_g2g: /* copy guest to guest */
+                       batch[i].len = seg->g2g.len;
+                       batch[i].source.u.ref = seg->g2g.src.ref;
+                       batch[i].source.domid = src;
+                       batch[i].source.offset = seg->g2g.src.offset;
+                       batch[i].dest.u.ref = seg->g2g.dst.ref;
+                       batch[i].dest.domid = dst;
+                       batch[i].dest.offset = seg->g2g.dst.offset;
+                       batch[i].flags = GNTCOPY_source_gref | 
GNTCOPY_dest_gref;
+                       break;
+               default:
+                       pr_debug("invalid grant-copy direction %d\n", dir);
+                       err = -EINVAL;
+                       goto out;
+               }
+       }
+
+       gnttab_batch_copy(batch, nr_segs2cp);
+       for (i = 0; i < nr_segs2cp; i++) {
+               err = put_user(batch[i].status, &__segments[i].status);
+               if (unlikely(err)) {
+                       pr_debug("failed to copy error code %d to user: %d\n",
+                                       batch[i].status, err);
+                       goto out;
+               }
+       }
+
+out:
+       for (i = 0; i < nr_pinned; i++)
+               put_page(pages[i]);
+
+       if (unlikely(err))
+               return err;
+
+       return nr_segs2cp;
+}
+
+static long gntdev_ioctl_grant_copy(struct gntdev_priv *priv, void __user *u)
+{
+       struct ioctl_gntdev_grant_copy op;
+       unsigned int remaining;
+       int err = 0;
+       unsigned long gcopy_cb = 0;
+
+       if (unlikely(copy_from_user(&op, u, sizeof(op)))) {
+               pr_debug("failed to copy grant-copy parameters from user");
+               err = -EFAULT;
+               goto out;
+       }
+
+       if (unlikely(op.dir != GNTCOPY_IOCTL_s2g && op.dir != GNTCOPY_IOCTL_g2s 
&&
+                       op.dir != GNTCOPY_IOCTL_g2g)) {
+               pr_debug("invalid copy direction %d\n", op.dir);
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (!op.count) {
+               pr_debug("no segments to transfer");
+               err = 0;
+               goto out;
+       }
+
+       gcopy_cb = get_zeroed_page(GFP_KERNEL);
+       if (unlikely(!gcopy_cb)) {
+               pr_debug("failed to allocate page");
+               err = -ENOMEM;
+               goto out;
+       }
+
+       remaining = op.count;
+
+       while (remaining > 0) {
+               err = gntdev_gcopy_batch(remaining, gcopy_cb,
+                               op.segments + (op.count - remaining), op.dir, 
op.src, op.dst);
+               if (unlikely(err < 0)) {
+                       pr_debug("failed to grant-copy %d segments: %d",
+                                       op.count - remaining, err);
+                       goto out;
+               }
+               remaining -= err;
+       }
+
+       err = 0;
+
+out:
+       if (likely(gcopy_cb))
+               free_page(gcopy_cb);
+       return err;
+}
+
 static long gntdev_ioctl(struct file *flip,
                         unsigned int cmd, unsigned long arg)
 {
@@ -724,6 +892,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 5304bd3..17da5e9 100644
--- a/include/uapi/xen/gntdev.h
+++ b/include/uapi/xen/gntdev.h
@@ -33,6 +33,12 @@
 #ifndef __LINUX_PUBLIC_GNTDEV_H__
 #define __LINUX_PUBLIC_GNTDEV_H__
 
+#ifdef __KERNEL__
+#include <linux/uio.h>
+#else
+#include <sys/uio.h>
+#endif
+
 struct ioctl_gntdev_grant_ref {
        /* The domain ID of the grant to be mapped. */
        uint32_t domid;
@@ -142,6 +148,69 @@ struct ioctl_gntdev_unmap_notify {
        uint32_t event_channel_port;
 };
 
+struct gntdev_grant_copy_segment {
+
+       union {
+               /* copy from (to) self to (from) guest */
+               struct {
+                       /*
+                        * source address and length
+                        */
+                       struct iovec iov;
+
+                       /* the granted page */
+                       uint32_t ref;
+
+                       /* offset in the granted page */
+                       uint16_t offset;
+               } self;
+
+               /* copy from guest to guest */
+               struct {
+                       uint16_t len;
+
+                       struct {
+                               /* the granted page */
+                               uint32_t ref;
+
+                               /* offset in the granted page */
+                               uint16_t offset;
+                       } src, dst;
+               } g2g;
+       };
+
+       /* grant copy result (GNTST_XXX) */
+       int16_t status;
+};
+
+/* grant-copy from self to guest */
+#define GNTCOPY_IOCTL_s2g 0
+/* grant-copy from guest to self */
+#define GNTCOPY_IOCTL_g2s 1
+/* grant-copy from guest to guest */
+#define GNTCOPY_IOCTL_g2g 2
+
+#define IOCTL_GNTDEV_GRANT_COPY \
+_IOC(_IOC_NONE, 'G', 8, sizeof(struct ioctl_gntdev_grant_copy))
+struct ioctl_gntdev_grant_copy {
+       /*
+        * copy direction, see GNTCOPY_IOCTL_XXX
+        */
+       int dir;
+
+       /*
+        * domain IDs:
+        *      When the source (destination) domain is the one executing the
+        *      grant-copy operation, the @src (@dst) parameter is ignored.
+        */
+       uint32_t src;
+       uint32_t dst;
+
+       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 */
-- 
1.7.9.5


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.