Re: [V2] venus: vdec: decoded picture buffer handling during reconfig sequence

From: Stanimir Varbanov
Date: Thu Jul 29 2021 - 05:50:13 EST




On 7/15/21 3:35 PM, Mansur Alisha Shaik wrote:
> In existing implementation, driver is freeing and un-mapping all the
> decoded picture buffers(DPB) as part of dynamic resolution change(DRC)
> handling. As a result, when firmware try to access the DPB buffer, from
> previous sequence, SMMU context fault is seen due to the buffer being
> already unmapped.
>
> With this change, driver defines ownership of each DPB buffer. If a buffer
> is owned by firmware, driver would skip from un-mapping the same.
>
> Signed-off-by: Mansur Alisha Shaik <mansur@xxxxxxxxxxxxxx>
>
> Changes in V2:
> - Fixed proto type warnings reported by kernel test robot
> ---
> drivers/media/platform/qcom/venus/core.h | 3 ++
> drivers/media/platform/qcom/venus/helpers.c | 38 ++++++++++++++++-----
> drivers/media/platform/qcom/venus/helpers.h | 18 ++++++++++
> drivers/media/platform/qcom/venus/vdec.c | 25 +++++++++++++-
> 4 files changed, 74 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h
> index 8df2d497d706..7ecbf9ed1acb 100644
> --- a/drivers/media/platform/qcom/venus/core.h
> +++ b/drivers/media/platform/qcom/venus/core.h
> @@ -450,6 +450,7 @@ struct venus_inst {
> bool next_buf_last;
> bool drain_active;
> enum venus_inst_modes flags;
> + u32 dpb_out_tag[VB2_MAX_FRAME];
> };
>
> #define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX)
> @@ -484,4 +485,6 @@ venus_caps_by_codec(struct venus_core *core, u32 codec, u32 domain)
> return NULL;
> }
>
> +void dpb_out_tag_init(struct venus_inst *inst);
> +
> #endif
> diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c
> index 1fe6d463dc99..4d308be712d7 100644
> --- a/drivers/media/platform/qcom/venus/helpers.c
> +++ b/drivers/media/platform/qcom/venus/helpers.c
> @@ -21,14 +21,6 @@
> #define NUM_MBS_720P (((1280 + 15) >> 4) * ((720 + 15) >> 4))
> #define NUM_MBS_4K (((4096 + 15) >> 4) * ((2304 + 15) >> 4))
>
> -struct intbuf {
> - struct list_head list;
> - u32 type;
> - size_t size;
> - void *va;
> - dma_addr_t da;
> - unsigned long attrs;
> -};
>
> bool venus_helper_check_codec(struct venus_inst *inst, u32 v4l2_pixfmt)
> {
> @@ -95,9 +87,16 @@ int venus_helper_queue_dpb_bufs(struct venus_inst *inst)
> fdata.device_addr = buf->da;
> fdata.buffer_type = buf->type;
>
> + if (buf->owned_by == FIRMWARE)
> + continue;
> +
> + fdata.clnt_data = buf->dpb_out_tag;
> +
> ret = hfi_session_process_buf(inst, &fdata);
> if (ret)
> goto fail;
> +
> + buf->owned_by = FIRMWARE;
> }
>
> fail:
> @@ -110,18 +109,37 @@ int venus_helper_free_dpb_bufs(struct venus_inst *inst)
> struct intbuf *buf, *n;
>
> list_for_each_entry_safe(buf, n, &inst->dpbbufs, list) {
> + if (buf->owned_by == FIRMWARE)
> + continue;
> +
> + inst->dpb_out_tag[buf->dpb_out_tag - VB2_MAX_FRAME] = 0;

This looks wrong. The dpb_out_tag is in range of 0 .. 31, then for
dpb_out_tag = 0 you will have negative array index. Could you revisit that.

> +
> list_del_init(&buf->list);
> dma_free_attrs(inst->core->dev, buf->size, buf->va, buf->da,
> buf->attrs);
> kfree(buf);
> }
>
> - INIT_LIST_HEAD(&inst->dpbbufs);

Instead of delete INIT_LIST_HEAD you can do:

