diff options
author | Daniel De Graaf <dgdegra@tycho.nsa.gov> | 2011-02-03 12:19:04 -0500 |
---|---|---|
committer | Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> | 2011-02-14 14:16:17 -0500 |
commit | bdc612dc6903c4ea06e40d02f84ad5e25d93459d (patch) | |
tree | f7f3e3272b21ae3c6015e86f9c1f7be31534704d /drivers/xen | |
parent | dd3140588d9551235ebc2a0dacdca098e7677573 (diff) |
xen/gntalloc,gntdev: Add unmap notify ioctl
This ioctl allows the users of a shared page to be notified when
the other end exits abnormally.
[v2: updated description in structs]
Signed-off-by: Daniel De Graaf <dgdegra@tycho.nsa.gov>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Diffstat (limited to 'drivers/xen')
-rw-r--r-- | drivers/xen/gntalloc.c | 59 | ||||
-rw-r--r-- | drivers/xen/gntdev.c | 61 |
2 files changed, 119 insertions, 1 deletions
diff --git a/drivers/xen/gntalloc.c b/drivers/xen/gntalloc.c index d06bf2b4cd07..a7ffdfe19fc9 100644 --- a/drivers/xen/gntalloc.c +++ b/drivers/xen/gntalloc.c | |||
@@ -60,11 +60,13 @@ | |||
60 | #include <linux/uaccess.h> | 60 | #include <linux/uaccess.h> |
61 | #include <linux/types.h> | 61 | #include <linux/types.h> |
62 | #include <linux/list.h> | 62 | #include <linux/list.h> |
63 | #include <linux/highmem.h> | ||
63 | 64 | ||
64 | #include <xen/xen.h> | 65 | #include <xen/xen.h> |
65 | #include <xen/page.h> | 66 | #include <xen/page.h> |
66 | #include <xen/grant_table.h> | 67 | #include <xen/grant_table.h> |
67 | #include <xen/gntalloc.h> | 68 | #include <xen/gntalloc.h> |
69 | #include <xen/events.h> | ||
68 | 70 | ||
69 | static int limit = 1024; | 71 | static int limit = 1024; |
70 | module_param(limit, int, 0644); | 72 | module_param(limit, int, 0644); |
@@ -75,6 +77,12 @@ static LIST_HEAD(gref_list); | |||
75 | static DEFINE_SPINLOCK(gref_lock); | 77 | static DEFINE_SPINLOCK(gref_lock); |
76 | static int gref_size; | 78 | static int gref_size; |
77 | 79 | ||
80 | struct notify_info { | ||
81 | uint16_t pgoff:12; /* Bits 0-11: Offset of the byte to clear */ | ||
82 | uint16_t flags:2; /* Bits 12-13: Unmap notification flags */ | ||
83 | int event; /* Port (event channel) to notify */ | ||
84 | }; | ||
85 | |||
78 | /* Metadata on a grant reference. */ | 86 | /* Metadata on a grant reference. */ |
79 | struct gntalloc_gref { | 87 | struct gntalloc_gref { |
80 | struct list_head next_gref; /* list entry gref_list */ | 88 | struct list_head next_gref; /* list entry gref_list */ |
@@ -83,6 +91,7 @@ struct gntalloc_gref { | |||
83 | uint64_t file_index; /* File offset for mmap() */ | 91 | uint64_t file_index; /* File offset for mmap() */ |
84 | unsigned int users; /* Use count - when zero, waiting on Xen */ | 92 | unsigned int users; /* Use count - when zero, waiting on Xen */ |
85 | grant_ref_t gref_id; /* The grant reference number */ | 93 | grant_ref_t gref_id; /* The grant reference number */ |
94 | struct notify_info notify; /* Unmap notification */ | ||
86 | }; | 95 | }; |
87 | 96 | ||
88 | struct gntalloc_file_private_data { | 97 | struct gntalloc_file_private_data { |
@@ -164,6 +173,16 @@ undo: | |||
164 | 173 | ||
165 | static void __del_gref(struct gntalloc_gref *gref) | 174 | static void __del_gref(struct gntalloc_gref *gref) |
166 | { | 175 | { |
176 | if (gref->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) { | ||
177 | uint8_t *tmp = kmap(gref->page); | ||
178 | tmp[gref->notify.pgoff] = 0; | ||
179 | kunmap(gref->page); | ||
180 | } | ||
181 | if (gref->notify.flags & UNMAP_NOTIFY_SEND_EVENT) | ||
182 | notify_remote_via_evtchn(gref->notify.event); | ||
183 | |||
184 | gref->notify.flags = 0; | ||
185 | |||
167 | if (gref->gref_id > 0) { | 186 | if (gref->gref_id > 0) { |
168 | if (gnttab_query_foreign_access(gref->gref_id)) | 187 | if (gnttab_query_foreign_access(gref->gref_id)) |
169 | return; | 188 | return; |
@@ -349,6 +368,43 @@ dealloc_grant_out: | |||
349 | return rc; | 368 | return rc; |
350 | } | 369 | } |
351 | 370 | ||
371 | static long gntalloc_ioctl_unmap_notify(struct gntalloc_file_private_data *priv, | ||
372 | void __user *arg) | ||
373 | { | ||
374 | struct ioctl_gntalloc_unmap_notify op; | ||
375 | struct gntalloc_gref *gref; | ||
376 | uint64_t index; | ||
377 | int pgoff; | ||
378 | int rc; | ||
379 | |||
380 | if (copy_from_user(&op, arg, sizeof(op))) | ||
381 | return -EFAULT; | ||
382 | |||
383 | index = op.index & ~(PAGE_SIZE - 1); | ||
384 | pgoff = op.index & (PAGE_SIZE - 1); | ||
385 | |||
386 | spin_lock(&gref_lock); | ||
387 | |||
388 | gref = find_grefs(priv, index, 1); | ||
389 | if (!gref) { | ||
390 | rc = -ENOENT; | ||
391 | goto unlock_out; | ||
392 | } | ||
393 | |||
394 | if (op.action & ~(UNMAP_NOTIFY_CLEAR_BYTE|UNMAP_NOTIFY_SEND_EVENT)) { | ||
395 | rc = -EINVAL; | ||
396 | goto unlock_out; | ||
397 | } | ||
398 | |||
399 | gref->notify.flags = op.action; | ||
400 | gref->notify.pgoff = pgoff; | ||
401 | gref->notify.event = op.event_channel_port; | ||
402 | rc = 0; | ||
403 | unlock_out: | ||
404 | spin_unlock(&gref_lock); | ||
405 | return rc; | ||
406 | } | ||
407 | |||
352 | static long gntalloc_ioctl(struct file *filp, unsigned int cmd, | 408 | static long gntalloc_ioctl(struct file *filp, unsigned int cmd, |
353 | unsigned long arg) | 409 | unsigned long arg) |
354 | { | 410 | { |
@@ -361,6 +417,9 @@ static long gntalloc_ioctl(struct file *filp, unsigned int cmd, | |||
361 | case IOCTL_GNTALLOC_DEALLOC_GREF: | 417 | case IOCTL_GNTALLOC_DEALLOC_GREF: |
362 | return gntalloc_ioctl_dealloc(priv, (void __user *)arg); | 418 | return gntalloc_ioctl_dealloc(priv, (void __user *)arg); |
363 | 419 | ||
420 | case IOCTL_GNTALLOC_SET_UNMAP_NOTIFY: | ||
421 | return gntalloc_ioctl_unmap_notify(priv, (void __user *)arg); | ||
422 | |||
364 | default: | 423 | default: |
365 | return -ENOIOCTLCMD; | 424 | return -ENOIOCTLCMD; |
366 | } | 425 | } |
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; |