video: driver: track dma buffer leak

Track dmabuf refcount during dma_buf_get() and dma_buf_put().
So if any dmabuf present with non-zero refcount during close,
then i.e a dmabuf leak. So during session close print error
and call dma_buf_put_refcount_completely() api to recursively
reduce the count to zero and will call dma_buf_put() to avoid
dmabuf leak from video driver side.

Change-Id: I2028c18ca067977eac3cdf0ce7d6cc4200739408
Signed-off-by: Govindaraj Rajagopal <grajagop@codeaurora.org>
This commit is contained in:
Govindaraj Rajagopal
2021-06-18 22:15:42 +05:30
parent 3730e55b29
commit 7f0c2442c5
5 changed files with 126 additions and 17 deletions

View File

@@ -131,6 +131,7 @@ struct msm_vidc_inst {
struct workqueue_struct *response_workq; struct workqueue_struct *response_workq;
struct list_head response_works; /* list of struct response_work */ struct list_head response_works; /* list of struct response_work */
struct list_head enc_input_crs; struct list_head enc_input_crs;
struct list_head dmabuf_tracker; /* list of struct msm_memory_dmabuf */
bool once_per_session_set; bool once_per_session_set;
bool ipsc_properties_set; bool ipsc_properties_set;
bool opsc_properties_set; bool opsc_properties_set;

View File

@@ -11,11 +11,18 @@
struct msm_vidc_core; struct msm_vidc_core;
struct msm_vidc_inst; struct msm_vidc_inst;
struct msm_memory_dmabuf {
struct list_head list;
struct dma_buf *dmabuf;
u32 refcount;
};
enum msm_memory_pool_type { enum msm_memory_pool_type {
MSM_MEM_POOL_BUFFER = 0, MSM_MEM_POOL_BUFFER = 0,
MSM_MEM_POOL_MAP, MSM_MEM_POOL_MAP,
MSM_MEM_POOL_ALLOC, MSM_MEM_POOL_ALLOC,
MSM_MEM_POOL_TIMESTAMP, MSM_MEM_POOL_TIMESTAMP,
MSM_MEM_POOL_DMABUF,
MSM_MEM_POOL_MAX, MSM_MEM_POOL_MAX,
}; };
@@ -40,8 +47,12 @@ int msm_vidc_memory_map(struct msm_vidc_core *core,
struct msm_vidc_map *map); struct msm_vidc_map *map);
int msm_vidc_memory_unmap(struct msm_vidc_core *core, int msm_vidc_memory_unmap(struct msm_vidc_core *core,
struct msm_vidc_map *map); struct msm_vidc_map *map);
struct dma_buf *msm_vidc_memory_get_dmabuf(int fd); struct dma_buf *msm_vidc_memory_get_dmabuf(struct msm_vidc_inst *inst,
void msm_vidc_memory_put_dmabuf(void *dmabuf); int fd);
void msm_vidc_memory_put_dmabuf(struct msm_vidc_inst *inst,
struct dma_buf *dmabuf);
void msm_vidc_memory_put_dmabuf_completely(struct msm_vidc_inst *inst,
struct msm_memory_dmabuf *buf);
int msm_memory_pools_init(struct msm_vidc_inst *inst); int msm_memory_pools_init(struct msm_vidc_inst *inst);
void msm_memory_pools_deinit(struct msm_vidc_inst *inst); void msm_memory_pools_deinit(struct msm_vidc_inst *inst);
void *msm_memory_alloc(struct msm_vidc_inst *inst, void *msm_memory_alloc(struct msm_vidc_inst *inst,

View File

@@ -841,6 +841,7 @@ void *msm_vidc_open(void *vidc_core, u32 session_type)
INIT_LIST_HEAD(&inst->children.list); INIT_LIST_HEAD(&inst->children.list);
INIT_LIST_HEAD(&inst->firmware.list); INIT_LIST_HEAD(&inst->firmware.list);
INIT_LIST_HEAD(&inst->enc_input_crs); INIT_LIST_HEAD(&inst->enc_input_crs);
INIT_LIST_HEAD(&inst->dmabuf_tracker);
for (i = 0; i < MAX_SIGNAL; i++) for (i = 0; i < MAX_SIGNAL; i++)
init_completion(&inst->completions[i]); init_completion(&inst->completions[i]);

View File

@@ -2008,7 +2008,7 @@ int msm_vidc_memory_unmap_completely(struct msm_vidc_inst *inst,
if (rc) if (rc)
break; break;
if (!map->refcount) { if (!map->refcount) {
msm_vidc_memory_put_dmabuf(map->dmabuf); msm_vidc_memory_put_dmabuf(inst, map->dmabuf);
list_del(&map->list); list_del(&map->list);
msm_memory_free(inst, MSM_MEM_POOL_MAP, map); msm_memory_free(inst, MSM_MEM_POOL_MAP, map);
break; break;
@@ -2266,7 +2266,7 @@ int msm_vidc_put_delayed_unmap(struct msm_vidc_inst *inst, struct msm_vidc_map *
i_vpr_e(inst, "%s: unmap failed\n", __func__); i_vpr_e(inst, "%s: unmap failed\n", __func__);
if (!map->refcount) { if (!map->refcount) {
msm_vidc_memory_put_dmabuf(map->dmabuf); msm_vidc_memory_put_dmabuf(inst, map->dmabuf);
list_del(&map->list); list_del(&map->list);
msm_memory_free(inst, MSM_MEM_POOL_MAP, map); msm_memory_free(inst, MSM_MEM_POOL_MAP, map);
} }
@@ -2334,7 +2334,7 @@ int msm_vidc_unmap_driver_buf(struct msm_vidc_inst *inst,
/* finally delete if refcount is zero */ /* finally delete if refcount is zero */
if (!map->refcount) { if (!map->refcount) {
msm_vidc_memory_put_dmabuf(map->dmabuf); msm_vidc_memory_put_dmabuf(inst, map->dmabuf);
list_del(&map->list); list_del(&map->list);
msm_memory_free(inst, MSM_MEM_POOL_MAP, map); msm_memory_free(inst, MSM_MEM_POOL_MAP, map);
} }
@@ -2378,7 +2378,7 @@ int msm_vidc_map_driver_buf(struct msm_vidc_inst *inst,
} }
INIT_LIST_HEAD(&map->list); INIT_LIST_HEAD(&map->list);
map->type = buf->type; map->type = buf->type;
map->dmabuf = msm_vidc_memory_get_dmabuf(buf->fd); map->dmabuf = msm_vidc_memory_get_dmabuf(inst, buf->fd);
if (!map->dmabuf) if (!map->dmabuf)
return -EINVAL; return -EINVAL;
map->region = msm_vidc_get_buffer_region(inst, buf->type, __func__); map->region = msm_vidc_get_buffer_region(inst, buf->type, __func__);
@@ -2386,7 +2386,7 @@ int msm_vidc_map_driver_buf(struct msm_vidc_inst *inst,
if (is_decode_session(inst) && is_output_buffer(buf->type)) { if (is_decode_session(inst) && is_output_buffer(buf->type)) {
rc = msm_vidc_get_delayed_unmap(inst, map); rc = msm_vidc_get_delayed_unmap(inst, map);
if (rc) { if (rc) {
msm_vidc_memory_put_dmabuf(map->dmabuf); msm_vidc_memory_put_dmabuf(inst, map->dmabuf);
msm_memory_free(inst, MSM_MEM_POOL_MAP, map); msm_memory_free(inst, MSM_MEM_POOL_MAP, map);
return rc; return rc;
} }
@@ -2414,7 +2414,7 @@ int msm_vidc_put_driver_buf(struct msm_vidc_inst *inst,
msm_vidc_unmap_driver_buf(inst, buf); msm_vidc_unmap_driver_buf(inst, buf);
msm_vidc_memory_put_dmabuf(buf->dmabuf); msm_vidc_memory_put_dmabuf(inst, buf->dmabuf);
/* delete the buffer from buffers->list */ /* delete the buffer from buffers->list */
list_del(&buf->list); list_del(&buf->list);
@@ -2456,7 +2456,7 @@ struct msm_vidc_buffer *msm_vidc_get_driver_buf(struct msm_vidc_inst *inst,
if (rc) if (rc)
goto error; goto error;
buf->dmabuf = msm_vidc_memory_get_dmabuf(buf->fd); buf->dmabuf = msm_vidc_memory_get_dmabuf(inst, buf->fd);
if (!buf->dmabuf) if (!buf->dmabuf)
goto error; goto error;
@@ -2470,7 +2470,7 @@ struct msm_vidc_buffer *msm_vidc_get_driver_buf(struct msm_vidc_inst *inst,
return buf; return buf;
error: error:
msm_vidc_memory_put_dmabuf(buf->dmabuf); msm_vidc_memory_put_dmabuf(inst, buf->dmabuf);
list_del(&buf->list); list_del(&buf->list);
msm_memory_free(inst, MSM_MEM_POOL_BUFFER, buf); msm_memory_free(inst, MSM_MEM_POOL_BUFFER, buf);
return NULL; return NULL;
@@ -4545,6 +4545,7 @@ void msm_vidc_destroy_buffers(struct msm_vidc_inst *inst)
struct msm_vidc_buffers *buffers; struct msm_vidc_buffers *buffers;
struct msm_vidc_buffer *buf, *dummy; struct msm_vidc_buffer *buf, *dummy;
struct msm_vidc_timestamp *ts, *dummy_ts; struct msm_vidc_timestamp *ts, *dummy_ts;
struct msm_memory_dmabuf *dbuf, *dummy_dbuf;
struct response_work *work, *dummy_work = NULL; struct response_work *work, *dummy_work = NULL;
static const enum msm_vidc_buffer_type ext_buf_types[] = { static const enum msm_vidc_buffer_type ext_buf_types[] = {
MSM_VIDC_BUF_INPUT, MSM_VIDC_BUF_INPUT,
@@ -4614,6 +4615,12 @@ void msm_vidc_destroy_buffers(struct msm_vidc_inst *inst)
msm_memory_free(inst, MSM_MEM_POOL_TIMESTAMP, ts); msm_memory_free(inst, MSM_MEM_POOL_TIMESTAMP, ts);
} }
list_for_each_entry_safe(dbuf, dummy_dbuf, &inst->dmabuf_tracker, list) {
i_vpr_e(inst, "%s: removing dma_buf %#x, refcount %u\n",
__func__, dbuf->dmabuf, dbuf->refcount);
msm_vidc_memory_put_dmabuf_completely(inst, dbuf);
}
list_for_each_entry_safe(work, dummy_work, &inst->response_works, list) { list_for_each_entry_safe(work, dummy_work, &inst->response_works, list) {
list_del(&work->list); list_del(&work->list);
kfree(work->data); kfree(work->data);

View File

@@ -60,28 +60,116 @@ exit:
return NULL; return NULL;
} }
struct dma_buf *msm_vidc_memory_get_dmabuf(int fd) struct dma_buf *msm_vidc_memory_get_dmabuf(struct msm_vidc_inst *inst, int fd)
{ {
struct dma_buf *dmabuf; struct msm_memory_dmabuf *buf = NULL;
struct dma_buf *dmabuf = NULL;
bool found = false;
if (!inst) {
d_vpr_e("%s: invalid params\n", __func__);
return NULL;
}
/* get local dmabuf ref for tracking */
dmabuf = dma_buf_get(fd); dmabuf = dma_buf_get(fd);
if (IS_ERR_OR_NULL(dmabuf)) { if (IS_ERR_OR_NULL(dmabuf)) {
d_vpr_e("Failed to get dmabuf for %d, error %ld\n", d_vpr_e("Failed to get dmabuf for %d, error %ld\n",
fd, PTR_ERR(dmabuf)); fd, PTR_ERR(dmabuf));
dmabuf = NULL; return NULL;
} }
/* track dmabuf - inc refcount if already present */
list_for_each_entry(buf, &inst->dmabuf_tracker, list) {
if (buf->dmabuf == dmabuf) {
buf->refcount++;
found = true;
break;
}
}
if (found) {
/* put local dmabuf ref */
dma_buf_put(dmabuf);
return dmabuf;
}
/* get tracker instance from pool */
buf = msm_memory_alloc(inst, MSM_MEM_POOL_DMABUF);
if (!buf) {
i_vpr_e(inst, "%s: dmabuf alloc failed\n", __func__);
dma_buf_put(dmabuf);
return NULL;
}
/* hold dmabuf strong ref in tracker */
buf->dmabuf = dmabuf;
buf->refcount = 1;
INIT_LIST_HEAD(&buf->list);
/* add new dmabuf entry to tracker */
list_add_tail(&buf->list, &inst->dmabuf_tracker);
return dmabuf; return dmabuf;
} }
void msm_vidc_memory_put_dmabuf(void *dmabuf) void msm_vidc_memory_put_dmabuf(struct msm_vidc_inst *inst, struct dma_buf *dmabuf)
{ {
if (!dmabuf) { struct msm_memory_dmabuf *buf = NULL;
d_vpr_e("%s: NULL dmabuf\n", __func__); bool found = false;
if (!inst || !dmabuf) {
d_vpr_e("%s: invalid params\n", __func__);
return; return;
} }
dma_buf_put((struct dma_buf *)dmabuf); /* track dmabuf - dec refcount if already present */
list_for_each_entry(buf, &inst->dmabuf_tracker, list) {
if (buf->dmabuf == dmabuf) {
buf->refcount--;
found = true;
break;
}
}
if (!found) {
i_vpr_e(inst, "%s: invalid dmabuf %#x\n", __func__, dmabuf);
return;
}
/* non-zero refcount - do nothing */
if (buf->refcount)
return;
/* remove dmabuf entry from tracker */
list_del(&buf->list);
/* release dmabuf strong ref from tracker */
dma_buf_put(buf->dmabuf);
/* put tracker instance back to pool */
msm_memory_free(inst, MSM_MEM_POOL_DMABUF, buf);
}
void msm_vidc_memory_put_dmabuf_completely(struct msm_vidc_inst *inst,
struct msm_memory_dmabuf *buf)
{
if (!inst || !buf) {
d_vpr_e("%s: invalid params\n", __func__);
return;
}
while (buf->refcount) {
buf->refcount--;
if (!buf->refcount) {
/* remove dmabuf entry from tracker */
list_del(&buf->list);
/* release dmabuf strong ref from tracker */
dma_buf_put(buf->dmabuf);
/* put tracker instance back to pool */
msm_memory_free(inst, MSM_MEM_POOL_DMABUF, buf);
break;
}
}
} }
int msm_vidc_memory_map(struct msm_vidc_core *core, struct msm_vidc_map *map) int msm_vidc_memory_map(struct msm_vidc_core *core, struct msm_vidc_map *map)
@@ -446,6 +534,7 @@ static struct msm_vidc_type_size_name buftype_size_name_arr[] = {
{MSM_MEM_POOL_MAP, sizeof(struct msm_vidc_map), "MSM_MEM_POOL_MAP" }, {MSM_MEM_POOL_MAP, sizeof(struct msm_vidc_map), "MSM_MEM_POOL_MAP" },
{MSM_MEM_POOL_ALLOC, sizeof(struct msm_vidc_alloc), "MSM_MEM_POOL_ALLOC" }, {MSM_MEM_POOL_ALLOC, sizeof(struct msm_vidc_alloc), "MSM_MEM_POOL_ALLOC" },
{MSM_MEM_POOL_TIMESTAMP, sizeof(struct msm_vidc_timestamp), "MSM_MEM_POOL_TIMESTAMP" }, {MSM_MEM_POOL_TIMESTAMP, sizeof(struct msm_vidc_timestamp), "MSM_MEM_POOL_TIMESTAMP" },
{MSM_MEM_POOL_DMABUF, sizeof(struct msm_memory_dmabuf), "MSM_MEM_POOL_DMABUF" },
}; };
int msm_memory_pools_init(struct msm_vidc_inst *inst) int msm_memory_pools_init(struct msm_vidc_inst *inst)