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

[PATCH Linux v4 16/16] xen/privcmd: Add new ABI to allow copying foreign memory



From: Frediano Ziglio <frediano.ziglio@xxxxxxxxxx>

This new ABI allows to copy foreign domain memory to/from a buffer.
This avoids having to map/copy/unmap foreign memory which is
expensive.
This operation is done particularly when migrating VMs.

Signed-off-by: Frediano Ziglio <frediano.ziglio@xxxxxxxxxx>
---
 arch/x86/include/asm/xen/interface.h |  1 +
 drivers/xen/privcmd.c                | 51 ++++++++++++++++++++++++++++
 include/uapi/xen/privcmd.h           | 10 ++++++
 include/xen/interface/memory.h       | 37 ++++++++++++++++++++
 4 files changed, 99 insertions(+)

diff --git a/arch/x86/include/asm/xen/interface.h 
b/arch/x86/include/asm/xen/interface.h
index a078a2b0f032..bac3c3bc60fd 100644
--- a/arch/x86/include/asm/xen/interface.h
+++ b/arch/x86/include/asm/xen/interface.h
@@ -91,6 +91,7 @@ DEFINE_GUEST_HANDLE(int);
 DEFINE_GUEST_HANDLE(void);
 DEFINE_GUEST_HANDLE(uint64_t);
 DEFINE_GUEST_HANDLE(uint32_t);
+DEFINE_GUEST_HANDLE(uint8_t);
 DEFINE_GUEST_HANDLE(xen_pfn_t);
 DEFINE_GUEST_HANDLE(xen_ulong_t);
 #endif
diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c
index 725a49a0eee7..4ae9138dd314 100644
--- a/drivers/xen/privcmd.c
+++ b/drivers/xen/privcmd.c
@@ -62,6 +62,10 @@ MODULE_LICENSE("GPL");
 
 #define PRIV_VMA_LOCKED ((void *)1)
 
+#ifndef UINT32_MAX
+#define UINT32_MAX ((uint32_t)~0U)
+#endif
+
 static unsigned int privcmd_dm_op_max_num = 16;
 module_param_named(dm_op_max_nr_bufs, privcmd_dm_op_max_num, uint, 0644);
 MODULE_PARM_DESC(dm_op_max_nr_bufs,
@@ -1522,6 +1526,49 @@ static inline void privcmd_ioeventfd_exit(void)
 }
 #endif /* CONFIG_XEN_PRIVCMD_EVENTFD */
 
+static long privcmd_ioctl_foreigncopy(
+       struct file *file, void __user *udata)
+{
+       const struct privcmd_data *const data = file->private_data;
+       long ret;
+       struct privcmd_foreigncopy copy;
+       struct xen_foreigncopy xcopy;
+
+       if (copy_from_user(&copy, udata, sizeof(copy)))
+               return -EFAULT;
+       if (copy.dir & ~1u)
+               return -EINVAL;
+       if (copy.num >= UINT32_MAX >> PAGE_SHIFT)
+               return -EINVAL;
+       if (!access_ok(copy.pfns, copy.num * sizeof(*copy.pfns)))
+               return -EFAULT;
+       if (!access_ok(copy.buffer, copy.num << PAGE_SHIFT))
+               return -EFAULT;
+
+       /* If restriction is in place, check the domid matches */
+       if (data->domid != DOMID_INVALID && data->domid != copy.dom)
+               return -EPERM;
+
+       xcopy.domid = copy.dom;
+       xcopy.flags = copy.dir;
+       xcopy.nr_frames = copy.num;
+       xcopy.frame_list = (void *) copy.pfns;
+       xcopy.buffer = copy.buffer;
+
+       ret = HYPERVISOR_memory_op(XENMEM_foreigncopy, &xcopy);
+
+       /* copy values back in case of error */
+       if (ret) {
+               copy.num = xcopy.nr_frames = copy.num;
+               copy.pfns = xcopy.frame_list;
+               copy.buffer = xcopy.buffer;
+               if (copy_to_user(udata, &copy, sizeof(copy)))
+                       ret = -EFAULT;
+       }
+
+       return ret;
+}
+
 static long privcmd_ioctl(struct file *file,
                          unsigned int cmd, unsigned long data)
 {
@@ -1569,6 +1616,10 @@ static long privcmd_ioctl(struct file *file,
                ret = privcmd_ioctl_pcidev_get_gsi(file, udata);
                break;
 
+       case IOCTL_PRIVCMD_FOREIGNCOPY:
+               ret = privcmd_ioctl_foreigncopy(file, udata);
+               break;
+
        default:
                break;
        }
diff --git a/include/uapi/xen/privcmd.h b/include/uapi/xen/privcmd.h
index 8e2c8fd44764..786d769ad4f8 100644
--- a/include/uapi/xen/privcmd.h
+++ b/include/uapi/xen/privcmd.h
@@ -131,6 +131,14 @@ struct privcmd_pcidev_get_gsi {
        __u32 gsi;
 };
 
+struct privcmd_foreigncopy {
+       domid_t dom;            /* foreign domain */
+       __u16 dir;              /* direction,  0 from, 1 to */
+       __u32 num;              /* number of pages to copy */
+       const xen_pfn_t __user *pfns;   /* array of pfns */
+       void __user *buffer;    /* buffer to copy to/from */
+};
+
 /*
  * @cmd: IOCTL_PRIVCMD_HYPERCALL
  * @arg: &privcmd_hypercall_t
@@ -164,5 +172,7 @@ struct privcmd_pcidev_get_gsi {
        _IOW('P', 9, struct privcmd_ioeventfd)
 #define IOCTL_PRIVCMD_PCIDEV_GET_GSI                           \
        _IOC(_IOC_NONE, 'P', 10, sizeof(struct privcmd_pcidev_get_gsi))
+#define IOCTL_PRIVCMD_FOREIGNCOPY                              \
+       _IOC(_IOC_NONE, 'P', 11, sizeof(struct privcmd_foreigncopy))
 
 #endif /* __LINUX_PUBLIC_PRIVCMD_H__ */
diff --git a/include/xen/interface/memory.h b/include/xen/interface/memory.h
index 1a371a825c55..5981402fccde 100644
--- a/include/xen/interface/memory.h
+++ b/include/xen/interface/memory.h
@@ -325,4 +325,41 @@ struct xen_mem_acquire_resource {
 };
 DEFINE_GUEST_HANDLE_STRUCT(xen_mem_acquire_resource);
 
+/*
+ * Copy memory from/to a given domain.
+ */
+#define XENMEM_foreigncopy 29
+struct xen_foreigncopy {
+    /* IN - The domain whose resource is to be copied */
+    domid_t domid;
+
+    /* IN - Flags */
+#define XENMEM_foreigncopy_from 0
+#define XENMEM_foreigncopy_to 1
+#define XENMEM_foreigncopy_direction 1
+    uint16_t flags;
+
+    /*
+     * IN
+     *
+     * As an IN parameter number of frames of the domain to be copied.
+     */
+    uint32_t nr_frames;
+
+    /*
+     * IN
+     *
+     * Frames to be copied.
+     */
+    GUEST_HANDLE(xen_pfn_t) frame_list;
+
+    /*
+     * IN/OUT
+     *
+     * Userspace buffer to read/write from.
+     */
+    GUEST_HANDLE(uint8_t) buffer;
+};
+DEFINE_GUEST_HANDLE_STRUCT(xen_foreigncopy);
+
 #endif /* __XEN_PUBLIC_MEMORY_H__ */
-- 
2.54.0




 


Rackspace

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