From: Paloma Arellano quic_parellan@quicinc.com
stable inclusion from stable-v6.7.4 commit 14f109bf74dd67e1d0469fed859c8e506b0df53f category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I96GNY CVE: CVE-2023-52586
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=l...
--------------------------------
[ Upstream commit 45284ff733e4caf6c118aae5131eb7e7cf3eea5a ]
Add a mutex lock to control vblank irq to synchronize vblank enable/disable operations happening from different threads to prevent race conditions while registering/unregistering the vblank irq callback.
v4: -Removed vblank_ctl_lock from dpu_encoder_virt, so it is only a parameter of dpu_encoder_phys. -Switch from atomic refcnt to a simple int counter as mutex has now been added v3: Mistakenly did not change wording in last version. It is done now. v2: Slightly changed wording of commit message
Signed-off-by: Paloma Arellano quic_parellan@quicinc.com Reviewed-by: Dmitry Baryshkov dmitry.baryshkov@linaro.org Patchwork: https://patchwork.freedesktop.org/patch/571854/ Link: https://lore.kernel.org/r/20231212231101.9240-2-quic_parellan@quicinc.com Signed-off-by: Dmitry Baryshkov dmitry.baryshkov@linaro.org Signed-off-by: Sasha Levin sashal@kernel.org Signed-off-by: Cheng Yu serein.chengyu@huawei.com --- .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 4 ++- .../drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c | 28 +++++++++++----- .../drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | 32 +++++++++++++------ 3 files changed, 45 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h index f8f25157f635..f32a05355e78 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h @@ -194,6 +194,7 @@ struct dpu_encoder_irq { * @hw_intf: Hardware interface to the intf registers * @dpu_kms: Pointer to the dpu_kms top level * @cached_mode: DRM mode cached at mode_set time, acted on in enable + * @vblank_ctl_lock: Vblank ctl mutex lock to protect vblank_refcount * @enabled: Whether the encoder has enabled and running a mode * @split_role: Role to play in a split-panel configuration * @intf_mode: Interface mode @@ -223,12 +224,13 @@ struct dpu_encoder_phys { struct dpu_hw_intf *hw_intf; struct dpu_kms *dpu_kms; struct drm_display_mode cached_mode; + struct mutex vblank_ctl_lock; enum dpu_enc_split_role split_role; enum dpu_intf_mode intf_mode; enum dpu_intf intf_idx; spinlock_t *enc_spinlock; enum dpu_enc_enable_state enable_state; - atomic_t vblank_refcount; + int vblank_refcount; atomic_t vsync_cnt; atomic_t underrun_cnt; atomic_t pending_ctlstart_cnt; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c index 8493d68ad841..cad7f5686dc0 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c @@ -263,7 +263,8 @@ static int dpu_encoder_phys_cmd_control_vblank_irq( return -EINVAL; }
- refcount = atomic_read(&phys_enc->vblank_refcount); + mutex_lock(&phys_enc->vblank_ctl_lock); + refcount = phys_enc->vblank_refcount;
/* Slave encoders don't report vblank */ if (!dpu_encoder_phys_cmd_is_master(phys_enc)) @@ -279,13 +280,21 @@ static int dpu_encoder_phys_cmd_control_vblank_irq( phys_enc->hw_pp->idx - PINGPONG_0, enable ? "true" : "false", refcount);
- if (enable && atomic_inc_return(&phys_enc->vblank_refcount) == 1) - ret = dpu_encoder_helper_register_irq(phys_enc, INTR_IDX_RDPTR); - else if (!enable && atomic_dec_return(&phys_enc->vblank_refcount) == 0) - ret = dpu_encoder_helper_unregister_irq(phys_enc, - INTR_IDX_RDPTR); + if (enable) { + if (phys_enc->vblank_refcount == 0) + ret = dpu_encoder_helper_register_irq(phys_enc, INTR_IDX_RDPTR); + if (!ret) + phys_enc->vblank_refcount++; + } else if (!enable) { + if (phys_enc->vblank_refcount == 1) + ret = dpu_encoder_helper_unregister_irq(phys_enc, + INTR_IDX_RDPTR); + if (!ret) + phys_enc->vblank_refcount--; + }
end: + mutex_unlock(&phys_enc->vblank_ctl_lock); if (ret) { DRM_ERROR("vblank irq err id:%u pp:%d ret:%d, enable %s/%d\n", DRMID(phys_enc->parent), @@ -301,7 +310,7 @@ static void dpu_encoder_phys_cmd_irq_control(struct dpu_encoder_phys *phys_enc, { trace_dpu_enc_phys_cmd_irq_ctrl(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0, - enable, atomic_read(&phys_enc->vblank_refcount)); + enable, phys_enc->vblank_refcount);
if (enable) { dpu_encoder_helper_register_irq(phys_enc, INTR_IDX_PINGPONG); @@ -724,6 +733,9 @@ struct dpu_encoder_phys *dpu_encoder_phys_cmd_init( phys_enc->hw_mdptop = p->dpu_kms->hw_mdp; phys_enc->intf_idx = p->intf_idx;
+ mutex_init(&phys_enc->vblank_ctl_lock); + phys_enc->vblank_refcount = 0; + dpu_encoder_phys_cmd_init_ops(&phys_enc->ops); phys_enc->parent = p->parent; phys_enc->parent_ops = p->parent_ops; @@ -765,7 +777,7 @@ struct dpu_encoder_phys *dpu_encoder_phys_cmd_init( irq->intr_idx = INTR_IDX_UNDERRUN; irq->cb.func = dpu_encoder_phys_cmd_underrun_irq;
- atomic_set(&phys_enc->vblank_refcount, 0); + phys_enc->vblank_refcount = 0; atomic_set(&phys_enc->pending_kickoff_cnt, 0); atomic_set(&phys_enc->pending_ctlstart_cnt, 0); atomic_set(&cmd_enc->pending_vblank_cnt, 0); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c index 805e059b50b7..3aaaf58e798b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c @@ -395,7 +395,8 @@ static int dpu_encoder_phys_vid_control_vblank_irq( int ret = 0; int refcount;
- refcount = atomic_read(&phys_enc->vblank_refcount); + mutex_lock(&phys_enc->vblank_ctl_lock); + refcount = phys_enc->vblank_refcount;
/* Slave encoders don't report vblank */ if (!dpu_encoder_phys_vid_is_master(phys_enc)) @@ -407,16 +408,24 @@ static int dpu_encoder_phys_vid_control_vblank_irq( goto end; }
- DRM_DEBUG_KMS("id:%u enable=%d/%d\n", DRMID(phys_enc->parent), enable, - atomic_read(&phys_enc->vblank_refcount)); + DRM_DEBUG_VBL("id:%u enable=%d/%d\n", DRMID(phys_enc->parent), enable, + refcount);
- if (enable && atomic_inc_return(&phys_enc->vblank_refcount) == 1) - ret = dpu_encoder_helper_register_irq(phys_enc, INTR_IDX_VSYNC); - else if (!enable && atomic_dec_return(&phys_enc->vblank_refcount) == 0) - ret = dpu_encoder_helper_unregister_irq(phys_enc, - INTR_IDX_VSYNC); + if (enable) { + if (phys_enc->vblank_refcount == 0) + ret = dpu_encoder_helper_register_irq(phys_enc, INTR_IDX_VSYNC); + if (!ret) + phys_enc->vblank_refcount++; + } else if (!enable) { + if (phys_enc->vblank_refcount == 1) + ret = dpu_encoder_helper_unregister_irq(phys_enc, + INTR_IDX_VSYNC); + if (!ret) + phys_enc->vblank_refcount--; + }
end: + mutex_unlock(&phys_enc->vblank_ctl_lock); if (ret) { DRM_ERROR("failed: id:%u intf:%d ret:%d enable:%d refcnt:%d\n", DRMID(phys_enc->parent), @@ -631,7 +640,7 @@ static void dpu_encoder_phys_vid_irq_control(struct dpu_encoder_phys *phys_enc, trace_dpu_enc_phys_vid_irq_ctrl(DRMID(phys_enc->parent), phys_enc->hw_intf->idx - INTF_0, enable, - atomic_read(&phys_enc->vblank_refcount)); + phys_enc->vblank_refcount);
if (enable) { ret = dpu_encoder_phys_vid_control_vblank_irq(phys_enc, true); @@ -700,6 +709,9 @@ struct dpu_encoder_phys *dpu_encoder_phys_vid_init(
DPU_DEBUG_VIDENC(phys_enc, "\n");
+ mutex_init(&phys_enc->vblank_ctl_lock); + phys_enc->vblank_refcount = 0; + dpu_encoder_phys_vid_init_ops(&phys_enc->ops); phys_enc->parent = p->parent; phys_enc->parent_ops = p->parent_ops; @@ -727,7 +739,7 @@ struct dpu_encoder_phys *dpu_encoder_phys_vid_init( irq->intr_idx = INTR_IDX_UNDERRUN; irq->cb.func = dpu_encoder_phys_vid_underrun_irq;
- atomic_set(&phys_enc->vblank_refcount, 0); + phys_enc->vblank_refcount = 0; atomic_set(&phys_enc->pending_kickoff_cnt, 0); init_waitqueue_head(&phys_enc->pending_kickoff_wq); phys_enc->enable_state = DPU_ENC_DISABLED;