/****************************************************************************** * 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 <kraxel@redhat.com> * * 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 */ #undef DEBUG #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt #include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/miscdevice.h>#include <linux/fs.h>#include <linux/mm.h>#include <linux/mman.h>#include <linux/mmu_notifier.h>#include <linux/types.h>#include <linux/uaccess.h>#include <linux/sched.h>#include <linux/spinlock.h>#include <linux/slab.h>#include <linux/highmem.h> #include <xen/xen.h>#include <xen/grant_table.h>#include <xen/balloon.h>#include <xen/gntdev.h>#include <xen/events.h>#include <asm/xen/hypervisor.h>#include <asm/xen/hypercall.h>#include <asm/xen/page.h> MODULE_LICENSE("GPL");MODULE_AUTHOR("Derek G. Murray <Derek.Murray@cl.cam.ac.uk>, " "Gerd Hoffmann <kraxel@redhat.com>");MODULE_DESCRIPTION("User-space granted page access driver"); static int limit = 1024*1024;module_param(limit, int, 0644);MODULE_PARM_DESC(limit, "Maximum number of grants that may be mapped by " "the gntdev device"); static atomic_t pages_mapped = ATOMIC_INIT(0); static int use_ptemod;#define populate_freeable_maps use_ptemod struct gntdev_priv { /* maps with visible offsets in the file descriptor */ struct list_head maps; /* maps that are not visible; will be freed on munmap. * Only populated if populate_freeable_maps == 1 */ struct list_head freeable_maps; /* lock protects maps and freeable_maps */ spinlock_t lock; struct mm_struct *mm; struct mmu_notifier mn;}; struct unmap_notify { int flags; /* Address relative to the start of the grant_map */ int addr; int event;}; struct grant_map { struct list_head next; struct vm_area_struct *vma; int index; int count; int flags; atomic_t users; struct unmap_notify notify; struct ioctl_gntdev_grant_ref *grants; struct gnttab_map_grant_ref *map_ops; struct gnttab_unmap_grant_ref *unmap_ops; struct gnttab_map_grant_ref *kmap_ops; struct page **pages;}; static int unmap_grant_pages(struct grant_map *map, int offset, int pages); /* ------------------------------------------------------------------ */ static void gntdev_print_maps(struct gntdev_priv *priv, char *text, int text_index){#ifdef DEBUG struct grant_map *map; pr_debug("%s: maps list (priv %p)\n", __func__, priv); list_for_each_entry(map, &priv->maps, next) pr_debug(" index %2d, count %2d %s\n", map->index, map->count, map->index == text_index && text ? text : "");#endif} static void gntdev_free_map(struct grant_map *map){ if (map == NULL) return; if (map->pages) free_xenballooned_pages(map->count, map->pages); kfree(map->pages); kfree(map->grants); kfree(map->map_ops); kfree(map->unmap_ops); kfree(map->kmap_ops); kfree(map);} static struct grant_map *gntdev_alloc_map(struct gntdev_priv *priv, int count){ struct grant_map *add; int i; add = kzalloc(sizeof(struct grant_map), GFP_KERNEL); if (NULL == add) return NULL; add->grants = kcalloc(count, sizeof(add->grants[0]), GFP_KERNEL); add->map_ops = kcalloc(count, sizeof(add->map_ops[0]), GFP_KERNEL); add->unmap_ops = kcalloc(count, sizeof(add->unmap_ops[0]), GFP_KERNEL); add->kmap_ops = kcalloc(count, sizeof(add->kmap_ops[0]), GFP_KERNEL); add->pages = kcalloc(count, sizeof(add->pages[0]), GFP_KERNEL); if (NULL == add->grants || NULL == add->map_ops || NULL == add->unmap_ops || NULL == add->kmap_ops || NULL == add->pages) goto err; if (alloc_xenballooned_pages(count, add->pages, false /* lowmem */)) goto err; for (i = 0; i < count; i++) { add->map_ops[i].handle = -1; add->unmap_ops[i].handle = -1; add->kmap_ops[i].handle = -1; } add->index = 0; add->count = count; atomic_set(&add->users, 1); return add; err: gntdev_free_map(add); return NULL;} static void gntdev_add_map(struct gntdev_priv *priv, struct grant_map *add){ struct grant_map *map; 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: gntdev_print_maps(priv, "[new]", add->index);} 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 (count && map->count != count) continue; return map; } return NULL;} static void gntdev_put_map(struct gntdev_priv *priv, struct grant_map *map){ if (!map) return; if (!atomic_dec_and_test(&map->users)) return; atomic_sub(map->count, &pages_mapped); if (map->notify.flags & UNMAP_NOTIFY_SEND_EVENT) { notify_remote_via_evtchn(map->notify.event); evtchn_put(map->notify.event); } if (populate_freeable_maps && priv) { spin_lock(&priv->lock); list_del(&map->next); spin_unlock(&priv->lock); } if (map->pages && !use_ptemod) unmap_grant_pages(map, 0, map->count); gntdev_free_map(map);} /* ------------------------------------------------------------------ */ 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; int flags = map->flags | GNTMAP_application_map | GNTMAP_contains_pte; u64 pte_maddr; BUG_ON(pgnr >= map->count); pte_maddr = arbitrary_virt_to_machine(pte).maddr; gnttab_set_map_op(&map->map_ops[pgnr], pte_maddr, flags, map->grants[pgnr].ref, map->grants[pgnr].domid); gnttab_set_unmap_op(&map->unmap_ops[pgnr], pte_maddr, flags, -1 /* handle */); return 0;} static int map_grant_pages(struct grant_map *map){ int i, err = 0; if (!use_ptemod) { /* Note: it could already be mapped */ if (map->map_ops[0].handle != -1) return 0; for (i = 0; i < map->count; i++) { unsigned long addr = (unsigned long) pfn_to_kaddr(page_to_pfn(map->pages[i])); gnttab_set_map_op(&map->map_ops[i], addr, map->flags, map->grants[i].ref, map->grants[i].domid); gnttab_set_unmap_op(&map->unmap_ops[i], addr, map->flags, -1 /* handle */); } } else { /* * Setup the map_ops corresponding to the pte entries pointing * to the kernel linear addresses of the struct pages. * These ptes are completely different from the user ptes dealt * with find_grant_ptes. */ for (i = 0; i < map->count; i++) { unsigned long address = (unsigned long) pfn_to_kaddr(page_to_pfn(map->pages[i])); BUG_ON(PageHighMem(map->pages[i])); gnttab_set_map_op(&map->kmap_ops[i], address, map->flags | GNTMAP_host_map, map->grants[i].ref, map->grants[i].domid); } } pr_debug("map %d+%d\n", map->index, map->count); err = gnttab_map_refs(map->map_ops, use_ptemod ? map->kmap_ops : NULL, map->pages, map->count); if (err) return err; for (i = 0; i < map->count; i++) { if (map->map_ops[i].status) err = -EINVAL; else { BUG_ON(map->map_ops[i].handle == -1); map->unmap_ops[i].handle = map->map_ops[i].handle; pr_debug("map handle=%d\n", 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 (map->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) { int pgno = (map->notify.addr >> PAGE_SHIFT); if (pgno >= offset && pgno < offset + pages) { /* No need for kmap, pages are in lowmem */ uint8_t *tmp = pfn_to_kaddr(page_to_pfn(map->pages[pgno])); tmp[map->notify.addr & (PAGE_SIZE-1)] = 0; map->notify.flags &= ~UNMAP_NOTIFY_CLEAR_BYTE; } } err = gnttab_unmap_refs(map->unmap_ops + offset, use_ptemod ? map->kmap_ops + offset : NULL, map->pages + offset, pages); if (err) return err; for (i = 0; i < pages; i++) { if (map->unmap_ops[offset+i].status) err = -EINVAL; pr_debug("unmap handle=%d st=%d\n", map->unmap_ops[offset+i].handle, map->unmap_ops[offset+i].status); map->unmap_ops[offset+i].handle = -1; } return err;} static int unmap_grant_pages(struct grant_map *map, int offset, int pages){ int range, err = 0; pr_debug("unmap %d+%d [%d+%d]\n", map->index, map->count, offset, pages); /* It is possible the requested range will have a "hole" where we * already unmapped some of the grants. Only unmap valid ranges. */ while (pages && !err) { while (pages && map->unmap_ops[offset].handle == -1) { offset++; pages--; } range = 0; while (range < pages) { if (map->unmap_ops[offset+range].handle == -1) { range--; break; } range++; } err = __unmap_grant_pages(map, offset, range); offset += range; pages -= range; } return err;} /* ------------------------------------------------------------------ */ static void gntdev_vma_open(struct vm_area_struct *vma){ struct grant_map *map = vma->vm_private_data; pr_debug("gntdev_vma_open %p\n", vma); atomic_inc(&map->users);} static void gntdev_vma_close(struct vm_area_struct *vma){ struct grant_map *map = vma->vm_private_data; struct file *file = vma->vm_file; struct gntdev_priv *priv = file->private_data; pr_debug("gntdev_vma_close %p\n", vma); if (use_ptemod) { /* It is possible that an mmu notifier could be running * concurrently, so take priv->lock to ensure that the vma won't * vanishing during the unmap_grant_pages call, since we will * spin here until that completes. Such a concurrent call will * not do any unmapping, since that has been done prior to * closing the vma, but it may still iterate the unmap_ops list. */ spin_lock(&priv->lock); map->vma = NULL; spin_unlock(&priv->lock); } vma->vm_private_data = NULL; gntdev_put_map(priv, map);} static struct vm_operations_struct gntdev_vmops = { .open = gntdev_vma_open, .close = gntdev_vma_close,}; /* ------------------------------------------------------------------ */ static void unmap_if_in_range(struct grant_map *map, unsigned long start, unsigned long end){ unsigned long mstart, mend; int err; if (!map->vma) return; if (map->vma->vm_start >= end) return; if (map->vma->vm_end <= start) return; mstart = max(start, map->vma->vm_start); mend = min(end, map->vma->vm_end); pr_debug("map %d+%d (%lx %lx), range %lx %lx, mrange %lx %lx\n", 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); WARN_ON(err);} 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; spin_lock(&priv->lock); list_for_each_entry(map, &priv->maps, next) { unmap_if_in_range(map, start, end); } list_for_each_entry(map, &priv->freeable_maps, next) { unmap_if_in_range(map, start, end); } spin_unlock(&priv->lock);} 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; spin_lock(&priv->lock); list_for_each_entry(map, &priv->maps, next) { if (!map->vma) continue; pr_debug("map %d+%d (%lx %lx)\n", map->index, map->count, map->vma->vm_start, map->vma->vm_end); err = unmap_grant_pages(map, /* offset */ 0, map->count); WARN_ON(err); } list_for_each_entry(map, &priv->freeable_maps, next) { if (!map->vma) continue; pr_debug("map %d+%d (%lx %lx)\n", map->index, map->count, map->vma->vm_start, map->vma->vm_end); err = unmap_grant_pages(map, /* offset */ 0, map->count); WARN_ON(err); } spin_unlock(&priv->lock);} static 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; int ret = 0; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; INIT_LIST_HEAD(&priv->maps); INIT_LIST_HEAD(&priv->freeable_maps);Lines 1–500 of 867. Load more
/****************************************************************************** * 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 <kraxel@redhat.com> * * 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 */ #undef DEBUG #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/miscdevice.h> #include <linux/fs.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/mmu_notifier.h> #include <linux/types.h> #include <linux/uaccess.h> #include <linux/sched.h> #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/highmem.h> #include <xen/xen.h> #include <xen/grant_table.h> #include <xen/balloon.h> #include <xen/gntdev.h> #include <xen/events.h> #include <asm/xen/hypervisor.h> #include <asm/xen/hypercall.h> #include <asm/xen/page.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Derek G. Murray <Derek.Murray@cl.cam.ac.uk>, " "Gerd Hoffmann <kraxel@redhat.com>"); MODULE_DESCRIPTION("User-space granted page access driver"); static int limit = 1024*1024; module_param(limit, int, 0644); MODULE_PARM_DESC(limit, "Maximum number of grants that may be mapped by " "the gntdev device"); static atomic_t pages_mapped = ATOMIC_INIT(0); static int use_ptemod; #define populate_freeable_maps use_ptemod struct gntdev_priv { /* maps with visible offsets in the file descriptor */ struct list_head maps; /* maps that are not visible; will be freed on munmap. * Only populated if populate_freeable_maps == 1 */ struct list_head freeable_maps; /* lock protects maps and freeable_maps */ spinlock_t lock; struct mm_struct *mm; struct mmu_notifier mn; }; struct unmap_notify { int flags; /* Address relative to the start of the grant_map */ int addr; int event; }; struct grant_map { struct list_head next; struct vm_area_struct *vma; int index; int count; int flags; atomic_t users; struct unmap_notify notify; struct ioctl_gntdev_grant_ref *grants; struct gnttab_map_grant_ref *map_ops; struct gnttab_unmap_grant_ref *unmap_ops; struct gnttab_map_grant_ref *kmap_ops; struct page **pages; }; static int unmap_grant_pages(struct grant_map *map, int offset, int pages); /* ------------------------------------------------------------------ */ static void gntdev_print_maps(struct gntdev_priv *priv, char *text, int text_index) { #ifdef DEBUG struct grant_map *map; pr_debug("%s: maps list (priv %p)\n", __func__, priv); list_for_each_entry(map, &priv->maps, next) pr_debug(" index %2d, count %2d %s\n", map->index, map->count, map->index == text_index && text ? text : ""); #endif } static void gntdev_free_map(struct grant_map *map) { if (map == NULL) return; if (map->pages) free_xenballooned_pages(map->count, map->pages); kfree(map->pages); kfree(map->grants); kfree(map->map_ops); kfree(map->unmap_ops); kfree(map->kmap_ops); kfree(map); } static struct grant_map *gntdev_alloc_map(struct gntdev_priv *priv, int count) { struct grant_map *add; int i; add = kzalloc(sizeof(struct grant_map), GFP_KERNEL); if (NULL == add) return NULL; add->grants = kcalloc(count, sizeof(add->grants[0]), GFP_KERNEL); add->map_ops = kcalloc(count, sizeof(add->map_ops[0]), GFP_KERNEL); add->unmap_ops = kcalloc(count, sizeof(add->unmap_ops[0]), GFP_KERNEL); add->kmap_ops = kcalloc(count, sizeof(add->kmap_ops[0]), GFP_KERNEL); add->pages = kcalloc(count, sizeof(add->pages[0]), GFP_KERNEL); if (NULL == add->grants || NULL == add->map_ops || NULL == add->unmap_ops || NULL == add->kmap_ops || NULL == add->pages) goto err; if (alloc_xenballooned_pages(count, add->pages, false /* lowmem */)) goto err; for (i = 0; i < count; i++) { add->map_ops[i].handle = -1; add->unmap_ops[i].handle = -1; add->kmap_ops[i].handle = -1; } add->index = 0; add->count = count; atomic_set(&add->users, 1); return add; err: gntdev_free_map(add); return NULL; } static void gntdev_add_map(struct gntdev_priv *priv, struct grant_map *add) { struct grant_map *map; 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: gntdev_print_maps(priv, "[new]", add->index); } 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 (count && map->count != count) continue; return map; } return NULL; } static void gntdev_put_map(struct gntdev_priv *priv, struct grant_map *map) { if (!map) return; if (!atomic_dec_and_test(&map->users)) return; atomic_sub(map->count, &pages_mapped); if (map->notify.flags & UNMAP_NOTIFY_SEND_EVENT) { notify_remote_via_evtchn(map->notify.event); evtchn_put(map->notify.event); } if (populate_freeable_maps && priv) { spin_lock(&priv->lock); list_del(&map->next); spin_unlock(&priv->lock); } if (map->pages && !use_ptemod) unmap_grant_pages(map, 0, map->count); gntdev_free_map(map); } /* ------------------------------------------------------------------ */ 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; int flags = map->flags | GNTMAP_application_map | GNTMAP_contains_pte; u64 pte_maddr; BUG_ON(pgnr >= map->count); pte_maddr = arbitrary_virt_to_machine(pte).maddr; gnttab_set_map_op(&map->map_ops[pgnr], pte_maddr, flags, map->grants[pgnr].ref, map->grants[pgnr].domid); gnttab_set_unmap_op(&map->unmap_ops[pgnr], pte_maddr, flags, -1 /* handle */); return 0; } static int map_grant_pages(struct grant_map *map) { int i, err = 0; if (!use_ptemod) { /* Note: it could already be mapped */ if (map->map_ops[0].handle != -1) return 0; for (i = 0; i < map->count; i++) { unsigned long addr = (unsigned long) pfn_to_kaddr(page_to_pfn(map->pages[i])); gnttab_set_map_op(&map->map_ops[i], addr, map->flags, map->grants[i].ref, map->grants[i].domid); gnttab_set_unmap_op(&map->unmap_ops[i], addr, map->flags, -1 /* handle */); } } else { /* * Setup the map_ops corresponding to the pte entries pointing * to the kernel linear addresses of the struct pages. * These ptes are completely different from the user ptes dealt * with find_grant_ptes. */ for (i = 0; i < map->count; i++) { unsigned long address = (unsigned long) pfn_to_kaddr(page_to_pfn(map->pages[i])); BUG_ON(PageHighMem(map->pages[i])); gnttab_set_map_op(&map->kmap_ops[i], address, map->flags | GNTMAP_host_map, map->grants[i].ref, map->grants[i].domid); } } pr_debug("map %d+%d\n", map->index, map->count); err = gnttab_map_refs(map->map_ops, use_ptemod ? map->kmap_ops : NULL, map->pages, map->count); if (err) return err; for (i = 0; i < map->count; i++) { if (map->map_ops[i].status) err = -EINVAL; else { BUG_ON(map->map_ops[i].handle == -1); map->unmap_ops[i].handle = map->map_ops[i].handle; pr_debug("map handle=%d\n", 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 (map->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) { int pgno = (map->notify.addr >> PAGE_SHIFT); if (pgno >= offset && pgno < offset + pages) { /* No need for kmap, pages are in lowmem */ uint8_t *tmp = pfn_to_kaddr(page_to_pfn(map->pages[pgno])); tmp[map->notify.addr & (PAGE_SIZE-1)] = 0; map->notify.flags &= ~UNMAP_NOTIFY_CLEAR_BYTE; } } err = gnttab_unmap_refs(map->unmap_ops + offset, use_ptemod ? map->kmap_ops + offset : NULL, map->pages + offset, pages); if (err) return err; for (i = 0; i < pages; i++) { if (map->unmap_ops[offset+i].status) err = -EINVAL; pr_debug("unmap handle=%d st=%d\n", map->unmap_ops[offset+i].handle, map->unmap_ops[offset+i].status); map->unmap_ops[offset+i].handle = -1; } return err; } static int unmap_grant_pages(struct grant_map *map, int offset, int pages) { int range, err = 0; pr_debug("unmap %d+%d [%d+%d]\n", map->index, map->count, offset, pages); /* It is possible the requested range will have a "hole" where we * already unmapped some of the grants. Only unmap valid ranges. */ while (pages && !err) { while (pages && map->unmap_ops[offset].handle == -1) { offset++; pages--; } range = 0; while (range < pages) { if (map->unmap_ops[offset+range].handle == -1) { range--; break; } range++; } err = __unmap_grant_pages(map, offset, range); offset += range; pages -= range; } return err; } /* ------------------------------------------------------------------ */ static void gntdev_vma_open(struct vm_area_struct *vma) { struct grant_map *map = vma->vm_private_data; pr_debug("gntdev_vma_open %p\n", vma); atomic_inc(&map->users); } static void gntdev_vma_close(struct vm_area_struct *vma) { struct grant_map *map = vma->vm_private_data; struct file *file = vma->vm_file; struct gntdev_priv *priv = file->private_data; pr_debug("gntdev_vma_close %p\n", vma); if (use_ptemod) { /* It is possible that an mmu notifier could be running * concurrently, so take priv->lock to ensure that the vma won't * vanishing during the unmap_grant_pages call, since we will * spin here until that completes. Such a concurrent call will * not do any unmapping, since that has been done prior to * closing the vma, but it may still iterate the unmap_ops list. */ spin_lock(&priv->lock); map->vma = NULL; spin_unlock(&priv->lock); } vma->vm_private_data = NULL; gntdev_put_map(priv, map); } static struct vm_operations_struct gntdev_vmops = { .open = gntdev_vma_open, .close = gntdev_vma_close, }; /* ------------------------------------------------------------------ */ static void unmap_if_in_range(struct grant_map *map, unsigned long start, unsigned long end) { unsigned long mstart, mend; int err; if (!map->vma) return; if (map->vma->vm_start >= end) return; if (map->vma->vm_end <= start) return; mstart = max(start, map->vma->vm_start); mend = min(end, map->vma->vm_end); pr_debug("map %d+%d (%lx %lx), range %lx %lx, mrange %lx %lx\n", 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); WARN_ON(err); } 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; spin_lock(&priv->lock); list_for_each_entry(map, &priv->maps, next) { unmap_if_in_range(map, start, end); } list_for_each_entry(map, &priv->freeable_maps, next) { unmap_if_in_range(map, start, end); } spin_unlock(&priv->lock); } 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; spin_lock(&priv->lock); list_for_each_entry(map, &priv->maps, next) { if (!map->vma) continue; pr_debug("map %d+%d (%lx %lx)\n", map->index, map->count, map->vma->vm_start, map->vma->vm_end); err = unmap_grant_pages(map, /* offset */ 0, map->count); WARN_ON(err); } list_for_each_entry(map, &priv->freeable_maps, next) { if (!map->vma) continue; pr_debug("map %d+%d (%lx %lx)\n", map->index, map->count, map->vma->vm_start, map->vma->vm_end); err = unmap_grant_pages(map, /* offset */ 0, map->count); WARN_ON(err); } spin_unlock(&priv->lock); } static 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; int ret = 0; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; INIT_LIST_HEAD(&priv->maps); INIT_LIST_HEAD(&priv->freeable_maps);