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:
@@ -131,6 +131,7 @@ struct msm_vidc_inst {
|
||||
struct workqueue_struct *response_workq;
|
||||
struct list_head response_works; /* list of struct response_work */
|
||||
struct list_head enc_input_crs;
|
||||
struct list_head dmabuf_tracker; /* list of struct msm_memory_dmabuf */
|
||||
bool once_per_session_set;
|
||||
bool ipsc_properties_set;
|
||||
bool opsc_properties_set;
|
||||
|
@@ -11,11 +11,18 @@
|
||||
struct msm_vidc_core;
|
||||
struct msm_vidc_inst;
|
||||
|
||||
struct msm_memory_dmabuf {
|
||||
struct list_head list;
|
||||
struct dma_buf *dmabuf;
|
||||
u32 refcount;
|
||||
};
|
||||
|
||||
enum msm_memory_pool_type {
|
||||
MSM_MEM_POOL_BUFFER = 0,
|
||||
MSM_MEM_POOL_MAP,
|
||||
MSM_MEM_POOL_ALLOC,
|
||||
MSM_MEM_POOL_TIMESTAMP,
|
||||
MSM_MEM_POOL_DMABUF,
|
||||
MSM_MEM_POOL_MAX,
|
||||
};
|
||||
|
||||
@@ -40,8 +47,12 @@ int msm_vidc_memory_map(struct msm_vidc_core *core,
|
||||
struct msm_vidc_map *map);
|
||||
int msm_vidc_memory_unmap(struct msm_vidc_core *core,
|
||||
struct msm_vidc_map *map);
|
||||
struct dma_buf *msm_vidc_memory_get_dmabuf(int fd);
|
||||
void msm_vidc_memory_put_dmabuf(void *dmabuf);
|
||||
struct dma_buf *msm_vidc_memory_get_dmabuf(struct msm_vidc_inst *inst,
|
||||
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);
|
||||
void msm_memory_pools_deinit(struct msm_vidc_inst *inst);
|
||||
void *msm_memory_alloc(struct msm_vidc_inst *inst,
|
||||
|
@@ -841,6 +841,7 @@ void *msm_vidc_open(void *vidc_core, u32 session_type)
|
||||
INIT_LIST_HEAD(&inst->children.list);
|
||||
INIT_LIST_HEAD(&inst->firmware.list);
|
||||
INIT_LIST_HEAD(&inst->enc_input_crs);
|
||||
INIT_LIST_HEAD(&inst->dmabuf_tracker);
|
||||
for (i = 0; i < MAX_SIGNAL; i++)
|
||||
init_completion(&inst->completions[i]);
|
||||
|
||||
|
@@ -2008,7 +2008,7 @@ int msm_vidc_memory_unmap_completely(struct msm_vidc_inst *inst,
|
||||
if (rc)
|
||||
break;
|
||||
if (!map->refcount) {
|
||||
msm_vidc_memory_put_dmabuf(map->dmabuf);
|
||||
msm_vidc_memory_put_dmabuf(inst, map->dmabuf);
|
||||
list_del(&map->list);
|
||||
msm_memory_free(inst, MSM_MEM_POOL_MAP, map);
|
||||
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__);
|
||||
|
||||
if (!map->refcount) {
|
||||
msm_vidc_memory_put_dmabuf(map->dmabuf);
|
||||
msm_vidc_memory_put_dmabuf(inst, map->dmabuf);
|
||||
list_del(&map->list);
|
||||
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 */
|
||||
if (!map->refcount) {
|
||||
msm_vidc_memory_put_dmabuf(map->dmabuf);
|
||||
msm_vidc_memory_put_dmabuf(inst, map->dmabuf);
|
||||
list_del(&map->list);
|
||||
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);
|
||||
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)
|
||||
return -EINVAL;
|
||||
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)) {
|
||||
rc = msm_vidc_get_delayed_unmap(inst, map);
|
||||
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);
|
||||
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_memory_put_dmabuf(buf->dmabuf);
|
||||
msm_vidc_memory_put_dmabuf(inst, buf->dmabuf);
|
||||
|
||||
/* delete the buffer from buffers->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)
|
||||
goto error;
|
||||
|
||||
buf->dmabuf = msm_vidc_memory_get_dmabuf(buf->fd);
|
||||
buf->dmabuf = msm_vidc_memory_get_dmabuf(inst, buf->fd);
|
||||
if (!buf->dmabuf)
|
||||
goto error;
|
||||
|
||||
@@ -2470,7 +2470,7 @@ struct msm_vidc_buffer *msm_vidc_get_driver_buf(struct msm_vidc_inst *inst,
|
||||
return buf;
|
||||
|
||||
error:
|
||||
msm_vidc_memory_put_dmabuf(buf->dmabuf);
|
||||
msm_vidc_memory_put_dmabuf(inst, buf->dmabuf);
|
||||
list_del(&buf->list);
|
||||
msm_memory_free(inst, MSM_MEM_POOL_BUFFER, buf);
|
||||
return NULL;
|
||||
@@ -4545,6 +4545,7 @@ void msm_vidc_destroy_buffers(struct msm_vidc_inst *inst)
|
||||
struct msm_vidc_buffers *buffers;
|
||||
struct msm_vidc_buffer *buf, *dummy;
|
||||
struct msm_vidc_timestamp *ts, *dummy_ts;
|
||||
struct msm_memory_dmabuf *dbuf, *dummy_dbuf;
|
||||
struct response_work *work, *dummy_work = NULL;
|
||||
static const enum msm_vidc_buffer_type ext_buf_types[] = {
|
||||
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);
|
||||
}
|
||||
|
||||
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_del(&work->list);
|
||||
kfree(work->data);
|
||||
|
@@ -60,28 +60,116 @@ exit:
|
||||
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);
|
||||
if (IS_ERR_OR_NULL(dmabuf)) {
|
||||
d_vpr_e("Failed to get dmabuf for %d, error %ld\n",
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
d_vpr_e("%s: NULL dmabuf\n", __func__);
|
||||
struct msm_memory_dmabuf *buf = NULL;
|
||||
bool found = false;
|
||||
|
||||
if (!inst || !dmabuf) {
|
||||
d_vpr_e("%s: invalid params\n", __func__);
|
||||
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)
|
||||
@@ -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_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_DMABUF, sizeof(struct msm_memory_dmabuf), "MSM_MEM_POOL_DMABUF" },
|
||||
};
|
||||
|
||||
int msm_memory_pools_init(struct msm_vidc_inst *inst)
|
||||
|
Reference in New Issue
Block a user