ソースを参照

disp: msm: dp: address race condition in LM allocation

Addresses a race condition which could cause the RM to overcommiting LMs to
DP when multiple DP displays are used.

sde_rm_get_resource_info now uses the RM's mutex and assumes a null
encoder is not a built in display so that it can return an accurate
count of unreserved resources.

DP layer now maintains internal accounting of LMs in use to avoid
validating modes that have insufficient remaining resources.

Change-Id: I908c1597c1d651b6f9c9b74a34137f30087d8801
Signed-off-by: Alex Danila <[email protected]>
Alex Danila 3 年 前
コミット
5ed42eb243
7 ファイル変更82 行追加17 行削除
  1. 56 9
      msm/dp/dp_display.c
  2. 1 0
      msm/dp/dp_display.h
  3. 4 0
      msm/dp/dp_drm.c
  4. 2 0
      msm/dp/dp_mst_drm.c
  5. 1 0
      msm/dp/dp_panel.h
  6. 2 0
      msm/msm_drv.h
  7. 16 8
      msm/sde/sde_rm.c

+ 56 - 9
msm/dp/dp_display.c

@@ -202,6 +202,7 @@ struct dp_display_private {
 	struct work_struct connect_work;
 	struct work_struct attention_work;
 	struct mutex session_lock;
+	struct mutex accounting_lock;
 	bool hdcp_delayed_off;
 	bool no_aux_switch;
 
@@ -209,6 +210,7 @@ struct dp_display_private {
 	struct dp_mst mst;
 
 	u32 tot_dsc_blks_in_use;
+	u32 tot_lm_blks_in_use;
 
 	bool process_hpd_connect;
 	struct dev_pm_qos_request pm_qos_req[NR_CPUS];
@@ -1483,6 +1485,25 @@ static int dp_display_usbpd_configure_cb(struct device *dev)
 	return 0;
 }
 
+static void dp_display_clear_reservation(struct dp_display *dp, struct dp_panel *panel)
+{
+	struct dp_display_private *dp_display;
+
+	if (!dp || !panel) {
+		DP_ERR("invalid params\n");
+		return;
+	}
+
+	dp_display = container_of(dp, struct dp_display_private, dp_display);
+
+	mutex_lock(&dp_display->accounting_lock);
+
+	dp_display->tot_lm_blks_in_use -= panel->max_lm;
+	panel->max_lm = 0;
+
+	mutex_unlock(&dp_display->accounting_lock);
+}
+
 static void dp_display_clear_dsc_resources(struct dp_display_private *dp,
 		struct dp_panel *panel)
 {
@@ -1562,6 +1583,7 @@ static void dp_display_clean(struct dp_display_private *dp)
 
 		dp_display_stream_pre_disable(dp, dp_panel);
 		dp_display_stream_disable(dp, dp_panel);
+		dp_display_clear_reservation(&dp->dp_display, dp_panel);
 		dp_panel->deinit(dp_panel, 0);
 	}
 
@@ -1589,6 +1611,8 @@ static int dp_display_handle_disconnect(struct dp_display_private *dp)
 
 	dp_display_host_unready(dp);
 
+	dp->tot_lm_blks_in_use = 0;
+
 	mutex_unlock(&dp->session_lock);
 
 	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state);
@@ -2002,6 +2026,7 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
 	};
 
 	mutex_init(&dp->session_lock);
+	mutex_init(&dp->accounting_lock);
 
 	dp->parser = dp_parser_get(dp->pdev);
 	if (IS_ERR(dp->parser)) {
@@ -2172,6 +2197,7 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
 
 	dp->cached_connector_status = connector_status_disconnected;
 	dp->tot_dsc_blks_in_use = 0;
+	dp->tot_lm_blks_in_use = 0;
 
 	dp->debug->hdcp_disabled = hdcp_disabled;
 	dp_display_update_hdcp_status(dp, true);
@@ -2806,6 +2832,8 @@ static int dp_display_unprepare(struct dp_display *dp_display, void *panel)
 	/* log this as it results from user action of cable dis-connection */
 	DP_INFO("[OK]\n");
 end:
+	dp->tot_lm_blks_in_use -= dp_panel->max_lm;
+	dp_panel->max_lm = 0;
 	dp_panel->deinit(dp_panel, flags);
 	mutex_unlock(&dp->session_lock);
 	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state);
