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

[Xen-devel] [PATCH 1/3] [RFC] User-space grant table device - main driver



A character device for accessing (in user-space) pages that have been granted by other domains.

Signed-off-by: Derek Murray <Derek.Murray@xxxxxxxxxxxx>
---
diff -r 8fa17d1560a9 linux-2.6-xen-sparse/drivers/xen/gntdev/gntdev.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/gntdev/gntdev.c Thu Mar 15 17:59:22 2007 +0000
@@ -0,0 +1,739 @@
+/ ************************************************************************ ******
+ * gntdev.c
+ *
+ * Device for accessing (in user-space) pages that have been granted by other
+ * domains.
+ *
+ * Copyright (c) 2006-2007, D G Murray.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <asm/atomic.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <xen/gnttab.h>
+#include <asm/hypervisor.h>
+#include <xen/balloon.h>
+#include <xen/evtchn.h>
+
+#include <linux/types.h>
+#include <xen/public/gntdev.h>
+
+
+#define DRIVER_AUTHOR "Derek G. Murray <Derek.Murray@xxxxxxxxxxxx>"
+#define DRIVER_DESC   "User-space granted page access driver"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+
+#define MAX_GRANTS 128
+
+/* A slot can be in one of three states:
+ *
+ * 0. GNTDEV_SLOT_INVALID:
+ * This slot is not associated with a grant reference, and is therefore free
+ *    to be overwritten by a new grant reference.
+ *
+ * 1. GNTDEV_SLOT_NOT_YET_MAPPED:
+ *    This slot is associated with a grant reference (via the
+ * IOCTL_GNTDEV_MAP_GRANT_REF ioctl), but it has not yet been mmap ()-ed.
+ *
+ * 2. GNTDEV_SLOT_MAPPED:
+ * This slot is associated with a grant reference, and has been mmap()-ed.
+ */
+typedef enum gntdev_slot_state {
+       GNTDEV_SLOT_INVALID = 0,        GNTDEV_SLOT_NOT_YET_MAPPED,
+       GNTDEV_SLOT_MAPPED
+} gntdev_slot_state_t;
+
+#define GNTDEV_FREE_LIST_INVALID -1
+
+/* Each opened instance of gntdev is associated with a list of grants,
+ * represented by an array of elements of the following type,
+ * gntdev_grant_info_t.
+ */
+typedef struct gntdev_grant_info {
+       gntdev_slot_state_t state;
+       union {
+               uint32_t free_list_index;
+               struct {
+                       domid_t domid;
+                       grant_ref_t ref;
+                       grant_handle_t handle;
+               } valid;
+       } u;
+} gntdev_grant_info_t;
+
+/* Private data structure, which is stored in the file pointer for files
+ * associated with this device.
+ */
+typedef struct gntdev_file_private_data {
+
+       /* Array of grant information. */
+       gntdev_grant_info_t grants[MAX_GRANTS];
+
+       /* An array of indices of free slots in the grants array.
+        * N.B. An entry in this list may temporarily have the value
+        * GNTDEV_FREE_LIST_INVALID if the corresponding slot has been removed
+        * from the list by the contiguous allocator, but the list has not yet
+        * been compressed. However, this is not visible across invocations of
+        * the device.
+        */
+       int32_t free_list[MAX_GRANTS];
+       
+       /* The number of free slots in the grants array. */
+       uint32_t free_list_size;
+       
+       /* Index of the next slot after the most recent contiguous allocation,
+        * for use in a next-fit allocator.
+        */
+       uint32_t next_fit_index;
+
+} gntdev_file_private_data_t;
+
+/* Module lifecycle operations. */
+static int __init gntdev_init(void);
+static void __exit gntdev_exit(void);
+
+module_init(gntdev_init);
+module_exit(gntdev_exit);
+
+/* File operations. */
+static int gntdev_open(struct inode *inode, struct file *flip);
+static int gntdev_release(struct inode *inode, struct file *flip);
+static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma);
+static int gntdev_ioctl (struct inode *inode, struct file *flip,
+                        unsigned int cmd, unsigned long arg);
+
+static struct file_operations gntdev_fops = {
+       .owner = THIS_MODULE,
+       .open = gntdev_open,
+       .release = gntdev_release,
+       .mmap = gntdev_mmap,
+       .ioctl = gntdev_ioctl
+};
+
+/* VM operations. */
+static void gntdev_unmap_page_range(struct vm_area_struct *vma);
+
+static struct vm_operations_struct gntdev_vmops = {
+       .unmap_page_range = gntdev_unmap_page_range
+};
+
+/* Global variables. */
+
+/* The driver major number, for use when unregistering the driver. */
+static int gntdev_major;
+
+#define GNTDEV_NAME "gntdev"
+
+/* Hacky: refers to the class in blktap implementation. */
+extern struct class *xen_class;
+
+/* Private functions. */
+static int setup_xen_class(void)
+{
+       int ret;
+
+       if (xen_class)
+               return 0;
+
+       xen_class = class_create(THIS_MODULE, "xen");
+       if ((ret = IS_ERR(xen_class))) {
+               xen_class = NULL;
+               return ret;
+       }
+       
+       return 0;
+
+}
+
+/* Interface functions. */
+
+/* Initialises the driver. Called when the module is loaded. */
+static int __init gntdev_init(void)
+{
+       struct class_device *device;
+
+       if (!is_running_on_xen()) {
+               printk(KERN_ERR "You must be running Xen to use gntdev\n");
+               return -ENODEV;
+       }
+
+       gntdev_major = register_chrdev(0, GNTDEV_NAME, &gntdev_fops);
+       if (gntdev_major < 0)
+       {
+               printk(KERN_ERR "Could not register gntdev device\n");
+               return -ENOMEM;
+       }
+
+       /* Note that if the sysfs code fails, we will still initialise the
+        * device, and output the major number so that the device can be
+        * created manually using mknod.
+        */
+       if (setup_xen_class()) {
+               printk(KERN_ERR "Error setting up xen_class\n");
+               printk(KERN_ERR "gntdev created with major number = %d\n",
+                      gntdev_major);
+               return 0;
+       }
+
+       device = class_device_create(xen_class, NULL, MKDEV(gntdev_major, 0),
+                                    NULL, GNTDEV_NAME);
+       if (IS_ERR(device)) {
+               printk(KERN_ERR "Error creating gntdev device in xen_class\n");
+               printk(KERN_ERR "gntdev created with major number = %d\n",
+                      gntdev_major);
+               return 0;
+       }
+
+       return 0;
+}
+
+/* Cleans up and unregisters the driver. Called when the driver is unloaded.
+ */
+static void __exit gntdev_exit(void)
+{
+       class_device_destroy(xen_class, MKDEV(gntdev_major, 0));
+       unregister_chrdev(gntdev_major, GNTDEV_NAME);
+}
+
+/* Called when the device is opened.
+ */
+static int gntdev_open(struct inode *inode, struct file *flip)
+{
+       gntdev_file_private_data_t *private_data;
+       int i;
+
+       try_module_get(THIS_MODULE);
+
+       /* Allocate space for the per-instance private data. */
+       private_data = kmalloc(sizeof(*private_data), GFP_KERNEL);
+
+       /* Initialise the free-list, which contains all slots at first.
+        */
+       for (i = 0; i < MAX_GRANTS; ++i) {
+               private_data->free_list[MAX_GRANTS - i - 1] = i;
+               private_data->grants[i].state = GNTDEV_SLOT_INVALID;
+               private_data->grants[i].u.free_list_index = MAX_GRANTS - i - 1;
+       }
+       private_data->free_list_size = MAX_GRANTS;
+       private_data->next_fit_index = 0;
+
+       flip->private_data = private_data;
+
+       return 0;
+}
+
+/* Called when the device is closed.
+ */
+static int gntdev_release(struct inode *inode, struct file *flip)
+{
+       if (flip->private_data) {
+               kfree(flip->private_data);
+       }
+       module_put(THIS_MODULE);
+       return 0;
+}
+
+/* Called when an attempt is made to mmap() the device. The private data from + * @flip contains the list of grant references that can be mapped. The vm_pgoff + * field of @vma contains the index into that list that refers to the grant
+ * reference that will be mapped. Only mappings that are a multiple of
+ * PAGE_SIZE are handled.
+ */
+static int gntdev_mmap (struct file *flip, struct vm_area_struct *vma) {
+
+       struct gnttab_map_grant_ref op;
+
+       unsigned long slot_index = vma->vm_pgoff;
+       uint32_t size = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+       uint64_t ptep;
+       int ret;
+       int flags;
+       int i;
+       gntdev_file_private_data_t *private_data = flip->private_data;
+
+       if (unlikely(!private_data)) {
+               printk(KERN_ERR "File's private data is NULL.\n");
+               return -EINVAL;
+       }
+
+       if (unlikely((size <= 0) || (size + slot_index) > MAX_GRANTS)) {
+               printk(KERN_ERR "Invalid number of pages or offset"
+                      "(num_pages = %d, first_slot = %ld).\n",
+                      size, slot_index);
+               return -ENXIO;
+       }
+
+       if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) {
+               printk(KERN_ERR "Writable mappings must be shared.\n");
+               return -EINVAL;
+       }
+
+       /* Slots must be in the NOT_YET_MAPPED state. */
+       for (i = 0; i < size; ++i) {
+               if (private_data->grants[slot_index + i].state !=
+                   GNTDEV_SLOT_NOT_YET_MAPPED) {
+                       printk(KERN_ERR "Slot (index = %ld) is in the wrong "
+                              "state (%d).\n", slot_index + i,
+                              private_data->grants[slot_index + i].state);
+                       return -EINVAL;
+               }
+       }
+
+       /* Install the hook for unmapping. */
+       vma->vm_ops = &gntdev_vmops;
+
+       /* The VM area contains pages from another VM.
+        * TODO: consider the implications of
+        * $LINUX/mm/memory.c::get_user_pages(), which checks this field and
+        * does something with the vm_private_data.
+        */
+       vma->vm_flags |= VM_FOREIGN;
+
+       /* This flag prevents Bad PTE errors when the memory is unmapped. */
+       vma->vm_flags |= VM_RESERVED;
+
+       /* This flag prevents this VM area being copied on a fork(). A better
+        * behaviour might be to explicitly carry out the appropriate mappings
+        * on fork(), but I don't know if there's a hook for this.
+        */
+       vma->vm_flags |= VM_DONTCOPY;
+
+       /* This flag ensures that the page tables are not unpinned before the
+        * VM area is unmapped. Therefore Xen still recognises the PTE as
+        * belonging to an L1 pagetable, and the grant unmap operation will
+        * succeed, even if the process does not exit cleanly.
+        */
+       vma->vm_mm->context.has_foreign_mappings = 1;
+
+       for (i = 0; i < size; ++i) {
+               
+               /* Get the machine address of the PTE for the user page. */
+               if ((ret = create_lookup_pte_addr(vma->vm_mm,
+                                                 vma->vm_start
+                                                 + (i << PAGE_SHIFT), &ptep)))
+               {
+                       printk(KERN_ERR "Error obtaining PTE pointer (%d).\n",
+                              ret);
+                       return -ENOMEM;
+               }
+               
+               /* Configure the map operation. */
+               
+               /* The reference is to be used by host CPUs. */
+               flags = GNTMAP_host_map;
+               
+               /* The reference is to be used by the current application. */
+               flags |= GNTMAP_application_map;
+               
+               /* The map request contains the machine address of the PTE to
+                * update.
+                */
+               flags |= GNTMAP_contains_pte;
+
+               gnttab_set_map_op(&op, ptep, flags,
+                                 private_data->grants[slot_index+i]
+                                 .u.valid.ref,
+                                 private_data->grants[slot_index+i]
+                                 .u.valid.domid);
+
+               /* Carry out the mapping of the grant reference. */
+               ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op,
+                                               1);
+               BUG_ON(ret);
+               if (op.status) {
+                       printk(KERN_ERR "Error mapping the grant reference "
+                              "(%d).\n", op.status);
+                       return -ENOMEM;
+               }
+
+               /* Record the grant handle, for use in the unmap operation. */
+               private_data->grants[slot_index+i].u.valid.handle = op.handle;
+               
+               private_data->grants[slot_index+i].state = GNTDEV_SLOT_MAPPED;
+
+       }
+
+       return 0;
+}
+
+/* This is called either by an explicit munmap() on an mmap()-ed granted page, + * or by the exit routine, if the application process does not unmap all of its
+ * grants explicitly.
+ */
+static void gntdev_unmap_page_range(struct vm_area_struct *vma) {
+       int flags;
+       gntdev_file_private_data_t *private_data
+               = (gntdev_file_private_data_t *) vma->vm_file->private_data;
+       int slot_index = vma->vm_pgoff;
+       int size = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+       uint64_t ptep;
+       int ret;
+       int i;
+       struct gnttab_unmap_grant_ref op;
+       
+       for (i = 0; i < size; ++i) {
+
+               /* Get the machine address of the PTE for the user page. */
+               ret = create_lookup_pte_addr(vma->vm_mm,
+                                            vma->vm_start + (i << PAGE_SHIFT),
+                                            &ptep);
+               BUG_ON(ret);
+               
+               /* Configure the unmap operation. */
+               
+               /* The unmap operation contains the machine address of the PTE
+                * to modify.
+                */
+               flags = GNTMAP_contains_pte;
+               
+               gnttab_set_unmap_op(&op, ptep, flags,
+                                   private_data->grants[slot_index+i]
+                                   .u.valid.handle);
+               
+               /* Carry out the unmapping of the grant reference. */
+               ret = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op,
+                                               1);
+               BUG_ON(ret);
+
+               /* Return slot to the not-yet-mapped state, so that it may be
+                * mapped again, or removed by a subsequent ioctl.
+                */
+               private_data->grants[slot_index+i].state =
+                       GNTDEV_SLOT_NOT_YET_MAPPED;
+
+       }
+}
+
+/* Adds information about a grant reference to the list of grants in the file's + * private data structure. Returns non-zero on failure. On success, sets the + * value of *offset to the offset that should be mmap()-ed in order to map the
+ * grant reference.
+ */
+static int add_grant_reference(struct file *flip,
+                              struct ioctl_gntdev_grant_ref *op,
+                              long *offset)
+{
+       gntdev_file_private_data_t *private_data
+               = (gntdev_file_private_data_t *) flip->private_data;
+
+       uint32_t slot_index;
+
+       if (unlikely(private_data->free_list_size == 0)) {
+               return -ENOMEM;
+       }
+
+       slot_index = private_data->free_list[--private_data->free_list_size];
+
+       /* Copy the grant information into file's private data. */
+       private_data->grants[slot_index].state = GNTDEV_SLOT_NOT_YET_MAPPED;
+       private_data->grants[slot_index].u.valid.domid = op->domid;
+       private_data->grants[slot_index].u.valid.ref = op->ref;
+
+       /* The offset is calculated as the index of the chosen entry in the
+        * file's private data's array of grant information. This is then
+        * shifted to give an offset into the virtual "file address space".
+        */
+       *offset = slot_index << PAGE_SHIFT;
+
+       return 0;
+
+}
+
+/* Adds the @count grant references to the contiguous range in the slot array + * beginning at @first_slot. It is assumed that @first_slot was returned by a
+ * previous invocation of find_contiguous_free_range(), during the same
+ * invocation of the driver.
+ */
+static int add_grant_references(struct file *flip,
+                               int count,
+                               struct ioctl_gntdev_grant_ref __user *ops,
+                               uint32_t first_slot)
+{
+
+       gntdev_file_private_data_t *private_data
+               = (gntdev_file_private_data_t *) flip->private_data;
+       struct ioctl_gntdev_grant_ref op;
+       int i, rc;
+       
+       for (i = 0; i < count; ++i) {
+
+               /* First, mark the slot's entry in the free list as invalid. */
+               int free_list_index =
+                       private_data->grants[first_slot+i].u.free_list_index;
+               private_data->free_list[free_list_index] =
+                       GNTDEV_FREE_LIST_INVALID;
+
+               /* Then, copy in the grant reference details. */
+               if ((rc = copy_from_user(&op, &ops[i], sizeof(op)))) {
+                       return rc;
+               }
+
+               /* Now, update the slot. */
+               private_data->grants[first_slot+i].state =
+                       GNTDEV_SLOT_NOT_YET_MAPPED;
+               private_data->grants[first_slot+i].u.valid.domid = op.domid;
+               private_data->grants[first_slot+i].u.valid.ref = op.ref;
+       }
+
+       return 0;       
+}
+
+/* Scans through the free list for @flip, removing entries that are marked as + * GNTDEV_SLOT_INVALID. This will reduce the recorded size of the free list to
+ * the number of valid entries.
+ */
+static void compress_free_list(struct file *flip) {
+       gntdev_file_private_data_t *private_data
+               = (gntdev_file_private_data_t *) flip->private_data;
+       int i, j = 0;
+       for (i = 0; i < MAX_GRANTS; ++i) {
+               if (private_data->free_list[i] != GNTDEV_FREE_LIST_INVALID) {
+                       private_data->free_list[j] =
+                               private_data->free_list[i];
+                       ++j;
+               } else {
+                       --private_data->free_list_size;
+               }
+       }
+}
+
+/* Searches the grant array in the private data of @flip for a range of
+ * @num_slots contiguous slots in the GNTDEV_SLOT_INVALID state.
+ *
+ * Returns the index of the first slot if a range is found, otherwise -ENOMEM.
+ */
+static int find_contiguous_free_range(struct file *flip,
+                                     uint32_t num_slots) {
+
+       gntdev_file_private_data_t *private_data
+               = (gntdev_file_private_data_t *) flip->private_data;
+       
+       int i;
+       int start_index = private_data->next_fit_index;
+       int range_start = 0, range_length;
+
+       /* First search from the start_index to the end of the array. */
+       range_length = 0;
+       for (i = start_index; i < MAX_GRANTS; ++i) {
+               if (private_data->grants[i].state == GNTDEV_SLOT_INVALID) {
+                       if (range_length == 0) {
+                               range_start = i;
+                       }
+                       ++range_length;
+                       if (range_length == num_slots) {
+                               return range_start;
+                       }
+               }
+       }
+       
+       /* Now search from the start of the array to the start_index. */
+       range_length = 0;
+       for (i = 0; i < start_index; ++i) {
+               if (private_data->grants[i].state == GNTDEV_SLOT_INVALID) {
+                       if (range_length == 0) {
+                               range_start = i;
+                       }
+                       ++range_length;
+                       if (range_length == num_slots) {
+                               return range_start;
+                       }
+               }
+       }
+       
+       return -ENOMEM;
+       
+}
+                               
+
+/* Called when an ioctl is made on the device.
+ */
+static int gntdev_ioctl(struct inode *inode, struct file *flip,
+                       unsigned int cmd, unsigned long arg)
+{
+
+       int rc = 0;
+
+       switch (cmd) {
+       case IOCTL_GNTDEV_MAP_GRANT_REF:
+       {
+               struct ioctl_gntdev_map_grant_ref op;
+               struct ioctl_gntdev_grant_ref ref;
+
+               down_write(&current->mm->mmap_sem);
+
+               if ((rc = copy_from_user(&op,
+                                        (void __user *) arg,
+                                        sizeof(op)))) {
+                       rc = -EFAULT;
+                       goto map_out;
+               }
+               if (unlikely(op.count <= 0)) {
+                       rc = -EINVAL;
+                       goto map_out;
+               }
+               if (op.count == 1) {
+                       if ((rc = copy_from_user(&ref,
+                                                (void __user *) op.refs,
+                                                sizeof(op)))) {
+                               printk(KERN_ERR "Copying op from user failed "
+                                      "(%d).\n", rc);
+                               rc = -EINVAL;
+                               goto map_out;
+                       }
+                       if ((rc = add_grant_reference(flip, &ref, &op.index))
+                           < 0) {
+                               printk(KERN_ERR "Adding grant reference "
+                                      "failed (%d).\n", rc);
+                               goto map_out;
+                       }
+               } else {
+                       if ((rc = find_contiguous_free_range(flip, op.count))
+                           < 0) {
+                               printk(KERN_ERR "Finding contiguous range "
+                                      "failed (%d).\n", rc);
+                               goto map_out;
+                       }
+                       op.index = rc << PAGE_SHIFT;
+                       if ((rc = add_grant_references(flip, op.count,
+                                                      op.refs, rc))) {
+                               printk(KERN_ERR "Adding grant references "
+                                      "failed (%d).\n", rc);
+                               goto map_out;
+                       }
+                       compress_free_list(flip);
+               }
+               if ((rc = copy_to_user((void __user *) arg,
+                                      &op,
+                                      sizeof(op)))) {
+                       printk(KERN_ERR "Copying result back to user failed "
+                              "(%d)\n", rc);
+                       rc = -EFAULT;
+                       goto map_out;
+               }
+       map_out:
+               up_write(&current->mm->mmap_sem);
+               return rc;
+       }
+       case IOCTL_GNTDEV_UNMAP_GRANT_REF:
+       {
+               struct ioctl_gntdev_unmap_grant_ref op;
+               gntdev_file_private_data_t *private_data =
+                       (gntdev_file_private_data_t *) flip->private_data;
+               int i, start_index;
+               down_write(&current->mm->mmap_sem);
+               
+               if ((rc = copy_from_user(&op,
+                                        (void __user *) arg,
+                                        sizeof(op)))) {
+                       rc = -EFAULT;
+                       goto unmap_out;
+               }
+
+               start_index = op.index >> PAGE_SHIFT;
+
+               /* First, check that all pages are in the NOT_YET_MAPPED
+                * state.
+                */
+               for (i = 0; i < op.count; ++i) {
+                       printk("Invalidating slot %d.\n", start_index + i);
+                       if (unlikely
+                           (private_data->grants[start_index + i].state
+                            != GNTDEV_SLOT_NOT_YET_MAPPED)) {
+                               if (private_data->grants[start_index + i].state
+                                   == GNTDEV_SLOT_INVALID) {
+                                       printk(KERN_ERR
+                                              "Tried to remove an invalid "
+                                              "grant at offset 0x%x.",
+                                              (start_index + i)
+                                              << PAGE_SHIFT);
+                                       rc = -EINVAL;
+                               } else {
+                                       printk(KERN_ERR
+                                              "Tried to remove a grant which "
+                                              "is currently mmap()-ed at "
+                                              "offset 0x%x.",
+                                              (start_index + i)
+                                              << PAGE_SHIFT);
+                                       rc = -EBUSY;
+                               }
+                               goto unmap_out;
+                       }
+               }
+
+               /* Unmap pages and add them to the free list.
+                */
+               for (i = 0; i < op.count; ++i) {
+                       private_data->grants[start_index + i].state =
+                               GNTDEV_SLOT_INVALID;
+                       private_data->free_list[private_data->free_list_size] =
+                               start_index + i;
+                       ++private_data->free_list_size;
+               }
+
+       unmap_out:
+               up_write(&current->mm->mmap_sem);
+               return rc;
+       }
+       case IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR:
+       {
+               struct ioctl_gntdev_get_offset_for_vaddr op;
+               struct vm_area_struct *vma;
+               down_read(&current->mm->mmap_sem);
+
+               if ((rc = copy_from_user(&op,
+                                        (void __user *) arg,
+                                        sizeof(op)))) {
+                       rc = -EFAULT;
+                       goto get_offset_out;
+               }
+
+               vma = find_vma(current->mm, (unsigned long) op.vaddr);
+               if (vma == NULL) {
+                       rc = -EFAULT;
+                       goto get_offset_out;
+               }
+               if (vma->vm_start != (unsigned long) op.vaddr) {
+                       printk(KERN_ERR "The vaddr specified in an "
+                              "IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR must be at "
+                              "the start of the VM area. vma->vm_start = "
+                              "%#lx; vaddr = %#lx\n",
+                              vma->vm_start, (unsigned long) op.vaddr);
+               }
+               op.offset = vma->vm_pgoff << PAGE_SHIFT;
+               op.count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+
+               if ((rc = copy_to_user((void __user *) arg,
+                                      &op,
+                                      sizeof(op)))) {
+                       rc = -EFAULT;
+                       goto get_offset_out;
+               }
+       get_offset_out:
+               up_read(&current->mm->mmap_sem);
+               return rc;
+       }
+       default:
+               return -ENOIOCTLCMD;
+       }
+
+       return 0;
+}
diff -r 8fa17d1560a9 linux-2.6-xen-sparse/include/xen/public/gntdev.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/linux-2.6-xen-sparse/include/xen/public/gntdev.h Thu Mar 15 17:59:22 2007 +0000
@@ -0,0 +1,101 @@
+/ ************************************************************************ ******
+ * gntdev.h
+ *
+ * Interface to /dev/xen/gntdev.
+ *
+ * Copyright (c) 2007, D G Murray
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef __LINUX_PUBLIC_GNTDEV_H__
+#define __LINUX_PUBLIC_GNTDEV_H__
+
+struct ioctl_gntdev_grant_ref {
+       /* The domain ID of the grant to be mapped. */
+       uint32_t domid;
+       /* The grant reference of the grant to be mapped. */
+       uint32_t ref;
+};
+
+/*
+ * Inserts the grant references into the mapping table of an instance
+ * of gntdev. N.B. This does not perform the mapping, which is deferred
+ * until mmap() is called with @index as the offset.
+ */
+#define IOCTL_GNTDEV_MAP_GRANT_REF \
+_IOC(_IOC_NONE, 'G', 0, sizeof(struct ioctl_gntdev_map_grant_ref))
+struct ioctl_gntdev_map_grant_ref {
+       /* IN parameters */
+       /* The number of grants to be mapped. */
+       uint32_t count;
+       /* Pointer to an array of grant references, of size @count. */
+       struct ioctl_gntdev_grant_ref *refs;
+       /* OUT parameters */
+       /* The offset to be used on a subsequent call to mmap(). */
+       long index;
+};
+
+/*
+ * Removes the grant references from the mapping table of an instance of + * of gntdev. N.B. munmap() must be called on the relevant virtual address(es)
+ * before this ioctl is called, or an error will result.
+ */
+#define IOCTL_GNTDEV_UNMAP_GRANT_REF \
+_IOC(_IOC_NONE, 'G', 1, sizeof(struct ioctl_gntdev_unmap_grant_ref))
+struct ioctl_gntdev_unmap_grant_ref {
+       /* IN parameters */
+       /* The offset was returned by the corresponding map operation. */
+       long index;
+       /* The number of pages to be unmapped. */
+       uint32_t count;
+};
+
+/*
+ * Returns the offset in the driver's address space that corresponds
+ * to @vaddr. This can be used to perform a munmap(), followed by an
+ * UNMAP_GRANT_REF ioctl, where no state about the offset is retained by + * the caller. The number of pages that were allocated at the same time as
+ * @vaddr is returned in @count.
+ *
+ * N.B. Where more than one page has been mapped into a contiguous range, the + * supplied @vaddr must correspond to the start of the range; otherwise + * an error will result. It is only possible to munmap() the entire + * contiguously-allocated range at once, and not any subrange thereof.
+ */
+#define IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR \
+_IOC(_IOC_NONE, 'G', 2, sizeof(struct ioctl_gntdev_get_offset_for_vaddr))
+struct ioctl_gntdev_get_offset_for_vaddr {
+       /* IN parameters */
+       /* The virtual address of the first mapped page in a range. */
+       void *vaddr;
+       /* OUT parameters */
+       /* The offset that was used in the initial mmap() operation. */
+       long offset;
+       /* The number of pages mapped in the VM area that begins at @vaddr. */
+       uint32_t count;
+};
+
+#endif /* __LINUX_PUBLIC_GNTDEV_H__ */


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

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