aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen/gntdev.c
diff options
context:
space:
mode:
authorDaniel De Graaf <dgdegra@tycho.nsa.gov>2013-01-02 17:57:12 -0500
committerKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>2013-01-15 16:01:30 -0500
commit16a1d0225e22e4e273e6b60a21db95decde666c2 (patch)
tree4a640870d7121933f8a94d0fbe7e5fe7014e1bf2 /drivers/xen/gntdev.c
parent2512f298cb9886e06938e761c9e924c8448d9ab8 (diff)
xen/gntdev: correctly unmap unlinked maps in mmu notifier
If gntdev_ioctl_unmap_grant_ref is called on a range before unmapping it, the entry is removed from priv->maps and the later call to mn_invl_range_start won't find it to do the unmapping. Fix this by creating another list of freeable maps that the mmu notifier can search and use to unmap grants. 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/gntdev.c')
-rw-r--r--drivers/xen/gntdev.c92
1 files changed, 63 insertions, 29 deletions
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index cca62cc8549b..9be3e5e46d1f 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -56,10 +56,15 @@ MODULE_PARM_DESC(limit, "Maximum number of grants that may be mapped by "
56static atomic_t pages_mapped = ATOMIC_INIT(0); 56static atomic_t pages_mapped = ATOMIC_INIT(0);
57 57
58static int use_ptemod; 58static int use_ptemod;
59#define populate_freeable_maps use_ptemod
59 60
60struct gntdev_priv { 61struct gntdev_priv {
62 /* maps with visible offsets in the file descriptor */
61 struct list_head maps; 63 struct list_head maps;
62 /* lock protects maps from concurrent changes */ 64 /* maps that are not visible; will be freed on munmap.
65 * Only populated if populate_freeable_maps == 1 */
66 struct list_head freeable_maps;
67 /* lock protects maps and freeable_maps */
63 spinlock_t lock; 68 spinlock_t lock;
64 struct mm_struct *mm; 69 struct mm_struct *mm;
65 struct mmu_notifier mn; 70 struct mmu_notifier mn;
@@ -193,7 +198,7 @@ static struct grant_map *gntdev_find_map_index(struct gntdev_priv *priv,
193 return NULL; 198 return NULL;
194} 199}
195 200
196static void gntdev_put_map(struct grant_map *map) 201static void gntdev_put_map(struct gntdev_priv *priv, struct grant_map *map)
197{ 202{
198 if (!map) 203 if (!map)
199 return; 204 return;
@@ -208,6 +213,12 @@ static void gntdev_put_map(struct grant_map *map)
208 evtchn_put(map->notify.event); 213 evtchn_put(map->notify.event);
209 } 214 }
210 215
216 if (populate_freeable_maps && priv) {
217 spin_lock(&priv->lock);
218 list_del(&map->next);
219 spin_unlock(&priv->lock);
220 }
221
211 if (map->pages && !use_ptemod) 222 if (map->pages && !use_ptemod)
212 unmap_grant_pages(map, 0, map->count); 223 unmap_grant_pages(map, 0, map->count);
213 gntdev_free_map(map); 224 gntdev_free_map(map);
@@ -376,11 +387,11 @@ static void gntdev_vma_open(struct vm_area_struct *vma)
376static void gntdev_vma_close(struct vm_area_struct *vma) 387static void gntdev_vma_close(struct vm_area_struct *vma)
377{ 388{
378 struct grant_map *map = vma->vm_private_data; 389 struct grant_map *map = vma->vm_private_data;
390 struct file *file = vma->vm_file;
391 struct gntdev_priv *priv = file->private_data;
379 392
380 pr_debug("gntdev_vma_close %p\n", vma); 393 pr_debug("gntdev_vma_close %p\n", vma);
381 if (use_ptemod) { 394 if (use_ptemod) {
382 struct file *file = vma->vm_file;
383 struct gntdev_priv *priv = file->private_data;
384 /* It is possible that an mmu notifier could be running 395 /* It is possible that an mmu notifier could be running
385 * concurrently, so take priv->lock to ensure that the vma won't 396 * concurrently, so take priv->lock to ensure that the vma won't
386 * vanishing during the unmap_grant_pages call, since we will 397 * vanishing during the unmap_grant_pages call, since we will
@@ -393,7 +404,7 @@ static void gntdev_vma_close(struct vm_area_struct *vma)
393 spin_unlock(&priv->lock); 404 spin_unlock(&priv->lock);
394 } 405 }
395 vma->vm_private_data = NULL; 406 vma->vm_private_data = NULL;
396 gntdev_put_map(map); 407 gntdev_put_map(priv, map);
397} 408}
398 409
399static struct vm_operations_struct gntdev_vmops = { 410static struct vm_operations_struct gntdev_vmops = {
@@ -403,33 +414,43 @@ static struct vm_operations_struct gntdev_vmops = {
403 414
404/* ------------------------------------------------------------------ */ 415/* ------------------------------------------------------------------ */
405 416
417static void unmap_if_in_range(struct grant_map *map,
418 unsigned long start, unsigned long end)
419{
420 unsigned long mstart, mend;
421 int err;
422
423 if (!map->vma)
424 return;
425 if (map->vma->vm_start >= end)
426 return;
427 if (map->vma->vm_end <= start)
428 return;
429 mstart = max(start, map->vma->vm_start);
430 mend = min(end, map->vma->vm_end);
431 pr_debug("map %d+%d (%lx %lx), range %lx %lx, mrange %lx %lx\n",
432 map->index, map->count,
433 map->vma->vm_start, map->vma->vm_end,
434 start, end, mstart, mend);
435 err = unmap_grant_pages(map,
436 (mstart - map->vma->vm_start) >> PAGE_SHIFT,
437 (mend - mstart) >> PAGE_SHIFT);
438 WARN_ON(err);
439}
440
406static void mn_invl_range_start(struct mmu_notifier *mn, 441static void mn_invl_range_start(struct mmu_notifier *mn,
407 struct mm_struct *mm, 442 struct mm_struct *mm,
408 unsigned long start, unsigned long end) 443 unsigned long start, unsigned long end)
409{ 444{
410 struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn); 445 struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn);
411 struct grant_map *map; 446 struct grant_map *map;
412 unsigned long mstart, mend;
413 int err;
414 447
415 spin_lock(&priv->lock); 448 spin_lock(&priv->lock);
416 list_for_each_entry(map, &priv->maps, next) { 449 list_for_each_entry(map, &priv->maps, next) {
417 if (!map->vma) 450 unmap_if_in_range(map, start, end);
418 continue; 451 }
419 if (map->vma->vm_start >= end) 452 list_for_each_entry(map, &priv->freeable_maps, next) {
420 continue; 453 unmap_if_in_range(map, start, end);
421 if (map->vma->vm_end <= start)
422 continue;
423 mstart = max(start, map->vma->vm_start);
424 mend = min(end, map->vma->vm_end);
425 pr_debug("map %d+%d (%lx %lx), range %lx %lx, mrange %lx %lx\n",
426 map->index, map->count,
427 map->vma->vm_start, map->vma->vm_end,
428 start, end, mstart, mend);
429 err = unmap_grant_pages(map,
430 (mstart - map->vma->vm_start) >> PAGE_SHIFT,
431 (mend - mstart) >> PAGE_SHIFT);
432 WARN_ON(err);
433 } 454 }
434 spin_unlock(&priv->lock); 455 spin_unlock(&priv->lock);
435} 456}
@@ -458,6 +479,15 @@ static void mn_release(struct mmu_notifier *mn,
458 err = unmap_grant_pages(map, /* offset */ 0, map->count); 479 err = unmap_grant_pages(map, /* offset */ 0, map->count);
459 WARN_ON(err); 480 WARN_ON(err);
460 } 481 }
482 list_for_each_entry(map, &priv->freeable_maps, next) {
483 if (!map->vma)
484 continue;
485 pr_debug("map %d+%d (%lx %lx)\n",
486 map->index, map->count,
487 map->vma->vm_start, map->vma->vm_end);
488 err = unmap_grant_pages(map, /* offset */ 0, map->count);
489 WARN_ON(err);
490 }
461 spin_unlock(&priv->lock); 491 spin_unlock(&priv->lock);
462} 492}
463 493
@@ -479,6 +509,7 @@ static int gntdev_open(struct inode *inode, struct file *flip)
479 return -ENOMEM; 509 return -ENOMEM;
480 510
481 INIT_LIST_HEAD(&priv->maps); 511 INIT_LIST_HEAD(&priv->maps);
512 INIT_LIST_HEAD(&priv->freeable_maps);
482 spin_lock_init(&priv->lock); 513 spin_lock_init(&priv->lock);
483 514
484 if (use_ptemod) { 515 if (use_ptemod) {
@@ -513,8 +544,9 @@ static int gntdev_release(struct inode *inode, struct file *flip)
513 while (!list_empty(&priv->maps)) { 544 while (!list_empty(&priv->maps)) {
514 map = list_entry(priv->maps.next, struct grant_map, next); 545 map = list_entry(priv->maps.next, struct grant_map, next);
515 list_del(&map->next); 546 list_del(&map->next);
516 gntdev_put_map(map); 547 gntdev_put_map(NULL /* already removed */, map);
517 } 548 }
549 WARN_ON(!list_empty(&priv->freeable_maps));
518 550
519 if (use_ptemod) 551 if (use_ptemod)
520 mmu_notifier_unregister(&priv->mn, priv->mm); 552 mmu_notifier_unregister(&priv->mn, priv->mm);
@@ -542,14 +574,14 @@ static long gntdev_ioctl_map_grant_ref(struct gntdev_priv *priv,
542 574
543 if (unlikely(atomic_add_return(op.count, &pages_mapped) > limit)) { 575 if (unlikely(atomic_add_return(op.count, &pages_mapped) > limit)) {
544 pr_debug("can't map: over limit\n"); 576 pr_debug("can't map: over limit\n");
545 gntdev_put_map(map); 577 gntdev_put_map(NULL, map);
546 return err; 578 return err;
547 } 579 }
548 580
549 if (copy_from_user(map->grants, &u->refs, 581 if (copy_from_user(map->grants, &u->refs,
550 sizeof(map->grants[0]) * op.count) != 0) { 582 sizeof(map->grants[0]) * op.count) != 0) {
551 gntdev_put_map(map); 583 gntdev_put_map(NULL, map);
552 return err; 584 return -EFAULT;
553 } 585 }
554 586
555 spin_lock(&priv->lock); 587 spin_lock(&priv->lock);
@@ -578,11 +610,13 @@ static long gntdev_ioctl_unmap_grant_ref(struct gntdev_priv *priv,
578 map = gntdev_find_map_index(priv, op.index >> PAGE_SHIFT, op.count); 610 map = gntdev_find_map_index(priv, op.index >> PAGE_SHIFT, op.count);
579 if (map) { 611 if (map) {
580 list_del(&map->next); 612 list_del(&map->next);
613 if (populate_freeable_maps)
614 list_add_tail(&map->next, &priv->freeable_maps);
581 err = 0; 615 err = 0;
582 } 616 }
583 spin_unlock(&priv->lock); 617 spin_unlock(&priv->lock);
584 if (map) 618 if (map)
585 gntdev_put_map(map); 619 gntdev_put_map(priv, map);
586 return err; 620 return err;
587} 621}
588 622
@@ -797,7 +831,7 @@ out_unlock_put:
797out_put_map: 831out_put_map:
798 if (use_ptemod) 832 if (use_ptemod)
799 map->vma = NULL; 833 map->vma = NULL;
800 gntdev_put_map(map); 834 gntdev_put_map(priv, map);
801 return err; 835 return err;
802} 836}
803 837