WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

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

To: xen-devel@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-devel] [PATCH 1/3] [RFC] User-space grant table device - main driver
From: Derek Murray <Derek.Murray@xxxxxxxxxxxx>
Date: Mon, 19 Mar 2007 10:40:25 +0000
Delivery-date: Mon, 19 Mar 2007 03:43:10 -0700
Envelope-to: www-data@xxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
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

<Prev in Thread] Current Thread [Next in Thread>
  • [Xen-devel] [PATCH 1/3] [RFC] User-space grant table device - main driver, Derek Murray <=