video: tegra: nvmap: Fix handle ref counting
authorRebecca Schultz Zavin <rebecca@android.com>
Thu, 28 Oct 2010 03:52:19 +0000 (20:52 -0700)
committerRebecca Schultz Zavin <rebecca@android.com>
Fri, 29 Oct 2010 01:05:59 +0000 (18:05 -0700)
In the current implementation handles hold references to a
client and clients hold references to their handles.  As a
result when a process terminates it's handles can't be cleaned
up and we leak memory.  Instead only hold references to handles
from clients.

Change-Id: Iba699e740a043deaf0a78b13b4ea01544675078f
Signed-off-by: Rebecca Schultz Zavin <rebecca@android.com>
drivers/video/tegra/nvmap/nvmap.h
drivers/video/tegra/nvmap/nvmap_dev.c
drivers/video/tegra/nvmap/nvmap_handle.c

index fd4bcd7a708193a8ab993426435ba30e632612e4..1e5b800baf7a68e8a9207f125662bc6c80dd55f7 100644 (file)
@@ -71,6 +71,7 @@ struct nvmap_handle {
        size_t size;            /* padded (as-allocated) size */
        size_t orig_size;       /* original (as-requested) size */
        struct nvmap_client *owner;
+       struct nvmap_device *dev;
        union {
                struct nvmap_pgalloc pgalloc;
                struct nvmap_heap_block *carveout;
@@ -79,6 +80,7 @@ struct nvmap_handle {
        bool secure;            /* zap IOVMM area on unpin */
        bool heap_pgalloc;      /* handle is page allocated (sysmem / iovmm) */
        bool alloc;             /* handle has memory allocated */
+       struct mutex lock;
 };
 
 struct nvmap_share {
@@ -161,6 +163,8 @@ void nvmap_carveout_commit_subtract(struct nvmap_client *client,
                                    struct nvmap_carveout_node *node,
                                    size_t len);
 
+struct nvmap_share *nvmap_get_share_from_dev(struct nvmap_device *dev);
+
 struct nvmap_handle *nvmap_validate_get(struct nvmap_client *client,
                                        unsigned long handle);
 
index 681c84413ed395eea29226197f298dc46ee13772..295289c3ce9acc52682e355e882bbbfb19342491 100644 (file)
@@ -119,6 +119,11 @@ struct device *nvmap_client_to_device(struct nvmap_client *client)
                return client->dev->dev_user.this_device;
 }
 
+struct nvmap_share *nvmap_get_share_from_dev(struct nvmap_device *dev)
+{
+       return &dev->iovmm_master;
+}
+
 /* allocates a PTE for the caller's use; returns the PTE pointer or
  * a negative errno. may be called from IRQs */
 pte_t **nvmap_alloc_pte_irq(struct nvmap_device *dev, void **vaddr)
@@ -289,6 +294,9 @@ void nvmap_carveout_commit_subtract(struct nvmap_client *client,
                                    struct nvmap_carveout_node *node,
                                    size_t len)
 {
+       if (!client)
+               return;
+
        mutex_lock(&node->clients_mutex);
        client->carveout_commit[node->index].commit -= len;
        BUG_ON(client->carveout_commit[node->index].commit < 0);
@@ -472,6 +480,11 @@ static void destroy_client(struct nvmap_client *client)
                smp_rmb();
                pins = atomic_read(&ref->pin);
 
+               mutex_lock(&ref->handle->lock);
+               if (ref->handle->owner == client)
+                   ref->handle->owner = NULL;
+               mutex_unlock(&ref->handle->lock);
+
                while (pins--)
                        nvmap_unpin_handles(client, &ref->handle, 1);
 
index be130b4be757f5a8f4d55f93c23d89dd7391bd08..09502bff4883bf3bc33447162cb9d8d885b3158c 100644 (file)
@@ -71,19 +71,21 @@ static inline void altfree(void *ptr, size_t len)
 
 void _nvmap_handle_free(struct nvmap_handle *h)
 {
-       struct nvmap_client *client = h->owner;
+       struct nvmap_device *dev = h->dev;
        unsigned int i, nr_page;
 
-       if (nvmap_handle_remove(client->dev, h) != 0)
+       if (nvmap_handle_remove(dev, h) != 0)
                return;
 
        if (!h->alloc)
                goto out;
 
        if (!h->heap_pgalloc) {
-               nvmap_carveout_commit_subtract(client,
+               mutex_lock(&h->lock);
+               nvmap_carveout_commit_subtract(h->owner,
                        nvmap_heap_to_arg(nvmap_block_to_heap(h->carveout)),
                        h->size);
+               mutex_unlock(&h->lock);
                nvmap_heap_free(h->carveout);
                goto out;
        }
@@ -93,7 +95,8 @@ void _nvmap_handle_free(struct nvmap_handle *h)
        BUG_ON(h->size & ~PAGE_MASK);
        BUG_ON(!h->pgalloc.pages);
 
-       nvmap_mru_remove(client->share, h);
+       nvmap_mru_remove(nvmap_get_share_from_dev(dev), h);
+
        if (h->pgalloc.area)
                tegra_iovmm_free_vm(h->pgalloc.area);
 
@@ -104,7 +107,6 @@ void _nvmap_handle_free(struct nvmap_handle *h)
 
 out:
        kfree(h);
-       nvmap_client_put(client);
 }
 
 extern void __flush_dcache_page(struct address_space *, struct page *);
@@ -423,10 +425,12 @@ struct nvmap_handle_ref *nvmap_create_handle(struct nvmap_client *client,
 
        atomic_set(&h->ref, 1);
        atomic_set(&h->pin, 0);
-       h->owner = nvmap_client_get(client);
+       h->owner = client;
+       h->dev = client->dev;
        BUG_ON(!h->owner);
        h->size = h->orig_size = size;
        h->flags = NVMAP_HANDLE_WRITE_COMBINE;
+       mutex_init(&h->lock);
 
        nvmap_handle_add(client->dev, h);