Merge branch 'stable/cleanups-3.2' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / drivers / xen / gntdev.c
index 772a5b8..3987132 100644 (file)
@@ -83,6 +83,7 @@ struct grant_map {
        struct ioctl_gntdev_grant_ref *grants;
        struct gnttab_map_grant_ref   *map_ops;
        struct gnttab_unmap_grant_ref *unmap_ops;
+       struct gnttab_map_grant_ref   *kmap_ops;
        struct page **pages;
 };
 
@@ -116,19 +117,22 @@ static struct grant_map *gntdev_alloc_map(struct gntdev_priv *priv, int count)
        add->grants    = kzalloc(sizeof(add->grants[0])    * count, GFP_KERNEL);
        add->map_ops   = kzalloc(sizeof(add->map_ops[0])   * count, GFP_KERNEL);
        add->unmap_ops = kzalloc(sizeof(add->unmap_ops[0]) * count, GFP_KERNEL);
+       add->kmap_ops  = kzalloc(sizeof(add->kmap_ops[0])  * count, GFP_KERNEL);
        add->pages     = kzalloc(sizeof(add->pages[0])     * count, GFP_KERNEL);
        if (NULL == add->grants    ||
            NULL == add->map_ops   ||
            NULL == add->unmap_ops ||
+           NULL == add->kmap_ops  ||
            NULL == add->pages)
                goto err;
 
-       if (alloc_xenballooned_pages(count, add->pages))
+       if (alloc_xenballooned_pages(count, add->pages, false /* lowmem */))
                goto err;
 
        for (i = 0; i < count; i++) {
                add->map_ops[i].handle = -1;
                add->unmap_ops[i].handle = -1;
+               add->kmap_ops[i].handle = -1;
        }
 
        add->index = 0;
@@ -142,6 +146,7 @@ err:
        kfree(add->grants);
        kfree(add->map_ops);
        kfree(add->unmap_ops);
+       kfree(add->kmap_ops);
        kfree(add);
        return NULL;
 }
@@ -242,10 +247,35 @@ static int map_grant_pages(struct grant_map *map)
                        gnttab_set_unmap_op(&map->unmap_ops[i], addr,
                                map->flags, -1 /* handle */);
                }
+       } else {
+               /*
+                * Setup the map_ops corresponding to the pte entries pointing
+                * to the kernel linear addresses of the struct pages.
+                * These ptes are completely different from the user ptes dealt
+                * with find_grant_ptes.
+                */
+               for (i = 0; i < map->count; i++) {
+                       unsigned level;
+                       unsigned long address = (unsigned long)
+                               pfn_to_kaddr(page_to_pfn(map->pages[i]));
+                       pte_t *ptep;
+                       u64 pte_maddr = 0;
+                       BUG_ON(PageHighMem(map->pages[i]));
+
+                       ptep = lookup_address(address, &level);
+                       pte_maddr = arbitrary_virt_to_machine(ptep).maddr;
+                       gnttab_set_map_op(&map->kmap_ops[i], pte_maddr,
+                               map->flags |
+                               GNTMAP_host_map |
+                               GNTMAP_contains_pte,
+                               map->grants[i].ref,
+                               map->grants[i].domid);
+               }
        }
 
        pr_debug("map %d+%d\n", map->index, map->count);
-       err = gnttab_map_refs(map->map_ops, map->pages, map->count);
+       err = gnttab_map_refs(map->map_ops, use_ptemod ? map->kmap_ops : NULL,
+                       map->pages, map->count);
        if (err)
                return err;
 
@@ -461,13 +491,11 @@ static int gntdev_release(struct inode *inode, struct file *flip)
 
        pr_debug("priv %p\n", priv);
 
-       spin_lock(&priv->lock);
        while (!list_empty(&priv->maps)) {
                map = list_entry(priv->maps.next, struct grant_map, next);
                list_del(&map->next);
                gntdev_put_map(map);
        }
-       spin_unlock(&priv->lock);
 
        if (use_ptemod)
                mmu_notifier_unregister(&priv->mn, priv->mm);
@@ -531,10 +559,11 @@ static long gntdev_ioctl_unmap_grant_ref(struct gntdev_priv *priv,
        map = gntdev_find_map_index(priv, op.index >> PAGE_SHIFT, op.count);
        if (map) {
                list_del(&map->next);
-               gntdev_put_map(map);
                err = 0;
        }
        spin_unlock(&priv->lock);
+       if (map)
+               gntdev_put_map(map);
        return err;
 }