aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen/gntalloc.c
diff options
context:
space:
mode:
authorDaniel De Graaf <dgdegra@tycho.nsa.gov>2011-11-28 11:49:11 -0500
committerKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>2011-12-16 11:26:10 -0500
commit243082e0d59f169a1fa502f51ee5a820889fae93 (patch)
tree8359afd781878ca4e75ca34f1cd328f469815f5d /drivers/xen/gntalloc.c
parent0105d2b4fbc24c2fb6ca9bae650784dd7ddf0b12 (diff)
xen/gntalloc: fix reference counts on multi-page mappings
When a multi-page mapping of gntalloc is created, the reference counts of all pages in the vma are incremented. However, the vma open/close operations only adjusted the reference count of the first page in the mapping, leaking the other pages. Store a struct in the vm_private_data to track the original page count to properly free the pages when the last reference to the vma is closed. Reported-by: Anil Madhavapeddy <anil@recoil.org> 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/gntalloc.c')
-rw-r--r--drivers/xen/gntalloc.c56
1 files changed, 43 insertions, 13 deletions
diff --git a/drivers/xen/gntalloc.c b/drivers/xen/gntalloc.c
index f330a4b8b685..e8ea56583b4c 100644
--- a/drivers/xen/gntalloc.c
+++ b/drivers/xen/gntalloc.c
@@ -99,6 +99,12 @@ struct gntalloc_file_private_data {
99 uint64_t index; 99 uint64_t index;
100}; 100};
101 101
102struct gntalloc_vma_private_data {
103 struct gntalloc_gref *gref;
104 int users;
105 int count;
106};
107
102static void __del_gref(struct gntalloc_gref *gref); 108static void __del_gref(struct gntalloc_gref *gref);
103 109
104static void do_cleanup(void) 110static void do_cleanup(void)
@@ -451,25 +457,39 @@ static long gntalloc_ioctl(struct file *filp, unsigned int cmd,
451 457
452static void gntalloc_vma_open(struct vm_area_struct *vma) 458static void gntalloc_vma_open(struct vm_area_struct *vma)
453{ 459{
454 struct gntalloc_gref *gref = vma->vm_private_data; 460 struct gntalloc_vma_private_data *priv = vma->vm_private_data;
455 if (!gref) 461
462 if (!priv)
456 return; 463 return;
457 464
458 mutex_lock(&gref_mutex); 465 mutex_lock(&gref_mutex);
459 gref->users++; 466 priv->users++;
460 mutex_unlock(&gref_mutex); 467 mutex_unlock(&gref_mutex);
461} 468}
462 469
463static void gntalloc_vma_close(struct vm_area_struct *vma) 470static void gntalloc_vma_close(struct vm_area_struct *vma)
464{ 471{
465 struct gntalloc_gref *gref = vma->vm_private_data; 472 struct gntalloc_vma_private_data *priv = vma->vm_private_data;
466 if (!gref) 473 struct gntalloc_gref *gref, *next;
474 int i;
475
476 if (!priv)
467 return; 477 return;
468 478
469 mutex_lock(&gref_mutex); 479 mutex_lock(&gref_mutex);
470 gref->users--; 480 priv->users--;
471 if (gref->users == 0) 481 if (priv->users == 0) {
472 __del_gref(gref); 482 gref = priv->gref;
483 for (i = 0; i < priv->count; i++) {
484 gref->users--;
485 next = list_entry(gref->next_gref.next,
486 struct gntalloc_gref, next_gref);
487 if (gref->users == 0)
488 __del_gref(gref);
489 gref = next;
490 }
491 kfree(priv);
492 }
473 mutex_unlock(&gref_mutex); 493 mutex_unlock(&gref_mutex);
474} 494}
475 495
@@ -481,19 +501,25 @@ static struct vm_operations_struct gntalloc_vmops = {
481static int gntalloc_mmap(struct file *filp, struct vm_area_struct *vma) 501static int gntalloc_mmap(struct file *filp, struct vm_area_struct *vma)
482{ 502{
483 struct gntalloc_file_private_data *priv = filp->private_data; 503 struct gntalloc_file_private_data *priv = filp->private_data;
504 struct gntalloc_vma_private_data *vm_priv;
484 struct gntalloc_gref *gref; 505 struct gntalloc_gref *gref;
485 int count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; 506 int count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
486 int rv, i; 507 int rv, i;
487 508
488 pr_debug("%s: priv %p, page %lu+%d\n", __func__,
489 priv, vma->vm_pgoff, count);
490
491 if (!(vma->vm_flags & VM_SHARED)) { 509 if (!(vma->vm_flags & VM_SHARED)) {
492 printk(KERN_ERR "%s: Mapping must be shared.\n", __func__); 510 printk(KERN_ERR "%s: Mapping must be shared.\n", __func__);
493 return -EINVAL; 511 return -EINVAL;
494 } 512 }
495 513
514 vm_priv = kmalloc(sizeof(*vm_priv), GFP_KERNEL);
515 if (!vm_priv)
516 return -ENOMEM;
517
496 mutex_lock(&gref_mutex); 518 mutex_lock(&gref_mutex);
519
520 pr_debug("%s: priv %p,%p, page %lu+%d\n", __func__,
521 priv, vm_priv, vma->vm_pgoff, count);
522
497 gref = find_grefs(priv, vma->vm_pgoff << PAGE_SHIFT, count); 523 gref = find_grefs(priv, vma->vm_pgoff << PAGE_SHIFT, count);
498 if (gref == NULL) { 524 if (gref == NULL) {
499 rv = -ENOENT; 525 rv = -ENOENT;
@@ -502,9 +528,13 @@ static int gntalloc_mmap(struct file *filp, struct vm_area_struct *vma)
502 goto out_unlock; 528 goto out_unlock;
503 } 529 }
504 530
505 vma->vm_private_data = gref; 531 vm_priv->gref = gref;
532 vm_priv->users = 1;
533 vm_priv->count = count;
534
535 vma->vm_private_data = vm_priv;
506 536
507 vma->vm_flags |= VM_RESERVED; 537 vma->vm_flags |= VM_RESERVED | VM_DONTEXPAND;
508 538
509 vma->vm_ops = &gntalloc_vmops; 539 vma->vm_ops = &gntalloc_vmops;
510 540