diff options
Diffstat (limited to 'drivers/xen/gntdev.c')
-rw-r--r-- | drivers/xen/gntdev.c | 130 |
1 files changed, 88 insertions, 42 deletions
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 2e22df2f7a3f..3c8803feba26 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 " | |||
56 | static atomic_t pages_mapped = ATOMIC_INIT(0); | 56 | static atomic_t pages_mapped = ATOMIC_INIT(0); |
57 | 57 | ||
58 | static int use_ptemod; | 58 | static int use_ptemod; |
59 | #define populate_freeable_maps use_ptemod | ||
59 | 60 | ||
60 | struct gntdev_priv { | 61 | struct 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 | ||
196 | static void gntdev_put_map(struct grant_map *map) | 201 | static 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); |
@@ -301,17 +312,10 @@ static int __unmap_grant_pages(struct grant_map *map, int offset, int pages) | |||
301 | 312 | ||
302 | if (map->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) { | 313 | if (map->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) { |
303 | int pgno = (map->notify.addr >> PAGE_SHIFT); | 314 | int pgno = (map->notify.addr >> PAGE_SHIFT); |
304 | if (pgno >= offset && pgno < offset + pages && use_ptemod) { | 315 | if (pgno >= offset && pgno < offset + pages) { |
305 | void __user *tmp = (void __user *) | 316 | /* No need for kmap, pages are in lowmem */ |
306 | map->vma->vm_start + map->notify.addr; | 317 | uint8_t *tmp = pfn_to_kaddr(page_to_pfn(map->pages[pgno])); |
307 | err = copy_to_user(tmp, &err, 1); | ||
308 | if (err) | ||
309 | return -EFAULT; | ||
310 | map->notify.flags &= ~UNMAP_NOTIFY_CLEAR_BYTE; | ||
311 | } else if (pgno >= offset && pgno < offset + pages) { | ||
312 | uint8_t *tmp = kmap(map->pages[pgno]); | ||
313 | tmp[map->notify.addr & (PAGE_SIZE-1)] = 0; | 318 | tmp[map->notify.addr & (PAGE_SIZE-1)] = 0; |
314 | kunmap(map->pages[pgno]); | ||
315 | map->notify.flags &= ~UNMAP_NOTIFY_CLEAR_BYTE; | 319 | map->notify.flags &= ~UNMAP_NOTIFY_CLEAR_BYTE; |
316 | } | 320 | } |
317 | } | 321 | } |
@@ -376,11 +380,24 @@ static void gntdev_vma_open(struct vm_area_struct *vma) | |||
376 | static void gntdev_vma_close(struct vm_area_struct *vma) | 380 | static void gntdev_vma_close(struct vm_area_struct *vma) |
377 | { | 381 | { |
378 | struct grant_map *map = vma->vm_private_data; | 382 | struct grant_map *map = vma->vm_private_data; |
383 | struct file *file = vma->vm_file; | ||
384 | struct gntdev_priv *priv = file->private_data; | ||
379 | 385 | ||
380 | pr_debug("gntdev_vma_close %p\n", vma); | 386 | pr_debug("gntdev_vma_close %p\n", vma); |
381 | map->vma = NULL; | 387 | if (use_ptemod) { |
388 | /* It is possible that an mmu notifier could be running | ||
389 | * concurrently, so take priv->lock to ensure that the vma won't | ||
390 | * vanishing during the unmap_grant_pages call, since we will | ||
391 | * spin here until that completes. Such a concurrent call will | ||
392 | * not do any unmapping, since that has been done prior to | ||
393 | * closing the vma, but it may still iterate the unmap_ops list. | ||
394 | */ | ||
395 | spin_lock(&priv->lock); | ||
396 | map->vma = NULL; | ||
397 | spin_unlock(&priv->lock); | ||
398 | } | ||
382 | vma->vm_private_data = NULL; | 399 | vma->vm_private_data = NULL; |
383 | gntdev_put_map(map); | 400 | gntdev_put_map(priv, map); |
384 | } | 401 | } |
385 | 402 | ||
386 | static struct vm_operations_struct gntdev_vmops = { | 403 | static struct vm_operations_struct gntdev_vmops = { |
@@ -390,33 +407,43 @@ static struct vm_operations_struct gntdev_vmops = { | |||
390 | 407 | ||
391 | /* ------------------------------------------------------------------ */ | 408 | /* ------------------------------------------------------------------ */ |
392 | 409 | ||
410 | static void unmap_if_in_range(struct grant_map *map, | ||
411 | unsigned long start, unsigned long end) | ||
412 | { | ||
413 | unsigned long mstart, mend; | ||
414 | int err; | ||
415 | |||
416 | if (!map->vma) | ||
417 | return; | ||
418 | if (map->vma->vm_start >= end) | ||
419 | return; | ||
420 | if (map->vma->vm_end <= start) | ||
421 | return; | ||
422 | mstart = max(start, map->vma->vm_start); | ||
423 | mend = min(end, map->vma->vm_end); | ||
424 | pr_debug("map %d+%d (%lx %lx), range %lx %lx, mrange %lx %lx\n", | ||
425 | map->index, map->count, | ||
426 | map->vma->vm_start, map->vma->vm_end, | ||
427 | start, end, mstart, mend); | ||
428 | err = unmap_grant_pages(map, | ||
429 | (mstart - map->vma->vm_start) >> PAGE_SHIFT, | ||
430 | (mend - mstart) >> PAGE_SHIFT); | ||
431 | WARN_ON(err); | ||
432 | } | ||
433 | |||
393 | static void mn_invl_range_start(struct mmu_notifier *mn, | 434 | static void mn_invl_range_start(struct mmu_notifier *mn, |
394 | struct mm_struct *mm, | 435 | struct mm_struct *mm, |
395 | unsigned long start, unsigned long end) | 436 | unsigned long start, unsigned long end) |
396 | { | 437 | { |
397 | struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn); | 438 | struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn); |
398 | struct grant_map *map; | 439 | struct grant_map *map; |
399 | unsigned long mstart, mend; | ||
400 | int err; | ||
401 | 440 | ||
402 | spin_lock(&priv->lock); | 441 | spin_lock(&priv->lock); |
403 | list_for_each_entry(map, &priv->maps, next) { | 442 | list_for_each_entry(map, &priv->maps, next) { |
404 | if (!map->vma) | 443 | unmap_if_in_range(map, start, end); |
405 | continue; | 444 | } |
406 | if (map->vma->vm_start >= end) | 445 | list_for_each_entry(map, &priv->freeable_maps, next) { |
407 | continue; | 446 | unmap_if_in_range(map, start, end); |
408 | if (map->vma->vm_end <= start) | ||
409 | continue; | ||
410 | mstart = max(start, map->vma->vm_start); | ||
411 | mend = min(end, map->vma->vm_end); | ||
412 | pr_debug("map %d+%d (%lx %lx), range %lx %lx, mrange %lx %lx\n", | ||
413 | map->index, map->count, | ||
414 | map->vma->vm_start, map->vma->vm_end, | ||
415 | start, end, mstart, mend); | ||
416 | err = unmap_grant_pages(map, | ||
417 | (mstart - map->vma->vm_start) >> PAGE_SHIFT, | ||
418 | (mend - mstart) >> PAGE_SHIFT); | ||
419 | WARN_ON(err); | ||
420 | } | 447 | } |
421 | spin_unlock(&priv->lock); | 448 | spin_unlock(&priv->lock); |
422 | } | 449 | } |
@@ -445,6 +472,15 @@ static void mn_release(struct mmu_notifier *mn, | |||
445 | err = unmap_grant_pages(map, /* offset */ 0, map->count); | 472 | err = unmap_grant_pages(map, /* offset */ 0, map->count); |
446 | WARN_ON(err); | 473 | WARN_ON(err); |
447 | } | 474 | } |
475 | list_for_each_entry(map, &priv->freeable_maps, next) { | ||
476 | if (!map->vma) | ||
477 | continue; | ||
478 | pr_debug("map %d+%d (%lx %lx)\n", | ||
479 | map->index, map->count, | ||
480 | map->vma->vm_start, map->vma->vm_end); | ||
481 | err = unmap_grant_pages(map, /* offset */ 0, map->count); | ||
482 | WARN_ON(err); | ||
483 | } | ||
448 | spin_unlock(&priv->lock); | 484 | spin_unlock(&priv->lock); |
449 | } | 485 | } |
450 | 486 | ||
@@ -466,6 +502,7 @@ static int gntdev_open(struct inode *inode, struct file *flip) | |||
466 | return -ENOMEM; | 502 | return -ENOMEM; |
467 | 503 | ||
468 | INIT_LIST_HEAD(&priv->maps); | 504 | INIT_LIST_HEAD(&priv->maps); |
505 | INIT_LIST_HEAD(&priv->freeable_maps); | ||
469 | spin_lock_init(&priv->lock); | 506 | spin_lock_init(&priv->lock); |
470 | 507 | ||
471 | if (use_ptemod) { | 508 | if (use_ptemod) { |
@@ -500,8 +537,9 @@ static int gntdev_release(struct inode *inode, struct file *flip) | |||
500 | while (!list_empty(&priv->maps)) { | 537 | while (!list_empty(&priv->maps)) { |
501 | map = list_entry(priv->maps.next, struct grant_map, next); | 538 | map = list_entry(priv->maps.next, struct grant_map, next); |
502 | list_del(&map->next); | 539 | list_del(&map->next); |
503 | gntdev_put_map(map); | 540 | gntdev_put_map(NULL /* already removed */, map); |
504 | } | 541 | } |
542 | WARN_ON(!list_empty(&priv->freeable_maps)); | ||
505 | 543 | ||
506 | if (use_ptemod) | 544 | if (use_ptemod) |
507 | mmu_notifier_unregister(&priv->mn, priv->mm); | 545 | mmu_notifier_unregister(&priv->mn, priv->mm); |
@@ -529,14 +567,14 @@ static long gntdev_ioctl_map_grant_ref(struct gntdev_priv *priv, | |||
529 | 567 | ||
530 | if (unlikely(atomic_add_return(op.count, &pages_mapped) > limit)) { | 568 | if (unlikely(atomic_add_return(op.count, &pages_mapped) > limit)) { |
531 | pr_debug("can't map: over limit\n"); | 569 | pr_debug("can't map: over limit\n"); |
532 | gntdev_put_map(map); | 570 | gntdev_put_map(NULL, map); |
533 | return err; | 571 | return err; |
534 | } | 572 | } |
535 | 573 | ||
536 | if (copy_from_user(map->grants, &u->refs, | 574 | if (copy_from_user(map->grants, &u->refs, |
537 | sizeof(map->grants[0]) * op.count) != 0) { | 575 | sizeof(map->grants[0]) * op.count) != 0) { |
538 | gntdev_put_map(map); | 576 | gntdev_put_map(NULL, map); |
539 | return err; | 577 | return -EFAULT; |
540 | } | 578 | } |
541 | 579 | ||
542 | spin_lock(&priv->lock); | 580 | spin_lock(&priv->lock); |
@@ -565,11 +603,13 @@ static long gntdev_ioctl_unmap_grant_ref(struct gntdev_priv *priv, | |||
565 | map = gntdev_find_map_index(priv, op.index >> PAGE_SHIFT, op.count); | 603 | map = gntdev_find_map_index(priv, op.index >> PAGE_SHIFT, op.count); |
566 | if (map) { | 604 | if (map) { |
567 | list_del(&map->next); | 605 | list_del(&map->next); |
606 | if (populate_freeable_maps) | ||
607 | list_add_tail(&map->next, &priv->freeable_maps); | ||
568 | err = 0; | 608 | err = 0; |
569 | } | 609 | } |
570 | spin_unlock(&priv->lock); | 610 | spin_unlock(&priv->lock); |
571 | if (map) | 611 | if (map) |
572 | gntdev_put_map(map); | 612 | gntdev_put_map(priv, map); |
573 | return err; | 613 | return err; |
574 | } | 614 | } |
575 | 615 | ||
@@ -579,25 +619,31 @@ static long gntdev_ioctl_get_offset_for_vaddr(struct gntdev_priv *priv, | |||
579 | struct ioctl_gntdev_get_offset_for_vaddr op; | 619 | struct ioctl_gntdev_get_offset_for_vaddr op; |
580 | struct vm_area_struct *vma; | 620 | struct vm_area_struct *vma; |
581 | struct grant_map *map; | 621 | struct grant_map *map; |
622 | int rv = -EINVAL; | ||
582 | 623 | ||
583 | if (copy_from_user(&op, u, sizeof(op)) != 0) | 624 | if (copy_from_user(&op, u, sizeof(op)) != 0) |
584 | return -EFAULT; | 625 | return -EFAULT; |
585 | pr_debug("priv %p, offset for vaddr %lx\n", priv, (unsigned long)op.vaddr); | 626 | pr_debug("priv %p, offset for vaddr %lx\n", priv, (unsigned long)op.vaddr); |
586 | 627 | ||
628 | down_read(¤t->mm->mmap_sem); | ||
587 | vma = find_vma(current->mm, op.vaddr); | 629 | vma = find_vma(current->mm, op.vaddr); |
588 | if (!vma || vma->vm_ops != &gntdev_vmops) | 630 | if (!vma || vma->vm_ops != &gntdev_vmops) |
589 | return -EINVAL; | 631 | goto out_unlock; |
590 | 632 | ||
591 | map = vma->vm_private_data; | 633 | map = vma->vm_private_data; |
592 | if (!map) | 634 | if (!map) |
593 | return -EINVAL; | 635 | goto out_unlock; |
594 | 636 | ||
595 | op.offset = map->index << PAGE_SHIFT; | 637 | op.offset = map->index << PAGE_SHIFT; |
596 | op.count = map->count; | 638 | op.count = map->count; |
639 | rv = 0; | ||
597 | 640 | ||
598 | if (copy_to_user(u, &op, sizeof(op)) != 0) | 641 | out_unlock: |
642 | up_read(¤t->mm->mmap_sem); | ||
643 | |||
644 | if (rv == 0 && copy_to_user(u, &op, sizeof(op)) != 0) | ||
599 | return -EFAULT; | 645 | return -EFAULT; |
600 | return 0; | 646 | return rv; |
601 | } | 647 | } |
602 | 648 | ||
603 | static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u) | 649 | static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u) |
@@ -778,7 +824,7 @@ out_unlock_put: | |||
778 | out_put_map: | 824 | out_put_map: |
779 | if (use_ptemod) | 825 | if (use_ptemod) |
780 | map->vma = NULL; | 826 | map->vma = NULL; |
781 | gntdev_put_map(map); | 827 | gntdev_put_map(priv, map); |
782 | return err; | 828 | return err; |
783 | } | 829 | } |
784 | 830 | ||