aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel De Graaf <dgdegra@tycho.nsa.gov>2011-02-03 12:19:04 -0500
committerKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>2011-02-14 14:16:17 -0500
commitbdc612dc6903c4ea06e40d02f84ad5e25d93459d (patch)
treef7f3e3272b21ae3c6015e86f9c1f7be31534704d
parentdd3140588d9551235ebc2a0dacdca098e7677573 (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>
-rw-r--r--drivers/xen/gntalloc.c59
-rw-r--r--drivers/xen/gntdev.c61
-rw-r--r--include/xen/gntalloc.h32
-rw-r--r--include/xen/gntdev.h31
4 files changed, 182 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
69static int limit = 1024; 71static int limit = 1024;
70module_param(limit, int, 0644); 72module_param(limit, int, 0644);
@@ -75,6 +77,12 @@ static LIST_HEAD(gref_list);
75static DEFINE_SPINLOCK(gref_lock); 77static DEFINE_SPINLOCK(gref_lock);
76static int gref_size; 78static int gref_size;
77 79
80struct 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. */
79struct gntalloc_gref { 87struct 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
88struct gntalloc_file_private_data { 97struct gntalloc_file_private_data {
@@ -164,6 +173,16 @@ undo:
164 173
165static void __del_gref(struct gntalloc_gref *gref) 174static 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
371static 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
352static long gntalloc_ioctl(struct file *filp, unsigned int cmd, 408static 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
67struct unmap_notify {
68 int flags;
69 /* Address relative to the start of the grant_map */
70 int addr;
71 int event;
72};
73
66struct grant_map { 74struct 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
545static 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
522static long gntdev_ioctl(struct file *flip, 578static 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;
diff --git a/include/xen/gntalloc.h b/include/xen/gntalloc.h
index bc3b85e8bff7..76bd58065f4f 100644
--- a/include/xen/gntalloc.h
+++ b/include/xen/gntalloc.h
@@ -47,4 +47,36 @@ struct ioctl_gntalloc_dealloc_gref {
47 /* Number of references to unmap */ 47 /* Number of references to unmap */
48 uint32_t count; 48 uint32_t count;
49}; 49};
50
51/*
52 * Sets up an unmap notification within the page, so that the other side can do
53 * cleanup if this side crashes. Required to implement cross-domain robust
54 * mutexes or close notification on communication channels.
55 *
56 * Each mapped page only supports one notification; multiple calls referring to
57 * the same page overwrite the previous notification. You must clear the
58 * notification prior to the IOCTL_GNTALLOC_DEALLOC_GREF if you do not want it
59 * to occur.
60 */
61#define IOCTL_GNTALLOC_SET_UNMAP_NOTIFY \
62_IOC(_IOC_NONE, 'G', 7, sizeof(struct ioctl_gntalloc_unmap_notify))
63struct ioctl_gntalloc_unmap_notify {
64 /* IN parameters */
65 /* Offset in the file descriptor for a byte within the page (same as
66 * used in mmap). If using UNMAP_NOTIFY_CLEAR_BYTE, this is the byte to
67 * be cleared. Otherwise, it can be any byte in the page whose
68 * notification we are adjusting.
69 */
70 uint64_t index;
71 /* Action(s) to take on unmap */
72 uint32_t action;
73 /* Event channel to notify */
74 uint32_t event_channel_port;
75};
76
77/* Clear (set to zero) the byte specified by index */
78#define UNMAP_NOTIFY_CLEAR_BYTE 0x1
79/* Send an interrupt on the indicated event channel */
80#define UNMAP_NOTIFY_SEND_EVENT 0x2
81
50#endif /* __LINUX_PUBLIC_GNTALLOC_H__ */ 82#endif /* __LINUX_PUBLIC_GNTALLOC_H__ */
diff --git a/include/xen/gntdev.h b/include/xen/gntdev.h
index eb23f4188f5a..5304bd3c84c5 100644
--- a/include/xen/gntdev.h
+++ b/include/xen/gntdev.h
@@ -116,4 +116,35 @@ struct ioctl_gntdev_set_max_grants {
116 uint32_t count; 116 uint32_t count;
117}; 117};
118 118
119/*
120 * Sets up an unmap notification within the page, so that the other side can do
121 * cleanup if this side crashes. Required to implement cross-domain robust
122 * mutexes or close notification on communication channels.
123 *
124 * Each mapped page only supports one notification; multiple calls referring to
125 * the same page overwrite the previous notification. You must clear the
126 * notification prior to the IOCTL_GNTALLOC_DEALLOC_GREF if you do not want it
127 * to occur.
128 */
129#define IOCTL_GNTDEV_SET_UNMAP_NOTIFY \
130_IOC(_IOC_NONE, 'G', 7, sizeof(struct ioctl_gntdev_unmap_notify))
131struct ioctl_gntdev_unmap_notify {
132 /* IN parameters */
133 /* Offset in the file descriptor for a byte within the page (same as
134 * used in mmap). If using UNMAP_NOTIFY_CLEAR_BYTE, this is the byte to
135 * be cleared. Otherwise, it can be any byte in the page whose
136 * notification we are adjusting.
137 */
138 uint64_t index;
139 /* Action(s) to take on unmap */
140 uint32_t action;
141 /* Event channel to notify */
142 uint32_t event_channel_port;
143};
144
145/* Clear (set to zero) the byte specified by index */
146#define UNMAP_NOTIFY_CLEAR_BYTE 0x1
147/* Send an interrupt on the indicated event channel */
148#define UNMAP_NOTIFY_SEND_EVENT 0x2
149
119#endif /* __LINUX_PUBLIC_GNTDEV_H__ */ 150#endif /* __LINUX_PUBLIC_GNTDEV_H__ */