Derek Murray
2007-Mar-19 10:40 UTC
[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@cl.cam.ac.uk> --- 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@cl.cam.ac.uk>" +#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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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@lists.xensource.com http://lists.xensource.com/xen-devel