summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRebecca Schultz Zavin <rebecca@android.com>2010-10-27 20:52:19 -0700
committerRebecca Schultz Zavin <rebecca@android.com>2010-10-28 18:05:59 -0700
commit95b5312b0ff1d6cd8cb6b8bd8cce49e79ad0dd23 (patch)
treed822a178094d120746aa9eef5686ab9e3e97d661
parent84b33d62a46d49a756e88f098a0d12636808e940 (diff)
video: tegra: nvmap: Fix handle ref counting
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>
-rw-r--r--drivers/video/tegra/nvmap/nvmap.h4
-rw-r--r--drivers/video/tegra/nvmap/nvmap_dev.c13
-rw-r--r--drivers/video/tegra/nvmap/nvmap_handle.c16
3 files changed, 27 insertions, 6 deletions
diff --git a/drivers/video/tegra/nvmap/nvmap.h b/drivers/video/tegra/nvmap/nvmap.h
index fd4bcd7a7081..1e5b800baf7a 100644
--- a/drivers/video/tegra/nvmap/nvmap.h
+++ b/drivers/video/tegra/nvmap/nvmap.h
@@ -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);
diff --git a/drivers/video/tegra/nvmap/nvmap_dev.c b/drivers/video/tegra/nvmap/nvmap_dev.c
index 681c84413ed3..295289c3ce9a 100644
--- a/drivers/video/tegra/nvmap/nvmap_dev.c
+++ b/drivers/video/tegra/nvmap/nvmap_dev.c
@@ -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);
diff --git a/drivers/video/tegra/nvmap/nvmap_handle.c b/drivers/video/tegra/nvmap/nvmap_handle.c
index be130b4be757..09502bff4883 100644
--- a/drivers/video/tegra/nvmap/nvmap_handle.c
+++ b/drivers/video/tegra/nvmap/nvmap_handle.c
@@ -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);