msm: vidc: Fix race condition between s_fmt & query_ctrl

- In s_fmt, there might be a codec change and here we are
  free the ctrl handler, at the same time client may call
  query ctrl and this will lead to null pointer access.
- To resolve null pointer access, do not free ctrl handler
  and add new codec controls to the same ctrl handler.

Change-Id: Iee87d5cb4d65e31d405cb1fc9f82bebab696d027
Signed-off-by: Vedang Nagar <quic_vnagar@quicinc.com>
This commit is contained in:
Vedang Nagar
2023-03-27 08:58:06 +05:30
parent 499b46ecf7
commit b4a1aefdd1
8 changed files with 86 additions and 40 deletions

View File

@@ -10,8 +10,8 @@
#include "msm_vidc_internal.h" #include "msm_vidc_internal.h"
#include "msm_vidc_inst.h" #include "msm_vidc_inst.h"
int msm_vidc_ctrl_init(struct msm_vidc_inst *inst); int msm_vidc_ctrl_handler_init(struct msm_vidc_inst *inst, bool init);
int msm_vidc_ctrl_deinit(struct msm_vidc_inst *inst); int msm_vidc_ctrl_handler_deinit(struct msm_vidc_inst *inst);
int msm_v4l2_op_s_ctrl(struct v4l2_ctrl *ctrl); int msm_v4l2_op_s_ctrl(struct v4l2_ctrl *ctrl);
int msm_v4l2_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl); int msm_v4l2_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl);
int msm_vidc_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl); int msm_vidc_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl);

View File

