Selaa lähdekoodia

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 <[email protected]>
Govindaraj Rajagopal 4 vuotta sitten
vanhempi
sitoutus
7f0c2442c5

+ 1 - 0
driver/vidc/inc/msm_vidc_inst.h

@@ -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;

+ 13 - 2
driver/vidc/inc/msm_vidc_memory.h

@@ -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,

+ 1 - 0
driver/vidc/src/msm_vidc.c

@@ -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]);
 

+ 15 - 8
driver/vidc/src/msm_vidc_driver.c

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

+ 96 - 7
driver/vidc/src/msm_vidc_memory.c

@@ -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)
+{
+	struct msm_memory_dmabuf *buf = NULL;
+	bool found = false;
+
+	if (!inst || !dmabuf) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return;
+	}
+
+	/* 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 (!dmabuf) {
-		d_vpr_e("%s: NULL dmabuf\n", __func__);
+	if (!inst || !buf) {
+		d_vpr_e("%s: invalid params\n", __func__);
 		return;
 	}
 
-	dma_buf_put((struct dma_buf *)dmabuf);
+	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)