From 2c8bc91488fc57438c43b3bb19deb7fdbc1e5119 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 20 Nov 2019 17:35:09 +0000 Subject: drm/dp_mst: fix multiple frees of tx->bytes Currently tx->bytes is being freed r->num_transactions number of times because tx is not being set correctly. Fix this by setting tx to &r->transactions[i] so that the correct objects are being freed on each loop iteration. Addresses-Coverity: ("Double free") Fixes: 2f015ec6eab6 ("drm/dp_mst: Add sideband down request tracing + selftests") Signed-off-by: Colin Ian King Signed-off-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/20191120173509.347490-1-colin.king@canonical.com --- drivers/gpu/drm/drm_dp_mst_topology.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index b854a422a523..1437bc46368b 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -504,8 +504,10 @@ drm_dp_decode_sideband_req(const struct drm_dp_sideband_msg_tx *raw, } if (failed) { - for (i = 0; i < r->num_transactions; i++) + for (i = 0; i < r->num_transactions; i++) { + tx = &r->transactions[i]; kfree(tx->bytes); + } return -ENOMEM; } -- cgit v1.2.3 From f79489074c59a5fd6bc35b21ca9911237993a045 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Wed, 28 Aug 2019 20:09:44 -0400 Subject: drm/dp_mst: Clear all payload id tables downstream when initializing It seems that on certain MST hubs, namely the CableMatters USB-C 2x DP hub, using the DP_PAYLOAD_ALLOCATE_SET and DP_PAYLOAD_TABLE_UPDATE_STATUS register ranges to clear any pre-existing payload allocations on the hub isn't always enough to reset things if the source device has been reset unexpectedly. Or at least, that's the current running theory. The precise behavior appears to be that when the source device gets reset unexpectedly, the hub begins reporting an available_pbn value of 0 for all of its ports. This is a bit inconsistent with the our theory, since this seems to happen even if previously set PBN allocations should have resulted in a non-zero available_pbn value. So, it's possible that something else may be going on here. Strangely though, sending a CLEAR_PAYLOAD_ID_TABLE broadcast request when initializing the MST topology seems to bring things into working order and make available_pbn work again. Since this is a pretty safe solution, let's go ahead and implement it. Changes since v1: * Change indenting on drm_dp_send_clear_payload_id_table() prototype * Remove some braces in drm_dp_send_clear_payload_id_table() * Reorganize some variable declarations in drm_dp_send_clear_payload_id_table() * Don't forget to handle DP_CLEAR_PAYLOAD_ID_TABLE in drm_dp_sideband_parse_reply() * Move drm_dp_send_clear_payload_id_table() call into drm_dp_mst_link_probe_work(), since we can't send sideband messages while under lock in drm_dp_mst_topology_mgr_set_mst() * Change commit message Acked-by: Daniel Vetter Signed-off-by: Sean Paul Signed-off-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190829000944.20722-1-lyude@redhat.com --- drivers/gpu/drm/drm_dp_mst_topology.c | 63 +++++++++++++++++++++++++++++++++-- include/drm/drm_dp_mst_helper.h | 16 ++++++--- 2 files changed, 72 insertions(+), 7 deletions(-) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 1437bc46368b..87fc44895d83 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -63,6 +63,11 @@ static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr, static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_branch *mstb); + +static void +drm_dp_send_clear_payload_id_table(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_branch *mstb); + static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_branch *mstb, struct drm_dp_mst_port *port); @@ -939,6 +944,8 @@ static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw, case DP_POWER_DOWN_PHY: case DP_POWER_UP_PHY: return drm_dp_sideband_parse_power_updown_phy_ack(raw, msg); + case DP_CLEAR_PAYLOAD_ID_TABLE: + return true; /* since there's nothing to parse */ default: DRM_ERROR("Got unknown reply 0x%02x (%s)\n", msg->req_type, drm_dp_mst_req_type_str(msg->req_type)); @@ -1037,6 +1044,15 @@ static int build_link_address(struct drm_dp_sideband_msg_tx *msg) return 0; } +static int build_clear_payload_id_table(struct drm_dp_sideband_msg_tx *msg) +{ + struct drm_dp_sideband_msg_req_body req; + + req.req_type = DP_CLEAR_PAYLOAD_ID_TABLE; + drm_dp_encode_sideband_req(&req, msg); + return 0; +} + static int build_enum_path_resources(struct drm_dp_sideband_msg_tx *msg, int port_num) { struct drm_dp_sideband_msg_req_body req; @@ -2166,8 +2182,12 @@ static void drm_dp_mst_link_probe_work(struct work_struct *work) struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, work); struct drm_dp_mst_branch *mstb; int ret; + bool clear_payload_id_table; mutex_lock(&mgr->lock); + clear_payload_id_table = !mgr->payload_id_table_cleared; + mgr->payload_id_table_cleared = true; + mstb = mgr->mst_primary; if (mstb) { ret = drm_dp_mst_topology_try_get_mstb(mstb); @@ -2175,10 +2195,24 @@ static void drm_dp_mst_link_probe_work(struct work_struct *work) mstb = NULL; } mutex_unlock(&mgr->lock); - if (mstb) { - drm_dp_check_and_send_link_address(mgr, mstb); - drm_dp_mst_topology_put_mstb(mstb); + if (!mstb) + return; + + /* + * Certain branch devices seem to incorrectly report an available_pbn + * of 0 on downstream sinks, even after clearing the + * DP_PAYLOAD_ALLOCATE_* registers in + * drm_dp_mst_topology_mgr_set_mst(). Namely, the CableMatters USB-C + * 2x DP hub. Sending a CLEAR_PAYLOAD_ID_TABLE message seems to make + * things work again. + */ + if (clear_payload_id_table) { + DRM_DEBUG_KMS("Clearing payload ID table\n"); + drm_dp_send_clear_payload_id_table(mgr, mstb); } + + drm_dp_check_and_send_link_address(mgr, mstb); + drm_dp_mst_topology_put_mstb(mstb); } static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr, @@ -2471,6 +2505,28 @@ out: kfree(txmsg); } +void drm_dp_send_clear_payload_id_table(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_branch *mstb) +{ + struct drm_dp_sideband_msg_tx *txmsg; + int len, ret; + + txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); + if (!txmsg) + return; + + txmsg->dst = mstb; + len = build_clear_payload_id_table(txmsg); + + drm_dp_queue_down_tx(mgr, txmsg); + + ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); + if (ret > 0 && txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) + DRM_DEBUG_KMS("clear payload table id nak received\n"); + + kfree(txmsg); +} + static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_branch *mstb, @@ -3062,6 +3118,7 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms mgr->payload_mask = 0; set_bit(0, &mgr->payload_mask); mgr->vcpi_mask = 0; + mgr->payload_id_table_cleared = false; } out_unlock: diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 4a25e0577ae0..a448d701dc7e 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -490,15 +490,23 @@ struct drm_dp_mst_topology_mgr { struct drm_dp_sideband_msg_rx up_req_recv; /** - * @lock: protects mst state, primary, dpcd. + * @lock: protects @mst_state, @mst_primary, @dpcd, and + * @payload_id_table_cleared. */ struct mutex lock; /** - * @mst_state: If this manager is enabled for an MST capable port. False - * if no MST sink/branch devices is connected. + * @mst_state: If this manager is enabled for an MST capable port. + * False if no MST sink/branch devices is connected. */ - bool mst_state; + bool mst_state : 1; + + /** + * @payload_id_table_cleared: Whether or not we've cleared the payload + * ID table for @mst_primary. Protected by @lock. + */ + bool payload_id_table_cleared : 1; + /** * @mst_primary: Pointer to the primary/first branch device. */ -- cgit v1.2.3 From 64e62bdf04ab8529f45ed0a85122c703035dec3a Mon Sep 17 00:00:00 2001 From: Wayne Lin Date: Thu, 5 Dec 2019 17:00:43 +0800 Subject: drm/dp_mst: Remove VCPI while disabling topology mgr [Why] This patch is trying to address the issue observed when hotplug DP daisy chain monitors. e.g. src-mstb-mstb-sst -> src (unplug) mstb-mstb-sst -> src-mstb-mstb-sst (plug in again) Once unplug a DP MST capable device, driver will call drm_dp_mst_topology_mgr_set_mst() to disable MST. In this function, it cleans data of topology manager while disabling mst_state. However, it doesn't clean up the proposed_vcpis of topology manager. If proposed_vcpi is not reset, once plug in MST daisy chain monitors later, code will fail at checking port validation while trying to allocate payloads. When MST capable device is plugged in again and try to allocate payloads by calling drm_dp_update_payload_part1(), this function will iterate over all proposed virtual channels to see if any proposed VCPI's num_slots is greater than 0. If any proposed VCPI's num_slots is greater than 0 and the port which the specific virtual channel directed to is not in the topology, code then fails at the port validation. Since there are stale VCPI allocations from the previous topology enablement in proposed_vcpi[], code will fail at port validation and reurn EINVAL. [How] Clean up the data of stale proposed_vcpi[] and reset mgr->proposed_vcpis to NULL while disabling mst in drm_dp_mst_topology_mgr_set_mst(). Changes since v1: *Add on more details in commit message to describe the issue which the patch is trying to fix Signed-off-by: Wayne Lin [added cc to stable] Signed-off-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/20191205090043.7580-1-Wayne.Lin@amd.com Cc: # v3.17+ --- drivers/gpu/drm/drm_dp_mst_topology.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 87fc44895d83..1770754bcd4a 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -3054,6 +3054,7 @@ static int drm_dp_get_vc_payload_bw(u8 dp_link_bw, u8 dp_link_count) int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state) { int ret = 0; + int i = 0; struct drm_dp_mst_branch *mstb = NULL; mutex_lock(&mgr->lock); @@ -3114,10 +3115,22 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms /* this can fail if the device is gone */ drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0); ret = 0; + mutex_lock(&mgr->payload_lock); memset(mgr->payloads, 0, mgr->max_payloads * sizeof(struct drm_dp_payload)); mgr->payload_mask = 0; set_bit(0, &mgr->payload_mask); + for (i = 0; i < mgr->max_payloads; i++) { + struct drm_dp_vcpi *vcpi = mgr->proposed_vcpis[i]; + + if (vcpi) { + vcpi->vcpi = 0; + vcpi->num_slots = 0; + } + mgr->proposed_vcpis[i] = NULL; + } mgr->vcpi_mask = 0; + mutex_unlock(&mgr->payload_lock); + mgr->payload_id_table_cleared = false; } -- cgit v1.2.3