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>
This commit is contained in:
Rebecca Schultz Zavin
2010-10-27 20:52:19 -07:00
parent 84b33d62a4
commit 95b5312b0f
3 changed files with 27 additions and 6 deletions

View 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);

View 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);

View 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);