@@ -86,7 +86,8 @@ struct msm_vidc_core {
u32 intr_status; u32 intr_status;
u32 spur_count; u32 spur_count;
u32 reg_count; u32 reg_count;
u32 codecs_count; u32 enc_codecs_count;
u32 dec_codecs_count;
struct msm_vidc_core_capability capabilities[CORE_CAP_MAX+1]; struct msm_vidc_core_capability capabilities[CORE_CAP_MAX+1];
struct msm_vidc_inst_capability *inst_caps; struct msm_vidc_inst_capability *inst_caps;
struct msm_vidc_mem_addr sfr; struct msm_vidc_mem_addr sfr;

View File

@@ -87,7 +87,6 @@ struct msm_vidc_inst {
struct v4l2_fh event_handler; struct v4l2_fh event_handler;
struct v4l2_m2m_dev *m2m_dev; struct v4l2_m2m_dev *m2m_dev;
struct v4l2_m2m_ctx *m2m_ctx; struct v4l2_m2m_ctx *m2m_ctx;
struct v4l2_ctrl **ctrls;
u32 num_ctrls; u32 num_ctrls;
enum hfi_rate_control hfi_rc_type; enum hfi_rate_control hfi_rc_type;
enum hfi_layer_encoding_type hfi_layer_type; enum hfi_layer_encoding_type hfi_layer_type;

View File

@@ -1047,4 +1047,8 @@ struct msm_vidc_sfr {
u8 rg_data[1]; u8 rg_data[1];
}; };
struct msm_vidc_ctrl_data {
bool skip_s_ctrl;
};
#endif // _MSM_VIDC_INTERNAL_H_ #endif // _MSM_VIDC_INTERNAL_H_

View File

@@ -44,6 +44,10 @@ struct msm_vdec_prop_type_handle {
static int msm_vdec_codec_change(struct msm_vidc_inst *inst, u32 v4l2_codec) static int msm_vdec_codec_change(struct msm_vidc_inst *inst, u32 v4l2_codec)
{ {
int rc = 0; int rc = 0;
bool session_init = false;
if (!inst->codec)
session_init = true;
if (inst->codec && inst->fmts[INPUT_PORT].fmt.pix_mp.pixelformat == v4l2_codec) if (inst->codec && inst->fmts[INPUT_PORT].fmt.pix_mp.pixelformat == v4l2_codec)
return 0; return 0;
@@ -68,11 +72,7 @@ static int msm_vdec_codec_change(struct msm_vidc_inst *inst, u32 v4l2_codec)
if (rc) if (rc)
goto exit; goto exit;
rc = msm_vidc_ctrl_deinit(inst); rc = msm_vidc_ctrl_handler_init(inst, session_init);
if (rc)
goto exit;
rc = msm_vidc_ctrl_init(inst);
if(rc) if(rc)
goto exit; goto exit;
@@ -2832,7 +2832,7 @@ int msm_vdec_inst_deinit(struct msm_vidc_inst *inst)
} }
/* cancel pending batch work */ /* cancel pending batch work */
cancel_batch_work(inst); cancel_batch_work(inst);
rc = msm_vidc_ctrl_deinit(inst); rc = msm_vidc_ctrl_handler_deinit(inst);
if (rc) if (rc)
return rc; return rc;

View File

@@ -62,6 +62,10 @@ struct msm_venc_prop_type_handle {
static int msm_venc_codec_change(struct msm_vidc_inst *inst, u32 v4l2_codec) static int msm_venc_codec_change(struct msm_vidc_inst *inst, u32 v4l2_codec)
{ {
int rc = 0; int rc = 0;
bool session_init = false;
if (!inst->codec)
session_init = true;
if (inst->codec && inst->fmts[OUTPUT_PORT].fmt.pix_mp.pixelformat == v4l2_codec) if (inst->codec && inst->fmts[OUTPUT_PORT].fmt.pix_mp.pixelformat == v4l2_codec)
return 0; return 0;
@@ -86,12 +90,8 @@ static int msm_venc_codec_change(struct msm_vidc_inst *inst, u32 v4l2_codec)
if (rc) if (rc)
goto exit; goto exit;
rc = msm_vidc_ctrl_deinit(inst); rc = msm_vidc_ctrl_handler_init(inst, session_init);
if (rc) if(rc)
goto exit;
rc = msm_vidc_ctrl_init(inst);
if (rc)
goto exit; goto exit;
rc = msm_vidc_update_buffer_count(inst, INPUT_PORT); rc = msm_vidc_update_buffer_count(inst, INPUT_PORT);
@@ -2018,7 +2018,7 @@ int msm_venc_inst_deinit(struct msm_vidc_inst *inst)
d_vpr_e("%s: invalid params\n", __func__); d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL; return -EINVAL;
} }
rc = msm_vidc_ctrl_deinit(inst); rc = msm_vidc_ctrl_handler_deinit(inst);
if (rc) if (rc)
return rc; return rc;

View File

@@ -466,7 +466,7 @@ error:
return rc; return rc;
} }
int msm_vidc_ctrl_deinit(struct msm_vidc_inst *inst) int msm_vidc_ctrl_handler_deinit(struct msm_vidc_inst *inst)
{ {
if (!inst) { if (!inst) {
d_vpr_e("%s: invalid parameters\n", __func__); d_vpr_e("%s: invalid parameters\n", __func__);
@@ -475,13 +475,11 @@ int msm_vidc_ctrl_deinit(struct msm_vidc_inst *inst)
i_vpr_h(inst, "%s(): num ctrls %d\n", __func__, inst->num_ctrls); i_vpr_h(inst, "%s(): num ctrls %d\n", __func__, inst->num_ctrls);
v4l2_ctrl_handler_free(&inst->ctrl_handler); v4l2_ctrl_handler_free(&inst->ctrl_handler);
memset(&inst->ctrl_handler, 0, sizeof(struct v4l2_ctrl_handler)); memset(&inst->ctrl_handler, 0, sizeof(struct v4l2_ctrl_handler));
msm_vidc_vmem_free((void **)&inst->ctrls);
inst->ctrls = NULL;
return 0; return 0;
} }
int msm_vidc_ctrl_init(struct msm_vidc_inst *inst) int msm_vidc_ctrl_handler_init(struct msm_vidc_inst *inst, bool init)
{ {
int rc = 0; int rc = 0;
struct msm_vidc_inst_cap *cap; struct msm_vidc_inst_cap *cap;
@@ -489,6 +487,7 @@ int msm_vidc_ctrl_init(struct msm_vidc_inst *inst)
int idx = 0; int idx = 0;
struct v4l2_ctrl_config ctrl_cfg = {0}; struct v4l2_ctrl_config ctrl_cfg = {0};
int num_ctrls = 0, ctrl_idx = 0; int num_ctrls = 0, ctrl_idx = 0;
u64 codecs_count, step_or_mask;
if (!inst || !inst->core) { if (!inst || !inst->core) {
d_vpr_e("%s: invalid params\n", __func__); d_vpr_e("%s: invalid params\n", __func__);
@@ -511,16 +510,18 @@ int msm_vidc_ctrl_init(struct msm_vidc_inst *inst)
__func__); __func__);
return -EINVAL; return -EINVAL;
} }
rc = msm_vidc_vmem_alloc(num_ctrls * sizeof(struct v4l2_ctrl *),
(void **)&inst->ctrls, __func__);
if (rc)
return rc;
rc = v4l2_ctrl_handler_init(&inst->ctrl_handler, num_ctrls); if (init) {
if (rc) { codecs_count = is_encode_session(inst) ?
i_vpr_e(inst, "control handler init failed, %d\n", core->enc_codecs_count :
inst->ctrl_handler.error); core->dec_codecs_count;
goto error; rc = v4l2_ctrl_handler_init(&inst->ctrl_handler,
INST_CAP_MAX * codecs_count);
if (rc) {
i_vpr_e(inst, "control handler init failed, %d\n",
inst->ctrl_handler.error);
goto error;
}
} }
for (idx = 0; idx < INST_CAP_MAX; idx++) { for (idx = 0; idx < INST_CAP_MAX; idx++) {
@@ -550,6 +551,32 @@ int msm_vidc_ctrl_init(struct msm_vidc_inst *inst)
memset(&ctrl_cfg, 0, sizeof(struct v4l2_ctrl_config)); memset(&ctrl_cfg, 0, sizeof(struct v4l2_ctrl_config));
/*
* few controls might have been already initialized in instance initialization,
* so modify the range values for them instead of initializing them again
*/
if (!init) {
struct msm_vidc_ctrl_data ctrl_priv_data;
ctrl = v4l2_ctrl_find(&inst->ctrl_handler, cap[idx].v4l2_id);
if (ctrl) {
step_or_mask = (cap[idx].flags & CAP_FLAG_MENU) ?
~(cap[idx].step_or_mask) :
cap[idx].step_or_mask;
memset(&ctrl_priv_data, 0, sizeof(struct msm_vidc_ctrl_data));
ctrl_priv_data.skip_s_ctrl = true;
ctrl->priv = &ctrl_priv_data;
v4l2_ctrl_modify_range(ctrl,
cap[idx].min,
cap[idx].max,
step_or_mask,
cap[idx].value);
/* reset private data to null to ensure s_ctrl not skipped */
ctrl->priv = NULL;
continue;
}
}
if (is_priv_ctrl(cap[idx].v4l2_id)) { if (is_priv_ctrl(cap[idx].v4l2_id)) {
/* add private control */ /* add private control */
ctrl_cfg.def = cap[idx].value; ctrl_cfg.def = cap[idx].value;
@@ -631,7 +658,6 @@ int msm_vidc_ctrl_init(struct msm_vidc_inst *inst)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
inst->ctrls[ctrl_idx] = ctrl;
ctrl_idx++; ctrl_idx++;
} }
inst->num_ctrls = num_ctrls; inst->num_ctrls = num_ctrls;
@@ -639,7 +665,7 @@ int msm_vidc_ctrl_init(struct msm_vidc_inst *inst)
return 0; return 0;
error: error:
msm_vidc_ctrl_deinit(inst); msm_vidc_ctrl_handler_deinit(inst);
return rc; return rc;
} }
@@ -894,6 +920,7 @@ int msm_vidc_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
int msm_v4l2_op_s_ctrl(struct v4l2_ctrl *ctrl) int msm_v4l2_op_s_ctrl(struct v4l2_ctrl *ctrl)
{ {
struct msm_vidc_inst *inst; struct msm_vidc_inst *inst;
struct msm_vidc_ctrl_data *priv_ctrl_data;
int rc = 0; int rc = 0;
if (!ctrl) { if (!ctrl) {
@@ -901,6 +928,17 @@ int msm_v4l2_op_s_ctrl(struct v4l2_ctrl *ctrl)
return -EINVAL; return -EINVAL;
} }
/*
* v4l2_ctrl_modify_range may internally call s_ctrl
* which will again try to acquire lock leading to deadlock,
* Add check to avoid such scenario.
*/
priv_ctrl_data = ctrl->priv ? ctrl->priv : NULL;
if (priv_ctrl_data && priv_ctrl_data->skip_s_ctrl) {
d_vpr_l("%s: skip s_ctrl (%s)\n", __func__, ctrl->name);
return 0;
}
inst = container_of(ctrl->handler, struct msm_vidc_inst, ctrl_handler); inst = container_of(ctrl->handler, struct msm_vidc_inst, ctrl_handler);
inst = get_inst_ref(g_core, inst); inst = get_inst_ref(g_core, inst);
if (!inst) { if (!inst) {

View File

@@ -3935,6 +3935,7 @@ int msm_vidc_get_inst_capability(struct msm_vidc_inst *inst)
{ {
int rc = 0; int rc = 0;
int i; int i;
u32 codecs_count = 0;
struct msm_vidc_core *core; struct msm_vidc_core *core;
if (!inst || !inst->core) { if (!inst || !inst->core) {
@@ -3943,7 +3944,9 @@ int msm_vidc_get_inst_capability(struct msm_vidc_inst *inst)
} }
core = inst->core; core = inst->core;
for (i = 0; i < core->codecs_count; i++) { codecs_count = core->enc_codecs_count + core->dec_codecs_count;
for (i = 0; i < codecs_count; i++) {
if (core->inst_caps[i].domain == inst->domain && if (core->inst_caps[i].domain == inst->domain &&
core->inst_caps[i].codec == inst->codec) { core->inst_caps[i].codec == inst->codec) {
i_vpr_h(inst, i_vpr_h(inst,
@@ -4061,8 +4064,8 @@ int msm_vidc_init_instance_caps(struct msm_vidc_core *core)
{ {
int rc = 0; int rc = 0;
u8 enc_valid_codecs, dec_valid_codecs; u8 enc_valid_codecs, dec_valid_codecs;
u8 count_bits, enc_codec_count; u8 count_bits, codecs_count = 0;
u8 codecs_count = 0; u8 enc_codecs_count = 0, dec_codecs_count = 0;
int i, j, check_bit; int i, j, check_bit;
int num_platform_cap_data, num_platform_cap_dependency_data; int num_platform_cap_data, num_platform_cap_dependency_data;
struct msm_platform_inst_capability *platform_cap_data = NULL; struct msm_platform_inst_capability *platform_cap_data = NULL;
@@ -4092,14 +4095,15 @@ int msm_vidc_init_instance_caps(struct msm_vidc_core *core)
enc_valid_codecs = core->capabilities[ENC_CODECS].value; enc_valid_codecs = core->capabilities[ENC_CODECS].value;
count_bits = enc_valid_codecs; count_bits = enc_valid_codecs;
COUNT_BITS(count_bits, codecs_count); COUNT_BITS(count_bits, enc_codecs_count);
enc_codec_count = codecs_count; core->enc_codecs_count = enc_codecs_count;
dec_valid_codecs = core->capabilities[DEC_CODECS].value; dec_valid_codecs = core->capabilities[DEC_CODECS].value;
count_bits = dec_valid_codecs; count_bits = dec_valid_codecs;
COUNT_BITS(count_bits, codecs_count); COUNT_BITS(count_bits, dec_codecs_count);
core->dec_codecs_count = dec_codecs_count;
core->codecs_count = codecs_count; codecs_count = enc_codecs_count + dec_codecs_count;
rc = msm_vidc_vmem_alloc(codecs_count * sizeof(struct msm_vidc_inst_capability), rc = msm_vidc_vmem_alloc(codecs_count * sizeof(struct msm_vidc_inst_capability),
(void **)&core->inst_caps, __func__); (void **)&core->inst_caps, __func__);
if (rc) if (rc)
@@ -4107,7 +4111,7 @@ int msm_vidc_init_instance_caps(struct msm_vidc_core *core)
check_bit = 0; check_bit = 0;
/* determine codecs for enc domain */ /* determine codecs for enc domain */
for (i = 0; i < enc_codec_count; i++) { for (i = 0; i < enc_codecs_count; i++) {
while (check_bit < (sizeof(enc_valid_codecs) * 8)) { while (check_bit < (sizeof(enc_valid_codecs) * 8)) {
if (enc_valid_codecs & BIT(check_bit)) { if (enc_valid_codecs & BIT(check_bit)) {
core->inst_caps[i].domain = MSM_VIDC_ENCODER; core->inst_caps[i].domain = MSM_VIDC_ENCODER;