Commit a4393a1e authored by Paloma Arellano's avatar Paloma Arellano Committed by Cheng Yu
Browse files

drm/msm/dpu: Add mutex lock in control vblank irq

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=linux-6.7.y&id=14f109bf74dd67e1d0469fed859c8e506b0df53f



--------------------------------

[ 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: default avatarPaloma Arellano <quic_parellan@quicinc.com>
Reviewed-by: default avatarDmitry 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: default avatarDmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
Signed-off-by: default avatarCheng Yu <serein.chengyu@huawei.com>
parent 32bb7b0a
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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;
+19 −8
Original line number Diff line number Diff line
@@ -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)
	if (enable) {
		if (phys_enc->vblank_refcount == 0)
			ret = dpu_encoder_helper_register_irq(phys_enc, INTR_IDX_RDPTR);
	else if (!enable && atomic_dec_return(&phys_enc->vblank_refcount) == 0)
		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,6 @@ 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);
	atomic_set(&phys_enc->pending_kickoff_cnt, 0);
	atomic_set(&phys_enc->pending_ctlstart_cnt, 0);
	atomic_set(&cmd_enc->pending_vblank_cnt, 0);
+21 −10
Original line number Diff line number Diff line
@@ -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)
	if (enable) {
		if (phys_enc->vblank_refcount == 0)
			ret = dpu_encoder_helper_register_irq(phys_enc, INTR_IDX_VSYNC);
	else if (!enable && atomic_dec_return(&phys_enc->vblank_refcount) == 0)
		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,6 @@ 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);
	atomic_set(&phys_enc->pending_kickoff_cnt, 0);
	init_waitqueue_head(&phys_enc->pending_kickoff_wq);
	phys_enc->enable_state = DPU_ENC_DISABLED;