From b431d81759b039efa661e663d82b7a77abe71cfc Mon Sep 17 00:00:00 2001 From: ming_qian <ming.qian@nxp.com> Date: Thu, 14 Mar 2019 10:23:23 +0800 Subject: [PATCH] MLK-21141: VPU Decoder: driver may crash when alloc or free dma It's caused by allocating and freeing dma buffer in the same time. and there is no any protection for this case. refine the driver to avoid this case. destroy workqueue before release resource Signed-off-by: ming_qian <ming.qian@nxp.com> --- drivers/mxc/vpu_malone/vpu_b0.c | 137 +++++++++++++++++++++----------- drivers/mxc/vpu_malone/vpu_b0.h | 1 + 2 files changed, 90 insertions(+), 48 deletions(-) diff --git a/drivers/mxc/vpu_malone/vpu_b0.c b/drivers/mxc/vpu_malone/vpu_b0.c index 2a4606d0f934c7..a811011cc20c87 100644 --- a/drivers/mxc/vpu_malone/vpu_b0.c +++ b/drivers/mxc/vpu_malone/vpu_b0.c @@ -908,26 +908,28 @@ static int free_dma_buffer(struct vpu_ctx *ctx, struct dma_buffer *buffer) init_dma_buffer(buffer); return 0; } -static int alloc_mbi_buffer(struct vpu_ctx *ctx, - struct queue_data *This, - u_int32 count) + +static u_int32 get_mbi_size(struct queue_data *queue) { - u_int32 uAlign = 0x800-1; - u_int32 mbi_num; + u_int32 uAlign = 0x800; u_int32 mbi_size; + + mbi_size = (queue->sizeimage[0] + queue->sizeimage[1])/4; + return ALIGN(mbi_size, uAlign); +} + +static int alloc_mbi_buffer(struct vpu_ctx *ctx) +{ u_int32 ret = 0; u_int32 i; - if (count >= MAX_MBI_NUM) - mbi_num = MAX_MBI_NUM; - else - mbi_num = count; - ctx->mbi_num = mbi_num; + for (i = 0; i < ctx->mbi_num; i++) { + if (ctx->mbi_buffer[i].dma_size >= ctx->mbi_size) + continue; - mbi_size = (This->sizeimage[0]+This->sizeimage[1])/4; - mbi_size = ((mbi_size + uAlign) & ~uAlign); - for (i = 0; i < mbi_num; i++) { - ctx->mbi_buffer[i].dma_size = mbi_size; + free_dma_buffer(ctx, &ctx->mbi_buffer[i]); + init_dma_buffer(&ctx->mbi_buffer[i]); + ctx->mbi_buffer[i].dma_size = ctx->mbi_size; ret = alloc_dma_buffer(ctx, &ctx->mbi_buffer[i]); if (ret) { vpu_dbg(LVL_ERR, "error: alloc mbi buffer fail\n"); @@ -938,6 +940,57 @@ static int alloc_mbi_buffer(struct vpu_ctx *ctx, return ret; } +static int alloc_dcp_buffer(struct vpu_ctx *ctx) +{ + u_int32 i; + int ret = 0; + + for (i = 0; i < ARRAY_SIZE(ctx->dcp_buffer); i++) { + if (ctx->dcp_buffer[i].dma_size >= DCP_SIZE) + continue; + + free_dma_buffer(ctx, &ctx->dcp_buffer[i]); + init_dma_buffer(&ctx->dcp_buffer[i]); + ctx->dcp_buffer[i].dma_size = DCP_SIZE; + ret = alloc_dma_buffer(ctx, &ctx->dcp_buffer[i]); + if (ret) { + vpu_dbg(LVL_ERR, "error: alloc dcp buffer fail\n"); + return ret; + } + } + + return ret; +} + +static int alloc_decoder_buffer(struct vpu_ctx *ctx) +{ + int ret; + + ret = alloc_mbi_buffer(ctx); + if (ret) + return ret; + + ret = alloc_dcp_buffer(ctx); + if (ret) + return ret; + + return 0; +} + +static int free_decoeder_buffer(struct vpu_ctx *ctx) +{ + u_int32 i; + + for (i = 0; i < ARRAY_SIZE(ctx->mbi_buffer); i++) + free_dma_buffer(ctx, &ctx->mbi_buffer[i]); + for (i = 0; i < ARRAY_SIZE(ctx->dcp_buffer); i++) + free_dma_buffer(ctx, &ctx->dcp_buffer[i]); + free_dma_buffer(ctx, &ctx->stream_buffer); + free_dma_buffer(ctx, &ctx->udata_buffer); + + return 0; +} + void clear_vb2_buf(struct queue_data *q_data) { struct vb2_data_req *p_data_req; @@ -961,7 +1014,6 @@ static int v4l2_ioctl_reqbufs(struct file *file, { struct vpu_ctx *ctx = v4l2_fh_to_ctx(fh); struct queue_data *q_data; - u_int32 i; int ret; vpu_dbg(LVL_INFO, "%s()\n", __func__); @@ -980,23 +1032,15 @@ static int v4l2_ioctl_reqbufs(struct file *file, ctx->buffer_null = false; ret = vpu_dec_queue_reqbufs(q_data, reqbuf); - if (!ret) { - if (reqbuf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - - if (reqbuf->count == 0) { - for (i = 0; i < MAX_MBI_NUM; i++) { - free_dma_buffer(ctx, &ctx->mbi_buffer[i]); - init_dma_buffer(&ctx->mbi_buffer[i]); - } - } else { - for (i = 0; i < reqbuf->count; i++) - q_data->vb2_reqs[i].status = FRAME_ALLOC; - ret = alloc_mbi_buffer(ctx, q_data, reqbuf->count); - } - } - } else if (reqbuf->count != 0) - vpu_dbg(LVL_ERR, "error: %s() can't request (%d) buffer ret=%d\n", + if (ret) { + vpu_dbg(LVL_ERR, "error: %s() can't request (%d) buffer : %d\n", __func__, reqbuf->count, ret); + return ret; + } + if (!V4L2_TYPE_IS_OUTPUT(reqbuf->type) && reqbuf->count > 0) { + ctx->mbi_size = get_mbi_size(q_data); + ctx->mbi_num = min_t(u32, reqbuf->count, MAX_MBI_NUM); + } return ret; } @@ -2299,15 +2343,17 @@ static void vpu_api_event_handler(struct vpu_ctx *ctx, u_int32 uStrIdx, u_int32 struct vb2_data_req; bool buffer_flag = false; + if (alloc_decoder_buffer(ctx)) { + vpu_dbg(LVL_ERR, "alloc decoder buffer fail\n"); + break; + } + vpu_dbg(LVL_INFO, "VID_API_EVENT_REQ_FRAME_BUFF, type=%d, size=%ld\n", pFSREQ->eType, sizeof(MEDIA_PLAYER_FSREQ)); if (pFSREQ->eType == MEDIAIP_DCP_REQ) { if (ctx->dcp_count >= MAX_DCP_NUM) { vpu_dbg(LVL_ERR, "error: request dcp buffers number is over MAX_DCP_NUM\n"); break; } - ctx->dcp_buffer[ctx->dcp_count].dma_size = DCP_SIZE; - alloc_dma_buffer(ctx, &ctx->dcp_buffer[ctx->dcp_count]); - local_cmddata[0] = ctx->dcp_count; local_cmddata[1] = ctx->dcp_buffer[ctx->dcp_count].dma_phy; local_cmddata[2] = DCP_SIZE; @@ -3388,6 +3434,7 @@ static int init_vpu_buffer(struct vpu_ctx *ctx) init_dma_buffer(&ctx->mbi_buffer[i]); ctx->mbi_count = 0; ctx->mbi_num = 0; + ctx->mbi_size = 0; init_dma_buffer(&ctx->stream_buffer); init_dma_buffer(&ctx->udata_buffer); @@ -3596,7 +3643,6 @@ static int v4l2_release(struct file *filp) struct video_device *vdev = video_devdata(filp); struct vpu_dev *dev = video_get_drvdata(vdev); struct vpu_ctx *ctx = v4l2_fh_to_ctx(filp->private_data); - u_int32 i; vpu_dbg(LVL_EVENT, "ctx[%d]: v4l2_release() - stopped(%d), finished(%d), eos_added(%d), total frame: %d\n", ctx->str_index, ctx->firmware_stopped, ctx->firmware_finished, ctx->eos_stop_added, ctx->frm_total_num); @@ -3612,20 +3658,20 @@ static int v4l2_release(struct file *filp) } } else vpu_dbg(LVL_INFO, "v4l2_release() - stopped(%d): skip VID_API_CMD_STOP\n", ctx->firmware_stopped); + + mutex_lock(&ctx->instance_mutex); + ctx->ctx_released = true; + kfifo_free(&ctx->msg_fifo); + destroy_workqueue(ctx->instance_wq); + mutex_unlock(&ctx->instance_mutex); + if (vpu_frmcrcdump_ena) close_crc_file(ctx); release_queue_data(ctx); ctrls_delete_decoder(ctx); v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); - for (i = 0; i < MAX_DCP_NUM; i++) - free_dma_buffer(ctx, &ctx->dcp_buffer[i]); - - for (i = 0; i < MAX_MBI_NUM; i++) - free_dma_buffer(ctx, &ctx->mbi_buffer[i]); - - free_dma_buffer(ctx, &ctx->stream_buffer); - free_dma_buffer(ctx, &ctx->udata_buffer); + free_decoeder_buffer(ctx); if (atomic64_read(&ctx->statistic.total_dma_size) != 0) vpu_dbg(LVL_ERR, "error: memory leak for vpu dma alloc buffer\n"); if (ctx->pSeqinfo) { @@ -3633,11 +3679,6 @@ static int v4l2_release(struct file *filp) ctx->pSeqinfo = NULL; atomic64_sub(sizeof(MediaIPFW_Video_SeqInfo), &ctx->statistic.total_alloc_size); } - mutex_lock(&ctx->instance_mutex); - ctx->ctx_released = true; - kfifo_free(&ctx->msg_fifo); - destroy_workqueue(ctx->instance_wq); - mutex_unlock(&ctx->instance_mutex); pm_runtime_put_sync(ctx->dev->generic_dev); diff --git a/drivers/mxc/vpu_malone/vpu_b0.h b/drivers/mxc/vpu_malone/vpu_b0.h index 8a805e347164a3..222a5584c3173b 100644 --- a/drivers/mxc/vpu_malone/vpu_b0.h +++ b/drivers/mxc/vpu_malone/vpu_b0.h @@ -293,6 +293,7 @@ struct vpu_ctx { wait_queue_head_t buffer_wq; u_int32 mbi_count; u_int32 mbi_num; + u_int32 mbi_size; u_int32 dcp_count; struct dma_buffer dpb_buffer; struct dma_buffer dcp_buffer[MAX_DCP_NUM]; -- GitLab