if (list_empty(&inst->dpbbufs)
INIT_LIST_HEAD(&inst->dpbbufs);

>
> return 0;
> }
> EXPORT_SYMBOL_GPL(venus_helper_free_dpb_bufs);
>
> +int venus_helper_get_free_dpb_tag(struct venus_inst *inst)
> +{
> + u32 i;
> +
> + for (i = 0; i < VB2_MAX_FRAME; i++) {
> + if (inst->dpb_out_tag[i] == 0) {
> + inst->dpb_out_tag[i] = i + VB2_MAX_FRAME;
> + return inst->dpb_out_tag[i];
> + }
> + }

Can we use kernel API for that? I think ida_alloc|free|destroy would
work nicely and also will save a lot of code lines.

> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(venus_helper_get_free_dpb_tag);
> +
> int venus_helper_alloc_dpb_bufs(struct venus_inst *inst)
> {
> struct venus_core *core = inst->core;
> @@ -171,6 +189,8 @@ int venus_helper_alloc_dpb_bufs(struct venus_inst *inst)
> ret = -ENOMEM;
> goto fail;
> }
> + buf->owned_by = DRIVER;
> + buf->dpb_out_tag = venus_helper_get_free_dpb_tag(inst);
>
> list_add_tail(&buf->list, &inst->dpbbufs);
> }
> diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h
> index e6269b4be3af..ea87a552c3ca 100644
> --- a/drivers/media/platform/qcom/venus/helpers.h
> +++ b/drivers/media/platform/qcom/venus/helpers.h
> @@ -8,6 +8,22 @@
>
> #include <media/videobuf2-v4l2.h>
>
> +enum dpb_buf_owner {
> + DRIVER,
> + FIRMWARE,
> +};
> +
> +struct intbuf {
> + struct list_head list;
> + u32 type;
> + size_t size;
> + void *va;
> + dma_addr_t da;
> + unsigned long attrs;
> + enum dpb_buf_owner owned_by;
> + u32 dpb_out_tag;
> +};
> +

If you make dpb buffer manipulations as venus_helpers you don't need to
move the prototype of the structure outside of helpers.c.

> struct venus_inst;
> struct venus_core;
>
> @@ -66,4 +82,6 @@ int venus_helper_get_profile_level(struct venus_inst *inst, u32 *profile, u32 *l
> int venus_helper_set_profile_level(struct venus_inst *inst, u32 profile, u32 level);
> int venus_helper_set_stride(struct venus_inst *inst, unsigned int aligned_width,
> unsigned int aligned_height);
> +int venus_helper_get_free_dpb_tag(struct venus_inst *inst);
> +
> #endif
> diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c
> index 198e47eb63f4..ba46085301ee 100644
> --- a/drivers/media/platform/qcom/venus/vdec.c
> +++ b/drivers/media/platform/qcom/venus/vdec.c
> @@ -1297,6 +1297,7 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type,
> struct vb2_v4l2_buffer *vbuf;
> struct vb2_buffer *vb;
> unsigned int type;
> + struct intbuf *dpb_buf;
>
> vdec_pm_touch(inst);
>
> @@ -1306,8 +1307,18 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type,
> type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
>
> vbuf = venus_helper_find_buf(inst, type, tag);
> - if (!vbuf)
> + if (!vbuf) {
> + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
> + buf_type == HFI_BUFFER_OUTPUT) {
> + list_for_each_entry(dpb_buf, &inst->dpbbufs, list) {
> + if (dpb_buf->dpb_out_tag == tag) {
> + dpb_buf->owned_by = DRIVER;
> + break;
> + }
> + }
> + }

Please make this chuck of code a new venus_helper_find_dpb_buf()

> return;
> + }
>
> vbuf->flags = flags;
> vbuf->field = V4L2_FIELD_NONE;
> @@ -1542,6 +1553,14 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq,
> return vb2_queue_init(dst_vq);
> }
>
> +void dpb_out_tag_init(struct venus_inst *inst)
> +{
> + u32 i;
> +
> + for (i = 0; i < VB2_MAX_FRAME; i++)
> + inst->dpb_out_tag[i] = 0;

This initialization cycle can be moved in vdec_inst_init(), but if you
migrate to ida_xxx APIs this will be part of venus_helper_alloc_dpb_bufs()

> +}
> +
> static int vdec_open(struct file *file)
> {
> struct venus_core *core = video_drvdata(file);
> @@ -1580,6 +1599,8 @@ static int vdec_open(struct file *file)
>
> vdec_inst_init(inst);
>
> + dpb_out_tag_init(inst);
> +
> /*
> * create m2m device for every instance, the m2m context scheduling
> * is made by firmware side so we do not need to care about.
> @@ -1622,6 +1643,8 @@ static int vdec_close(struct file *file)
>
> vdec_pm_get(inst);
>
> + venus_helper_free_dpb_bufs(inst);
> + INIT_LIST_HEAD(&inst->dpbbufs);
> v4l2_m2m_ctx_release(inst->m2m_ctx);
> v4l2_m2m_release(inst->m2m_dev);
> vdec_ctrl_deinit(inst);
>
> base-commit: c1a6d08348fc729e59776fe22878d4ce8fb97cde
>

--
regards,
Stan