@@ -2864,11 +2892,14 @@ static int dp_display_validate_topology(struct dp_display_private *dp,
 	u32 num_lm = 0, num_dsc = 0, num_3dmux = 0;
 	bool dsc_capable = dp_mode->capabilities & DP_PANEL_CAPS_DSC;
 	u32 fps = dp_mode->timing.refresh_rate;
+	int avail_lm = 0;
+
+	mutex_lock(&dp->accounting_lock);
 
 	rc = msm_get_mixer_count(priv, mode, avail_res, &num_lm);
 	if (rc) {
 		DP_ERR("error getting mixer count. rc:%d\n", rc);
-		return rc;
+		goto end;
 	}
 
 	/* Merge using DSC, if enabled */
@@ -2876,7 +2907,7 @@ static int dp_display_validate_topology(struct dp_display_private *dp,
 		rc = msm_get_dsc_count(priv, mode->hdisplay, &num_dsc);
 		if (rc) {
 			DP_ERR("error getting dsc count. rc:%d\n", rc);
-			return rc;
+			goto end;
 		}
 
 		num_dsc = max(num_lm, num_dsc);
@@ -2886,7 +2917,8 @@ static int dp_display_validate_topology(struct dp_display_private *dp,
 					avail_res->num_lm);
 			/* Clear DSC caps and retry */
 			dp_mode->capabilities &= ~DP_PANEL_CAPS_DSC;
-			return -EAGAIN;
+			rc = -EAGAIN;
+			goto end;
 		} else {
 			/* Only DSCMERGE is supported on DP */
 			num_lm = num_dsc;
@@ -2897,24 +2929,36 @@ static int dp_display_validate_topology(struct dp_display_private *dp,
 		num_3dmux = 1;
 	}
 
-	if (num_lm > avail_res->num_lm) {
+	avail_lm = avail_res->num_lm + avail_res->num_lm_in_use - dp->tot_lm_blks_in_use;
+	if ((num_lm > dp_panel->max_lm) && (num_lm > avail_lm)) {
 		DP_DEBUG("mode %sx%d is invalid, not enough lm %d %d\n",
-				mode->name, fps, num_lm, num_lm, avail_res->num_lm);
-		return -EPERM;
+				mode->name, fps, num_lm, avail_res->num_lm);
+		rc = -EPERM;
+		goto end;
 	} else if (!num_dsc && (num_lm == dual && !num_3dmux)) {
 		DP_DEBUG("mode %sx%d is invalid, not enough 3dmux %d %d\n",
 				mode->name, fps, num_3dmux, avail_res->num_3dmux);
-		return -EPERM;
+		rc = -EPERM;
+		goto end;
 	} else if (num_lm == quad && num_dsc != quad)  {
 		DP_DEBUG("mode %sx%d is invalid, unsupported DP topology lm:%d dsc:%d\n",
 				mode->name, fps, num_lm, num_dsc);
-		return -EPERM;
+		rc = -EPERM;
+		goto end;
 	}
 
 	DP_DEBUG_V("mode %sx%d is valid, supported DP topology lm:%d dsc:%d 3dmux:%d\n",
 				mode->name, fps, num_lm, num_dsc, num_3dmux);
 
-	return 0;
+	dp->tot_lm_blks_in_use -= dp_panel->max_lm;
+	dp_panel->max_lm = num_lm > avail_res->num_lm_in_use ? max(dp_panel->max_lm, num_lm) : 0;
+	dp->tot_lm_blks_in_use += dp_panel->max_lm;
+
+	rc = 0;
+
+end:
+	mutex_unlock(&dp->accounting_lock);
+	return rc;
 }
 
 static enum drm_mode_status dp_display_validate_mode(
@@ -2970,6 +3014,8 @@ static enum drm_mode_status dp_display_validate_mode(
 
 	mode_status = MODE_OK;
 end:
+	if (mode_status != MODE_OK)
+		dp_display_clear_reservation(dp_display, dp_panel);
 	mutex_unlock(&dp->session_lock);
 
 	DP_DEBUG_V("[%s] mode is %s\n", mode->name,
@@ -3636,6 +3682,7 @@ static int dp_display_probe(struct platform_device *pdev)
 	g_dp_display->set_colorspace = dp_display_setup_colospace;
 	g_dp_display->get_available_dp_resources =
 					dp_display_get_available_dp_resources;
+	g_dp_display->clear_reservation = dp_display_clear_reservation;
 
 	rc = component_add(&pdev->dev, &dp_display_comp_ops);
 	if (rc) {

+ 1 - 0
msm/dp/dp_display.h

@@ -105,6 +105,7 @@ struct dp_display {
 	int (*get_available_dp_resources)(struct dp_display *dp_display,
 			const struct msm_resource_caps_info *avail_res,
 			struct msm_resource_caps_info *max_dp_avail_res);
+	void (*clear_reservation)(struct dp_display *dp, struct dp_panel *panel);
 };
 
 void *get_ipc_log_context(void);

+ 4 - 0
msm/dp/dp_drm.c

@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
@@ -256,6 +257,8 @@ static void dp_bridge_mode_set(struct drm_bridge *drm_bridge,
 
 	dp->convert_to_dp_mode(dp, bridge->dp_panel, adjusted_mode,
 			&bridge->dp_mode);
+
+	dp->clear_reservation(dp, bridge->dp_panel);
 }
 
 static bool dp_bridge_mode_fixup(struct drm_bridge *drm_bridge,
@@ -289,6 +292,7 @@ static bool dp_bridge_mode_fixup(struct drm_bridge *drm_bridge,
 	dp = bridge->display;
 
 	dp->convert_to_dp_mode(dp, bridge->dp_panel, mode, &dp_mode);
+	dp->clear_reservation(dp, bridge->dp_panel);
 	convert_to_drm_mode(&dp_mode, adjusted_mode);
 end:
 	return ret;

+ 2 - 0
msm/dp/dp_mst_drm.c

@@ -345,6 +345,7 @@ static bool dp_mst_bridge_mode_fixup(struct drm_bridge *drm_bridge,
 	dp = bridge->display;
 
 	dp->convert_to_dp_mode(dp, bridge_state->dp_panel, mode, &dp_mode);
+	dp->clear_reservation(dp, bridge_state->dp_panel);
 	convert_to_drm_mode(&dp_mode, adjusted_mode);
 
 	DP_MST_DEBUG("mst bridge [%d] mode:%s fixup\n", bridge->id, mode->name);
@@ -775,6 +776,7 @@ static void dp_mst_bridge_mode_set(struct drm_bridge *drm_bridge,
 	memcpy(&bridge->drm_mode, adjusted_mode, sizeof(bridge->drm_mode));
 	dp->convert_to_dp_mode(dp, bridge->dp_panel, adjusted_mode,
 			&bridge->dp_mode);
+	dp->clear_reservation(dp, dp_bridge_state->dp_panel);
 
 	DP_MST_INFO("mst bridge:%d conn:%d mode set complete %s\n", bridge->id,
 			DP_MST_CONN_ID(bridge), mode->name);

+ 1 - 0
msm/dp/dp_panel.h

@@ -137,6 +137,7 @@ struct dp_panel {
 	u32 pbn;
 
 	u32 dsc_blks_in_use;
+	u32 max_lm;
 	/* DRM connector assosiated with this panel */
 	struct drm_connector *connector;
 

+ 2 - 0
msm/msm_drv.h

@@ -828,6 +828,7 @@ struct msm_mode_info {
 
 /**
  * struct msm_resource_caps_info - defines hw resources
+ * @num_lm_in_use       number of layer mixers allocated to a specified encoder
  * @num_lm              number of layer mixers available
  * @num_dsc             number of dsc available
  * @num_vdc             number of vdc available
@@ -836,6 +837,7 @@ struct msm_mode_info {
  * @max_mixer_width:    max width supported by layer mixer
  */
 struct msm_resource_caps_info {
+	uint32_t num_lm_in_use;
 	uint32_t num_lm;
 	uint32_t num_dsc;
 	uint32_t num_vdc;

+ 16 - 8
msm/sde/sde_rm.c

@@ -284,27 +284,33 @@ void sde_rm_get_resource_info(struct sde_rm *rm,
 {
 	struct sde_rm_hw_blk *blk;
 	enum sde_hw_blk_type type;
-	struct sde_rm_rsvp rsvp;
 	const struct sde_lm_cfg *lm_cfg;
 	bool is_built_in, is_pref;
 	u32 lm_pref = (BIT(SDE_DISP_PRIMARY_PREF) | BIT(SDE_DISP_SECONDARY_PREF));
 
+	mutex_lock(&rm->rm_lock);
+
 	/* Get all currently available resources */
 	memcpy(avail_res, &rm->avail_res,
 			sizeof(rm->avail_res));
 
+	/**
+	 * When the encoder is null, assume display is external in order to return the count of
+	 * availalbe non-preferred LMs
+	 */
 	if (!drm_enc)
-		return;
-
-	is_built_in = sde_encoder_is_built_in_display(drm_enc);
-
-	rsvp.enc_id = drm_enc->base.id;
+		is_built_in = false;
+	else
+		is_built_in = sde_encoder_is_built_in_display(drm_enc);
 
 	for (type = 0; type < SDE_HW_BLK_MAX; type++) {
 		list_for_each_entry(blk, &rm->hw_blks[type], list) {
 			/* Add back resources allocated to the given encoder */
-			if (blk->rsvp && blk->rsvp->enc_id == rsvp.enc_id)
+			if (blk->rsvp && drm_enc && blk->rsvp->enc_id == drm_enc->base.id) {
 				_sde_rm_inc_resource_info(rm, avail_res, blk);
+				if (type == SDE_HW_BLK_LM)
+					avail_res->num_lm_in_use++;
+			}
 
 			/**
 			 * Remove unallocated preferred lms that cannot reserved
@@ -314,11 +320,13 @@ void sde_rm_get_resource_info(struct sde_rm *rm,
 				lm_cfg = to_sde_hw_mixer(blk->hw)->cap;
 				is_pref = lm_cfg->features & lm_pref;
 
-				if (!blk->rsvp && !is_built_in && is_pref)
+				if (!blk->rsvp && !blk->rsvp_nxt && !is_built_in && is_pref)
 					_sde_rm_dec_resource_info(rm, avail_res, blk);
 			}
 		}
 	}
+
+	mutex_unlock(&rm->rm_lock);
 }
 
 static void _sde_rm_print_rsvps(