Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Daniel De Graaf and committed by
Konrad Rzeszutek Wilk
bdc612dc dd314058

+182 -1
+59
drivers/xen/gntalloc.c
··· 60 60 #include <linux/uaccess.h> 61 61 #include <linux/types.h> 62 62 #include <linux/list.h> 63 + #include <linux/highmem.h> 63 64 64 65 #include <xen/xen.h> 65 66 #include <xen/page.h> 66 67 #include <xen/grant_table.h> 67 68 #include <xen/gntalloc.h> 69 + #include <xen/events.h> 68 70 69 71 static int limit = 1024; 70 72 module_param(limit, int, 0644); ··· 77 75 static DEFINE_SPINLOCK(gref_lock); 78 76 static int gref_size; 79 77 78 + struct notify_info { 79 + uint16_t pgoff:12; /* Bits 0-11: Offset of the byte to clear */ 80 + uint16_t flags:2; /* Bits 12-13: Unmap notification flags */ 81 + int event; /* Port (event channel) to notify */ 82 + }; 83 + 80 84 /* Metadata on a grant reference. */ 81 85 struct gntalloc_gref { 82 86 struct list_head next_gref; /* list entry gref_list */ ··· 91 83 uint64_t file_index; /* File offset for mmap() */ 92 84 unsigned int users; /* Use count - when zero, waiting on Xen */ 93 85 grant_ref_t gref_id; /* The grant reference number */ 86 + struct notify_info notify; /* Unmap notification */ 94 87 }; 95 88 96 89 struct gntalloc_file_private_data { ··· 173 164 174 165 static void __del_gref(struct gntalloc_gref *gref) 175 166 { 167 + if (gref->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) { 168 + uint8_t *tmp = kmap(gref->page); 169 + tmp[gref->notify.pgoff] = 0; 170 + kunmap(gref->page); 171 + } 172 + if (gref->notify.flags & UNMAP_NOTIFY_SEND_EVENT) 173 + notify_remote_via_evtchn(gref->notify.event); 174 + 175 + gref->notify.flags = 0; 176 + 176 177 if (gref->gref_id > 0) { 177 178 if (gnttab_query_foreign_access(gref->gref_id)) 178 179 return; ··· 368 349 return rc; 369 350 } 370 351 352 + static long gntalloc_ioctl_unmap_notify(struct gntalloc_file_private_data *priv, 353 + void __user *arg) 354 + { 355 + struct ioctl_gntalloc_unmap_notify op; 356 + struct gntalloc_gref *gref; 357 + uint64_t index; 358 + int pgoff; 359 + int rc; 360 + 361 + if (copy_from_user(&op, arg, sizeof(op))) 362 + return -EFAULT; 363 + 364 + index = op.index & ~(PAGE_SIZE - 1); 365 + pgoff = op.index & (PAGE_SIZE - 1); 366 + 367 + spin_lock(&gref_lock); 368 + 369 + gref = find_grefs(priv, index, 1); 370 + if (!gref) { 371 + rc = -ENOENT; 372 + goto unlock_out; 373 + } 374 + 375 + if (op.action & ~(UNMAP_NOTIFY_CLEAR_BYTE|UNMAP_NOTIFY_SEND_EVENT)) { 376 + rc = -EINVAL; 377 + goto unlock_out; 378 + } 379 + 380 + gref->notify.flags = op.action; 381 + gref->notify.pgoff = pgoff; 382 + gref->notify.event = op.event_channel_port; 383 + rc = 0; 384 + unlock_out: 385 + spin_unlock(&gref_lock); 386 + return rc; 387 + } 388 + 371 389 static long gntalloc_ioctl(struct file *filp, unsigned int cmd, 372 390 unsigned long arg) 373 391 { ··· 416 360 417 361 case IOCTL_GNTALLOC_DEALLOC_GREF: 418 362 return gntalloc_ioctl_dealloc(priv, (void __user *)arg); 363 + 364 + case IOCTL_GNTALLOC_SET_UNMAP_NOTIFY: 365 + return gntalloc_ioctl_unmap_notify(priv, (void __user *)arg); 419 366 420 367 default: 421 368 return -ENOIOCTLCMD;
+60 -1
drivers/xen/gntdev.c
··· 37 37 #include <xen/xen.h> 38 38 #include <xen/grant_table.h> 39 39 #include <xen/gntdev.h> 40 + #include <xen/events.h> 40 41 #include <asm/xen/hypervisor.h> 41 42 #include <asm/xen/hypercall.h> 42 43 #include <asm/xen/page.h> ··· 64 63 struct mmu_notifier mn; 65 64 }; 66 65 66 + struct unmap_notify { 67 + int flags; 68 + /* Address relative to the start of the grant_map */ 69 + int addr; 70 + int event; 71 + }; 72 + 67 73 struct grant_map { 68 74 struct list_head next; 69 75 struct vm_area_struct *vma; ··· 79 71 int flags; 80 72 int is_mapped; 81 73 atomic_t users; 74 + struct unmap_notify notify; 82 75 struct ioctl_gntdev_grant_ref *grants; 83 76 struct gnttab_map_grant_ref *map_ops; 84 77 struct gnttab_unmap_grant_ref *unmap_ops; ··· 174 165 list_for_each_entry(map, &priv->maps, next) { 175 166 if (map->index != index) 176 167 continue; 177 - if (map->count != count) 168 + if (count && map->count != count) 178 169 continue; 179 170 return map; 180 171 } ··· 192 183 return; 193 184 194 185 atomic_sub(map->count, &pages_mapped); 186 + 187 + if (map->notify.flags & UNMAP_NOTIFY_SEND_EVENT) { 188 + notify_remote_via_evtchn(map->notify.event); 189 + } 195 190 196 191 if (map->pages) { 197 192 if (!use_ptemod) ··· 286 273 static int unmap_grant_pages(struct grant_map *map, int offset, int pages) 287 274 { 288 275 int i, err = 0; 276 + 277 + if (map->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) { 278 + int pgno = (map->notify.addr >> PAGE_SHIFT); 279 + if (pgno >= offset && pgno < offset + pages) { 280 + uint8_t *tmp = kmap(map->pages[pgno]); 281 + tmp[map->notify.addr & (PAGE_SIZE-1)] = 0; 282 + kunmap(map->pages[pgno]); 283 + map->notify.flags &= ~UNMAP_NOTIFY_CLEAR_BYTE; 284 + } 285 + } 289 286 290 287 pr_debug("map %d+%d [%d+%d]\n", map->index, map->count, offset, pages); 291 288 err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages, pages); ··· 542 519 return 0; 543 520 } 544 521 522 + static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u) 523 + { 524 + struct ioctl_gntdev_unmap_notify op; 525 + struct grant_map *map; 526 + int rc; 527 + 528 + if (copy_from_user(&op, u, sizeof(op))) 529 + return -EFAULT; 530 + 531 + if (op.action & ~(UNMAP_NOTIFY_CLEAR_BYTE|UNMAP_NOTIFY_SEND_EVENT)) 532 + return -EINVAL; 533 + 534 + spin_lock(&priv->lock); 535 + 536 + list_for_each_entry(map, &priv->maps, next) { 537 + uint64_t begin = map->index << PAGE_SHIFT; 538 + uint64_t end = (map->index + map->count) << PAGE_SHIFT; 539 + if (op.index >= begin && op.index < end) 540 + goto found; 541 + } 542 + rc = -ENOENT; 543 + goto unlock_out; 544 + 545 + found: 546 + map->notify.flags = op.action; 547 + map->notify.addr = op.index - (map->index << PAGE_SHIFT); 548 + map->notify.event = op.event_channel_port; 549 + rc = 0; 550 + unlock_out: 551 + spin_unlock(&priv->lock); 552 + return rc; 553 + } 554 + 545 555 static long gntdev_ioctl(struct file *flip, 546 556 unsigned int cmd, unsigned long arg) 547 557 { ··· 590 534 591 535 case IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR: 592 536 return gntdev_ioctl_get_offset_for_vaddr(priv, ptr); 537 + 538 + case IOCTL_GNTDEV_SET_UNMAP_NOTIFY: 539 + return gntdev_ioctl_notify(priv, ptr); 593 540 594 541 default: 595 542 pr_debug("priv %p, unknown cmd %x\n", priv, cmd);
+32
include/xen/gntalloc.h
··· 47 47 /* Number of references to unmap */ 48 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)) 63 + struct 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 82 #endif /* __LINUX_PUBLIC_GNTALLOC_H__ */
+31
include/xen/gntdev.h
··· 116 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)) 131 + struct 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 150 #endif /* __LINUX_PUBLIC_GNTDEV_H__ */