diff options
Diffstat (limited to 'drivers/xen/gntdev.c')
-rw-r--r-- | drivers/xen/gntdev.c | 61 |
1 files changed, 60 insertions, 1 deletions
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index bcaf797216d1..9694a1a8b2e2 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include <xen/xen.h> | 37 | #include <xen/xen.h> |
38 | #include <xen/grant_table.h> | 38 | #include <xen/grant_table.h> |
39 | #include <xen/gntdev.h> | 39 | #include <xen/gntdev.h> |
40 | #include <xen/events.h> | ||
40 | #include <asm/xen/hypervisor.h> | 41 | #include <asm/xen/hypervisor.h> |
41 | #include <asm/xen/hypercall.h> | 42 | #include <asm/xen/hypercall.h> |
42 | #include <asm/xen/page.h> | 43 | #include <asm/xen/page.h> |
@@ -63,6 +64,13 @@ struct gntdev_priv { | |||
63 | struct mmu_notifier mn; | 64 | struct mmu_notifier mn; |
64 | }; | 65 | }; |
65 | 66 | ||
67 | struct unmap_notify { | ||
68 | int flags; | ||
69 | /* Address relative to the start of the grant_map */ | ||
70 | int addr; | ||
71 | int event; | ||
72 | }; | ||
73 | |||
66 | struct grant_map { | 74 | struct grant_map { |
67 | struct list_head next; | 75 | struct list_head next; |
68 | struct vm_area_struct *vma; | 76 | struct vm_area_struct *vma; |
@@ -71,6 +79,7 @@ struct grant_map { | |||
71 | int flags; | 79 | int flags; |
72 | int is_mapped; | 80 | int is_mapped; |
73 | atomic_t users; | 81 | atomic_t users; |
82 | struct unmap_notify notify; | ||
74 | struct ioctl_gntdev_grant_ref *grants; | 83 | struct ioctl_gntdev_grant_ref *grants; |
75 | struct gnttab_map_grant_ref *map_ops; | 84 | struct gnttab_map_grant_ref *map_ops; |
76 | struct gnttab_unmap_grant_ref *unmap_ops; | 85 | struct gnttab_unmap_grant_ref *unmap_ops; |
@@ -165,7 +174,7 @@ static struct grant_map *gntdev_find_map_index(struct gntdev_priv *priv, | |||
165 | list_for_each_entry(map, &priv->maps, next) { | 174 | list_for_each_entry(map, &priv->maps, next) { |
166 | if (map->index != index) | 175 | if (map->index != index) |
167 | continue; | 176 | continue; |
168 | if (map->count != count) | 177 | if (count && map->count != count) |
169 | continue; | 178 | continue; |
170 | return map; | 179 | return map; |
171 | } | 180 | } |
@@ -184,6 +193,10 @@ static void gntdev_put_map(struct grant_map *map) | |||
184 | 193 | ||
185 | atomic_sub(map->count, &pages_mapped); | 194 | atomic_sub(map->count, &pages_mapped); |
186 | 195 | ||
196 | if (map->notify.flags & UNMAP_NOTIFY_SEND_EVENT) { | ||
197 | notify_remote_via_evtchn(map->notify.event); | ||
198 | } | ||
199 | |||
187 | if (map->pages) { | 200 | if (map->pages) { |
188 | if (!use_ptemod) | 201 | if (!use_ptemod) |
189 | unmap_grant_pages(map, 0, map->count); | 202 | unmap_grant_pages(map, 0, map->count); |
@@ -274,6 +287,16 @@ static int unmap_grant_pages(struct grant_map *map, int offset, int pages) | |||
274 | { | 287 | { |
275 | int i, err = 0; | 288 | int i, err = 0; |
276 | 289 | ||
290 | if (map->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) { | ||
291 | int pgno = (map->notify.addr >> PAGE_SHIFT); | ||
292 | if (pgno >= offset && pgno < offset + pages) { | ||
293 | uint8_t *tmp = kmap(map->pages[pgno]); | ||
294 | tmp[map->notify.addr & (PAGE_SIZE-1)] = 0; | ||
295 | kunmap(map->pages[pgno]); | ||
296 | map->notify.flags &= ~UNMAP_NOTIFY_CLEAR_BYTE; | ||
297 | } | ||
298 | } | ||
299 | |||
277 | pr_debug("map %d+%d [%d+%d]\n", map->index, map->count, offset, pages); | 300 | pr_debug("map %d+%d [%d+%d]\n", map->index, map->count, offset, pages); |
278 | err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages, pages); | 301 | err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages, pages); |
279 | if (err) | 302 | if (err) |
@@ -519,6 +542,39 @@ static long gntdev_ioctl_get_offset_for_vaddr(struct gntdev_priv *priv, | |||
519 | return 0; | 542 | return 0; |
520 | } | 543 | } |
521 | 544 | ||
545 | static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u) | ||
546 | { | ||
547 | struct ioctl_gntdev_unmap_notify op; | ||
548 | struct grant_map *map; | ||
549 | int rc; | ||
550 | |||
551 | if (copy_from_user(&op, u, sizeof(op))) | ||
552 | return -EFAULT; | ||
553 | |||
554 | if (op.action & ~(UNMAP_NOTIFY_CLEAR_BYTE|UNMAP_NOTIFY_SEND_EVENT)) | ||
555 | return -EINVAL; | ||
556 | |||
557 | spin_lock(&priv->lock); | ||
558 | |||
559 | list_for_each_entry(map, &priv->maps, next) { | ||
560 | uint64_t begin = map->index << PAGE_SHIFT; | ||
561 | uint64_t end = (map->index + map->count) << PAGE_SHIFT; | ||
562 | if (op.index >= begin && op.index < end) | ||
563 | goto found; | ||
564 | } | ||
565 | rc = -ENOENT; | ||
566 | goto unlock_out; | ||
567 | |||
568 | found: | ||
569 | map->notify.flags = op.action; | ||
570 | map->notify.addr = op.index - (map->index << PAGE_SHIFT); | ||
571 | map->notify.event = op.event_channel_port; | ||
572 | rc = 0; | ||
573 | unlock_out: | ||
574 | spin_unlock(&priv->lock); | ||
575 | return rc; | ||
576 | } | ||
577 | |||
522 | static long gntdev_ioctl(struct file *flip, | 578 | static long gntdev_ioctl(struct file *flip, |
523 | unsigned int cmd, unsigned long arg) | 579 | unsigned int cmd, unsigned long arg) |
524 | { | 580 | { |
@@ -535,6 +591,9 @@ static long gntdev_ioctl(struct file *flip, | |||
535 | case IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR: | 591 | case IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR: |
536 | return gntdev_ioctl_get_offset_for_vaddr(priv, ptr); | 592 | return gntdev_ioctl_get_offset_for_vaddr(priv, ptr); |
537 | 593 | ||
594 | case IOCTL_GNTDEV_SET_UNMAP_NOTIFY: | ||
595 | return gntdev_ioctl_notify(priv, ptr); | ||
596 | |||
538 | default: | 597 | default: |
539 | pr_debug("priv %p, unknown cmd %x\n", priv, cmd); | 598 | pr_debug("priv %p, unknown cmd %x\n", priv, cmd); |
540 | return -ENOIOCTLCMD; | 599 | return -ENOIOCTLCMD; |