From 95b5312b0ff1d6cd8cb6b8bd8cce49e79ad0dd23 Mon Sep 17 00:00:00 2001 From: Rebecca Schultz Zavin Date: Wed, 27 Oct 2010 20:52:19 -0700 Subject: 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 --- drivers/video/tegra/nvmap/nvmap.h | 4 ++++ drivers/video/tegra/nvmap/nvmap_dev.c | 13 +++++++++++++ drivers/video/tegra/nvmap/nvmap_handle.c | 16 ++++++++++------ 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); -- cgit v1.2.3