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 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;
|
||||||
|
@@ -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,
|
||||||
|
@@ -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]);
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
|
@@ -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)
|
||||||
|
Reference in New Issue
Block a user