From mboxrd@z Thu Jan 1 00:00:00 1970 From: Jeremy Fitzhardinge Subject: Re: Re: pv_ops & gntdev? Date: Tue, 03 Mar 2009 14:59:34 -0800 Message-ID: <49ADB656.7080404@goop.org> References: <49A44030.2070709@redhat.com> <49A4640E.1000807@goop.org> <49A470DD.2000008@redhat.com> <49A517F6.30005@redhat.com> <49A58506.2020407@goop.org> <49A58FFF.3050604@redhat.com> <49A5A4BD.7080207@goop.org> <49A5B5DD.60309@redhat.com> <49A5B9AA.7010709@goop.org> <49A5BB7E.8030503@redhat.com> <49A5BC9D.5010801@goop.org> <49A5C208.2040509@redhat.com> <49A5C5FB.6080000@goop.org> <49A5C957.7060205@redhat.com> <49A5CD57.2010300@goop.org> <49AC635F.5010906@redhat.com> Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Return-path: In-Reply-To: <49AC635F.5010906@redhat.com> List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xensource.com Errors-To: xen-devel-bounces@lists.xensource.com To: Gerd Hoffmann Cc: Xen Development Mailing List List-Id: xen-devel@lists.xenproject.org Gerd Hoffmann wrote: > Jeremy Fitzhardinge wrote: > >> Gerd Hoffmann wrote: >> >>> mmu motifier will fit the bill and additionally work fine on partial >>> unmaps. >>> >> OK, it'll be interesting to see how that turns out. >> > > Prelimary version attached. > > Features: > * compiles ;) > * survived light testing. > * mapping via xc_gnttab_map_grant_ref*s* (i.e. multiple grants at once) > works stable. Last time I tried that with 2.6.18 I quickly killed > the host kernel. > * much smaller than the 2.6.18 version. > * maps grants directly, doesn't burn pfns and kernel address space. > Do you have a test program? > Issues: > (1) Grant teardown on unclean exit doesn't work yet. Doesn't crash, > but I think I leak the pages because they are not unmapped via > grant api. > (2) Also on unclean exit my release callbacks are not called for some > silly reason, to be investigated. Memory leak. Probably related > to (1). Maybe I did something wrong with the VM flags ... > (3) There are probably some nasty corner cases not handled correctly > yet, the whole thing needs a careful review once the two issues > listed above are fixed. > > Reviews, comments and hints are welcome, > Comments below. There's quite a bit of whitespace damage and formatting strangeness (NULL == tests, for example). I've checked in a couple of followup patches to address some of the comments below, but not all. > diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig > index 0138113..555e4b6 100644 > --- a/drivers/xen/Kconfig > +++ b/drivers/xen/Kconfig > @@ -74,9 +74,17 @@ config XEN_COMPAT_XENFS > a xen platform. > If in doubt, say yes. > > +config XEN_GNTDEV > + bool "userspace grant access device driver" > + depends on XEN > + default XEN_DOM0 > + select MMU_NOTIFIER > + help > + Allows userspace processes use grants. > + > config XEN_XENBUS_FRONTEND > tristate > > config XEN_S3 > def_bool y > - depends on XEN_DOM0 && ACPI > \ No newline at end of file > + depends on XEN_DOM0 && ACPI > diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile > index 4b01fc8..c2180d9 100644 > --- a/drivers/xen/Makefile > +++ b/drivers/xen/Makefile > @@ -5,8 +5,9 @@ obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o > obj-$(CONFIG_XEN_XENCOMM) += xencomm.o > obj-$(CONFIG_XEN_BALLOON) += balloon.o > obj-$(CONFIG_XEN_DEV_EVTCHN) += evtchn.o > +obj-$(CONFIG_XEN_GNTDEV) += gntdev.o > obj-$(CONFIG_XEN_BLKDEV_BACKEND) += blkback/ > obj-$(CONFIG_XEN_NETDEV_BACKEND) += netback/ > obj-$(CONFIG_XENFS) += xenfs/ > > -obj-$(CONFIG_XEN_S3) += acpi.o > \ No newline at end of file > +obj-$(CONFIG_XEN_S3) += acpi.o > diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c > new file mode 100644 > index 0000000..3dd0e5f > --- /dev/null > +++ b/drivers/xen/gntdev.c > @@ -0,0 +1,594 @@ > +/****************************************************************************** > + * gntdev.c > + * > + * Device for accessing (in user-space) pages that have been granted by other > + * domains. > + * > + * Copyright (c) 2006-2007, D G Murray. > + * (c) 2009 Gerd Hoffmann > + * > + * 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > + > +MODULE_LICENSE("GPL"); > +MODULE_AUTHOR("Derek G. Murray , " > + "Gerd Hoffmann "); > +MODULE_DESCRIPTION("User-space granted page access driver"); > + > +static int debug = 1; > +module_param(debug, int, 0644); > +static int limit = 1024; > +module_param(limit, int, 0644); > + > +struct gntdev_priv { > + struct list_head maps; > + uint32_t used; > + uint32_t limit; > + struct rw_semaphore sem; > + struct mm_struct *mm; > + struct mmu_notifier mn; > +}; > + > +struct grant_info { > + domid_t domid; > + grant_ref_t ref; > + grant_handle_t handle; > +}; > + > +struct grant_map { > + struct list_head next; > + struct gntdev_priv *priv; > + struct vm_area_struct *vma; > + int index; > + int count; > + int flags; > + int is_mapped; > + struct ioctl_gntdev_grant_ref *grants; > + struct gnttab_map_grant_ref *map_ops; > + struct gnttab_unmap_grant_ref *unmap_ops; > +}; > + > +/* ------------------------------------------------------------------ */ > + > +static void gntdev_print_maps(struct gntdev_priv *priv, > + char *text, int text_index) > +{ > + struct grant_map *map; > + > + printk("%s: maps list (priv %p, usage %d/%d)\n", > + __FUNCTION__, priv, priv->used, priv->limit); > + list_for_each_entry(map, &priv->maps, next) > + printk(" index %2d, count %2d %s\n", > + map->index, map->count, > + map->index == text_index && text ? text : ""); > +} > + > +static struct grant_map *gntdev_add_map(struct gntdev_priv *priv, int count) > +{ > + struct grant_map *map, *add; > + > + add = kzalloc(sizeof(struct grant_map), GFP_KERNEL); > + if (NULL == add) > + return NULL; > + > + add->grants = kzalloc(sizeof(add->grants[0]) * count, GFP_KERNEL); > + add->map_ops = kzalloc(sizeof(add->map_ops[0]) * count, GFP_KERNEL); > + add->unmap_ops = kzalloc(sizeof(add->unmap_ops[0]) * count, GFP_KERNEL); > + if (NULL == add->grants || > + NULL == add->map_ops || > + NULL == add->unmap_ops) { > + kfree(add->grants); > + kfree(add->map_ops); > + kfree(add->unmap_ops); > + kfree(add); > + return NULL; > + } > + > + add->index = 0; > + add->count = count; > + add->priv = priv; > + > + down_write(&priv->sem); > + if (add->count + priv->used > priv->limit) { > + up_write(&priv->sem); > + kfree(add); > Leaks ->grants, ->map_ops, ->unmap_ops? > + return NULL; > + } > + > + list_for_each_entry(map, &priv->maps, next) { > + if (add->index + add->count < map->index) { > + list_add_tail(&add->next, &map->next); > + goto done; > + } > + add->index = map->index + map->count; > + } > + list_add_tail(&add->next, &priv->maps); > + > +done: > + priv->used += add->count; > + if (debug > 1) > + gntdev_print_maps(priv, "[new]", add->index); > + up_write(&priv->sem); > + return add; > +} > + > +static struct grant_map *gntdev_find_map_index(struct gntdev_priv *priv, int index, > + int count) > +{ > + struct grant_map *map; > + > + list_for_each_entry(map, &priv->maps, next) { > + if (map->index != index) > + continue; > + if (map->count != count) > + continue; > + return map; > + } > + return NULL; > +} > + > +static struct grant_map *gntdev_find_map_vaddr(struct gntdev_priv *priv, > + unsigned long vaddr) > +{ > + struct grant_map *map; > + > + list_for_each_entry(map, &priv->maps, next) { > + if (!map->vma) > + continue; > + if (vaddr < map->vma->vm_start) > + continue; > + if (vaddr >= map->vma->vm_end) > + continue; > + return map; > + } > + return NULL; > +} > + > +static int gntdev_del_map(struct gntdev_priv *priv, int index, > + int count) > +{ > + struct grant_map *map; > + int i, err = -EINVAL; > + > + down_write(&priv->sem); > + map = gntdev_find_map_index(priv, index, count); > + if (NULL == map) > + goto out; > + > + err = -EBUSY; > + if (map->vma) > + goto out; > + for (i = 0; i < map->count; i++) > + if (map->unmap_ops[i].handle) > + goto out; > + > + list_del(&map->next); > + kfree(map->grants); > + kfree(map->map_ops); > + kfree(map->unmap_ops); > + kfree(map); > + priv->used -= map->count; > + err = 0; > + > +out: > + up_write(&priv->sem); > + return err; > +} > + > +/* ------------------------------------------------------------------ */ > + > +static int find_grant_ptes(pte_t *pte, pgtable_t token, unsigned long addr, void *data) > +{ > + struct grant_map *map = data; > + unsigned int pgnr = (addr - map->vma->vm_start) >> PAGE_SHIFT; > + u64 mpte; > pteval_t or pte_t > + > + BUG_ON(pgnr >= map->count); > + mpte = (u64)pfn_to_mfn(page_to_pfn(token)) << PAGE_SHIFT; > + mpte |= (unsigned long)pte & ~PAGE_MASK; > (!) You're casting pte_t * to unsigned long, masking off the lower bits then oring them into your pte. That doesn't look like it will mean very much... I guess this explains your not-unmapping bug. This should probably be using mfn_pte() anyway: mfn = pfn_to_mfn(page_to_pfn(token)); pgprot = __pgprot(pte->pte & PTE_FLAGS_MASK); mpte = mfn_pte(mfn, pgprot); > + gnttab_set_map_op(&map->map_ops[pgnr], mpte, map->flags, > + map->grants[pgnr].ref, > + map->grants[pgnr].domid); > + gnttab_set_unmap_op(&map->unmap_ops[pgnr], mpte, map->flags, > + 0 /* handle */); > + return 0; > +} > + > +static int map_grant_pages(struct grant_map *map) > +{ > + int i, err = 0; > + > + if (debug) > + printk("%s: map %d+%d\n", __FUNCTION__, map->index, map->count); > + BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, > + map->map_ops, map->count)); > WARN_ON() is better here, because we can probably limp on if it fails. > + for (i = 0; i < map->count; i++) { > + if (map->map_ops[i].status) > + err = -EINVAL; > + map->unmap_ops[i].handle = map->map_ops[i].handle; > + } > + return err; > +} > + > +static int unmap_grant_pages(struct grant_map *map, int offset, int pages) > +{ > + int i, err = 0; > + > + if (debug) > + printk("%s: map %d+%d [%d+%d]\n", __FUNCTION__, > + map->index, map->count, offset, pages); > + BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, > + map->unmap_ops + offset, pages)); > Ditto. > + for (i = 0; i < pages; i++) { > + if (map->unmap_ops[offset+i].status) > + err = -EINVAL; > + map->unmap_ops[offset+i].handle = 0; > + } > + return err; > +} > + > +/* ------------------------------------------------------------------ */ > + > +static void gntdev_vma_close(struct vm_area_struct *vma) > +{ > + struct grant_map *map = vma->vm_private_data; > + > + if (debug > 1) > + printk("%s\n", __FUNCTION__); > + map->is_mapped = 0; > + map->vma = NULL; > + vma->vm_private_data = NULL; > +} > + > +static int gntdev_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) > +{ > + if (debug) > + printk("%s: vaddr %p, pgoff %ld (shouldn't happen)\n", > + __FUNCTION__, vmf->virtual_address, vmf->pgoff); > + vmf->flags = VM_FAULT_ERROR; > + return 0; > +} > + > +static struct vm_operations_struct gntdev_vmops = { > + .close = gntdev_vma_close, > + .fault = gntdev_vma_fault, > +}; > + > +/* ------------------------------------------------------------------ */ > + > +static void mn_invl_range_start(struct mmu_notifier *mn, > + struct mm_struct *mm, > + unsigned long start, unsigned long end) > +{ > + struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn); > + struct grant_map *map; > + unsigned long mstart, mend; > + int err; > + > + down_read(&priv->sem); > + list_for_each_entry(map, &priv->maps, next) { > + if (!map->vma) > + continue; > + if (!map->is_mapped) > + continue; > + if (map->vma->vm_start >= end) > + continue; > + if (map->vma->vm_end <= start) > + continue; > + mstart = max(start, map->vma->vm_start); > + mend = min(end, map->vma->vm_end); > + if (debug > 1) > + printk("%s: map %d+%d (%lx %lx), range %lx %lx, mrange %lx %lx\n", > + __FUNCTION__, map->index, map->count, > + map->vma->vm_start, map->vma->vm_end, > + start, end, mstart, mend); > + err = unmap_grant_pages(map, > + (mstart - map->vma->vm_start) >> PAGE_SHIFT, > + (mend - mstart) >> PAGE_SHIFT); > + BUG_ON(err); > + } > + up_read(&priv->sem); > +} > + > +static void mn_invl_page(struct mmu_notifier *mn, > + struct mm_struct *mm, > + unsigned long address) > +{ > + mn_invl_range_start(mn, mm, address, address + PAGE_SIZE); > +} > + > +static void mn_release(struct mmu_notifier *mn, > + struct mm_struct *mm) > +{ > + struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn); > + struct grant_map *map; > + int err; > + > + down_read(&priv->sem); > + list_for_each_entry(map, &priv->maps, next) { > + if (!map->vma) > + continue; > + if (debug > 1) > + printk("%s: map %d+%d (%lx %lx)\n", > + __FUNCTION__, map->index, map->count, > + map->vma->vm_start, map->vma->vm_end); > + err = unmap_grant_pages(map, 0, map->count); > + BUG_ON(err); > + } > + up_read(&priv->sem); > +} > + > +struct mmu_notifier_ops gntdev_mmu_ops = { > + .release = mn_release, > + .invalidate_page = mn_invl_page, > + .invalidate_range_start = mn_invl_range_start, > +}; > + > +/* ------------------------------------------------------------------ */ > + > +static int gntdev_open(struct inode *inode, struct file *flip) > +{ > + struct gntdev_priv *priv; > + > + priv = kzalloc(sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + INIT_LIST_HEAD(&priv->maps); > + init_rwsem(&priv->sem); > + priv->limit = limit; > + priv->mm = get_task_mm(current); > Is this to cope with the /dev/gnttab fd being inherited by/passed to some other process, even if the mappings don't follow it? > + priv->mn.ops = &gntdev_mmu_ops; > + mmu_notifier_register(&priv->mn, priv->mm); > + flip->private_data = priv; > + > + if (debug) > + printk("%s: priv %p\n", __FUNCTION__, priv); > + > + return 0; > +} > + > +static int gntdev_release(struct inode *inode, struct file *flip) > +{ > + struct gntdev_priv *priv = flip->private_data; > + > + if (debug) > + printk("%s: priv %p\n", __FUNCTION__, priv); > + > + BUG_ON(!list_empty(&priv->maps)); > + mmu_notifier_unregister(&priv->mn, priv->mm); > + mm_release(current, priv->mm); > + kfree(priv); > + return 0; > +} > + > +static long gntdev_ioctl(struct file *flip, > + unsigned int cmd, unsigned long arg) > +{ > + struct gntdev_priv *priv = flip->private_data; > + void __user *ptr = (void __user *)arg; > + > + switch (cmd) { > + case IOCTL_GNTDEV_MAP_GRANT_REF: > ioctl switches should be split into separate functions. > + { > + struct ioctl_gntdev_map_grant_ref op; > + struct ioctl_gntdev_map_grant_ref __user *u = ptr; > + struct grant_map *map; > + > + if (copy_from_user(&op, u, sizeof(op)) != 0) > + return -EFAULT; > + if (debug > 1) > + printk("%s: priv %p, add %d\n", __FUNCTION__, priv, > + op.count); > + if (unlikely(op.count <= 0)) > + return -EINVAL; > + if (unlikely(op.count > priv->limit)) > + return -EINVAL; > + > + map = gntdev_add_map(priv, op.count); > + if (!map) > + return -ENOMEM; > + if (copy_from_user(map->grants, &u->refs, > + sizeof(map->grants[0]) * op.count) != 0) { > + gntdev_del_map(priv, map->index, map->count); > + return -EFAULT; > + } > + > + op.index = map->index << PAGE_SHIFT; > + if (copy_to_user(u, &op, sizeof(op)) != 0) { > + gntdev_del_map(priv, map->index, map->count); > + return -EFAULT; > + } > + > + return 0; > + } > + case IOCTL_GNTDEV_UNMAP_GRANT_REF: > + { > + struct ioctl_gntdev_unmap_grant_ref op; > + > + if (copy_from_user(&op, ptr, sizeof(op)) != 0) > + return -EFAULT; > + if (debug > 1) > + printk("%s: priv %p, del %d+%d\n", __FUNCTION__, priv, > + (int)op.index, (int)op.count); > + > + return gntdev_del_map(priv, op.index >> PAGE_SHIFT, op.count); > + } > + case IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR: > + { > + struct ioctl_gntdev_get_offset_for_vaddr op; > + struct grant_map *map; > + > + if (copy_from_user(&op, ptr, sizeof(op)) != 0) > + return -EFAULT; > + if (debug > 1) > + printk("%s: priv %p, offset for vaddr %lx\n", __FUNCTION__, priv, > + (unsigned long)op.vaddr); > + > + down_read(&priv->sem); > + map = gntdev_find_map_vaddr(priv, op.vaddr); > + if (map == NULL || > + map->vma->vm_start != op.vaddr) { > + up_read(&priv->sem); > + return -EINVAL; > + } > + op.offset = map->index << PAGE_SHIFT; > + op.count = map->count; > + up_read(&priv->sem); > + > + if (copy_to_user(ptr, &op, sizeof(op)) != 0) > + return -EFAULT; > + return 0; > + } > + case IOCTL_GNTDEV_SET_MAX_GRANTS: > + { > + struct ioctl_gntdev_set_max_grants op; > + > + if (copy_from_user(&op, ptr, sizeof(op)) != 0) > + return -EFAULT; > + if (debug) > + printk("%s: priv %p, limit %d\n", __FUNCTION__, priv, op.count); > + if (op.count > limit) > + return -EINVAL; > + down_write(&priv->sem); > + priv->limit = op.count; > + up_write(&priv->sem); > + return 0; > + } > + default: > + if (debug) > + printk("%s: priv %p, unknown cmd %x\n", __FUNCTION__, priv, cmd); > + return -ENOIOCTLCMD; > + } > + > + return 0; > +} > + > +static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma) > +{ > + struct gntdev_priv *priv = flip->private_data; > + int index = vma->vm_pgoff; > + int count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; > + struct grant_map *map; > + int err = -EINVAL; > + > + if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) > + return -EINVAL; > + > + if (debug > 1) > + printk("%s: map %d+%d at %lx (pgoff %lx)\n", __FUNCTION__, > + index, count, vma->vm_start, vma->vm_pgoff); > + > + down_read(&priv->sem); > + map = gntdev_find_map_index(priv, index, count); > + if (!map) > + goto unlock_out; > + if (map->vma) > + goto unlock_out; > + if (priv->mm != vma->vm_mm) { > + printk("%s: Huh? Other mm?\n", __FUNCTION__); > + goto unlock_out; > + } > + > + vma->vm_ops = &gntdev_vmops; > + > + vma->vm_flags |= VM_RESERVED; > + vma->vm_flags |= VM_DONTCOPY; > + vma->vm_flags |= VM_DONTEXPAND; > VM_IO? > + > + vma->vm_private_data = map; > + map->vma = vma; > + > + map->flags = GNTMAP_host_map | GNTMAP_application_map | GNTMAP_contains_pte; > + if (!(vma->vm_flags & VM_WRITE)) > + map->flags |= GNTMAP_readonly; > + > + err = apply_to_page_range(vma->vm_mm, vma->vm_start, > + vma->vm_end - vma->vm_start, > + find_grant_ptes, map); > + if (err) { > + goto unlock_out; > + if (debug) > + printk("%s: find_grant_ptes() failure.\n", __FUNCTION__); > + } > + > + err = map_grant_pages(map); > + if (err) { > + goto unlock_out; > + if (debug) > + printk("%s: map_grant_pages() failure.\n", __FUNCTION__); > + } > + map->is_mapped = 1; > + > +unlock_out: > + up_read(&priv->sem); > + return err; > +} > + > +static const struct file_operations gntdev_fops = { > + .owner = THIS_MODULE, > + .open = gntdev_open, > + .release = gntdev_release, > + .mmap = gntdev_mmap, > + .unlocked_ioctl = gntdev_ioctl > +}; > + > +static struct miscdevice gntdev_miscdev = { > + .minor = MISC_DYNAMIC_MINOR, > + .name = "gntdev", > + .fops = &gntdev_fops, > +}; > + > +/* ------------------------------------------------------------------ */ > + > +static int __init gntdev_init(void) > +{ > + int err; > + > + if (!xen_domain()) > + return -ENODEV; > + > + err = misc_register(&gntdev_miscdev); > + if (err != 0) { > + printk(KERN_ERR "Could not register gntdev device\n"); > + return err; > + } > + return 0; > +} > + > +static void __exit gntdev_exit(void) > +{ > + misc_deregister(&gntdev_miscdev); > +} > + > +module_init(gntdev_init); > +module_exit(gntdev_exit); > + > +/* ------------------------------------------------------------------ */ > + > diff --git a/include/xen/gntdev.h b/include/xen/gntdev.h > new file mode 100644 > index 0000000..8bd1467 > --- /dev/null > +++ b/include/xen/gntdev.h > @@ -0,0 +1,119 @@ > +/****************************************************************************** > + * 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; > + uint32_t pad; > + /* OUT parameters */ > + /* The offset to be used on a subsequent call to mmap(). */ > + uint64_t index; > + /* Variable IN parameter. */ > + /* Array of grant references, of size @count. */ > + struct ioctl_gntdev_grant_ref refs[1]; > +}; > + > +/* > + * 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. */ > + uint64_t index; > + /* The number of pages to be unmapped. */ > + uint32_t count; > + uint32_t pad; > +}; > + > +/* > + * 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. */ > + uint64_t vaddr; > + /* OUT parameters */ > + /* The offset that was used in the initial mmap() operation. */ > + uint64_t offset; > + /* The number of pages mapped in the VM area that begins at @vaddr. */ > + uint32_t count; > + uint32_t pad; > +}; > + > +/* > + * Sets the maximum number of grants that may mapped at once by this gntdev > + * instance. > + * > + * N.B. This must be called before any other ioctl is performed on the device. > + */ > +#define IOCTL_GNTDEV_SET_MAX_GRANTS \ > +_IOC(_IOC_NONE, 'G', 3, sizeof(struct ioctl_gntdev_set_max_grants)) > +struct ioctl_gntdev_set_max_grants { > + /* IN parameter */ > + /* The maximum number of grants that may be mapped at once. */ > + uint32_t count; > +}; > + > +#endif /* __LINUX_PUBLIC_GNTDEV_H__ */ >