linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 01/27] drm/dp_mst: Move link address dumping into a function
       [not found] <20190903204645.25487-1-lyude@redhat.com>
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-25 17:45   ` Sean Paul
  2019-09-03 20:45 ` [PATCH v2 02/27] drm/dp_mst: Get rid of list clear in destroy_connector_work Lyude Paul
                   ` (25 subsequent siblings)
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, linux-kernel

Makes things easier to read.

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Reviewed-by: Daniel Vetter <daniel@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 35 ++++++++++++++++++---------
 1 file changed, 23 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 82add736e17d..36db66a0ddb1 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -2103,6 +2103,28 @@ static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr,
 	mutex_unlock(&mgr->qlock);
 }
 
+static void
+drm_dp_dump_link_address(struct drm_dp_link_address_ack_reply *reply)
+{
+	struct drm_dp_link_addr_reply_port *port_reply;
+	int i;
+
+	for (i = 0; i < reply->nports; i++) {
+		port_reply = &reply->ports[i];
+		DRM_DEBUG_KMS("port %d: input %d, pdt: %d, pn: %d, dpcd_rev: %02x, mcs: %d, ddps: %d, ldps %d, sdp %d/%d\n",
+			      i,
+			      port_reply->input_port,
+			      port_reply->peer_device_type,
+			      port_reply->port_number,
+			      port_reply->dpcd_revision,
+			      port_reply->mcs,
+			      port_reply->ddps,
+			      port_reply->legacy_device_plug_status,
+			      port_reply->num_sdp_streams,
+			      port_reply->num_sdp_stream_sinks);
+	}
+}
+
 static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
 				     struct drm_dp_mst_branch *mstb)
 {
@@ -2128,18 +2150,7 @@ static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
 			DRM_DEBUG_KMS("link address nak received\n");
 		} else {
 			DRM_DEBUG_KMS("link address reply: %d\n", txmsg->reply.u.link_addr.nports);
-			for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) {
-				DRM_DEBUG_KMS("port %d: input %d, pdt: %d, pn: %d, dpcd_rev: %02x, mcs: %d, ddps: %d, ldps %d, sdp %d/%d\n", i,
-				       txmsg->reply.u.link_addr.ports[i].input_port,
-				       txmsg->reply.u.link_addr.ports[i].peer_device_type,
-				       txmsg->reply.u.link_addr.ports[i].port_number,
-				       txmsg->reply.u.link_addr.ports[i].dpcd_revision,
-				       txmsg->reply.u.link_addr.ports[i].mcs,
-				       txmsg->reply.u.link_addr.ports[i].ddps,
-				       txmsg->reply.u.link_addr.ports[i].legacy_device_plug_status,
-				       txmsg->reply.u.link_addr.ports[i].num_sdp_streams,
-				       txmsg->reply.u.link_addr.ports[i].num_sdp_stream_sinks);
-			}
+			drm_dp_dump_link_address(&txmsg->reply.u.link_addr);
 
 			drm_dp_check_mstb_guid(mstb, txmsg->reply.u.link_addr.guid);
 
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 02/27] drm/dp_mst: Get rid of list clear in destroy_connector_work
       [not found] <20190903204645.25487-1-lyude@redhat.com>
  2019-09-03 20:45 ` [PATCH v2 01/27] drm/dp_mst: Move link address dumping into a function Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-25 17:45   ` Sean Paul
  2019-09-03 20:45 ` [PATCH v2 03/27] drm/dp_mst: Destroy MSTBs asynchronously Lyude Paul
                   ` (24 subsequent siblings)
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

This seems to be some leftover detritus from before the port/mstb kref
cleanup and doesn't do anything anymore, so get rid of it.

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 36db66a0ddb1..3054ec622506 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -3760,8 +3760,6 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)
 		list_del(&port->next);
 		mutex_unlock(&mgr->destroy_connector_lock);
 
-		INIT_LIST_HEAD(&port->next);
-
 		mgr->cbs->destroy_connector(mgr, port->connector);
 
 		drm_dp_port_teardown_pdt(port, port->pdt);
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 03/27] drm/dp_mst: Destroy MSTBs asynchronously
       [not found] <20190903204645.25487-1-lyude@redhat.com>
  2019-09-03 20:45 ` [PATCH v2 01/27] drm/dp_mst: Move link address dumping into a function Lyude Paul
  2019-09-03 20:45 ` [PATCH v2 02/27] drm/dp_mst: Get rid of list clear in destroy_connector_work Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-25 18:16   ` Sean Paul
  2019-09-03 20:45 ` [PATCH v2 04/27] drm/dp_mst: Move test_calc_pbn_mode() into an actual selftest Lyude Paul
                   ` (23 subsequent siblings)
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, linux-kernel

When reprobing an MST topology during resume, we have to account for the
fact that while we were suspended it's possible that mstbs may have been
removed from any ports in the topology. Since iterating downwards in the
topology requires that we hold &mgr->lock, destroying MSTBs from this
context would result in attempting to lock &mgr->lock a second time and
deadlocking.

So, fix this by first moving destruction of MSTBs into
destroy_connector_work, then rename destroy_connector_work and friends
to reflect that they now destroy both ports and mstbs.

Changes since v1:
* s/destroy_connector_list/destroy_port_list/
  s/connector_destroy_lock/delayed_destroy_lock/
  s/connector_destroy_work/delayed_destroy_work/
  s/drm_dp_finish_destroy_branch_device/drm_dp_delayed_destroy_mstb/
  s/drm_dp_finish_destroy_port/drm_dp_delayed_destroy_port/
  - danvet
* Use two loops in drm_dp_delayed_destroy_work() - danvet
* Better explain why we need to do this - danvet
* Use cancel_work_sync() instead of flush_work() - flush_work() doesn't
  account for work requeing

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 162 +++++++++++++++++---------
 include/drm/drm_dp_mst_helper.h       |  26 +++--
 2 files changed, 127 insertions(+), 61 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 3054ec622506..738f260d4b15 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -1113,34 +1113,17 @@ static void drm_dp_destroy_mst_branch_device(struct kref *kref)
 	struct drm_dp_mst_branch *mstb =
 		container_of(kref, struct drm_dp_mst_branch, topology_kref);
 	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
-	struct drm_dp_mst_port *port, *tmp;
-	bool wake_tx = false;
 
-	mutex_lock(&mgr->lock);
-	list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
-		list_del(&port->next);
-		drm_dp_mst_topology_put_port(port);
-	}
-	mutex_unlock(&mgr->lock);
-
-	/* drop any tx slots msg */
-	mutex_lock(&mstb->mgr->qlock);
-	if (mstb->tx_slots[0]) {
-		mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
-		mstb->tx_slots[0] = NULL;
-		wake_tx = true;
-	}
-	if (mstb->tx_slots[1]) {
-		mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
-		mstb->tx_slots[1] = NULL;
-		wake_tx = true;
-	}
-	mutex_unlock(&mstb->mgr->qlock);
+	INIT_LIST_HEAD(&mstb->destroy_next);
 
-	if (wake_tx)
-		wake_up_all(&mstb->mgr->tx_waitq);
-
-	drm_dp_mst_put_mstb_malloc(mstb);
+	/*
+	 * This can get called under mgr->mutex, so we need to perform the
+	 * actual destruction of the mstb in another worker
+	 */
+	mutex_lock(&mgr->delayed_destroy_lock);
+	list_add(&mstb->destroy_next, &mgr->destroy_branch_device_list);
+	mutex_unlock(&mgr->delayed_destroy_lock);
+	schedule_work(&mgr->delayed_destroy_work);
 }
 
 /**
@@ -1255,10 +1238,10 @@ static void drm_dp_destroy_port(struct kref *kref)
 			 * we might be holding the mode_config.mutex
 			 * from an EDID retrieval */
 
-			mutex_lock(&mgr->destroy_connector_lock);
-			list_add(&port->next, &mgr->destroy_connector_list);
-			mutex_unlock(&mgr->destroy_connector_lock);
-			schedule_work(&mgr->destroy_connector_work);
+			mutex_lock(&mgr->delayed_destroy_lock);
+			list_add(&port->next, &mgr->destroy_port_list);
+			mutex_unlock(&mgr->delayed_destroy_lock);
+			schedule_work(&mgr->delayed_destroy_work);
 			return;
 		}
 		/* no need to clean up vcpi
@@ -2792,7 +2775,7 @@ void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr)
 			   DP_MST_EN | DP_UPSTREAM_IS_SRC);
 	mutex_unlock(&mgr->lock);
 	flush_work(&mgr->work);
-	flush_work(&mgr->destroy_connector_work);
+	flush_work(&mgr->delayed_destroy_work);
 }
 EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend);
 
@@ -3740,34 +3723,104 @@ static void drm_dp_tx_work(struct work_struct *work)
 	mutex_unlock(&mgr->qlock);
 }
 
-static void drm_dp_destroy_connector_work(struct work_struct *work)
+static inline void
+drm_dp_delayed_destroy_port(struct drm_dp_mst_port *port)
 {
-	struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, destroy_connector_work);
-	struct drm_dp_mst_port *port;
-	bool send_hotplug = false;
+	port->mgr->cbs->destroy_connector(port->mgr, port->connector);
+
+	drm_dp_port_teardown_pdt(port, port->pdt);
+	port->pdt = DP_PEER_DEVICE_NONE;
+
+	drm_dp_mst_put_port_malloc(port);
+}
+
+static inline void
+drm_dp_delayed_destroy_mstb(struct drm_dp_mst_branch *mstb)
+{
+	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
+	struct drm_dp_mst_port *port, *tmp;
+	bool wake_tx = false;
+
+	mutex_lock(&mgr->lock);
+	list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
+		list_del(&port->next);
+		drm_dp_mst_topology_put_port(port);
+	}
+	mutex_unlock(&mgr->lock);
+
+	/* drop any tx slots msg */
+	mutex_lock(&mstb->mgr->qlock);
+	if (mstb->tx_slots[0]) {
+		mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
+		mstb->tx_slots[0] = NULL;
+		wake_tx = true;
+	}
+	if (mstb->tx_slots[1]) {
+		mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
+		mstb->tx_slots[1] = NULL;
+		wake_tx = true;
+	}
+	mutex_unlock(&mstb->mgr->qlock);
+
+	if (wake_tx)
+		wake_up_all(&mstb->mgr->tx_waitq);
+
+	drm_dp_mst_put_mstb_malloc(mstb);
+}
+
+static void drm_dp_delayed_destroy_work(struct work_struct *work)
+{
+	struct drm_dp_mst_topology_mgr *mgr =
+		container_of(work, struct drm_dp_mst_topology_mgr,
+			     delayed_destroy_work);
+	bool send_hotplug = false, go_again;
+
 	/*
 	 * Not a regular list traverse as we have to drop the destroy
-	 * connector lock before destroying the connector, to avoid AB->BA
+	 * connector lock before destroying the mstb/port, to avoid AB->BA
 	 * ordering between this lock and the config mutex.
 	 */
-	for (;;) {
-		mutex_lock(&mgr->destroy_connector_lock);
-		port = list_first_entry_or_null(&mgr->destroy_connector_list, struct drm_dp_mst_port, next);
-		if (!port) {
-			mutex_unlock(&mgr->destroy_connector_lock);
-			break;
+	do {
+		go_again = false;
+
+		for (;;) {
+			struct drm_dp_mst_branch *mstb;
+
+			mutex_lock(&mgr->delayed_destroy_lock);
+			mstb = list_first_entry_or_null(&mgr->destroy_branch_device_list,
+							struct drm_dp_mst_branch,
+							destroy_next);
+			if (mstb)
+				list_del(&mstb->destroy_next);
+			mutex_unlock(&mgr->delayed_destroy_lock);
+
+			if (!mstb)
+				break;
+
+			drm_dp_delayed_destroy_mstb(mstb);
+			go_again = true;
 		}
-		list_del(&port->next);
-		mutex_unlock(&mgr->destroy_connector_lock);
 
-		mgr->cbs->destroy_connector(mgr, port->connector);
+		for (;;) {
+			struct drm_dp_mst_port *port;
 
-		drm_dp_port_teardown_pdt(port, port->pdt);
-		port->pdt = DP_PEER_DEVICE_NONE;
+			mutex_lock(&mgr->delayed_destroy_lock);
+			port = list_first_entry_or_null(&mgr->destroy_port_list,
+							struct drm_dp_mst_port,
+							next);
+			if (port)
+				list_del(&port->next);
+			mutex_unlock(&mgr->delayed_destroy_lock);
+
+			if (!port)
+				break;
+
+			drm_dp_delayed_destroy_port(port);
+			send_hotplug = true;
+			go_again = true;
+		}
+	} while (go_again);
 
-		drm_dp_mst_put_port_malloc(port);
-		send_hotplug = true;
-	}
 	if (send_hotplug)
 		drm_kms_helper_hotplug_event(mgr->dev);
 }
@@ -3957,12 +4010,13 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
 	mutex_init(&mgr->lock);
 	mutex_init(&mgr->qlock);
 	mutex_init(&mgr->payload_lock);
-	mutex_init(&mgr->destroy_connector_lock);
+	mutex_init(&mgr->delayed_destroy_lock);
 	INIT_LIST_HEAD(&mgr->tx_msg_downq);
-	INIT_LIST_HEAD(&mgr->destroy_connector_list);
+	INIT_LIST_HEAD(&mgr->destroy_port_list);
+	INIT_LIST_HEAD(&mgr->destroy_branch_device_list);
 	INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work);
 	INIT_WORK(&mgr->tx_work, drm_dp_tx_work);
-	INIT_WORK(&mgr->destroy_connector_work, drm_dp_destroy_connector_work);
+	INIT_WORK(&mgr->delayed_destroy_work, drm_dp_delayed_destroy_work);
 	init_waitqueue_head(&mgr->tx_waitq);
 	mgr->dev = dev;
 	mgr->aux = aux;
@@ -4005,7 +4059,7 @@ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
 {
 	drm_dp_mst_topology_mgr_set_mst(mgr, false);
 	flush_work(&mgr->work);
-	flush_work(&mgr->destroy_connector_work);
+	cancel_work_sync(&mgr->delayed_destroy_work);
 	mutex_lock(&mgr->payload_lock);
 	kfree(mgr->payloads);
 	mgr->payloads = NULL;
diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
index fc349204a71b..4a4507fe928d 100644
--- a/include/drm/drm_dp_mst_helper.h
+++ b/include/drm/drm_dp_mst_helper.h
@@ -143,6 +143,12 @@ struct drm_dp_mst_branch {
 	 */
 	struct kref malloc_kref;
 
+	/**
+	 * @destroy_next: linked-list entry used by
+	 * drm_dp_delayed_destroy_work()
+	 */
+	struct list_head destroy_next;
+
 	u8 rad[8];
 	u8 lct;
 	int num_ports;
@@ -575,18 +581,24 @@ struct drm_dp_mst_topology_mgr {
 	struct work_struct tx_work;
 
 	/**
-	 * @destroy_connector_list: List of to be destroyed connectors.
+	 * @destroy_port_list: List of to be destroyed connectors.
+	 */
+	struct list_head destroy_port_list;
+	/**
+	 * @destroy_branch_device_list: List of to be destroyed branch
+	 * devices.
 	 */
-	struct list_head destroy_connector_list;
+	struct list_head destroy_branch_device_list;
 	/**
-	 * @destroy_connector_lock: Protects @connector_list.
+	 * @delayed_destroy_lock: Protects @destroy_port_list and
+	 * @destroy_branch_device_list.
 	 */
-	struct mutex destroy_connector_lock;
+	struct mutex delayed_destroy_lock;
 	/**
-	 * @destroy_connector_work: Work item to destroy connectors. Needed to
-	 * avoid locking inversion.
+	 * @delayed_destroy_work: Work item to destroy MST port and branch
+	 * devices, needed to avoid locking inversion.
 	 */
-	struct work_struct destroy_connector_work;
+	struct work_struct delayed_destroy_work;
 };
 
 int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 04/27] drm/dp_mst: Move test_calc_pbn_mode() into an actual selftest
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (2 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 03/27] drm/dp_mst: Destroy MSTBs asynchronously Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-25 18:17   ` Sean Paul
  2019-09-03 20:45 ` [PATCH v2 05/27] drm/print: Add drm_err_printer() Lyude Paul
                   ` (22 subsequent siblings)
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, Thomas Hellstrom,
	Deepak Rawat, Alexandru Gheorghe, Thomas Gleixner, linux-kernel

Yes, apparently we've been testing this for every single driver load for
quite a long time now. At least that means our PBN calculation is solid!

Anyway, introduce self tests for MST and move this into there.

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c         | 27 ---------------
 drivers/gpu/drm/selftests/Makefile            |  2 +-
 .../gpu/drm/selftests/drm_modeset_selftests.h |  1 +
 .../drm/selftests/test-drm_dp_mst_helper.c    | 34 +++++++++++++++++++
 .../drm/selftests/test-drm_modeset_common.h   |  1 +
 5 files changed, 37 insertions(+), 28 deletions(-)
 create mode 100644 drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 738f260d4b15..6f7f449ca12b 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -47,7 +47,6 @@
  */
 static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
 				  char *buf);
-static int test_calc_pbn_mode(void);
 
 static void drm_dp_mst_topology_put_port(struct drm_dp_mst_port *port);
 
@@ -3561,30 +3560,6 @@ int drm_dp_calc_pbn_mode(int clock, int bpp)
 }
 EXPORT_SYMBOL(drm_dp_calc_pbn_mode);
 
-static int test_calc_pbn_mode(void)
-{
-	int ret;
-	ret = drm_dp_calc_pbn_mode(154000, 30);
-	if (ret != 689) {
-		DRM_ERROR("PBN calculation test failed - clock %d, bpp %d, expected PBN %d, actual PBN %d.\n",
-				154000, 30, 689, ret);
-		return -EINVAL;
-	}
-	ret = drm_dp_calc_pbn_mode(234000, 30);
-	if (ret != 1047) {
-		DRM_ERROR("PBN calculation test failed - clock %d, bpp %d, expected PBN %d, actual PBN %d.\n",
-				234000, 30, 1047, ret);
-		return -EINVAL;
-	}
-	ret = drm_dp_calc_pbn_mode(297000, 24);
-	if (ret != 1063) {
-		DRM_ERROR("PBN calculation test failed - clock %d, bpp %d, expected PBN %d, actual PBN %d.\n",
-				297000, 24, 1063, ret);
-		return -EINVAL;
-	}
-	return 0;
-}
-
 /* we want to kick the TX after we've ack the up/down IRQs. */
 static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr)
 {
@@ -4033,8 +4008,6 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
 	if (!mgr->proposed_vcpis)
 		return -ENOMEM;
 	set_bit(0, &mgr->payload_mask);
-	if (test_calc_pbn_mode() < 0)
-		DRM_ERROR("MST PBN self-test failed\n");
 
 	mst_state = kzalloc(sizeof(*mst_state), GFP_KERNEL);
 	if (mst_state == NULL)
diff --git a/drivers/gpu/drm/selftests/Makefile b/drivers/gpu/drm/selftests/Makefile
index aae88f8a016c..d2137342b371 100644
--- a/drivers/gpu/drm/selftests/Makefile
+++ b/drivers/gpu/drm/selftests/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 test-drm_modeset-y := test-drm_modeset_common.o test-drm_plane_helper.o \
                       test-drm_format.o test-drm_framebuffer.o \
-		      test-drm_damage_helper.o
+		      test-drm_damage_helper.o test-drm_dp_mst_helper.o
 
 obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_modeset.o test-drm_cmdline_parser.o
diff --git a/drivers/gpu/drm/selftests/drm_modeset_selftests.h b/drivers/gpu/drm/selftests/drm_modeset_selftests.h
index 464753746013..dec3ee3ec96f 100644
--- a/drivers/gpu/drm/selftests/drm_modeset_selftests.h
+++ b/drivers/gpu/drm/selftests/drm_modeset_selftests.h
@@ -32,3 +32,4 @@ selftest(damage_iter_damage_one_intersect, igt_damage_iter_damage_one_intersect)
 selftest(damage_iter_damage_one_outside, igt_damage_iter_damage_one_outside)
 selftest(damage_iter_damage_src_moved, igt_damage_iter_damage_src_moved)
 selftest(damage_iter_damage_not_visible, igt_damage_iter_damage_not_visible)
+selftest(dp_mst_calc_pbn_mode, igt_dp_mst_calc_pbn_mode)
diff --git a/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c b/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
new file mode 100644
index 000000000000..9baa5171988d
--- /dev/null
+++ b/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Test cases for for the DRM DP MST helpers
+ */
+
+#include <drm/drm_dp_mst_helper.h>
+#include <drm/drm_print.h>
+
+#include "test-drm_modeset_common.h"
+
+int igt_dp_mst_calc_pbn_mode(void *ignored)
+{
+	int pbn, i;
+	const struct {
+		int rate;
+		int bpp;
+		int expected;
+	} test_params[] = {
+		{ 154000, 30, 689 },
+		{ 234000, 30, 1047 },
+		{ 297000, 24, 1063 },
+	};
+
+	for (i = 0; i < ARRAY_SIZE(test_params); i++) {
+		pbn = drm_dp_calc_pbn_mode(test_params[i].rate,
+					   test_params[i].bpp);
+		FAIL(pbn != test_params[i].expected,
+		     "Expected PBN %d for clock %d bpp %d, got %d\n",
+		     test_params[i].expected, test_params[i].rate,
+		     test_params[i].bpp, pbn);
+	}
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/selftests/test-drm_modeset_common.h b/drivers/gpu/drm/selftests/test-drm_modeset_common.h
index 8c76f09c12d1..590bda35a683 100644
--- a/drivers/gpu/drm/selftests/test-drm_modeset_common.h
+++ b/drivers/gpu/drm/selftests/test-drm_modeset_common.h
@@ -39,5 +39,6 @@ int igt_damage_iter_damage_one_intersect(void *ignored);
 int igt_damage_iter_damage_one_outside(void *ignored);
 int igt_damage_iter_damage_src_moved(void *ignored);
 int igt_damage_iter_damage_not_visible(void *ignored);
+int igt_dp_mst_calc_pbn_mode(void *ignored);
 
 #endif
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 05/27] drm/print: Add drm_err_printer()
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (3 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 04/27] drm/dp_mst: Move test_calc_pbn_mode() into an actual selftest Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-03 20:45 ` [PATCH v2 06/27] drm/dp_mst: Combine redundant cases in drm_dp_encode_sideband_req() Lyude Paul
                   ` (21 subsequent siblings)
  26 siblings, 0 replies; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

A simple convienence function that returns a drm_printer which prints
using pr_err()

Changes since v1:
* Make __drm_printfn_err() more consistent with DRM_ERROR() - danvet

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_print.c |  6 ++++++
 include/drm/drm_print.h     | 17 +++++++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/drivers/gpu/drm/drm_print.c b/drivers/gpu/drm/drm_print.c
index a17c8a14dba4..ad302d71eeee 100644
--- a/drivers/gpu/drm/drm_print.c
+++ b/drivers/gpu/drm/drm_print.c
@@ -147,6 +147,12 @@ void __drm_printfn_debug(struct drm_printer *p, struct va_format *vaf)
 }
 EXPORT_SYMBOL(__drm_printfn_debug);
 
+void __drm_printfn_err(struct drm_printer *p, struct va_format *vaf)
+{
+	pr_err("*ERROR* %s %pV", p->prefix, vaf);
+}
+EXPORT_SYMBOL(__drm_printfn_err);
+
 /**
  * drm_puts - print a const string to a &drm_printer stream
  * @p: the &drm printer
diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h
index a5d6f2f3e430..112165d3195d 100644
--- a/include/drm/drm_print.h
+++ b/include/drm/drm_print.h
@@ -83,6 +83,7 @@ void __drm_printfn_seq_file(struct drm_printer *p, struct va_format *vaf);
 void __drm_puts_seq_file(struct drm_printer *p, const char *str);
 void __drm_printfn_info(struct drm_printer *p, struct va_format *vaf);
 void __drm_printfn_debug(struct drm_printer *p, struct va_format *vaf);
+void __drm_printfn_err(struct drm_printer *p, struct va_format *vaf);
 
 __printf(2, 3)
 void drm_printf(struct drm_printer *p, const char *f, ...);
@@ -227,6 +228,22 @@ static inline struct drm_printer drm_debug_printer(const char *prefix)
 	return p;
 }
 
+/**
+ * drm_err_printer - construct a &drm_printer that outputs to pr_err()
+ * @prefix: debug output prefix
+ *
+ * RETURNS:
+ * The &drm_printer object
+ */
+static inline struct drm_printer drm_err_printer(const char *prefix)
+{
+	struct drm_printer p = {
+		.printfn = __drm_printfn_err,
+		.prefix = prefix
+	};
+	return p;
+}
+
 /*
  * The following categories are defined:
  *
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 06/27] drm/dp_mst: Combine redundant cases in drm_dp_encode_sideband_req()
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (4 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 05/27] drm/print: Add drm_err_printer() Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-03 21:35   ` Dave Airlie
  2019-09-03 21:57   ` [PATCH v3] " Lyude Paul
  2019-09-03 20:45 ` [PATCH v2 07/27] drm/dp_mst: Add sideband down request tracing + selftests Lyude Paul
                   ` (20 subsequent siblings)
  26 siblings, 2 replies; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Daniel Vetter, Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Maarten Lankhorst, Maxime Ripard, Sean Paul,
	David Airlie, Daniel Vetter, linux-kernel

Noticed this while working on adding a drm_dp_decode_sideband_req().
DP_POWER_DOWN_PHY/DP_POWER_UP_PHY both use the same struct, so we can
just combine their cases.

Signed-off-by: Lyude Paul <lyude@redhat.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 6f7f449ca12b..1c862749cb63 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -271,6 +271,8 @@ static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req,
 
 	switch (req->req_type) {
 	case DP_ENUM_PATH_RESOURCES:
+	case DP_POWER_DOWN_PHY:
+	case DP_POWER_UP_PHY:
 		buf[idx] = (req->u.port_num.port_number & 0xf) << 4;
 		idx++;
 		break;
@@ -358,12 +360,6 @@ static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req,
 		memcpy(&buf[idx], req->u.i2c_write.bytes, req->u.i2c_write.num_bytes);
 		idx += req->u.i2c_write.num_bytes;
 		break;
-
-	case DP_POWER_DOWN_PHY:
-	case DP_POWER_UP_PHY:
-		buf[idx] = (req->u.port_num.port_number & 0xf) << 4;
-		idx++;
-		break;
 	}
 	raw->cur_len = idx;
 }
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 07/27] drm/dp_mst: Add sideband down request tracing + selftests
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (5 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 06/27] drm/dp_mst: Combine redundant cases in drm_dp_encode_sideband_req() Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-10  9:01   ` Jani Nikula
  2019-09-03 20:45 ` [PATCH v2 08/27] drm/dp_mst: Remove PDT teardown in drm_dp_destroy_port() and refactor Lyude Paul
                   ` (19 subsequent siblings)
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, Thomas Hellstrom,
	Alexandru Gheorghe, Deepak Rawat, linux-kernel

Unfortunately the DP MST helpers do not have much in the way of
debugging utilities. So, let's add some!

This adds basic debugging output for down sideband requests that we send
from the driver, so that we can actually discern what's happening when
sideband requests timeout.

Since there wasn't really a good way of testing that any of this worked,
I ended up writing simple selftests that lightly test sideband message
encoding and decoding as well. Enjoy!

Changes since v1:
* Clean up DO_TEST() and sideband_msg_req_encode_decode() - danvet
* Get rid of pr_fmt(), just define a prefix string instead and use
  drm_printf()
* Check highest bit of VCPI in drm_dp_decode_sideband_req() - danvet
* Make the switch case order between drm_dp_decode_sideband_req() and
  drm_dp_encode_sideband_req() the same - danvet
* Only check DRM_UT_DP - danvet
* Clean up sideband_msg_req_equal() from selftests a bit, and add
  comments explaining why we can't just use memcmp - danvet

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c         | 309 +++++++++++++++++-
 .../gpu/drm/drm_dp_mst_topology_internal.h    |  24 ++
 .../gpu/drm/selftests/drm_modeset_selftests.h |   1 +
 .../drm/selftests/test-drm_dp_mst_helper.c    | 204 ++++++++++++
 .../drm/selftests/test-drm_modeset_common.h   |   1 +
 include/drm/drm_dp_mst_helper.h               |   2 +-
 6 files changed, 536 insertions(+), 5 deletions(-)
 create mode 100644 drivers/gpu/drm/drm_dp_mst_topology_internal.h

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 1c862749cb63..f5f1d8b50fb6 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -37,6 +37,7 @@
 #include <drm/drm_probe_helper.h>
 
 #include "drm_crtc_helper_internal.h"
+#include "drm_dp_mst_topology_internal.h"
 
 /**
  * DOC: dp mst helper
@@ -73,6 +74,8 @@ static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux);
 static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux);
 static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr);
 
+#define DBG_PREFIX "[dp_mst]"
+
 #define DP_STR(x) [DP_ ## x] = #x
 
 static const char *drm_dp_mst_req_type_str(u8 req_type)
@@ -129,6 +132,43 @@ static const char *drm_dp_mst_nak_reason_str(u8 nak_reason)
 }
 
 #undef DP_STR
+#define DP_STR(x) [DRM_DP_SIDEBAND_TX_ ## x] = #x
+
+static const char *drm_dp_mst_sideband_tx_state_str(int state)
+{
+	static const char * const sideband_reason_str[] = {
+		DP_STR(QUEUED),
+		DP_STR(START_SEND),
+		DP_STR(SENT),
+		DP_STR(RX),
+		DP_STR(TIMEOUT),
+	};
+
+	if (state >= ARRAY_SIZE(sideband_reason_str) ||
+	    !sideband_reason_str[state])
+		return "unknown";
+
+	return sideband_reason_str[state];
+}
+
+static int
+drm_dp_mst_rad_to_str(const u8 rad[8], u8 lct, char *out, size_t len)
+{
+	int i;
+	u8 unpacked_rad[16];
+
+	for (i = 0; i < lct; i++) {
+		if (i % 2)
+			unpacked_rad[i] = rad[i / 2] >> 4;
+		else
+			unpacked_rad[i] = rad[i / 2] & BIT_MASK(4);
+	}
+
+	/* TODO: Eventually add something to printk so we can format the rad
+	 * like this: 1.2.3
+	 */
+	return snprintf(out, len, "%*phC", lct, unpacked_rad);
+}
 
 /* sideband msg handling */
 static u8 drm_dp_msg_header_crc4(const uint8_t *data, size_t num_nibbles)
@@ -261,8 +301,9 @@ static bool drm_dp_decode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr,
 	return true;
 }
 
-static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req,
-				       struct drm_dp_sideband_msg_tx *raw)
+void
+drm_dp_encode_sideband_req(const struct drm_dp_sideband_msg_req_body *req,
+			   struct drm_dp_sideband_msg_tx *raw)
 {
 	int idx = 0;
 	int i;
@@ -363,6 +404,251 @@ static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req,
 	}
 	raw->cur_len = idx;
 }
+EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_encode_sideband_req);
+
+/* Decode a sideband request we've encoded, mainly used for debugging */
+int
+drm_dp_decode_sideband_req(const struct drm_dp_sideband_msg_tx *raw,
+			   struct drm_dp_sideband_msg_req_body *req)
+{
+	const u8 *buf = raw->msg;
+	int i, idx = 0;
+
+	req->req_type = buf[idx++] & 0x7f;
+	switch (req->req_type) {
+	case DP_ENUM_PATH_RESOURCES:
+	case DP_POWER_DOWN_PHY:
+	case DP_POWER_UP_PHY:
+		req->u.port_num.port_number = (buf[idx] >> 4) & 0xf;
+		break;
+	case DP_ALLOCATE_PAYLOAD:
+		{
+			struct drm_dp_allocate_payload *a =
+				&req->u.allocate_payload;
+
+			a->number_sdp_streams = buf[idx] & 0xf;
+			a->port_number = (buf[idx] >> 4) & 0xf;
+
+			WARN_ON(buf[++idx] & 0x80);
+			a->vcpi = buf[idx] & 0x7f;
+
+			a->pbn = buf[++idx] << 8;
+			a->pbn |= buf[++idx];
+
+			idx++;
+			for (i = 0; i < a->number_sdp_streams; i++) {
+				a->sdp_stream_sink[i] =
+					(buf[idx + (i / 2)] >> ((i % 2) ? 0 : 4)) & 0xf;
+			}
+		}
+		break;
+	case DP_QUERY_PAYLOAD:
+		req->u.query_payload.port_number = (buf[idx] >> 4) & 0xf;
+		WARN_ON(buf[++idx] & 0x80);
+		req->u.query_payload.vcpi = buf[idx] & 0x7f;
+		break;
+	case DP_REMOTE_DPCD_READ:
+		{
+			struct drm_dp_remote_dpcd_read *r = &req->u.dpcd_read;
+
+			r->port_number = (buf[idx] >> 4) & 0xf;
+
+			r->dpcd_address = (buf[idx] << 16) & 0xf0000;
+			r->dpcd_address |= (buf[++idx] << 8) & 0xff00;
+			r->dpcd_address |= buf[++idx] & 0xff;
+
+			r->num_bytes = buf[++idx];
+		}
+		break;
+	case DP_REMOTE_DPCD_WRITE:
+		{
+			struct drm_dp_remote_dpcd_write *w =
+				&req->u.dpcd_write;
+
+			w->port_number = (buf[idx] >> 4) & 0xf;
+
+			w->dpcd_address = (buf[idx] << 16) & 0xf0000;
+			w->dpcd_address |= (buf[++idx] << 8) & 0xff00;
+			w->dpcd_address |= buf[++idx] & 0xff;
+
+			w->num_bytes = buf[++idx];
+
+			w->bytes = kmemdup(&buf[++idx], w->num_bytes,
+					   GFP_KERNEL);
+			if (!w->bytes)
+				return -ENOMEM;
+		}
+		break;
+	case DP_REMOTE_I2C_READ:
+		{
+			struct drm_dp_remote_i2c_read *r = &req->u.i2c_read;
+			struct drm_dp_remote_i2c_read_tx *tx;
+			bool failed = false;
+
+			r->num_transactions = buf[idx] & 0x3;
+			r->port_number = (buf[idx] >> 4) & 0xf;
+			for (i = 0; i < r->num_transactions; i++) {
+				tx = &r->transactions[i];
+
+				tx->i2c_dev_id = buf[++idx] & 0x7f;
+				tx->num_bytes = buf[++idx];
+				tx->bytes = kmemdup(&buf[++idx],
+						    tx->num_bytes,
+						    GFP_KERNEL);
+				if (!tx->bytes) {
+					failed = true;
+					break;
+				}
+				idx += tx->num_bytes;
+				tx->no_stop_bit = (buf[idx] >> 5) & 0x1;
+				tx->i2c_transaction_delay = buf[idx] & 0xf;
+			}
+
+			if (failed) {
+				for (i = 0; i < r->num_transactions; i++)
+					kfree(tx->bytes);
+				return -ENOMEM;
+			}
+
+			r->read_i2c_device_id = buf[++idx] & 0x7f;
+			r->num_bytes_read = buf[++idx];
+		}
+		break;
+	case DP_REMOTE_I2C_WRITE:
+		{
+			struct drm_dp_remote_i2c_write *w = &req->u.i2c_write;
+
+			w->port_number = (buf[idx] >> 4) & 0xf;
+			w->write_i2c_device_id = buf[++idx] & 0x7f;
+			w->num_bytes = buf[++idx];
+			w->bytes = kmemdup(&buf[++idx], w->num_bytes,
+					   GFP_KERNEL);
+			if (!w->bytes)
+				return -ENOMEM;
+		}
+		break;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_decode_sideband_req);
+
+void
+drm_dp_dump_sideband_msg_req_body(const struct drm_dp_sideband_msg_req_body *req,
+				  int indent, struct drm_printer *printer)
+{
+	int i;
+
+#define P(f, ...) drm_printf_indent(printer, indent, f, ##__VA_ARGS__)
+	if (req->req_type == DP_LINK_ADDRESS) {
+		/* No contents to print */
+		P("type=%s\n", drm_dp_mst_req_type_str(req->req_type));
+		return;
+	}
+
+	P("type=%s contents:\n", drm_dp_mst_req_type_str(req->req_type));
+	indent++;
+
+	switch (req->req_type) {
+	case DP_ENUM_PATH_RESOURCES:
+	case DP_POWER_DOWN_PHY:
+	case DP_POWER_UP_PHY:
+		P("port=%d\n", req->u.port_num.port_number);
+		break;
+	case DP_ALLOCATE_PAYLOAD:
+		P("port=%d vcpi=%d pbn=%d sdp_streams=%d %*ph\n",
+		  req->u.allocate_payload.port_number,
+		  req->u.allocate_payload.vcpi, req->u.allocate_payload.pbn,
+		  req->u.allocate_payload.number_sdp_streams,
+		  req->u.allocate_payload.number_sdp_streams,
+		  req->u.allocate_payload.sdp_stream_sink);
+		break;
+	case DP_QUERY_PAYLOAD:
+		P("port=%d vcpi=%d\n",
+		  req->u.query_payload.port_number,
+		  req->u.query_payload.vcpi);
+		break;
+	case DP_REMOTE_DPCD_READ:
+		P("port=%d dpcd_addr=%05x len=%d\n",
+		  req->u.dpcd_read.port_number, req->u.dpcd_read.dpcd_address,
+		  req->u.dpcd_read.num_bytes);
+		break;
+	case DP_REMOTE_DPCD_WRITE:
+		P("port=%d addr=%05x len=%d: %*ph\n",
+		  req->u.dpcd_write.port_number,
+		  req->u.dpcd_write.dpcd_address,
+		  req->u.dpcd_write.num_bytes, req->u.dpcd_write.num_bytes,
+		  req->u.dpcd_write.bytes);
+		break;
+	case DP_REMOTE_I2C_READ:
+		P("port=%d num_tx=%d id=%d size=%d:\n",
+		  req->u.i2c_read.port_number,
+		  req->u.i2c_read.num_transactions,
+		  req->u.i2c_read.read_i2c_device_id,
+		  req->u.i2c_read.num_bytes_read);
+
+		indent++;
+		for (i = 0; i < req->u.i2c_read.num_transactions; i++) {
+			const struct drm_dp_remote_i2c_read_tx *rtx =
+				&req->u.i2c_read.transactions[i];
+
+			P("%d: id=%03d size=%03d no_stop_bit=%d tx_delay=%03d: %*ph\n",
+			  i, rtx->i2c_dev_id, rtx->num_bytes,
+			  rtx->no_stop_bit, rtx->i2c_transaction_delay,
+			  rtx->num_bytes, rtx->bytes);
+		}
+		break;
+	case DP_REMOTE_I2C_WRITE:
+		P("port=%d id=%d size=%d: %*ph\n",
+		  req->u.i2c_write.port_number,
+		  req->u.i2c_write.write_i2c_device_id,
+		  req->u.i2c_write.num_bytes, req->u.i2c_write.num_bytes,
+		  req->u.i2c_write.bytes);
+		break;
+	default:
+		P("???\n");
+		break;
+	}
+#undef P
+}
+EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_dump_sideband_msg_req_body);
+
+static inline void
+drm_dp_mst_dump_sideband_msg_tx(struct drm_printer *p,
+				const struct drm_dp_sideband_msg_tx *txmsg)
+{
+	struct drm_dp_sideband_msg_req_body req;
+	char buf[64];
+	int ret;
+	int i;
+
+	drm_dp_mst_rad_to_str(txmsg->dst->rad, txmsg->dst->lct, buf,
+			      sizeof(buf));
+	drm_printf(p, "txmsg cur_offset=%x cur_len=%x seqno=%x state=%s path_msg=%d dst=%s\n",
+		   txmsg->cur_offset, txmsg->cur_len, txmsg->seqno,
+		   drm_dp_mst_sideband_tx_state_str(txmsg->state),
+		   txmsg->path_msg, buf);
+
+	ret = drm_dp_decode_sideband_req(txmsg, &req);
+	if (ret) {
+		drm_printf(p, "<failed to decode sideband req: %d>\n", ret);
+		return;
+	}
+	drm_dp_dump_sideband_msg_req_body(&req, 1, p);
+
+	switch (req.req_type) {
+	case DP_REMOTE_DPCD_WRITE:
+		kfree(req.u.dpcd_write.bytes);
+		break;
+	case DP_REMOTE_I2C_READ:
+		for (i = 0; i < req.u.i2c_read.num_transactions; i++)
+			kfree(req.u.i2c_read.transactions[i].bytes);
+		break;
+	case DP_REMOTE_I2C_WRITE:
+		kfree(req.u.i2c_write.bytes);
+		break;
+	}
+}
 
 static void drm_dp_crc_sideband_chunk_req(u8 *msg, u8 len)
 {
@@ -894,6 +1180,11 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,
 		}
 	}
 out:
+	if (unlikely(ret == -EIO && drm_debug & DRM_UT_DP)) {
+		struct drm_printer p = drm_debug_printer(DBG_PREFIX);
+
+		drm_dp_mst_dump_sideband_msg_tx(&p, txmsg);
+	}
 	mutex_unlock(&mgr->qlock);
 
 	return ret;
@@ -2013,8 +2304,11 @@ static int process_single_tx_qlock(struct drm_dp_mst_topology_mgr *mgr,
 	idx += tosend + 1;
 
 	ret = drm_dp_send_sideband_msg(mgr, up, chunk, idx);
-	if (ret) {
-		DRM_DEBUG_KMS("sideband msg failed to send\n");
+	if (unlikely(ret && drm_debug & DRM_UT_DP)) {
+		struct drm_printer p = drm_debug_printer(DBG_PREFIX);
+
+		drm_printf(&p, "sideband msg failed to send\n");
+		drm_dp_mst_dump_sideband_msg_tx(&p, txmsg);
 		return ret;
 	}
 
@@ -2076,6 +2370,13 @@ static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr,
 {
 	mutex_lock(&mgr->qlock);
 	list_add_tail(&txmsg->next, &mgr->tx_msg_downq);
+
+	if (unlikely(drm_debug & DRM_UT_DP)) {
+		struct drm_printer p = drm_debug_printer(DBG_PREFIX);
+
+		drm_dp_mst_dump_sideband_msg_tx(&p, txmsg);
+	}
+
 	if (list_is_singular(&mgr->tx_msg_downq))
 		process_single_down_tx_qlock(mgr);
 	mutex_unlock(&mgr->qlock);
diff --git a/drivers/gpu/drm/drm_dp_mst_topology_internal.h b/drivers/gpu/drm/drm_dp_mst_topology_internal.h
new file mode 100644
index 000000000000..eeda9a61c657
--- /dev/null
+++ b/drivers/gpu/drm/drm_dp_mst_topology_internal.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Declarations for DP MST related functions which are only used in selftests
+ *
+ * Copyright © 2018 Red Hat
+ * Authors:
+ *     Lyude Paul <lyude@redhat.com>
+ */
+
+#ifndef _DRM_DP_MST_HELPER_INTERNAL_H_
+#define _DRM_DP_MST_HELPER_INTERNAL_H_
+
+#include <drm/drm_dp_mst_helper.h>
+
+void
+drm_dp_encode_sideband_req(const struct drm_dp_sideband_msg_req_body *req,
+			   struct drm_dp_sideband_msg_tx *raw);
+int drm_dp_decode_sideband_req(const struct drm_dp_sideband_msg_tx *raw,
+			       struct drm_dp_sideband_msg_req_body *req);
+void
+drm_dp_dump_sideband_msg_req_body(const struct drm_dp_sideband_msg_req_body *req,
+				  int indent, struct drm_printer *printer);
+
+#endif /* !_DRM_DP_MST_HELPER_INTERNAL_H_ */
diff --git a/drivers/gpu/drm/selftests/drm_modeset_selftests.h b/drivers/gpu/drm/selftests/drm_modeset_selftests.h
index dec3ee3ec96f..1898de0b4a4d 100644
--- a/drivers/gpu/drm/selftests/drm_modeset_selftests.h
+++ b/drivers/gpu/drm/selftests/drm_modeset_selftests.h
@@ -33,3 +33,4 @@ selftest(damage_iter_damage_one_outside, igt_damage_iter_damage_one_outside)
 selftest(damage_iter_damage_src_moved, igt_damage_iter_damage_src_moved)
 selftest(damage_iter_damage_not_visible, igt_damage_iter_damage_not_visible)
 selftest(dp_mst_calc_pbn_mode, igt_dp_mst_calc_pbn_mode)
+selftest(dp_mst_sideband_msg_req_decode, igt_dp_mst_sideband_msg_req_decode)
diff --git a/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c b/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
index 9baa5171988d..af2b2de65316 100644
--- a/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
+++ b/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
@@ -3,9 +3,12 @@
  * Test cases for for the DRM DP MST helpers
  */
 
+#define PREFIX_STR "[drm_dp_mst_helper]"
+
 #include <drm/drm_dp_mst_helper.h>
 #include <drm/drm_print.h>
 
+#include "../drm_dp_mst_topology_internal.h"
 #include "test-drm_modeset_common.h"
 
 int igt_dp_mst_calc_pbn_mode(void *ignored)
@@ -32,3 +35,204 @@ int igt_dp_mst_calc_pbn_mode(void *ignored)
 
 	return 0;
 }
+
+static bool
+sideband_msg_req_equal(const struct drm_dp_sideband_msg_req_body *in,
+		       const struct drm_dp_sideband_msg_req_body *out)
+{
+	const struct drm_dp_remote_i2c_read_tx *txin, *txout;
+	int i;
+
+	if (in->req_type != out->req_type)
+		return false;
+
+	switch (in->req_type) {
+	/*
+	 * Compare struct members manually for request types which can't be
+	 * compared simply using memcmp(). This is because said request types
+	 * contain pointers to other allocated structs
+	 */
+	case DP_REMOTE_I2C_READ:
+#define IN in->u.i2c_read
+#define OUT out->u.i2c_read
+		if (IN.num_bytes_read != OUT.num_bytes_read ||
+		    IN.num_transactions != OUT.num_transactions ||
+		    IN.port_number != OUT.port_number ||
+		    IN.read_i2c_device_id != OUT.read_i2c_device_id)
+			return false;
+
+		for (i = 0; i < IN.num_transactions; i++) {
+			txin = &IN.transactions[i];
+			txout = &OUT.transactions[i];
+
+			if (txin->i2c_dev_id != txout->i2c_dev_id ||
+			    txin->no_stop_bit != txout->no_stop_bit ||
+			    txin->num_bytes != txout->num_bytes ||
+			    txin->i2c_transaction_delay !=
+			    txout->i2c_transaction_delay)
+				return false;
+
+			if (memcmp(txin->bytes, txout->bytes,
+				   txin->num_bytes) != 0)
+				return false;
+		}
+		break;
+#undef IN
+#undef OUT
+
+	case DP_REMOTE_DPCD_WRITE:
+#define IN in->u.dpcd_write
+#define OUT out->u.dpcd_write
+		if (IN.dpcd_address != OUT.dpcd_address ||
+		    IN.num_bytes != OUT.num_bytes ||
+		    IN.port_number != OUT.port_number)
+			return false;
+
+		return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0;
+#undef IN
+#undef OUT
+
+	case DP_REMOTE_I2C_WRITE:
+#define IN in->u.i2c_write
+#define OUT out->u.i2c_write
+		if (IN.port_number != OUT.port_number ||
+		    IN.write_i2c_device_id != OUT.write_i2c_device_id ||
+		    IN.num_bytes != OUT.num_bytes)
+			return false;
+
+		return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0;
+#undef IN
+#undef OUT
+
+	default:
+		return memcmp(in, out, sizeof(*in)) == 0;
+	}
+
+	return true;
+}
+
+static bool
+sideband_msg_req_encode_decode(struct drm_dp_sideband_msg_req_body *in)
+{
+	struct drm_dp_sideband_msg_req_body out = {0};
+	struct drm_printer p = drm_err_printer(PREFIX_STR);
+	struct drm_dp_sideband_msg_tx txmsg;
+	int i, ret;
+
+	drm_dp_encode_sideband_req(in, &txmsg);
+	ret = drm_dp_decode_sideband_req(&txmsg, &out);
+	if (ret < 0) {
+		drm_printf(&p, "Failed to decode sideband request: %d\n",
+			   ret);
+		return false;
+	}
+
+	if (!sideband_msg_req_equal(in, &out)) {
+		drm_printf(&p, "Encode/decode failed, expected:\n");
+		drm_dp_dump_sideband_msg_req_body(in, 1, &p);
+		drm_printf(&p, "Got:\n");
+		drm_dp_dump_sideband_msg_req_body(&out, 1, &p);
+		return false;
+	}
+
+	switch (in->req_type) {
+	case DP_REMOTE_DPCD_WRITE:
+		kfree(out.u.dpcd_write.bytes);
+		break;
+	case DP_REMOTE_I2C_READ:
+		for (i = 0; i < out.u.i2c_read.num_transactions; i++)
+			kfree(out.u.i2c_read.transactions[i].bytes);
+		break;
+	case DP_REMOTE_I2C_WRITE:
+		kfree(out.u.i2c_write.bytes);
+		break;
+	}
+
+	/* Clear everything but the req_type for the input */
+	memset(&in->u, 0, sizeof(in->u));
+
+	return true;
+}
+
+int igt_dp_mst_sideband_msg_req_decode(void *unused)
+{
+	struct drm_dp_sideband_msg_req_body in = { 0 };
+	u8 data[] = { 0xff, 0x0, 0xdd };
+	int i;
+
+#define DO_TEST() FAIL_ON(!sideband_msg_req_encode_decode(&in))
+
+	in.req_type = DP_ENUM_PATH_RESOURCES;
+	in.u.port_num.port_number = 5;
+	DO_TEST();
+
+	in.req_type = DP_POWER_UP_PHY;
+	in.u.port_num.port_number = 5;
+	DO_TEST();
+
+	in.req_type = DP_POWER_DOWN_PHY;
+	in.u.port_num.port_number = 5;
+	DO_TEST();
+
+	in.req_type = DP_ALLOCATE_PAYLOAD;
+	in.u.allocate_payload.number_sdp_streams = 3;
+	for (i = 0; i < in.u.allocate_payload.number_sdp_streams; i++)
+		in.u.allocate_payload.sdp_stream_sink[i] = i + 1;
+	DO_TEST();
+	in.u.allocate_payload.port_number = 0xf;
+	DO_TEST();
+	in.u.allocate_payload.vcpi = 0x7f;
+	DO_TEST();
+	in.u.allocate_payload.pbn = U16_MAX;
+	DO_TEST();
+
+	in.req_type = DP_QUERY_PAYLOAD;
+	in.u.query_payload.port_number = 0xf;
+	DO_TEST();
+	in.u.query_payload.vcpi = 0x7f;
+	DO_TEST();
+
+	in.req_type = DP_REMOTE_DPCD_READ;
+	in.u.dpcd_read.port_number = 0xf;
+	DO_TEST();
+	in.u.dpcd_read.dpcd_address = 0xfedcb;
+	DO_TEST();
+	in.u.dpcd_read.num_bytes = U8_MAX;
+	DO_TEST();
+
+	in.req_type = DP_REMOTE_DPCD_WRITE;
+	in.u.dpcd_write.port_number = 0xf;
+	DO_TEST();
+	in.u.dpcd_write.dpcd_address = 0xfedcb;
+	DO_TEST();
+	in.u.dpcd_write.num_bytes = ARRAY_SIZE(data);
+	in.u.dpcd_write.bytes = data;
+	DO_TEST();
+
+	in.req_type = DP_REMOTE_I2C_READ;
+	in.u.i2c_read.port_number = 0xf;
+	DO_TEST();
+	in.u.i2c_read.read_i2c_device_id = 0x7f;
+	DO_TEST();
+	in.u.i2c_read.num_transactions = 3;
+	in.u.i2c_read.num_bytes_read = ARRAY_SIZE(data) * 3;
+	for (i = 0; i < in.u.i2c_read.num_transactions; i++) {
+		in.u.i2c_read.transactions[i].bytes = data;
+		in.u.i2c_read.transactions[i].num_bytes = ARRAY_SIZE(data);
+		in.u.i2c_read.transactions[i].i2c_dev_id = 0x7f & ~i;
+		in.u.i2c_read.transactions[i].i2c_transaction_delay = 0xf & ~i;
+	}
+	DO_TEST();
+
+	in.req_type = DP_REMOTE_I2C_WRITE;
+	in.u.i2c_write.port_number = 0xf;
+	DO_TEST();
+	in.u.i2c_write.write_i2c_device_id = 0x7f;
+	DO_TEST();
+	in.u.i2c_write.num_bytes = ARRAY_SIZE(data);
+	in.u.i2c_write.bytes = data;
+	DO_TEST();
+
+#undef DO_TEST
+	return 0;
+}
diff --git a/drivers/gpu/drm/selftests/test-drm_modeset_common.h b/drivers/gpu/drm/selftests/test-drm_modeset_common.h
index 590bda35a683..0fcb8bbc6a1b 100644
--- a/drivers/gpu/drm/selftests/test-drm_modeset_common.h
+++ b/drivers/gpu/drm/selftests/test-drm_modeset_common.h
@@ -40,5 +40,6 @@ int igt_damage_iter_damage_one_outside(void *ignored);
 int igt_damage_iter_damage_src_moved(void *ignored);
 int igt_damage_iter_damage_not_visible(void *ignored);
 int igt_dp_mst_calc_pbn_mode(void *ignored);
+int igt_dp_mst_sideband_msg_req_decode(void *ignored);
 
 #endif
diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
index 4a4507fe928d..5423a8adda78 100644
--- a/include/drm/drm_dp_mst_helper.h
+++ b/include/drm/drm_dp_mst_helper.h
@@ -293,7 +293,7 @@ struct drm_dp_remote_dpcd_write {
 struct drm_dp_remote_i2c_read {
 	u8 num_transactions;
 	u8 port_number;
-	struct {
+	struct drm_dp_remote_i2c_read_tx {
 		u8 i2c_dev_id;
 		u8 num_bytes;
 		u8 *bytes;
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 08/27] drm/dp_mst: Remove PDT teardown in drm_dp_destroy_port() and refactor
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (6 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 07/27] drm/dp_mst: Add sideband down request tracing + selftests Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-25 19:00   ` Sean Paul
  2019-09-03 20:45 ` [PATCH v2 09/27] drm/dp_mst: Refactor drm_dp_send_enum_path_resources Lyude Paul
                   ` (18 subsequent siblings)
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

This will allow us to add some locking for port->* members, in
particular the PDT and ->connector, which can't be done from
drm_dp_destroy_port() since we don't know what locks the caller might be
holding.

Changes since v2:
* Clarify commit message

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 40 +++++++++++----------------
 1 file changed, 16 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index f5f1d8b50fb6..af3189df28aa 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -1511,31 +1511,22 @@ static void drm_dp_destroy_port(struct kref *kref)
 		container_of(kref, struct drm_dp_mst_port, topology_kref);
 	struct drm_dp_mst_topology_mgr *mgr = port->mgr;
 
-	if (!port->input) {
-		kfree(port->cached_edid);
+	/* There's nothing that needs locking to destroy an input port yet */
+	if (port->input) {
+		drm_dp_mst_put_port_malloc(port);
+		return;
+	}
 
-		/*
-		 * The only time we don't have a connector
-		 * on an output port is if the connector init
-		 * fails.
-		 */
-		if (port->connector) {
-			/* we can't destroy the connector here, as
-			 * we might be holding the mode_config.mutex
-			 * from an EDID retrieval */
+	kfree(port->cached_edid);
 
-			mutex_lock(&mgr->delayed_destroy_lock);
-			list_add(&port->next, &mgr->destroy_port_list);
-			mutex_unlock(&mgr->delayed_destroy_lock);
-			schedule_work(&mgr->delayed_destroy_work);
-			return;
-		}
-		/* no need to clean up vcpi
-		 * as if we have no connector we never setup a vcpi */
-		drm_dp_port_teardown_pdt(port, port->pdt);
-		port->pdt = DP_PEER_DEVICE_NONE;
-	}
-	drm_dp_mst_put_port_malloc(port);
+	/*
+	 * we can't destroy the connector here, as we might be holding the
+	 * mode_config.mutex from an EDID retrieval
+	 */
+	mutex_lock(&mgr->delayed_destroy_lock);
+	list_add(&port->next, &mgr->destroy_port_list);
+	mutex_unlock(&mgr->delayed_destroy_lock);
+	schedule_work(&mgr->delayed_destroy_work);
 }
 
 /**
@@ -3998,7 +3989,8 @@ static void drm_dp_tx_work(struct work_struct *work)
 static inline void
 drm_dp_delayed_destroy_port(struct drm_dp_mst_port *port)
 {
-	port->mgr->cbs->destroy_connector(port->mgr, port->connector);
+	if (port->connector)
+		port->mgr->cbs->destroy_connector(port->mgr, port->connector);
 
 	drm_dp_port_teardown_pdt(port, port->pdt);
 	port->pdt = DP_PEER_DEVICE_NONE;
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 09/27] drm/dp_mst: Refactor drm_dp_send_enum_path_resources
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (7 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 08/27] drm/dp_mst: Remove PDT teardown in drm_dp_destroy_port() and refactor Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-03 20:45 ` [PATCH v2 10/27] drm/dp_mst: Remove huge conditional in drm_dp_mst_handle_up_req() Lyude Paul
                   ` (17 subsequent siblings)
  26 siblings, 0 replies; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

Use more pointers so we don't have to write out
txmsg->reply.u.path_resources each time. Also, fix line wrapping +
rearrange local variables.

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 24 ++++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index af3189df28aa..241c66f75bed 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -2437,12 +2437,14 @@ static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
 	kfree(txmsg);
 }
 
-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)
+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)
 {
-	int len;
+	struct drm_dp_enum_path_resources_ack_reply *path_res;
 	struct drm_dp_sideband_msg_tx *txmsg;
+	int len;
 	int ret;
 
 	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
@@ -2456,14 +2458,20 @@ static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
 
 	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
 	if (ret > 0) {
+		path_res = &txmsg->reply.u.path_resources;
+
 		if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) {
 			DRM_DEBUG_KMS("enum path resources nak received\n");
 		} else {
-			if (port->port_num != txmsg->reply.u.path_resources.port_number)
+			if (port->port_num != path_res->port_number)
 				DRM_ERROR("got incorrect port in response\n");
-			DRM_DEBUG_KMS("enum path resources %d: %d %d\n", txmsg->reply.u.path_resources.port_number, txmsg->reply.u.path_resources.full_payload_bw_number,
-			       txmsg->reply.u.path_resources.avail_payload_bw_number);
-			port->available_pbn = txmsg->reply.u.path_resources.avail_payload_bw_number;
+
+			DRM_DEBUG_KMS("enum path resources %d: %d %d\n",
+				      path_res->port_number,
+				      path_res->full_payload_bw_number,
+				      path_res->avail_payload_bw_number);
+			port->available_pbn =
+				path_res->avail_payload_bw_number;
 		}
 	}
 
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 10/27] drm/dp_mst: Remove huge conditional in drm_dp_mst_handle_up_req()
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (8 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 09/27] drm/dp_mst: Refactor drm_dp_send_enum_path_resources Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-03 20:45 ` [PATCH v2 11/27] drm/dp_mst: Constify guid in drm_dp_get_mst_branch_by_guid() Lyude Paul
                   ` (16 subsequent siblings)
  26 siblings, 0 replies; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

Which reduces indentation and makes this function more legible.

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 90 +++++++++++++--------------
 1 file changed, 45 insertions(+), 45 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 241c66f75bed..43452872efad 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -3245,7 +3245,9 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
 
 static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
 {
-	int ret = 0;
+	struct drm_dp_sideband_msg_req_body msg;
+	struct drm_dp_mst_branch *mstb = NULL;
+	bool seqno;
 
 	if (!drm_dp_get_one_sb_msg(mgr, true)) {
 		memset(&mgr->up_req_recv, 0,
@@ -3253,62 +3255,60 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
 		return 0;
 	}
 
-	if (mgr->up_req_recv.have_eomt) {
-		struct drm_dp_sideband_msg_req_body msg;
-		struct drm_dp_mst_branch *mstb = NULL;
-		bool seqno;
-
-		if (!mgr->up_req_recv.initial_hdr.broadcast) {
-			mstb = drm_dp_get_mst_branch_device(mgr,
-							    mgr->up_req_recv.initial_hdr.lct,
-							    mgr->up_req_recv.initial_hdr.rad);
-			if (!mstb) {
-				DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
-				memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
-				return 0;
-			}
+	if (!mgr->up_req_recv.have_eomt)
+		return 0;
+
+	if (!mgr->up_req_recv.initial_hdr.broadcast) {
+		mstb = drm_dp_get_mst_branch_device(mgr,
+						    mgr->up_req_recv.initial_hdr.lct,
+						    mgr->up_req_recv.initial_hdr.rad);
+		if (!mstb) {
+			DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
+			memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+			return 0;
 		}
+	}
 
-		seqno = mgr->up_req_recv.initial_hdr.seqno;
-		drm_dp_sideband_parse_req(&mgr->up_req_recv, &msg);
+	seqno = mgr->up_req_recv.initial_hdr.seqno;
+	drm_dp_sideband_parse_req(&mgr->up_req_recv, &msg);
 
-		if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
-			drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno, false);
+	if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
+		drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno, false);
 
-			if (!mstb)
-				mstb = drm_dp_get_mst_branch_device_by_guid(mgr, msg.u.conn_stat.guid);
+		if (!mstb)
+			mstb = drm_dp_get_mst_branch_device_by_guid(mgr, msg.u.conn_stat.guid);
 
-			if (!mstb) {
-				DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
-				memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
-				return 0;
-			}
+		if (!mstb) {
+			DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
+			memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+			return 0;
+		}
 
-			drm_dp_update_port(mstb, &msg.u.conn_stat);
+		drm_dp_update_port(mstb, &msg.u.conn_stat);
 
-			DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n", msg.u.conn_stat.port_number, msg.u.conn_stat.legacy_device_plug_status, msg.u.conn_stat.displayport_device_plug_status, msg.u.conn_stat.message_capability_status, msg.u.conn_stat.input_port, msg.u.conn_stat.peer_device_type);
-			drm_kms_helper_hotplug_event(mgr->dev);
-
-		} else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
-			drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno, false);
-			if (!mstb)
-				mstb = drm_dp_get_mst_branch_device_by_guid(mgr, msg.u.resource_stat.guid);
+		DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n", msg.u.conn_stat.port_number, msg.u.conn_stat.legacy_device_plug_status, msg.u.conn_stat.displayport_device_plug_status, msg.u.conn_stat.message_capability_status, msg.u.conn_stat.input_port, msg.u.conn_stat.peer_device_type);
+		drm_kms_helper_hotplug_event(mgr->dev);
 
-			if (!mstb) {
-				DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
-				memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
-				return 0;
-			}
+	} else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
+		drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno, false);
+		if (!mstb)
+			mstb = drm_dp_get_mst_branch_device_by_guid(mgr, msg.u.resource_stat.guid);
 
-			DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n", msg.u.resource_stat.port_number, msg.u.resource_stat.available_pbn);
+		if (!mstb) {
+			DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
+			memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+			return 0;
 		}
 
-		if (mstb)
-			drm_dp_mst_topology_put_mstb(mstb);
-
-		memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+		DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n", msg.u.resource_stat.port_number, msg.u.resource_stat.available_pbn);
 	}
-	return ret;
+
+	if (mstb)
+		drm_dp_mst_topology_put_mstb(mstb);
+
+	memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+
+	return 0;
 }
 
 /**
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 11/27] drm/dp_mst: Constify guid in drm_dp_get_mst_branch_by_guid()
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (9 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 10/27] drm/dp_mst: Remove huge conditional in drm_dp_mst_handle_up_req() Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-03 21:41   ` Dave Airlie
  2019-09-03 20:45 ` [PATCH v2 12/27] drm/dp_mst: Refactor drm_dp_mst_handle_up_req() Lyude Paul
                   ` (15 subsequent siblings)
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

And it's helper, we'll be using this in just a moment.

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 43452872efad..b44d3696c09a 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -2060,7 +2060,7 @@ static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_
 
 static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper(
 	struct drm_dp_mst_branch *mstb,
-	uint8_t *guid)
+	const uint8_t *guid)
 {
 	struct drm_dp_mst_branch *found_mstb;
 	struct drm_dp_mst_port *port;
@@ -2084,7 +2084,7 @@ static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper(
 
 static struct drm_dp_mst_branch *
 drm_dp_get_mst_branch_device_by_guid(struct drm_dp_mst_topology_mgr *mgr,
-				     uint8_t *guid)
+				     const uint8_t *guid)
 {
 	struct drm_dp_mst_branch *mstb;
 	int ret;
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 12/27] drm/dp_mst: Refactor drm_dp_mst_handle_up_req()
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (10 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 11/27] drm/dp_mst: Constify guid in drm_dp_get_mst_branch_by_guid() Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-03 20:45 ` [PATCH v2 13/27] drm/dp_mst: Refactor drm_dp_mst_handle_down_rep() Lyude Paul
                   ` (14 subsequent siblings)
  26 siblings, 0 replies; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

There's a couple of changes here, so to summarize:

* Remove the big ugly mgr->up_req_recv.have_eomt conditional to save on
  indenting
* Store &mgr->up_req_recv.initial_hdr in a variable so we don't keep
  going over 80 character long lines
* De-duplicate code for calling drm_dp_send_up_ack_reply() and getting
  the MSTB via it's GUID
* Remove all of the duplicate calls to memset() and just use a goto
  instead
* Actually do line wrapping
* Remove the unnecessary if (mstb) check before calling
  drm_dp_mst_topology_put_mstb() - we are guaranteed to always have
  mstb != NULL at that point in the function

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 75 ++++++++++++++-------------
 1 file changed, 38 insertions(+), 37 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index b44d3696c09a..64487098158a 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -3246,68 +3246,69 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
 static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
 {
 	struct drm_dp_sideband_msg_req_body msg;
+	struct drm_dp_sideband_msg_hdr *hdr = &mgr->up_req_recv.initial_hdr;
 	struct drm_dp_mst_branch *mstb = NULL;
+	const u8 *guid;
 	bool seqno;
 
-	if (!drm_dp_get_one_sb_msg(mgr, true)) {
-		memset(&mgr->up_req_recv, 0,
-		       sizeof(struct drm_dp_sideband_msg_rx));
-		return 0;
-	}
+	if (!drm_dp_get_one_sb_msg(mgr, true))
+		goto out;
 
 	if (!mgr->up_req_recv.have_eomt)
 		return 0;
 
-	if (!mgr->up_req_recv.initial_hdr.broadcast) {
-		mstb = drm_dp_get_mst_branch_device(mgr,
-						    mgr->up_req_recv.initial_hdr.lct,
-						    mgr->up_req_recv.initial_hdr.rad);
+	if (!hdr->broadcast) {
+		mstb = drm_dp_get_mst_branch_device(mgr, hdr->lct, hdr->rad);
 		if (!mstb) {
-			DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
-			memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
-			return 0;
+			DRM_DEBUG_KMS("Got MST reply from unknown device %d\n",
+				      hdr->lct);
+			goto out;
 		}
 	}
 
-	seqno = mgr->up_req_recv.initial_hdr.seqno;
+	seqno = hdr->seqno;
 	drm_dp_sideband_parse_req(&mgr->up_req_recv, &msg);
 
-	if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
-		drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno, false);
+	if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY)
+		guid = msg.u.conn_stat.guid;
+	else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY)
+		guid = msg.u.resource_stat.guid;
+	else
+		goto out;
 
-		if (!mstb)
-			mstb = drm_dp_get_mst_branch_device_by_guid(mgr, msg.u.conn_stat.guid);
+	drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno,
+				 false);
 
+	if (!mstb) {
+		mstb = drm_dp_get_mst_branch_device_by_guid(mgr, guid);
 		if (!mstb) {
-			DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
-			memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
-			return 0;
+			DRM_DEBUG_KMS("Got MST reply from unknown device %d\n",
+				      hdr->lct);
+			goto out;
 		}
+	}
 
+	if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
 		drm_dp_update_port(mstb, &msg.u.conn_stat);
 
-		DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n", msg.u.conn_stat.port_number, msg.u.conn_stat.legacy_device_plug_status, msg.u.conn_stat.displayport_device_plug_status, msg.u.conn_stat.message_capability_status, msg.u.conn_stat.input_port, msg.u.conn_stat.peer_device_type);
-		drm_kms_helper_hotplug_event(mgr->dev);
+		DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n",
+			      msg.u.conn_stat.port_number,
+			      msg.u.conn_stat.legacy_device_plug_status,
+			      msg.u.conn_stat.displayport_device_plug_status,
+			      msg.u.conn_stat.message_capability_status,
+			      msg.u.conn_stat.input_port,
+			      msg.u.conn_stat.peer_device_type);
 
+		drm_kms_helper_hotplug_event(mgr->dev);
 	} else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
-		drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno, false);
-		if (!mstb)
-			mstb = drm_dp_get_mst_branch_device_by_guid(mgr, msg.u.resource_stat.guid);
-
-		if (!mstb) {
-			DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
-			memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
-			return 0;
-		}
-
-		DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n", msg.u.resource_stat.port_number, msg.u.resource_stat.available_pbn);
+		DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n",
+			      msg.u.resource_stat.port_number,
+			      msg.u.resource_stat.available_pbn);
 	}
 
-	if (mstb)
-		drm_dp_mst_topology_put_mstb(mstb);
-
+	drm_dp_mst_topology_put_mstb(mstb);
+out:
 	memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
-
 	return 0;
 }
 
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 13/27] drm/dp_mst: Refactor drm_dp_mst_handle_down_rep()
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (11 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 12/27] drm/dp_mst: Refactor drm_dp_mst_handle_up_req() Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-03 20:45 ` [PATCH v2 14/27] drm/dp_mst: Destroy topology_mgr mutexes Lyude Paul
                   ` (13 subsequent siblings)
  26 siblings, 0 replies; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

* Remove the big ugly have_eomt conditional
* Store &mgr->down_rep_recv.initial_hdr in a var to make line wrapping
  easier
* Remove duplicate memset() calls
* Actually wrap lines

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 102 +++++++++++++-------------
 1 file changed, 50 insertions(+), 52 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 64487098158a..74161f442584 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -3179,68 +3179,66 @@ static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up)
 
 static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
 {
-	int ret = 0;
+	struct drm_dp_sideband_msg_tx *txmsg;
+	struct drm_dp_mst_branch *mstb;
+	struct drm_dp_sideband_msg_hdr *hdr = &mgr->down_rep_recv.initial_hdr;
+	int slot = -1;
+
+	if (!drm_dp_get_one_sb_msg(mgr, false))
+		goto clear_down_rep_recv;
 
-	if (!drm_dp_get_one_sb_msg(mgr, false)) {
-		memset(&mgr->down_rep_recv, 0,
-		       sizeof(struct drm_dp_sideband_msg_rx));
+	if (!mgr->down_rep_recv.have_eomt)
 		return 0;
+
+	mstb = drm_dp_get_mst_branch_device(mgr, hdr->lct, hdr->rad);
+	if (!mstb) {
+		DRM_DEBUG_KMS("Got MST reply from unknown device %d\n",
+			      hdr->lct);
+		goto clear_down_rep_recv;
 	}
 
-	if (mgr->down_rep_recv.have_eomt) {
-		struct drm_dp_sideband_msg_tx *txmsg;
-		struct drm_dp_mst_branch *mstb;
-		int slot = -1;
-		mstb = drm_dp_get_mst_branch_device(mgr,
-						    mgr->down_rep_recv.initial_hdr.lct,
-						    mgr->down_rep_recv.initial_hdr.rad);
+	/* find the message */
+	slot = hdr->seqno;
+	mutex_lock(&mgr->qlock);
+	txmsg = mstb->tx_slots[slot];
+	/* remove from slots */
+	mutex_unlock(&mgr->qlock);
 
-		if (!mstb) {
-			DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->down_rep_recv.initial_hdr.lct);
-			memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
-			return 0;
-		}
+	if (!txmsg) {
+		DRM_DEBUG_KMS("Got MST reply with no msg %p %d %d %02x %02x\n",
+			      mstb, hdr->seqno, hdr->lct, hdr->rad[0],
+			      mgr->down_rep_recv.msg[0]);
+		goto no_msg;
+	}
 
-		/* find the message */
-		slot = mgr->down_rep_recv.initial_hdr.seqno;
-		mutex_lock(&mgr->qlock);
-		txmsg = mstb->tx_slots[slot];
-		/* remove from slots */
-		mutex_unlock(&mgr->qlock);
-
-		if (!txmsg) {
-			DRM_DEBUG_KMS("Got MST reply with no msg %p %d %d %02x %02x\n",
-			       mstb,
-			       mgr->down_rep_recv.initial_hdr.seqno,
-			       mgr->down_rep_recv.initial_hdr.lct,
-				      mgr->down_rep_recv.initial_hdr.rad[0],
-				      mgr->down_rep_recv.msg[0]);
-			drm_dp_mst_topology_put_mstb(mstb);
-			memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
-			return 0;
-		}
+	drm_dp_sideband_parse_reply(&mgr->down_rep_recv, &txmsg->reply);
 
-		drm_dp_sideband_parse_reply(&mgr->down_rep_recv, &txmsg->reply);
+	if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK)
+		DRM_DEBUG_KMS("Got NAK reply: req 0x%02x (%s), reason 0x%02x (%s), nak data 0x%02x\n",
+			      txmsg->reply.req_type,
+			      drm_dp_mst_req_type_str(txmsg->reply.req_type),
+			      txmsg->reply.u.nak.reason,
+			      drm_dp_mst_nak_reason_str(txmsg->reply.u.nak.reason),
+			      txmsg->reply.u.nak.nak_data);
 
-		if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK)
-			DRM_DEBUG_KMS("Got NAK reply: req 0x%02x (%s), reason 0x%02x (%s), nak data 0x%02x\n",
-				      txmsg->reply.req_type,
-				      drm_dp_mst_req_type_str(txmsg->reply.req_type),
-				      txmsg->reply.u.nak.reason,
-				      drm_dp_mst_nak_reason_str(txmsg->reply.u.nak.reason),
-				      txmsg->reply.u.nak.nak_data);
-
-		memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
-		drm_dp_mst_topology_put_mstb(mstb);
+	memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+	drm_dp_mst_topology_put_mstb(mstb);
 
-		mutex_lock(&mgr->qlock);
-		txmsg->state = DRM_DP_SIDEBAND_TX_RX;
-		mstb->tx_slots[slot] = NULL;
-		mutex_unlock(&mgr->qlock);
+	mutex_lock(&mgr->qlock);
+	txmsg->state = DRM_DP_SIDEBAND_TX_RX;
+	mstb->tx_slots[slot] = NULL;
+	mutex_unlock(&mgr->qlock);
 
-		wake_up_all(&mgr->tx_waitq);
-	}
-	return ret;
+	wake_up_all(&mgr->tx_waitq);
+
+	return 0;
+
+no_msg:
+	drm_dp_mst_topology_put_mstb(mstb);
+clear_down_rep_recv:
+	memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+
+	return 0;
 }
 
 static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 14/27] drm/dp_mst: Destroy topology_mgr mutexes
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (12 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 13/27] drm/dp_mst: Refactor drm_dp_mst_handle_down_rep() Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-25 19:14   ` Sean Paul
  2019-09-03 20:45 ` [PATCH v2 15/27] drm/dp_mst: Cleanup drm_dp_send_link_address() a bit Lyude Paul
                   ` (12 subsequent siblings)
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

Turns out we've been forgetting for a while now to actually destroy any
of the mutexes that we create in drm_dp_mst_topology_mgr. So, let's do
that.

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 74161f442584..2f88cc173500 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -4339,6 +4339,11 @@ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
 	mgr->aux = NULL;
 	drm_atomic_private_obj_fini(&mgr->base);
 	mgr->funcs = NULL;
+
+	mutex_destroy(&mgr->delayed_destroy_lock);
+	mutex_destroy(&mgr->payload_lock);
+	mutex_destroy(&mgr->qlock);
+	mutex_destroy(&mgr->lock);
 }
 EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);
 
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 15/27] drm/dp_mst: Cleanup drm_dp_send_link_address() a bit
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (13 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 14/27] drm/dp_mst: Destroy topology_mgr mutexes Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-03 21:42   ` Dave Airlie
  2019-09-03 20:45 ` [PATCH v2 16/27] drm/dp_mst: Refactor pdt setup/teardown, add more locking Lyude Paul
                   ` (11 subsequent siblings)
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

Declare local pointer to the drm_dp_link_address_ack_reply struct
instead of constantly dereferencing it through the union in
txmsg->reply. Then, invert the order of conditionals so we don't have to
do the bulk of the work inside them, and can wrap lines even less. Then
finally, rearrange variable declarations a bit.

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 42 +++++++++++++++------------
 1 file changed, 23 insertions(+), 19 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 2f88cc173500..d1610434a0cb 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -2398,9 +2398,9 @@ drm_dp_dump_link_address(struct drm_dp_link_address_ack_reply *reply)
 static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
 				     struct drm_dp_mst_branch *mstb)
 {
-	int len;
 	struct drm_dp_sideband_msg_tx *txmsg;
-	int ret;
+	struct drm_dp_link_address_ack_reply *reply;
+	int i, len, ret;
 
 	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
 	if (!txmsg)
@@ -2412,28 +2412,32 @@ static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
 	mstb->link_address_sent = true;
 	drm_dp_queue_down_tx(mgr, txmsg);
 
+	/* FIXME: Actually do some real error handling here */
 	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
-	if (ret > 0) {
-		int i;
+	if (ret <= 0) {
+		DRM_ERROR("Sending link address failed with %d\n", ret);
+		goto out;
+	}
+	if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) {
+		DRM_ERROR("link address NAK received\n");
+		ret = -EIO;
+		goto out;
+	}
 
-		if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) {
-			DRM_DEBUG_KMS("link address nak received\n");
-		} else {
-			DRM_DEBUG_KMS("link address reply: %d\n", txmsg->reply.u.link_addr.nports);
-			drm_dp_dump_link_address(&txmsg->reply.u.link_addr);
+	reply = &txmsg->reply.u.link_addr;
+	DRM_DEBUG_KMS("link address reply: %d\n", reply->nports);
+	drm_dp_dump_link_address(reply);
 
-			drm_dp_check_mstb_guid(mstb, txmsg->reply.u.link_addr.guid);
+	drm_dp_check_mstb_guid(mstb, reply->guid);
 
-			for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) {
-				drm_dp_add_port(mstb, mgr->dev, &txmsg->reply.u.link_addr.ports[i]);
-			}
-			drm_kms_helper_hotplug_event(mgr->dev);
-		}
-	} else {
-		mstb->link_address_sent = false;
-		DRM_DEBUG_KMS("link address failed %d\n", ret);
-	}
+	for (i = 0; i < reply->nports; i++)
+		drm_dp_add_port(mstb, mgr->dev, &reply->ports[i]);
+
+	drm_kms_helper_hotplug_event(mgr->dev);
 
+out:
+	if (ret <= 0)
+		mstb->link_address_sent = false;
 	kfree(txmsg);
 }
 
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 16/27] drm/dp_mst: Refactor pdt setup/teardown, add more locking
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (14 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 15/27] drm/dp_mst: Cleanup drm_dp_send_link_address() a bit Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-25 19:27   ` Sean Paul
  2019-09-03 20:45 ` [PATCH v2 17/27] drm/dp_mst: Rename drm_dp_add_port and drm_dp_update_port Lyude Paul
                   ` (10 subsequent siblings)
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

Since we're going to be implementing suspend/resume reprobing very soon,
we need to make sure we are extra careful to ensure that our locking
actually protects the topology state where we expect it to. Turns out
this isn't the case with drm_dp_port_setup_pdt() and
drm_dp_port_teardown_pdt(), both of which change port->mstb without
grabbing &mgr->lock.

Additionally, since most callers of these functions are just using it to
teardown the port's previous PDT and setup a new one we can simplify
things a bit and combine drm_dp_port_setup_pdt() and
drm_dp_port_teardown_pdt() into a single function:
drm_dp_port_set_pdt(). This function also handles actually ensuring that
we grab the correct locks when we need to modify port->mstb.

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 181 +++++++++++++++-----------
 include/drm/drm_dp_mst_helper.h       |   6 +-
 2 files changed, 110 insertions(+), 77 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index d1610434a0cb..9944ef2ce885 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -1487,24 +1487,6 @@ drm_dp_mst_topology_put_mstb(struct drm_dp_mst_branch *mstb)
 	kref_put(&mstb->topology_kref, drm_dp_destroy_mst_branch_device);
 }
 
-static void drm_dp_port_teardown_pdt(struct drm_dp_mst_port *port, int old_pdt)
-{
-	struct drm_dp_mst_branch *mstb;
-
-	switch (old_pdt) {
-	case DP_PEER_DEVICE_DP_LEGACY_CONV:
-	case DP_PEER_DEVICE_SST_SINK:
-		/* remove i2c over sideband */
-		drm_dp_mst_unregister_i2c_bus(&port->aux);
-		break;
-	case DP_PEER_DEVICE_MST_BRANCHING:
-		mstb = port->mstb;
-		port->mstb = NULL;
-		drm_dp_mst_topology_put_mstb(mstb);
-		break;
-	}
-}
-
 static void drm_dp_destroy_port(struct kref *kref)
 {
 	struct drm_dp_mst_port *port =
@@ -1714,38 +1696,79 @@ static u8 drm_dp_calculate_rad(struct drm_dp_mst_port *port,
 	return parent_lct + 1;
 }
 
-/*
- * return sends link address for new mstb
- */
-static bool drm_dp_port_setup_pdt(struct drm_dp_mst_port *port)
+static int drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt)
 {
-	int ret;
-	u8 rad[6], lct;
-	bool send_link = false;
+	struct drm_dp_mst_topology_mgr *mgr = port->mgr;
+	struct drm_dp_mst_branch *mstb;
+	u8 rad[8], lct;
+	int ret = 0;
+
+	if (port->pdt == new_pdt)
+		return 0;
+
+	/* Teardown the old pdt, if there is one */
+	switch (port->pdt) {
+	case DP_PEER_DEVICE_DP_LEGACY_CONV:
+	case DP_PEER_DEVICE_SST_SINK:
+		/*
+		 * If the new PDT would also have an i2c bus, don't bother
+		 * with reregistering it
+		 */
+		if (new_pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
+		    new_pdt == DP_PEER_DEVICE_SST_SINK) {
+			port->pdt = new_pdt;
+			return 0;
+		}
+
+		/* remove i2c over sideband */
+		drm_dp_mst_unregister_i2c_bus(&port->aux);
+		break;
+	case DP_PEER_DEVICE_MST_BRANCHING:
+		mutex_lock(&mgr->lock);
+		drm_dp_mst_topology_put_mstb(port->mstb);
+		port->mstb = NULL;
+		mutex_unlock(&mgr->lock);
+		break;
+	}
+
+	port->pdt = new_pdt;
 	switch (port->pdt) {
 	case DP_PEER_DEVICE_DP_LEGACY_CONV:
 	case DP_PEER_DEVICE_SST_SINK:
 		/* add i2c over sideband */
 		ret = drm_dp_mst_register_i2c_bus(&port->aux);
 		break;
+
 	case DP_PEER_DEVICE_MST_BRANCHING:
 		lct = drm_dp_calculate_rad(port, rad);
+		mstb = drm_dp_add_mst_branch_device(lct, rad);
+		if (!mstb) {
+			ret = -ENOMEM;
+			DRM_ERROR("Failed to create MSTB for port %p", port);
+			goto out;
+		}
 
-		port->mstb = drm_dp_add_mst_branch_device(lct, rad);
-		if (port->mstb) {
-			port->mstb->mgr = port->mgr;
-			port->mstb->port_parent = port;
-			/*
-			 * Make sure this port's memory allocation stays
-			 * around until its child MSTB releases it
-			 */
-			drm_dp_mst_get_port_malloc(port);
+		mutex_lock(&mgr->lock);
+		port->mstb = mstb;
+		mstb->mgr = port->mgr;
+		mstb->port_parent = port;
 
-			send_link = true;
-		}
+		/*
+		 * Make sure this port's memory allocation stays
+		 * around until its child MSTB releases it
+		 */
+		drm_dp_mst_get_port_malloc(port);
+		mutex_unlock(&mgr->lock);
+
+		/* And make sure we send a link address for this */
+		ret = 1;
 		break;
 	}
-	return send_link;
+
+out:
+	if (ret < 0)
+		port->pdt = DP_PEER_DEVICE_NONE;
+	return ret;
 }
 
 /**
@@ -1881,10 +1904,9 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
 			    struct drm_device *dev,
 			    struct drm_dp_link_addr_reply_port *port_msg)
 {
+	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
 	struct drm_dp_mst_port *port;
-	bool ret;
 	bool created = false;
-	int old_pdt = 0;
 	int old_ddps = 0;
 
 	port = drm_dp_get_port(mstb, port_msg->port_number);
@@ -1896,7 +1918,7 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
 		kref_init(&port->malloc_kref);
 		port->parent = mstb;
 		port->port_num = port_msg->port_number;
-		port->mgr = mstb->mgr;
+		port->mgr = mgr;
 		port->aux.name = "DPMST";
 		port->aux.dev = dev->dev;
 		port->aux.is_remote = true;
@@ -1909,11 +1931,9 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
 
 		created = true;
 	} else {
-		old_pdt = port->pdt;
 		old_ddps = port->ddps;
 	}
 
-	port->pdt = port_msg->peer_device_type;
 	port->input = port_msg->input_port;
 	port->mcs = port_msg->mcs;
 	port->ddps = port_msg->ddps;
@@ -1925,29 +1945,33 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
 	/* manage mstb port lists with mgr lock - take a reference
 	   for this list */
 	if (created) {
-		mutex_lock(&mstb->mgr->lock);
+		mutex_lock(&mgr->lock);
 		drm_dp_mst_topology_get_port(port);
 		list_add(&port->next, &mstb->ports);
-		mutex_unlock(&mstb->mgr->lock);
+		mutex_unlock(&mgr->lock);
 	}
 
 	if (old_ddps != port->ddps) {
 		if (port->ddps) {
 			if (!port->input) {
-				drm_dp_send_enum_path_resources(mstb->mgr,
-								mstb, port);
+				drm_dp_send_enum_path_resources(mgr, mstb,
+								port);
 			}
 		} else {
 			port->available_pbn = 0;
 		}
 	}
 
-	if (old_pdt != port->pdt && !port->input) {
-		drm_dp_port_teardown_pdt(port, old_pdt);
-
-		ret = drm_dp_port_setup_pdt(port);
-		if (ret == true)
-			drm_dp_send_link_address(mstb->mgr, port->mstb);
+	if (!port->input) {
+		int ret = drm_dp_port_set_pdt(port,
+					      port_msg->peer_device_type);
+		if (ret == 1) {
+			drm_dp_send_link_address(mgr, port->mstb);
+		} else if (ret < 0) {
+			DRM_ERROR("Failed to change PDT on port %p: %d\n",
+				  port, ret);
+			goto fail;
+		}
 	}
 
 	if (created && !port->input) {
@@ -1955,18 +1979,11 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
 
 		build_mst_prop_path(mstb, port->port_num, proppath,
 				    sizeof(proppath));
-		port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr,
-								   port,
-								   proppath);
-		if (!port->connector) {
-			/* remove it from the port list */
-			mutex_lock(&mstb->mgr->lock);
-			list_del(&port->next);
-			mutex_unlock(&mstb->mgr->lock);
-			/* drop port list reference */
-			drm_dp_mst_topology_put_port(port);
-			goto out;
-		}
+		port->connector = (*mgr->cbs->add_connector)(mgr, port,
+							     proppath);
+		if (!port->connector)
+			goto fail;
+
 		if ((port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
 		     port->pdt == DP_PEER_DEVICE_SST_SINK) &&
 		    port->port_num >= DP_MST_LOGICAL_PORT_0) {
@@ -1974,28 +1991,38 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
 							 &port->aux.ddc);
 			drm_connector_set_tile_property(port->connector);
 		}
-		(*mstb->mgr->cbs->register_connector)(port->connector);
+
+		(*mgr->cbs->register_connector)(port->connector);
 	}
 
-out:
 	/* put reference to this port */
 	drm_dp_mst_topology_put_port(port);
+	return;
+
+fail:
+	/* Remove it from the port list */
+	mutex_lock(&mgr->lock);
+	list_del(&port->next);
+	mutex_unlock(&mgr->lock);
+
+	/* Drop the port list reference */
+	drm_dp_mst_topology_put_port(port);
+	/* And now drop our reference */
+	drm_dp_mst_topology_put_port(port);
 }
 
 static void drm_dp_update_port(struct drm_dp_mst_branch *mstb,
 			       struct drm_dp_connection_status_notify *conn_stat)
 {
 	struct drm_dp_mst_port *port;
-	int old_pdt;
 	int old_ddps;
 	bool dowork = false;
+
 	port = drm_dp_get_port(mstb, conn_stat->port_number);
 	if (!port)
 		return;
 
 	old_ddps = port->ddps;
-	old_pdt = port->pdt;
-	port->pdt = conn_stat->peer_device_type;
 	port->mcs = conn_stat->message_capability_status;
 	port->ldps = conn_stat->legacy_device_plug_status;
 	port->ddps = conn_stat->displayport_device_plug_status;
@@ -2007,11 +2034,17 @@ static void drm_dp_update_port(struct drm_dp_mst_branch *mstb,
 			port->available_pbn = 0;
 		}
 	}
-	if (old_pdt != port->pdt && !port->input) {
-		drm_dp_port_teardown_pdt(port, old_pdt);
 
-		if (drm_dp_port_setup_pdt(port))
+	if (!port->input) {
+		int ret = drm_dp_port_set_pdt(port,
+					      conn_stat->peer_device_type);
+		if (ret == 1) {
 			dowork = true;
+		} else if (ret < 0) {
+			DRM_ERROR("Failed to change PDT for port %p: %d\n",
+				  port, ret);
+			dowork = false;
+		}
 	}
 
 	drm_dp_mst_topology_put_port(port);
@@ -4003,9 +4036,7 @@ drm_dp_delayed_destroy_port(struct drm_dp_mst_port *port)
 	if (port->connector)
 		port->mgr->cbs->destroy_connector(port->mgr, port->connector);
 
-	drm_dp_port_teardown_pdt(port, port->pdt);
-	port->pdt = DP_PEER_DEVICE_NONE;
-
+	drm_dp_port_set_pdt(port, DP_PEER_DEVICE_NONE);
 	drm_dp_mst_put_port_malloc(port);
 }
 
diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
index 5423a8adda78..f253ee43e9d9 100644
--- a/include/drm/drm_dp_mst_helper.h
+++ b/include/drm/drm_dp_mst_helper.h
@@ -55,8 +55,10 @@ struct drm_dp_vcpi {
  * @num_sdp_stream_sinks: Number of stream sinks
  * @available_pbn: Available bandwidth for this port.
  * @next: link to next port on this branch device
- * @mstb: branch device attach below this port
- * @aux: i2c aux transport to talk to device connected to this port.
+ * @mstb: branch device on this port, protected by
+ * &drm_dp_mst_topology_mgr.lock
+ * @aux: i2c aux transport to talk to device connected to this port, protected
+ * by &drm_dp_mst_topology_mgr.lock
  * @parent: branch device parent of this port
  * @vcpi: Virtual Channel Payload info for this port.
  * @connector: DRM connector this port is connected to.
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 17/27] drm/dp_mst: Rename drm_dp_add_port and drm_dp_update_port
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (15 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 16/27] drm/dp_mst: Refactor pdt setup/teardown, add more locking Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-25 19:30   ` Sean Paul
  2019-09-03 20:45 ` [PATCH v2 18/27] drm/dp_mst: Remove lies in {up,down}_rep_recv documentation Lyude Paul
                   ` (9 subsequent siblings)
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

The names for these functions are rather confusing. drm_dp_add_port()
sounds like a function that would simply create a port and add it to a
topology, and do nothing more. Similarly, drm_dp_update_port() would be
assumed to be the function that should be used to update port
information after initial creation.

While those assumptions are currently correct in how these functions are
used, a quick glance at drm_dp_add_port() reveals that drm_dp_add_port()
can also update the information on a port, and seems explicitly designed
to do so. This can be explained pretty simply by the fact that there's
more situations that would involve updating the port information based
on a link address response as opposed to a connection status
notification than the driver's initial topology probe. Case in point:
reprobing link addresses after suspend/resume.

Since we're about to start using drm_dp_add_port() differently for
suspend/resume reprobing, let's rename both functions to clarify what
they actually do.

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 9944ef2ce885..cfaf9eb7ace9 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -1900,9 +1900,10 @@ void drm_dp_mst_connector_early_unregister(struct drm_connector *connector,
 }
 EXPORT_SYMBOL(drm_dp_mst_connector_early_unregister);
 
-static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
-			    struct drm_device *dev,
-			    struct drm_dp_link_addr_reply_port *port_msg)
+static void
+drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
+				    struct drm_device *dev,
+				    struct drm_dp_link_addr_reply_port *port_msg)
 {
 	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
 	struct drm_dp_mst_port *port;
@@ -2011,8 +2012,9 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
 	drm_dp_mst_topology_put_port(port);
 }
 
-static void drm_dp_update_port(struct drm_dp_mst_branch *mstb,
-			       struct drm_dp_connection_status_notify *conn_stat)
+static void
+drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
+			    struct drm_dp_connection_status_notify *conn_stat)
 {
 	struct drm_dp_mst_port *port;
 	int old_ddps;
@@ -2464,7 +2466,8 @@ static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
 	drm_dp_check_mstb_guid(mstb, reply->guid);
 
 	for (i = 0; i < reply->nports; i++)
-		drm_dp_add_port(mstb, mgr->dev, &reply->ports[i]);
+		drm_dp_mst_handle_link_address_port(mstb, mgr->dev,
+						    &reply->ports[i]);
 
 	drm_kms_helper_hotplug_event(mgr->dev);
 
@@ -3324,7 +3327,7 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
 	}
 
 	if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
-		drm_dp_update_port(mstb, &msg.u.conn_stat);
+		drm_dp_mst_handle_conn_stat(mstb, &msg.u.conn_stat);
 
 		DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n",
 			      msg.u.conn_stat.port_number,
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 18/27] drm/dp_mst: Remove lies in {up,down}_rep_recv documentation
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (16 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 17/27] drm/dp_mst: Rename drm_dp_add_port and drm_dp_update_port Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-25 19:32   ` Sean Paul
  2019-09-03 20:45 ` [PATCH v2 19/27] drm/dp_mst: Handle UP requests asynchronously Lyude Paul
                   ` (8 subsequent siblings)
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

These are most certainly accessed from far more than the mgr work. In
fact, up_req_recv is -only- ever accessed from outside the mgr work.

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 include/drm/drm_dp_mst_helper.h | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
index f253ee43e9d9..8ba2a01324bb 100644
--- a/include/drm/drm_dp_mst_helper.h
+++ b/include/drm/drm_dp_mst_helper.h
@@ -489,15 +489,11 @@ struct drm_dp_mst_topology_mgr {
 	int conn_base_id;
 
 	/**
-	 * @down_rep_recv: Message receiver state for down replies. This and
-	 * @up_req_recv are only ever access from the work item, which is
-	 * serialised.
+	 * @down_rep_recv: Message receiver state for down replies.
 	 */
 	struct drm_dp_sideband_msg_rx down_rep_recv;
 	/**
-	 * @up_req_recv: Message receiver state for up requests. This and
-	 * @down_rep_recv are only ever access from the work item, which is
-	 * serialised.
+	 * @up_req_recv: Message receiver state for up requests.
 	 */
 	struct drm_dp_sideband_msg_rx up_req_recv;
 
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 19/27] drm/dp_mst: Handle UP requests asynchronously
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (17 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 18/27] drm/dp_mst: Remove lies in {up,down}_rep_recv documentation Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-25 19:46   ` Sean Paul
  2019-09-03 20:45 ` [PATCH v2 20/27] drm/dp_mst: Protect drm_dp_mst_port members with connection_mutex Lyude Paul
                   ` (7 subsequent siblings)
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

Once upon a time, hotplugging devices on MST branches actually worked in
DRM. Now, it only works in amdgpu (likely because of how it's hotplug
handlers are implemented). On both i915 and nouveau, hotplug
notifications from MST branches are noticed - but trying to respond to
them causes messaging timeouts and causes the whole topology state to go
out of sync with reality, usually resulting in the user needing to
replug the entire topology in hopes that it actually fixes things.

The reason for this is because the way we currently handle UP requests
in MST is completely bogus. drm_dp_mst_handle_up_req() is called from
drm_dp_mst_hpd_irq(), which is usually called from the driver's hotplug
handler. Because we handle sending the hotplug event from this function,
we actually cause the driver's hotplug handler (and in turn, all
sideband transactions) to block on
drm_device->mode_config.connection_mutex. This makes it impossible to
send any sideband messages from the driver's connector probing
functions, resulting in the aforementioned sideband message timeout.

There's even more problems with this beyond breaking hotplugging on MST
branch devices. It also makes it almost impossible to protect
drm_dp_mst_port struct members under a lock because we then have to
worry about dealing with all of the lock dependency issues that ensue.

So, let's finally actually fix this issue by handling the processing of
up requests asyncronously. This way we can send sideband messages from
most contexts without having to deal with getting blocked if we hold
connection_mutex. This also fixes MST branch device hotplugging on i915,
finally!

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 146 +++++++++++++++++++-------
 include/drm/drm_dp_mst_helper.h       |  16 +++
 2 files changed, 122 insertions(+), 40 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index cfaf9eb7ace9..5101eeab4485 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -46,6 +46,12 @@
  * protocol. The helpers contain a topology manager and bandwidth manager.
  * The helpers encapsulate the sending and received of sideband msgs.
  */
+struct drm_dp_pending_up_req {
+	struct drm_dp_sideband_msg_hdr hdr;
+	struct drm_dp_sideband_msg_req_body msg;
+	struct list_head next;
+};
+
 static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
 				  char *buf);
 
@@ -3109,6 +3115,7 @@ void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr)
 	drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
 			   DP_MST_EN | DP_UPSTREAM_IS_SRC);
 	mutex_unlock(&mgr->lock);
+	flush_work(&mgr->up_req_work);
 	flush_work(&mgr->work);
 	flush_work(&mgr->delayed_destroy_work);
 }
@@ -3281,12 +3288,70 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
 	return 0;
 }
 
+static inline void
+drm_dp_mst_process_up_req(struct drm_dp_mst_topology_mgr *mgr,
+			  struct drm_dp_pending_up_req *up_req)
+{
+	struct drm_dp_mst_branch *mstb = NULL;
+	struct drm_dp_sideband_msg_req_body *msg = &up_req->msg;
+	struct drm_dp_sideband_msg_hdr *hdr = &up_req->hdr;
+
+	if (hdr->broadcast) {
+		const u8 *guid = NULL;
+
+		if (msg->req_type == DP_CONNECTION_STATUS_NOTIFY)
+			guid = msg->u.conn_stat.guid;
+		else if (msg->req_type == DP_RESOURCE_STATUS_NOTIFY)
+			guid = msg->u.resource_stat.guid;
+
+		mstb = drm_dp_get_mst_branch_device_by_guid(mgr, guid);
+	} else {
+		mstb = drm_dp_get_mst_branch_device(mgr, hdr->lct, hdr->rad);
+	}
+
+	if (!mstb) {
+		DRM_DEBUG_KMS("Got MST reply from unknown device %d\n",
+			      hdr->lct);
+		return;
+	}
+
+	/* TODO: Add missing handler for DP_RESOURCE_STATUS_NOTIFY events */
+	if (msg->req_type == DP_CONNECTION_STATUS_NOTIFY) {
+		drm_dp_mst_handle_conn_stat(mstb, &msg->u.conn_stat);
+		drm_kms_helper_hotplug_event(mgr->dev);
+	}
+
+	drm_dp_mst_topology_put_mstb(mstb);
+}
+
+static void drm_dp_mst_up_req_work(struct work_struct *work)
+{
+	struct drm_dp_mst_topology_mgr *mgr =
+		container_of(work, struct drm_dp_mst_topology_mgr,
+			     up_req_work);
+	struct drm_dp_pending_up_req *up_req;
+
+	while (true) {
+		mutex_lock(&mgr->up_req_lock);
+		up_req = list_first_entry_or_null(&mgr->up_req_list,
+						  struct drm_dp_pending_up_req,
+						  next);
+		if (up_req)
+			list_del(&up_req->next);
+		mutex_unlock(&mgr->up_req_lock);
+
+		if (!up_req)
+			break;
+
+		drm_dp_mst_process_up_req(mgr, up_req);
+		kfree(up_req);
+	}
+}
+
 static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
 {
-	struct drm_dp_sideband_msg_req_body msg;
 	struct drm_dp_sideband_msg_hdr *hdr = &mgr->up_req_recv.initial_hdr;
-	struct drm_dp_mst_branch *mstb = NULL;
-	const u8 *guid;
+	struct drm_dp_pending_up_req *up_req;
 	bool seqno;
 
 	if (!drm_dp_get_one_sb_msg(mgr, true))
@@ -3295,56 +3360,53 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
 	if (!mgr->up_req_recv.have_eomt)
 		return 0;
 
-	if (!hdr->broadcast) {
-		mstb = drm_dp_get_mst_branch_device(mgr, hdr->lct, hdr->rad);
-		if (!mstb) {
-			DRM_DEBUG_KMS("Got MST reply from unknown device %d\n",
-				      hdr->lct);
-			goto out;
-		}
+	up_req = kzalloc(sizeof(*up_req), GFP_KERNEL);
+	if (!up_req) {
+		DRM_ERROR("Not enough memory to process MST up req\n");
+		return -ENOMEM;
 	}
+	INIT_LIST_HEAD(&up_req->next);
 
 	seqno = hdr->seqno;
-	drm_dp_sideband_parse_req(&mgr->up_req_recv, &msg);
+	drm_dp_sideband_parse_req(&mgr->up_req_recv, &up_req->msg);
 
-	if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY)
-		guid = msg.u.conn_stat.guid;
-	else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY)
-		guid = msg.u.resource_stat.guid;
-	else
+	if (up_req->msg.req_type != DP_CONNECTION_STATUS_NOTIFY &&
+	    up_req->msg.req_type != DP_RESOURCE_STATUS_NOTIFY) {
+		DRM_DEBUG_KMS("Received unknown up req type, ignoring: %x\n",
+			      up_req->msg.req_type);
+		kfree(up_req);
 		goto out;
-
-	drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno,
-				 false);
-
-	if (!mstb) {
-		mstb = drm_dp_get_mst_branch_device_by_guid(mgr, guid);
-		if (!mstb) {
-			DRM_DEBUG_KMS("Got MST reply from unknown device %d\n",
-				      hdr->lct);
-			goto out;
-		}
 	}
 
-	if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
-		drm_dp_mst_handle_conn_stat(mstb, &msg.u.conn_stat);
+	drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, up_req->msg.req_type,
+				 seqno, false);
+
+	if (up_req->msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
+		const struct drm_dp_connection_status_notify *conn_stat =
+			&up_req->msg.u.conn_stat;
 
 		DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n",
-			      msg.u.conn_stat.port_number,
-			      msg.u.conn_stat.legacy_device_plug_status,
-			      msg.u.conn_stat.displayport_device_plug_status,
-			      msg.u.conn_stat.message_capability_status,
-			      msg.u.conn_stat.input_port,
-			      msg.u.conn_stat.peer_device_type);
+			      conn_stat->port_number,
+			      conn_stat->legacy_device_plug_status,
+			      conn_stat->displayport_device_plug_status,
+			      conn_stat->message_capability_status,
+			      conn_stat->input_port,
+			      conn_stat->peer_device_type);
+	} else if (up_req->msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
+		const struct drm_dp_resource_status_notify *res_stat =
+			&up_req->msg.u.resource_stat;
 
-		drm_kms_helper_hotplug_event(mgr->dev);
-	} else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
 		DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n",
-			      msg.u.resource_stat.port_number,
-			      msg.u.resource_stat.available_pbn);
+			      res_stat->port_number,
+			      res_stat->available_pbn);
 	}
 
-	drm_dp_mst_topology_put_mstb(mstb);
+	up_req->hdr = *hdr;
+	mutex_lock(&mgr->up_req_lock);
+	list_add_tail(&up_req->next, &mgr->up_req_list);
+	mutex_unlock(&mgr->up_req_lock);
+	queue_work(system_long_wq, &mgr->up_req_work);
+
 out:
 	memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
 	return 0;
@@ -4320,12 +4382,15 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
 	mutex_init(&mgr->qlock);
 	mutex_init(&mgr->payload_lock);
 	mutex_init(&mgr->delayed_destroy_lock);
+	mutex_init(&mgr->up_req_lock);
 	INIT_LIST_HEAD(&mgr->tx_msg_downq);
 	INIT_LIST_HEAD(&mgr->destroy_port_list);
 	INIT_LIST_HEAD(&mgr->destroy_branch_device_list);
+	INIT_LIST_HEAD(&mgr->up_req_list);
 	INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work);
 	INIT_WORK(&mgr->tx_work, drm_dp_tx_work);
 	INIT_WORK(&mgr->delayed_destroy_work, drm_dp_delayed_destroy_work);
+	INIT_WORK(&mgr->up_req_work, drm_dp_mst_up_req_work);
 	init_waitqueue_head(&mgr->tx_waitq);
 	mgr->dev = dev;
 	mgr->aux = aux;
@@ -4382,6 +4447,7 @@ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
 	mutex_destroy(&mgr->payload_lock);
 	mutex_destroy(&mgr->qlock);
 	mutex_destroy(&mgr->lock);
+	mutex_destroy(&mgr->up_req_lock);
 }
 EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);
 
diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
index 8ba2a01324bb..7d80c38ee00e 100644
--- a/include/drm/drm_dp_mst_helper.h
+++ b/include/drm/drm_dp_mst_helper.h
@@ -597,6 +597,22 @@ struct drm_dp_mst_topology_mgr {
 	 * devices, needed to avoid locking inversion.
 	 */
 	struct work_struct delayed_destroy_work;
+
+	/**
+	 * @up_req_list: List of pending up requests from the topology that
+	 * need to be processed, in chronological order.
+	 */
+	struct list_head up_req_list;
+	/**
+	 * @up_req_lock: Protects @up_req_list
+	 */
+	struct mutex up_req_lock;
+	/**
+	 * @up_req_work: Work item to process up requests received from the
+	 * topology. Needed to avoid blocking hotplug handling and sideband
+	 * transmissions.
+	 */
+	struct work_struct up_req_work;
 };
 
 int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 20/27] drm/dp_mst: Protect drm_dp_mst_port members with connection_mutex
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (18 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 19/27] drm/dp_mst: Handle UP requests asynchronously Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-25 20:00   ` Sean Paul
  2019-09-03 20:45 ` [PATCH v2 21/27] drm/dp_mst: Don't forget to update port->input in drm_dp_mst_handle_conn_stat() Lyude Paul
                   ` (6 subsequent siblings)
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

Yes-you read that right. Currently there is literally no locking in
place for any of the drm_dp_mst_port struct members that can be modified
in response to a link address response, or a connection status response.
Which literally means if we're unlucky enough to have any sort of
hotplugging event happen before we're finished with reprobing link
addresses, we'll race and the contents of said struct members becomes
undefined. Fun!

So, finally add some simple locking protections to our MST helpers by
protecting any drm_dp_mst_port members which can be changed by link
address responses or connection status notifications under
drm_device->mode_config.connection_mutex.

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 144 +++++++++++++++++++-------
 include/drm/drm_dp_mst_helper.h       |  39 +++++--
 2 files changed, 133 insertions(+), 50 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 5101eeab4485..259634c5d6dc 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -1354,6 +1354,7 @@ static void drm_dp_free_mst_port(struct kref *kref)
 		container_of(kref, struct drm_dp_mst_port, malloc_kref);
 
 	drm_dp_mst_put_mstb_malloc(port->parent);
+	mutex_destroy(&port->lock);
 	kfree(port);
 }
 
@@ -1906,6 +1907,36 @@ void drm_dp_mst_connector_early_unregister(struct drm_connector *connector,
 }
 EXPORT_SYMBOL(drm_dp_mst_connector_early_unregister);
 
+static void
+drm_dp_mst_port_add_connector(struct drm_dp_mst_branch *mstb,
+			      struct drm_dp_mst_port *port)
+{
+	struct drm_dp_mst_topology_mgr *mgr = port->mgr;
+	char proppath[255];
+	int ret;
+
+	build_mst_prop_path(mstb, port->port_num, proppath, sizeof(proppath));
+	port->connector = mgr->cbs->add_connector(mgr, port, proppath);
+	if (!port->connector) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	if ((port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
+	     port->pdt == DP_PEER_DEVICE_SST_SINK) &&
+	    port->port_num >= DP_MST_LOGICAL_PORT_0) {
+		port->cached_edid = drm_get_edid(port->connector,
+						 &port->aux.ddc);
+		drm_connector_set_tile_property(port->connector);
+	}
+
+	mgr->cbs->register_connector(port->connector);
+	return;
+
+error:
+	DRM_ERROR("Failed to create connector for port %p: %d\n", port, ret);
+}
+
 static void
 drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
 				    struct drm_device *dev,
@@ -1913,8 +1944,12 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
 {
 	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
 	struct drm_dp_mst_port *port;
-	bool created = false;
-	int old_ddps = 0;
+	struct drm_dp_mst_branch *child_mstb = NULL;
+	struct drm_connector *connector_to_destroy = NULL;
+	int old_ddps = 0, ret;
+	u8 new_pdt = DP_PEER_DEVICE_NONE;
+	bool created = false, send_link_addr = false,
+	     create_connector = false;
 
 	port = drm_dp_get_port(mstb, port_msg->port_number);
 	if (!port) {
@@ -1923,6 +1958,7 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
 			return;
 		kref_init(&port->topology_kref);
 		kref_init(&port->malloc_kref);
+		mutex_init(&port->lock);
 		port->parent = mstb;
 		port->port_num = port_msg->port_number;
 		port->mgr = mgr;
@@ -1937,11 +1973,17 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
 		drm_dp_mst_get_mstb_malloc(mstb);
 
 		created = true;
-	} else {
-		old_ddps = port->ddps;
 	}
 
+	mutex_lock(&port->lock);
+	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+
+	if (!created)
+		old_ddps = port->ddps;
+
 	port->input = port_msg->input_port;
+	if (!port->input)
+		new_pdt = port_msg->peer_device_type;
 	port->mcs = port_msg->mcs;
 	port->ddps = port_msg->ddps;
 	port->ldps = port_msg->legacy_device_plug_status;
@@ -1969,44 +2011,58 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
 		}
 	}
 
-	if (!port->input) {
-		int ret = drm_dp_port_set_pdt(port,
-					      port_msg->peer_device_type);
-		if (ret == 1) {
-			drm_dp_send_link_address(mgr, port->mstb);
-		} else if (ret < 0) {
-			DRM_ERROR("Failed to change PDT on port %p: %d\n",
-				  port, ret);
-			goto fail;
+	ret = drm_dp_port_set_pdt(port, new_pdt);
+	if (ret == 1) {
+		send_link_addr = true;
+	} else if (ret < 0) {
+		DRM_ERROR("Failed to change PDT on port %p: %d\n",
+			  port, ret);
+		goto fail_unlock;
+	}
+
+	if (send_link_addr) {
+		mutex_lock(&mgr->lock);
+		if (port->mstb) {
+			child_mstb = port->mstb;
+			drm_dp_mst_get_mstb_malloc(child_mstb);
 		}
+		mutex_unlock(&mgr->lock);
 	}
 
-	if (created && !port->input) {
-		char proppath[255];
+	/*
+	 * We unset port->connector before dropping connection_mutex so that
+	 * there's no chance any of the atomic MST helpers can accidentally
+	 * associate a to-be-destroyed connector with a port.
+	 */
+	if (port->connector && port->input) {
+		connector_to_destroy = port->connector;
+		port->connector = NULL;
+	} else if (!port->connector && !port->input) {
+		create_connector = true;
+	}
 
-		build_mst_prop_path(mstb, port->port_num, proppath,
-				    sizeof(proppath));
-		port->connector = (*mgr->cbs->add_connector)(mgr, port,
-							     proppath);
-		if (!port->connector)
-			goto fail;
+	drm_modeset_unlock(&dev->mode_config.connection_mutex);
 
-		if ((port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
-		     port->pdt == DP_PEER_DEVICE_SST_SINK) &&
-		    port->port_num >= DP_MST_LOGICAL_PORT_0) {
-			port->cached_edid = drm_get_edid(port->connector,
-							 &port->aux.ddc);
-			drm_connector_set_tile_property(port->connector);
-		}
+	if (connector_to_destroy)
+		mgr->cbs->destroy_connector(mgr, connector_to_destroy);
+	else if (create_connector)
+		drm_dp_mst_port_add_connector(mstb, port);
+
+	mutex_unlock(&port->lock);
 
-		(*mgr->cbs->register_connector)(port->connector);
+	if (send_link_addr && child_mstb) {
+		drm_dp_send_link_address(mgr, child_mstb);
+		drm_dp_mst_put_mstb_malloc(child_mstb);
 	}
 
 	/* put reference to this port */
 	drm_dp_mst_topology_put_port(port);
 	return;
 
-fail:
+fail_unlock:
+	drm_modeset_unlock(&dev->mode_config.connection_mutex);
+	mutex_unlock(&port->lock);
+
 	/* Remove it from the port list */
 	mutex_lock(&mgr->lock);
 	list_del(&port->next);
@@ -2022,6 +2078,7 @@ static void
 drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
 			    struct drm_dp_connection_status_notify *conn_stat)
 {
+	struct drm_device *dev = mstb->mgr->dev;
 	struct drm_dp_mst_port *port;
 	int old_ddps;
 	bool dowork = false;
@@ -2030,6 +2087,8 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
 	if (!port)
 		return;
 
+	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+
 	old_ddps = port->ddps;
 	port->mcs = conn_stat->message_capability_status;
 	port->ldps = conn_stat->legacy_device_plug_status;
@@ -2055,6 +2114,7 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
 		}
 	}
 
+	drm_modeset_unlock(&dev->mode_config.connection_mutex);
 	drm_dp_mst_topology_put_port(port);
 	if (dowork)
 		queue_work(system_long_wq, &mstb->mgr->work);
@@ -2147,28 +2207,34 @@ drm_dp_get_mst_branch_device_by_guid(struct drm_dp_mst_topology_mgr *mgr,
 static void drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
 					       struct drm_dp_mst_branch *mstb)
 {
+	struct drm_device *dev = mgr->dev;
 	struct drm_dp_mst_port *port;
-	struct drm_dp_mst_branch *mstb_child;
+
 	if (!mstb->link_address_sent)
 		drm_dp_send_link_address(mgr, mstb);
 
 	list_for_each_entry(port, &mstb->ports, next) {
-		if (port->input)
-			continue;
+		struct drm_dp_mst_branch *mstb_child = NULL;
+
+		drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
 
-		if (!port->ddps)
+		if (port->input || !port->ddps) {
+			drm_modeset_unlock(&dev->mode_config.connection_mutex);
 			continue;
+		}
 
 		if (!port->available_pbn)
 			drm_dp_send_enum_path_resources(mgr, mstb, port);
 
-		if (port->mstb) {
+		if (port->mstb)
 			mstb_child = drm_dp_mst_topology_get_mstb_validated(
 			    mgr, port->mstb);
-			if (mstb_child) {
-				drm_dp_check_and_send_link_address(mgr, mstb_child);
-				drm_dp_mst_topology_put_mstb(mstb_child);
-			}
+
+		drm_modeset_unlock(&dev->mode_config.connection_mutex);
+
+		if (mstb_child) {
+			drm_dp_check_and_send_link_address(mgr, mstb_child);
+			drm_dp_mst_topology_put_mstb(mstb_child);
 		}
 	}
 }
diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
index 7d80c38ee00e..1efbb086f7ac 100644
--- a/include/drm/drm_dp_mst_helper.h
+++ b/include/drm/drm_dp_mst_helper.h
@@ -45,23 +45,34 @@ struct drm_dp_vcpi {
 /**
  * struct drm_dp_mst_port - MST port
  * @port_num: port number
- * @input: if this port is an input port.
- * @mcs: message capability status - DP 1.2 spec.
- * @ddps: DisplayPort Device Plug Status - DP 1.2
- * @pdt: Peer Device Type
- * @ldps: Legacy Device Plug Status
- * @dpcd_rev: DPCD revision of device on this port
- * @num_sdp_streams: Number of simultaneous streams
- * @num_sdp_stream_sinks: Number of stream sinks
- * @available_pbn: Available bandwidth for this port.
+ * @input: if this port is an input port. Protected by
+ * &drm_device.mode_config.connection_mutex.
+ * @mcs: message capability status - DP 1.2 spec. Protected by
+ * &drm_device.mode_config.connection_mutex.
+ * @ddps: DisplayPort Device Plug Status - DP 1.2. Protected by
+ * &drm_device.mode_config.connection_mutex.
+ * @pdt: Peer Device Type. Protected by
+ * &drm_device.mode_config.connection_mutex.
+ * @ldps: Legacy Device Plug Status. Protected by
+ * &drm_device.mode_config.connection_mutex.
+ * @dpcd_rev: DPCD revision of device on this port. Protected by
+ * &drm_device.mode_config.connection_mutex.
+ * @num_sdp_streams: Number of simultaneous streams. Protected by
+ * &drm_device.mode_config.connection_mutex.
+ * @num_sdp_stream_sinks: Number of stream sinks. Protected by
+ * &drm_device.mode_config.connection_mutex.
+ * @available_pbn: Available bandwidth for this port. Protected by
+ * &drm_device.mode_config.connection_mutex.
  * @next: link to next port on this branch device
  * @mstb: branch device on this port, protected by
  * &drm_dp_mst_topology_mgr.lock
  * @aux: i2c aux transport to talk to device connected to this port, protected
- * by &drm_dp_mst_topology_mgr.lock
+ * by &drm_device.mode_config.connection_mutex.
  * @parent: branch device parent of this port
  * @vcpi: Virtual Channel Payload info for this port.
- * @connector: DRM connector this port is connected to.
+ * @connector: DRM connector this port is connected to. Protected by @lock.
+ * When there is already a connector registered for this port, this is also
+ * protected by &drm_device.mode_config.connection_mutex.
  * @mgr: topology manager this port lives under.
  *
  * This structure represents an MST port endpoint on a device somewhere
@@ -100,6 +111,12 @@ struct drm_dp_mst_port {
 	struct drm_connector *connector;
 	struct drm_dp_mst_topology_mgr *mgr;
 
+	/**
+	 * @lock: Protects @connector. If needed, this lock should be grabbed
+	 * before &drm_device.mode_config.connection_mutex.
+	 */
+	struct mutex lock;
+
 	/**
 	 * @cached_edid: for DP logical ports - make tiling work by ensuring
 	 * that the EDID for all connectors is read immediately.
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 21/27] drm/dp_mst: Don't forget to update port->input in drm_dp_mst_handle_conn_stat()
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (19 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 20/27] drm/dp_mst: Protect drm_dp_mst_port members with connection_mutex Lyude Paul
@ 2019-09-03 20:45 ` Lyude Paul
  2019-09-25 20:03   ` Sean Paul
  2019-09-03 20:46 ` [PATCH v2 22/27] drm/nouveau: Don't grab runtime PM refs for HPD IRQs Lyude Paul
                   ` (5 subsequent siblings)
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:45 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

This probably hasn't caused any problems up until now since it's
probably nearly impossible to encounter this in the wild, however if we
were to receive a connection status notification from the MST hub after
resume while we're in the middle of reprobing the link addresses for a
topology then there's a much larger chance that a port could have
changed from being an output port to input port (or vice versa). If we
forget to update this bit of information, we'll potentially ignore a
valid PDT change on a downstream port because we think it's an input
port.

So, make sure we read the input_port field in connection status
notifications in drm_dp_mst_handle_conn_stat() to prevent this from
happening once we've implemented suspend/resume reprobing.

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 51 +++++++++++++++++++--------
 1 file changed, 37 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 259634c5d6dc..e407aba1fbd2 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -2078,18 +2078,23 @@ static void
 drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
 			    struct drm_dp_connection_status_notify *conn_stat)
 {
-	struct drm_device *dev = mstb->mgr->dev;
+	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
+	struct drm_device *dev = mgr->dev;
 	struct drm_dp_mst_port *port;
-	int old_ddps;
-	bool dowork = false;
+	struct drm_connector *connector_to_destroy = NULL;
+	int old_ddps, ret;
+	u8 new_pdt;
+	bool dowork = false, create_connector = false;
 
 	port = drm_dp_get_port(mstb, conn_stat->port_number);
 	if (!port)
 		return;
 
+	mutex_lock(&port->lock);
 	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
 
 	old_ddps = port->ddps;
+	port->input = conn_stat->input_port;
 	port->mcs = conn_stat->message_capability_status;
 	port->ldps = conn_stat->legacy_device_plug_status;
 	port->ddps = conn_stat->displayport_device_plug_status;
@@ -2102,23 +2107,41 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
 		}
 	}
 
-	if (!port->input) {
-		int ret = drm_dp_port_set_pdt(port,
-					      conn_stat->peer_device_type);
-		if (ret == 1) {
-			dowork = true;
-		} else if (ret < 0) {
-			DRM_ERROR("Failed to change PDT for port %p: %d\n",
-				  port, ret);
-			dowork = false;
-		}
+	new_pdt = port->input ? DP_PEER_DEVICE_NONE : conn_stat->peer_device_type;
+
+	ret = drm_dp_port_set_pdt(port, new_pdt);
+	if (ret == 1) {
+		dowork = true;
+	} else if (ret < 0) {
+		DRM_ERROR("Failed to change PDT for port %p: %d\n",
+			  port, ret);
+		dowork = false;
+	}
+
+	/*
+	 * We unset port->connector before dropping connection_mutex so that
+	 * there's no chance any of the atomic MST helpers can accidentally
+	 * associate a to-be-destroyed connector with a port.
+	 */
+	if (port->connector && port->input) {
+		connector_to_destroy = port->connector;
+		port->connector = NULL;
+	} else if (!port->connector && !port->input) {
+		create_connector = true;
 	}
 
 	drm_modeset_unlock(&dev->mode_config.connection_mutex);
+
+	if (connector_to_destroy)
+		mgr->cbs->destroy_connector(mgr, connector_to_destroy);
+	else if (create_connector)
+		drm_dp_mst_port_add_connector(mstb, port);
+
+	mutex_unlock(&port->lock);
+
 	drm_dp_mst_topology_put_port(port);
 	if (dowork)
 		queue_work(system_long_wq, &mstb->mgr->work);
-
 }
 
 static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_topology_mgr *mgr,
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 22/27] drm/nouveau: Don't grab runtime PM refs for HPD IRQs
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (20 preceding siblings ...)
  2019-09-03 20:45 ` [PATCH v2 21/27] drm/dp_mst: Don't forget to update port->input in drm_dp_mst_handle_conn_stat() Lyude Paul
@ 2019-09-03 20:46 ` Lyude Paul
  2019-09-25 20:06   ` Sean Paul
  2019-09-03 20:46 ` [PATCH v2 23/27] drm/amdgpu: Iterate through DRM connectors correctly Lyude Paul
                   ` (4 subsequent siblings)
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:46 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Ben Skeggs, David Airlie,
	Daniel Vetter, linux-kernel

In order for suspend/resume reprobing to work, we need to be able to
perform sideband communications during suspend/resume, along with
runtime PM suspend/resume. In order to do so, we also need to make sure
that nouveau doesn't bother grabbing a runtime PM reference to do so,
since otherwise we'll start deadlocking runtime PM again.

Note that we weren't able to do this before, because of the DP MST
helpers processing UP requests from topologies in the same context as
drm_dp_mst_hpd_irq() which would have caused us to open ourselves up to
receiving hotplug events and deadlocking with runtime suspend/resume.
Now that those requests are handled asynchronously, this change should
be completely safe.

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/nouveau/nouveau_connector.c | 33 +++++++++++----------
 1 file changed, 17 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 56871d34e3fb..f276918d3f3b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -1131,6 +1131,16 @@ nouveau_connector_hotplug(struct nvif_notify *notify)
 	const char *name = connector->name;
 	struct nouveau_encoder *nv_encoder;
 	int ret;
+	bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG);
+
+	if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) {
+		NV_DEBUG(drm, "service %s\n", name);
+		drm_dp_cec_irq(&nv_connector->aux);
+		if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP)))
+			nv50_mstm_service(nv_encoder->dp.mstm);
+
+		return NVIF_NOTIFY_KEEP;
+	}
 
 	ret = pm_runtime_get(drm->dev->dev);
 	if (ret == 0) {
@@ -1151,25 +1161,16 @@ nouveau_connector_hotplug(struct nvif_notify *notify)
 		return NVIF_NOTIFY_DROP;
 	}
 
-	if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) {
-		NV_DEBUG(drm, "service %s\n", name);
-		drm_dp_cec_irq(&nv_connector->aux);
-		if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP)))
-			nv50_mstm_service(nv_encoder->dp.mstm);
-	} else {
-		bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG);
-
+	if (!plugged)
+		drm_dp_cec_unset_edid(&nv_connector->aux);
+	NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name);
+	if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP))) {
 		if (!plugged)
-			drm_dp_cec_unset_edid(&nv_connector->aux);
-		NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name);
-		if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP))) {
-			if (!plugged)
-				nv50_mstm_remove(nv_encoder->dp.mstm);
-		}
-
-		drm_helper_hpd_irq_event(connector->dev);
+			nv50_mstm_remove(nv_encoder->dp.mstm);
 	}
 
+	drm_helper_hpd_irq_event(connector->dev);
+
 	pm_runtime_mark_last_busy(drm->dev->dev);
 	pm_runtime_put_autosuspend(drm->dev->dev);
 	return NVIF_NOTIFY_KEEP;
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 23/27] drm/amdgpu: Iterate through DRM connectors correctly
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (21 preceding siblings ...)
  2019-09-03 20:46 ` [PATCH v2 22/27] drm/nouveau: Don't grab runtime PM refs for HPD IRQs Lyude Paul
@ 2019-09-03 20:46 ` Lyude Paul
  2019-09-13 20:45   ` Alex Deucher
  2019-09-03 20:46 ` [PATCH v2 24/27] drm/amdgpu/dm: Resume short HPD IRQs before resuming MST topology Lyude Paul
                   ` (3 subsequent siblings)
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:46 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Alex Deucher,
	Christian König, David (ChunMing) Zhou, David Airlie,
	Daniel Vetter, Harry Wentland, Leo Li, Sam Ravnborg,
	Neil Armstrong, Andrzej Pietrasiewicz, Hawking Zhang, Huang Rui,
	Andrey Grodzovsky, xinhui pan, Emily Deng, Rex Zhu, Tao Zhou,
	Evan Quan, Michel Dänzer, Nicholas Kazlauskas, Yu Zhao,
	Shirish S, David Francis, Felix Kuehling, Thierry Reding,
	Jani Nikula, Colin Ian King, Russell King, Mario Kleiner,
	Bhawanpreet Lakha, Markus Elfring, linux-kernel

Currently, every single piece of code in amdgpu that loops through
connectors does it incorrectly and doesn't use the proper list iteration
helpers, drm_connector_list_iter_begin() and
drm_connector_list_iter_end(). Yeesh.

So, do that.

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 .../gpu/drm/amd/amdgpu/amdgpu_connectors.c    | 13 +++++-
 drivers/gpu/drm/amd/amdgpu/amdgpu_device.c    | 20 +++++++---
 drivers/gpu/drm/amd/amdgpu/amdgpu_display.c   |  5 ++-
 drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c  | 40 +++++++++++++------
 drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c       |  5 ++-
 drivers/gpu/drm/amd/amdgpu/dce_v10_0.c        | 34 ++++++++++++----
 drivers/gpu/drm/amd/amdgpu/dce_v11_0.c        | 34 ++++++++++++----
 drivers/gpu/drm/amd/amdgpu/dce_v6_0.c         | 40 ++++++++++++++-----
 drivers/gpu/drm/amd/amdgpu/dce_v8_0.c         | 34 ++++++++++++----
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 33 ++++++++-------
 .../drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c | 10 ++++-
 11 files changed, 195 insertions(+), 73 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
index ece55c8fa673..bd31bb595c04 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
@@ -1022,8 +1022,12 @@ amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force)
 			 */
 			if (amdgpu_connector->shared_ddc && (ret == connector_status_connected)) {
 				struct drm_connector *list_connector;
+				struct drm_connector_list_iter iter;
 				struct amdgpu_connector *list_amdgpu_connector;
-				list_for_each_entry(list_connector, &dev->mode_config.connector_list, head) {
+
+				drm_connector_list_iter_begin(dev, &iter);
+				drm_for_each_connector_iter(list_connector,
+							    &iter) {
 					if (connector == list_connector)
 						continue;
 					list_amdgpu_connector = to_amdgpu_connector(list_connector);
@@ -1040,6 +1044,7 @@ amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force)
 						}
 					}
 				}
+				drm_connector_list_iter_end(&iter);
 			}
 		}
 	}
@@ -1501,6 +1506,7 @@ amdgpu_connector_add(struct amdgpu_device *adev,
 {
 	struct drm_device *dev = adev->ddev;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	struct amdgpu_connector *amdgpu_connector;
 	struct amdgpu_connector_atom_dig *amdgpu_dig_connector;
 	struct drm_encoder *encoder;
@@ -1515,10 +1521,12 @@ amdgpu_connector_add(struct amdgpu_device *adev,
 		return;
 
 	/* see if we already added it */
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		amdgpu_connector = to_amdgpu_connector(connector);
 		if (amdgpu_connector->connector_id == connector_id) {
 			amdgpu_connector->devices |= supported_device;
+			drm_connector_list_iter_end(&iter);
 			return;
 		}
 		if (amdgpu_connector->ddc_bus && i2c_bus->valid) {
@@ -1533,6 +1541,7 @@ amdgpu_connector_add(struct amdgpu_device *adev,
 			}
 		}
 	}
+	drm_connector_list_iter_end(&iter);
 
 	/* check if it's a dp bridge */
 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 2f884699eaef..acd39ce9b08e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -3004,6 +3004,7 @@ int amdgpu_device_suspend(struct drm_device *dev, bool suspend, bool fbcon)
 	struct amdgpu_device *adev;
 	struct drm_crtc *crtc;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	int r;
 
 	if (dev == NULL || dev->dev_private == NULL) {
@@ -3026,9 +3027,11 @@ int amdgpu_device_suspend(struct drm_device *dev, bool suspend, bool fbcon)
 	if (!amdgpu_device_has_dc_support(adev)) {
 		/* turn off display hw */
 		drm_modeset_lock_all(dev);
-		list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-			drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
-		}
+		drm_connector_list_iter_begin(dev, &iter);
+		drm_for_each_connector_iter(connector, &iter)
+			drm_helper_connector_dpms(connector,
+						  DRM_MODE_DPMS_OFF);
+		drm_connector_list_iter_end(&iter);
 		drm_modeset_unlock_all(dev);
 			/* unpin the front buffers and cursors */
 		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
@@ -3107,6 +3110,7 @@ int amdgpu_device_suspend(struct drm_device *dev, bool suspend, bool fbcon)
 int amdgpu_device_resume(struct drm_device *dev, bool resume, bool fbcon)
 {
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	struct amdgpu_device *adev = dev->dev_private;
 	struct drm_crtc *crtc;
 	int r = 0;
@@ -3177,9 +3181,13 @@ int amdgpu_device_resume(struct drm_device *dev, bool resume, bool fbcon)
 
 			/* turn on display hw */
 			drm_modeset_lock_all(dev);
-			list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-				drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
-			}
+
+			drm_connector_list_iter_begin(dev, &iter);
+			drm_for_each_connector_iter(connector, &iter)
+				drm_helper_connector_dpms(connector,
+							  DRM_MODE_DPMS_ON);
+			drm_connector_list_iter_end(&iter);
+
 			drm_modeset_unlock_all(dev);
 		}
 		amdgpu_fbdev_set_suspend(adev, 0);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
index 1d4aaa9580f4..d2dd59a95e8a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
@@ -370,11 +370,13 @@ void amdgpu_display_print_display_setup(struct drm_device *dev)
 	struct amdgpu_connector *amdgpu_connector;
 	struct drm_encoder *encoder;
 	struct amdgpu_encoder *amdgpu_encoder;
+	struct drm_connector_list_iter iter;
 	uint32_t devices;
 	int i = 0;
 
+	drm_connector_list_iter_begin(dev, &iter);
 	DRM_INFO("AMDGPU Display Connectors\n");
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	drm_for_each_connector_iter(connector, &iter) {
 		amdgpu_connector = to_amdgpu_connector(connector);
 		DRM_INFO("Connector %d:\n", i);
 		DRM_INFO("  %s\n", connector->name);
@@ -438,6 +440,7 @@ void amdgpu_display_print_display_setup(struct drm_device *dev)
 		}
 		i++;
 	}
+	drm_connector_list_iter_end(&iter);
 }
 
 /**
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c
index 571a6dfb473e..61fcf247a638 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c
@@ -37,12 +37,14 @@ amdgpu_link_encoder_connector(struct drm_device *dev)
 {
 	struct amdgpu_device *adev = dev->dev_private;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	struct amdgpu_connector *amdgpu_connector;
 	struct drm_encoder *encoder;
 	struct amdgpu_encoder *amdgpu_encoder;
 
+	drm_connector_list_iter_begin(dev, &iter);
 	/* walk the list and link encoders to connectors */
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	drm_for_each_connector_iter(connector, &iter) {
 		amdgpu_connector = to_amdgpu_connector(connector);
 		list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
 			amdgpu_encoder = to_amdgpu_encoder(encoder);
@@ -55,6 +57,7 @@ amdgpu_link_encoder_connector(struct drm_device *dev)
 			}
 		}
 	}
+	drm_connector_list_iter_end(&iter);
 }
 
 void amdgpu_encoder_set_active_device(struct drm_encoder *encoder)
@@ -62,8 +65,10 @@ void amdgpu_encoder_set_active_device(struct drm_encoder *encoder)
 	struct drm_device *dev = encoder->dev;
 	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		if (connector->encoder == encoder) {
 			struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
 			amdgpu_encoder->active_device = amdgpu_encoder->devices & amdgpu_connector->devices;
@@ -72,6 +77,7 @@ void amdgpu_encoder_set_active_device(struct drm_encoder *encoder)
 				  amdgpu_connector->devices, encoder->encoder_type);
 		}
 	}
+	drm_connector_list_iter_end(&iter);
 }
 
 struct drm_connector *
@@ -79,15 +85,20 @@ amdgpu_get_connector_for_encoder(struct drm_encoder *encoder)
 {
 	struct drm_device *dev = encoder->dev;
 	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
-	struct drm_connector *connector;
+	struct drm_connector *connector, *found = NULL;
+	struct drm_connector_list_iter iter;
 	struct amdgpu_connector *amdgpu_connector;
 
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		amdgpu_connector = to_amdgpu_connector(connector);
-		if (amdgpu_encoder->active_device & amdgpu_connector->devices)
-			return connector;
+		if (amdgpu_encoder->active_device & amdgpu_connector->devices) {
+			found = connector;
+			break;
+		}
 	}
-	return NULL;
+	drm_connector_list_iter_end(&iter);
+	return found;
 }
 
 struct drm_connector *
@@ -95,15 +106,20 @@ amdgpu_get_connector_for_encoder_init(struct drm_encoder *encoder)
 {
 	struct drm_device *dev = encoder->dev;
 	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
-	struct drm_connector *connector;
+	struct drm_connector *connector, *found = NULL;
+	struct drm_connector_list_iter iter;
 	struct amdgpu_connector *amdgpu_connector;
 
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		amdgpu_connector = to_amdgpu_connector(connector);
-		if (amdgpu_encoder->devices & amdgpu_connector->devices)
-			return connector;
+		if (amdgpu_encoder->devices & amdgpu_connector->devices) {
+			found = connector;
+			break;
+		}
 	}
-	return NULL;
+	drm_connector_list_iter_end(&iter);
+	return found;
 }
 
 struct drm_encoder *amdgpu_get_external_encoder(struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
index 2a3f5ec298db..977e121204e6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
@@ -87,10 +87,13 @@ static void amdgpu_hotplug_work_func(struct work_struct *work)
 	struct drm_device *dev = adev->ddev;
 	struct drm_mode_config *mode_config = &dev->mode_config;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 
 	mutex_lock(&mode_config->mutex);
-	list_for_each_entry(connector, &mode_config->connector_list, head)
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter)
 		amdgpu_connector_hotplug(connector);
+	drm_connector_list_iter_end(&iter);
 	mutex_unlock(&mode_config->mutex);
 	/* Just fire off a uevent and let userspace tell us what to do */
 	drm_helper_hpd_irq_event(dev);
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
index 645550e7caf5..be82871ac3bd 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
@@ -330,9 +330,11 @@ static void dce_v10_0_hpd_init(struct amdgpu_device *adev)
 {
 	struct drm_device *dev = adev->ddev;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	u32 tmp;
 
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
 
 		if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
@@ -368,6 +370,7 @@ static void dce_v10_0_hpd_init(struct amdgpu_device *adev)
 		amdgpu_irq_get(adev, &adev->hpd_irq,
 			       amdgpu_connector->hpd.hpd);
 	}
+	drm_connector_list_iter_end(&iter);
 }
 
 /**
@@ -382,9 +385,11 @@ static void dce_v10_0_hpd_fini(struct amdgpu_device *adev)
 {
 	struct drm_device *dev = adev->ddev;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	u32 tmp;
 
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
 
 		if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
@@ -397,6 +402,7 @@ static void dce_v10_0_hpd_fini(struct amdgpu_device *adev)
 		amdgpu_irq_put(adev, &adev->hpd_irq,
 			       amdgpu_connector->hpd.hpd);
 	}
+	drm_connector_list_iter_end(&iter);
 }
 
 static u32 dce_v10_0_hpd_get_gpio_reg(struct amdgpu_device *adev)
@@ -1219,10 +1225,12 @@ static void dce_v10_0_afmt_audio_select_pin(struct drm_encoder *encoder)
 static void dce_v10_0_audio_write_latency_fields(struct drm_encoder *encoder,
 						struct drm_display_mode *mode)
 {
-	struct amdgpu_device *adev = encoder->dev->dev_private;
+	struct drm_device *dev = encoder->dev;
+	struct amdgpu_device *adev = dev->dev_private;
 	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
 	struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	struct amdgpu_connector *amdgpu_connector = NULL;
 	u32 tmp;
 	int interlace = 0;
@@ -1230,12 +1238,14 @@ static void dce_v10_0_audio_write_latency_fields(struct drm_encoder *encoder,
 	if (!dig || !dig->afmt || !dig->afmt->pin)
 		return;
 
-	list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		if (connector->encoder == encoder) {
 			amdgpu_connector = to_amdgpu_connector(connector);
 			break;
 		}
 	}
+	drm_connector_list_iter_end(&iter);
 
 	if (!amdgpu_connector) {
 		DRM_ERROR("Couldn't find encoder's connector\n");
@@ -1261,10 +1271,12 @@ static void dce_v10_0_audio_write_latency_fields(struct drm_encoder *encoder,
 
 static void dce_v10_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
 {
-	struct amdgpu_device *adev = encoder->dev->dev_private;
+	struct drm_device *dev = encoder->dev;
+	struct amdgpu_device *adev = dev->dev_private;
 	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
 	struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	struct amdgpu_connector *amdgpu_connector = NULL;
 	u32 tmp;
 	u8 *sadb = NULL;
@@ -1273,12 +1285,14 @@ static void dce_v10_0_audio_write_speaker_allocation(struct drm_encoder *encoder
 	if (!dig || !dig->afmt || !dig->afmt->pin)
 		return;
 
-	list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		if (connector->encoder == encoder) {
 			amdgpu_connector = to_amdgpu_connector(connector);
 			break;
 		}
 	}
+	drm_connector_list_iter_end(&iter);
 
 	if (!amdgpu_connector) {
 		DRM_ERROR("Couldn't find encoder's connector\n");
@@ -1313,10 +1327,12 @@ static void dce_v10_0_audio_write_speaker_allocation(struct drm_encoder *encoder
 
 static void dce_v10_0_audio_write_sad_regs(struct drm_encoder *encoder)
 {
-	struct amdgpu_device *adev = encoder->dev->dev_private;
+	struct drm_device *dev = encoder->dev;
+	struct amdgpu_device *adev = dev->dev_private;
 	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
 	struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	struct amdgpu_connector *amdgpu_connector = NULL;
 	struct cea_sad *sads;
 	int i, sad_count;
@@ -1339,12 +1355,14 @@ static void dce_v10_0_audio_write_sad_regs(struct drm_encoder *encoder)
 	if (!dig || !dig->afmt || !dig->afmt->pin)
 		return;
 
-	list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		if (connector->encoder == encoder) {
 			amdgpu_connector = to_amdgpu_connector(connector);
 			break;
 		}
 	}
+	drm_connector_list_iter_end(&iter);
 
 	if (!amdgpu_connector) {
 		DRM_ERROR("Couldn't find encoder's connector\n");
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
index d9f470632b2c..bde48775cf1b 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
@@ -348,9 +348,11 @@ static void dce_v11_0_hpd_init(struct amdgpu_device *adev)
 {
 	struct drm_device *dev = adev->ddev;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	u32 tmp;
 
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
 
 		if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
@@ -385,6 +387,7 @@ static void dce_v11_0_hpd_init(struct amdgpu_device *adev)
 		dce_v11_0_hpd_set_polarity(adev, amdgpu_connector->hpd.hpd);
 		amdgpu_irq_get(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
 	}
+	drm_connector_list_iter_end(&iter);
 }
 
 /**
@@ -399,9 +402,11 @@ static void dce_v11_0_hpd_fini(struct amdgpu_device *adev)
 {
 	struct drm_device *dev = adev->ddev;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	u32 tmp;
 
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
 
 		if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
@@ -413,6 +418,7 @@ static void dce_v11_0_hpd_fini(struct amdgpu_device *adev)
 
 		amdgpu_irq_put(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
 	}
+	drm_connector_list_iter_end(&iter);
 }
 
 static u32 dce_v11_0_hpd_get_gpio_reg(struct amdgpu_device *adev)
@@ -1245,10 +1251,12 @@ static void dce_v11_0_afmt_audio_select_pin(struct drm_encoder *encoder)
 static void dce_v11_0_audio_write_latency_fields(struct drm_encoder *encoder,
 						struct drm_display_mode *mode)
 {
-	struct amdgpu_device *adev = encoder->dev->dev_private;
+	struct drm_device *dev = encoder->dev;
+	struct amdgpu_device *adev = dev->dev_private;
 	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
 	struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	struct amdgpu_connector *amdgpu_connector = NULL;
 	u32 tmp;
 	int interlace = 0;
@@ -1256,12 +1264,14 @@ static void dce_v11_0_audio_write_latency_fields(struct drm_encoder *encoder,
 	if (!dig || !dig->afmt || !dig->afmt->pin)
 		return;
 
-	list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		if (connector->encoder == encoder) {
 			amdgpu_connector = to_amdgpu_connector(connector);
 			break;
 		}
 	}
+	drm_connector_list_iter_end(&iter);
 
 	if (!amdgpu_connector) {
 		DRM_ERROR("Couldn't find encoder's connector\n");
@@ -1287,10 +1297,12 @@ static void dce_v11_0_audio_write_latency_fields(struct drm_encoder *encoder,
 
 static void dce_v11_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
 {
-	struct amdgpu_device *adev = encoder->dev->dev_private;
+	struct drm_device *dev = encoder->dev;
+	struct amdgpu_device *adev = dev->dev_private;
 	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
 	struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	struct amdgpu_connector *amdgpu_connector = NULL;
 	u32 tmp;
 	u8 *sadb = NULL;
@@ -1299,12 +1311,14 @@ static void dce_v11_0_audio_write_speaker_allocation(struct drm_encoder *encoder
 	if (!dig || !dig->afmt || !dig->afmt->pin)
 		return;
 
-	list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		if (connector->encoder == encoder) {
 			amdgpu_connector = to_amdgpu_connector(connector);
 			break;
 		}
 	}
+	drm_connector_list_iter_end(&iter);
 
 	if (!amdgpu_connector) {
 		DRM_ERROR("Couldn't find encoder's connector\n");
@@ -1339,10 +1353,12 @@ static void dce_v11_0_audio_write_speaker_allocation(struct drm_encoder *encoder
 
 static void dce_v11_0_audio_write_sad_regs(struct drm_encoder *encoder)
 {
-	struct amdgpu_device *adev = encoder->dev->dev_private;
+	struct drm_device *dev = encoder->dev;
+	struct amdgpu_device *adev = dev->dev_private;
 	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
 	struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	struct amdgpu_connector *amdgpu_connector = NULL;
 	struct cea_sad *sads;
 	int i, sad_count;
@@ -1365,12 +1381,14 @@ static void dce_v11_0_audio_write_sad_regs(struct drm_encoder *encoder)
 	if (!dig || !dig->afmt || !dig->afmt->pin)
 		return;
 
-	list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		if (connector->encoder == encoder) {
 			amdgpu_connector = to_amdgpu_connector(connector);
 			break;
 		}
 	}
+	drm_connector_list_iter_end(&iter);
 
 	if (!amdgpu_connector) {
 		DRM_ERROR("Couldn't find encoder's connector\n");
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
index 3eb2e7429269..65f61de931d7 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
@@ -281,9 +281,11 @@ static void dce_v6_0_hpd_init(struct amdgpu_device *adev)
 {
 	struct drm_device *dev = adev->ddev;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	u32 tmp;
 
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
 
 		if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
@@ -309,7 +311,7 @@ static void dce_v6_0_hpd_init(struct amdgpu_device *adev)
 		dce_v6_0_hpd_set_polarity(adev, amdgpu_connector->hpd.hpd);
 		amdgpu_irq_get(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
 	}
-
+	drm_connector_list_iter_end(&iter);
 }
 
 /**
@@ -324,9 +326,11 @@ static void dce_v6_0_hpd_fini(struct amdgpu_device *adev)
 {
 	struct drm_device *dev = adev->ddev;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	u32 tmp;
 
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
 
 		if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
@@ -338,6 +342,7 @@ static void dce_v6_0_hpd_fini(struct amdgpu_device *adev)
 
 		amdgpu_irq_put(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
 	}
+	drm_connector_list_iter_end(&iter);
 }
 
 static u32 dce_v6_0_hpd_get_gpio_reg(struct amdgpu_device *adev)
@@ -1124,20 +1129,24 @@ static void dce_v6_0_audio_select_pin(struct drm_encoder *encoder)
 static void dce_v6_0_audio_write_latency_fields(struct drm_encoder *encoder,
 						struct drm_display_mode *mode)
 {
-	struct amdgpu_device *adev = encoder->dev->dev_private;
+	struct drm_device *dev = encoder->dev;
+	struct amdgpu_device *adev = dev->dev_private;
 	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
 	struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	struct amdgpu_connector *amdgpu_connector = NULL;
 	int interlace = 0;
 	u32 tmp;
 
-	list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		if (connector->encoder == encoder) {
 			amdgpu_connector = to_amdgpu_connector(connector);
 			break;
 		}
 	}
+	drm_connector_list_iter_end(&iter);
 
 	if (!amdgpu_connector) {
 		DRM_ERROR("Couldn't find encoder's connector\n");
@@ -1164,21 +1173,25 @@ static void dce_v6_0_audio_write_latency_fields(struct drm_encoder *encoder,
 
 static void dce_v6_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
 {
-	struct amdgpu_device *adev = encoder->dev->dev_private;
+	struct drm_device *dev = encoder->dev;
+	struct amdgpu_device *adev = dev->dev_private;
 	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
 	struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	struct amdgpu_connector *amdgpu_connector = NULL;
 	u8 *sadb = NULL;
 	int sad_count;
 	u32 tmp;
 
-	list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		if (connector->encoder == encoder) {
 			amdgpu_connector = to_amdgpu_connector(connector);
 			break;
 		}
 	}
+	drm_connector_list_iter_end(&iter);
 
 	if (!amdgpu_connector) {
 		DRM_ERROR("Couldn't find encoder's connector\n");
@@ -1221,10 +1234,12 @@ static void dce_v6_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
 
 static void dce_v6_0_audio_write_sad_regs(struct drm_encoder *encoder)
 {
-	struct amdgpu_device *adev = encoder->dev->dev_private;
+	struct drm_device *dev = encoder->dev;
+	struct amdgpu_device *adev = dev->dev_private;
 	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
 	struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	struct amdgpu_connector *amdgpu_connector = NULL;
 	struct cea_sad *sads;
 	int i, sad_count;
@@ -1244,12 +1259,14 @@ static void dce_v6_0_audio_write_sad_regs(struct drm_encoder *encoder)
 		{ ixAZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR13, HDMI_AUDIO_CODING_TYPE_WMA_PRO },
 	};
 
-	list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		if (connector->encoder == encoder) {
 			amdgpu_connector = to_amdgpu_connector(connector);
 			break;
 		}
 	}
+	drm_connector_list_iter_end(&iter);
 
 	if (!amdgpu_connector) {
 		DRM_ERROR("Couldn't find encoder's connector\n");
@@ -1632,6 +1649,7 @@ static void dce_v6_0_afmt_setmode(struct drm_encoder *encoder,
 	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
 	struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	struct amdgpu_connector *amdgpu_connector = NULL;
 	int em = amdgpu_atombios_encoder_get_encoder_mode(encoder);
 	int bpc = 8;
@@ -1639,12 +1657,14 @@ static void dce_v6_0_afmt_setmode(struct drm_encoder *encoder,
 	if (!dig || !dig->afmt)
 		return;
 
-	list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		if (connector->encoder == encoder) {
 			amdgpu_connector = to_amdgpu_connector(connector);
 			break;
 		}
 	}
+	drm_connector_list_iter_end(&iter);
 
 	if (!amdgpu_connector) {
 		DRM_ERROR("Couldn't find encoder's connector\n");
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
index a16c5e9e610e..e5f50882a51d 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
@@ -275,9 +275,11 @@ static void dce_v8_0_hpd_init(struct amdgpu_device *adev)
 {
 	struct drm_device *dev = adev->ddev;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	u32 tmp;
 
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
 
 		if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
@@ -303,6 +305,7 @@ static void dce_v8_0_hpd_init(struct amdgpu_device *adev)
 		dce_v8_0_hpd_set_polarity(adev, amdgpu_connector->hpd.hpd);
 		amdgpu_irq_get(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
 	}
+	drm_connector_list_iter_end(&iter);
 }
 
 /**
@@ -317,9 +320,11 @@ static void dce_v8_0_hpd_fini(struct amdgpu_device *adev)
 {
 	struct drm_device *dev = adev->ddev;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	u32 tmp;
 
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
 
 		if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
@@ -331,6 +336,7 @@ static void dce_v8_0_hpd_fini(struct amdgpu_device *adev)
 
 		amdgpu_irq_put(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
 	}
+	drm_connector_list_iter_end(&iter);
 }
 
 static u32 dce_v8_0_hpd_get_gpio_reg(struct amdgpu_device *adev)
@@ -1157,10 +1163,12 @@ static void dce_v8_0_afmt_audio_select_pin(struct drm_encoder *encoder)
 static void dce_v8_0_audio_write_latency_fields(struct drm_encoder *encoder,
 						struct drm_display_mode *mode)
 {
-	struct amdgpu_device *adev = encoder->dev->dev_private;
+	struct drm_device *dev = encoder->dev;
+	struct amdgpu_device *adev = dev->dev_private;
 	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
 	struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	struct amdgpu_connector *amdgpu_connector = NULL;
 	u32 tmp = 0, offset;
 
@@ -1169,12 +1177,14 @@ static void dce_v8_0_audio_write_latency_fields(struct drm_encoder *encoder,
 
 	offset = dig->afmt->pin->offset;
 
-	list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		if (connector->encoder == encoder) {
 			amdgpu_connector = to_amdgpu_connector(connector);
 			break;
 		}
 	}
+	drm_connector_list_iter_end(&iter);
 
 	if (!amdgpu_connector) {
 		DRM_ERROR("Couldn't find encoder's connector\n");
@@ -1214,10 +1224,12 @@ static void dce_v8_0_audio_write_latency_fields(struct drm_encoder *encoder,
 
 static void dce_v8_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
 {
-	struct amdgpu_device *adev = encoder->dev->dev_private;
+	struct drm_device *dev = encoder->dev;
+	struct amdgpu_device *adev = dev->dev_private;
 	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
 	struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	struct amdgpu_connector *amdgpu_connector = NULL;
 	u32 offset, tmp;
 	u8 *sadb = NULL;
@@ -1228,12 +1240,14 @@ static void dce_v8_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
 
 	offset = dig->afmt->pin->offset;
 
-	list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		if (connector->encoder == encoder) {
 			amdgpu_connector = to_amdgpu_connector(connector);
 			break;
 		}
 	}
+	drm_connector_list_iter_end(&iter);
 
 	if (!amdgpu_connector) {
 		DRM_ERROR("Couldn't find encoder's connector\n");
@@ -1263,11 +1277,13 @@ static void dce_v8_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
 
 static void dce_v8_0_audio_write_sad_regs(struct drm_encoder *encoder)
 {
-	struct amdgpu_device *adev = encoder->dev->dev_private;
+	struct drm_device *dev = encoder->dev;
+	struct amdgpu_device *adev = dev->dev_private;
 	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
 	struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
 	u32 offset;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	struct amdgpu_connector *amdgpu_connector = NULL;
 	struct cea_sad *sads;
 	int i, sad_count;
@@ -1292,12 +1308,14 @@ static void dce_v8_0_audio_write_sad_regs(struct drm_encoder *encoder)
 
 	offset = dig->afmt->pin->offset;
 
-	list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		if (connector->encoder == encoder) {
 			amdgpu_connector = to_amdgpu_connector(connector);
 			break;
 		}
 	}
+	drm_connector_list_iter_end(&iter);
 
 	if (!amdgpu_connector) {
 		DRM_ERROR("Couldn't find encoder's connector\n");
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 0a71ed1e7762..73630e2940d4 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -896,27 +896,29 @@ static int detect_mst_link_for_all_connectors(struct drm_device *dev)
 {
 	struct amdgpu_dm_connector *aconnector;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	int ret = 0;
 
-	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
-
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		aconnector = to_amdgpu_dm_connector(connector);
 		if (aconnector->dc_link->type == dc_connection_mst_branch &&
 		    aconnector->mst_mgr.aux) {
 			DRM_DEBUG_DRIVER("DM_MST: starting TM on aconnector: %p [id: %d]\n",
-					aconnector, aconnector->base.base.id);
+					 aconnector,
+					 aconnector->base.base.id);
 
 			ret = drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_mgr, true);
 			if (ret < 0) {
 				DRM_ERROR("DM_MST: Failed to start MST\n");
-				((struct dc_link *)aconnector->dc_link)->type = dc_connection_single;
-				return ret;
-				}
+				aconnector->dc_link->type =
+					dc_connection_single;
+				break;
 			}
+		}
 	}
+	drm_connector_list_iter_end(&iter);
 
-	drm_modeset_unlock(&dev->mode_config.connection_mutex);
 	return ret;
 }
 
@@ -954,14 +956,13 @@ static void s3_handle_mst(struct drm_device *dev, bool suspend)
 {
 	struct amdgpu_dm_connector *aconnector;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	struct drm_dp_mst_topology_mgr *mgr;
 	int ret;
 	bool need_hotplug = false;
 
-	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
-
-	list_for_each_entry(connector, &dev->mode_config.connector_list,
-			    head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		aconnector = to_amdgpu_dm_connector(connector);
 		if (aconnector->dc_link->type != dc_connection_mst_branch ||
 		    aconnector->mst_port)
@@ -979,8 +980,7 @@ static void s3_handle_mst(struct drm_device *dev, bool suspend)
 			}
 		}
 	}
-
-	drm_modeset_unlock(&dev->mode_config.connection_mutex);
+	drm_connector_list_iter_end(&iter);
 
 	if (need_hotplug)
 		drm_kms_helper_hotplug_event(dev);
@@ -1162,6 +1162,7 @@ static int dm_resume(void *handle)
 	struct amdgpu_display_manager *dm = &adev->dm;
 	struct amdgpu_dm_connector *aconnector;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *new_crtc_state;
 	struct dm_crtc_state *dm_new_crtc_state;
@@ -1194,7 +1195,8 @@ static int dm_resume(void *handle)
 	amdgpu_dm_irq_resume_early(adev);
 
 	/* Do detection*/
-	list_for_each_entry(connector, &ddev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(ddev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		aconnector = to_amdgpu_dm_connector(connector);
 
 		/*
@@ -1222,6 +1224,7 @@ static int dm_resume(void *handle)
 		amdgpu_dm_update_connector_after_detect(aconnector);
 		mutex_unlock(&aconnector->hpd_lock);
 	}
+	drm_connector_list_iter_end(&iter);
 
 	/* Force mode set in atomic commit */
 	for_each_new_crtc_in_state(dm->cached_state, crtc, new_crtc_state, i)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
index fa5d503d379c..64445c4cc4c2 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
@@ -732,8 +732,10 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
 {
 	struct drm_device *dev = adev->ddev;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		struct amdgpu_dm_connector *amdgpu_dm_connector =
 				to_amdgpu_dm_connector(connector);
 
@@ -751,6 +753,7 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
 					true);
 		}
 	}
+	drm_connector_list_iter_end(&iter);
 }
 
 /**
@@ -765,8 +768,10 @@ void amdgpu_dm_hpd_fini(struct amdgpu_device *adev)
 {
 	struct drm_device *dev = adev->ddev;
 	struct drm_connector *connector;
+	struct drm_connector_list_iter iter;
 
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	drm_connector_list_iter_begin(dev, &iter);
+	drm_for_each_connector_iter(connector, &iter) {
 		struct amdgpu_dm_connector *amdgpu_dm_connector =
 				to_amdgpu_dm_connector(connector);
 		const struct dc_link *dc_link = amdgpu_dm_connector->dc_link;
@@ -779,4 +784,5 @@ void amdgpu_dm_hpd_fini(struct amdgpu_device *adev)
 					false);
 		}
 	}
+	drm_connector_list_iter_end(&iter);
 }
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 24/27] drm/amdgpu/dm: Resume short HPD IRQs before resuming MST topology
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (22 preceding siblings ...)
  2019-09-03 20:46 ` [PATCH v2 23/27] drm/amdgpu: Iterate through DRM connectors correctly Lyude Paul
@ 2019-09-03 20:46 ` Lyude Paul
  2019-09-13 20:46   ` Alex Deucher
                     ` (2 more replies)
  2019-09-03 20:46 ` [PATCH v2 25/27] drm/dp_mst: Add basic topology reprobing when resuming Lyude Paul
                   ` (2 subsequent siblings)
  26 siblings, 3 replies; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:46 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Harry Wentland, Leo Li,
	Alex Deucher, Christian König, David (ChunMing) Zhou,
	David Airlie, Daniel Vetter, Nicholas Kazlauskas, David Francis,
	Mario Kleiner, Bhawanpreet Lakha, linux-kernel

Since we're going to be reprobing the entire topology state on resume
now using sideband transactions, we need to ensure that we actually have
short HPD irqs enabled before calling drm_dp_mst_topology_mgr_resume().
So, do that.

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 73630e2940d4..4d3c8bff77da 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -1185,15 +1185,15 @@ static int dm_resume(void *handle)
 	/* program HPD filter */
 	dc_resume(dm->dc);
 
-	/* On resume we need to  rewrite the MSTM control bits to enamble MST*/
-	s3_handle_mst(ddev, false);
-
 	/*
 	 * early enable HPD Rx IRQ, should be done before set mode as short
 	 * pulse interrupts are used for MST
 	 */
 	amdgpu_dm_irq_resume_early(adev);
 
+	/* On resume we need to  rewrite the MSTM control bits to enamble MST*/
+	s3_handle_mst(ddev, false);
+
 	/* Do detection*/
 	drm_connector_list_iter_begin(ddev, &iter);
 	drm_for_each_connector_iter(connector, &iter) {
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 25/27] drm/dp_mst: Add basic topology reprobing when resuming
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (23 preceding siblings ...)
  2019-09-03 20:46 ` [PATCH v2 24/27] drm/amdgpu/dm: Resume short HPD IRQs before resuming MST topology Lyude Paul
@ 2019-09-03 20:46 ` Lyude Paul
  2019-09-27 13:52   ` Sean Paul
  2019-09-03 20:46 ` [PATCH v2 26/27] drm/dp_mst: Also print unhashed pointers for malloc/topology references Lyude Paul
  2019-09-03 20:46 ` [PATCH v2 27/27] drm/dp_mst: Add topology ref history tracking for debugging Lyude Paul
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:46 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Harry Wentland, Leo Li,
	Alex Deucher, Christian König, David (ChunMing) Zhou,
	David Airlie, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
	Ben Skeggs, Nicholas Kazlauskas, David Francis, Mario Kleiner,
	Bhawanpreet Lakha, Chris Wilson, Manasi Navare,
	Dhinakaran Pandiyan, José Roberto de Souza, Karol Herbst,
	Laurent Pinchart, Ilia Mirkin, linux-kernel, intel-gfx

Finally! For a very long time, our MST helpers have had one very
annoying issue: They don't know how to reprobe the topology state when
coming out of suspend. This means that if a user has a machine connected
to an MST topology and decides to suspend their machine, we lose all
topology changes that happened during that period. That can be a big
problem if the machine was connected to a different topology on the same
port before resuming, as we won't bother reprobing any of the ports and
likely cause the user's monitors not to come back up as expected.

So, we start fixing this by teaching our MST helpers how to reprobe the
link addresses of each connected topology when resuming. As it turns
out, the behavior that we want here is identical to the behavior we want
when initially probing a newly connected MST topology, with a couple of
important differences:

- We need to be more careful about handling the potential races between
  events from the MST hub that could change the topology state as we're
  performing the link address reprobe
- We need to be more careful about handling unlikely state changes on
  ports - such as an input port turning into an output port, something
  that would be far more likely to happen in situations like the MST hub
  we're connected to being changed while we're suspend

Both of which have been solved by previous commits. That leaves one
requirement:

- We need to prune any MST ports in our in-memory topology state that
  were present when suspending, but have not appeared in the post-resume
  link address response from their parent branch device

Which we can now handle in this commit by modifying
drm_dp_send_link_address(). We then introduce suspend/resume reprobing
by introducing drm_dp_mst_topology_mgr_invalidate_mstb(), which we call
in drm_dp_mst_topology_mgr_suspend() to traverse the in-memory topology
state to indicate that each mstb needs it's link address resent and PBN
resources reprobed.

On resume, we start back up &mgr->work and have it reprobe the topology
in the same way we would on a hotplug, removing any leftover ports that
no longer appear in the topology state.

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |   2 +-
 drivers/gpu/drm/drm_dp_mst_topology.c         | 138 +++++++++++++-----
 drivers/gpu/drm/i915/display/intel_dp.c       |   3 +-
 drivers/gpu/drm/nouveau/dispnv50/disp.c       |   6 +-
 include/drm/drm_dp_mst_helper.h               |   3 +-
 5 files changed, 112 insertions(+), 40 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 4d3c8bff77da..27ee3e045b86 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -973,7 +973,7 @@ static void s3_handle_mst(struct drm_device *dev, bool suspend)
 		if (suspend) {
 			drm_dp_mst_topology_mgr_suspend(mgr);
 		} else {
-			ret = drm_dp_mst_topology_mgr_resume(mgr);
+			ret = drm_dp_mst_topology_mgr_resume(mgr, true);
 			if (ret < 0) {
 				drm_dp_mst_topology_mgr_set_mst(mgr, false);
 				need_hotplug = true;
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index e407aba1fbd2..2fe24e366925 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -2020,6 +2020,14 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
 		goto fail_unlock;
 	}
 
+	/*
+	 * If this port wasn't just created, then we're reprobing because
+	 * we're coming out of suspend. In this case, always resend the link
+	 * address if there's an MSTB on this port
+	 */
+	if (!created && port->pdt == DP_PEER_DEVICE_MST_BRANCHING)
+		send_link_addr = true;
+
 	if (send_link_addr) {
 		mutex_lock(&mgr->lock);
 		if (port->mstb) {
@@ -2530,7 +2538,8 @@ static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
 {
 	struct drm_dp_sideband_msg_tx *txmsg;
 	struct drm_dp_link_address_ack_reply *reply;
-	int i, len, ret;
+	struct drm_dp_mst_port *port, *tmp;
+	int i, len, ret, port_mask = 0;
 
 	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
 	if (!txmsg)
@@ -2560,9 +2569,28 @@ static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
 
 	drm_dp_check_mstb_guid(mstb, reply->guid);
 
-	for (i = 0; i < reply->nports; i++)
+	for (i = 0; i < reply->nports; i++) {
+		port_mask |= BIT(reply->ports[i].port_number);
 		drm_dp_mst_handle_link_address_port(mstb, mgr->dev,
 						    &reply->ports[i]);
+	}
+
+	/* Prune any ports that are currently a part of mstb in our in-memory
+	 * topology, but were not seen in this link address. Usually this
+	 * means that they were removed while the topology was out of sync,
+	 * e.g. during suspend/resume
+	 */
+	mutex_lock(&mgr->lock);
+	list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
+		if (port_mask & BIT(port->port_num))
+			continue;
+
+		DRM_DEBUG_KMS("port %d was not in link address, removing\n",
+			      port->port_num);
+		list_del(&port->next);
+		drm_dp_mst_topology_put_port(port);
+	}
+	mutex_unlock(&mgr->lock);
 
 	drm_kms_helper_hotplug_event(mgr->dev);
 
@@ -3191,6 +3219,23 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms
 }
 EXPORT_SYMBOL(drm_dp_mst_topology_mgr_set_mst);
 
+static void
+drm_dp_mst_topology_mgr_invalidate_mstb(struct drm_dp_mst_branch *mstb)
+{
+	struct drm_dp_mst_port *port;
+
+	/* The link address will need to be re-sent on resume */
+	mstb->link_address_sent = false;
+
+	list_for_each_entry(port, &mstb->ports, next) {
+		/* The PBN for each port will also need to be re-probed */
+		port->available_pbn = 0;
+
+		if (port->mstb)
+			drm_dp_mst_topology_mgr_invalidate_mstb(port->mstb);
+	}
+}
+
 /**
  * drm_dp_mst_topology_mgr_suspend() - suspend the MST manager
  * @mgr: manager to suspend
@@ -3207,60 +3252,85 @@ void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr)
 	flush_work(&mgr->up_req_work);
 	flush_work(&mgr->work);
 	flush_work(&mgr->delayed_destroy_work);
+
+	mutex_lock(&mgr->lock);
+	if (mgr->mst_state && mgr->mst_primary)
+		drm_dp_mst_topology_mgr_invalidate_mstb(mgr->mst_primary);
+	mutex_unlock(&mgr->lock);
 }
 EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend);
 
 /**
  * drm_dp_mst_topology_mgr_resume() - resume the MST manager
  * @mgr: manager to resume
+ * @sync: whether or not to perform topology reprobing synchronously
  *
  * This will fetch DPCD and see if the device is still there,
  * if it is, it will rewrite the MSTM control bits, and return.
  *
- * if the device fails this returns -1, and the driver should do
+ * If the device fails this returns -1, and the driver should do
  * a full MST reprobe, in case we were undocked.
+ *
+ * During system resume (where it is assumed that the driver will be calling
+ * drm_atomic_helper_resume()) this function should be called beforehand with
+ * @sync set to true. In contexts like runtime resume where the driver is not
+ * expected to be calling drm_atomic_helper_resume(), this function should be
+ * called with @sync set to false in order to avoid deadlocking.
+ *
+ * Returns: -1 if the MST topology was removed while we were suspended, 0
+ * otherwise.
  */
-int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr)
+int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr,
+				   bool sync)
 {
-	int ret = 0;
+	int ret;
+	u8 guid[16];
 
 	mutex_lock(&mgr->lock);
+	if (!mgr->mst_primary)
+		goto out_fail;
 
-	if (mgr->mst_primary) {
-		int sret;
-		u8 guid[16];
+	ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd,
+			       DP_RECEIVER_CAP_SIZE);
+	if (ret != DP_RECEIVER_CAP_SIZE) {
+		DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n");
+		goto out_fail;
+	}
 
-		sret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE);
-		if (sret != DP_RECEIVER_CAP_SIZE) {
-			DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n");
-			ret = -1;
-			goto out_unlock;
-		}
+	ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
+				 DP_MST_EN |
+				 DP_UP_REQ_EN |
+				 DP_UPSTREAM_IS_SRC);
+	if (ret < 0) {
+		DRM_DEBUG_KMS("mst write failed - undocked during suspend?\n");
+		goto out_fail;
+	}
 
-		ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
-					 DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC);
-		if (ret < 0) {
-			DRM_DEBUG_KMS("mst write failed - undocked during suspend?\n");
-			ret = -1;
-			goto out_unlock;
-		}
+	/* Some hubs forget their guids after they resume */
+	ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16);
+	if (ret != 16) {
+		DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n");
+		goto out_fail;
+	}
+	drm_dp_check_mstb_guid(mgr->mst_primary, guid);
 
-		/* Some hubs forget their guids after they resume */
-		sret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16);
-		if (sret != 16) {
-			DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n");
-			ret = -1;
-			goto out_unlock;
-		}
-		drm_dp_check_mstb_guid(mgr->mst_primary, guid);
+	/* For the final step of resuming the topology, we need to bring the
+	 * state of our in-memory topology back into sync with reality. So,
+	 * restart the probing process as if we're probing a new hub
+	 */
+	queue_work(system_long_wq, &mgr->work);
+	mutex_unlock(&mgr->lock);
 
-		ret = 0;
-	} else
-		ret = -1;
+	if (sync) {
+		DRM_DEBUG_KMS("Waiting for link probe work to finish re-syncing topology...\n");
+		flush_work(&mgr->work);
+	}
 
-out_unlock:
+	return 0;
+
+out_fail:
 	mutex_unlock(&mgr->lock);
-	return ret;
+	return -1;
 }
 EXPORT_SYMBOL(drm_dp_mst_topology_mgr_resume);
 
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 5673ed75e428..b78364dcdef9 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -7400,7 +7400,8 @@ void intel_dp_mst_resume(struct drm_i915_private *dev_priv)
 		if (!intel_dp->can_mst)
 			continue;
 
-		ret = drm_dp_mst_topology_mgr_resume(&intel_dp->mst_mgr);
+		ret = drm_dp_mst_topology_mgr_resume(&intel_dp->mst_mgr,
+						     true);
 		if (ret) {
 			intel_dp->is_mst = false;
 			drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr,
diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c
index 307584107d77..e459e2a79d78 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c
@@ -1309,14 +1309,14 @@ nv50_mstm_fini(struct nv50_mstm *mstm)
 }
 
 static void
-nv50_mstm_init(struct nv50_mstm *mstm)
+nv50_mstm_init(struct nv50_mstm *mstm, bool runtime)
 {
 	int ret;
 
 	if (!mstm || !mstm->mgr.mst_state)
 		return;
 
-	ret = drm_dp_mst_topology_mgr_resume(&mstm->mgr);
+	ret = drm_dp_mst_topology_mgr_resume(&mstm->mgr, !runtime);
 	if (ret == -1) {
 		drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false);
 		drm_kms_helper_hotplug_event(mstm->mgr.dev);
@@ -2262,7 +2262,7 @@ nv50_display_init(struct drm_device *dev, bool resume, bool runtime)
 		if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) {
 			struct nouveau_encoder *nv_encoder =
 				nouveau_encoder(encoder);
-			nv50_mstm_init(nv_encoder->dp.mstm);
+			nv50_mstm_init(nv_encoder->dp.mstm, runtime);
 		}
 	}
 
diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
index 1efbb086f7ac..1bdee5ee6dcd 100644
--- a/include/drm/drm_dp_mst_helper.h
+++ b/include/drm/drm_dp_mst_helper.h
@@ -685,7 +685,8 @@ void drm_dp_mst_dump_topology(struct seq_file *m,
 
 void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);
 int __must_check
-drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr);
+drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr,
+			       bool sync);
 
 ssize_t drm_dp_mst_dpcd_read(struct drm_dp_aux *aux,
 			     unsigned int offset, void *buffer, size_t size);
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 26/27] drm/dp_mst: Also print unhashed pointers for malloc/topology references
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (24 preceding siblings ...)
  2019-09-03 20:46 ` [PATCH v2 25/27] drm/dp_mst: Add basic topology reprobing when resuming Lyude Paul
@ 2019-09-03 20:46 ` Lyude Paul
  2019-09-27 14:25   ` Sean Paul
  2019-09-03 20:46 ` [PATCH v2 27/27] drm/dp_mst: Add topology ref history tracking for debugging Lyude Paul
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:46 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

Currently we only print mstb/port pointer addresses in our malloc and
topology refcount functions using the hashed-by-default %p, but
unfortunately if you're trying to debug a use-after-free error caused by
a refcounting error then this really isn't terribly useful. On the other
hand though, everything in the rest of the DP MST helpers uses hashed
pointer values as well and probably isn't useful to convert to unhashed.
So, let's just get the best of both worlds and print both the hashed and
unhashed pointer in our malloc/topology refcount debugging output. This
will hopefully make it a lot easier to figure out which port/mstb is
causing KASAN to get upset.

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 34 ++++++++++++++++-----------
 1 file changed, 20 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 2fe24e366925..5b5c0b3b3c0e 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -1327,7 +1327,8 @@ static void
 drm_dp_mst_get_mstb_malloc(struct drm_dp_mst_branch *mstb)
 {
 	kref_get(&mstb->malloc_kref);
-	DRM_DEBUG("mstb %p (%d)\n", mstb, kref_read(&mstb->malloc_kref));
+	DRM_DEBUG("mstb %p/%px (%d)\n",
+		  mstb, mstb, kref_read(&mstb->malloc_kref));
 }
 
 /**
@@ -1344,7 +1345,8 @@ drm_dp_mst_get_mstb_malloc(struct drm_dp_mst_branch *mstb)
 static void
 drm_dp_mst_put_mstb_malloc(struct drm_dp_mst_branch *mstb)
 {
-	DRM_DEBUG("mstb %p (%d)\n", mstb, kref_read(&mstb->malloc_kref) - 1);
+	DRM_DEBUG("mstb %p/%px (%d)\n",
+		  mstb, mstb, kref_read(&mstb->malloc_kref) - 1);
 	kref_put(&mstb->malloc_kref, drm_dp_free_mst_branch_device);
 }
 
@@ -1379,7 +1381,8 @@ void
 drm_dp_mst_get_port_malloc(struct drm_dp_mst_port *port)
 {
 	kref_get(&port->malloc_kref);
-	DRM_DEBUG("port %p (%d)\n", port, kref_read(&port->malloc_kref));
+	DRM_DEBUG("port %p/%px (%d)\n",
+		  port, port, kref_read(&port->malloc_kref));
 }
 EXPORT_SYMBOL(drm_dp_mst_get_port_malloc);
 
@@ -1396,7 +1399,8 @@ EXPORT_SYMBOL(drm_dp_mst_get_port_malloc);
 void
 drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port)
 {
-	DRM_DEBUG("port %p (%d)\n", port, kref_read(&port->malloc_kref) - 1);
+	DRM_DEBUG("port %p/%px (%d)\n",
+		  port, port, kref_read(&port->malloc_kref) - 1);
 	kref_put(&port->malloc_kref, drm_dp_free_mst_port);
 }
 EXPORT_SYMBOL(drm_dp_mst_put_port_malloc);
@@ -1447,8 +1451,8 @@ drm_dp_mst_topology_try_get_mstb(struct drm_dp_mst_branch *mstb)
 	int ret = kref_get_unless_zero(&mstb->topology_kref);
 
 	if (ret)
-		DRM_DEBUG("mstb %p (%d)\n", mstb,
-			  kref_read(&mstb->topology_kref));
+		DRM_DEBUG("mstb %p/%px (%d)\n",
+			  mstb, mstb, kref_read(&mstb->topology_kref));
 
 	return ret;
 }
@@ -1471,7 +1475,8 @@ static void drm_dp_mst_topology_get_mstb(struct drm_dp_mst_branch *mstb)
 {
 	WARN_ON(kref_read(&mstb->topology_kref) == 0);
 	kref_get(&mstb->topology_kref);
-	DRM_DEBUG("mstb %p (%d)\n", mstb, kref_read(&mstb->topology_kref));
+	DRM_DEBUG("mstb %p/%px (%d)\n",
+		  mstb, mstb, kref_read(&mstb->topology_kref));
 }
 
 /**
@@ -1489,8 +1494,8 @@ static void drm_dp_mst_topology_get_mstb(struct drm_dp_mst_branch *mstb)
 static void
 drm_dp_mst_topology_put_mstb(struct drm_dp_mst_branch *mstb)
 {
-	DRM_DEBUG("mstb %p (%d)\n",
-		  mstb, kref_read(&mstb->topology_kref) - 1);
+	DRM_DEBUG("mstb %p/%px (%d)\n",
+		  mstb, mstb, kref_read(&mstb->topology_kref) - 1);
 	kref_put(&mstb->topology_kref, drm_dp_destroy_mst_branch_device);
 }
 
@@ -1546,8 +1551,8 @@ drm_dp_mst_topology_try_get_port(struct drm_dp_mst_port *port)
 	int ret = kref_get_unless_zero(&port->topology_kref);
 
 	if (ret)
-		DRM_DEBUG("port %p (%d)\n", port,
-			  kref_read(&port->topology_kref));
+		DRM_DEBUG("port %p/%px (%d)\n",
+			  port, port, kref_read(&port->topology_kref));
 
 	return ret;
 }
@@ -1569,7 +1574,8 @@ static void drm_dp_mst_topology_get_port(struct drm_dp_mst_port *port)
 {
 	WARN_ON(kref_read(&port->topology_kref) == 0);
 	kref_get(&port->topology_kref);
-	DRM_DEBUG("port %p (%d)\n", port, kref_read(&port->topology_kref));
+	DRM_DEBUG("port %p/%px (%d)\n",
+		  port, port, kref_read(&port->topology_kref));
 }
 
 /**
@@ -1585,8 +1591,8 @@ static void drm_dp_mst_topology_get_port(struct drm_dp_mst_port *port)
  */
 static void drm_dp_mst_topology_put_port(struct drm_dp_mst_port *port)
 {
-	DRM_DEBUG("port %p (%d)\n",
-		  port, kref_read(&port->topology_kref) - 1);
+	DRM_DEBUG("port %p/%px (%d)\n",
+		  port, port, kref_read(&port->topology_kref) - 1);
 	kref_put(&port->topology_kref, drm_dp_destroy_port);
 }
 
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v2 27/27] drm/dp_mst: Add topology ref history tracking for debugging
       [not found] <20190903204645.25487-1-lyude@redhat.com>
                   ` (25 preceding siblings ...)
  2019-09-03 20:46 ` [PATCH v2 26/27] drm/dp_mst: Also print unhashed pointers for malloc/topology references Lyude Paul
@ 2019-09-03 20:46 ` Lyude Paul
  2019-09-27 14:51   ` Sean Paul
  26 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 20:46 UTC (permalink / raw)
  To: dri-devel, nouveau, amd-gfx
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

For very subtle mistakes with topology refs, it can be rather difficult
to trace them down with the debugging info that we already have. I had
one such issue recently while trying to implement suspend/resume
reprobing for MST, and ended up coming up with this.

Inspired by Chris Wilson's wakeref tracking for i915, this adds a very
similar feature to the DP MST helpers, which allows for partial tracking
of topology refs for both ports and branch devices. This is a lot less
advanced then wakeref tracking: we merely keep a count of all of the
spots where a topology ref has been grabbed or dropped, then dump out
that history in chronological order when a port or branch device's
topology refcount reaches 0. So far, I've found this incredibly useful
for debugging topology refcount errors.

Since this has the potential to be somewhat slow and loud, we add an
expert kernel config option to enable or disable this feature,
CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS.

Changes since v1:
* Don't forget to destroy topology_ref_history_lock

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/Kconfig               |  14 ++
 drivers/gpu/drm/drm_dp_mst_topology.c | 233 +++++++++++++++++++++++++-
 include/drm/drm_dp_mst_helper.h       |  45 +++++
 3 files changed, 288 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index e67c194c2aca..44fc2c2a6e2c 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -93,6 +93,20 @@ config DRM_KMS_FB_HELPER
 	help
 	  FBDEV helpers for KMS drivers.
 
+config DRM_DEBUG_DP_MST_TOPOLOGY_REFS
+        bool "Enable refcount backtrace history in the DP MST helpers"
+        select STACKDEPOT
+        depends on DRM_KMS_HELPER
+        depends on DEBUG_KERNEL
+        depends on EXPERT
+        help
+          Enables debug tracing for topology refs in DRM's DP MST helpers. A
+          history of each topology reference/dereference will be printed to the
+          kernel log once a port or branch device's topology refcount reaches 0.
+
+          This has the potential to use a lot of memory and print some very
+          large kernel messages. If in doubt, say "N".
+
 config DRM_FBDEV_EMULATION
 	bool "Enable legacy fbdev support for your modesetting driver"
 	depends on DRM
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 5b5c0b3b3c0e..18f9a02927d9 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -28,6 +28,13 @@
 #include <linux/sched.h>
 #include <linux/seq_file.h>
 
+#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
+#include <linux/stackdepot.h>
+#include <linux/sort.h>
+#include <linux/timekeeping.h>
+#include <linux/math64.h>
+#endif
+
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_dp_mst_helper.h>
@@ -1405,12 +1412,189 @@ drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port)
 }
 EXPORT_SYMBOL(drm_dp_mst_put_port_malloc);
 
+#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
+
+#define STACK_DEPTH 8
+
+static noinline void
+__topology_ref_save(struct drm_dp_mst_topology_mgr *mgr,
+		    struct drm_dp_mst_topology_ref_history *history,
+		    enum drm_dp_mst_topology_ref_type type)
+{
+	struct drm_dp_mst_topology_ref_entry *entry = NULL;
+	depot_stack_handle_t backtrace;
+	ulong stack_entries[STACK_DEPTH];
+	uint n;
+	int i;
+
+	n = stack_trace_save(stack_entries, ARRAY_SIZE(stack_entries), 1);
+	backtrace = stack_depot_save(stack_entries, n, GFP_KERNEL);
+	if (!backtrace)
+		goto fail_alloc;
+
+	/* Try to find an existing entry for this backtrace */
+	for (i = 0; i < history->len; i++) {
+		if (history->entries[i].backtrace == backtrace) {
+			entry = &history->entries[i];
+			break;
+		}
+	}
+
+	/* Otherwise add one */
+	if (!entry) {
+		struct drm_dp_mst_topology_ref_entry *new;
+		int new_len = history->len + 1;
+
+		new = krealloc(history->entries, sizeof(*new) * new_len,
+			       GFP_KERNEL);
+		if (!new)
+			goto fail_alloc;
+
+		entry = &new[history->len];
+		history->len = new_len;
+		history->entries = new;
+
+		entry->backtrace = backtrace;
+		entry->type = type;
+		entry->count = 0;
+	}
+	entry->count++;
+	entry->ts_nsec = ktime_get_ns();
+
+	return;
+fail_alloc:
+	DRM_WARN_ONCE("Failed to allocate memory for topology refcount backtrace\n");
+}
+
+static int
+topology_ref_history_cmp(const void *a, const void *b)
+{
+	const struct drm_dp_mst_topology_ref_entry *entry_a = a, *entry_b = b;
+
+	if (entry_a->ts_nsec > entry_b->ts_nsec)
+		return 1;
+	else if (entry_a->ts_nsec < entry_b->ts_nsec)
+		return -1;
+	else
+		return 0;
+}
+
+static inline const char *
+topology_ref_type_to_str(enum drm_dp_mst_topology_ref_type type)
+{
+	if (type == DRM_DP_MST_TOPOLOGY_REF_GET)
+		return "get";
+	else
+		return "put";
+}
+
+static void
+__dump_topology_ref_history(struct drm_dp_mst_topology_ref_history *history,
+			    void *ptr, const char *type_str)
+{
+	struct drm_printer p = drm_debug_printer(DBG_PREFIX);
+	char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	int i;
+
+	if (!buf)
+		return;
+
+	if (!history->len)
+		goto out;
+
+	/* First, sort the list so that it goes from oldest to newest
+	 * reference entry
+	 */
+	sort(history->entries, history->len, sizeof(*history->entries),
+	     topology_ref_history_cmp, NULL);
+
+	drm_printf(&p,
+		   "%s (%p/%px) topology count reached 0, dumping history:\n",
+		   type_str, ptr, ptr);
+
+	for (i = 0; i < history->len; i++) {
+		const struct drm_dp_mst_topology_ref_entry *entry =
+			&history->entries[i];
+		ulong *entries;
+		uint nr_entries;
+		u64 ts_nsec = entry->ts_nsec;
+		u64 rem_nsec = do_div(ts_nsec, 1000000000);
+
+		nr_entries = stack_depot_fetch(entry->backtrace, &entries);
+		stack_trace_snprint(buf, PAGE_SIZE, entries, nr_entries, 4);
+
+		drm_printf(&p, "  %d %ss (last at %5llu.%06llu):\n%s",
+			   entry->count,
+			   topology_ref_type_to_str(entry->type),
+			   ts_nsec, rem_nsec / 1000, buf);
+	}
+
+	/* Now free the history, since this is the only time we expose it */
+	kfree(history->entries);
+out:
+	kfree(buf);
+}
+
+static __always_inline void
+drm_dp_mst_dump_mstb_topology_history(struct drm_dp_mst_branch *mstb)
+{
+	__dump_topology_ref_history(&mstb->topology_ref_history, mstb,
+				    "MSTB");
+}
+
+static __always_inline void
+drm_dp_mst_dump_port_topology_history(struct drm_dp_mst_port *port)
+{
+	__dump_topology_ref_history(&port->topology_ref_history, port,
+				    "Port");
+}
+
+static __always_inline void
+save_mstb_topology_ref(struct drm_dp_mst_branch *mstb,
+		       enum drm_dp_mst_topology_ref_type type)
+{
+	__topology_ref_save(mstb->mgr, &mstb->topology_ref_history, type);
+}
+
+static __always_inline void
+save_port_topology_ref(struct drm_dp_mst_port *port,
+		       enum drm_dp_mst_topology_ref_type type)
+{
+	__topology_ref_save(port->mgr, &port->topology_ref_history, type);
+}
+
+static inline void
+topology_ref_history_lock(struct drm_dp_mst_topology_mgr *mgr)
+{
+	mutex_lock(&mgr->topology_ref_history_lock);
+}
+
+static inline void
+topology_ref_history_unlock(struct drm_dp_mst_topology_mgr *mgr)
+{
+	mutex_unlock(&mgr->topology_ref_history_lock);
+}
+#else
+static inline void
+topology_ref_history_lock(struct drm_dp_mst_topology_mgr *mgr) {}
+static inline void
+topology_ref_history_unlock(struct drm_dp_mst_topology_mgr *mgr) {}
+static inline void
+drm_dp_mst_dump_mstb_topology_history(struct drm_dp_mst_branch *mstb) {}
+static inline void
+drm_dp_mst_dump_port_topology_history(struct drm_dp_mst_port *port) {}
+#define save_mstb_topology_ref(mstb, type)
+#define save_port_topology_ref(port, type)
+#endif
+
 static void drm_dp_destroy_mst_branch_device(struct kref *kref)
 {
 	struct drm_dp_mst_branch *mstb =
 		container_of(kref, struct drm_dp_mst_branch, topology_kref);
 	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
 
+	drm_dp_mst_dump_mstb_topology_history(mstb);
+
 	INIT_LIST_HEAD(&mstb->destroy_next);
 
 	/*
@@ -1448,11 +1632,18 @@ static void drm_dp_destroy_mst_branch_device(struct kref *kref)
 static int __must_check
 drm_dp_mst_topology_try_get_mstb(struct drm_dp_mst_branch *mstb)
 {
-	int ret = kref_get_unless_zero(&mstb->topology_kref);
+	int ret;
 
-	if (ret)
+	topology_ref_history_lock(mstb->mgr);
+	ret = kref_get_unless_zero(&mstb->topology_kref);
+
+	if (ret) {
 		DRM_DEBUG("mstb %p/%px (%d)\n",
 			  mstb, mstb, kref_read(&mstb->topology_kref));
+		save_mstb_topology_ref(mstb, DRM_DP_MST_TOPOLOGY_REF_GET);
+	}
+
+	topology_ref_history_unlock(mstb->mgr);
 
 	return ret;
 }
@@ -1473,10 +1664,15 @@ drm_dp_mst_topology_try_get_mstb(struct drm_dp_mst_branch *mstb)
  */
 static void drm_dp_mst_topology_get_mstb(struct drm_dp_mst_branch *mstb)
 {
+	topology_ref_history_lock(mstb->mgr);
+
+	save_mstb_topology_ref(mstb, DRM_DP_MST_TOPOLOGY_REF_GET);
 	WARN_ON(kref_read(&mstb->topology_kref) == 0);
 	kref_get(&mstb->topology_kref);
 	DRM_DEBUG("mstb %p/%px (%d)\n",
 		  mstb, mstb, kref_read(&mstb->topology_kref));
+
+	topology_ref_history_unlock(mstb->mgr);
 }
 
 /**
@@ -1494,9 +1690,14 @@ static void drm_dp_mst_topology_get_mstb(struct drm_dp_mst_branch *mstb)
 static void
 drm_dp_mst_topology_put_mstb(struct drm_dp_mst_branch *mstb)
 {
+	topology_ref_history_lock(mstb->mgr);
+
 	DRM_DEBUG("mstb %p/%px (%d)\n",
 		  mstb, mstb, kref_read(&mstb->topology_kref) - 1);
+	save_mstb_topology_ref(mstb, DRM_DP_MST_TOPOLOGY_REF_PUT);
 	kref_put(&mstb->topology_kref, drm_dp_destroy_mst_branch_device);
+
+	topology_ref_history_unlock(mstb->mgr);
 }
 
 static void drm_dp_destroy_port(struct kref *kref)
@@ -1505,6 +1706,8 @@ static void drm_dp_destroy_port(struct kref *kref)
 		container_of(kref, struct drm_dp_mst_port, topology_kref);
 	struct drm_dp_mst_topology_mgr *mgr = port->mgr;
 
+	drm_dp_mst_dump_port_topology_history(port);
+
 	/* There's nothing that needs locking to destroy an input port yet */
 	if (port->input) {
 		drm_dp_mst_put_port_malloc(port);
@@ -1548,12 +1751,18 @@ static void drm_dp_destroy_port(struct kref *kref)
 static int __must_check
 drm_dp_mst_topology_try_get_port(struct drm_dp_mst_port *port)
 {
-	int ret = kref_get_unless_zero(&port->topology_kref);
+	int ret;
+
+	topology_ref_history_lock(port->mgr);
+	ret = kref_get_unless_zero(&port->topology_kref);
 
-	if (ret)
+	if (ret) {
 		DRM_DEBUG("port %p/%px (%d)\n",
 			  port, port, kref_read(&port->topology_kref));
+		save_port_topology_ref(port, DRM_DP_MST_TOPOLOGY_REF_GET);
+	}
 
+	topology_ref_history_unlock(port->mgr);
 	return ret;
 }
 
@@ -1572,10 +1781,15 @@ drm_dp_mst_topology_try_get_port(struct drm_dp_mst_port *port)
  */
 static void drm_dp_mst_topology_get_port(struct drm_dp_mst_port *port)
 {
+	topology_ref_history_lock(port->mgr);
+
 	WARN_ON(kref_read(&port->topology_kref) == 0);
 	kref_get(&port->topology_kref);
 	DRM_DEBUG("port %p/%px (%d)\n",
 		  port, port, kref_read(&port->topology_kref));
+	save_port_topology_ref(port, DRM_DP_MST_TOPOLOGY_REF_GET);
+
+	topology_ref_history_unlock(port->mgr);
 }
 
 /**
@@ -1591,9 +1805,14 @@ static void drm_dp_mst_topology_get_port(struct drm_dp_mst_port *port)
  */
 static void drm_dp_mst_topology_put_port(struct drm_dp_mst_port *port)
 {
+	topology_ref_history_lock(port->mgr);
+
 	DRM_DEBUG("port %p/%px (%d)\n",
 		  port, port, kref_read(&port->topology_kref) - 1);
+	save_port_topology_ref(port, DRM_DP_MST_TOPOLOGY_REF_PUT);
 	kref_put(&port->topology_kref, drm_dp_destroy_port);
+
+	topology_ref_history_unlock(port->mgr);
 }
 
 static struct drm_dp_mst_branch *
@@ -4548,6 +4767,9 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
 	mutex_init(&mgr->payload_lock);
 	mutex_init(&mgr->delayed_destroy_lock);
 	mutex_init(&mgr->up_req_lock);
+#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
+	mutex_init(&mgr->topology_ref_history_lock);
+#endif
 	INIT_LIST_HEAD(&mgr->tx_msg_downq);
 	INIT_LIST_HEAD(&mgr->destroy_port_list);
 	INIT_LIST_HEAD(&mgr->destroy_branch_device_list);
@@ -4613,6 +4835,9 @@ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
 	mutex_destroy(&mgr->qlock);
 	mutex_destroy(&mgr->lock);
 	mutex_destroy(&mgr->up_req_lock);
+#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
+	mutex_destroy(&mgr->topology_ref_history_lock);
+#endif
 }
 EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);
 
diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
index 1bdee5ee6dcd..75b8fba6f399 100644
--- a/include/drm/drm_dp_mst_helper.h
+++ b/include/drm/drm_dp_mst_helper.h
@@ -26,6 +26,26 @@
 #include <drm/drm_dp_helper.h>
 #include <drm/drm_atomic.h>
 
+#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
+#include <linux/stackdepot.h>
+#include <linux/timekeeping.h>
+
+enum drm_dp_mst_topology_ref_type {
+	DRM_DP_MST_TOPOLOGY_REF_GET,
+	DRM_DP_MST_TOPOLOGY_REF_PUT,
+};
+
+struct drm_dp_mst_topology_ref_history {
+	struct drm_dp_mst_topology_ref_entry {
+		enum drm_dp_mst_topology_ref_type type;
+		int count;
+		ktime_t ts_nsec;
+		depot_stack_handle_t backtrace;
+	} *entries;
+	int len;
+};
+#endif /* IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS) */
+
 struct drm_dp_mst_branch;
 
 /**
@@ -92,6 +112,14 @@ struct drm_dp_mst_port {
 	 */
 	struct kref malloc_kref;
 
+#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
+	/**
+	 * @topology_ref_history: A history of each topology
+	 * reference/dereference. See CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS.
+	 */
+	struct drm_dp_mst_topology_ref_history topology_ref_history;
+#endif
+
 	u8 port_num;
 	bool input;
 	bool mcs;
@@ -162,6 +190,14 @@ struct drm_dp_mst_branch {
 	 */
 	struct kref malloc_kref;
 
+#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
+	/**
+	 * @topology_ref_history: A history of each topology
+	 * reference/dereference. See CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS.
+	 */
+	struct drm_dp_mst_topology_ref_history topology_ref_history;
+#endif
+
 	/**
 	 * @destroy_next: linked-list entry used by
 	 * drm_dp_delayed_destroy_work()
@@ -630,6 +666,15 @@ struct drm_dp_mst_topology_mgr {
 	 * transmissions.
 	 */
 	struct work_struct up_req_work;
+
+#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
+	/**
+	 * @topology_ref_history_lock: protects
+	 * &drm_dp_mst_port.topology_ref_history and
+	 * &drm_dp_mst_branch.topology_ref_history.
+	 */
+	struct mutex topology_ref_history_lock;
+#endif
 };
 
 int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 06/27] drm/dp_mst: Combine redundant cases in drm_dp_encode_sideband_req()
  2019-09-03 20:45 ` [PATCH v2 06/27] drm/dp_mst: Combine redundant cases in drm_dp_encode_sideband_req() Lyude Paul
@ 2019-09-03 21:35   ` Dave Airlie
  2019-09-03 21:57   ` [PATCH v3] " Lyude Paul
  1 sibling, 0 replies; 62+ messages in thread
From: Dave Airlie @ 2019-09-03 21:35 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx mailing list, Sean Paul,
	David Airlie, Daniel Vetter, Imre Deak, Maarten Lankhorst, LKML,
	Maxime Ripard, Juston Li, Daniel Vetter, Harry Wentland,
	Ville Syrjälä

On Wed, 4 Sep 2019 at 06:48, Lyude Paul <lyude@redhat.com> wrote:
>
> Noticed this while working on adding a drm_dp_decode_sideband_req().
> DP_POWER_DOWN_PHY/DP_POWER_UP_PHY both use the same struct, so we can
> just combine their cases.

both use the same struct as enum path resources?

Since otherwise the patch doesn't make sense.

With that fixed:
Reviewed-by: Dave Airlie <airlied@redhat.com>

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 11/27] drm/dp_mst: Constify guid in drm_dp_get_mst_branch_by_guid()
  2019-09-03 20:45 ` [PATCH v2 11/27] drm/dp_mst: Constify guid in drm_dp_get_mst_branch_by_guid() Lyude Paul
@ 2019-09-03 21:41   ` Dave Airlie
  0 siblings, 0 replies; 62+ messages in thread
From: Dave Airlie @ 2019-09-03 21:41 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx mailing list, Sean Paul,
	David Airlie, Daniel Vetter, Imre Deak, Maarten Lankhorst, LKML,
	Maxime Ripard, Juston Li, Daniel Vetter, Harry Wentland,
	Ville Syrjälä

On Wed, 4 Sep 2019 at 06:48, Lyude Paul <lyude@redhat.com> wrote:
>
> And it's helper, we'll be using this in just a moment.
>

Reviewed-by: Dave Airlie <airlied@redhat.com>

> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>
> ---
>  drivers/gpu/drm/drm_dp_mst_topology.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
> index 43452872efad..b44d3696c09a 100644
> --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> @@ -2060,7 +2060,7 @@ static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_
>
>  static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper(
>         struct drm_dp_mst_branch *mstb,
> -       uint8_t *guid)
> +       const uint8_t *guid)
>  {
>         struct drm_dp_mst_branch *found_mstb;
>         struct drm_dp_mst_port *port;
> @@ -2084,7 +2084,7 @@ static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper(
>
>  static struct drm_dp_mst_branch *
>  drm_dp_get_mst_branch_device_by_guid(struct drm_dp_mst_topology_mgr *mgr,
> -                                    uint8_t *guid)
> +                                    const uint8_t *guid)
>  {
>         struct drm_dp_mst_branch *mstb;
>         int ret;
> --
> 2.21.0
>
> _______________________________________________
> amd-gfx mailing list
> amd-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 15/27] drm/dp_mst: Cleanup drm_dp_send_link_address() a bit
  2019-09-03 20:45 ` [PATCH v2 15/27] drm/dp_mst: Cleanup drm_dp_send_link_address() a bit Lyude Paul
@ 2019-09-03 21:42   ` Dave Airlie
  0 siblings, 0 replies; 62+ messages in thread
From: Dave Airlie @ 2019-09-03 21:42 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx mailing list, Sean Paul,
	David Airlie, Daniel Vetter, Imre Deak, Maarten Lankhorst, LKML,
	Maxime Ripard, Juston Li, Daniel Vetter, Harry Wentland,
	Ville Syrjälä

On Wed, 4 Sep 2019 at 06:48, Lyude Paul <lyude@redhat.com> wrote:
>
> Declare local pointer to the drm_dp_link_address_ack_reply struct
> instead of constantly dereferencing it through the union in
> txmsg->reply. Then, invert the order of conditionals so we don't have to
> do the bulk of the work inside them, and can wrap lines even less. Then
> finally, rearrange variable declarations a bit.
>
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>

Reviewed-by: Dave Airlie <airlied@redhat.com>

^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v3] drm/dp_mst: Combine redundant cases in drm_dp_encode_sideband_req()
  2019-09-03 20:45 ` [PATCH v2 06/27] drm/dp_mst: Combine redundant cases in drm_dp_encode_sideband_req() Lyude Paul
  2019-09-03 21:35   ` Dave Airlie
@ 2019-09-03 21:57   ` Lyude Paul
  1 sibling, 0 replies; 62+ messages in thread
From: Lyude Paul @ 2019-09-03 21:57 UTC (permalink / raw)
  To: dri-devel
  Cc: Dave Airlie, Daniel Vetter, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Maarten Lankhorst, Maxime Ripard, Sean Paul,
	David Airlie, Daniel Vetter, linux-kernel

Noticed this while working on adding a drm_dp_decode_sideband_req().
DP_POWER_DOWN_PHY/DP_POWER_UP_PHY both use the same struct as
DP_ENUM_PATH_RESOURCES, so we can just combine their cases.

Changes since v2:
* Fix commit message

Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Dave Airlie <airlied@redhat.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 6f7f449ca12b..1c862749cb63 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -271,6 +271,8 @@ static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req,
 
 	switch (req->req_type) {
 	case DP_ENUM_PATH_RESOURCES:
+	case DP_POWER_DOWN_PHY:
+	case DP_POWER_UP_PHY:
 		buf[idx] = (req->u.port_num.port_number & 0xf) << 4;
 		idx++;
 		break;
@@ -358,12 +360,6 @@ static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req,
 		memcpy(&buf[idx], req->u.i2c_write.bytes, req->u.i2c_write.num_bytes);
 		idx += req->u.i2c_write.num_bytes;
 		break;
-
-	case DP_POWER_DOWN_PHY:
-	case DP_POWER_UP_PHY:
-		buf[idx] = (req->u.port_num.port_number & 0xf) << 4;
-		idx++;
-		break;
 	}
 	raw->cur_len = idx;
 }
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 07/27] drm/dp_mst: Add sideband down request tracing + selftests
  2019-09-03 20:45 ` [PATCH v2 07/27] drm/dp_mst: Add sideband down request tracing + selftests Lyude Paul
@ 2019-09-10  9:01   ` Jani Nikula
  0 siblings, 0 replies; 62+ messages in thread
From: Jani Nikula @ 2019-09-10  9:01 UTC (permalink / raw)
  To: Lyude Paul, dri-devel, nouveau, amd-gfx
  Cc: Sean Paul, Thomas Hellstrom, David Airlie, Daniel Vetter,
	Alexandru Gheorghe, linux-kernel, Maxime Ripard, Juston Li,
	Deepak Rawat, Harry Wentland

On Tue, 03 Sep 2019, Lyude Paul <lyude@redhat.com> wrote:
> Unfortunately the DP MST helpers do not have much in the way of
> debugging utilities. So, let's add some!
>
> This adds basic debugging output for down sideband requests that we send
> from the driver, so that we can actually discern what's happening when
> sideband requests timeout.
>
> Since there wasn't really a good way of testing that any of this worked,
> I ended up writing simple selftests that lightly test sideband message
> encoding and decoding as well. Enjoy!
>
> Changes since v1:
> * Clean up DO_TEST() and sideband_msg_req_encode_decode() - danvet
> * Get rid of pr_fmt(), just define a prefix string instead and use
>   drm_printf()
> * Check highest bit of VCPI in drm_dp_decode_sideband_req() - danvet
> * Make the switch case order between drm_dp_decode_sideband_req() and
>   drm_dp_encode_sideband_req() the same - danvet
> * Only check DRM_UT_DP - danvet
> * Clean up sideband_msg_req_equal() from selftests a bit, and add
>   comments explaining why we can't just use memcmp - danvet
>
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>
> ---
>  drivers/gpu/drm/drm_dp_mst_topology.c         | 309 +++++++++++++++++-
>  .../gpu/drm/drm_dp_mst_topology_internal.h    |  24 ++
>  .../gpu/drm/selftests/drm_modeset_selftests.h |   1 +
>  .../drm/selftests/test-drm_dp_mst_helper.c    | 204 ++++++++++++
>  .../drm/selftests/test-drm_modeset_common.h   |   1 +
>  include/drm/drm_dp_mst_helper.h               |   2 +-
>  6 files changed, 536 insertions(+), 5 deletions(-)
>  create mode 100644 drivers/gpu/drm/drm_dp_mst_topology_internal.h
>
> diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
> index 1c862749cb63..f5f1d8b50fb6 100644
> --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> @@ -37,6 +37,7 @@
>  #include <drm/drm_probe_helper.h>
>  
>  #include "drm_crtc_helper_internal.h"
> +#include "drm_dp_mst_topology_internal.h"
>  
>  /**
>   * DOC: dp mst helper
> @@ -73,6 +74,8 @@ static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux);
>  static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux);
>  static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr);
>  
> +#define DBG_PREFIX "[dp_mst]"
> +
>  #define DP_STR(x) [DP_ ## x] = #x
>  
>  static const char *drm_dp_mst_req_type_str(u8 req_type)
> @@ -129,6 +132,43 @@ static const char *drm_dp_mst_nak_reason_str(u8 nak_reason)
>  }
>  
>  #undef DP_STR
> +#define DP_STR(x) [DRM_DP_SIDEBAND_TX_ ## x] = #x
> +
> +static const char *drm_dp_mst_sideband_tx_state_str(int state)
> +{
> +	static const char * const sideband_reason_str[] = {
> +		DP_STR(QUEUED),
> +		DP_STR(START_SEND),
> +		DP_STR(SENT),
> +		DP_STR(RX),
> +		DP_STR(TIMEOUT),
> +	};
> +
> +	if (state >= ARRAY_SIZE(sideband_reason_str) ||
> +	    !sideband_reason_str[state])
> +		return "unknown";
> +
> +	return sideband_reason_str[state];
> +}
> +
> +static int
> +drm_dp_mst_rad_to_str(const u8 rad[8], u8 lct, char *out, size_t len)
> +{
> +	int i;
> +	u8 unpacked_rad[16];
> +
> +	for (i = 0; i < lct; i++) {
> +		if (i % 2)
> +			unpacked_rad[i] = rad[i / 2] >> 4;
> +		else
> +			unpacked_rad[i] = rad[i / 2] & BIT_MASK(4);
> +	}
> +
> +	/* TODO: Eventually add something to printk so we can format the rad
> +	 * like this: 1.2.3
> +	 */
> +	return snprintf(out, len, "%*phC", lct, unpacked_rad);
> +}
>  
>  /* sideband msg handling */
>  static u8 drm_dp_msg_header_crc4(const uint8_t *data, size_t num_nibbles)
> @@ -261,8 +301,9 @@ static bool drm_dp_decode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr,
>  	return true;
>  }
>  
> -static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req,
> -				       struct drm_dp_sideband_msg_tx *raw)
> +void
> +drm_dp_encode_sideband_req(const struct drm_dp_sideband_msg_req_body *req,
> +			   struct drm_dp_sideband_msg_tx *raw)
>  {
>  	int idx = 0;
>  	int i;
> @@ -363,6 +404,251 @@ static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req,
>  	}
>  	raw->cur_len = idx;
>  }
> +EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_encode_sideband_req);
> +
> +/* Decode a sideband request we've encoded, mainly used for debugging */
> +int
> +drm_dp_decode_sideband_req(const struct drm_dp_sideband_msg_tx *raw,
> +			   struct drm_dp_sideband_msg_req_body *req)
> +{
> +	const u8 *buf = raw->msg;
> +	int i, idx = 0;
> +
> +	req->req_type = buf[idx++] & 0x7f;
> +	switch (req->req_type) {
> +	case DP_ENUM_PATH_RESOURCES:
> +	case DP_POWER_DOWN_PHY:
> +	case DP_POWER_UP_PHY:
> +		req->u.port_num.port_number = (buf[idx] >> 4) & 0xf;
> +		break;
> +	case DP_ALLOCATE_PAYLOAD:
> +		{
> +			struct drm_dp_allocate_payload *a =
> +				&req->u.allocate_payload;
> +
> +			a->number_sdp_streams = buf[idx] & 0xf;
> +			a->port_number = (buf[idx] >> 4) & 0xf;
> +
> +			WARN_ON(buf[++idx] & 0x80);
> +			a->vcpi = buf[idx] & 0x7f;
> +
> +			a->pbn = buf[++idx] << 8;
> +			a->pbn |= buf[++idx];
> +
> +			idx++;
> +			for (i = 0; i < a->number_sdp_streams; i++) {
> +				a->sdp_stream_sink[i] =
> +					(buf[idx + (i / 2)] >> ((i % 2) ? 0 : 4)) & 0xf;
> +			}
> +		}
> +		break;
> +	case DP_QUERY_PAYLOAD:
> +		req->u.query_payload.port_number = (buf[idx] >> 4) & 0xf;
> +		WARN_ON(buf[++idx] & 0x80);
> +		req->u.query_payload.vcpi = buf[idx] & 0x7f;
> +		break;
> +	case DP_REMOTE_DPCD_READ:
> +		{
> +			struct drm_dp_remote_dpcd_read *r = &req->u.dpcd_read;
> +
> +			r->port_number = (buf[idx] >> 4) & 0xf;
> +
> +			r->dpcd_address = (buf[idx] << 16) & 0xf0000;
> +			r->dpcd_address |= (buf[++idx] << 8) & 0xff00;
> +			r->dpcd_address |= buf[++idx] & 0xff;
> +
> +			r->num_bytes = buf[++idx];
> +		}
> +		break;
> +	case DP_REMOTE_DPCD_WRITE:
> +		{
> +			struct drm_dp_remote_dpcd_write *w =
> +				&req->u.dpcd_write;
> +
> +			w->port_number = (buf[idx] >> 4) & 0xf;
> +
> +			w->dpcd_address = (buf[idx] << 16) & 0xf0000;
> +			w->dpcd_address |= (buf[++idx] << 8) & 0xff00;
> +			w->dpcd_address |= buf[++idx] & 0xff;
> +
> +			w->num_bytes = buf[++idx];
> +
> +			w->bytes = kmemdup(&buf[++idx], w->num_bytes,
> +					   GFP_KERNEL);
> +			if (!w->bytes)
> +				return -ENOMEM;
> +		}
> +		break;
> +	case DP_REMOTE_I2C_READ:
> +		{
> +			struct drm_dp_remote_i2c_read *r = &req->u.i2c_read;
> +			struct drm_dp_remote_i2c_read_tx *tx;
> +			bool failed = false;
> +
> +			r->num_transactions = buf[idx] & 0x3;
> +			r->port_number = (buf[idx] >> 4) & 0xf;
> +			for (i = 0; i < r->num_transactions; i++) {
> +				tx = &r->transactions[i];
> +
> +				tx->i2c_dev_id = buf[++idx] & 0x7f;
> +				tx->num_bytes = buf[++idx];
> +				tx->bytes = kmemdup(&buf[++idx],
> +						    tx->num_bytes,
> +						    GFP_KERNEL);
> +				if (!tx->bytes) {
> +					failed = true;
> +					break;
> +				}
> +				idx += tx->num_bytes;
> +				tx->no_stop_bit = (buf[idx] >> 5) & 0x1;
> +				tx->i2c_transaction_delay = buf[idx] & 0xf;
> +			}
> +
> +			if (failed) {
> +				for (i = 0; i < r->num_transactions; i++)
> +					kfree(tx->bytes);
> +				return -ENOMEM;
> +			}
> +
> +			r->read_i2c_device_id = buf[++idx] & 0x7f;
> +			r->num_bytes_read = buf[++idx];
> +		}
> +		break;
> +	case DP_REMOTE_I2C_WRITE:
> +		{
> +			struct drm_dp_remote_i2c_write *w = &req->u.i2c_write;
> +
> +			w->port_number = (buf[idx] >> 4) & 0xf;
> +			w->write_i2c_device_id = buf[++idx] & 0x7f;
> +			w->num_bytes = buf[++idx];
> +			w->bytes = kmemdup(&buf[++idx], w->num_bytes,
> +					   GFP_KERNEL);
> +			if (!w->bytes)
> +				return -ENOMEM;
> +		}
> +		break;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_decode_sideband_req);
> +
> +void
> +drm_dp_dump_sideband_msg_req_body(const struct drm_dp_sideband_msg_req_body *req,
> +				  int indent, struct drm_printer *printer)
> +{
> +	int i;
> +
> +#define P(f, ...) drm_printf_indent(printer, indent, f, ##__VA_ARGS__)
> +	if (req->req_type == DP_LINK_ADDRESS) {
> +		/* No contents to print */
> +		P("type=%s\n", drm_dp_mst_req_type_str(req->req_type));
> +		return;
> +	}
> +
> +	P("type=%s contents:\n", drm_dp_mst_req_type_str(req->req_type));
> +	indent++;
> +
> +	switch (req->req_type) {
> +	case DP_ENUM_PATH_RESOURCES:
> +	case DP_POWER_DOWN_PHY:
> +	case DP_POWER_UP_PHY:
> +		P("port=%d\n", req->u.port_num.port_number);
> +		break;
> +	case DP_ALLOCATE_PAYLOAD:
> +		P("port=%d vcpi=%d pbn=%d sdp_streams=%d %*ph\n",
> +		  req->u.allocate_payload.port_number,
> +		  req->u.allocate_payload.vcpi, req->u.allocate_payload.pbn,
> +		  req->u.allocate_payload.number_sdp_streams,
> +		  req->u.allocate_payload.number_sdp_streams,
> +		  req->u.allocate_payload.sdp_stream_sink);
> +		break;
> +	case DP_QUERY_PAYLOAD:
> +		P("port=%d vcpi=%d\n",
> +		  req->u.query_payload.port_number,
> +		  req->u.query_payload.vcpi);
> +		break;
> +	case DP_REMOTE_DPCD_READ:
> +		P("port=%d dpcd_addr=%05x len=%d\n",
> +		  req->u.dpcd_read.port_number, req->u.dpcd_read.dpcd_address,
> +		  req->u.dpcd_read.num_bytes);
> +		break;
> +	case DP_REMOTE_DPCD_WRITE:
> +		P("port=%d addr=%05x len=%d: %*ph\n",
> +		  req->u.dpcd_write.port_number,
> +		  req->u.dpcd_write.dpcd_address,
> +		  req->u.dpcd_write.num_bytes, req->u.dpcd_write.num_bytes,
> +		  req->u.dpcd_write.bytes);
> +		break;
> +	case DP_REMOTE_I2C_READ:
> +		P("port=%d num_tx=%d id=%d size=%d:\n",
> +		  req->u.i2c_read.port_number,
> +		  req->u.i2c_read.num_transactions,
> +		  req->u.i2c_read.read_i2c_device_id,
> +		  req->u.i2c_read.num_bytes_read);
> +
> +		indent++;
> +		for (i = 0; i < req->u.i2c_read.num_transactions; i++) {
> +			const struct drm_dp_remote_i2c_read_tx *rtx =
> +				&req->u.i2c_read.transactions[i];
> +
> +			P("%d: id=%03d size=%03d no_stop_bit=%d tx_delay=%03d: %*ph\n",
> +			  i, rtx->i2c_dev_id, rtx->num_bytes,
> +			  rtx->no_stop_bit, rtx->i2c_transaction_delay,
> +			  rtx->num_bytes, rtx->bytes);
> +		}
> +		break;
> +	case DP_REMOTE_I2C_WRITE:
> +		P("port=%d id=%d size=%d: %*ph\n",
> +		  req->u.i2c_write.port_number,
> +		  req->u.i2c_write.write_i2c_device_id,
> +		  req->u.i2c_write.num_bytes, req->u.i2c_write.num_bytes,
> +		  req->u.i2c_write.bytes);
> +		break;
> +	default:
> +		P("???\n");
> +		break;
> +	}
> +#undef P
> +}
> +EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_dump_sideband_msg_req_body);
> +
> +static inline void
> +drm_dp_mst_dump_sideband_msg_tx(struct drm_printer *p,
> +				const struct drm_dp_sideband_msg_tx *txmsg)
> +{
> +	struct drm_dp_sideband_msg_req_body req;
> +	char buf[64];
> +	int ret;
> +	int i;
> +
> +	drm_dp_mst_rad_to_str(txmsg->dst->rad, txmsg->dst->lct, buf,
> +			      sizeof(buf));
> +	drm_printf(p, "txmsg cur_offset=%x cur_len=%x seqno=%x state=%s path_msg=%d dst=%s\n",
> +		   txmsg->cur_offset, txmsg->cur_len, txmsg->seqno,
> +		   drm_dp_mst_sideband_tx_state_str(txmsg->state),
> +		   txmsg->path_msg, buf);
> +
> +	ret = drm_dp_decode_sideband_req(txmsg, &req);
> +	if (ret) {
> +		drm_printf(p, "<failed to decode sideband req: %d>\n", ret);
> +		return;
> +	}
> +	drm_dp_dump_sideband_msg_req_body(&req, 1, p);
> +
> +	switch (req.req_type) {
> +	case DP_REMOTE_DPCD_WRITE:
> +		kfree(req.u.dpcd_write.bytes);
> +		break;
> +	case DP_REMOTE_I2C_READ:
> +		for (i = 0; i < req.u.i2c_read.num_transactions; i++)
> +			kfree(req.u.i2c_read.transactions[i].bytes);
> +		break;
> +	case DP_REMOTE_I2C_WRITE:
> +		kfree(req.u.i2c_write.bytes);
> +		break;
> +	}
> +}
>  
>  static void drm_dp_crc_sideband_chunk_req(u8 *msg, u8 len)
>  {
> @@ -894,6 +1180,11 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,
>  		}
>  	}
>  out:
> +	if (unlikely(ret == -EIO && drm_debug & DRM_UT_DP)) {
> +		struct drm_printer p = drm_debug_printer(DBG_PREFIX);
> +
> +		drm_dp_mst_dump_sideband_msg_tx(&p, txmsg);
> +	}
>  	mutex_unlock(&mgr->qlock);
>  
>  	return ret;
> @@ -2013,8 +2304,11 @@ static int process_single_tx_qlock(struct drm_dp_mst_topology_mgr *mgr,
>  	idx += tosend + 1;
>  
>  	ret = drm_dp_send_sideband_msg(mgr, up, chunk, idx);
> -	if (ret) {
> -		DRM_DEBUG_KMS("sideband msg failed to send\n");
> +	if (unlikely(ret && drm_debug & DRM_UT_DP)) {
> +		struct drm_printer p = drm_debug_printer(DBG_PREFIX);
> +
> +		drm_printf(&p, "sideband msg failed to send\n");
> +		drm_dp_mst_dump_sideband_msg_tx(&p, txmsg);
>  		return ret;
>  	}
>  
> @@ -2076,6 +2370,13 @@ static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr,
>  {
>  	mutex_lock(&mgr->qlock);
>  	list_add_tail(&txmsg->next, &mgr->tx_msg_downq);
> +
> +	if (unlikely(drm_debug & DRM_UT_DP)) {
> +		struct drm_printer p = drm_debug_printer(DBG_PREFIX);
> +
> +		drm_dp_mst_dump_sideband_msg_tx(&p, txmsg);
> +	}
> +
>  	if (list_is_singular(&mgr->tx_msg_downq))
>  		process_single_down_tx_qlock(mgr);
>  	mutex_unlock(&mgr->qlock);
> diff --git a/drivers/gpu/drm/drm_dp_mst_topology_internal.h b/drivers/gpu/drm/drm_dp_mst_topology_internal.h
> new file mode 100644
> index 000000000000..eeda9a61c657
> --- /dev/null
> +++ b/drivers/gpu/drm/drm_dp_mst_topology_internal.h
> @@ -0,0 +1,24 @@
> +/* SPDX-License-Identifier: GPL-2.0-only
> + *
> + * Declarations for DP MST related functions which are only used in selftests
> + *
> + * Copyright © 2018 Red Hat
> + * Authors:
> + *     Lyude Paul <lyude@redhat.com>
> + */
> +
> +#ifndef _DRM_DP_MST_HELPER_INTERNAL_H_
> +#define _DRM_DP_MST_HELPER_INTERNAL_H_
> +
> +#include <drm/drm_dp_mst_helper.h>
> +
> +void
> +drm_dp_encode_sideband_req(const struct drm_dp_sideband_msg_req_body *req,
> +			   struct drm_dp_sideband_msg_tx *raw);
> +int drm_dp_decode_sideband_req(const struct drm_dp_sideband_msg_tx *raw,
> +			       struct drm_dp_sideband_msg_req_body *req);
> +void
> +drm_dp_dump_sideband_msg_req_body(const struct drm_dp_sideband_msg_req_body *req,
> +				  int indent, struct drm_printer *printer);
> +
> +#endif /* !_DRM_DP_MST_HELPER_INTERNAL_H_ */
> diff --git a/drivers/gpu/drm/selftests/drm_modeset_selftests.h b/drivers/gpu/drm/selftests/drm_modeset_selftests.h
> index dec3ee3ec96f..1898de0b4a4d 100644
> --- a/drivers/gpu/drm/selftests/drm_modeset_selftests.h
> +++ b/drivers/gpu/drm/selftests/drm_modeset_selftests.h
> @@ -33,3 +33,4 @@ selftest(damage_iter_damage_one_outside, igt_damage_iter_damage_one_outside)
>  selftest(damage_iter_damage_src_moved, igt_damage_iter_damage_src_moved)
>  selftest(damage_iter_damage_not_visible, igt_damage_iter_damage_not_visible)
>  selftest(dp_mst_calc_pbn_mode, igt_dp_mst_calc_pbn_mode)
> +selftest(dp_mst_sideband_msg_req_decode, igt_dp_mst_sideband_msg_req_decode)
> diff --git a/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c b/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
> index 9baa5171988d..af2b2de65316 100644
> --- a/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
> +++ b/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
> @@ -3,9 +3,12 @@
>   * Test cases for for the DRM DP MST helpers
>   */
>  
> +#define PREFIX_STR "[drm_dp_mst_helper]"
> +
>  #include <drm/drm_dp_mst_helper.h>
>  #include <drm/drm_print.h>
>  
> +#include "../drm_dp_mst_topology_internal.h"
>  #include "test-drm_modeset_common.h"
>  
>  int igt_dp_mst_calc_pbn_mode(void *ignored)
> @@ -32,3 +35,204 @@ int igt_dp_mst_calc_pbn_mode(void *ignored)
>  
>  	return 0;
>  }
> +
> +static bool
> +sideband_msg_req_equal(const struct drm_dp_sideband_msg_req_body *in,
> +		       const struct drm_dp_sideband_msg_req_body *out)
> +{
> +	const struct drm_dp_remote_i2c_read_tx *txin, *txout;
> +	int i;
> +
> +	if (in->req_type != out->req_type)
> +		return false;
> +
> +	switch (in->req_type) {
> +	/*
> +	 * Compare struct members manually for request types which can't be
> +	 * compared simply using memcmp(). This is because said request types
> +	 * contain pointers to other allocated structs
> +	 */
> +	case DP_REMOTE_I2C_READ:
> +#define IN in->u.i2c_read
> +#define OUT out->u.i2c_read
> +		if (IN.num_bytes_read != OUT.num_bytes_read ||
> +		    IN.num_transactions != OUT.num_transactions ||
> +		    IN.port_number != OUT.port_number ||
> +		    IN.read_i2c_device_id != OUT.read_i2c_device_id)
> +			return false;
> +
> +		for (i = 0; i < IN.num_transactions; i++) {
> +			txin = &IN.transactions[i];
> +			txout = &OUT.transactions[i];
> +
> +			if (txin->i2c_dev_id != txout->i2c_dev_id ||
> +			    txin->no_stop_bit != txout->no_stop_bit ||
> +			    txin->num_bytes != txout->num_bytes ||
> +			    txin->i2c_transaction_delay !=
> +			    txout->i2c_transaction_delay)
> +				return false;
> +
> +			if (memcmp(txin->bytes, txout->bytes,
> +				   txin->num_bytes) != 0)
> +				return false;
> +		}
> +		break;
> +#undef IN
> +#undef OUT
> +
> +	case DP_REMOTE_DPCD_WRITE:
> +#define IN in->u.dpcd_write
> +#define OUT out->u.dpcd_write
> +		if (IN.dpcd_address != OUT.dpcd_address ||
> +		    IN.num_bytes != OUT.num_bytes ||
> +		    IN.port_number != OUT.port_number)
> +			return false;
> +
> +		return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0;
> +#undef IN
> +#undef OUT
> +
> +	case DP_REMOTE_I2C_WRITE:
> +#define IN in->u.i2c_write
> +#define OUT out->u.i2c_write
> +		if (IN.port_number != OUT.port_number ||
> +		    IN.write_i2c_device_id != OUT.write_i2c_device_id ||
> +		    IN.num_bytes != OUT.num_bytes)
> +			return false;
> +
> +		return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0;
> +#undef IN
> +#undef OUT
> +
> +	default:
> +		return memcmp(in, out, sizeof(*in)) == 0;
> +	}
> +
> +	return true;
> +}
> +
> +static bool
> +sideband_msg_req_encode_decode(struct drm_dp_sideband_msg_req_body *in)
> +{
> +	struct drm_dp_sideband_msg_req_body out = {0};
> +	struct drm_printer p = drm_err_printer(PREFIX_STR);
> +	struct drm_dp_sideband_msg_tx txmsg;
> +	int i, ret;
> +
> +	drm_dp_encode_sideband_req(in, &txmsg);
> +	ret = drm_dp_decode_sideband_req(&txmsg, &out);
> +	if (ret < 0) {
> +		drm_printf(&p, "Failed to decode sideband request: %d\n",
> +			   ret);

Is this the sole reason of adding drm_err_printer()? Where do you intend
to take error printing next, in general? Why isn't DRM_ERROR() the right
thing to do here?

It's just that I think debug and error printing from drm is a mess, and
instead of cleaning it up, we seem to be piling on new stuff without
direction.

BR,
Jani.



> +		return false;
> +	}
> +
> +	if (!sideband_msg_req_equal(in, &out)) {
> +		drm_printf(&p, "Encode/decode failed, expected:\n");
> +		drm_dp_dump_sideband_msg_req_body(in, 1, &p);
> +		drm_printf(&p, "Got:\n");
> +		drm_dp_dump_sideband_msg_req_body(&out, 1, &p);
> +		return false;
> +	}
> +
> +	switch (in->req_type) {
> +	case DP_REMOTE_DPCD_WRITE:
> +		kfree(out.u.dpcd_write.bytes);
> +		break;
> +	case DP_REMOTE_I2C_READ:
> +		for (i = 0; i < out.u.i2c_read.num_transactions; i++)
> +			kfree(out.u.i2c_read.transactions[i].bytes);
> +		break;
> +	case DP_REMOTE_I2C_WRITE:
> +		kfree(out.u.i2c_write.bytes);
> +		break;
> +	}
> +
> +	/* Clear everything but the req_type for the input */
> +	memset(&in->u, 0, sizeof(in->u));
> +
> +	return true;
> +}
> +
> +int igt_dp_mst_sideband_msg_req_decode(void *unused)
> +{
> +	struct drm_dp_sideband_msg_req_body in = { 0 };
> +	u8 data[] = { 0xff, 0x0, 0xdd };
> +	int i;
> +
> +#define DO_TEST() FAIL_ON(!sideband_msg_req_encode_decode(&in))
> +
> +	in.req_type = DP_ENUM_PATH_RESOURCES;
> +	in.u.port_num.port_number = 5;
> +	DO_TEST();
> +
> +	in.req_type = DP_POWER_UP_PHY;
> +	in.u.port_num.port_number = 5;
> +	DO_TEST();
> +
> +	in.req_type = DP_POWER_DOWN_PHY;
> +	in.u.port_num.port_number = 5;
> +	DO_TEST();
> +
> +	in.req_type = DP_ALLOCATE_PAYLOAD;
> +	in.u.allocate_payload.number_sdp_streams = 3;
> +	for (i = 0; i < in.u.allocate_payload.number_sdp_streams; i++)
> +		in.u.allocate_payload.sdp_stream_sink[i] = i + 1;
> +	DO_TEST();
> +	in.u.allocate_payload.port_number = 0xf;
> +	DO_TEST();
> +	in.u.allocate_payload.vcpi = 0x7f;
> +	DO_TEST();
> +	in.u.allocate_payload.pbn = U16_MAX;
> +	DO_TEST();
> +
> +	in.req_type = DP_QUERY_PAYLOAD;
> +	in.u.query_payload.port_number = 0xf;
> +	DO_TEST();
> +	in.u.query_payload.vcpi = 0x7f;
> +	DO_TEST();
> +
> +	in.req_type = DP_REMOTE_DPCD_READ;
> +	in.u.dpcd_read.port_number = 0xf;
> +	DO_TEST();
> +	in.u.dpcd_read.dpcd_address = 0xfedcb;
> +	DO_TEST();
> +	in.u.dpcd_read.num_bytes = U8_MAX;
> +	DO_TEST();
> +
> +	in.req_type = DP_REMOTE_DPCD_WRITE;
> +	in.u.dpcd_write.port_number = 0xf;
> +	DO_TEST();
> +	in.u.dpcd_write.dpcd_address = 0xfedcb;
> +	DO_TEST();
> +	in.u.dpcd_write.num_bytes = ARRAY_SIZE(data);
> +	in.u.dpcd_write.bytes = data;
> +	DO_TEST();
> +
> +	in.req_type = DP_REMOTE_I2C_READ;
> +	in.u.i2c_read.port_number = 0xf;
> +	DO_TEST();
> +	in.u.i2c_read.read_i2c_device_id = 0x7f;
> +	DO_TEST();
> +	in.u.i2c_read.num_transactions = 3;
> +	in.u.i2c_read.num_bytes_read = ARRAY_SIZE(data) * 3;
> +	for (i = 0; i < in.u.i2c_read.num_transactions; i++) {
> +		in.u.i2c_read.transactions[i].bytes = data;
> +		in.u.i2c_read.transactions[i].num_bytes = ARRAY_SIZE(data);
> +		in.u.i2c_read.transactions[i].i2c_dev_id = 0x7f & ~i;
> +		in.u.i2c_read.transactions[i].i2c_transaction_delay = 0xf & ~i;
> +	}
> +	DO_TEST();
> +
> +	in.req_type = DP_REMOTE_I2C_WRITE;
> +	in.u.i2c_write.port_number = 0xf;
> +	DO_TEST();
> +	in.u.i2c_write.write_i2c_device_id = 0x7f;
> +	DO_TEST();
> +	in.u.i2c_write.num_bytes = ARRAY_SIZE(data);
> +	in.u.i2c_write.bytes = data;
> +	DO_TEST();
> +
> +#undef DO_TEST
> +	return 0;
> +}
> diff --git a/drivers/gpu/drm/selftests/test-drm_modeset_common.h b/drivers/gpu/drm/selftests/test-drm_modeset_common.h
> index 590bda35a683..0fcb8bbc6a1b 100644
> --- a/drivers/gpu/drm/selftests/test-drm_modeset_common.h
> +++ b/drivers/gpu/drm/selftests/test-drm_modeset_common.h
> @@ -40,5 +40,6 @@ int igt_damage_iter_damage_one_outside(void *ignored);
>  int igt_damage_iter_damage_src_moved(void *ignored);
>  int igt_damage_iter_damage_not_visible(void *ignored);
>  int igt_dp_mst_calc_pbn_mode(void *ignored);
> +int igt_dp_mst_sideband_msg_req_decode(void *ignored);
>  
>  #endif
> diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
> index 4a4507fe928d..5423a8adda78 100644
> --- a/include/drm/drm_dp_mst_helper.h
> +++ b/include/drm/drm_dp_mst_helper.h
> @@ -293,7 +293,7 @@ struct drm_dp_remote_dpcd_write {
>  struct drm_dp_remote_i2c_read {
>  	u8 num_transactions;
>  	u8 port_number;
> -	struct {
> +	struct drm_dp_remote_i2c_read_tx {
>  		u8 i2c_dev_id;
>  		u8 num_bytes;
>  		u8 *bytes;

-- 
Jani Nikula, Intel Open Source Graphics Center

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 23/27] drm/amdgpu: Iterate through DRM connectors correctly
  2019-09-03 20:46 ` [PATCH v2 23/27] drm/amdgpu: Iterate through DRM connectors correctly Lyude Paul
@ 2019-09-13 20:45   ` Alex Deucher
  2019-09-27 13:48     ` Alex Deucher
  0 siblings, 1 reply; 62+ messages in thread
From: Alex Deucher @ 2019-09-13 20:45 UTC (permalink / raw)
  To: Lyude Paul
  Cc: Maling list - DRI developers, nouveau, amd-gfx list,
	Neil Armstrong, David Airlie, Daniel Vetter, Imre Deak, Tao Zhou,
	Huang Rui, Shirish S, Sam Ravnborg, Markus Elfring,
	Ville Syrjälä,
	David (ChunMing) Zhou, Mario Kleiner, Yu Zhao, Bhawanpreet Lakha,
	David Francis, Jani Nikula, Thierry Reding, Harry Wentland,
	Juston Li, Andrey Grodzovsky, Leo Li, Emily Deng, Russell King,
	Evan Quan, Harry Wentland, Felix Kuehling, xinhui pan,
	Michel Dänzer, LKML, Andrzej Pietrasiewicz, Daniel Vetter,
	Alex Deucher, Colin Ian King, Nicholas Kazlauskas, Rex Zhu,
	Christian König, Hawking Zhang

On Tue, Sep 3, 2019 at 4:49 PM Lyude Paul <lyude@redhat.com> wrote:
>
> Currently, every single piece of code in amdgpu that loops through
> connectors does it incorrectly and doesn't use the proper list iteration
> helpers, drm_connector_list_iter_begin() and
> drm_connector_list_iter_end(). Yeesh.
>
> So, do that.

In fairness, I think the origin of this code predated the iterators.
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>

>
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>
> ---
>  .../gpu/drm/amd/amdgpu/amdgpu_connectors.c    | 13 +++++-
>  drivers/gpu/drm/amd/amdgpu/amdgpu_device.c    | 20 +++++++---
>  drivers/gpu/drm/amd/amdgpu/amdgpu_display.c   |  5 ++-
>  drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c  | 40 +++++++++++++------
>  drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c       |  5 ++-
>  drivers/gpu/drm/amd/amdgpu/dce_v10_0.c        | 34 ++++++++++++----
>  drivers/gpu/drm/amd/amdgpu/dce_v11_0.c        | 34 ++++++++++++----
>  drivers/gpu/drm/amd/amdgpu/dce_v6_0.c         | 40 ++++++++++++++-----
>  drivers/gpu/drm/amd/amdgpu/dce_v8_0.c         | 34 ++++++++++++----
>  .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 33 ++++++++-------
>  .../drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c | 10 ++++-
>  11 files changed, 195 insertions(+), 73 deletions(-)
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
> index ece55c8fa673..bd31bb595c04 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
> @@ -1022,8 +1022,12 @@ amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force)
>                          */
>                         if (amdgpu_connector->shared_ddc && (ret == connector_status_connected)) {
>                                 struct drm_connector *list_connector;
> +                               struct drm_connector_list_iter iter;
>                                 struct amdgpu_connector *list_amdgpu_connector;
> -                               list_for_each_entry(list_connector, &dev->mode_config.connector_list, head) {
> +
> +                               drm_connector_list_iter_begin(dev, &iter);
> +                               drm_for_each_connector_iter(list_connector,
> +                                                           &iter) {
>                                         if (connector == list_connector)
>                                                 continue;
>                                         list_amdgpu_connector = to_amdgpu_connector(list_connector);
> @@ -1040,6 +1044,7 @@ amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force)
>                                                 }
>                                         }
>                                 }
> +                               drm_connector_list_iter_end(&iter);
>                         }
>                 }
>         }
> @@ -1501,6 +1506,7 @@ amdgpu_connector_add(struct amdgpu_device *adev,
>  {
>         struct drm_device *dev = adev->ddev;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         struct amdgpu_connector *amdgpu_connector;
>         struct amdgpu_connector_atom_dig *amdgpu_dig_connector;
>         struct drm_encoder *encoder;
> @@ -1515,10 +1521,12 @@ amdgpu_connector_add(struct amdgpu_device *adev,
>                 return;
>
>         /* see if we already added it */
> -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 amdgpu_connector = to_amdgpu_connector(connector);
>                 if (amdgpu_connector->connector_id == connector_id) {
>                         amdgpu_connector->devices |= supported_device;
> +                       drm_connector_list_iter_end(&iter);
>                         return;
>                 }
>                 if (amdgpu_connector->ddc_bus && i2c_bus->valid) {
> @@ -1533,6 +1541,7 @@ amdgpu_connector_add(struct amdgpu_device *adev,
>                         }
>                 }
>         }
> +       drm_connector_list_iter_end(&iter);
>
>         /* check if it's a dp bridge */
>         list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> index 2f884699eaef..acd39ce9b08e 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> @@ -3004,6 +3004,7 @@ int amdgpu_device_suspend(struct drm_device *dev, bool suspend, bool fbcon)
>         struct amdgpu_device *adev;
>         struct drm_crtc *crtc;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         int r;
>
>         if (dev == NULL || dev->dev_private == NULL) {
> @@ -3026,9 +3027,11 @@ int amdgpu_device_suspend(struct drm_device *dev, bool suspend, bool fbcon)
>         if (!amdgpu_device_has_dc_support(adev)) {
>                 /* turn off display hw */
>                 drm_modeset_lock_all(dev);
> -               list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> -                       drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
> -               }
> +               drm_connector_list_iter_begin(dev, &iter);
> +               drm_for_each_connector_iter(connector, &iter)
> +                       drm_helper_connector_dpms(connector,
> +                                                 DRM_MODE_DPMS_OFF);
> +               drm_connector_list_iter_end(&iter);
>                 drm_modeset_unlock_all(dev);
>                         /* unpin the front buffers and cursors */
>                 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
> @@ -3107,6 +3110,7 @@ int amdgpu_device_suspend(struct drm_device *dev, bool suspend, bool fbcon)
>  int amdgpu_device_resume(struct drm_device *dev, bool resume, bool fbcon)
>  {
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         struct amdgpu_device *adev = dev->dev_private;
>         struct drm_crtc *crtc;
>         int r = 0;
> @@ -3177,9 +3181,13 @@ int amdgpu_device_resume(struct drm_device *dev, bool resume, bool fbcon)
>
>                         /* turn on display hw */
>                         drm_modeset_lock_all(dev);
> -                       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> -                               drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
> -                       }
> +
> +                       drm_connector_list_iter_begin(dev, &iter);
> +                       drm_for_each_connector_iter(connector, &iter)
> +                               drm_helper_connector_dpms(connector,
> +                                                         DRM_MODE_DPMS_ON);
> +                       drm_connector_list_iter_end(&iter);
> +
>                         drm_modeset_unlock_all(dev);
>                 }
>                 amdgpu_fbdev_set_suspend(adev, 0);
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
> index 1d4aaa9580f4..d2dd59a95e8a 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
> @@ -370,11 +370,13 @@ void amdgpu_display_print_display_setup(struct drm_device *dev)
>         struct amdgpu_connector *amdgpu_connector;
>         struct drm_encoder *encoder;
>         struct amdgpu_encoder *amdgpu_encoder;
> +       struct drm_connector_list_iter iter;
>         uint32_t devices;
>         int i = 0;
>
> +       drm_connector_list_iter_begin(dev, &iter);
>         DRM_INFO("AMDGPU Display Connectors\n");
> -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +       drm_for_each_connector_iter(connector, &iter) {
>                 amdgpu_connector = to_amdgpu_connector(connector);
>                 DRM_INFO("Connector %d:\n", i);
>                 DRM_INFO("  %s\n", connector->name);
> @@ -438,6 +440,7 @@ void amdgpu_display_print_display_setup(struct drm_device *dev)
>                 }
>                 i++;
>         }
> +       drm_connector_list_iter_end(&iter);
>  }
>
>  /**
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c
> index 571a6dfb473e..61fcf247a638 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c
> @@ -37,12 +37,14 @@ amdgpu_link_encoder_connector(struct drm_device *dev)
>  {
>         struct amdgpu_device *adev = dev->dev_private;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         struct amdgpu_connector *amdgpu_connector;
>         struct drm_encoder *encoder;
>         struct amdgpu_encoder *amdgpu_encoder;
>
> +       drm_connector_list_iter_begin(dev, &iter);
>         /* walk the list and link encoders to connectors */
> -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +       drm_for_each_connector_iter(connector, &iter) {
>                 amdgpu_connector = to_amdgpu_connector(connector);
>                 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
>                         amdgpu_encoder = to_amdgpu_encoder(encoder);
> @@ -55,6 +57,7 @@ amdgpu_link_encoder_connector(struct drm_device *dev)
>                         }
>                 }
>         }
> +       drm_connector_list_iter_end(&iter);
>  }
>
>  void amdgpu_encoder_set_active_device(struct drm_encoder *encoder)
> @@ -62,8 +65,10 @@ void amdgpu_encoder_set_active_device(struct drm_encoder *encoder)
>         struct drm_device *dev = encoder->dev;
>         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>
> -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 if (connector->encoder == encoder) {
>                         struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
>                         amdgpu_encoder->active_device = amdgpu_encoder->devices & amdgpu_connector->devices;
> @@ -72,6 +77,7 @@ void amdgpu_encoder_set_active_device(struct drm_encoder *encoder)
>                                   amdgpu_connector->devices, encoder->encoder_type);
>                 }
>         }
> +       drm_connector_list_iter_end(&iter);
>  }
>
>  struct drm_connector *
> @@ -79,15 +85,20 @@ amdgpu_get_connector_for_encoder(struct drm_encoder *encoder)
>  {
>         struct drm_device *dev = encoder->dev;
>         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
> -       struct drm_connector *connector;
> +       struct drm_connector *connector, *found = NULL;
> +       struct drm_connector_list_iter iter;
>         struct amdgpu_connector *amdgpu_connector;
>
> -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 amdgpu_connector = to_amdgpu_connector(connector);
> -               if (amdgpu_encoder->active_device & amdgpu_connector->devices)
> -                       return connector;
> +               if (amdgpu_encoder->active_device & amdgpu_connector->devices) {
> +                       found = connector;
> +                       break;
> +               }
>         }
> -       return NULL;
> +       drm_connector_list_iter_end(&iter);
> +       return found;
>  }
>
>  struct drm_connector *
> @@ -95,15 +106,20 @@ amdgpu_get_connector_for_encoder_init(struct drm_encoder *encoder)
>  {
>         struct drm_device *dev = encoder->dev;
>         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
> -       struct drm_connector *connector;
> +       struct drm_connector *connector, *found = NULL;
> +       struct drm_connector_list_iter iter;
>         struct amdgpu_connector *amdgpu_connector;
>
> -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 amdgpu_connector = to_amdgpu_connector(connector);
> -               if (amdgpu_encoder->devices & amdgpu_connector->devices)
> -                       return connector;
> +               if (amdgpu_encoder->devices & amdgpu_connector->devices) {
> +                       found = connector;
> +                       break;
> +               }
>         }
> -       return NULL;
> +       drm_connector_list_iter_end(&iter);
> +       return found;
>  }
>
>  struct drm_encoder *amdgpu_get_external_encoder(struct drm_encoder *encoder)
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
> index 2a3f5ec298db..977e121204e6 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
> @@ -87,10 +87,13 @@ static void amdgpu_hotplug_work_func(struct work_struct *work)
>         struct drm_device *dev = adev->ddev;
>         struct drm_mode_config *mode_config = &dev->mode_config;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>
>         mutex_lock(&mode_config->mutex);
> -       list_for_each_entry(connector, &mode_config->connector_list, head)
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter)
>                 amdgpu_connector_hotplug(connector);
> +       drm_connector_list_iter_end(&iter);
>         mutex_unlock(&mode_config->mutex);
>         /* Just fire off a uevent and let userspace tell us what to do */
>         drm_helper_hpd_irq_event(dev);
> diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
> index 645550e7caf5..be82871ac3bd 100644
> --- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
> +++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
> @@ -330,9 +330,11 @@ static void dce_v10_0_hpd_init(struct amdgpu_device *adev)
>  {
>         struct drm_device *dev = adev->ddev;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         u32 tmp;
>
> -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
>
>                 if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
> @@ -368,6 +370,7 @@ static void dce_v10_0_hpd_init(struct amdgpu_device *adev)
>                 amdgpu_irq_get(adev, &adev->hpd_irq,
>                                amdgpu_connector->hpd.hpd);
>         }
> +       drm_connector_list_iter_end(&iter);
>  }
>
>  /**
> @@ -382,9 +385,11 @@ static void dce_v10_0_hpd_fini(struct amdgpu_device *adev)
>  {
>         struct drm_device *dev = adev->ddev;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         u32 tmp;
>
> -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
>
>                 if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
> @@ -397,6 +402,7 @@ static void dce_v10_0_hpd_fini(struct amdgpu_device *adev)
>                 amdgpu_irq_put(adev, &adev->hpd_irq,
>                                amdgpu_connector->hpd.hpd);
>         }
> +       drm_connector_list_iter_end(&iter);
>  }
>
>  static u32 dce_v10_0_hpd_get_gpio_reg(struct amdgpu_device *adev)
> @@ -1219,10 +1225,12 @@ static void dce_v10_0_afmt_audio_select_pin(struct drm_encoder *encoder)
>  static void dce_v10_0_audio_write_latency_fields(struct drm_encoder *encoder,
>                                                 struct drm_display_mode *mode)
>  {
> -       struct amdgpu_device *adev = encoder->dev->dev_private;
> +       struct drm_device *dev = encoder->dev;
> +       struct amdgpu_device *adev = dev->dev_private;
>         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
>         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         struct amdgpu_connector *amdgpu_connector = NULL;
>         u32 tmp;
>         int interlace = 0;
> @@ -1230,12 +1238,14 @@ static void dce_v10_0_audio_write_latency_fields(struct drm_encoder *encoder,
>         if (!dig || !dig->afmt || !dig->afmt->pin)
>                 return;
>
> -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 if (connector->encoder == encoder) {
>                         amdgpu_connector = to_amdgpu_connector(connector);
>                         break;
>                 }
>         }
> +       drm_connector_list_iter_end(&iter);
>
>         if (!amdgpu_connector) {
>                 DRM_ERROR("Couldn't find encoder's connector\n");
> @@ -1261,10 +1271,12 @@ static void dce_v10_0_audio_write_latency_fields(struct drm_encoder *encoder,
>
>  static void dce_v10_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
>  {
> -       struct amdgpu_device *adev = encoder->dev->dev_private;
> +       struct drm_device *dev = encoder->dev;
> +       struct amdgpu_device *adev = dev->dev_private;
>         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
>         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         struct amdgpu_connector *amdgpu_connector = NULL;
>         u32 tmp;
>         u8 *sadb = NULL;
> @@ -1273,12 +1285,14 @@ static void dce_v10_0_audio_write_speaker_allocation(struct drm_encoder *encoder
>         if (!dig || !dig->afmt || !dig->afmt->pin)
>                 return;
>
> -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 if (connector->encoder == encoder) {
>                         amdgpu_connector = to_amdgpu_connector(connector);
>                         break;
>                 }
>         }
> +       drm_connector_list_iter_end(&iter);
>
>         if (!amdgpu_connector) {
>                 DRM_ERROR("Couldn't find encoder's connector\n");
> @@ -1313,10 +1327,12 @@ static void dce_v10_0_audio_write_speaker_allocation(struct drm_encoder *encoder
>
>  static void dce_v10_0_audio_write_sad_regs(struct drm_encoder *encoder)
>  {
> -       struct amdgpu_device *adev = encoder->dev->dev_private;
> +       struct drm_device *dev = encoder->dev;
> +       struct amdgpu_device *adev = dev->dev_private;
>         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
>         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         struct amdgpu_connector *amdgpu_connector = NULL;
>         struct cea_sad *sads;
>         int i, sad_count;
> @@ -1339,12 +1355,14 @@ static void dce_v10_0_audio_write_sad_regs(struct drm_encoder *encoder)
>         if (!dig || !dig->afmt || !dig->afmt->pin)
>                 return;
>
> -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 if (connector->encoder == encoder) {
>                         amdgpu_connector = to_amdgpu_connector(connector);
>                         break;
>                 }
>         }
> +       drm_connector_list_iter_end(&iter);
>
>         if (!amdgpu_connector) {
>                 DRM_ERROR("Couldn't find encoder's connector\n");
> diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
> index d9f470632b2c..bde48775cf1b 100644
> --- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
> +++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
> @@ -348,9 +348,11 @@ static void dce_v11_0_hpd_init(struct amdgpu_device *adev)
>  {
>         struct drm_device *dev = adev->ddev;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         u32 tmp;
>
> -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
>
>                 if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
> @@ -385,6 +387,7 @@ static void dce_v11_0_hpd_init(struct amdgpu_device *adev)
>                 dce_v11_0_hpd_set_polarity(adev, amdgpu_connector->hpd.hpd);
>                 amdgpu_irq_get(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
>         }
> +       drm_connector_list_iter_end(&iter);
>  }
>
>  /**
> @@ -399,9 +402,11 @@ static void dce_v11_0_hpd_fini(struct amdgpu_device *adev)
>  {
>         struct drm_device *dev = adev->ddev;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         u32 tmp;
>
> -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
>
>                 if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
> @@ -413,6 +418,7 @@ static void dce_v11_0_hpd_fini(struct amdgpu_device *adev)
>
>                 amdgpu_irq_put(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
>         }
> +       drm_connector_list_iter_end(&iter);
>  }
>
>  static u32 dce_v11_0_hpd_get_gpio_reg(struct amdgpu_device *adev)
> @@ -1245,10 +1251,12 @@ static void dce_v11_0_afmt_audio_select_pin(struct drm_encoder *encoder)
>  static void dce_v11_0_audio_write_latency_fields(struct drm_encoder *encoder,
>                                                 struct drm_display_mode *mode)
>  {
> -       struct amdgpu_device *adev = encoder->dev->dev_private;
> +       struct drm_device *dev = encoder->dev;
> +       struct amdgpu_device *adev = dev->dev_private;
>         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
>         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         struct amdgpu_connector *amdgpu_connector = NULL;
>         u32 tmp;
>         int interlace = 0;
> @@ -1256,12 +1264,14 @@ static void dce_v11_0_audio_write_latency_fields(struct drm_encoder *encoder,
>         if (!dig || !dig->afmt || !dig->afmt->pin)
>                 return;
>
> -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 if (connector->encoder == encoder) {
>                         amdgpu_connector = to_amdgpu_connector(connector);
>                         break;
>                 }
>         }
> +       drm_connector_list_iter_end(&iter);
>
>         if (!amdgpu_connector) {
>                 DRM_ERROR("Couldn't find encoder's connector\n");
> @@ -1287,10 +1297,12 @@ static void dce_v11_0_audio_write_latency_fields(struct drm_encoder *encoder,
>
>  static void dce_v11_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
>  {
> -       struct amdgpu_device *adev = encoder->dev->dev_private;
> +       struct drm_device *dev = encoder->dev;
> +       struct amdgpu_device *adev = dev->dev_private;
>         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
>         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         struct amdgpu_connector *amdgpu_connector = NULL;
>         u32 tmp;
>         u8 *sadb = NULL;
> @@ -1299,12 +1311,14 @@ static void dce_v11_0_audio_write_speaker_allocation(struct drm_encoder *encoder
>         if (!dig || !dig->afmt || !dig->afmt->pin)
>                 return;
>
> -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 if (connector->encoder == encoder) {
>                         amdgpu_connector = to_amdgpu_connector(connector);
>                         break;
>                 }
>         }
> +       drm_connector_list_iter_end(&iter);
>
>         if (!amdgpu_connector) {
>                 DRM_ERROR("Couldn't find encoder's connector\n");
> @@ -1339,10 +1353,12 @@ static void dce_v11_0_audio_write_speaker_allocation(struct drm_encoder *encoder
>
>  static void dce_v11_0_audio_write_sad_regs(struct drm_encoder *encoder)
>  {
> -       struct amdgpu_device *adev = encoder->dev->dev_private;
> +       struct drm_device *dev = encoder->dev;
> +       struct amdgpu_device *adev = dev->dev_private;
>         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
>         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         struct amdgpu_connector *amdgpu_connector = NULL;
>         struct cea_sad *sads;
>         int i, sad_count;
> @@ -1365,12 +1381,14 @@ static void dce_v11_0_audio_write_sad_regs(struct drm_encoder *encoder)
>         if (!dig || !dig->afmt || !dig->afmt->pin)
>                 return;
>
> -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 if (connector->encoder == encoder) {
>                         amdgpu_connector = to_amdgpu_connector(connector);
>                         break;
>                 }
>         }
> +       drm_connector_list_iter_end(&iter);
>
>         if (!amdgpu_connector) {
>                 DRM_ERROR("Couldn't find encoder's connector\n");
> diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
> index 3eb2e7429269..65f61de931d7 100644
> --- a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
> +++ b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
> @@ -281,9 +281,11 @@ static void dce_v6_0_hpd_init(struct amdgpu_device *adev)
>  {
>         struct drm_device *dev = adev->ddev;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         u32 tmp;
>
> -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
>
>                 if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
> @@ -309,7 +311,7 @@ static void dce_v6_0_hpd_init(struct amdgpu_device *adev)
>                 dce_v6_0_hpd_set_polarity(adev, amdgpu_connector->hpd.hpd);
>                 amdgpu_irq_get(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
>         }
> -
> +       drm_connector_list_iter_end(&iter);
>  }
>
>  /**
> @@ -324,9 +326,11 @@ static void dce_v6_0_hpd_fini(struct amdgpu_device *adev)
>  {
>         struct drm_device *dev = adev->ddev;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         u32 tmp;
>
> -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
>
>                 if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
> @@ -338,6 +342,7 @@ static void dce_v6_0_hpd_fini(struct amdgpu_device *adev)
>
>                 amdgpu_irq_put(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
>         }
> +       drm_connector_list_iter_end(&iter);
>  }
>
>  static u32 dce_v6_0_hpd_get_gpio_reg(struct amdgpu_device *adev)
> @@ -1124,20 +1129,24 @@ static void dce_v6_0_audio_select_pin(struct drm_encoder *encoder)
>  static void dce_v6_0_audio_write_latency_fields(struct drm_encoder *encoder,
>                                                 struct drm_display_mode *mode)
>  {
> -       struct amdgpu_device *adev = encoder->dev->dev_private;
> +       struct drm_device *dev = encoder->dev;
> +       struct amdgpu_device *adev = dev->dev_private;
>         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
>         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         struct amdgpu_connector *amdgpu_connector = NULL;
>         int interlace = 0;
>         u32 tmp;
>
> -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 if (connector->encoder == encoder) {
>                         amdgpu_connector = to_amdgpu_connector(connector);
>                         break;
>                 }
>         }
> +       drm_connector_list_iter_end(&iter);
>
>         if (!amdgpu_connector) {
>                 DRM_ERROR("Couldn't find encoder's connector\n");
> @@ -1164,21 +1173,25 @@ static void dce_v6_0_audio_write_latency_fields(struct drm_encoder *encoder,
>
>  static void dce_v6_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
>  {
> -       struct amdgpu_device *adev = encoder->dev->dev_private;
> +       struct drm_device *dev = encoder->dev;
> +       struct amdgpu_device *adev = dev->dev_private;
>         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
>         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         struct amdgpu_connector *amdgpu_connector = NULL;
>         u8 *sadb = NULL;
>         int sad_count;
>         u32 tmp;
>
> -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 if (connector->encoder == encoder) {
>                         amdgpu_connector = to_amdgpu_connector(connector);
>                         break;
>                 }
>         }
> +       drm_connector_list_iter_end(&iter);
>
>         if (!amdgpu_connector) {
>                 DRM_ERROR("Couldn't find encoder's connector\n");
> @@ -1221,10 +1234,12 @@ static void dce_v6_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
>
>  static void dce_v6_0_audio_write_sad_regs(struct drm_encoder *encoder)
>  {
> -       struct amdgpu_device *adev = encoder->dev->dev_private;
> +       struct drm_device *dev = encoder->dev;
> +       struct amdgpu_device *adev = dev->dev_private;
>         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
>         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         struct amdgpu_connector *amdgpu_connector = NULL;
>         struct cea_sad *sads;
>         int i, sad_count;
> @@ -1244,12 +1259,14 @@ static void dce_v6_0_audio_write_sad_regs(struct drm_encoder *encoder)
>                 { ixAZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR13, HDMI_AUDIO_CODING_TYPE_WMA_PRO },
>         };
>
> -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 if (connector->encoder == encoder) {
>                         amdgpu_connector = to_amdgpu_connector(connector);
>                         break;
>                 }
>         }
> +       drm_connector_list_iter_end(&iter);
>
>         if (!amdgpu_connector) {
>                 DRM_ERROR("Couldn't find encoder's connector\n");
> @@ -1632,6 +1649,7 @@ static void dce_v6_0_afmt_setmode(struct drm_encoder *encoder,
>         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
>         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         struct amdgpu_connector *amdgpu_connector = NULL;
>         int em = amdgpu_atombios_encoder_get_encoder_mode(encoder);
>         int bpc = 8;
> @@ -1639,12 +1657,14 @@ static void dce_v6_0_afmt_setmode(struct drm_encoder *encoder,
>         if (!dig || !dig->afmt)
>                 return;
>
> -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 if (connector->encoder == encoder) {
>                         amdgpu_connector = to_amdgpu_connector(connector);
>                         break;
>                 }
>         }
> +       drm_connector_list_iter_end(&iter);
>
>         if (!amdgpu_connector) {
>                 DRM_ERROR("Couldn't find encoder's connector\n");
> diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
> index a16c5e9e610e..e5f50882a51d 100644
> --- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
> +++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
> @@ -275,9 +275,11 @@ static void dce_v8_0_hpd_init(struct amdgpu_device *adev)
>  {
>         struct drm_device *dev = adev->ddev;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         u32 tmp;
>
> -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
>
>                 if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
> @@ -303,6 +305,7 @@ static void dce_v8_0_hpd_init(struct amdgpu_device *adev)
>                 dce_v8_0_hpd_set_polarity(adev, amdgpu_connector->hpd.hpd);
>                 amdgpu_irq_get(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
>         }
> +       drm_connector_list_iter_end(&iter);
>  }
>
>  /**
> @@ -317,9 +320,11 @@ static void dce_v8_0_hpd_fini(struct amdgpu_device *adev)
>  {
>         struct drm_device *dev = adev->ddev;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         u32 tmp;
>
> -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
>
>                 if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
> @@ -331,6 +336,7 @@ static void dce_v8_0_hpd_fini(struct amdgpu_device *adev)
>
>                 amdgpu_irq_put(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
>         }
> +       drm_connector_list_iter_end(&iter);
>  }
>
>  static u32 dce_v8_0_hpd_get_gpio_reg(struct amdgpu_device *adev)
> @@ -1157,10 +1163,12 @@ static void dce_v8_0_afmt_audio_select_pin(struct drm_encoder *encoder)
>  static void dce_v8_0_audio_write_latency_fields(struct drm_encoder *encoder,
>                                                 struct drm_display_mode *mode)
>  {
> -       struct amdgpu_device *adev = encoder->dev->dev_private;
> +       struct drm_device *dev = encoder->dev;
> +       struct amdgpu_device *adev = dev->dev_private;
>         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
>         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         struct amdgpu_connector *amdgpu_connector = NULL;
>         u32 tmp = 0, offset;
>
> @@ -1169,12 +1177,14 @@ static void dce_v8_0_audio_write_latency_fields(struct drm_encoder *encoder,
>
>         offset = dig->afmt->pin->offset;
>
> -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 if (connector->encoder == encoder) {
>                         amdgpu_connector = to_amdgpu_connector(connector);
>                         break;
>                 }
>         }
> +       drm_connector_list_iter_end(&iter);
>
>         if (!amdgpu_connector) {
>                 DRM_ERROR("Couldn't find encoder's connector\n");
> @@ -1214,10 +1224,12 @@ static void dce_v8_0_audio_write_latency_fields(struct drm_encoder *encoder,
>
>  static void dce_v8_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
>  {
> -       struct amdgpu_device *adev = encoder->dev->dev_private;
> +       struct drm_device *dev = encoder->dev;
> +       struct amdgpu_device *adev = dev->dev_private;
>         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
>         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         struct amdgpu_connector *amdgpu_connector = NULL;
>         u32 offset, tmp;
>         u8 *sadb = NULL;
> @@ -1228,12 +1240,14 @@ static void dce_v8_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
>
>         offset = dig->afmt->pin->offset;
>
> -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 if (connector->encoder == encoder) {
>                         amdgpu_connector = to_amdgpu_connector(connector);
>                         break;
>                 }
>         }
> +       drm_connector_list_iter_end(&iter);
>
>         if (!amdgpu_connector) {
>                 DRM_ERROR("Couldn't find encoder's connector\n");
> @@ -1263,11 +1277,13 @@ static void dce_v8_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
>
>  static void dce_v8_0_audio_write_sad_regs(struct drm_encoder *encoder)
>  {
> -       struct amdgpu_device *adev = encoder->dev->dev_private;
> +       struct drm_device *dev = encoder->dev;
> +       struct amdgpu_device *adev = dev->dev_private;
>         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
>         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
>         u32 offset;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         struct amdgpu_connector *amdgpu_connector = NULL;
>         struct cea_sad *sads;
>         int i, sad_count;
> @@ -1292,12 +1308,14 @@ static void dce_v8_0_audio_write_sad_regs(struct drm_encoder *encoder)
>
>         offset = dig->afmt->pin->offset;
>
> -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 if (connector->encoder == encoder) {
>                         amdgpu_connector = to_amdgpu_connector(connector);
>                         break;
>                 }
>         }
> +       drm_connector_list_iter_end(&iter);
>
>         if (!amdgpu_connector) {
>                 DRM_ERROR("Couldn't find encoder's connector\n");
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> index 0a71ed1e7762..73630e2940d4 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> @@ -896,27 +896,29 @@ static int detect_mst_link_for_all_connectors(struct drm_device *dev)
>  {
>         struct amdgpu_dm_connector *aconnector;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         int ret = 0;
>
> -       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> -
> -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 aconnector = to_amdgpu_dm_connector(connector);
>                 if (aconnector->dc_link->type == dc_connection_mst_branch &&
>                     aconnector->mst_mgr.aux) {
>                         DRM_DEBUG_DRIVER("DM_MST: starting TM on aconnector: %p [id: %d]\n",
> -                                       aconnector, aconnector->base.base.id);
> +                                        aconnector,
> +                                        aconnector->base.base.id);
>
>                         ret = drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_mgr, true);
>                         if (ret < 0) {
>                                 DRM_ERROR("DM_MST: Failed to start MST\n");
> -                               ((struct dc_link *)aconnector->dc_link)->type = dc_connection_single;
> -                               return ret;
> -                               }
> +                               aconnector->dc_link->type =
> +                                       dc_connection_single;
> +                               break;
>                         }
> +               }
>         }
> +       drm_connector_list_iter_end(&iter);
>
> -       drm_modeset_unlock(&dev->mode_config.connection_mutex);
>         return ret;
>  }
>
> @@ -954,14 +956,13 @@ static void s3_handle_mst(struct drm_device *dev, bool suspend)
>  {
>         struct amdgpu_dm_connector *aconnector;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         struct drm_dp_mst_topology_mgr *mgr;
>         int ret;
>         bool need_hotplug = false;
>
> -       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> -
> -       list_for_each_entry(connector, &dev->mode_config.connector_list,
> -                           head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 aconnector = to_amdgpu_dm_connector(connector);
>                 if (aconnector->dc_link->type != dc_connection_mst_branch ||
>                     aconnector->mst_port)
> @@ -979,8 +980,7 @@ static void s3_handle_mst(struct drm_device *dev, bool suspend)
>                         }
>                 }
>         }
> -
> -       drm_modeset_unlock(&dev->mode_config.connection_mutex);
> +       drm_connector_list_iter_end(&iter);
>
>         if (need_hotplug)
>                 drm_kms_helper_hotplug_event(dev);
> @@ -1162,6 +1162,7 @@ static int dm_resume(void *handle)
>         struct amdgpu_display_manager *dm = &adev->dm;
>         struct amdgpu_dm_connector *aconnector;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>         struct drm_crtc *crtc;
>         struct drm_crtc_state *new_crtc_state;
>         struct dm_crtc_state *dm_new_crtc_state;
> @@ -1194,7 +1195,8 @@ static int dm_resume(void *handle)
>         amdgpu_dm_irq_resume_early(adev);
>
>         /* Do detection*/
> -       list_for_each_entry(connector, &ddev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(ddev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 aconnector = to_amdgpu_dm_connector(connector);
>
>                 /*
> @@ -1222,6 +1224,7 @@ static int dm_resume(void *handle)
>                 amdgpu_dm_update_connector_after_detect(aconnector);
>                 mutex_unlock(&aconnector->hpd_lock);
>         }
> +       drm_connector_list_iter_end(&iter);
>
>         /* Force mode set in atomic commit */
>         for_each_new_crtc_in_state(dm->cached_state, crtc, new_crtc_state, i)
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
> index fa5d503d379c..64445c4cc4c2 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
> @@ -732,8 +732,10 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
>  {
>         struct drm_device *dev = adev->ddev;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>
> -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 struct amdgpu_dm_connector *amdgpu_dm_connector =
>                                 to_amdgpu_dm_connector(connector);
>
> @@ -751,6 +753,7 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
>                                         true);
>                 }
>         }
> +       drm_connector_list_iter_end(&iter);
>  }
>
>  /**
> @@ -765,8 +768,10 @@ void amdgpu_dm_hpd_fini(struct amdgpu_device *adev)
>  {
>         struct drm_device *dev = adev->ddev;
>         struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
>
> -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +       drm_connector_list_iter_begin(dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter) {
>                 struct amdgpu_dm_connector *amdgpu_dm_connector =
>                                 to_amdgpu_dm_connector(connector);
>                 const struct dc_link *dc_link = amdgpu_dm_connector->dc_link;
> @@ -779,4 +784,5 @@ void amdgpu_dm_hpd_fini(struct amdgpu_device *adev)
>                                         false);
>                 }
>         }
> +       drm_connector_list_iter_end(&iter);
>  }
> --
> 2.21.0
>
> _______________________________________________
> amd-gfx mailing list
> amd-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 24/27] drm/amdgpu/dm: Resume short HPD IRQs before resuming MST topology
  2019-09-03 20:46 ` [PATCH v2 24/27] drm/amdgpu/dm: Resume short HPD IRQs before resuming MST topology Lyude Paul
@ 2019-09-13 20:46   ` Alex Deucher
  2019-09-25 20:08   ` Sean Paul
  2019-09-25 21:52   ` [PATCH v4] " Lyude Paul
  2 siblings, 0 replies; 62+ messages in thread
From: Alex Deucher @ 2019-09-13 20:46 UTC (permalink / raw)
  To: Lyude Paul
  Cc: Maling list - DRI developers, nouveau, amd-gfx list, Leo Li,
	Daniel Vetter, LKML, Nicholas Kazlauskas, Bhawanpreet Lakha,
	David Airlie, Juston Li, Alex Deucher, David Francis,
	Harry Wentland, Christian König

On Tue, Sep 3, 2019 at 4:49 PM Lyude Paul <lyude@redhat.com> wrote:
>
> Since we're going to be reprobing the entire topology state on resume
> now using sideband transactions, we need to ensure that we actually have
> short HPD irqs enabled before calling drm_dp_mst_topology_mgr_resume().
> So, do that.
>
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>

Acked-by: Alex Deucher <alexander.deucher@amd.com>

> ---
>  drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> index 73630e2940d4..4d3c8bff77da 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> @@ -1185,15 +1185,15 @@ static int dm_resume(void *handle)
>         /* program HPD filter */
>         dc_resume(dm->dc);
>
> -       /* On resume we need to  rewrite the MSTM control bits to enamble MST*/
> -       s3_handle_mst(ddev, false);
> -
>         /*
>          * early enable HPD Rx IRQ, should be done before set mode as short
>          * pulse interrupts are used for MST
>          */
>         amdgpu_dm_irq_resume_early(adev);
>
> +       /* On resume we need to  rewrite the MSTM control bits to enamble MST*/
> +       s3_handle_mst(ddev, false);
> +
>         /* Do detection*/
>         drm_connector_list_iter_begin(ddev, &iter);
>         drm_for_each_connector_iter(connector, &iter) {
> --
> 2.21.0
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 01/27] drm/dp_mst: Move link address dumping into a function
  2019-09-03 20:45 ` [PATCH v2 01/27] drm/dp_mst: Move link address dumping into a function Lyude Paul
@ 2019-09-25 17:45   ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2019-09-25 17:45 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, linux-kernel

On Tue, Sep 03, 2019 at 04:45:39PM -0400, Lyude Paul wrote:
> Makes things easier to read.
> 
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Reviewed-by: Daniel Vetter <daniel@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>

Reviewed-by: Sean Paul <sean@poorly.run>

> ---
>  drivers/gpu/drm/drm_dp_mst_topology.c | 35 ++++++++++++++++++---------
>  1 file changed, 23 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
> index 82add736e17d..36db66a0ddb1 100644
> --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> @@ -2103,6 +2103,28 @@ static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr,
>  	mutex_unlock(&mgr->qlock);
>  }
>  
> +static void
> +drm_dp_dump_link_address(struct drm_dp_link_address_ack_reply *reply)
> +{
> +	struct drm_dp_link_addr_reply_port *port_reply;
> +	int i;
> +
> +	for (i = 0; i < reply->nports; i++) {
> +		port_reply = &reply->ports[i];
> +		DRM_DEBUG_KMS("port %d: input %d, pdt: %d, pn: %d, dpcd_rev: %02x, mcs: %d, ddps: %d, ldps %d, sdp %d/%d\n",
> +			      i,
> +			      port_reply->input_port,
> +			      port_reply->peer_device_type,
> +			      port_reply->port_number,
> +			      port_reply->dpcd_revision,
> +			      port_reply->mcs,
> +			      port_reply->ddps,
> +			      port_reply->legacy_device_plug_status,
> +			      port_reply->num_sdp_streams,
> +			      port_reply->num_sdp_stream_sinks);
> +	}
> +}
> +
>  static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
>  				     struct drm_dp_mst_branch *mstb)
>  {
> @@ -2128,18 +2150,7 @@ static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
>  			DRM_DEBUG_KMS("link address nak received\n");
>  		} else {
>  			DRM_DEBUG_KMS("link address reply: %d\n", txmsg->reply.u.link_addr.nports);
> -			for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) {
> -				DRM_DEBUG_KMS("port %d: input %d, pdt: %d, pn: %d, dpcd_rev: %02x, mcs: %d, ddps: %d, ldps %d, sdp %d/%d\n", i,
> -				       txmsg->reply.u.link_addr.ports[i].input_port,
> -				       txmsg->reply.u.link_addr.ports[i].peer_device_type,
> -				       txmsg->reply.u.link_addr.ports[i].port_number,
> -				       txmsg->reply.u.link_addr.ports[i].dpcd_revision,
> -				       txmsg->reply.u.link_addr.ports[i].mcs,
> -				       txmsg->reply.u.link_addr.ports[i].ddps,
> -				       txmsg->reply.u.link_addr.ports[i].legacy_device_plug_status,
> -				       txmsg->reply.u.link_addr.ports[i].num_sdp_streams,
> -				       txmsg->reply.u.link_addr.ports[i].num_sdp_stream_sinks);
> -			}
> +			drm_dp_dump_link_address(&txmsg->reply.u.link_addr);
>  
>  			drm_dp_check_mstb_guid(mstb, txmsg->reply.u.link_addr.guid);
>  
> -- 
> 2.21.0
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 02/27] drm/dp_mst: Get rid of list clear in destroy_connector_work
  2019-09-03 20:45 ` [PATCH v2 02/27] drm/dp_mst: Get rid of list clear in destroy_connector_work Lyude Paul
@ 2019-09-25 17:45   ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2019-09-25 17:45 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

On Tue, Sep 03, 2019 at 04:45:40PM -0400, Lyude Paul wrote:
> This seems to be some leftover detritus from before the port/mstb kref
> cleanup and doesn't do anything anymore, so get rid of it.
> 
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>

Reviewed-by: Sean Paul <sean@poorly.run>

> ---
>  drivers/gpu/drm/drm_dp_mst_topology.c | 2 --
>  1 file changed, 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
> index 36db66a0ddb1..3054ec622506 100644
> --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> @@ -3760,8 +3760,6 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)
>  		list_del(&port->next);
>  		mutex_unlock(&mgr->destroy_connector_lock);
>  
> -		INIT_LIST_HEAD(&port->next);
> -
>  		mgr->cbs->destroy_connector(mgr, port->connector);
>  
>  		drm_dp_port_teardown_pdt(port, port->pdt);
> -- 
> 2.21.0
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 03/27] drm/dp_mst: Destroy MSTBs asynchronously
  2019-09-03 20:45 ` [PATCH v2 03/27] drm/dp_mst: Destroy MSTBs asynchronously Lyude Paul
@ 2019-09-25 18:16   ` Sean Paul
  2019-09-25 20:08     ` Lyude Paul
  0 siblings, 1 reply; 62+ messages in thread
From: Sean Paul @ 2019-09-25 18:16 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, linux-kernel

On Tue, Sep 03, 2019 at 04:45:41PM -0400, Lyude Paul wrote:
> When reprobing an MST topology during resume, we have to account for the
> fact that while we were suspended it's possible that mstbs may have been
> removed from any ports in the topology. Since iterating downwards in the
> topology requires that we hold &mgr->lock, destroying MSTBs from this
> context would result in attempting to lock &mgr->lock a second time and
> deadlocking.
> 
> So, fix this by first moving destruction of MSTBs into
> destroy_connector_work, then rename destroy_connector_work and friends
> to reflect that they now destroy both ports and mstbs.
> 
> Changes since v1:
> * s/destroy_connector_list/destroy_port_list/
>   s/connector_destroy_lock/delayed_destroy_lock/
>   s/connector_destroy_work/delayed_destroy_work/
>   s/drm_dp_finish_destroy_branch_device/drm_dp_delayed_destroy_mstb/
>   s/drm_dp_finish_destroy_port/drm_dp_delayed_destroy_port/
>   - danvet
> * Use two loops in drm_dp_delayed_destroy_work() - danvet
> * Better explain why we need to do this - danvet
> * Use cancel_work_sync() instead of flush_work() - flush_work() doesn't
>   account for work requeing
> 
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>

Took me a while to grok this, and I'm still not 100% confident my mental model
is correct, so please bear with me while I ask silly questions :)

Now that the destroy is delayed, and the port remains in the topology, is it
possible we will underflow the topology kref by calling put_mstb multiple times?
It looks like that would result in a WARN from refcount.c, and wouldn't call the
destroy function multiple times, so that's nice :)

Similarly, is there any defense against calling get_mstb() between destroy() and
the delayed destroy worker running?

Sean

> ---
>  drivers/gpu/drm/drm_dp_mst_topology.c | 162 +++++++++++++++++---------
>  include/drm/drm_dp_mst_helper.h       |  26 +++--
>  2 files changed, 127 insertions(+), 61 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
> index 3054ec622506..738f260d4b15 100644
> --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> @@ -1113,34 +1113,17 @@ static void drm_dp_destroy_mst_branch_device(struct kref *kref)
>  	struct drm_dp_mst_branch *mstb =
>  		container_of(kref, struct drm_dp_mst_branch, topology_kref);
>  	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
> -	struct drm_dp_mst_port *port, *tmp;
> -	bool wake_tx = false;
>  
> -	mutex_lock(&mgr->lock);
> -	list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
> -		list_del(&port->next);
> -		drm_dp_mst_topology_put_port(port);
> -	}
> -	mutex_unlock(&mgr->lock);
> -
> -	/* drop any tx slots msg */
> -	mutex_lock(&mstb->mgr->qlock);
> -	if (mstb->tx_slots[0]) {
> -		mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
> -		mstb->tx_slots[0] = NULL;
> -		wake_tx = true;
> -	}
> -	if (mstb->tx_slots[1]) {
> -		mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
> -		mstb->tx_slots[1] = NULL;
> -		wake_tx = true;
> -	}
> -	mutex_unlock(&mstb->mgr->qlock);
> +	INIT_LIST_HEAD(&mstb->destroy_next);
>  
> -	if (wake_tx)
> -		wake_up_all(&mstb->mgr->tx_waitq);
> -
> -	drm_dp_mst_put_mstb_malloc(mstb);
> +	/*
> +	 * This can get called under mgr->mutex, so we need to perform the
> +	 * actual destruction of the mstb in another worker
> +	 */
> +	mutex_lock(&mgr->delayed_destroy_lock);
> +	list_add(&mstb->destroy_next, &mgr->destroy_branch_device_list);
> +	mutex_unlock(&mgr->delayed_destroy_lock);
> +	schedule_work(&mgr->delayed_destroy_work);
>  }
>  
>  /**
> @@ -1255,10 +1238,10 @@ static void drm_dp_destroy_port(struct kref *kref)
>  			 * we might be holding the mode_config.mutex
>  			 * from an EDID retrieval */
>  
> -			mutex_lock(&mgr->destroy_connector_lock);
> -			list_add(&port->next, &mgr->destroy_connector_list);
> -			mutex_unlock(&mgr->destroy_connector_lock);
> -			schedule_work(&mgr->destroy_connector_work);
> +			mutex_lock(&mgr->delayed_destroy_lock);
> +			list_add(&port->next, &mgr->destroy_port_list);
> +			mutex_unlock(&mgr->delayed_destroy_lock);
> +			schedule_work(&mgr->delayed_destroy_work);
>  			return;
>  		}
>  		/* no need to clean up vcpi
> @@ -2792,7 +2775,7 @@ void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr)
>  			   DP_MST_EN | DP_UPSTREAM_IS_SRC);
>  	mutex_unlock(&mgr->lock);
>  	flush_work(&mgr->work);
> -	flush_work(&mgr->destroy_connector_work);
> +	flush_work(&mgr->delayed_destroy_work);
>  }
>  EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend);
>  
> @@ -3740,34 +3723,104 @@ static void drm_dp_tx_work(struct work_struct *work)
>  	mutex_unlock(&mgr->qlock);
>  }
>  
> -static void drm_dp_destroy_connector_work(struct work_struct *work)
> +static inline void
> +drm_dp_delayed_destroy_port(struct drm_dp_mst_port *port)
>  {
> -	struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, destroy_connector_work);
> -	struct drm_dp_mst_port *port;
> -	bool send_hotplug = false;
> +	port->mgr->cbs->destroy_connector(port->mgr, port->connector);
> +
> +	drm_dp_port_teardown_pdt(port, port->pdt);
> +	port->pdt = DP_PEER_DEVICE_NONE;
> +
> +	drm_dp_mst_put_port_malloc(port);
> +}
> +
> +static inline void
> +drm_dp_delayed_destroy_mstb(struct drm_dp_mst_branch *mstb)
> +{
> +	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
> +	struct drm_dp_mst_port *port, *tmp;
> +	bool wake_tx = false;
> +
> +	mutex_lock(&mgr->lock);
> +	list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
> +		list_del(&port->next);
> +		drm_dp_mst_topology_put_port(port);
> +	}
> +	mutex_unlock(&mgr->lock);
> +
> +	/* drop any tx slots msg */
> +	mutex_lock(&mstb->mgr->qlock);
> +	if (mstb->tx_slots[0]) {
> +		mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
> +		mstb->tx_slots[0] = NULL;
> +		wake_tx = true;
> +	}
> +	if (mstb->tx_slots[1]) {
> +		mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
> +		mstb->tx_slots[1] = NULL;
> +		wake_tx = true;
> +	}
> +	mutex_unlock(&mstb->mgr->qlock);
> +
> +	if (wake_tx)
> +		wake_up_all(&mstb->mgr->tx_waitq);
> +
> +	drm_dp_mst_put_mstb_malloc(mstb);
> +}
> +
> +static void drm_dp_delayed_destroy_work(struct work_struct *work)
> +{
> +	struct drm_dp_mst_topology_mgr *mgr =
> +		container_of(work, struct drm_dp_mst_topology_mgr,
> +			     delayed_destroy_work);
> +	bool send_hotplug = false, go_again;
> +
>  	/*
>  	 * Not a regular list traverse as we have to drop the destroy
> -	 * connector lock before destroying the connector, to avoid AB->BA
> +	 * connector lock before destroying the mstb/port, to avoid AB->BA
>  	 * ordering between this lock and the config mutex.
>  	 */
> -	for (;;) {
> -		mutex_lock(&mgr->destroy_connector_lock);
> -		port = list_first_entry_or_null(&mgr->destroy_connector_list, struct drm_dp_mst_port, next);
> -		if (!port) {
> -			mutex_unlock(&mgr->destroy_connector_lock);
> -			break;
> +	do {
> +		go_again = false;
> +
> +		for (;;) {
> +			struct drm_dp_mst_branch *mstb;
> +
> +			mutex_lock(&mgr->delayed_destroy_lock);
> +			mstb = list_first_entry_or_null(&mgr->destroy_branch_device_list,
> +							struct drm_dp_mst_branch,
> +							destroy_next);
> +			if (mstb)
> +				list_del(&mstb->destroy_next);
> +			mutex_unlock(&mgr->delayed_destroy_lock);
> +
> +			if (!mstb)
> +				break;
> +
> +			drm_dp_delayed_destroy_mstb(mstb);
> +			go_again = true;
>  		}
> -		list_del(&port->next);
> -		mutex_unlock(&mgr->destroy_connector_lock);
>  
> -		mgr->cbs->destroy_connector(mgr, port->connector);
> +		for (;;) {
> +			struct drm_dp_mst_port *port;
>  
> -		drm_dp_port_teardown_pdt(port, port->pdt);
> -		port->pdt = DP_PEER_DEVICE_NONE;
> +			mutex_lock(&mgr->delayed_destroy_lock);
> +			port = list_first_entry_or_null(&mgr->destroy_port_list,
> +							struct drm_dp_mst_port,
> +							next);
> +			if (port)
> +				list_del(&port->next);
> +			mutex_unlock(&mgr->delayed_destroy_lock);
> +
> +			if (!port)
> +				break;
> +
> +			drm_dp_delayed_destroy_port(port);
> +			send_hotplug = true;
> +			go_again = true;
> +		}
> +	} while (go_again);
>  
> -		drm_dp_mst_put_port_malloc(port);
> -		send_hotplug = true;
> -	}
>  	if (send_hotplug)
>  		drm_kms_helper_hotplug_event(mgr->dev);
>  }
> @@ -3957,12 +4010,13 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
>  	mutex_init(&mgr->lock);
>  	mutex_init(&mgr->qlock);
>  	mutex_init(&mgr->payload_lock);
> -	mutex_init(&mgr->destroy_connector_lock);
> +	mutex_init(&mgr->delayed_destroy_lock);
>  	INIT_LIST_HEAD(&mgr->tx_msg_downq);
> -	INIT_LIST_HEAD(&mgr->destroy_connector_list);
> +	INIT_LIST_HEAD(&mgr->destroy_port_list);
> +	INIT_LIST_HEAD(&mgr->destroy_branch_device_list);
>  	INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work);
>  	INIT_WORK(&mgr->tx_work, drm_dp_tx_work);
> -	INIT_WORK(&mgr->destroy_connector_work, drm_dp_destroy_connector_work);
> +	INIT_WORK(&mgr->delayed_destroy_work, drm_dp_delayed_destroy_work);
>  	init_waitqueue_head(&mgr->tx_waitq);
>  	mgr->dev = dev;
>  	mgr->aux = aux;
> @@ -4005,7 +4059,7 @@ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
>  {
>  	drm_dp_mst_topology_mgr_set_mst(mgr, false);
>  	flush_work(&mgr->work);
> -	flush_work(&mgr->destroy_connector_work);
> +	cancel_work_sync(&mgr->delayed_destroy_work);
>  	mutex_lock(&mgr->payload_lock);
>  	kfree(mgr->payloads);
>  	mgr->payloads = NULL;
> diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
> index fc349204a71b..4a4507fe928d 100644
> --- a/include/drm/drm_dp_mst_helper.h
> +++ b/include/drm/drm_dp_mst_helper.h
> @@ -143,6 +143,12 @@ struct drm_dp_mst_branch {
>  	 */
>  	struct kref malloc_kref;
>  
> +	/**
> +	 * @destroy_next: linked-list entry used by
> +	 * drm_dp_delayed_destroy_work()
> +	 */
> +	struct list_head destroy_next;
> +
>  	u8 rad[8];
>  	u8 lct;
>  	int num_ports;
> @@ -575,18 +581,24 @@ struct drm_dp_mst_topology_mgr {
>  	struct work_struct tx_work;
>  
>  	/**
> -	 * @destroy_connector_list: List of to be destroyed connectors.
> +	 * @destroy_port_list: List of to be destroyed connectors.
> +	 */
> +	struct list_head destroy_port_list;
> +	/**
> +	 * @destroy_branch_device_list: List of to be destroyed branch
> +	 * devices.
>  	 */
> -	struct list_head destroy_connector_list;
> +	struct list_head destroy_branch_device_list;
>  	/**
> -	 * @destroy_connector_lock: Protects @connector_list.
> +	 * @delayed_destroy_lock: Protects @destroy_port_list and
> +	 * @destroy_branch_device_list.
>  	 */
> -	struct mutex destroy_connector_lock;
> +	struct mutex delayed_destroy_lock;
>  	/**
> -	 * @destroy_connector_work: Work item to destroy connectors. Needed to
> -	 * avoid locking inversion.
> +	 * @delayed_destroy_work: Work item to destroy MST port and branch
> +	 * devices, needed to avoid locking inversion.
>  	 */
> -	struct work_struct destroy_connector_work;
> +	struct work_struct delayed_destroy_work;
>  };
>  
>  int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
> -- 
> 2.21.0
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 04/27] drm/dp_mst: Move test_calc_pbn_mode() into an actual selftest
  2019-09-03 20:45 ` [PATCH v2 04/27] drm/dp_mst: Move test_calc_pbn_mode() into an actual selftest Lyude Paul
@ 2019-09-25 18:17   ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2019-09-25 18:17 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, Thomas Hellstrom,
	Deepak Rawat, Alexandru Gheorghe, Thomas Gleixner, linux-kernel

On Tue, Sep 03, 2019 at 04:45:42PM -0400, Lyude Paul wrote:
> Yes, apparently we've been testing this for every single driver load for
> quite a long time now. At least that means our PBN calculation is solid!
> 
> Anyway, introduce self tests for MST and move this into there.
> 
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>

Reviewed-by: Sean Paul <sean@poorly.run>

> ---
>  drivers/gpu/drm/drm_dp_mst_topology.c         | 27 ---------------
>  drivers/gpu/drm/selftests/Makefile            |  2 +-
>  .../gpu/drm/selftests/drm_modeset_selftests.h |  1 +
>  .../drm/selftests/test-drm_dp_mst_helper.c    | 34 +++++++++++++++++++
>  .../drm/selftests/test-drm_modeset_common.h   |  1 +
>  5 files changed, 37 insertions(+), 28 deletions(-)
>  create mode 100644 drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
> 
> diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
> index 738f260d4b15..6f7f449ca12b 100644
> --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> @@ -47,7 +47,6 @@
>   */
>  static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
>  				  char *buf);
> -static int test_calc_pbn_mode(void);
>  
>  static void drm_dp_mst_topology_put_port(struct drm_dp_mst_port *port);
>  
> @@ -3561,30 +3560,6 @@ int drm_dp_calc_pbn_mode(int clock, int bpp)
>  }
>  EXPORT_SYMBOL(drm_dp_calc_pbn_mode);
>  
> -static int test_calc_pbn_mode(void)
> -{
> -	int ret;
> -	ret = drm_dp_calc_pbn_mode(154000, 30);
> -	if (ret != 689) {
> -		DRM_ERROR("PBN calculation test failed - clock %d, bpp %d, expected PBN %d, actual PBN %d.\n",
> -				154000, 30, 689, ret);
> -		return -EINVAL;
> -	}
> -	ret = drm_dp_calc_pbn_mode(234000, 30);
> -	if (ret != 1047) {
> -		DRM_ERROR("PBN calculation test failed - clock %d, bpp %d, expected PBN %d, actual PBN %d.\n",
> -				234000, 30, 1047, ret);
> -		return -EINVAL;
> -	}
> -	ret = drm_dp_calc_pbn_mode(297000, 24);
> -	if (ret != 1063) {
> -		DRM_ERROR("PBN calculation test failed - clock %d, bpp %d, expected PBN %d, actual PBN %d.\n",
> -				297000, 24, 1063, ret);
> -		return -EINVAL;
> -	}
> -	return 0;
> -}
> -
>  /* we want to kick the TX after we've ack the up/down IRQs. */
>  static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr)
>  {
> @@ -4033,8 +4008,6 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
>  	if (!mgr->proposed_vcpis)
>  		return -ENOMEM;
>  	set_bit(0, &mgr->payload_mask);
> -	if (test_calc_pbn_mode() < 0)
> -		DRM_ERROR("MST PBN self-test failed\n");
>  
>  	mst_state = kzalloc(sizeof(*mst_state), GFP_KERNEL);
>  	if (mst_state == NULL)
> diff --git a/drivers/gpu/drm/selftests/Makefile b/drivers/gpu/drm/selftests/Makefile
> index aae88f8a016c..d2137342b371 100644
> --- a/drivers/gpu/drm/selftests/Makefile
> +++ b/drivers/gpu/drm/selftests/Makefile
> @@ -1,6 +1,6 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  test-drm_modeset-y := test-drm_modeset_common.o test-drm_plane_helper.o \
>                        test-drm_format.o test-drm_framebuffer.o \
> -		      test-drm_damage_helper.o
> +		      test-drm_damage_helper.o test-drm_dp_mst_helper.o
>  
>  obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_modeset.o test-drm_cmdline_parser.o
> diff --git a/drivers/gpu/drm/selftests/drm_modeset_selftests.h b/drivers/gpu/drm/selftests/drm_modeset_selftests.h
> index 464753746013..dec3ee3ec96f 100644
> --- a/drivers/gpu/drm/selftests/drm_modeset_selftests.h
> +++ b/drivers/gpu/drm/selftests/drm_modeset_selftests.h
> @@ -32,3 +32,4 @@ selftest(damage_iter_damage_one_intersect, igt_damage_iter_damage_one_intersect)
>  selftest(damage_iter_damage_one_outside, igt_damage_iter_damage_one_outside)
>  selftest(damage_iter_damage_src_moved, igt_damage_iter_damage_src_moved)
>  selftest(damage_iter_damage_not_visible, igt_damage_iter_damage_not_visible)
> +selftest(dp_mst_calc_pbn_mode, igt_dp_mst_calc_pbn_mode)
> diff --git a/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c b/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
> new file mode 100644
> index 000000000000..9baa5171988d
> --- /dev/null
> +++ b/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
> @@ -0,0 +1,34 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Test cases for for the DRM DP MST helpers
> + */
> +
> +#include <drm/drm_dp_mst_helper.h>
> +#include <drm/drm_print.h>
> +
> +#include "test-drm_modeset_common.h"
> +
> +int igt_dp_mst_calc_pbn_mode(void *ignored)
> +{
> +	int pbn, i;
> +	const struct {
> +		int rate;
> +		int bpp;
> +		int expected;
> +	} test_params[] = {
> +		{ 154000, 30, 689 },
> +		{ 234000, 30, 1047 },
> +		{ 297000, 24, 1063 },
> +	};
> +
> +	for (i = 0; i < ARRAY_SIZE(test_params); i++) {
> +		pbn = drm_dp_calc_pbn_mode(test_params[i].rate,
> +					   test_params[i].bpp);
> +		FAIL(pbn != test_params[i].expected,
> +		     "Expected PBN %d for clock %d bpp %d, got %d\n",
> +		     test_params[i].expected, test_params[i].rate,
> +		     test_params[i].bpp, pbn);
> +	}
> +
> +	return 0;
> +}
> diff --git a/drivers/gpu/drm/selftests/test-drm_modeset_common.h b/drivers/gpu/drm/selftests/test-drm_modeset_common.h
> index 8c76f09c12d1..590bda35a683 100644
> --- a/drivers/gpu/drm/selftests/test-drm_modeset_common.h
> +++ b/drivers/gpu/drm/selftests/test-drm_modeset_common.h
> @@ -39,5 +39,6 @@ int igt_damage_iter_damage_one_intersect(void *ignored);
>  int igt_damage_iter_damage_one_outside(void *ignored);
>  int igt_damage_iter_damage_src_moved(void *ignored);
>  int igt_damage_iter_damage_not_visible(void *ignored);
> +int igt_dp_mst_calc_pbn_mode(void *ignored);
>  
>  #endif
> -- 
> 2.21.0
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 08/27] drm/dp_mst: Remove PDT teardown in drm_dp_destroy_port() and refactor
  2019-09-03 20:45 ` [PATCH v2 08/27] drm/dp_mst: Remove PDT teardown in drm_dp_destroy_port() and refactor Lyude Paul
@ 2019-09-25 19:00   ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2019-09-25 19:00 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

On Tue, Sep 03, 2019 at 04:45:46PM -0400, Lyude Paul wrote:
> This will allow us to add some locking for port->* members, in
> particular the PDT and ->connector, which can't be done from
> drm_dp_destroy_port() since we don't know what locks the caller might be
> holding.

Might be nice to mention that this is already done in the delayed destroy worker
so readers don't need to go looking for it. Perhaps update this when you apply
the patch.

> 
> Changes since v2:
> * Clarify commit message
> 
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>

Reviewed-by: Sean Paul <sean@poorly.run>

> ---
>  drivers/gpu/drm/drm_dp_mst_topology.c | 40 +++++++++++----------------
>  1 file changed, 16 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
> index f5f1d8b50fb6..af3189df28aa 100644
> --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> @@ -1511,31 +1511,22 @@ static void drm_dp_destroy_port(struct kref *kref)
>  		container_of(kref, struct drm_dp_mst_port, topology_kref);
>  	struct drm_dp_mst_topology_mgr *mgr = port->mgr;
>  
> -	if (!port->input) {
> -		kfree(port->cached_edid);
> +	/* There's nothing that needs locking to destroy an input port yet */
> +	if (port->input) {
> +		drm_dp_mst_put_port_malloc(port);
> +		return;
> +	}
>  
> -		/*
> -		 * The only time we don't have a connector
> -		 * on an output port is if the connector init
> -		 * fails.
> -		 */
> -		if (port->connector) {
> -			/* we can't destroy the connector here, as
> -			 * we might be holding the mode_config.mutex
> -			 * from an EDID retrieval */
> +	kfree(port->cached_edid);
>  
> -			mutex_lock(&mgr->delayed_destroy_lock);
> -			list_add(&port->next, &mgr->destroy_port_list);
> -			mutex_unlock(&mgr->delayed_destroy_lock);
> -			schedule_work(&mgr->delayed_destroy_work);
> -			return;
> -		}
> -		/* no need to clean up vcpi
> -		 * as if we have no connector we never setup a vcpi */
> -		drm_dp_port_teardown_pdt(port, port->pdt);
> -		port->pdt = DP_PEER_DEVICE_NONE;
> -	}
> -	drm_dp_mst_put_port_malloc(port);
> +	/*
> +	 * we can't destroy the connector here, as we might be holding the
> +	 * mode_config.mutex from an EDID retrieval
> +	 */
> +	mutex_lock(&mgr->delayed_destroy_lock);
> +	list_add(&port->next, &mgr->destroy_port_list);
> +	mutex_unlock(&mgr->delayed_destroy_lock);
> +	schedule_work(&mgr->delayed_destroy_work);
>  }
>  
>  /**
> @@ -3998,7 +3989,8 @@ static void drm_dp_tx_work(struct work_struct *work)
>  static inline void
>  drm_dp_delayed_destroy_port(struct drm_dp_mst_port *port)
>  {
> -	port->mgr->cbs->destroy_connector(port->mgr, port->connector);
> +	if (port->connector)
> +		port->mgr->cbs->destroy_connector(port->mgr, port->connector);
>  
>  	drm_dp_port_teardown_pdt(port, port->pdt);
>  	port->pdt = DP_PEER_DEVICE_NONE;
> -- 
> 2.21.0
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 14/27] drm/dp_mst: Destroy topology_mgr mutexes
  2019-09-03 20:45 ` [PATCH v2 14/27] drm/dp_mst: Destroy topology_mgr mutexes Lyude Paul
@ 2019-09-25 19:14   ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2019-09-25 19:14 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

On Tue, Sep 03, 2019 at 04:45:52PM -0400, Lyude Paul wrote:
> Turns out we've been forgetting for a while now to actually destroy any
> of the mutexes that we create in drm_dp_mst_topology_mgr. So, let's do
> that.
> 
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>

Cleanup is overrated :)

Reviewed-by: Sean Paul <sean@poorly.run>


> ---
>  drivers/gpu/drm/drm_dp_mst_topology.c | 5 +++++
>  1 file changed, 5 insertions(+)
> 
> diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
> index 74161f442584..2f88cc173500 100644
> --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> @@ -4339,6 +4339,11 @@ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
>  	mgr->aux = NULL;
>  	drm_atomic_private_obj_fini(&mgr->base);
>  	mgr->funcs = NULL;
> +
> +	mutex_destroy(&mgr->delayed_destroy_lock);
> +	mutex_destroy(&mgr->payload_lock);
> +	mutex_destroy(&mgr->qlock);
> +	mutex_destroy(&mgr->lock);
>  }
>  EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);
>  
> -- 
> 2.21.0
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 16/27] drm/dp_mst: Refactor pdt setup/teardown, add more locking
  2019-09-03 20:45 ` [PATCH v2 16/27] drm/dp_mst: Refactor pdt setup/teardown, add more locking Lyude Paul
@ 2019-09-25 19:27   ` Sean Paul
  2019-09-25 21:00     ` Lyude Paul
  0 siblings, 1 reply; 62+ messages in thread
From: Sean Paul @ 2019-09-25 19:27 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

On Tue, Sep 03, 2019 at 04:45:54PM -0400, Lyude Paul wrote:
> Since we're going to be implementing suspend/resume reprobing very soon,
> we need to make sure we are extra careful to ensure that our locking
> actually protects the topology state where we expect it to. Turns out
> this isn't the case with drm_dp_port_setup_pdt() and
> drm_dp_port_teardown_pdt(), both of which change port->mstb without
> grabbing &mgr->lock.
> 
> Additionally, since most callers of these functions are just using it to
> teardown the port's previous PDT and setup a new one we can simplify
> things a bit and combine drm_dp_port_setup_pdt() and
> drm_dp_port_teardown_pdt() into a single function:
> drm_dp_port_set_pdt(). This function also handles actually ensuring that
> we grab the correct locks when we need to modify port->mstb.
> 
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>
> ---
>  drivers/gpu/drm/drm_dp_mst_topology.c | 181 +++++++++++++++-----------
>  include/drm/drm_dp_mst_helper.h       |   6 +-
>  2 files changed, 110 insertions(+), 77 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
> index d1610434a0cb..9944ef2ce885 100644
> --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> @@ -1487,24 +1487,6 @@ drm_dp_mst_topology_put_mstb(struct drm_dp_mst_branch *mstb)
>  	kref_put(&mstb->topology_kref, drm_dp_destroy_mst_branch_device);
>  }
>  
> -static void drm_dp_port_teardown_pdt(struct drm_dp_mst_port *port, int old_pdt)
> -{
> -	struct drm_dp_mst_branch *mstb;
> -
> -	switch (old_pdt) {
> -	case DP_PEER_DEVICE_DP_LEGACY_CONV:
> -	case DP_PEER_DEVICE_SST_SINK:
> -		/* remove i2c over sideband */
> -		drm_dp_mst_unregister_i2c_bus(&port->aux);
> -		break;
> -	case DP_PEER_DEVICE_MST_BRANCHING:
> -		mstb = port->mstb;
> -		port->mstb = NULL;
> -		drm_dp_mst_topology_put_mstb(mstb);
> -		break;
> -	}
> -}
> -
>  static void drm_dp_destroy_port(struct kref *kref)
>  {
>  	struct drm_dp_mst_port *port =
> @@ -1714,38 +1696,79 @@ static u8 drm_dp_calculate_rad(struct drm_dp_mst_port *port,
>  	return parent_lct + 1;
>  }
>  
> -/*
> - * return sends link address for new mstb
> - */
> -static bool drm_dp_port_setup_pdt(struct drm_dp_mst_port *port)
> +static int drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt)
>  {
> -	int ret;
> -	u8 rad[6], lct;
> -	bool send_link = false;
> +	struct drm_dp_mst_topology_mgr *mgr = port->mgr;
> +	struct drm_dp_mst_branch *mstb;
> +	u8 rad[8], lct;
> +	int ret = 0;
> +
> +	if (port->pdt == new_pdt)

Shouldn't we also ensure that access to port->pdt is also locked?

Sean

> +		return 0;
> +
> +	/* Teardown the old pdt, if there is one */
> +	switch (port->pdt) {
> +	case DP_PEER_DEVICE_DP_LEGACY_CONV:
> +	case DP_PEER_DEVICE_SST_SINK:
> +		/*
> +		 * If the new PDT would also have an i2c bus, don't bother
> +		 * with reregistering it
> +		 */
> +		if (new_pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
> +		    new_pdt == DP_PEER_DEVICE_SST_SINK) {
> +			port->pdt = new_pdt;
> +			return 0;
> +		}
> +
> +		/* remove i2c over sideband */
> +		drm_dp_mst_unregister_i2c_bus(&port->aux);
> +		break;
> +	case DP_PEER_DEVICE_MST_BRANCHING:
> +		mutex_lock(&mgr->lock);
> +		drm_dp_mst_topology_put_mstb(port->mstb);
> +		port->mstb = NULL;
> +		mutex_unlock(&mgr->lock);
> +		break;
> +	}
> +
> +	port->pdt = new_pdt;
>  	switch (port->pdt) {
>  	case DP_PEER_DEVICE_DP_LEGACY_CONV:
>  	case DP_PEER_DEVICE_SST_SINK:
>  		/* add i2c over sideband */
>  		ret = drm_dp_mst_register_i2c_bus(&port->aux);
>  		break;
> +
>  	case DP_PEER_DEVICE_MST_BRANCHING:
>  		lct = drm_dp_calculate_rad(port, rad);
> +		mstb = drm_dp_add_mst_branch_device(lct, rad);
> +		if (!mstb) {
> +			ret = -ENOMEM;
> +			DRM_ERROR("Failed to create MSTB for port %p", port);
> +			goto out;
> +		}
>  
> -		port->mstb = drm_dp_add_mst_branch_device(lct, rad);
> -		if (port->mstb) {
> -			port->mstb->mgr = port->mgr;
> -			port->mstb->port_parent = port;
> -			/*
> -			 * Make sure this port's memory allocation stays
> -			 * around until its child MSTB releases it
> -			 */
> -			drm_dp_mst_get_port_malloc(port);
> +		mutex_lock(&mgr->lock);
> +		port->mstb = mstb;
> +		mstb->mgr = port->mgr;
> +		mstb->port_parent = port;
>  
> -			send_link = true;
> -		}
> +		/*
> +		 * Make sure this port's memory allocation stays
> +		 * around until its child MSTB releases it
> +		 */
> +		drm_dp_mst_get_port_malloc(port);
> +		mutex_unlock(&mgr->lock);
> +
> +		/* And make sure we send a link address for this */
> +		ret = 1;
>  		break;
>  	}
> -	return send_link;
> +
> +out:
> +	if (ret < 0)
> +		port->pdt = DP_PEER_DEVICE_NONE;
> +	return ret;
>  }
>  
>  /**
> @@ -1881,10 +1904,9 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
>  			    struct drm_device *dev,
>  			    struct drm_dp_link_addr_reply_port *port_msg)
>  {
> +	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
>  	struct drm_dp_mst_port *port;
> -	bool ret;
>  	bool created = false;
> -	int old_pdt = 0;
>  	int old_ddps = 0;
>  
>  	port = drm_dp_get_port(mstb, port_msg->port_number);
> @@ -1896,7 +1918,7 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
>  		kref_init(&port->malloc_kref);
>  		port->parent = mstb;
>  		port->port_num = port_msg->port_number;
> -		port->mgr = mstb->mgr;
> +		port->mgr = mgr;
>  		port->aux.name = "DPMST";
>  		port->aux.dev = dev->dev;
>  		port->aux.is_remote = true;
> @@ -1909,11 +1931,9 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
>  
>  		created = true;
>  	} else {
> -		old_pdt = port->pdt;
>  		old_ddps = port->ddps;
>  	}
>  
> -	port->pdt = port_msg->peer_device_type;
>  	port->input = port_msg->input_port;
>  	port->mcs = port_msg->mcs;
>  	port->ddps = port_msg->ddps;
> @@ -1925,29 +1945,33 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
>  	/* manage mstb port lists with mgr lock - take a reference
>  	   for this list */
>  	if (created) {
> -		mutex_lock(&mstb->mgr->lock);
> +		mutex_lock(&mgr->lock);
>  		drm_dp_mst_topology_get_port(port);
>  		list_add(&port->next, &mstb->ports);
> -		mutex_unlock(&mstb->mgr->lock);
> +		mutex_unlock(&mgr->lock);
>  	}
>  
>  	if (old_ddps != port->ddps) {
>  		if (port->ddps) {
>  			if (!port->input) {
> -				drm_dp_send_enum_path_resources(mstb->mgr,
> -								mstb, port);
> +				drm_dp_send_enum_path_resources(mgr, mstb,
> +								port);
>  			}
>  		} else {
>  			port->available_pbn = 0;
>  		}
>  	}
>  
> -	if (old_pdt != port->pdt && !port->input) {
> -		drm_dp_port_teardown_pdt(port, old_pdt);
> -
> -		ret = drm_dp_port_setup_pdt(port);
> -		if (ret == true)
> -			drm_dp_send_link_address(mstb->mgr, port->mstb);
> +	if (!port->input) {
> +		int ret = drm_dp_port_set_pdt(port,
> +					      port_msg->peer_device_type);
> +		if (ret == 1) {
> +			drm_dp_send_link_address(mgr, port->mstb);
> +		} else if (ret < 0) {
> +			DRM_ERROR("Failed to change PDT on port %p: %d\n",
> +				  port, ret);
> +			goto fail;
> +		}
>  	}
>  
>  	if (created && !port->input) {
> @@ -1955,18 +1979,11 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
>  
>  		build_mst_prop_path(mstb, port->port_num, proppath,
>  				    sizeof(proppath));
> -		port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr,
> -								   port,
> -								   proppath);
> -		if (!port->connector) {
> -			/* remove it from the port list */
> -			mutex_lock(&mstb->mgr->lock);
> -			list_del(&port->next);
> -			mutex_unlock(&mstb->mgr->lock);
> -			/* drop port list reference */
> -			drm_dp_mst_topology_put_port(port);
> -			goto out;
> -		}
> +		port->connector = (*mgr->cbs->add_connector)(mgr, port,
> +							     proppath);
> +		if (!port->connector)
> +			goto fail;
> +
>  		if ((port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
>  		     port->pdt == DP_PEER_DEVICE_SST_SINK) &&
>  		    port->port_num >= DP_MST_LOGICAL_PORT_0) {
> @@ -1974,28 +1991,38 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
>  							 &port->aux.ddc);
>  			drm_connector_set_tile_property(port->connector);
>  		}
> -		(*mstb->mgr->cbs->register_connector)(port->connector);
> +
> +		(*mgr->cbs->register_connector)(port->connector);
>  	}
>  
> -out:
>  	/* put reference to this port */
>  	drm_dp_mst_topology_put_port(port);
> +	return;
> +
> +fail:
> +	/* Remove it from the port list */
> +	mutex_lock(&mgr->lock);
> +	list_del(&port->next);
> +	mutex_unlock(&mgr->lock);
> +
> +	/* Drop the port list reference */
> +	drm_dp_mst_topology_put_port(port);
> +	/* And now drop our reference */
> +	drm_dp_mst_topology_put_port(port);
>  }
>  
>  static void drm_dp_update_port(struct drm_dp_mst_branch *mstb,
>  			       struct drm_dp_connection_status_notify *conn_stat)
>  {
>  	struct drm_dp_mst_port *port;
> -	int old_pdt;
>  	int old_ddps;
>  	bool dowork = false;
> +
>  	port = drm_dp_get_port(mstb, conn_stat->port_number);
>  	if (!port)
>  		return;
>  
>  	old_ddps = port->ddps;
> -	old_pdt = port->pdt;
> -	port->pdt = conn_stat->peer_device_type;
>  	port->mcs = conn_stat->message_capability_status;
>  	port->ldps = conn_stat->legacy_device_plug_status;
>  	port->ddps = conn_stat->displayport_device_plug_status;
> @@ -2007,11 +2034,17 @@ static void drm_dp_update_port(struct drm_dp_mst_branch *mstb,
>  			port->available_pbn = 0;
>  		}
>  	}
> -	if (old_pdt != port->pdt && !port->input) {
> -		drm_dp_port_teardown_pdt(port, old_pdt);
>  
> -		if (drm_dp_port_setup_pdt(port))
> +	if (!port->input) {
> +		int ret = drm_dp_port_set_pdt(port,
> +					      conn_stat->peer_device_type);
> +		if (ret == 1) {
>  			dowork = true;
> +		} else if (ret < 0) {
> +			DRM_ERROR("Failed to change PDT for port %p: %d\n",
> +				  port, ret);
> +			dowork = false;
> +		}
>  	}
>  
>  	drm_dp_mst_topology_put_port(port);
> @@ -4003,9 +4036,7 @@ drm_dp_delayed_destroy_port(struct drm_dp_mst_port *port)
>  	if (port->connector)
>  		port->mgr->cbs->destroy_connector(port->mgr, port->connector);
>  
> -	drm_dp_port_teardown_pdt(port, port->pdt);
> -	port->pdt = DP_PEER_DEVICE_NONE;
> -
> +	drm_dp_port_set_pdt(port, DP_PEER_DEVICE_NONE);
>  	drm_dp_mst_put_port_malloc(port);
>  }
>  
> diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
> index 5423a8adda78..f253ee43e9d9 100644
> --- a/include/drm/drm_dp_mst_helper.h
> +++ b/include/drm/drm_dp_mst_helper.h
> @@ -55,8 +55,10 @@ struct drm_dp_vcpi {
>   * @num_sdp_stream_sinks: Number of stream sinks
>   * @available_pbn: Available bandwidth for this port.
>   * @next: link to next port on this branch device
> - * @mstb: branch device attach below this port
> - * @aux: i2c aux transport to talk to device connected to this port.
> + * @mstb: branch device on this port, protected by
> + * &drm_dp_mst_topology_mgr.lock
> + * @aux: i2c aux transport to talk to device connected to this port, protected
> + * by &drm_dp_mst_topology_mgr.lock
>   * @parent: branch device parent of this port
>   * @vcpi: Virtual Channel Payload info for this port.
>   * @connector: DRM connector this port is connected to.
> -- 
> 2.21.0
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 17/27] drm/dp_mst: Rename drm_dp_add_port and drm_dp_update_port
  2019-09-03 20:45 ` [PATCH v2 17/27] drm/dp_mst: Rename drm_dp_add_port and drm_dp_update_port Lyude Paul
@ 2019-09-25 19:30   ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2019-09-25 19:30 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

On Tue, Sep 03, 2019 at 04:45:55PM -0400, Lyude Paul wrote:
> The names for these functions are rather confusing. drm_dp_add_port()
> sounds like a function that would simply create a port and add it to a
> topology, and do nothing more. Similarly, drm_dp_update_port() would be
> assumed to be the function that should be used to update port
> information after initial creation.
> 
> While those assumptions are currently correct in how these functions are
> used, a quick glance at drm_dp_add_port() reveals that drm_dp_add_port()
> can also update the information on a port, and seems explicitly designed
> to do so. This can be explained pretty simply by the fact that there's
> more situations that would involve updating the port information based
> on a link address response as opposed to a connection status
> notification than the driver's initial topology probe. Case in point:
> reprobing link addresses after suspend/resume.
> 
> Since we're about to start using drm_dp_add_port() differently for
> suspend/resume reprobing, let's rename both functions to clarify what
> they actually do.
> 
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>

Reviewed-by: Sean Paul <sean@poorly.run>

> ---
>  drivers/gpu/drm/drm_dp_mst_topology.c | 17 ++++++++++-------
>  1 file changed, 10 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
> index 9944ef2ce885..cfaf9eb7ace9 100644
> --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> @@ -1900,9 +1900,10 @@ void drm_dp_mst_connector_early_unregister(struct drm_connector *connector,
>  }
>  EXPORT_SYMBOL(drm_dp_mst_connector_early_unregister);
>  
> -static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
> -			    struct drm_device *dev,
> -			    struct drm_dp_link_addr_reply_port *port_msg)
> +static void
> +drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
> +				    struct drm_device *dev,
> +				    struct drm_dp_link_addr_reply_port *port_msg)
>  {
>  	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
>  	struct drm_dp_mst_port *port;
> @@ -2011,8 +2012,9 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
>  	drm_dp_mst_topology_put_port(port);
>  }
>  
> -static void drm_dp_update_port(struct drm_dp_mst_branch *mstb,
> -			       struct drm_dp_connection_status_notify *conn_stat)
> +static void
> +drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
> +			    struct drm_dp_connection_status_notify *conn_stat)
>  {
>  	struct drm_dp_mst_port *port;
>  	int old_ddps;
> @@ -2464,7 +2466,8 @@ static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
>  	drm_dp_check_mstb_guid(mstb, reply->guid);
>  
>  	for (i = 0; i < reply->nports; i++)
> -		drm_dp_add_port(mstb, mgr->dev, &reply->ports[i]);
> +		drm_dp_mst_handle_link_address_port(mstb, mgr->dev,
> +						    &reply->ports[i]);
>  
>  	drm_kms_helper_hotplug_event(mgr->dev);
>  
> @@ -3324,7 +3327,7 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
>  	}
>  
>  	if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
> -		drm_dp_update_port(mstb, &msg.u.conn_stat);
> +		drm_dp_mst_handle_conn_stat(mstb, &msg.u.conn_stat);
>  
>  		DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n",
>  			      msg.u.conn_stat.port_number,
> -- 
> 2.21.0
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 18/27] drm/dp_mst: Remove lies in {up,down}_rep_recv documentation
  2019-09-03 20:45 ` [PATCH v2 18/27] drm/dp_mst: Remove lies in {up,down}_rep_recv documentation Lyude Paul
@ 2019-09-25 19:32   ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2019-09-25 19:32 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

On Tue, Sep 03, 2019 at 04:45:56PM -0400, Lyude Paul wrote:
> These are most certainly accessed from far more than the mgr work. In
> fact, up_req_recv is -only- ever accessed from outside the mgr work.
> 
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>

Reviewed-by: Sean Paul <sean@poorly.run>

> ---
>  include/drm/drm_dp_mst_helper.h | 8 ++------
>  1 file changed, 2 insertions(+), 6 deletions(-)
> 
> diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
> index f253ee43e9d9..8ba2a01324bb 100644
> --- a/include/drm/drm_dp_mst_helper.h
> +++ b/include/drm/drm_dp_mst_helper.h
> @@ -489,15 +489,11 @@ struct drm_dp_mst_topology_mgr {
>  	int conn_base_id;
>  
>  	/**
> -	 * @down_rep_recv: Message receiver state for down replies. This and
> -	 * @up_req_recv are only ever access from the work item, which is
> -	 * serialised.
> +	 * @down_rep_recv: Message receiver state for down replies.
>  	 */
>  	struct drm_dp_sideband_msg_rx down_rep_recv;
>  	/**
> -	 * @up_req_recv: Message receiver state for up requests. This and
> -	 * @down_rep_recv are only ever access from the work item, which is
> -	 * serialised.
> +	 * @up_req_recv: Message receiver state for up requests.
>  	 */
>  	struct drm_dp_sideband_msg_rx up_req_recv;
>  
> -- 
> 2.21.0
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 19/27] drm/dp_mst: Handle UP requests asynchronously
  2019-09-03 20:45 ` [PATCH v2 19/27] drm/dp_mst: Handle UP requests asynchronously Lyude Paul
@ 2019-09-25 19:46   ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2019-09-25 19:46 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

On Tue, Sep 03, 2019 at 04:45:57PM -0400, Lyude Paul wrote:
> Once upon a time, hotplugging devices on MST branches actually worked in
> DRM. Now, it only works in amdgpu (likely because of how it's hotplug
> handlers are implemented). On both i915 and nouveau, hotplug
> notifications from MST branches are noticed - but trying to respond to
> them causes messaging timeouts and causes the whole topology state to go
> out of sync with reality, usually resulting in the user needing to
> replug the entire topology in hopes that it actually fixes things.
> 
> The reason for this is because the way we currently handle UP requests
> in MST is completely bogus. drm_dp_mst_handle_up_req() is called from
> drm_dp_mst_hpd_irq(), which is usually called from the driver's hotplug
> handler. Because we handle sending the hotplug event from this function,
> we actually cause the driver's hotplug handler (and in turn, all
> sideband transactions) to block on
> drm_device->mode_config.connection_mutex. This makes it impossible to
> send any sideband messages from the driver's connector probing
> functions, resulting in the aforementioned sideband message timeout.
> 
> There's even more problems with this beyond breaking hotplugging on MST
> branch devices. It also makes it almost impossible to protect
> drm_dp_mst_port struct members under a lock because we then have to
> worry about dealing with all of the lock dependency issues that ensue.
> 
> So, let's finally actually fix this issue by handling the processing of
> up requests asyncronously. This way we can send sideband messages from
> most contexts without having to deal with getting blocked if we hold
> connection_mutex. This also fixes MST branch device hotplugging on i915,
> finally!
> 
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>

Looks really good!

Reviewed-by: Sean Paul <sean@poorly.run>


> ---
>  drivers/gpu/drm/drm_dp_mst_topology.c | 146 +++++++++++++++++++-------
>  include/drm/drm_dp_mst_helper.h       |  16 +++
>  2 files changed, 122 insertions(+), 40 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
> index cfaf9eb7ace9..5101eeab4485 100644
> --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> @@ -46,6 +46,12 @@
>   * protocol. The helpers contain a topology manager and bandwidth manager.
>   * The helpers encapsulate the sending and received of sideband msgs.
>   */
> +struct drm_dp_pending_up_req {
> +	struct drm_dp_sideband_msg_hdr hdr;
> +	struct drm_dp_sideband_msg_req_body msg;
> +	struct list_head next;
> +};
> +
>  static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
>  				  char *buf);
>  
> @@ -3109,6 +3115,7 @@ void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr)
>  	drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
>  			   DP_MST_EN | DP_UPSTREAM_IS_SRC);
>  	mutex_unlock(&mgr->lock);
> +	flush_work(&mgr->up_req_work);
>  	flush_work(&mgr->work);
>  	flush_work(&mgr->delayed_destroy_work);
>  }
> @@ -3281,12 +3288,70 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
>  	return 0;
>  }
>  
> +static inline void
> +drm_dp_mst_process_up_req(struct drm_dp_mst_topology_mgr *mgr,
> +			  struct drm_dp_pending_up_req *up_req)
> +{
> +	struct drm_dp_mst_branch *mstb = NULL;
> +	struct drm_dp_sideband_msg_req_body *msg = &up_req->msg;
> +	struct drm_dp_sideband_msg_hdr *hdr = &up_req->hdr;
> +
> +	if (hdr->broadcast) {
> +		const u8 *guid = NULL;
> +
> +		if (msg->req_type == DP_CONNECTION_STATUS_NOTIFY)
> +			guid = msg->u.conn_stat.guid;
> +		else if (msg->req_type == DP_RESOURCE_STATUS_NOTIFY)
> +			guid = msg->u.resource_stat.guid;
> +
> +		mstb = drm_dp_get_mst_branch_device_by_guid(mgr, guid);
> +	} else {
> +		mstb = drm_dp_get_mst_branch_device(mgr, hdr->lct, hdr->rad);
> +	}
> +
> +	if (!mstb) {
> +		DRM_DEBUG_KMS("Got MST reply from unknown device %d\n",
> +			      hdr->lct);
> +		return;
> +	}
> +
> +	/* TODO: Add missing handler for DP_RESOURCE_STATUS_NOTIFY events */
> +	if (msg->req_type == DP_CONNECTION_STATUS_NOTIFY) {
> +		drm_dp_mst_handle_conn_stat(mstb, &msg->u.conn_stat);
> +		drm_kms_helper_hotplug_event(mgr->dev);
> +	}
> +
> +	drm_dp_mst_topology_put_mstb(mstb);
> +}
> +
> +static void drm_dp_mst_up_req_work(struct work_struct *work)
> +{
> +	struct drm_dp_mst_topology_mgr *mgr =
> +		container_of(work, struct drm_dp_mst_topology_mgr,
> +			     up_req_work);
> +	struct drm_dp_pending_up_req *up_req;
> +
> +	while (true) {
> +		mutex_lock(&mgr->up_req_lock);
> +		up_req = list_first_entry_or_null(&mgr->up_req_list,
> +						  struct drm_dp_pending_up_req,
> +						  next);
> +		if (up_req)
> +			list_del(&up_req->next);
> +		mutex_unlock(&mgr->up_req_lock);
> +
> +		if (!up_req)
> +			break;
> +
> +		drm_dp_mst_process_up_req(mgr, up_req);
> +		kfree(up_req);
> +	}
> +}
> +
>  static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
>  {
> -	struct drm_dp_sideband_msg_req_body msg;
>  	struct drm_dp_sideband_msg_hdr *hdr = &mgr->up_req_recv.initial_hdr;
> -	struct drm_dp_mst_branch *mstb = NULL;
> -	const u8 *guid;
> +	struct drm_dp_pending_up_req *up_req;
>  	bool seqno;
>  
>  	if (!drm_dp_get_one_sb_msg(mgr, true))
> @@ -3295,56 +3360,53 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
>  	if (!mgr->up_req_recv.have_eomt)
>  		return 0;
>  
> -	if (!hdr->broadcast) {
> -		mstb = drm_dp_get_mst_branch_device(mgr, hdr->lct, hdr->rad);
> -		if (!mstb) {
> -			DRM_DEBUG_KMS("Got MST reply from unknown device %d\n",
> -				      hdr->lct);
> -			goto out;
> -		}
> +	up_req = kzalloc(sizeof(*up_req), GFP_KERNEL);
> +	if (!up_req) {
> +		DRM_ERROR("Not enough memory to process MST up req\n");
> +		return -ENOMEM;
>  	}
> +	INIT_LIST_HEAD(&up_req->next);
>  
>  	seqno = hdr->seqno;
> -	drm_dp_sideband_parse_req(&mgr->up_req_recv, &msg);
> +	drm_dp_sideband_parse_req(&mgr->up_req_recv, &up_req->msg);
>  
> -	if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY)
> -		guid = msg.u.conn_stat.guid;
> -	else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY)
> -		guid = msg.u.resource_stat.guid;
> -	else
> +	if (up_req->msg.req_type != DP_CONNECTION_STATUS_NOTIFY &&
> +	    up_req->msg.req_type != DP_RESOURCE_STATUS_NOTIFY) {
> +		DRM_DEBUG_KMS("Received unknown up req type, ignoring: %x\n",
> +			      up_req->msg.req_type);
> +		kfree(up_req);
>  		goto out;
> -
> -	drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno,
> -				 false);
> -
> -	if (!mstb) {
> -		mstb = drm_dp_get_mst_branch_device_by_guid(mgr, guid);
> -		if (!mstb) {
> -			DRM_DEBUG_KMS("Got MST reply from unknown device %d\n",
> -				      hdr->lct);
> -			goto out;
> -		}
>  	}
>  
> -	if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
> -		drm_dp_mst_handle_conn_stat(mstb, &msg.u.conn_stat);
> +	drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, up_req->msg.req_type,
> +				 seqno, false);
> +
> +	if (up_req->msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
> +		const struct drm_dp_connection_status_notify *conn_stat =
> +			&up_req->msg.u.conn_stat;
>  
>  		DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n",
> -			      msg.u.conn_stat.port_number,
> -			      msg.u.conn_stat.legacy_device_plug_status,
> -			      msg.u.conn_stat.displayport_device_plug_status,
> -			      msg.u.conn_stat.message_capability_status,
> -			      msg.u.conn_stat.input_port,
> -			      msg.u.conn_stat.peer_device_type);
> +			      conn_stat->port_number,
> +			      conn_stat->legacy_device_plug_status,
> +			      conn_stat->displayport_device_plug_status,
> +			      conn_stat->message_capability_status,
> +			      conn_stat->input_port,
> +			      conn_stat->peer_device_type);
> +	} else if (up_req->msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
> +		const struct drm_dp_resource_status_notify *res_stat =
> +			&up_req->msg.u.resource_stat;
>  
> -		drm_kms_helper_hotplug_event(mgr->dev);
> -	} else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
>  		DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n",
> -			      msg.u.resource_stat.port_number,
> -			      msg.u.resource_stat.available_pbn);
> +			      res_stat->port_number,
> +			      res_stat->available_pbn);
>  	}
>  
> -	drm_dp_mst_topology_put_mstb(mstb);
> +	up_req->hdr = *hdr;
> +	mutex_lock(&mgr->up_req_lock);
> +	list_add_tail(&up_req->next, &mgr->up_req_list);
> +	mutex_unlock(&mgr->up_req_lock);
> +	queue_work(system_long_wq, &mgr->up_req_work);
> +
>  out:
>  	memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
>  	return 0;
> @@ -4320,12 +4382,15 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
>  	mutex_init(&mgr->qlock);
>  	mutex_init(&mgr->payload_lock);
>  	mutex_init(&mgr->delayed_destroy_lock);
> +	mutex_init(&mgr->up_req_lock);
>  	INIT_LIST_HEAD(&mgr->tx_msg_downq);
>  	INIT_LIST_HEAD(&mgr->destroy_port_list);
>  	INIT_LIST_HEAD(&mgr->destroy_branch_device_list);
> +	INIT_LIST_HEAD(&mgr->up_req_list);
>  	INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work);
>  	INIT_WORK(&mgr->tx_work, drm_dp_tx_work);
>  	INIT_WORK(&mgr->delayed_destroy_work, drm_dp_delayed_destroy_work);
> +	INIT_WORK(&mgr->up_req_work, drm_dp_mst_up_req_work);
>  	init_waitqueue_head(&mgr->tx_waitq);
>  	mgr->dev = dev;
>  	mgr->aux = aux;
> @@ -4382,6 +4447,7 @@ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
>  	mutex_destroy(&mgr->payload_lock);
>  	mutex_destroy(&mgr->qlock);
>  	mutex_destroy(&mgr->lock);
> +	mutex_destroy(&mgr->up_req_lock);
>  }
>  EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);
>  
> diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
> index 8ba2a01324bb..7d80c38ee00e 100644
> --- a/include/drm/drm_dp_mst_helper.h
> +++ b/include/drm/drm_dp_mst_helper.h
> @@ -597,6 +597,22 @@ struct drm_dp_mst_topology_mgr {
>  	 * devices, needed to avoid locking inversion.
>  	 */
>  	struct work_struct delayed_destroy_work;
> +
> +	/**
> +	 * @up_req_list: List of pending up requests from the topology that
> +	 * need to be processed, in chronological order.
> +	 */
> +	struct list_head up_req_list;
> +	/**
> +	 * @up_req_lock: Protects @up_req_list
> +	 */
> +	struct mutex up_req_lock;
> +	/**
> +	 * @up_req_work: Work item to process up requests received from the
> +	 * topology. Needed to avoid blocking hotplug handling and sideband
> +	 * transmissions.
> +	 */
> +	struct work_struct up_req_work;
>  };
>  
>  int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
> -- 
> 2.21.0
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 20/27] drm/dp_mst: Protect drm_dp_mst_port members with connection_mutex
  2019-09-03 20:45 ` [PATCH v2 20/27] drm/dp_mst: Protect drm_dp_mst_port members with connection_mutex Lyude Paul
@ 2019-09-25 20:00   ` Sean Paul
  2019-09-25 21:01     ` Lyude Paul
  0 siblings, 1 reply; 62+ messages in thread
From: Sean Paul @ 2019-09-25 20:00 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

On Tue, Sep 03, 2019 at 04:45:58PM -0400, Lyude Paul wrote:
> Yes-you read that right. Currently there is literally no locking in
> place for any of the drm_dp_mst_port struct members that can be modified
> in response to a link address response, or a connection status response.
> Which literally means if we're unlucky enough to have any sort of
> hotplugging event happen before we're finished with reprobing link
> addresses, we'll race and the contents of said struct members becomes
> undefined. Fun!
> 
> So, finally add some simple locking protections to our MST helpers by
> protecting any drm_dp_mst_port members which can be changed by link
> address responses or connection status notifications under
> drm_device->mode_config.connection_mutex.
> 
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>
> ---
>  drivers/gpu/drm/drm_dp_mst_topology.c | 144 +++++++++++++++++++-------
>  include/drm/drm_dp_mst_helper.h       |  39 +++++--
>  2 files changed, 133 insertions(+), 50 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
> index 5101eeab4485..259634c5d6dc 100644
> --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> @@ -1354,6 +1354,7 @@ static void drm_dp_free_mst_port(struct kref *kref)
>  		container_of(kref, struct drm_dp_mst_port, malloc_kref);
>  
>  	drm_dp_mst_put_mstb_malloc(port->parent);
> +	mutex_destroy(&port->lock);
>  	kfree(port);
>  }
>  
> @@ -1906,6 +1907,36 @@ void drm_dp_mst_connector_early_unregister(struct drm_connector *connector,
>  }
>  EXPORT_SYMBOL(drm_dp_mst_connector_early_unregister);
>  
> +static void
> +drm_dp_mst_port_add_connector(struct drm_dp_mst_branch *mstb,
> +			      struct drm_dp_mst_port *port)
> +{
> +	struct drm_dp_mst_topology_mgr *mgr = port->mgr;
> +	char proppath[255];
> +	int ret;
> +
> +	build_mst_prop_path(mstb, port->port_num, proppath, sizeof(proppath));
> +	port->connector = mgr->cbs->add_connector(mgr, port, proppath);
> +	if (!port->connector) {
> +		ret = -ENOMEM;
> +		goto error;
> +	}
> +
> +	if ((port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
> +	     port->pdt == DP_PEER_DEVICE_SST_SINK) &&
> +	    port->port_num >= DP_MST_LOGICAL_PORT_0) {
> +		port->cached_edid = drm_get_edid(port->connector,
> +						 &port->aux.ddc);
> +		drm_connector_set_tile_property(port->connector);
> +	}
> +
> +	mgr->cbs->register_connector(port->connector);
> +	return;
> +
> +error:
> +	DRM_ERROR("Failed to create connector for port %p: %d\n", port, ret);
> +}
> +
>  static void
>  drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
>  				    struct drm_device *dev,
> @@ -1913,8 +1944,12 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
>  {
>  	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
>  	struct drm_dp_mst_port *port;
> -	bool created = false;
> -	int old_ddps = 0;
> +	struct drm_dp_mst_branch *child_mstb = NULL;
> +	struct drm_connector *connector_to_destroy = NULL;
> +	int old_ddps = 0, ret;
> +	u8 new_pdt = DP_PEER_DEVICE_NONE;
> +	bool created = false, send_link_addr = false,
> +	     create_connector = false;
>  
>  	port = drm_dp_get_port(mstb, port_msg->port_number);
>  	if (!port) {
> @@ -1923,6 +1958,7 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
>  			return;
>  		kref_init(&port->topology_kref);
>  		kref_init(&port->malloc_kref);
> +		mutex_init(&port->lock);
>  		port->parent = mstb;
>  		port->port_num = port_msg->port_number;
>  		port->mgr = mgr;
> @@ -1937,11 +1973,17 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
>  		drm_dp_mst_get_mstb_malloc(mstb);
>  
>  		created = true;
> -	} else {
> -		old_ddps = port->ddps;
>  	}
>  
> +	mutex_lock(&port->lock);
> +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> +
> +	if (!created)
> +		old_ddps = port->ddps;
> +
>  	port->input = port_msg->input_port;
> +	if (!port->input)
> +		new_pdt = port_msg->peer_device_type;
>  	port->mcs = port_msg->mcs;
>  	port->ddps = port_msg->ddps;
>  	port->ldps = port_msg->legacy_device_plug_status;
> @@ -1969,44 +2011,58 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
>  		}
>  	}
>  
> -	if (!port->input) {
> -		int ret = drm_dp_port_set_pdt(port,
> -					      port_msg->peer_device_type);
> -		if (ret == 1) {
> -			drm_dp_send_link_address(mgr, port->mstb);
> -		} else if (ret < 0) {
> -			DRM_ERROR("Failed to change PDT on port %p: %d\n",
> -				  port, ret);
> -			goto fail;
> +	ret = drm_dp_port_set_pdt(port, new_pdt);
> +	if (ret == 1) {
> +		send_link_addr = true;
> +	} else if (ret < 0) {
> +		DRM_ERROR("Failed to change PDT on port %p: %d\n",
> +			  port, ret);
> +		goto fail_unlock;
> +	}
> +
> +	if (send_link_addr) {
> +		mutex_lock(&mgr->lock);
> +		if (port->mstb) {
> +			child_mstb = port->mstb;
> +			drm_dp_mst_get_mstb_malloc(child_mstb);
>  		}
> +		mutex_unlock(&mgr->lock);
>  	}
>  
> -	if (created && !port->input) {
> -		char proppath[255];
> +	/*
> +	 * We unset port->connector before dropping connection_mutex so that
> +	 * there's no chance any of the atomic MST helpers can accidentally
> +	 * associate a to-be-destroyed connector with a port.
> +	 */
> +	if (port->connector && port->input) {
> +		connector_to_destroy = port->connector;
> +		port->connector = NULL;
> +	} else if (!port->connector && !port->input) {
> +		create_connector = true;
> +	}
>  
> -		build_mst_prop_path(mstb, port->port_num, proppath,
> -				    sizeof(proppath));
> -		port->connector = (*mgr->cbs->add_connector)(mgr, port,
> -							     proppath);
> -		if (!port->connector)
> -			goto fail;
> +	drm_modeset_unlock(&dev->mode_config.connection_mutex);

Do you drop this early b/c it deadlocks with something upstack? If so, I
wonder if you could plumb an acquire context through the appropriate
functions to avoid needing the port->lock at all?

Sean

>  
> -		if ((port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
> -		     port->pdt == DP_PEER_DEVICE_SST_SINK) &&
> -		    port->port_num >= DP_MST_LOGICAL_PORT_0) {
> -			port->cached_edid = drm_get_edid(port->connector,
> -							 &port->aux.ddc);
> -			drm_connector_set_tile_property(port->connector);
> -		}
> +	if (connector_to_destroy)
> +		mgr->cbs->destroy_connector(mgr, connector_to_destroy);
> +	else if (create_connector)
> +		drm_dp_mst_port_add_connector(mstb, port);
> +
> +	mutex_unlock(&port->lock);
>  
> -		(*mgr->cbs->register_connector)(port->connector);
> +	if (send_link_addr && child_mstb) {
> +		drm_dp_send_link_address(mgr, child_mstb);
> +		drm_dp_mst_put_mstb_malloc(child_mstb);
>  	}
>  
>  	/* put reference to this port */
>  	drm_dp_mst_topology_put_port(port);
>  	return;
>  
> -fail:
> +fail_unlock:
> +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
> +	mutex_unlock(&port->lock);
> +
>  	/* Remove it from the port list */
>  	mutex_lock(&mgr->lock);
>  	list_del(&port->next);
> @@ -2022,6 +2078,7 @@ static void
>  drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
>  			    struct drm_dp_connection_status_notify *conn_stat)
>  {
> +	struct drm_device *dev = mstb->mgr->dev;
>  	struct drm_dp_mst_port *port;
>  	int old_ddps;
>  	bool dowork = false;
> @@ -2030,6 +2087,8 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
>  	if (!port)
>  		return;
>  
> +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> +
>  	old_ddps = port->ddps;
>  	port->mcs = conn_stat->message_capability_status;
>  	port->ldps = conn_stat->legacy_device_plug_status;
> @@ -2055,6 +2114,7 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
>  		}
>  	}
>  
> +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
>  	drm_dp_mst_topology_put_port(port);
>  	if (dowork)
>  		queue_work(system_long_wq, &mstb->mgr->work);
> @@ -2147,28 +2207,34 @@ drm_dp_get_mst_branch_device_by_guid(struct drm_dp_mst_topology_mgr *mgr,
>  static void drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
>  					       struct drm_dp_mst_branch *mstb)
>  {
> +	struct drm_device *dev = mgr->dev;
>  	struct drm_dp_mst_port *port;
> -	struct drm_dp_mst_branch *mstb_child;
> +
>  	if (!mstb->link_address_sent)
>  		drm_dp_send_link_address(mgr, mstb);
>  
>  	list_for_each_entry(port, &mstb->ports, next) {
> -		if (port->input)
> -			continue;
> +		struct drm_dp_mst_branch *mstb_child = NULL;
> +
> +		drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>  
> -		if (!port->ddps)
> +		if (port->input || !port->ddps) {
> +			drm_modeset_unlock(&dev->mode_config.connection_mutex);
>  			continue;
> +		}
>  
>  		if (!port->available_pbn)
>  			drm_dp_send_enum_path_resources(mgr, mstb, port);
>  
> -		if (port->mstb) {
> +		if (port->mstb)
>  			mstb_child = drm_dp_mst_topology_get_mstb_validated(
>  			    mgr, port->mstb);
> -			if (mstb_child) {
> -				drm_dp_check_and_send_link_address(mgr, mstb_child);
> -				drm_dp_mst_topology_put_mstb(mstb_child);
> -			}
> +
> +		drm_modeset_unlock(&dev->mode_config.connection_mutex);
> +
> +		if (mstb_child) {
> +			drm_dp_check_and_send_link_address(mgr, mstb_child);
> +			drm_dp_mst_topology_put_mstb(mstb_child);
>  		}
>  	}
>  }
> diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
> index 7d80c38ee00e..1efbb086f7ac 100644
> --- a/include/drm/drm_dp_mst_helper.h
> +++ b/include/drm/drm_dp_mst_helper.h
> @@ -45,23 +45,34 @@ struct drm_dp_vcpi {
>  /**
>   * struct drm_dp_mst_port - MST port
>   * @port_num: port number
> - * @input: if this port is an input port.
> - * @mcs: message capability status - DP 1.2 spec.
> - * @ddps: DisplayPort Device Plug Status - DP 1.2
> - * @pdt: Peer Device Type
> - * @ldps: Legacy Device Plug Status
> - * @dpcd_rev: DPCD revision of device on this port
> - * @num_sdp_streams: Number of simultaneous streams
> - * @num_sdp_stream_sinks: Number of stream sinks
> - * @available_pbn: Available bandwidth for this port.
> + * @input: if this port is an input port. Protected by
> + * &drm_device.mode_config.connection_mutex.
> + * @mcs: message capability status - DP 1.2 spec. Protected by
> + * &drm_device.mode_config.connection_mutex.
> + * @ddps: DisplayPort Device Plug Status - DP 1.2. Protected by
> + * &drm_device.mode_config.connection_mutex.
> + * @pdt: Peer Device Type. Protected by
> + * &drm_device.mode_config.connection_mutex.
> + * @ldps: Legacy Device Plug Status. Protected by
> + * &drm_device.mode_config.connection_mutex.
> + * @dpcd_rev: DPCD revision of device on this port. Protected by
> + * &drm_device.mode_config.connection_mutex.
> + * @num_sdp_streams: Number of simultaneous streams. Protected by
> + * &drm_device.mode_config.connection_mutex.
> + * @num_sdp_stream_sinks: Number of stream sinks. Protected by
> + * &drm_device.mode_config.connection_mutex.
> + * @available_pbn: Available bandwidth for this port. Protected by
> + * &drm_device.mode_config.connection_mutex.
>   * @next: link to next port on this branch device
>   * @mstb: branch device on this port, protected by
>   * &drm_dp_mst_topology_mgr.lock
>   * @aux: i2c aux transport to talk to device connected to this port, protected
> - * by &drm_dp_mst_topology_mgr.lock
> + * by &drm_device.mode_config.connection_mutex.
>   * @parent: branch device parent of this port
>   * @vcpi: Virtual Channel Payload info for this port.
> - * @connector: DRM connector this port is connected to.
> + * @connector: DRM connector this port is connected to. Protected by @lock.
> + * When there is already a connector registered for this port, this is also
> + * protected by &drm_device.mode_config.connection_mutex.
>   * @mgr: topology manager this port lives under.
>   *
>   * This structure represents an MST port endpoint on a device somewhere
> @@ -100,6 +111,12 @@ struct drm_dp_mst_port {
>  	struct drm_connector *connector;
>  	struct drm_dp_mst_topology_mgr *mgr;
>  
> +	/**
> +	 * @lock: Protects @connector. If needed, this lock should be grabbed
> +	 * before &drm_device.mode_config.connection_mutex.
> +	 */
> +	struct mutex lock;
> +
>  	/**
>  	 * @cached_edid: for DP logical ports - make tiling work by ensuring
>  	 * that the EDID for all connectors is read immediately.
> -- 
> 2.21.0
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 21/27] drm/dp_mst: Don't forget to update port->input in drm_dp_mst_handle_conn_stat()
  2019-09-03 20:45 ` [PATCH v2 21/27] drm/dp_mst: Don't forget to update port->input in drm_dp_mst_handle_conn_stat() Lyude Paul
@ 2019-09-25 20:03   ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2019-09-25 20:03 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

On Tue, Sep 03, 2019 at 04:45:59PM -0400, Lyude Paul wrote:
> This probably hasn't caused any problems up until now since it's
> probably nearly impossible to encounter this in the wild, however if we
> were to receive a connection status notification from the MST hub after
> resume while we're in the middle of reprobing the link addresses for a
> topology then there's a much larger chance that a port could have
> changed from being an output port to input port (or vice versa). If we
> forget to update this bit of information, we'll potentially ignore a
> valid PDT change on a downstream port because we think it's an input
> port.
> 
> So, make sure we read the input_port field in connection status
> notifications in drm_dp_mst_handle_conn_stat() to prevent this from
> happening once we've implemented suspend/resume reprobing.
> 
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>

Nice catch! Same comment here re: port->mutex, but we can sort that out on the
other thread

Reviewed-by: Sean Paul <sean@poorly.run>


> ---
>  drivers/gpu/drm/drm_dp_mst_topology.c | 51 +++++++++++++++++++--------
>  1 file changed, 37 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
> index 259634c5d6dc..e407aba1fbd2 100644
> --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> @@ -2078,18 +2078,23 @@ static void
>  drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
>  			    struct drm_dp_connection_status_notify *conn_stat)
>  {
> -	struct drm_device *dev = mstb->mgr->dev;
> +	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
> +	struct drm_device *dev = mgr->dev;
>  	struct drm_dp_mst_port *port;
> -	int old_ddps;
> -	bool dowork = false;
> +	struct drm_connector *connector_to_destroy = NULL;
> +	int old_ddps, ret;
> +	u8 new_pdt;
> +	bool dowork = false, create_connector = false;
>  
>  	port = drm_dp_get_port(mstb, conn_stat->port_number);
>  	if (!port)
>  		return;
>  
> +	mutex_lock(&port->lock);
>  	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>  
>  	old_ddps = port->ddps;
> +	port->input = conn_stat->input_port;
>  	port->mcs = conn_stat->message_capability_status;
>  	port->ldps = conn_stat->legacy_device_plug_status;
>  	port->ddps = conn_stat->displayport_device_plug_status;
> @@ -2102,23 +2107,41 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
>  		}
>  	}
>  
> -	if (!port->input) {
> -		int ret = drm_dp_port_set_pdt(port,
> -					      conn_stat->peer_device_type);
> -		if (ret == 1) {
> -			dowork = true;
> -		} else if (ret < 0) {
> -			DRM_ERROR("Failed to change PDT for port %p: %d\n",
> -				  port, ret);
> -			dowork = false;
> -		}
> +	new_pdt = port->input ? DP_PEER_DEVICE_NONE : conn_stat->peer_device_type;
> +
> +	ret = drm_dp_port_set_pdt(port, new_pdt);
> +	if (ret == 1) {
> +		dowork = true;
> +	} else if (ret < 0) {
> +		DRM_ERROR("Failed to change PDT for port %p: %d\n",
> +			  port, ret);
> +		dowork = false;
> +	}
> +
> +	/*
> +	 * We unset port->connector before dropping connection_mutex so that
> +	 * there's no chance any of the atomic MST helpers can accidentally
> +	 * associate a to-be-destroyed connector with a port.
> +	 */
> +	if (port->connector && port->input) {
> +		connector_to_destroy = port->connector;
> +		port->connector = NULL;
> +	} else if (!port->connector && !port->input) {
> +		create_connector = true;
>  	}
>  
>  	drm_modeset_unlock(&dev->mode_config.connection_mutex);
> +
> +	if (connector_to_destroy)
> +		mgr->cbs->destroy_connector(mgr, connector_to_destroy);
> +	else if (create_connector)
> +		drm_dp_mst_port_add_connector(mstb, port);
> +
> +	mutex_unlock(&port->lock);
> +
>  	drm_dp_mst_topology_put_port(port);
>  	if (dowork)
>  		queue_work(system_long_wq, &mstb->mgr->work);
> -
>  }
>  
>  static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_topology_mgr *mgr,
> -- 
> 2.21.0
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 22/27] drm/nouveau: Don't grab runtime PM refs for HPD IRQs
  2019-09-03 20:46 ` [PATCH v2 22/27] drm/nouveau: Don't grab runtime PM refs for HPD IRQs Lyude Paul
@ 2019-09-25 20:06   ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2019-09-25 20:06 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Ben Skeggs, David Airlie,
	Daniel Vetter, linux-kernel

On Tue, Sep 03, 2019 at 04:46:00PM -0400, Lyude Paul wrote:
> In order for suspend/resume reprobing to work, we need to be able to
> perform sideband communications during suspend/resume, along with
> runtime PM suspend/resume. In order to do so, we also need to make sure
> that nouveau doesn't bother grabbing a runtime PM reference to do so,
> since otherwise we'll start deadlocking runtime PM again.
> 
> Note that we weren't able to do this before, because of the DP MST
> helpers processing UP requests from topologies in the same context as
> drm_dp_mst_hpd_irq() which would have caused us to open ourselves up to
> receiving hotplug events and deadlocking with runtime suspend/resume.
> Now that those requests are handled asynchronously, this change should
> be completely safe.
> 
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>

Seems reasonable to me, but would feel better if a nouveau person confirmed

Reviewed-by: Sean Paul <sean@poorly.run>


> ---
>  drivers/gpu/drm/nouveau/nouveau_connector.c | 33 +++++++++++----------
>  1 file changed, 17 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
> index 56871d34e3fb..f276918d3f3b 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_connector.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
> @@ -1131,6 +1131,16 @@ nouveau_connector_hotplug(struct nvif_notify *notify)
>  	const char *name = connector->name;
>  	struct nouveau_encoder *nv_encoder;
>  	int ret;
> +	bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG);
> +
> +	if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) {
> +		NV_DEBUG(drm, "service %s\n", name);
> +		drm_dp_cec_irq(&nv_connector->aux);
> +		if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP)))
> +			nv50_mstm_service(nv_encoder->dp.mstm);
> +
> +		return NVIF_NOTIFY_KEEP;
> +	}
>  
>  	ret = pm_runtime_get(drm->dev->dev);
>  	if (ret == 0) {
> @@ -1151,25 +1161,16 @@ nouveau_connector_hotplug(struct nvif_notify *notify)
>  		return NVIF_NOTIFY_DROP;
>  	}
>  
> -	if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) {
> -		NV_DEBUG(drm, "service %s\n", name);
> -		drm_dp_cec_irq(&nv_connector->aux);
> -		if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP)))
> -			nv50_mstm_service(nv_encoder->dp.mstm);
> -	} else {
> -		bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG);
> -
> +	if (!plugged)
> +		drm_dp_cec_unset_edid(&nv_connector->aux);
> +	NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name);
> +	if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP))) {
>  		if (!plugged)
> -			drm_dp_cec_unset_edid(&nv_connector->aux);
> -		NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name);
> -		if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP))) {
> -			if (!plugged)
> -				nv50_mstm_remove(nv_encoder->dp.mstm);
> -		}
> -
> -		drm_helper_hpd_irq_event(connector->dev);
> +			nv50_mstm_remove(nv_encoder->dp.mstm);
>  	}
>  
> +	drm_helper_hpd_irq_event(connector->dev);
> +
>  	pm_runtime_mark_last_busy(drm->dev->dev);
>  	pm_runtime_put_autosuspend(drm->dev->dev);
>  	return NVIF_NOTIFY_KEEP;
> -- 
> 2.21.0
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 03/27] drm/dp_mst: Destroy MSTBs asynchronously
  2019-09-25 18:16   ` Sean Paul
@ 2019-09-25 20:08     ` Lyude Paul
  2019-09-27 13:31       ` Sean Paul
  0 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-25 20:08 UTC (permalink / raw)
  To: Sean Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	David Airlie, linux-kernel

On Wed, 2019-09-25 at 14:16 -0400, Sean Paul wrote:
> On Tue, Sep 03, 2019 at 04:45:41PM -0400, Lyude Paul wrote:
> > When reprobing an MST topology during resume, we have to account for the
> > fact that while we were suspended it's possible that mstbs may have been
> > removed from any ports in the topology. Since iterating downwards in the
> > topology requires that we hold &mgr->lock, destroying MSTBs from this
> > context would result in attempting to lock &mgr->lock a second time and
> > deadlocking.
> > 
> > So, fix this by first moving destruction of MSTBs into
> > destroy_connector_work, then rename destroy_connector_work and friends
> > to reflect that they now destroy both ports and mstbs.
> > 
> > Changes since v1:
> > * s/destroy_connector_list/destroy_port_list/
> >   s/connector_destroy_lock/delayed_destroy_lock/
> >   s/connector_destroy_work/delayed_destroy_work/
> >   s/drm_dp_finish_destroy_branch_device/drm_dp_delayed_destroy_mstb/
> >   s/drm_dp_finish_destroy_port/drm_dp_delayed_destroy_port/
> >   - danvet
> > * Use two loops in drm_dp_delayed_destroy_work() - danvet
> > * Better explain why we need to do this - danvet
> > * Use cancel_work_sync() instead of flush_work() - flush_work() doesn't
> >   account for work requeing
> > 
> > Cc: Juston Li <juston.li@intel.com>
> > Cc: Imre Deak <imre.deak@intel.com>
> > Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > Cc: Harry Wentland <hwentlan@amd.com>
> > Cc: Daniel Vetter <daniel@ffwll.ch>
> > Signed-off-by: Lyude Paul <lyude@redhat.com>
> 
> Took me a while to grok this, and I'm still not 100% confident my mental
> model
> is correct, so please bear with me while I ask silly questions :)
> 
> Now that the destroy is delayed, and the port remains in the topology, is it
> possible we will underflow the topology kref by calling put_mstb multiple
> times?
> It looks like that would result in a WARN from refcount.c, and wouldn't call
> the
> destroy function multiple times, so that's nice :)
> 
> Similarly, is there any defense against calling get_mstb() between destroy()
> and
> the delayed destroy worker running?
> 
Good question! There's only one instance where we unconditionally grab an MSTB
ref, drm_dp_mst_topology_mgr_set_mst(), and in that location we're guaranteed
to be the only one with access to that mstb until we drop &mgr->lock.
Everywhere else we use drm_dp_mst_topology_try_get_mstb(), which uses
kref_get_unless_zero() to protect against that kind of situation (and forces
the caller to check with __must_check)

> Sean
> 
> > ---
> >  drivers/gpu/drm/drm_dp_mst_topology.c | 162 +++++++++++++++++---------
> >  include/drm/drm_dp_mst_helper.h       |  26 +++--
> >  2 files changed, 127 insertions(+), 61 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c
> > b/drivers/gpu/drm/drm_dp_mst_topology.c
> > index 3054ec622506..738f260d4b15 100644
> > --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> > +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> > @@ -1113,34 +1113,17 @@ static void
> > drm_dp_destroy_mst_branch_device(struct kref *kref)
> >  	struct drm_dp_mst_branch *mstb =
> >  		container_of(kref, struct drm_dp_mst_branch, topology_kref);
> >  	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
> > -	struct drm_dp_mst_port *port, *tmp;
> > -	bool wake_tx = false;
> >  
> > -	mutex_lock(&mgr->lock);
> > -	list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
> > -		list_del(&port->next);
> > -		drm_dp_mst_topology_put_port(port);
> > -	}
> > -	mutex_unlock(&mgr->lock);
> > -
> > -	/* drop any tx slots msg */
> > -	mutex_lock(&mstb->mgr->qlock);
> > -	if (mstb->tx_slots[0]) {
> > -		mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
> > -		mstb->tx_slots[0] = NULL;
> > -		wake_tx = true;
> > -	}
> > -	if (mstb->tx_slots[1]) {
> > -		mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
> > -		mstb->tx_slots[1] = NULL;
> > -		wake_tx = true;
> > -	}
> > -	mutex_unlock(&mstb->mgr->qlock);
> > +	INIT_LIST_HEAD(&mstb->destroy_next);
> >  
> > -	if (wake_tx)
> > -		wake_up_all(&mstb->mgr->tx_waitq);
> > -
> > -	drm_dp_mst_put_mstb_malloc(mstb);
> > +	/*
> > +	 * This can get called under mgr->mutex, so we need to perform the
> > +	 * actual destruction of the mstb in another worker
> > +	 */
> > +	mutex_lock(&mgr->delayed_destroy_lock);
> > +	list_add(&mstb->destroy_next, &mgr->destroy_branch_device_list);
> > +	mutex_unlock(&mgr->delayed_destroy_lock);
> > +	schedule_work(&mgr->delayed_destroy_work);
> >  }
> >  
> >  /**
> > @@ -1255,10 +1238,10 @@ static void drm_dp_destroy_port(struct kref *kref)
> >  			 * we might be holding the mode_config.mutex
> >  			 * from an EDID retrieval */
> >  
> > -			mutex_lock(&mgr->destroy_connector_lock);
> > -			list_add(&port->next, &mgr->destroy_connector_list);
> > -			mutex_unlock(&mgr->destroy_connector_lock);
> > -			schedule_work(&mgr->destroy_connector_work);
> > +			mutex_lock(&mgr->delayed_destroy_lock);
> > +			list_add(&port->next, &mgr->destroy_port_list);
> > +			mutex_unlock(&mgr->delayed_destroy_lock);
> > +			schedule_work(&mgr->delayed_destroy_work);
> >  			return;
> >  		}
> >  		/* no need to clean up vcpi
> > @@ -2792,7 +2775,7 @@ void drm_dp_mst_topology_mgr_suspend(struct
> > drm_dp_mst_topology_mgr *mgr)
> >  			   DP_MST_EN | DP_UPSTREAM_IS_SRC);
> >  	mutex_unlock(&mgr->lock);
> >  	flush_work(&mgr->work);
> > -	flush_work(&mgr->destroy_connector_work);
> > +	flush_work(&mgr->delayed_destroy_work);
> >  }
> >  EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend);
> >  
> > @@ -3740,34 +3723,104 @@ static void drm_dp_tx_work(struct work_struct
> > *work)
> >  	mutex_unlock(&mgr->qlock);
> >  }
> >  
> > -static void drm_dp_destroy_connector_work(struct work_struct *work)
> > +static inline void
> > +drm_dp_delayed_destroy_port(struct drm_dp_mst_port *port)
> >  {
> > -	struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct
> > drm_dp_mst_topology_mgr, destroy_connector_work);
> > -	struct drm_dp_mst_port *port;
> > -	bool send_hotplug = false;
> > +	port->mgr->cbs->destroy_connector(port->mgr, port->connector);
> > +
> > +	drm_dp_port_teardown_pdt(port, port->pdt);
> > +	port->pdt = DP_PEER_DEVICE_NONE;
> > +
> > +	drm_dp_mst_put_port_malloc(port);
> > +}
> > +
> > +static inline void
> > +drm_dp_delayed_destroy_mstb(struct drm_dp_mst_branch *mstb)
> > +{
> > +	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
> > +	struct drm_dp_mst_port *port, *tmp;
> > +	bool wake_tx = false;
> > +
> > +	mutex_lock(&mgr->lock);
> > +	list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
> > +		list_del(&port->next);
> > +		drm_dp_mst_topology_put_port(port);
> > +	}
> > +	mutex_unlock(&mgr->lock);
> > +
> > +	/* drop any tx slots msg */
> > +	mutex_lock(&mstb->mgr->qlock);
> > +	if (mstb->tx_slots[0]) {
> > +		mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
> > +		mstb->tx_slots[0] = NULL;
> > +		wake_tx = true;
> > +	}
> > +	if (mstb->tx_slots[1]) {
> > +		mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
> > +		mstb->tx_slots[1] = NULL;
> > +		wake_tx = true;
> > +	}
> > +	mutex_unlock(&mstb->mgr->qlock);
> > +
> > +	if (wake_tx)
> > +		wake_up_all(&mstb->mgr->tx_waitq);
> > +
> > +	drm_dp_mst_put_mstb_malloc(mstb);
> > +}
> > +
> > +static void drm_dp_delayed_destroy_work(struct work_struct *work)
> > +{
> > +	struct drm_dp_mst_topology_mgr *mgr =
> > +		container_of(work, struct drm_dp_mst_topology_mgr,
> > +			     delayed_destroy_work);
> > +	bool send_hotplug = false, go_again;
> > +
> >  	/*
> >  	 * Not a regular list traverse as we have to drop the destroy
> > -	 * connector lock before destroying the connector, to avoid AB->BA
> > +	 * connector lock before destroying the mstb/port, to avoid AB->BA
> >  	 * ordering between this lock and the config mutex.
> >  	 */
> > -	for (;;) {
> > -		mutex_lock(&mgr->destroy_connector_lock);
> > -		port = list_first_entry_or_null(&mgr->destroy_connector_list,
> > struct drm_dp_mst_port, next);
> > -		if (!port) {
> > -			mutex_unlock(&mgr->destroy_connector_lock);
> > -			break;
> > +	do {
> > +		go_again = false;
> > +
> > +		for (;;) {
> > +			struct drm_dp_mst_branch *mstb;
> > +
> > +			mutex_lock(&mgr->delayed_destroy_lock);
> > +			mstb = list_first_entry_or_null(&mgr-
> > >destroy_branch_device_list,
> > +							struct
> > drm_dp_mst_branch,
> > +							destroy_next);
> > +			if (mstb)
> > +				list_del(&mstb->destroy_next);
> > +			mutex_unlock(&mgr->delayed_destroy_lock);
> > +
> > +			if (!mstb)
> > +				break;
> > +
> > +			drm_dp_delayed_destroy_mstb(mstb);
> > +			go_again = true;
> >  		}
> > -		list_del(&port->next);
> > -		mutex_unlock(&mgr->destroy_connector_lock);
> >  
> > -		mgr->cbs->destroy_connector(mgr, port->connector);
> > +		for (;;) {
> > +			struct drm_dp_mst_port *port;
> >  
> > -		drm_dp_port_teardown_pdt(port, port->pdt);
> > -		port->pdt = DP_PEER_DEVICE_NONE;
> > +			mutex_lock(&mgr->delayed_destroy_lock);
> > +			port = list_first_entry_or_null(&mgr-
> > >destroy_port_list,
> > +							struct
> > drm_dp_mst_port,
> > +							next);
> > +			if (port)
> > +				list_del(&port->next);
> > +			mutex_unlock(&mgr->delayed_destroy_lock);
> > +
> > +			if (!port)
> > +				break;
> > +
> > +			drm_dp_delayed_destroy_port(port);
> > +			send_hotplug = true;
> > +			go_again = true;
> > +		}
> > +	} while (go_again);
> >  
> > -		drm_dp_mst_put_port_malloc(port);
> > -		send_hotplug = true;
> > -	}
> >  	if (send_hotplug)
> >  		drm_kms_helper_hotplug_event(mgr->dev);
> >  }
> > @@ -3957,12 +4010,13 @@ int drm_dp_mst_topology_mgr_init(struct
> > drm_dp_mst_topology_mgr *mgr,
> >  	mutex_init(&mgr->lock);
> >  	mutex_init(&mgr->qlock);
> >  	mutex_init(&mgr->payload_lock);
> > -	mutex_init(&mgr->destroy_connector_lock);
> > +	mutex_init(&mgr->delayed_destroy_lock);
> >  	INIT_LIST_HEAD(&mgr->tx_msg_downq);
> > -	INIT_LIST_HEAD(&mgr->destroy_connector_list);
> > +	INIT_LIST_HEAD(&mgr->destroy_port_list);
> > +	INIT_LIST_HEAD(&mgr->destroy_branch_device_list);
> >  	INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work);
> >  	INIT_WORK(&mgr->tx_work, drm_dp_tx_work);
> > -	INIT_WORK(&mgr->destroy_connector_work,
> > drm_dp_destroy_connector_work);
> > +	INIT_WORK(&mgr->delayed_destroy_work, drm_dp_delayed_destroy_work);
> >  	init_waitqueue_head(&mgr->tx_waitq);
> >  	mgr->dev = dev;
> >  	mgr->aux = aux;
> > @@ -4005,7 +4059,7 @@ void drm_dp_mst_topology_mgr_destroy(struct
> > drm_dp_mst_topology_mgr *mgr)
> >  {
> >  	drm_dp_mst_topology_mgr_set_mst(mgr, false);
> >  	flush_work(&mgr->work);
> > -	flush_work(&mgr->destroy_connector_work);
> > +	cancel_work_sync(&mgr->delayed_destroy_work);
> >  	mutex_lock(&mgr->payload_lock);
> >  	kfree(mgr->payloads);
> >  	mgr->payloads = NULL;
> > diff --git a/include/drm/drm_dp_mst_helper.h
> > b/include/drm/drm_dp_mst_helper.h
> > index fc349204a71b..4a4507fe928d 100644
> > --- a/include/drm/drm_dp_mst_helper.h
> > +++ b/include/drm/drm_dp_mst_helper.h
> > @@ -143,6 +143,12 @@ struct drm_dp_mst_branch {
> >  	 */
> >  	struct kref malloc_kref;
> >  
> > +	/**
> > +	 * @destroy_next: linked-list entry used by
> > +	 * drm_dp_delayed_destroy_work()
> > +	 */
> > +	struct list_head destroy_next;
> > +
> >  	u8 rad[8];
> >  	u8 lct;
> >  	int num_ports;
> > @@ -575,18 +581,24 @@ struct drm_dp_mst_topology_mgr {
> >  	struct work_struct tx_work;
> >  
> >  	/**
> > -	 * @destroy_connector_list: List of to be destroyed connectors.
> > +	 * @destroy_port_list: List of to be destroyed connectors.
> > +	 */
> > +	struct list_head destroy_port_list;
> > +	/**
> > +	 * @destroy_branch_device_list: List of to be destroyed branch
> > +	 * devices.
> >  	 */
> > -	struct list_head destroy_connector_list;
> > +	struct list_head destroy_branch_device_list;
> >  	/**
> > -	 * @destroy_connector_lock: Protects @connector_list.
> > +	 * @delayed_destroy_lock: Protects @destroy_port_list and
> > +	 * @destroy_branch_device_list.
> >  	 */
> > -	struct mutex destroy_connector_lock;
> > +	struct mutex delayed_destroy_lock;
> >  	/**
> > -	 * @destroy_connector_work: Work item to destroy connectors. Needed to
> > -	 * avoid locking inversion.
> > +	 * @delayed_destroy_work: Work item to destroy MST port and branch
> > +	 * devices, needed to avoid locking inversion.
> >  	 */
> > -	struct work_struct destroy_connector_work;
> > +	struct work_struct delayed_destroy_work;
> >  };
> >  
> >  int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
> > -- 
> > 2.21.0
> > 
-- 
Cheers,
	Lyude Paul


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 24/27] drm/amdgpu/dm: Resume short HPD IRQs before resuming MST topology
  2019-09-03 20:46 ` [PATCH v2 24/27] drm/amdgpu/dm: Resume short HPD IRQs before resuming MST topology Lyude Paul
  2019-09-13 20:46   ` Alex Deucher
@ 2019-09-25 20:08   ` Sean Paul
  2019-09-25 21:52   ` [PATCH v4] " Lyude Paul
  2 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2019-09-25 20:08 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Harry Wentland, Leo Li,
	Alex Deucher, Christian König, David (ChunMing) Zhou,
	David Airlie, Daniel Vetter, Nicholas Kazlauskas, David Francis,
	Mario Kleiner, Bhawanpreet Lakha, linux-kernel

On Tue, Sep 03, 2019 at 04:46:02PM -0400, Lyude Paul wrote:
> Since we're going to be reprobing the entire topology state on resume
> now using sideband transactions, we need to ensure that we actually have
> short HPD irqs enabled before calling drm_dp_mst_topology_mgr_resume().
> So, do that.
> 
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>
> ---
>  drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> index 73630e2940d4..4d3c8bff77da 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> @@ -1185,15 +1185,15 @@ static int dm_resume(void *handle)
>  	/* program HPD filter */
>  	dc_resume(dm->dc);
>  
> -	/* On resume we need to  rewrite the MSTM control bits to enamble MST*/
> -	s3_handle_mst(ddev, false);
> -
>  	/*
>  	 * early enable HPD Rx IRQ, should be done before set mode as short
>  	 * pulse interrupts are used for MST
>  	 */
>  	amdgpu_dm_irq_resume_early(adev);
>  
> +	/* On resume we need to  rewrite the MSTM control bits to enamble MST*/

While we're here,

s/  / / && s/enamble/enable/ && s_*/_ */_

> +	s3_handle_mst(ddev, false);
> +
>  	/* Do detection*/
>  	drm_connector_list_iter_begin(ddev, &iter);
>  	drm_for_each_connector_iter(connector, &iter) {
> -- 
> 2.21.0
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 16/27] drm/dp_mst: Refactor pdt setup/teardown, add more locking
  2019-09-25 19:27   ` Sean Paul
@ 2019-09-25 21:00     ` Lyude Paul
  2019-09-27 13:30       ` Sean Paul
  0 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-25 21:00 UTC (permalink / raw)
  To: Sean Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	David Airlie, Daniel Vetter, linux-kernel

On Wed, 2019-09-25 at 15:27 -0400, Sean Paul wrote:
> On Tue, Sep 03, 2019 at 04:45:54PM -0400, Lyude Paul wrote:
> > Since we're going to be implementing suspend/resume reprobing very soon,
> > we need to make sure we are extra careful to ensure that our locking
> > actually protects the topology state where we expect it to. Turns out
> > this isn't the case with drm_dp_port_setup_pdt() and
> > drm_dp_port_teardown_pdt(), both of which change port->mstb without
> > grabbing &mgr->lock.
> > 
> > Additionally, since most callers of these functions are just using it to
> > teardown the port's previous PDT and setup a new one we can simplify
> > things a bit and combine drm_dp_port_setup_pdt() and
> > drm_dp_port_teardown_pdt() into a single function:
> > drm_dp_port_set_pdt(). This function also handles actually ensuring that
> > we grab the correct locks when we need to modify port->mstb.
> > 
> > Cc: Juston Li <juston.li@intel.com>
> > Cc: Imre Deak <imre.deak@intel.com>
> > Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > Cc: Harry Wentland <hwentlan@amd.com>
> > Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> > Signed-off-by: Lyude Paul <lyude@redhat.com>
> > ---
> >  drivers/gpu/drm/drm_dp_mst_topology.c | 181 +++++++++++++++-----------
> >  include/drm/drm_dp_mst_helper.h       |   6 +-
> >  2 files changed, 110 insertions(+), 77 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c
> > b/drivers/gpu/drm/drm_dp_mst_topology.c
> > index d1610434a0cb..9944ef2ce885 100644
> > --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> > +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> > @@ -1487,24 +1487,6 @@ drm_dp_mst_topology_put_mstb(struct
> > drm_dp_mst_branch *mstb)
> >  	kref_put(&mstb->topology_kref, drm_dp_destroy_mst_branch_device);
> >  }
> >  
> > -static void drm_dp_port_teardown_pdt(struct drm_dp_mst_port *port, int
> > old_pdt)
> > -{
> > -	struct drm_dp_mst_branch *mstb;
> > -
> > -	switch (old_pdt) {
> > -	case DP_PEER_DEVICE_DP_LEGACY_CONV:
> > -	case DP_PEER_DEVICE_SST_SINK:
> > -		/* remove i2c over sideband */
> > -		drm_dp_mst_unregister_i2c_bus(&port->aux);
> > -		break;
> > -	case DP_PEER_DEVICE_MST_BRANCHING:
> > -		mstb = port->mstb;
> > -		port->mstb = NULL;
> > -		drm_dp_mst_topology_put_mstb(mstb);
> > -		break;
> > -	}
> > -}
> > -
> >  static void drm_dp_destroy_port(struct kref *kref)
> >  {
> >  	struct drm_dp_mst_port *port =
> > @@ -1714,38 +1696,79 @@ static u8 drm_dp_calculate_rad(struct
> > drm_dp_mst_port *port,
> >  	return parent_lct + 1;
> >  }
> >  
> > -/*
> > - * return sends link address for new mstb
> > - */
> > -static bool drm_dp_port_setup_pdt(struct drm_dp_mst_port *port)
> > +static int drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt)
> >  {
> > -	int ret;
> > -	u8 rad[6], lct;
> > -	bool send_link = false;
> > +	struct drm_dp_mst_topology_mgr *mgr = port->mgr;
> > +	struct drm_dp_mst_branch *mstb;
> > +	u8 rad[8], lct;
> > +	int ret = 0;
> > +
> > +	if (port->pdt == new_pdt)
> 
> Shouldn't we also ensure that access to port->pdt is also locked?
> 

It's specifically port->mstb that needs to be protected under lock. We don't
use port->pdt for traversing the topology at all, so keeping it under
connection_mutex is sufficient.

> Sean
> 
> > +		return 0;
> > +
> > +	/* Teardown the old pdt, if there is one */
> > +	switch (port->pdt) {
> > +	case DP_PEER_DEVICE_DP_LEGACY_CONV:
> > +	case DP_PEER_DEVICE_SST_SINK:
> > +		/*
> > +		 * If the new PDT would also have an i2c bus, don't bother
> > +		 * with reregistering it
> > +		 */
> > +		if (new_pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
> > +		    new_pdt == DP_PEER_DEVICE_SST_SINK) {
> > +			port->pdt = new_pdt;
> > +			return 0;
> > +		}
> > +
> > +		/* remove i2c over sideband */
> > +		drm_dp_mst_unregister_i2c_bus(&port->aux);
> > +		break;
> > +	case DP_PEER_DEVICE_MST_BRANCHING:
> > +		mutex_lock(&mgr->lock);
> > +		drm_dp_mst_topology_put_mstb(port->mstb);
> > +		port->mstb = NULL;
> > +		mutex_unlock(&mgr->lock);
> > +		break;
> > +	}
> > +
> > +	port->pdt = new_pdt;
> >  	switch (port->pdt) {
> >  	case DP_PEER_DEVICE_DP_LEGACY_CONV:
> >  	case DP_PEER_DEVICE_SST_SINK:
> >  		/* add i2c over sideband */
> >  		ret = drm_dp_mst_register_i2c_bus(&port->aux);
> >  		break;
> > +
> >  	case DP_PEER_DEVICE_MST_BRANCHING:
> >  		lct = drm_dp_calculate_rad(port, rad);
> > +		mstb = drm_dp_add_mst_branch_device(lct, rad);
> > +		if (!mstb) {
> > +			ret = -ENOMEM;
> > +			DRM_ERROR("Failed to create MSTB for port %p", port);
> > +			goto out;
> > +		}
> >  
> > -		port->mstb = drm_dp_add_mst_branch_device(lct, rad);
> > -		if (port->mstb) {
> > -			port->mstb->mgr = port->mgr;
> > -			port->mstb->port_parent = port;
> > -			/*
> > -			 * Make sure this port's memory allocation stays
> > -			 * around until its child MSTB releases it
> > -			 */
> > -			drm_dp_mst_get_port_malloc(port);
> > +		mutex_lock(&mgr->lock);
> > +		port->mstb = mstb;
> > +		mstb->mgr = port->mgr;
> > +		mstb->port_parent = port;
> >  
> > -			send_link = true;
> > -		}
> > +		/*
> > +		 * Make sure this port's memory allocation stays
> > +		 * around until its child MSTB releases it
> > +		 */
> > +		drm_dp_mst_get_port_malloc(port);
> > +		mutex_unlock(&mgr->lock);
> > +
> > +		/* And make sure we send a link address for this */
> > +		ret = 1;
> >  		break;
> >  	}
> > -	return send_link;
> > +
> > +out:
> > +	if (ret < 0)
> > +		port->pdt = DP_PEER_DEVICE_NONE;
> > +	return ret;
> >  }
> >  
> >  /**
> > @@ -1881,10 +1904,9 @@ static void drm_dp_add_port(struct
> > drm_dp_mst_branch *mstb,
> >  			    struct drm_device *dev,
> >  			    struct drm_dp_link_addr_reply_port *port_msg)
> >  {
> > +	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
> >  	struct drm_dp_mst_port *port;
> > -	bool ret;
> >  	bool created = false;
> > -	int old_pdt = 0;
> >  	int old_ddps = 0;
> >  
> >  	port = drm_dp_get_port(mstb, port_msg->port_number);
> > @@ -1896,7 +1918,7 @@ static void drm_dp_add_port(struct drm_dp_mst_branch
> > *mstb,
> >  		kref_init(&port->malloc_kref);
> >  		port->parent = mstb;
> >  		port->port_num = port_msg->port_number;
> > -		port->mgr = mstb->mgr;
> > +		port->mgr = mgr;
> >  		port->aux.name = "DPMST";
> >  		port->aux.dev = dev->dev;
> >  		port->aux.is_remote = true;
> > @@ -1909,11 +1931,9 @@ static void drm_dp_add_port(struct
> > drm_dp_mst_branch *mstb,
> >  
> >  		created = true;
> >  	} else {
> > -		old_pdt = port->pdt;
> >  		old_ddps = port->ddps;
> >  	}
> >  
> > -	port->pdt = port_msg->peer_device_type;
> >  	port->input = port_msg->input_port;
> >  	port->mcs = port_msg->mcs;
> >  	port->ddps = port_msg->ddps;
> > @@ -1925,29 +1945,33 @@ static void drm_dp_add_port(struct
> > drm_dp_mst_branch *mstb,
> >  	/* manage mstb port lists with mgr lock - take a reference
> >  	   for this list */
> >  	if (created) {
> > -		mutex_lock(&mstb->mgr->lock);
> > +		mutex_lock(&mgr->lock);
> >  		drm_dp_mst_topology_get_port(port);
> >  		list_add(&port->next, &mstb->ports);
> > -		mutex_unlock(&mstb->mgr->lock);
> > +		mutex_unlock(&mgr->lock);
> >  	}
> >  
> >  	if (old_ddps != port->ddps) {
> >  		if (port->ddps) {
> >  			if (!port->input) {
> > -				drm_dp_send_enum_path_resources(mstb->mgr,
> > -								mstb, port);
> > +				drm_dp_send_enum_path_resources(mgr, mstb,
> > +								port);
> >  			}
> >  		} else {
> >  			port->available_pbn = 0;
> >  		}
> >  	}
> >  
> > -	if (old_pdt != port->pdt && !port->input) {
> > -		drm_dp_port_teardown_pdt(port, old_pdt);
> > -
> > -		ret = drm_dp_port_setup_pdt(port);
> > -		if (ret == true)
> > -			drm_dp_send_link_address(mstb->mgr, port->mstb);
> > +	if (!port->input) {
> > +		int ret = drm_dp_port_set_pdt(port,
> > +					      port_msg->peer_device_type);
> > +		if (ret == 1) {
> > +			drm_dp_send_link_address(mgr, port->mstb);
> > +		} else if (ret < 0) {
> > +			DRM_ERROR("Failed to change PDT on port %p: %d\n",
> > +				  port, ret);
> > +			goto fail;
> > +		}
> >  	}
> >  
> >  	if (created && !port->input) {
> > @@ -1955,18 +1979,11 @@ static void drm_dp_add_port(struct
> > drm_dp_mst_branch *mstb,
> >  
> >  		build_mst_prop_path(mstb, port->port_num, proppath,
> >  				    sizeof(proppath));
> > -		port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr,
> > -								   port,
> > -								   proppath);
> > -		if (!port->connector) {
> > -			/* remove it from the port list */
> > -			mutex_lock(&mstb->mgr->lock);
> > -			list_del(&port->next);
> > -			mutex_unlock(&mstb->mgr->lock);
> > -			/* drop port list reference */
> > -			drm_dp_mst_topology_put_port(port);
> > -			goto out;
> > -		}
> > +		port->connector = (*mgr->cbs->add_connector)(mgr, port,
> > +							     proppath);
> > +		if (!port->connector)
> > +			goto fail;
> > +
> >  		if ((port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
> >  		     port->pdt == DP_PEER_DEVICE_SST_SINK) &&
> >  		    port->port_num >= DP_MST_LOGICAL_PORT_0) {
> > @@ -1974,28 +1991,38 @@ static void drm_dp_add_port(struct
> > drm_dp_mst_branch *mstb,
> >  							 &port->aux.ddc);
> >  			drm_connector_set_tile_property(port->connector);
> >  		}
> > -		(*mstb->mgr->cbs->register_connector)(port->connector);
> > +
> > +		(*mgr->cbs->register_connector)(port->connector);
> >  	}
> >  
> > -out:
> >  	/* put reference to this port */
> >  	drm_dp_mst_topology_put_port(port);
> > +	return;
> > +
> > +fail:
> > +	/* Remove it from the port list */
> > +	mutex_lock(&mgr->lock);
> > +	list_del(&port->next);
> > +	mutex_unlock(&mgr->lock);
> > +
> > +	/* Drop the port list reference */
> > +	drm_dp_mst_topology_put_port(port);
> > +	/* And now drop our reference */
> > +	drm_dp_mst_topology_put_port(port);
> >  }
> >  
> >  static void drm_dp_update_port(struct drm_dp_mst_branch *mstb,
> >  			       struct drm_dp_connection_status_notify
> > *conn_stat)
> >  {
> >  	struct drm_dp_mst_port *port;
> > -	int old_pdt;
> >  	int old_ddps;
> >  	bool dowork = false;
> > +
> >  	port = drm_dp_get_port(mstb, conn_stat->port_number);
> >  	if (!port)
> >  		return;
> >  
> >  	old_ddps = port->ddps;
> > -	old_pdt = port->pdt;
> > -	port->pdt = conn_stat->peer_device_type;
> >  	port->mcs = conn_stat->message_capability_status;
> >  	port->ldps = conn_stat->legacy_device_plug_status;
> >  	port->ddps = conn_stat->displayport_device_plug_status;
> > @@ -2007,11 +2034,17 @@ static void drm_dp_update_port(struct
> > drm_dp_mst_branch *mstb,
> >  			port->available_pbn = 0;
> >  		}
> >  	}
> > -	if (old_pdt != port->pdt && !port->input) {
> > -		drm_dp_port_teardown_pdt(port, old_pdt);
> >  
> > -		if (drm_dp_port_setup_pdt(port))
> > +	if (!port->input) {
> > +		int ret = drm_dp_port_set_pdt(port,
> > +					      conn_stat->peer_device_type);
> > +		if (ret == 1) {
> >  			dowork = true;
> > +		} else if (ret < 0) {
> > +			DRM_ERROR("Failed to change PDT for port %p: %d\n",
> > +				  port, ret);
> > +			dowork = false;
> > +		}
> >  	}
> >  
> >  	drm_dp_mst_topology_put_port(port);
> > @@ -4003,9 +4036,7 @@ drm_dp_delayed_destroy_port(struct drm_dp_mst_port
> > *port)
> >  	if (port->connector)
> >  		port->mgr->cbs->destroy_connector(port->mgr, port->connector);
> >  
> > -	drm_dp_port_teardown_pdt(port, port->pdt);
> > -	port->pdt = DP_PEER_DEVICE_NONE;
> > -
> > +	drm_dp_port_set_pdt(port, DP_PEER_DEVICE_NONE);
> >  	drm_dp_mst_put_port_malloc(port);
> >  }
> >  
> > diff --git a/include/drm/drm_dp_mst_helper.h
> > b/include/drm/drm_dp_mst_helper.h
> > index 5423a8adda78..f253ee43e9d9 100644
> > --- a/include/drm/drm_dp_mst_helper.h
> > +++ b/include/drm/drm_dp_mst_helper.h
> > @@ -55,8 +55,10 @@ struct drm_dp_vcpi {
> >   * @num_sdp_stream_sinks: Number of stream sinks
> >   * @available_pbn: Available bandwidth for this port.
> >   * @next: link to next port on this branch device
> > - * @mstb: branch device attach below this port
> > - * @aux: i2c aux transport to talk to device connected to this port.
> > + * @mstb: branch device on this port, protected by
> > + * &drm_dp_mst_topology_mgr.lock
> > + * @aux: i2c aux transport to talk to device connected to this port,
> > protected
> > + * by &drm_dp_mst_topology_mgr.lock
> >   * @parent: branch device parent of this port
> >   * @vcpi: Virtual Channel Payload info for this port.
> >   * @connector: DRM connector this port is connected to.
> > -- 
> > 2.21.0
> > 
-- 
Cheers,
	Lyude Paul


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 20/27] drm/dp_mst: Protect drm_dp_mst_port members with connection_mutex
  2019-09-25 20:00   ` Sean Paul
@ 2019-09-25 21:01     ` Lyude Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Lyude Paul @ 2019-09-25 21:01 UTC (permalink / raw)
  To: Sean Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	David Airlie, Daniel Vetter, linux-kernel

On Wed, 2019-09-25 at 16:00 -0400, Sean Paul wrote:
> On Tue, Sep 03, 2019 at 04:45:58PM -0400, Lyude Paul wrote:
> > Yes-you read that right. Currently there is literally no locking in
> > place for any of the drm_dp_mst_port struct members that can be modified
> > in response to a link address response, or a connection status response.
> > Which literally means if we're unlucky enough to have any sort of
> > hotplugging event happen before we're finished with reprobing link
> > addresses, we'll race and the contents of said struct members becomes
> > undefined. Fun!
> > 
> > So, finally add some simple locking protections to our MST helpers by
> > protecting any drm_dp_mst_port members which can be changed by link
> > address responses or connection status notifications under
> > drm_device->mode_config.connection_mutex.
> > 
> > Cc: Juston Li <juston.li@intel.com>
> > Cc: Imre Deak <imre.deak@intel.com>
> > Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > Cc: Harry Wentland <hwentlan@amd.com>
> > Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> > Signed-off-by: Lyude Paul <lyude@redhat.com>
> > ---
> >  drivers/gpu/drm/drm_dp_mst_topology.c | 144 +++++++++++++++++++-------
> >  include/drm/drm_dp_mst_helper.h       |  39 +++++--
> >  2 files changed, 133 insertions(+), 50 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c
> > b/drivers/gpu/drm/drm_dp_mst_topology.c
> > index 5101eeab4485..259634c5d6dc 100644
> > --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> > +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> > @@ -1354,6 +1354,7 @@ static void drm_dp_free_mst_port(struct kref *kref)
> >  		container_of(kref, struct drm_dp_mst_port, malloc_kref);
> >  
> >  	drm_dp_mst_put_mstb_malloc(port->parent);
> > +	mutex_destroy(&port->lock);
> >  	kfree(port);
> >  }
> >  
> > @@ -1906,6 +1907,36 @@ void drm_dp_mst_connector_early_unregister(struct
> > drm_connector *connector,
> >  }
> >  EXPORT_SYMBOL(drm_dp_mst_connector_early_unregister);
> >  
> > +static void
> > +drm_dp_mst_port_add_connector(struct drm_dp_mst_branch *mstb,
> > +			      struct drm_dp_mst_port *port)
> > +{
> > +	struct drm_dp_mst_topology_mgr *mgr = port->mgr;
> > +	char proppath[255];
> > +	int ret;
> > +
> > +	build_mst_prop_path(mstb, port->port_num, proppath, sizeof(proppath));
> > +	port->connector = mgr->cbs->add_connector(mgr, port, proppath);
> > +	if (!port->connector) {
> > +		ret = -ENOMEM;
> > +		goto error;
> > +	}
> > +
> > +	if ((port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
> > +	     port->pdt == DP_PEER_DEVICE_SST_SINK) &&
> > +	    port->port_num >= DP_MST_LOGICAL_PORT_0) {
> > +		port->cached_edid = drm_get_edid(port->connector,
> > +						 &port->aux.ddc);
> > +		drm_connector_set_tile_property(port->connector);
> > +	}
> > +
> > +	mgr->cbs->register_connector(port->connector);
> > +	return;
> > +
> > +error:
> > +	DRM_ERROR("Failed to create connector for port %p: %d\n", port, ret);
> > +}
> > +
> >  static void
> >  drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
> >  				    struct drm_device *dev,
> > @@ -1913,8 +1944,12 @@ drm_dp_mst_handle_link_address_port(struct
> > drm_dp_mst_branch *mstb,
> >  {
> >  	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
> >  	struct drm_dp_mst_port *port;
> > -	bool created = false;
> > -	int old_ddps = 0;
> > +	struct drm_dp_mst_branch *child_mstb = NULL;
> > +	struct drm_connector *connector_to_destroy = NULL;
> > +	int old_ddps = 0, ret;
> > +	u8 new_pdt = DP_PEER_DEVICE_NONE;
> > +	bool created = false, send_link_addr = false,
> > +	     create_connector = false;
> >  
> >  	port = drm_dp_get_port(mstb, port_msg->port_number);
> >  	if (!port) {
> > @@ -1923,6 +1958,7 @@ drm_dp_mst_handle_link_address_port(struct
> > drm_dp_mst_branch *mstb,
> >  			return;
> >  		kref_init(&port->topology_kref);
> >  		kref_init(&port->malloc_kref);
> > +		mutex_init(&port->lock);
> >  		port->parent = mstb;
> >  		port->port_num = port_msg->port_number;
> >  		port->mgr = mgr;
> > @@ -1937,11 +1973,17 @@ drm_dp_mst_handle_link_address_port(struct
> > drm_dp_mst_branch *mstb,
> >  		drm_dp_mst_get_mstb_malloc(mstb);
> >  
> >  		created = true;
> > -	} else {
> > -		old_ddps = port->ddps;
> >  	}
> >  
> > +	mutex_lock(&port->lock);
> > +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> > +
> > +	if (!created)
> > +		old_ddps = port->ddps;
> > +
> >  	port->input = port_msg->input_port;
> > +	if (!port->input)
> > +		new_pdt = port_msg->peer_device_type;
> >  	port->mcs = port_msg->mcs;
> >  	port->ddps = port_msg->ddps;
> >  	port->ldps = port_msg->legacy_device_plug_status;
> > @@ -1969,44 +2011,58 @@ drm_dp_mst_handle_link_address_port(struct
> > drm_dp_mst_branch *mstb,
> >  		}
> >  	}
> >  
> > -	if (!port->input) {
> > -		int ret = drm_dp_port_set_pdt(port,
> > -					      port_msg->peer_device_type);
> > -		if (ret == 1) {
> > -			drm_dp_send_link_address(mgr, port->mstb);
> > -		} else if (ret < 0) {
> > -			DRM_ERROR("Failed to change PDT on port %p: %d\n",
> > -				  port, ret);
> > -			goto fail;
> > +	ret = drm_dp_port_set_pdt(port, new_pdt);
> > +	if (ret == 1) {
> > +		send_link_addr = true;
> > +	} else if (ret < 0) {
> > +		DRM_ERROR("Failed to change PDT on port %p: %d\n",
> > +			  port, ret);
> > +		goto fail_unlock;
> > +	}
> > +
> > +	if (send_link_addr) {
> > +		mutex_lock(&mgr->lock);
> > +		if (port->mstb) {
> > +			child_mstb = port->mstb;
> > +			drm_dp_mst_get_mstb_malloc(child_mstb);
> >  		}
> > +		mutex_unlock(&mgr->lock);
> >  	}
> >  
> > -	if (created && !port->input) {
> > -		char proppath[255];
> > +	/*
> > +	 * We unset port->connector before dropping connection_mutex so that
> > +	 * there's no chance any of the atomic MST helpers can accidentally
> > +	 * associate a to-be-destroyed connector with a port.
> > +	 */
> > +	if (port->connector && port->input) {
> > +		connector_to_destroy = port->connector;
> > +		port->connector = NULL;
> > +	} else if (!port->connector && !port->input) {
> > +		create_connector = true;
> > +	}
> >  
> > -		build_mst_prop_path(mstb, port->port_num, proppath,
> > -				    sizeof(proppath));
> > -		port->connector = (*mgr->cbs->add_connector)(mgr, port,
> > -							     proppath);
> > -		if (!port->connector)
> > -			goto fail;
> > +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
> 
> Do you drop this early b/c it deadlocks with something upstack? If so, I
> wonder if you could plumb an acquire context through the appropriate
> functions to avoid needing the port->lock at all?

I'll give this a shot, as it would definitely be nicer then having port->lock
> 
> Sean
> 
> >  
> > -		if ((port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
> > -		     port->pdt == DP_PEER_DEVICE_SST_SINK) &&
> > -		    port->port_num >= DP_MST_LOGICAL_PORT_0) {
> > -			port->cached_edid = drm_get_edid(port->connector,
> > -							 &port->aux.ddc);
> > -			drm_connector_set_tile_property(port->connector);
> > -		}
> > +	if (connector_to_destroy)
> > +		mgr->cbs->destroy_connector(mgr, connector_to_destroy);
> > +	else if (create_connector)
> > +		drm_dp_mst_port_add_connector(mstb, port);
> > +
> > +	mutex_unlock(&port->lock);
> >  
> > -		(*mgr->cbs->register_connector)(port->connector);
> > +	if (send_link_addr && child_mstb) {
> > +		drm_dp_send_link_address(mgr, child_mstb);
> > +		drm_dp_mst_put_mstb_malloc(child_mstb);
> >  	}
> >  
> >  	/* put reference to this port */
> >  	drm_dp_mst_topology_put_port(port);
> >  	return;
> >  
> > -fail:
> > +fail_unlock:
> > +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
> > +	mutex_unlock(&port->lock);
> > +
> >  	/* Remove it from the port list */
> >  	mutex_lock(&mgr->lock);
> >  	list_del(&port->next);
> > @@ -2022,6 +2078,7 @@ static void
> >  drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
> >  			    struct drm_dp_connection_status_notify *conn_stat)
> >  {
> > +	struct drm_device *dev = mstb->mgr->dev;
> >  	struct drm_dp_mst_port *port;
> >  	int old_ddps;
> >  	bool dowork = false;
> > @@ -2030,6 +2087,8 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch
> > *mstb,
> >  	if (!port)
> >  		return;
> >  
> > +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> > +
> >  	old_ddps = port->ddps;
> >  	port->mcs = conn_stat->message_capability_status;
> >  	port->ldps = conn_stat->legacy_device_plug_status;
> > @@ -2055,6 +2114,7 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch
> > *mstb,
> >  		}
> >  	}
> >  
> > +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
> >  	drm_dp_mst_topology_put_port(port);
> >  	if (dowork)
> >  		queue_work(system_long_wq, &mstb->mgr->work);
> > @@ -2147,28 +2207,34 @@ drm_dp_get_mst_branch_device_by_guid(struct
> > drm_dp_mst_topology_mgr *mgr,
> >  static void drm_dp_check_and_send_link_address(struct
> > drm_dp_mst_topology_mgr *mgr,
> >  					       struct drm_dp_mst_branch *mstb)
> >  {
> > +	struct drm_device *dev = mgr->dev;
> >  	struct drm_dp_mst_port *port;
> > -	struct drm_dp_mst_branch *mstb_child;
> > +
> >  	if (!mstb->link_address_sent)
> >  		drm_dp_send_link_address(mgr, mstb);
> >  
> >  	list_for_each_entry(port, &mstb->ports, next) {
> > -		if (port->input)
> > -			continue;
> > +		struct drm_dp_mst_branch *mstb_child = NULL;
> > +
> > +		drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> >  
> > -		if (!port->ddps)
> > +		if (port->input || !port->ddps) {
> > +			drm_modeset_unlock(&dev-
> > >mode_config.connection_mutex);
> >  			continue;
> > +		}
> >  
> >  		if (!port->available_pbn)
> >  			drm_dp_send_enum_path_resources(mgr, mstb, port);
> >  
> > -		if (port->mstb) {
> > +		if (port->mstb)
> >  			mstb_child = drm_dp_mst_topology_get_mstb_validated(
> >  			    mgr, port->mstb);
> > -			if (mstb_child) {
> > -				drm_dp_check_and_send_link_address(mgr,
> > mstb_child);
> > -				drm_dp_mst_topology_put_mstb(mstb_child);
> > -			}
> > +
> > +		drm_modeset_unlock(&dev->mode_config.connection_mutex);
> > +
> > +		if (mstb_child) {
> > +			drm_dp_check_and_send_link_address(mgr, mstb_child);
> > +			drm_dp_mst_topology_put_mstb(mstb_child);
> >  		}
> >  	}
> >  }
> > diff --git a/include/drm/drm_dp_mst_helper.h
> > b/include/drm/drm_dp_mst_helper.h
> > index 7d80c38ee00e..1efbb086f7ac 100644
> > --- a/include/drm/drm_dp_mst_helper.h
> > +++ b/include/drm/drm_dp_mst_helper.h
> > @@ -45,23 +45,34 @@ struct drm_dp_vcpi {
> >  /**
> >   * struct drm_dp_mst_port - MST port
> >   * @port_num: port number
> > - * @input: if this port is an input port.
> > - * @mcs: message capability status - DP 1.2 spec.
> > - * @ddps: DisplayPort Device Plug Status - DP 1.2
> > - * @pdt: Peer Device Type
> > - * @ldps: Legacy Device Plug Status
> > - * @dpcd_rev: DPCD revision of device on this port
> > - * @num_sdp_streams: Number of simultaneous streams
> > - * @num_sdp_stream_sinks: Number of stream sinks
> > - * @available_pbn: Available bandwidth for this port.
> > + * @input: if this port is an input port. Protected by
> > + * &drm_device.mode_config.connection_mutex.
> > + * @mcs: message capability status - DP 1.2 spec. Protected by
> > + * &drm_device.mode_config.connection_mutex.
> > + * @ddps: DisplayPort Device Plug Status - DP 1.2. Protected by
> > + * &drm_device.mode_config.connection_mutex.
> > + * @pdt: Peer Device Type. Protected by
> > + * &drm_device.mode_config.connection_mutex.
> > + * @ldps: Legacy Device Plug Status. Protected by
> > + * &drm_device.mode_config.connection_mutex.
> > + * @dpcd_rev: DPCD revision of device on this port. Protected by
> > + * &drm_device.mode_config.connection_mutex.
> > + * @num_sdp_streams: Number of simultaneous streams. Protected by
> > + * &drm_device.mode_config.connection_mutex.
> > + * @num_sdp_stream_sinks: Number of stream sinks. Protected by
> > + * &drm_device.mode_config.connection_mutex.
> > + * @available_pbn: Available bandwidth for this port. Protected by
> > + * &drm_device.mode_config.connection_mutex.
> >   * @next: link to next port on this branch device
> >   * @mstb: branch device on this port, protected by
> >   * &drm_dp_mst_topology_mgr.lock
> >   * @aux: i2c aux transport to talk to device connected to this port,
> > protected
> > - * by &drm_dp_mst_topology_mgr.lock
> > + * by &drm_device.mode_config.connection_mutex.
> >   * @parent: branch device parent of this port
> >   * @vcpi: Virtual Channel Payload info for this port.
> > - * @connector: DRM connector this port is connected to.
> > + * @connector: DRM connector this port is connected to. Protected by
> > @lock.
> > + * When there is already a connector registered for this port, this is
> > also
> > + * protected by &drm_device.mode_config.connection_mutex.
> >   * @mgr: topology manager this port lives under.
> >   *
> >   * This structure represents an MST port endpoint on a device somewhere
> > @@ -100,6 +111,12 @@ struct drm_dp_mst_port {
> >  	struct drm_connector *connector;
> >  	struct drm_dp_mst_topology_mgr *mgr;
> >  
> > +	/**
> > +	 * @lock: Protects @connector. If needed, this lock should be grabbed
> > +	 * before &drm_device.mode_config.connection_mutex.
> > +	 */
> > +	struct mutex lock;
> > +
> >  	/**
> >  	 * @cached_edid: for DP logical ports - make tiling work by ensuring
> >  	 * that the EDID for all connectors is read immediately.
> > -- 
> > 2.21.0
> > 
-- 
Cheers,
	Lyude Paul


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v4] drm/amdgpu/dm: Resume short HPD IRQs before resuming MST topology
  2019-09-03 20:46 ` [PATCH v2 24/27] drm/amdgpu/dm: Resume short HPD IRQs before resuming MST topology Lyude Paul
  2019-09-13 20:46   ` Alex Deucher
  2019-09-25 20:08   ` Sean Paul
@ 2019-09-25 21:52   ` Lyude Paul
  2019-09-27 13:29     ` Alex Deucher
  2 siblings, 1 reply; 62+ messages in thread
From: Lyude Paul @ 2019-09-25 21:52 UTC (permalink / raw)
  To: dri-devel
  Cc: Juston Li, Imre Deak, Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Alex Deucher, Harry Wentland,
	Leo Li, Christian König, David (ChunMing) Zhou,
	David Airlie, Daniel Vetter, Nicholas Kazlauskas, David Francis,
	Mario Kleiner, amd-gfx, linux-kernel

Since we're going to be reprobing the entire topology state on resume
now using sideband transactions, we need to ensure that we actually have
short HPD irqs enabled before calling drm_dp_mst_topology_mgr_resume().
So, do that.

Changes since v4:
* Fix typo in comments

Cc: Juston Li <juston.li@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Harry Wentland <hwentlan@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Lyude Paul <lyude@redhat.com>
Acked-by: Alex Deucher <alexander.deucher@amd.com>
---
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 18927758a010..bce9a298bc45 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -1186,15 +1186,15 @@ static int dm_resume(void *handle)
 	/* program HPD filter */
 	dc_resume(dm->dc);
 
-	/* On resume we need to  rewrite the MSTM control bits to enamble MST*/
-	s3_handle_mst(ddev, false);
-
 	/*
 	 * early enable HPD Rx IRQ, should be done before set mode as short
 	 * pulse interrupts are used for MST
 	 */
 	amdgpu_dm_irq_resume_early(adev);
 
+	/* On resume we need to rewrite the MSTM control bits to enable MST*/
+	s3_handle_mst(ddev, false);
+
 	/* Do detection*/
 	drm_connector_list_iter_begin(ddev, &iter);
 	drm_for_each_connector_iter(connector, &iter) {
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* Re: [PATCH v4] drm/amdgpu/dm: Resume short HPD IRQs before resuming MST topology
  2019-09-25 21:52   ` [PATCH v4] " Lyude Paul
@ 2019-09-27 13:29     ` Alex Deucher
  0 siblings, 0 replies; 62+ messages in thread
From: Alex Deucher @ 2019-09-27 13:29 UTC (permalink / raw)
  To: Lyude Paul
  Cc: Maling list - DRI developers, David (ChunMing) Zhou,
	Mario Kleiner, Leo Li, Daniel Vetter, Imre Deak, LKML,
	amd-gfx list, Nicholas Kazlauskas, David Airlie, Juston Li,
	Daniel Vetter, Harry Wentland, Alex Deucher, David Francis,
	Harry Wentland, Christian König, Ville Syrjälä

On Wed, Sep 25, 2019 at 5:53 PM Lyude Paul <lyude@redhat.com> wrote:
>
> Since we're going to be reprobing the entire topology state on resume
> now using sideband transactions, we need to ensure that we actually have
> short HPD irqs enabled before calling drm_dp_mst_topology_mgr_resume().
> So, do that.
>
> Changes since v4:
> * Fix typo in comments
>
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>
> Acked-by: Alex Deucher <alexander.deucher@amd.com>

Applied.  Thanks!

Alex

> ---
>  drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> index 18927758a010..bce9a298bc45 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> @@ -1186,15 +1186,15 @@ static int dm_resume(void *handle)
>         /* program HPD filter */
>         dc_resume(dm->dc);
>
> -       /* On resume we need to  rewrite the MSTM control bits to enamble MST*/
> -       s3_handle_mst(ddev, false);
> -
>         /*
>          * early enable HPD Rx IRQ, should be done before set mode as short
>          * pulse interrupts are used for MST
>          */
>         amdgpu_dm_irq_resume_early(adev);
>
> +       /* On resume we need to rewrite the MSTM control bits to enable MST*/
> +       s3_handle_mst(ddev, false);
> +
>         /* Do detection*/
>         drm_connector_list_iter_begin(ddev, &iter);
>         drm_for_each_connector_iter(connector, &iter) {
> --
> 2.21.0
>
> _______________________________________________
> amd-gfx mailing list
> amd-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 16/27] drm/dp_mst: Refactor pdt setup/teardown, add more locking
  2019-09-25 21:00     ` Lyude Paul
@ 2019-09-27 13:30       ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2019-09-27 13:30 UTC (permalink / raw)
  To: Lyude Paul
  Cc: Sean Paul, dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	David Airlie, Daniel Vetter, linux-kernel

On Wed, Sep 25, 2019 at 05:00:00PM -0400, Lyude Paul wrote:
> On Wed, 2019-09-25 at 15:27 -0400, Sean Paul wrote:
> > On Tue, Sep 03, 2019 at 04:45:54PM -0400, Lyude Paul wrote:
> > > Since we're going to be implementing suspend/resume reprobing very soon,
> > > we need to make sure we are extra careful to ensure that our locking
> > > actually protects the topology state where we expect it to. Turns out
> > > this isn't the case with drm_dp_port_setup_pdt() and
> > > drm_dp_port_teardown_pdt(), both of which change port->mstb without
> > > grabbing &mgr->lock.
> > > 
> > > Additionally, since most callers of these functions are just using it to
> > > teardown the port's previous PDT and setup a new one we can simplify
> > > things a bit and combine drm_dp_port_setup_pdt() and
> > > drm_dp_port_teardown_pdt() into a single function:
> > > drm_dp_port_set_pdt(). This function also handles actually ensuring that
> > > we grab the correct locks when we need to modify port->mstb.
> > > 
> > > Cc: Juston Li <juston.li@intel.com>
> > > Cc: Imre Deak <imre.deak@intel.com>
> > > Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > Cc: Harry Wentland <hwentlan@amd.com>
> > > Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> > > Signed-off-by: Lyude Paul <lyude@redhat.com>
> > > ---
> > >  drivers/gpu/drm/drm_dp_mst_topology.c | 181 +++++++++++++++-----------
> > >  include/drm/drm_dp_mst_helper.h       |   6 +-
> > >  2 files changed, 110 insertions(+), 77 deletions(-)
> > > 
> > > diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c
> > > b/drivers/gpu/drm/drm_dp_mst_topology.c
> > > index d1610434a0cb..9944ef2ce885 100644
> > > --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> > > +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> > > @@ -1487,24 +1487,6 @@ drm_dp_mst_topology_put_mstb(struct
> > > drm_dp_mst_branch *mstb)
> > >  	kref_put(&mstb->topology_kref, drm_dp_destroy_mst_branch_device);
> > >  }
> > >  
> > > -static void drm_dp_port_teardown_pdt(struct drm_dp_mst_port *port, int
> > > old_pdt)
> > > -{
> > > -	struct drm_dp_mst_branch *mstb;
> > > -
> > > -	switch (old_pdt) {
> > > -	case DP_PEER_DEVICE_DP_LEGACY_CONV:
> > > -	case DP_PEER_DEVICE_SST_SINK:
> > > -		/* remove i2c over sideband */
> > > -		drm_dp_mst_unregister_i2c_bus(&port->aux);
> > > -		break;
> > > -	case DP_PEER_DEVICE_MST_BRANCHING:
> > > -		mstb = port->mstb;
> > > -		port->mstb = NULL;
> > > -		drm_dp_mst_topology_put_mstb(mstb);
> > > -		break;
> > > -	}
> > > -}
> > > -
> > >  static void drm_dp_destroy_port(struct kref *kref)
> > >  {
> > >  	struct drm_dp_mst_port *port =
> > > @@ -1714,38 +1696,79 @@ static u8 drm_dp_calculate_rad(struct
> > > drm_dp_mst_port *port,
> > >  	return parent_lct + 1;
> > >  }
> > >  
> > > -/*
> > > - * return sends link address for new mstb
> > > - */
> > > -static bool drm_dp_port_setup_pdt(struct drm_dp_mst_port *port)
> > > +static int drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt)
> > >  {
> > > -	int ret;
> > > -	u8 rad[6], lct;
> > > -	bool send_link = false;
> > > +	struct drm_dp_mst_topology_mgr *mgr = port->mgr;
> > > +	struct drm_dp_mst_branch *mstb;
> > > +	u8 rad[8], lct;
> > > +	int ret = 0;
> > > +
> > > +	if (port->pdt == new_pdt)
> > 
> > Shouldn't we also ensure that access to port->pdt is also locked?
> > 
> 
> It's specifically port->mstb that needs to be protected under lock. We don't
> use port->pdt for traversing the topology at all, so keeping it under
> connection_mutex is sufficient.
> 

I hadn't gotten to the connection_mutex patch yet when I made that comment :)

Reviewed-by: Sean Paul <sean@poorly.run>


> > Sean
> > 
> > > +		return 0;
> > > +
> > > +	/* Teardown the old pdt, if there is one */
> > > +	switch (port->pdt) {
> > > +	case DP_PEER_DEVICE_DP_LEGACY_CONV:
> > > +	case DP_PEER_DEVICE_SST_SINK:
> > > +		/*
> > > +		 * If the new PDT would also have an i2c bus, don't bother
> > > +		 * with reregistering it
> > > +		 */
> > > +		if (new_pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
> > > +		    new_pdt == DP_PEER_DEVICE_SST_SINK) {
> > > +			port->pdt = new_pdt;
> > > +			return 0;
> > > +		}
> > > +
> > > +		/* remove i2c over sideband */
> > > +		drm_dp_mst_unregister_i2c_bus(&port->aux);
> > > +		break;
> > > +	case DP_PEER_DEVICE_MST_BRANCHING:
> > > +		mutex_lock(&mgr->lock);
> > > +		drm_dp_mst_topology_put_mstb(port->mstb);
> > > +		port->mstb = NULL;
> > > +		mutex_unlock(&mgr->lock);
> > > +		break;
> > > +	}
> > > +
> > > +	port->pdt = new_pdt;
> > >  	switch (port->pdt) {
> > >  	case DP_PEER_DEVICE_DP_LEGACY_CONV:
> > >  	case DP_PEER_DEVICE_SST_SINK:
> > >  		/* add i2c over sideband */
> > >  		ret = drm_dp_mst_register_i2c_bus(&port->aux);
> > >  		break;
> > > +
> > >  	case DP_PEER_DEVICE_MST_BRANCHING:
> > >  		lct = drm_dp_calculate_rad(port, rad);
> > > +		mstb = drm_dp_add_mst_branch_device(lct, rad);
> > > +		if (!mstb) {
> > > +			ret = -ENOMEM;
> > > +			DRM_ERROR("Failed to create MSTB for port %p", port);
> > > +			goto out;
> > > +		}
> > >  
> > > -		port->mstb = drm_dp_add_mst_branch_device(lct, rad);
> > > -		if (port->mstb) {
> > > -			port->mstb->mgr = port->mgr;
> > > -			port->mstb->port_parent = port;
> > > -			/*
> > > -			 * Make sure this port's memory allocation stays
> > > -			 * around until its child MSTB releases it
> > > -			 */
> > > -			drm_dp_mst_get_port_malloc(port);
> > > +		mutex_lock(&mgr->lock);
> > > +		port->mstb = mstb;
> > > +		mstb->mgr = port->mgr;
> > > +		mstb->port_parent = port;
> > >  
> > > -			send_link = true;
> > > -		}
> > > +		/*
> > > +		 * Make sure this port's memory allocation stays
> > > +		 * around until its child MSTB releases it
> > > +		 */
> > > +		drm_dp_mst_get_port_malloc(port);
> > > +		mutex_unlock(&mgr->lock);
> > > +
> > > +		/* And make sure we send a link address for this */
> > > +		ret = 1;
> > >  		break;
> > >  	}
> > > -	return send_link;
> > > +
> > > +out:
> > > +	if (ret < 0)
> > > +		port->pdt = DP_PEER_DEVICE_NONE;
> > > +	return ret;
> > >  }
> > >  
> > >  /**
> > > @@ -1881,10 +1904,9 @@ static void drm_dp_add_port(struct
> > > drm_dp_mst_branch *mstb,
> > >  			    struct drm_device *dev,
> > >  			    struct drm_dp_link_addr_reply_port *port_msg)
> > >  {
> > > +	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
> > >  	struct drm_dp_mst_port *port;
> > > -	bool ret;
> > >  	bool created = false;
> > > -	int old_pdt = 0;
> > >  	int old_ddps = 0;
> > >  
> > >  	port = drm_dp_get_port(mstb, port_msg->port_number);
> > > @@ -1896,7 +1918,7 @@ static void drm_dp_add_port(struct drm_dp_mst_branch
> > > *mstb,
> > >  		kref_init(&port->malloc_kref);
> > >  		port->parent = mstb;
> > >  		port->port_num = port_msg->port_number;
> > > -		port->mgr = mstb->mgr;
> > > +		port->mgr = mgr;
> > >  		port->aux.name = "DPMST";
> > >  		port->aux.dev = dev->dev;
> > >  		port->aux.is_remote = true;
> > > @@ -1909,11 +1931,9 @@ static void drm_dp_add_port(struct
> > > drm_dp_mst_branch *mstb,
> > >  
> > >  		created = true;
> > >  	} else {
> > > -		old_pdt = port->pdt;
> > >  		old_ddps = port->ddps;
> > >  	}
> > >  
> > > -	port->pdt = port_msg->peer_device_type;
> > >  	port->input = port_msg->input_port;
> > >  	port->mcs = port_msg->mcs;
> > >  	port->ddps = port_msg->ddps;
> > > @@ -1925,29 +1945,33 @@ static void drm_dp_add_port(struct
> > > drm_dp_mst_branch *mstb,
> > >  	/* manage mstb port lists with mgr lock - take a reference
> > >  	   for this list */
> > >  	if (created) {
> > > -		mutex_lock(&mstb->mgr->lock);
> > > +		mutex_lock(&mgr->lock);
> > >  		drm_dp_mst_topology_get_port(port);
> > >  		list_add(&port->next, &mstb->ports);
> > > -		mutex_unlock(&mstb->mgr->lock);
> > > +		mutex_unlock(&mgr->lock);
> > >  	}
> > >  
> > >  	if (old_ddps != port->ddps) {
> > >  		if (port->ddps) {
> > >  			if (!port->input) {
> > > -				drm_dp_send_enum_path_resources(mstb->mgr,
> > > -								mstb, port);
> > > +				drm_dp_send_enum_path_resources(mgr, mstb,
> > > +								port);
> > >  			}
> > >  		} else {
> > >  			port->available_pbn = 0;
> > >  		}
> > >  	}
> > >  
> > > -	if (old_pdt != port->pdt && !port->input) {
> > > -		drm_dp_port_teardown_pdt(port, old_pdt);
> > > -
> > > -		ret = drm_dp_port_setup_pdt(port);
> > > -		if (ret == true)
> > > -			drm_dp_send_link_address(mstb->mgr, port->mstb);
> > > +	if (!port->input) {
> > > +		int ret = drm_dp_port_set_pdt(port,
> > > +					      port_msg->peer_device_type);
> > > +		if (ret == 1) {
> > > +			drm_dp_send_link_address(mgr, port->mstb);
> > > +		} else if (ret < 0) {
> > > +			DRM_ERROR("Failed to change PDT on port %p: %d\n",
> > > +				  port, ret);
> > > +			goto fail;
> > > +		}
> > >  	}
> > >  
> > >  	if (created && !port->input) {
> > > @@ -1955,18 +1979,11 @@ static void drm_dp_add_port(struct
> > > drm_dp_mst_branch *mstb,
> > >  
> > >  		build_mst_prop_path(mstb, port->port_num, proppath,
> > >  				    sizeof(proppath));
> > > -		port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr,
> > > -								   port,
> > > -								   proppath);
> > > -		if (!port->connector) {
> > > -			/* remove it from the port list */
> > > -			mutex_lock(&mstb->mgr->lock);
> > > -			list_del(&port->next);
> > > -			mutex_unlock(&mstb->mgr->lock);
> > > -			/* drop port list reference */
> > > -			drm_dp_mst_topology_put_port(port);
> > > -			goto out;
> > > -		}
> > > +		port->connector = (*mgr->cbs->add_connector)(mgr, port,
> > > +							     proppath);
> > > +		if (!port->connector)
> > > +			goto fail;
> > > +
> > >  		if ((port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
> > >  		     port->pdt == DP_PEER_DEVICE_SST_SINK) &&
> > >  		    port->port_num >= DP_MST_LOGICAL_PORT_0) {
> > > @@ -1974,28 +1991,38 @@ static void drm_dp_add_port(struct
> > > drm_dp_mst_branch *mstb,
> > >  							 &port->aux.ddc);
> > >  			drm_connector_set_tile_property(port->connector);
> > >  		}
> > > -		(*mstb->mgr->cbs->register_connector)(port->connector);
> > > +
> > > +		(*mgr->cbs->register_connector)(port->connector);
> > >  	}
> > >  
> > > -out:
> > >  	/* put reference to this port */
> > >  	drm_dp_mst_topology_put_port(port);
> > > +	return;
> > > +
> > > +fail:
> > > +	/* Remove it from the port list */
> > > +	mutex_lock(&mgr->lock);
> > > +	list_del(&port->next);
> > > +	mutex_unlock(&mgr->lock);
> > > +
> > > +	/* Drop the port list reference */
> > > +	drm_dp_mst_topology_put_port(port);
> > > +	/* And now drop our reference */
> > > +	drm_dp_mst_topology_put_port(port);
> > >  }
> > >  
> > >  static void drm_dp_update_port(struct drm_dp_mst_branch *mstb,
> > >  			       struct drm_dp_connection_status_notify
> > > *conn_stat)
> > >  {
> > >  	struct drm_dp_mst_port *port;
> > > -	int old_pdt;
> > >  	int old_ddps;
> > >  	bool dowork = false;
> > > +
> > >  	port = drm_dp_get_port(mstb, conn_stat->port_number);
> > >  	if (!port)
> > >  		return;
> > >  
> > >  	old_ddps = port->ddps;
> > > -	old_pdt = port->pdt;
> > > -	port->pdt = conn_stat->peer_device_type;
> > >  	port->mcs = conn_stat->message_capability_status;
> > >  	port->ldps = conn_stat->legacy_device_plug_status;
> > >  	port->ddps = conn_stat->displayport_device_plug_status;
> > > @@ -2007,11 +2034,17 @@ static void drm_dp_update_port(struct
> > > drm_dp_mst_branch *mstb,
> > >  			port->available_pbn = 0;
> > >  		}
> > >  	}
> > > -	if (old_pdt != port->pdt && !port->input) {
> > > -		drm_dp_port_teardown_pdt(port, old_pdt);
> > >  
> > > -		if (drm_dp_port_setup_pdt(port))
> > > +	if (!port->input) {
> > > +		int ret = drm_dp_port_set_pdt(port,
> > > +					      conn_stat->peer_device_type);
> > > +		if (ret == 1) {
> > >  			dowork = true;
> > > +		} else if (ret < 0) {
> > > +			DRM_ERROR("Failed to change PDT for port %p: %d\n",
> > > +				  port, ret);
> > > +			dowork = false;
> > > +		}
> > >  	}
> > >  
> > >  	drm_dp_mst_topology_put_port(port);
> > > @@ -4003,9 +4036,7 @@ drm_dp_delayed_destroy_port(struct drm_dp_mst_port
> > > *port)
> > >  	if (port->connector)
> > >  		port->mgr->cbs->destroy_connector(port->mgr, port->connector);
> > >  
> > > -	drm_dp_port_teardown_pdt(port, port->pdt);
> > > -	port->pdt = DP_PEER_DEVICE_NONE;
> > > -
> > > +	drm_dp_port_set_pdt(port, DP_PEER_DEVICE_NONE);
> > >  	drm_dp_mst_put_port_malloc(port);
> > >  }
> > >  
> > > diff --git a/include/drm/drm_dp_mst_helper.h
> > > b/include/drm/drm_dp_mst_helper.h
> > > index 5423a8adda78..f253ee43e9d9 100644
> > > --- a/include/drm/drm_dp_mst_helper.h
> > > +++ b/include/drm/drm_dp_mst_helper.h
> > > @@ -55,8 +55,10 @@ struct drm_dp_vcpi {
> > >   * @num_sdp_stream_sinks: Number of stream sinks
> > >   * @available_pbn: Available bandwidth for this port.
> > >   * @next: link to next port on this branch device
> > > - * @mstb: branch device attach below this port
> > > - * @aux: i2c aux transport to talk to device connected to this port.
> > > + * @mstb: branch device on this port, protected by
> > > + * &drm_dp_mst_topology_mgr.lock
> > > + * @aux: i2c aux transport to talk to device connected to this port,
> > > protected
> > > + * by &drm_dp_mst_topology_mgr.lock
> > >   * @parent: branch device parent of this port
> > >   * @vcpi: Virtual Channel Payload info for this port.
> > >   * @connector: DRM connector this port is connected to.
> > > -- 
> > > 2.21.0
> > > 
> -- 
> Cheers,
> 	Lyude Paul
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 03/27] drm/dp_mst: Destroy MSTBs asynchronously
  2019-09-25 20:08     ` Lyude Paul
@ 2019-09-27 13:31       ` Sean Paul
  2019-10-08  9:45         ` Daniel Vetter
  0 siblings, 1 reply; 62+ messages in thread
From: Sean Paul @ 2019-09-27 13:31 UTC (permalink / raw)
  To: Lyude Paul
  Cc: Sean Paul, dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	David Airlie, linux-kernel

On Wed, Sep 25, 2019 at 04:08:22PM -0400, Lyude Paul wrote:
> On Wed, 2019-09-25 at 14:16 -0400, Sean Paul wrote:
> > On Tue, Sep 03, 2019 at 04:45:41PM -0400, Lyude Paul wrote:
> > > When reprobing an MST topology during resume, we have to account for the
> > > fact that while we were suspended it's possible that mstbs may have been
> > > removed from any ports in the topology. Since iterating downwards in the
> > > topology requires that we hold &mgr->lock, destroying MSTBs from this
> > > context would result in attempting to lock &mgr->lock a second time and
> > > deadlocking.
> > > 
> > > So, fix this by first moving destruction of MSTBs into
> > > destroy_connector_work, then rename destroy_connector_work and friends
> > > to reflect that they now destroy both ports and mstbs.
> > > 
> > > Changes since v1:
> > > * s/destroy_connector_list/destroy_port_list/
> > >   s/connector_destroy_lock/delayed_destroy_lock/
> > >   s/connector_destroy_work/delayed_destroy_work/
> > >   s/drm_dp_finish_destroy_branch_device/drm_dp_delayed_destroy_mstb/
> > >   s/drm_dp_finish_destroy_port/drm_dp_delayed_destroy_port/
> > >   - danvet
> > > * Use two loops in drm_dp_delayed_destroy_work() - danvet
> > > * Better explain why we need to do this - danvet
> > > * Use cancel_work_sync() instead of flush_work() - flush_work() doesn't
> > >   account for work requeing
> > > 
> > > Cc: Juston Li <juston.li@intel.com>
> > > Cc: Imre Deak <imre.deak@intel.com>
> > > Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > Cc: Harry Wentland <hwentlan@amd.com>
> > > Cc: Daniel Vetter <daniel@ffwll.ch>
> > > Signed-off-by: Lyude Paul <lyude@redhat.com>
> > 
> > Took me a while to grok this, and I'm still not 100% confident my mental
> > model
> > is correct, so please bear with me while I ask silly questions :)
> > 
> > Now that the destroy is delayed, and the port remains in the topology, is it
> > possible we will underflow the topology kref by calling put_mstb multiple
> > times?
> > It looks like that would result in a WARN from refcount.c, and wouldn't call
> > the
> > destroy function multiple times, so that's nice :)
> > 
> > Similarly, is there any defense against calling get_mstb() between destroy()
> > and
> > the delayed destroy worker running?
> > 
> Good question! There's only one instance where we unconditionally grab an MSTB
> ref, drm_dp_mst_topology_mgr_set_mst(), and in that location we're guaranteed
> to be the only one with access to that mstb until we drop &mgr->lock.
> Everywhere else we use drm_dp_mst_topology_try_get_mstb(), which uses
> kref_get_unless_zero() to protect against that kind of situation (and forces
> the caller to check with __must_check)

Awesome, thanks for the breakdown!


Reviewed-by: Sean Paul <sean@poorly.run>


> 
> > Sean
> > 
> > > ---
> > >  drivers/gpu/drm/drm_dp_mst_topology.c | 162 +++++++++++++++++---------
> > >  include/drm/drm_dp_mst_helper.h       |  26 +++--
> > >  2 files changed, 127 insertions(+), 61 deletions(-)
> > > 
> > > diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c
> > > b/drivers/gpu/drm/drm_dp_mst_topology.c
> > > index 3054ec622506..738f260d4b15 100644
> > > --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> > > +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> > > @@ -1113,34 +1113,17 @@ static void
> > > drm_dp_destroy_mst_branch_device(struct kref *kref)
> > >  	struct drm_dp_mst_branch *mstb =
> > >  		container_of(kref, struct drm_dp_mst_branch, topology_kref);
> > >  	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
> > > -	struct drm_dp_mst_port *port, *tmp;
> > > -	bool wake_tx = false;
> > >  
> > > -	mutex_lock(&mgr->lock);
> > > -	list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
> > > -		list_del(&port->next);
> > > -		drm_dp_mst_topology_put_port(port);
> > > -	}
> > > -	mutex_unlock(&mgr->lock);
> > > -
> > > -	/* drop any tx slots msg */
> > > -	mutex_lock(&mstb->mgr->qlock);
> > > -	if (mstb->tx_slots[0]) {
> > > -		mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
> > > -		mstb->tx_slots[0] = NULL;
> > > -		wake_tx = true;
> > > -	}
> > > -	if (mstb->tx_slots[1]) {
> > > -		mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
> > > -		mstb->tx_slots[1] = NULL;
> > > -		wake_tx = true;
> > > -	}
> > > -	mutex_unlock(&mstb->mgr->qlock);
> > > +	INIT_LIST_HEAD(&mstb->destroy_next);
> > >  
> > > -	if (wake_tx)
> > > -		wake_up_all(&mstb->mgr->tx_waitq);
> > > -
> > > -	drm_dp_mst_put_mstb_malloc(mstb);
> > > +	/*
> > > +	 * This can get called under mgr->mutex, so we need to perform the
> > > +	 * actual destruction of the mstb in another worker
> > > +	 */
> > > +	mutex_lock(&mgr->delayed_destroy_lock);
> > > +	list_add(&mstb->destroy_next, &mgr->destroy_branch_device_list);
> > > +	mutex_unlock(&mgr->delayed_destroy_lock);
> > > +	schedule_work(&mgr->delayed_destroy_work);
> > >  }
> > >  
> > >  /**
> > > @@ -1255,10 +1238,10 @@ static void drm_dp_destroy_port(struct kref *kref)
> > >  			 * we might be holding the mode_config.mutex
> > >  			 * from an EDID retrieval */
> > >  
> > > -			mutex_lock(&mgr->destroy_connector_lock);
> > > -			list_add(&port->next, &mgr->destroy_connector_list);
> > > -			mutex_unlock(&mgr->destroy_connector_lock);
> > > -			schedule_work(&mgr->destroy_connector_work);
> > > +			mutex_lock(&mgr->delayed_destroy_lock);
> > > +			list_add(&port->next, &mgr->destroy_port_list);
> > > +			mutex_unlock(&mgr->delayed_destroy_lock);
> > > +			schedule_work(&mgr->delayed_destroy_work);
> > >  			return;
> > >  		}
> > >  		/* no need to clean up vcpi
> > > @@ -2792,7 +2775,7 @@ void drm_dp_mst_topology_mgr_suspend(struct
> > > drm_dp_mst_topology_mgr *mgr)
> > >  			   DP_MST_EN | DP_UPSTREAM_IS_SRC);
> > >  	mutex_unlock(&mgr->lock);
> > >  	flush_work(&mgr->work);
> > > -	flush_work(&mgr->destroy_connector_work);
> > > +	flush_work(&mgr->delayed_destroy_work);
> > >  }
> > >  EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend);
> > >  
> > > @@ -3740,34 +3723,104 @@ static void drm_dp_tx_work(struct work_struct
> > > *work)
> > >  	mutex_unlock(&mgr->qlock);
> > >  }
> > >  
> > > -static void drm_dp_destroy_connector_work(struct work_struct *work)
> > > +static inline void
> > > +drm_dp_delayed_destroy_port(struct drm_dp_mst_port *port)
> > >  {
> > > -	struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct
> > > drm_dp_mst_topology_mgr, destroy_connector_work);
> > > -	struct drm_dp_mst_port *port;
> > > -	bool send_hotplug = false;
> > > +	port->mgr->cbs->destroy_connector(port->mgr, port->connector);
> > > +
> > > +	drm_dp_port_teardown_pdt(port, port->pdt);
> > > +	port->pdt = DP_PEER_DEVICE_NONE;
> > > +
> > > +	drm_dp_mst_put_port_malloc(port);
> > > +}
> > > +
> > > +static inline void
> > > +drm_dp_delayed_destroy_mstb(struct drm_dp_mst_branch *mstb)
> > > +{
> > > +	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
> > > +	struct drm_dp_mst_port *port, *tmp;
> > > +	bool wake_tx = false;
> > > +
> > > +	mutex_lock(&mgr->lock);
> > > +	list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
> > > +		list_del(&port->next);
> > > +		drm_dp_mst_topology_put_port(port);
> > > +	}
> > > +	mutex_unlock(&mgr->lock);
> > > +
> > > +	/* drop any tx slots msg */
> > > +	mutex_lock(&mstb->mgr->qlock);
> > > +	if (mstb->tx_slots[0]) {
> > > +		mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
> > > +		mstb->tx_slots[0] = NULL;
> > > +		wake_tx = true;
> > > +	}
> > > +	if (mstb->tx_slots[1]) {
> > > +		mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
> > > +		mstb->tx_slots[1] = NULL;
> > > +		wake_tx = true;
> > > +	}
> > > +	mutex_unlock(&mstb->mgr->qlock);
> > > +
> > > +	if (wake_tx)
> > > +		wake_up_all(&mstb->mgr->tx_waitq);
> > > +
> > > +	drm_dp_mst_put_mstb_malloc(mstb);
> > > +}
> > > +
> > > +static void drm_dp_delayed_destroy_work(struct work_struct *work)
> > > +{
> > > +	struct drm_dp_mst_topology_mgr *mgr =
> > > +		container_of(work, struct drm_dp_mst_topology_mgr,
> > > +			     delayed_destroy_work);
> > > +	bool send_hotplug = false, go_again;
> > > +
> > >  	/*
> > >  	 * Not a regular list traverse as we have to drop the destroy
> > > -	 * connector lock before destroying the connector, to avoid AB->BA
> > > +	 * connector lock before destroying the mstb/port, to avoid AB->BA
> > >  	 * ordering between this lock and the config mutex.
> > >  	 */
> > > -	for (;;) {
> > > -		mutex_lock(&mgr->destroy_connector_lock);
> > > -		port = list_first_entry_or_null(&mgr->destroy_connector_list,
> > > struct drm_dp_mst_port, next);
> > > -		if (!port) {
> > > -			mutex_unlock(&mgr->destroy_connector_lock);
> > > -			break;
> > > +	do {
> > > +		go_again = false;
> > > +
> > > +		for (;;) {
> > > +			struct drm_dp_mst_branch *mstb;
> > > +
> > > +			mutex_lock(&mgr->delayed_destroy_lock);
> > > +			mstb = list_first_entry_or_null(&mgr-
> > > >destroy_branch_device_list,
> > > +							struct
> > > drm_dp_mst_branch,
> > > +							destroy_next);
> > > +			if (mstb)
> > > +				list_del(&mstb->destroy_next);
> > > +			mutex_unlock(&mgr->delayed_destroy_lock);
> > > +
> > > +			if (!mstb)
> > > +				break;
> > > +
> > > +			drm_dp_delayed_destroy_mstb(mstb);
> > > +			go_again = true;
> > >  		}
> > > -		list_del(&port->next);
> > > -		mutex_unlock(&mgr->destroy_connector_lock);
> > >  
> > > -		mgr->cbs->destroy_connector(mgr, port->connector);
> > > +		for (;;) {
> > > +			struct drm_dp_mst_port *port;
> > >  
> > > -		drm_dp_port_teardown_pdt(port, port->pdt);
> > > -		port->pdt = DP_PEER_DEVICE_NONE;
> > > +			mutex_lock(&mgr->delayed_destroy_lock);
> > > +			port = list_first_entry_or_null(&mgr-
> > > >destroy_port_list,
> > > +							struct
> > > drm_dp_mst_port,
> > > +							next);
> > > +			if (port)
> > > +				list_del(&port->next);
> > > +			mutex_unlock(&mgr->delayed_destroy_lock);
> > > +
> > > +			if (!port)
> > > +				break;
> > > +
> > > +			drm_dp_delayed_destroy_port(port);
> > > +			send_hotplug = true;
> > > +			go_again = true;
> > > +		}
> > > +	} while (go_again);
> > >  
> > > -		drm_dp_mst_put_port_malloc(port);
> > > -		send_hotplug = true;
> > > -	}
> > >  	if (send_hotplug)
> > >  		drm_kms_helper_hotplug_event(mgr->dev);
> > >  }
> > > @@ -3957,12 +4010,13 @@ int drm_dp_mst_topology_mgr_init(struct
> > > drm_dp_mst_topology_mgr *mgr,
> > >  	mutex_init(&mgr->lock);
> > >  	mutex_init(&mgr->qlock);
> > >  	mutex_init(&mgr->payload_lock);
> > > -	mutex_init(&mgr->destroy_connector_lock);
> > > +	mutex_init(&mgr->delayed_destroy_lock);
> > >  	INIT_LIST_HEAD(&mgr->tx_msg_downq);
> > > -	INIT_LIST_HEAD(&mgr->destroy_connector_list);
> > > +	INIT_LIST_HEAD(&mgr->destroy_port_list);
> > > +	INIT_LIST_HEAD(&mgr->destroy_branch_device_list);
> > >  	INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work);
> > >  	INIT_WORK(&mgr->tx_work, drm_dp_tx_work);
> > > -	INIT_WORK(&mgr->destroy_connector_work,
> > > drm_dp_destroy_connector_work);
> > > +	INIT_WORK(&mgr->delayed_destroy_work, drm_dp_delayed_destroy_work);
> > >  	init_waitqueue_head(&mgr->tx_waitq);
> > >  	mgr->dev = dev;
> > >  	mgr->aux = aux;
> > > @@ -4005,7 +4059,7 @@ void drm_dp_mst_topology_mgr_destroy(struct
> > > drm_dp_mst_topology_mgr *mgr)
> > >  {
> > >  	drm_dp_mst_topology_mgr_set_mst(mgr, false);
> > >  	flush_work(&mgr->work);
> > > -	flush_work(&mgr->destroy_connector_work);
> > > +	cancel_work_sync(&mgr->delayed_destroy_work);
> > >  	mutex_lock(&mgr->payload_lock);
> > >  	kfree(mgr->payloads);
> > >  	mgr->payloads = NULL;
> > > diff --git a/include/drm/drm_dp_mst_helper.h
> > > b/include/drm/drm_dp_mst_helper.h
> > > index fc349204a71b..4a4507fe928d 100644
> > > --- a/include/drm/drm_dp_mst_helper.h
> > > +++ b/include/drm/drm_dp_mst_helper.h
> > > @@ -143,6 +143,12 @@ struct drm_dp_mst_branch {
> > >  	 */
> > >  	struct kref malloc_kref;
> > >  
> > > +	/**
> > > +	 * @destroy_next: linked-list entry used by
> > > +	 * drm_dp_delayed_destroy_work()
> > > +	 */
> > > +	struct list_head destroy_next;
> > > +
> > >  	u8 rad[8];
> > >  	u8 lct;
> > >  	int num_ports;
> > > @@ -575,18 +581,24 @@ struct drm_dp_mst_topology_mgr {
> > >  	struct work_struct tx_work;
> > >  
> > >  	/**
> > > -	 * @destroy_connector_list: List of to be destroyed connectors.
> > > +	 * @destroy_port_list: List of to be destroyed connectors.
> > > +	 */
> > > +	struct list_head destroy_port_list;
> > > +	/**
> > > +	 * @destroy_branch_device_list: List of to be destroyed branch
> > > +	 * devices.
> > >  	 */
> > > -	struct list_head destroy_connector_list;
> > > +	struct list_head destroy_branch_device_list;
> > >  	/**
> > > -	 * @destroy_connector_lock: Protects @connector_list.
> > > +	 * @delayed_destroy_lock: Protects @destroy_port_list and
> > > +	 * @destroy_branch_device_list.
> > >  	 */
> > > -	struct mutex destroy_connector_lock;
> > > +	struct mutex delayed_destroy_lock;
> > >  	/**
> > > -	 * @destroy_connector_work: Work item to destroy connectors. Needed to
> > > -	 * avoid locking inversion.
> > > +	 * @delayed_destroy_work: Work item to destroy MST port and branch
> > > +	 * devices, needed to avoid locking inversion.
> > >  	 */
> > > -	struct work_struct destroy_connector_work;
> > > +	struct work_struct delayed_destroy_work;
> > >  };
> > >  
> > >  int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
> > > -- 
> > > 2.21.0
> > > 
> -- 
> Cheers,
> 	Lyude Paul
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 23/27] drm/amdgpu: Iterate through DRM connectors correctly
  2019-09-13 20:45   ` Alex Deucher
@ 2019-09-27 13:48     ` Alex Deucher
  0 siblings, 0 replies; 62+ messages in thread
From: Alex Deucher @ 2019-09-27 13:48 UTC (permalink / raw)
  To: Lyude Paul
  Cc: Maling list - DRI developers, nouveau, amd-gfx list,
	Neil Armstrong, David Airlie, Daniel Vetter, Imre Deak, Tao Zhou,
	Huang Rui, Shirish S, Sam Ravnborg, Markus Elfring,
	Ville Syrjälä,
	David (ChunMing) Zhou, Mario Kleiner, Yu Zhao, Bhawanpreet Lakha,
	David Francis, Jani Nikula, Thierry Reding, Harry Wentland,
	Juston Li, Andrey Grodzovsky, Leo Li, Emily Deng, Russell King,
	Evan Quan, Harry Wentland, Felix Kuehling, xinhui pan,
	Michel Dänzer, LKML, Andrzej Pietrasiewicz, Daniel Vetter,
	Alex Deucher, Colin Ian King, Nicholas Kazlauskas, Rex Zhu,
	Christian König, Hawking Zhang

On Fri, Sep 13, 2019 at 4:45 PM Alex Deucher <alexdeucher@gmail.com> wrote:
>
> On Tue, Sep 3, 2019 at 4:49 PM Lyude Paul <lyude@redhat.com> wrote:
> >
> > Currently, every single piece of code in amdgpu that loops through
> > connectors does it incorrectly and doesn't use the proper list iteration
> > helpers, drm_connector_list_iter_begin() and
> > drm_connector_list_iter_end(). Yeesh.
> >
> > So, do that.
>
> In fairness, I think the origin of this code predated the iterators.
> Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
>

Applied.  Thanks!

Alex

> >
> > Cc: Juston Li <juston.li@intel.com>
> > Cc: Imre Deak <imre.deak@intel.com>
> > Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > Cc: Harry Wentland <hwentlan@amd.com>
> > Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> > Signed-off-by: Lyude Paul <lyude@redhat.com>
> > ---
> >  .../gpu/drm/amd/amdgpu/amdgpu_connectors.c    | 13 +++++-
> >  drivers/gpu/drm/amd/amdgpu/amdgpu_device.c    | 20 +++++++---
> >  drivers/gpu/drm/amd/amdgpu/amdgpu_display.c   |  5 ++-
> >  drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c  | 40 +++++++++++++------
> >  drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c       |  5 ++-
> >  drivers/gpu/drm/amd/amdgpu/dce_v10_0.c        | 34 ++++++++++++----
> >  drivers/gpu/drm/amd/amdgpu/dce_v11_0.c        | 34 ++++++++++++----
> >  drivers/gpu/drm/amd/amdgpu/dce_v6_0.c         | 40 ++++++++++++++-----
> >  drivers/gpu/drm/amd/amdgpu/dce_v8_0.c         | 34 ++++++++++++----
> >  .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 33 ++++++++-------
> >  .../drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c | 10 ++++-
> >  11 files changed, 195 insertions(+), 73 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
> > index ece55c8fa673..bd31bb595c04 100644
> > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
> > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
> > @@ -1022,8 +1022,12 @@ amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force)
> >                          */
> >                         if (amdgpu_connector->shared_ddc && (ret == connector_status_connected)) {
> >                                 struct drm_connector *list_connector;
> > +                               struct drm_connector_list_iter iter;
> >                                 struct amdgpu_connector *list_amdgpu_connector;
> > -                               list_for_each_entry(list_connector, &dev->mode_config.connector_list, head) {
> > +
> > +                               drm_connector_list_iter_begin(dev, &iter);
> > +                               drm_for_each_connector_iter(list_connector,
> > +                                                           &iter) {
> >                                         if (connector == list_connector)
> >                                                 continue;
> >                                         list_amdgpu_connector = to_amdgpu_connector(list_connector);
> > @@ -1040,6 +1044,7 @@ amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force)
> >                                                 }
> >                                         }
> >                                 }
> > +                               drm_connector_list_iter_end(&iter);
> >                         }
> >                 }
> >         }
> > @@ -1501,6 +1506,7 @@ amdgpu_connector_add(struct amdgpu_device *adev,
> >  {
> >         struct drm_device *dev = adev->ddev;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         struct amdgpu_connector *amdgpu_connector;
> >         struct amdgpu_connector_atom_dig *amdgpu_dig_connector;
> >         struct drm_encoder *encoder;
> > @@ -1515,10 +1521,12 @@ amdgpu_connector_add(struct amdgpu_device *adev,
> >                 return;
> >
> >         /* see if we already added it */
> > -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 amdgpu_connector = to_amdgpu_connector(connector);
> >                 if (amdgpu_connector->connector_id == connector_id) {
> >                         amdgpu_connector->devices |= supported_device;
> > +                       drm_connector_list_iter_end(&iter);
> >                         return;
> >                 }
> >                 if (amdgpu_connector->ddc_bus && i2c_bus->valid) {
> > @@ -1533,6 +1541,7 @@ amdgpu_connector_add(struct amdgpu_device *adev,
> >                         }
> >                 }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >
> >         /* check if it's a dp bridge */
> >         list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
> > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> > index 2f884699eaef..acd39ce9b08e 100644
> > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
> > @@ -3004,6 +3004,7 @@ int amdgpu_device_suspend(struct drm_device *dev, bool suspend, bool fbcon)
> >         struct amdgpu_device *adev;
> >         struct drm_crtc *crtc;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         int r;
> >
> >         if (dev == NULL || dev->dev_private == NULL) {
> > @@ -3026,9 +3027,11 @@ int amdgpu_device_suspend(struct drm_device *dev, bool suspend, bool fbcon)
> >         if (!amdgpu_device_has_dc_support(adev)) {
> >                 /* turn off display hw */
> >                 drm_modeset_lock_all(dev);
> > -               list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > -                       drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
> > -               }
> > +               drm_connector_list_iter_begin(dev, &iter);
> > +               drm_for_each_connector_iter(connector, &iter)
> > +                       drm_helper_connector_dpms(connector,
> > +                                                 DRM_MODE_DPMS_OFF);
> > +               drm_connector_list_iter_end(&iter);
> >                 drm_modeset_unlock_all(dev);
> >                         /* unpin the front buffers and cursors */
> >                 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
> > @@ -3107,6 +3110,7 @@ int amdgpu_device_suspend(struct drm_device *dev, bool suspend, bool fbcon)
> >  int amdgpu_device_resume(struct drm_device *dev, bool resume, bool fbcon)
> >  {
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         struct amdgpu_device *adev = dev->dev_private;
> >         struct drm_crtc *crtc;
> >         int r = 0;
> > @@ -3177,9 +3181,13 @@ int amdgpu_device_resume(struct drm_device *dev, bool resume, bool fbcon)
> >
> >                         /* turn on display hw */
> >                         drm_modeset_lock_all(dev);
> > -                       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > -                               drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
> > -                       }
> > +
> > +                       drm_connector_list_iter_begin(dev, &iter);
> > +                       drm_for_each_connector_iter(connector, &iter)
> > +                               drm_helper_connector_dpms(connector,
> > +                                                         DRM_MODE_DPMS_ON);
> > +                       drm_connector_list_iter_end(&iter);
> > +
> >                         drm_modeset_unlock_all(dev);
> >                 }
> >                 amdgpu_fbdev_set_suspend(adev, 0);
> > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
> > index 1d4aaa9580f4..d2dd59a95e8a 100644
> > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
> > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
> > @@ -370,11 +370,13 @@ void amdgpu_display_print_display_setup(struct drm_device *dev)
> >         struct amdgpu_connector *amdgpu_connector;
> >         struct drm_encoder *encoder;
> >         struct amdgpu_encoder *amdgpu_encoder;
> > +       struct drm_connector_list_iter iter;
> >         uint32_t devices;
> >         int i = 0;
> >
> > +       drm_connector_list_iter_begin(dev, &iter);
> >         DRM_INFO("AMDGPU Display Connectors\n");
> > -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 amdgpu_connector = to_amdgpu_connector(connector);
> >                 DRM_INFO("Connector %d:\n", i);
> >                 DRM_INFO("  %s\n", connector->name);
> > @@ -438,6 +440,7 @@ void amdgpu_display_print_display_setup(struct drm_device *dev)
> >                 }
> >                 i++;
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >  }
> >
> >  /**
> > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c
> > index 571a6dfb473e..61fcf247a638 100644
> > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c
> > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c
> > @@ -37,12 +37,14 @@ amdgpu_link_encoder_connector(struct drm_device *dev)
> >  {
> >         struct amdgpu_device *adev = dev->dev_private;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         struct amdgpu_connector *amdgpu_connector;
> >         struct drm_encoder *encoder;
> >         struct amdgpu_encoder *amdgpu_encoder;
> >
> > +       drm_connector_list_iter_begin(dev, &iter);
> >         /* walk the list and link encoders to connectors */
> > -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 amdgpu_connector = to_amdgpu_connector(connector);
> >                 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
> >                         amdgpu_encoder = to_amdgpu_encoder(encoder);
> > @@ -55,6 +57,7 @@ amdgpu_link_encoder_connector(struct drm_device *dev)
> >                         }
> >                 }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >  }
> >
> >  void amdgpu_encoder_set_active_device(struct drm_encoder *encoder)
> > @@ -62,8 +65,10 @@ void amdgpu_encoder_set_active_device(struct drm_encoder *encoder)
> >         struct drm_device *dev = encoder->dev;
> >         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >
> > -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 if (connector->encoder == encoder) {
> >                         struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
> >                         amdgpu_encoder->active_device = amdgpu_encoder->devices & amdgpu_connector->devices;
> > @@ -72,6 +77,7 @@ void amdgpu_encoder_set_active_device(struct drm_encoder *encoder)
> >                                   amdgpu_connector->devices, encoder->encoder_type);
> >                 }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >  }
> >
> >  struct drm_connector *
> > @@ -79,15 +85,20 @@ amdgpu_get_connector_for_encoder(struct drm_encoder *encoder)
> >  {
> >         struct drm_device *dev = encoder->dev;
> >         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
> > -       struct drm_connector *connector;
> > +       struct drm_connector *connector, *found = NULL;
> > +       struct drm_connector_list_iter iter;
> >         struct amdgpu_connector *amdgpu_connector;
> >
> > -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 amdgpu_connector = to_amdgpu_connector(connector);
> > -               if (amdgpu_encoder->active_device & amdgpu_connector->devices)
> > -                       return connector;
> > +               if (amdgpu_encoder->active_device & amdgpu_connector->devices) {
> > +                       found = connector;
> > +                       break;
> > +               }
> >         }
> > -       return NULL;
> > +       drm_connector_list_iter_end(&iter);
> > +       return found;
> >  }
> >
> >  struct drm_connector *
> > @@ -95,15 +106,20 @@ amdgpu_get_connector_for_encoder_init(struct drm_encoder *encoder)
> >  {
> >         struct drm_device *dev = encoder->dev;
> >         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
> > -       struct drm_connector *connector;
> > +       struct drm_connector *connector, *found = NULL;
> > +       struct drm_connector_list_iter iter;
> >         struct amdgpu_connector *amdgpu_connector;
> >
> > -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 amdgpu_connector = to_amdgpu_connector(connector);
> > -               if (amdgpu_encoder->devices & amdgpu_connector->devices)
> > -                       return connector;
> > +               if (amdgpu_encoder->devices & amdgpu_connector->devices) {
> > +                       found = connector;
> > +                       break;
> > +               }
> >         }
> > -       return NULL;
> > +       drm_connector_list_iter_end(&iter);
> > +       return found;
> >  }
> >
> >  struct drm_encoder *amdgpu_get_external_encoder(struct drm_encoder *encoder)
> > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
> > index 2a3f5ec298db..977e121204e6 100644
> > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
> > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
> > @@ -87,10 +87,13 @@ static void amdgpu_hotplug_work_func(struct work_struct *work)
> >         struct drm_device *dev = adev->ddev;
> >         struct drm_mode_config *mode_config = &dev->mode_config;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >
> >         mutex_lock(&mode_config->mutex);
> > -       list_for_each_entry(connector, &mode_config->connector_list, head)
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter)
> >                 amdgpu_connector_hotplug(connector);
> > +       drm_connector_list_iter_end(&iter);
> >         mutex_unlock(&mode_config->mutex);
> >         /* Just fire off a uevent and let userspace tell us what to do */
> >         drm_helper_hpd_irq_event(dev);
> > diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
> > index 645550e7caf5..be82871ac3bd 100644
> > --- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
> > +++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
> > @@ -330,9 +330,11 @@ static void dce_v10_0_hpd_init(struct amdgpu_device *adev)
> >  {
> >         struct drm_device *dev = adev->ddev;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         u32 tmp;
> >
> > -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
> >
> >                 if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
> > @@ -368,6 +370,7 @@ static void dce_v10_0_hpd_init(struct amdgpu_device *adev)
> >                 amdgpu_irq_get(adev, &adev->hpd_irq,
> >                                amdgpu_connector->hpd.hpd);
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >  }
> >
> >  /**
> > @@ -382,9 +385,11 @@ static void dce_v10_0_hpd_fini(struct amdgpu_device *adev)
> >  {
> >         struct drm_device *dev = adev->ddev;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         u32 tmp;
> >
> > -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
> >
> >                 if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
> > @@ -397,6 +402,7 @@ static void dce_v10_0_hpd_fini(struct amdgpu_device *adev)
> >                 amdgpu_irq_put(adev, &adev->hpd_irq,
> >                                amdgpu_connector->hpd.hpd);
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >  }
> >
> >  static u32 dce_v10_0_hpd_get_gpio_reg(struct amdgpu_device *adev)
> > @@ -1219,10 +1225,12 @@ static void dce_v10_0_afmt_audio_select_pin(struct drm_encoder *encoder)
> >  static void dce_v10_0_audio_write_latency_fields(struct drm_encoder *encoder,
> >                                                 struct drm_display_mode *mode)
> >  {
> > -       struct amdgpu_device *adev = encoder->dev->dev_private;
> > +       struct drm_device *dev = encoder->dev;
> > +       struct amdgpu_device *adev = dev->dev_private;
> >         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
> >         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         struct amdgpu_connector *amdgpu_connector = NULL;
> >         u32 tmp;
> >         int interlace = 0;
> > @@ -1230,12 +1238,14 @@ static void dce_v10_0_audio_write_latency_fields(struct drm_encoder *encoder,
> >         if (!dig || !dig->afmt || !dig->afmt->pin)
> >                 return;
> >
> > -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 if (connector->encoder == encoder) {
> >                         amdgpu_connector = to_amdgpu_connector(connector);
> >                         break;
> >                 }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >
> >         if (!amdgpu_connector) {
> >                 DRM_ERROR("Couldn't find encoder's connector\n");
> > @@ -1261,10 +1271,12 @@ static void dce_v10_0_audio_write_latency_fields(struct drm_encoder *encoder,
> >
> >  static void dce_v10_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
> >  {
> > -       struct amdgpu_device *adev = encoder->dev->dev_private;
> > +       struct drm_device *dev = encoder->dev;
> > +       struct amdgpu_device *adev = dev->dev_private;
> >         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
> >         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         struct amdgpu_connector *amdgpu_connector = NULL;
> >         u32 tmp;
> >         u8 *sadb = NULL;
> > @@ -1273,12 +1285,14 @@ static void dce_v10_0_audio_write_speaker_allocation(struct drm_encoder *encoder
> >         if (!dig || !dig->afmt || !dig->afmt->pin)
> >                 return;
> >
> > -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 if (connector->encoder == encoder) {
> >                         amdgpu_connector = to_amdgpu_connector(connector);
> >                         break;
> >                 }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >
> >         if (!amdgpu_connector) {
> >                 DRM_ERROR("Couldn't find encoder's connector\n");
> > @@ -1313,10 +1327,12 @@ static void dce_v10_0_audio_write_speaker_allocation(struct drm_encoder *encoder
> >
> >  static void dce_v10_0_audio_write_sad_regs(struct drm_encoder *encoder)
> >  {
> > -       struct amdgpu_device *adev = encoder->dev->dev_private;
> > +       struct drm_device *dev = encoder->dev;
> > +       struct amdgpu_device *adev = dev->dev_private;
> >         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
> >         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         struct amdgpu_connector *amdgpu_connector = NULL;
> >         struct cea_sad *sads;
> >         int i, sad_count;
> > @@ -1339,12 +1355,14 @@ static void dce_v10_0_audio_write_sad_regs(struct drm_encoder *encoder)
> >         if (!dig || !dig->afmt || !dig->afmt->pin)
> >                 return;
> >
> > -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 if (connector->encoder == encoder) {
> >                         amdgpu_connector = to_amdgpu_connector(connector);
> >                         break;
> >                 }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >
> >         if (!amdgpu_connector) {
> >                 DRM_ERROR("Couldn't find encoder's connector\n");
> > diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
> > index d9f470632b2c..bde48775cf1b 100644
> > --- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
> > +++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
> > @@ -348,9 +348,11 @@ static void dce_v11_0_hpd_init(struct amdgpu_device *adev)
> >  {
> >         struct drm_device *dev = adev->ddev;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         u32 tmp;
> >
> > -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
> >
> >                 if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
> > @@ -385,6 +387,7 @@ static void dce_v11_0_hpd_init(struct amdgpu_device *adev)
> >                 dce_v11_0_hpd_set_polarity(adev, amdgpu_connector->hpd.hpd);
> >                 amdgpu_irq_get(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >  }
> >
> >  /**
> > @@ -399,9 +402,11 @@ static void dce_v11_0_hpd_fini(struct amdgpu_device *adev)
> >  {
> >         struct drm_device *dev = adev->ddev;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         u32 tmp;
> >
> > -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
> >
> >                 if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
> > @@ -413,6 +418,7 @@ static void dce_v11_0_hpd_fini(struct amdgpu_device *adev)
> >
> >                 amdgpu_irq_put(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >  }
> >
> >  static u32 dce_v11_0_hpd_get_gpio_reg(struct amdgpu_device *adev)
> > @@ -1245,10 +1251,12 @@ static void dce_v11_0_afmt_audio_select_pin(struct drm_encoder *encoder)
> >  static void dce_v11_0_audio_write_latency_fields(struct drm_encoder *encoder,
> >                                                 struct drm_display_mode *mode)
> >  {
> > -       struct amdgpu_device *adev = encoder->dev->dev_private;
> > +       struct drm_device *dev = encoder->dev;
> > +       struct amdgpu_device *adev = dev->dev_private;
> >         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
> >         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         struct amdgpu_connector *amdgpu_connector = NULL;
> >         u32 tmp;
> >         int interlace = 0;
> > @@ -1256,12 +1264,14 @@ static void dce_v11_0_audio_write_latency_fields(struct drm_encoder *encoder,
> >         if (!dig || !dig->afmt || !dig->afmt->pin)
> >                 return;
> >
> > -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 if (connector->encoder == encoder) {
> >                         amdgpu_connector = to_amdgpu_connector(connector);
> >                         break;
> >                 }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >
> >         if (!amdgpu_connector) {
> >                 DRM_ERROR("Couldn't find encoder's connector\n");
> > @@ -1287,10 +1297,12 @@ static void dce_v11_0_audio_write_latency_fields(struct drm_encoder *encoder,
> >
> >  static void dce_v11_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
> >  {
> > -       struct amdgpu_device *adev = encoder->dev->dev_private;
> > +       struct drm_device *dev = encoder->dev;
> > +       struct amdgpu_device *adev = dev->dev_private;
> >         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
> >         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         struct amdgpu_connector *amdgpu_connector = NULL;
> >         u32 tmp;
> >         u8 *sadb = NULL;
> > @@ -1299,12 +1311,14 @@ static void dce_v11_0_audio_write_speaker_allocation(struct drm_encoder *encoder
> >         if (!dig || !dig->afmt || !dig->afmt->pin)
> >                 return;
> >
> > -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 if (connector->encoder == encoder) {
> >                         amdgpu_connector = to_amdgpu_connector(connector);
> >                         break;
> >                 }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >
> >         if (!amdgpu_connector) {
> >                 DRM_ERROR("Couldn't find encoder's connector\n");
> > @@ -1339,10 +1353,12 @@ static void dce_v11_0_audio_write_speaker_allocation(struct drm_encoder *encoder
> >
> >  static void dce_v11_0_audio_write_sad_regs(struct drm_encoder *encoder)
> >  {
> > -       struct amdgpu_device *adev = encoder->dev->dev_private;
> > +       struct drm_device *dev = encoder->dev;
> > +       struct amdgpu_device *adev = dev->dev_private;
> >         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
> >         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         struct amdgpu_connector *amdgpu_connector = NULL;
> >         struct cea_sad *sads;
> >         int i, sad_count;
> > @@ -1365,12 +1381,14 @@ static void dce_v11_0_audio_write_sad_regs(struct drm_encoder *encoder)
> >         if (!dig || !dig->afmt || !dig->afmt->pin)
> >                 return;
> >
> > -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 if (connector->encoder == encoder) {
> >                         amdgpu_connector = to_amdgpu_connector(connector);
> >                         break;
> >                 }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >
> >         if (!amdgpu_connector) {
> >                 DRM_ERROR("Couldn't find encoder's connector\n");
> > diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
> > index 3eb2e7429269..65f61de931d7 100644
> > --- a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
> > +++ b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
> > @@ -281,9 +281,11 @@ static void dce_v6_0_hpd_init(struct amdgpu_device *adev)
> >  {
> >         struct drm_device *dev = adev->ddev;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         u32 tmp;
> >
> > -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
> >
> >                 if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
> > @@ -309,7 +311,7 @@ static void dce_v6_0_hpd_init(struct amdgpu_device *adev)
> >                 dce_v6_0_hpd_set_polarity(adev, amdgpu_connector->hpd.hpd);
> >                 amdgpu_irq_get(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
> >         }
> > -
> > +       drm_connector_list_iter_end(&iter);
> >  }
> >
> >  /**
> > @@ -324,9 +326,11 @@ static void dce_v6_0_hpd_fini(struct amdgpu_device *adev)
> >  {
> >         struct drm_device *dev = adev->ddev;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         u32 tmp;
> >
> > -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
> >
> >                 if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
> > @@ -338,6 +342,7 @@ static void dce_v6_0_hpd_fini(struct amdgpu_device *adev)
> >
> >                 amdgpu_irq_put(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >  }
> >
> >  static u32 dce_v6_0_hpd_get_gpio_reg(struct amdgpu_device *adev)
> > @@ -1124,20 +1129,24 @@ static void dce_v6_0_audio_select_pin(struct drm_encoder *encoder)
> >  static void dce_v6_0_audio_write_latency_fields(struct drm_encoder *encoder,
> >                                                 struct drm_display_mode *mode)
> >  {
> > -       struct amdgpu_device *adev = encoder->dev->dev_private;
> > +       struct drm_device *dev = encoder->dev;
> > +       struct amdgpu_device *adev = dev->dev_private;
> >         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
> >         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         struct amdgpu_connector *amdgpu_connector = NULL;
> >         int interlace = 0;
> >         u32 tmp;
> >
> > -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 if (connector->encoder == encoder) {
> >                         amdgpu_connector = to_amdgpu_connector(connector);
> >                         break;
> >                 }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >
> >         if (!amdgpu_connector) {
> >                 DRM_ERROR("Couldn't find encoder's connector\n");
> > @@ -1164,21 +1173,25 @@ static void dce_v6_0_audio_write_latency_fields(struct drm_encoder *encoder,
> >
> >  static void dce_v6_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
> >  {
> > -       struct amdgpu_device *adev = encoder->dev->dev_private;
> > +       struct drm_device *dev = encoder->dev;
> > +       struct amdgpu_device *adev = dev->dev_private;
> >         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
> >         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         struct amdgpu_connector *amdgpu_connector = NULL;
> >         u8 *sadb = NULL;
> >         int sad_count;
> >         u32 tmp;
> >
> > -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 if (connector->encoder == encoder) {
> >                         amdgpu_connector = to_amdgpu_connector(connector);
> >                         break;
> >                 }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >
> >         if (!amdgpu_connector) {
> >                 DRM_ERROR("Couldn't find encoder's connector\n");
> > @@ -1221,10 +1234,12 @@ static void dce_v6_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
> >
> >  static void dce_v6_0_audio_write_sad_regs(struct drm_encoder *encoder)
> >  {
> > -       struct amdgpu_device *adev = encoder->dev->dev_private;
> > +       struct drm_device *dev = encoder->dev;
> > +       struct amdgpu_device *adev = dev->dev_private;
> >         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
> >         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         struct amdgpu_connector *amdgpu_connector = NULL;
> >         struct cea_sad *sads;
> >         int i, sad_count;
> > @@ -1244,12 +1259,14 @@ static void dce_v6_0_audio_write_sad_regs(struct drm_encoder *encoder)
> >                 { ixAZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR13, HDMI_AUDIO_CODING_TYPE_WMA_PRO },
> >         };
> >
> > -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 if (connector->encoder == encoder) {
> >                         amdgpu_connector = to_amdgpu_connector(connector);
> >                         break;
> >                 }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >
> >         if (!amdgpu_connector) {
> >                 DRM_ERROR("Couldn't find encoder's connector\n");
> > @@ -1632,6 +1649,7 @@ static void dce_v6_0_afmt_setmode(struct drm_encoder *encoder,
> >         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
> >         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         struct amdgpu_connector *amdgpu_connector = NULL;
> >         int em = amdgpu_atombios_encoder_get_encoder_mode(encoder);
> >         int bpc = 8;
> > @@ -1639,12 +1657,14 @@ static void dce_v6_0_afmt_setmode(struct drm_encoder *encoder,
> >         if (!dig || !dig->afmt)
> >                 return;
> >
> > -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 if (connector->encoder == encoder) {
> >                         amdgpu_connector = to_amdgpu_connector(connector);
> >                         break;
> >                 }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >
> >         if (!amdgpu_connector) {
> >                 DRM_ERROR("Couldn't find encoder's connector\n");
> > diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
> > index a16c5e9e610e..e5f50882a51d 100644
> > --- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
> > +++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
> > @@ -275,9 +275,11 @@ static void dce_v8_0_hpd_init(struct amdgpu_device *adev)
> >  {
> >         struct drm_device *dev = adev->ddev;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         u32 tmp;
> >
> > -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
> >
> >                 if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
> > @@ -303,6 +305,7 @@ static void dce_v8_0_hpd_init(struct amdgpu_device *adev)
> >                 dce_v8_0_hpd_set_polarity(adev, amdgpu_connector->hpd.hpd);
> >                 amdgpu_irq_get(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >  }
> >
> >  /**
> > @@ -317,9 +320,11 @@ static void dce_v8_0_hpd_fini(struct amdgpu_device *adev)
> >  {
> >         struct drm_device *dev = adev->ddev;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         u32 tmp;
> >
> > -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
> >
> >                 if (amdgpu_connector->hpd.hpd >= adev->mode_info.num_hpd)
> > @@ -331,6 +336,7 @@ static void dce_v8_0_hpd_fini(struct amdgpu_device *adev)
> >
> >                 amdgpu_irq_put(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >  }
> >
> >  static u32 dce_v8_0_hpd_get_gpio_reg(struct amdgpu_device *adev)
> > @@ -1157,10 +1163,12 @@ static void dce_v8_0_afmt_audio_select_pin(struct drm_encoder *encoder)
> >  static void dce_v8_0_audio_write_latency_fields(struct drm_encoder *encoder,
> >                                                 struct drm_display_mode *mode)
> >  {
> > -       struct amdgpu_device *adev = encoder->dev->dev_private;
> > +       struct drm_device *dev = encoder->dev;
> > +       struct amdgpu_device *adev = dev->dev_private;
> >         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
> >         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         struct amdgpu_connector *amdgpu_connector = NULL;
> >         u32 tmp = 0, offset;
> >
> > @@ -1169,12 +1177,14 @@ static void dce_v8_0_audio_write_latency_fields(struct drm_encoder *encoder,
> >
> >         offset = dig->afmt->pin->offset;
> >
> > -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 if (connector->encoder == encoder) {
> >                         amdgpu_connector = to_amdgpu_connector(connector);
> >                         break;
> >                 }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >
> >         if (!amdgpu_connector) {
> >                 DRM_ERROR("Couldn't find encoder's connector\n");
> > @@ -1214,10 +1224,12 @@ static void dce_v8_0_audio_write_latency_fields(struct drm_encoder *encoder,
> >
> >  static void dce_v8_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
> >  {
> > -       struct amdgpu_device *adev = encoder->dev->dev_private;
> > +       struct drm_device *dev = encoder->dev;
> > +       struct amdgpu_device *adev = dev->dev_private;
> >         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
> >         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         struct amdgpu_connector *amdgpu_connector = NULL;
> >         u32 offset, tmp;
> >         u8 *sadb = NULL;
> > @@ -1228,12 +1240,14 @@ static void dce_v8_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
> >
> >         offset = dig->afmt->pin->offset;
> >
> > -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 if (connector->encoder == encoder) {
> >                         amdgpu_connector = to_amdgpu_connector(connector);
> >                         break;
> >                 }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >
> >         if (!amdgpu_connector) {
> >                 DRM_ERROR("Couldn't find encoder's connector\n");
> > @@ -1263,11 +1277,13 @@ static void dce_v8_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
> >
> >  static void dce_v8_0_audio_write_sad_regs(struct drm_encoder *encoder)
> >  {
> > -       struct amdgpu_device *adev = encoder->dev->dev_private;
> > +       struct drm_device *dev = encoder->dev;
> > +       struct amdgpu_device *adev = dev->dev_private;
> >         struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
> >         struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
> >         u32 offset;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         struct amdgpu_connector *amdgpu_connector = NULL;
> >         struct cea_sad *sads;
> >         int i, sad_count;
> > @@ -1292,12 +1308,14 @@ static void dce_v8_0_audio_write_sad_regs(struct drm_encoder *encoder)
> >
> >         offset = dig->afmt->pin->offset;
> >
> > -       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 if (connector->encoder == encoder) {
> >                         amdgpu_connector = to_amdgpu_connector(connector);
> >                         break;
> >                 }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >
> >         if (!amdgpu_connector) {
> >                 DRM_ERROR("Couldn't find encoder's connector\n");
> > diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> > index 0a71ed1e7762..73630e2940d4 100644
> > --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> > +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> > @@ -896,27 +896,29 @@ static int detect_mst_link_for_all_connectors(struct drm_device *dev)
> >  {
> >         struct amdgpu_dm_connector *aconnector;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         int ret = 0;
> >
> > -       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> > -
> > -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 aconnector = to_amdgpu_dm_connector(connector);
> >                 if (aconnector->dc_link->type == dc_connection_mst_branch &&
> >                     aconnector->mst_mgr.aux) {
> >                         DRM_DEBUG_DRIVER("DM_MST: starting TM on aconnector: %p [id: %d]\n",
> > -                                       aconnector, aconnector->base.base.id);
> > +                                        aconnector,
> > +                                        aconnector->base.base.id);
> >
> >                         ret = drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_mgr, true);
> >                         if (ret < 0) {
> >                                 DRM_ERROR("DM_MST: Failed to start MST\n");
> > -                               ((struct dc_link *)aconnector->dc_link)->type = dc_connection_single;
> > -                               return ret;
> > -                               }
> > +                               aconnector->dc_link->type =
> > +                                       dc_connection_single;
> > +                               break;
> >                         }
> > +               }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >
> > -       drm_modeset_unlock(&dev->mode_config.connection_mutex);
> >         return ret;
> >  }
> >
> > @@ -954,14 +956,13 @@ static void s3_handle_mst(struct drm_device *dev, bool suspend)
> >  {
> >         struct amdgpu_dm_connector *aconnector;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         struct drm_dp_mst_topology_mgr *mgr;
> >         int ret;
> >         bool need_hotplug = false;
> >
> > -       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> > -
> > -       list_for_each_entry(connector, &dev->mode_config.connector_list,
> > -                           head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 aconnector = to_amdgpu_dm_connector(connector);
> >                 if (aconnector->dc_link->type != dc_connection_mst_branch ||
> >                     aconnector->mst_port)
> > @@ -979,8 +980,7 @@ static void s3_handle_mst(struct drm_device *dev, bool suspend)
> >                         }
> >                 }
> >         }
> > -
> > -       drm_modeset_unlock(&dev->mode_config.connection_mutex);
> > +       drm_connector_list_iter_end(&iter);
> >
> >         if (need_hotplug)
> >                 drm_kms_helper_hotplug_event(dev);
> > @@ -1162,6 +1162,7 @@ static int dm_resume(void *handle)
> >         struct amdgpu_display_manager *dm = &adev->dm;
> >         struct amdgpu_dm_connector *aconnector;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >         struct drm_crtc *crtc;
> >         struct drm_crtc_state *new_crtc_state;
> >         struct dm_crtc_state *dm_new_crtc_state;
> > @@ -1194,7 +1195,8 @@ static int dm_resume(void *handle)
> >         amdgpu_dm_irq_resume_early(adev);
> >
> >         /* Do detection*/
> > -       list_for_each_entry(connector, &ddev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(ddev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 aconnector = to_amdgpu_dm_connector(connector);
> >
> >                 /*
> > @@ -1222,6 +1224,7 @@ static int dm_resume(void *handle)
> >                 amdgpu_dm_update_connector_after_detect(aconnector);
> >                 mutex_unlock(&aconnector->hpd_lock);
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >
> >         /* Force mode set in atomic commit */
> >         for_each_new_crtc_in_state(dm->cached_state, crtc, new_crtc_state, i)
> > diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
> > index fa5d503d379c..64445c4cc4c2 100644
> > --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
> > +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
> > @@ -732,8 +732,10 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
> >  {
> >         struct drm_device *dev = adev->ddev;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >
> > -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 struct amdgpu_dm_connector *amdgpu_dm_connector =
> >                                 to_amdgpu_dm_connector(connector);
> >
> > @@ -751,6 +753,7 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
> >                                         true);
> >                 }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >  }
> >
> >  /**
> > @@ -765,8 +768,10 @@ void amdgpu_dm_hpd_fini(struct amdgpu_device *adev)
> >  {
> >         struct drm_device *dev = adev->ddev;
> >         struct drm_connector *connector;
> > +       struct drm_connector_list_iter iter;
> >
> > -       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> > +       drm_connector_list_iter_begin(dev, &iter);
> > +       drm_for_each_connector_iter(connector, &iter) {
> >                 struct amdgpu_dm_connector *amdgpu_dm_connector =
> >                                 to_amdgpu_dm_connector(connector);
> >                 const struct dc_link *dc_link = amdgpu_dm_connector->dc_link;
> > @@ -779,4 +784,5 @@ void amdgpu_dm_hpd_fini(struct amdgpu_device *adev)
> >                                         false);
> >                 }
> >         }
> > +       drm_connector_list_iter_end(&iter);
> >  }
> > --
> > 2.21.0
> >
> > _______________________________________________
> > amd-gfx mailing list
> > amd-gfx@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/amd-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 25/27] drm/dp_mst: Add basic topology reprobing when resuming
  2019-09-03 20:46 ` [PATCH v2 25/27] drm/dp_mst: Add basic topology reprobing when resuming Lyude Paul
@ 2019-09-27 13:52   ` Sean Paul
  2019-10-09 19:06     ` Lyude Paul
  0 siblings, 1 reply; 62+ messages in thread
From: Sean Paul @ 2019-09-27 13:52 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Harry Wentland, Leo Li,
	Alex Deucher, Christian König, David (ChunMing) Zhou,
	David Airlie, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
	Ben Skeggs, Nicholas Kazlauskas, David Francis, Mario Kleiner,
	Bhawanpreet Lakha, Chris Wilson, Manasi Navare,
	Dhinakaran Pandiyan, José Roberto de Souza, Karol Herbst,
	Laurent Pinchart, Ilia Mirkin, linux-kernel, intel-gfx

On Tue, Sep 03, 2019 at 04:46:03PM -0400, Lyude Paul wrote:
> Finally! For a very long time, our MST helpers have had one very
> annoying issue: They don't know how to reprobe the topology state when
> coming out of suspend. This means that if a user has a machine connected
> to an MST topology and decides to suspend their machine, we lose all
> topology changes that happened during that period. That can be a big
> problem if the machine was connected to a different topology on the same
> port before resuming, as we won't bother reprobing any of the ports and
> likely cause the user's monitors not to come back up as expected.
> 
> So, we start fixing this by teaching our MST helpers how to reprobe the
> link addresses of each connected topology when resuming. As it turns
> out, the behavior that we want here is identical to the behavior we want
> when initially probing a newly connected MST topology, with a couple of
> important differences:
> 
> - We need to be more careful about handling the potential races between
>   events from the MST hub that could change the topology state as we're
>   performing the link address reprobe
> - We need to be more careful about handling unlikely state changes on
>   ports - such as an input port turning into an output port, something
>   that would be far more likely to happen in situations like the MST hub
>   we're connected to being changed while we're suspend
> 
> Both of which have been solved by previous commits. That leaves one
> requirement:
> 
> - We need to prune any MST ports in our in-memory topology state that
>   were present when suspending, but have not appeared in the post-resume
>   link address response from their parent branch device
> 
> Which we can now handle in this commit by modifying
> drm_dp_send_link_address(). We then introduce suspend/resume reprobing
> by introducing drm_dp_mst_topology_mgr_invalidate_mstb(), which we call
> in drm_dp_mst_topology_mgr_suspend() to traverse the in-memory topology
> state to indicate that each mstb needs it's link address resent and PBN
> resources reprobed.
> 
> On resume, we start back up &mgr->work and have it reprobe the topology
> in the same way we would on a hotplug, removing any leftover ports that
> no longer appear in the topology state.
> 
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>
> ---
>  .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |   2 +-
>  drivers/gpu/drm/drm_dp_mst_topology.c         | 138 +++++++++++++-----
>  drivers/gpu/drm/i915/display/intel_dp.c       |   3 +-
>  drivers/gpu/drm/nouveau/dispnv50/disp.c       |   6 +-
>  include/drm/drm_dp_mst_helper.h               |   3 +-
>  5 files changed, 112 insertions(+), 40 deletions(-)
> 
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> index 4d3c8bff77da..27ee3e045b86 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> @@ -973,7 +973,7 @@ static void s3_handle_mst(struct drm_device *dev, bool suspend)
>  		if (suspend) {
>  			drm_dp_mst_topology_mgr_suspend(mgr);
>  		} else {
> -			ret = drm_dp_mst_topology_mgr_resume(mgr);
> +			ret = drm_dp_mst_topology_mgr_resume(mgr, true);
>  			if (ret < 0) {
>  				drm_dp_mst_topology_mgr_set_mst(mgr, false);
>  				need_hotplug = true;
> diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
> index e407aba1fbd2..2fe24e366925 100644
> --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> @@ -2020,6 +2020,14 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
>  		goto fail_unlock;
>  	}
>  
> +	/*
> +	 * If this port wasn't just created, then we're reprobing because
> +	 * we're coming out of suspend. In this case, always resend the link
> +	 * address if there's an MSTB on this port
> +	 */
> +	if (!created && port->pdt == DP_PEER_DEVICE_MST_BRANCHING)
> +		send_link_addr = true;
> +
>  	if (send_link_addr) {
>  		mutex_lock(&mgr->lock);
>  		if (port->mstb) {
> @@ -2530,7 +2538,8 @@ static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
>  {
>  	struct drm_dp_sideband_msg_tx *txmsg;
>  	struct drm_dp_link_address_ack_reply *reply;
> -	int i, len, ret;
> +	struct drm_dp_mst_port *port, *tmp;
> +	int i, len, ret, port_mask = 0;
>  
>  	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
>  	if (!txmsg)
> @@ -2560,9 +2569,28 @@ static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
>  
>  	drm_dp_check_mstb_guid(mstb, reply->guid);
>  
> -	for (i = 0; i < reply->nports; i++)
> +	for (i = 0; i < reply->nports; i++) {
> +		port_mask |= BIT(reply->ports[i].port_number);
>  		drm_dp_mst_handle_link_address_port(mstb, mgr->dev,
>  						    &reply->ports[i]);
> +	}
> +
> +	/* Prune any ports that are currently a part of mstb in our in-memory
> +	 * topology, but were not seen in this link address. Usually this
> +	 * means that they were removed while the topology was out of sync,
> +	 * e.g. during suspend/resume
> +	 */
> +	mutex_lock(&mgr->lock);
> +	list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
> +		if (port_mask & BIT(port->port_num))
> +			continue;
> +
> +		DRM_DEBUG_KMS("port %d was not in link address, removing\n",
> +			      port->port_num);
> +		list_del(&port->next);
> +		drm_dp_mst_topology_put_port(port);
> +	}
> +	mutex_unlock(&mgr->lock);
>  
>  	drm_kms_helper_hotplug_event(mgr->dev);
>  
> @@ -3191,6 +3219,23 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms
>  }
>  EXPORT_SYMBOL(drm_dp_mst_topology_mgr_set_mst);
>  
> +static void
> +drm_dp_mst_topology_mgr_invalidate_mstb(struct drm_dp_mst_branch *mstb)
> +{
> +	struct drm_dp_mst_port *port;
> +
> +	/* The link address will need to be re-sent on resume */
> +	mstb->link_address_sent = false;
> +
> +	list_for_each_entry(port, &mstb->ports, next) {
> +		/* The PBN for each port will also need to be re-probed */
> +		port->available_pbn = 0;
> +
> +		if (port->mstb)
> +			drm_dp_mst_topology_mgr_invalidate_mstb(port->mstb);
> +	}
> +}
> +
>  /**
>   * drm_dp_mst_topology_mgr_suspend() - suspend the MST manager
>   * @mgr: manager to suspend
> @@ -3207,60 +3252,85 @@ void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr)
>  	flush_work(&mgr->up_req_work);
>  	flush_work(&mgr->work);
>  	flush_work(&mgr->delayed_destroy_work);
> +
> +	mutex_lock(&mgr->lock);
> +	if (mgr->mst_state && mgr->mst_primary)
> +		drm_dp_mst_topology_mgr_invalidate_mstb(mgr->mst_primary);
> +	mutex_unlock(&mgr->lock);
>  }
>  EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend);
>  
>  /**
>   * drm_dp_mst_topology_mgr_resume() - resume the MST manager
>   * @mgr: manager to resume
> + * @sync: whether or not to perform topology reprobing synchronously
>   *
>   * This will fetch DPCD and see if the device is still there,
>   * if it is, it will rewrite the MSTM control bits, and return.
>   *
> - * if the device fails this returns -1, and the driver should do
> + * If the device fails this returns -1, and the driver should do
>   * a full MST reprobe, in case we were undocked.

nit: I don't think this sentence applies any longer since we're doing the reprobe.

> + *
> + * During system resume (where it is assumed that the driver will be calling
> + * drm_atomic_helper_resume()) this function should be called beforehand with
> + * @sync set to true. In contexts like runtime resume where the driver is not
> + * expected to be calling drm_atomic_helper_resume(), this function should be
> + * called with @sync set to false in order to avoid deadlocking.
> + *
> + * Returns: -1 if the MST topology was removed while we were suspended, 0
> + * otherwise.
>   */
> -int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr)
> +int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr,
> +				   bool sync)
>  {
> -	int ret = 0;
> +	int ret;
> +	u8 guid[16];
>  
>  	mutex_lock(&mgr->lock);
> +	if (!mgr->mst_primary)
> +		goto out_fail;
>  
> -	if (mgr->mst_primary) {
> -		int sret;
> -		u8 guid[16];
> +	ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd,
> +			       DP_RECEIVER_CAP_SIZE);
> +	if (ret != DP_RECEIVER_CAP_SIZE) {
> +		DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n");
> +		goto out_fail;
> +	}
>  
> -		sret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE);
> -		if (sret != DP_RECEIVER_CAP_SIZE) {
> -			DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n");
> -			ret = -1;
> -			goto out_unlock;
> -		}
> +	ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
> +				 DP_MST_EN |
> +				 DP_UP_REQ_EN |
> +				 DP_UPSTREAM_IS_SRC);
> +	if (ret < 0) {
> +		DRM_DEBUG_KMS("mst write failed - undocked during suspend?\n");
> +		goto out_fail;
> +	}
>  
> -		ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
> -					 DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC);
> -		if (ret < 0) {
> -			DRM_DEBUG_KMS("mst write failed - undocked during suspend?\n");
> -			ret = -1;
> -			goto out_unlock;
> -		}
> +	/* Some hubs forget their guids after they resume */
> +	ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16);
> +	if (ret != 16) {
> +		DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n");
> +		goto out_fail;
> +	}
> +	drm_dp_check_mstb_guid(mgr->mst_primary, guid);
>  
> -		/* Some hubs forget their guids after they resume */
> -		sret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16);
> -		if (sret != 16) {
> -			DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n");
> -			ret = -1;
> -			goto out_unlock;
> -		}
> -		drm_dp_check_mstb_guid(mgr->mst_primary, guid);
> +	/* For the final step of resuming the topology, we need to bring the
> +	 * state of our in-memory topology back into sync with reality. So,
> +	 * restart the probing process as if we're probing a new hub
> +	 */
> +	queue_work(system_long_wq, &mgr->work);
> +	mutex_unlock(&mgr->lock);
>  
> -		ret = 0;
> -	} else
> -		ret = -1;
> +	if (sync) {
> +		DRM_DEBUG_KMS("Waiting for link probe work to finish re-syncing topology...\n");
> +		flush_work(&mgr->work);
> +	}

It took me longer than I'd like to admit to realize that most of the diff in
this function is just removing the indent. Would you mind splitting that out
into a separate patch so the reprobe change is more obvious?

With these nits fixed,

Reviewed-by: Sean Paul <sean@poorly.run>


>  
> -out_unlock:
> +	return 0;
> +
> +out_fail:
>  	mutex_unlock(&mgr->lock);
> -	return ret;
> +	return -1;
>  }
>  EXPORT_SYMBOL(drm_dp_mst_topology_mgr_resume);
>  
> diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
> index 5673ed75e428..b78364dcdef9 100644
> --- a/drivers/gpu/drm/i915/display/intel_dp.c
> +++ b/drivers/gpu/drm/i915/display/intel_dp.c
> @@ -7400,7 +7400,8 @@ void intel_dp_mst_resume(struct drm_i915_private *dev_priv)
>  		if (!intel_dp->can_mst)
>  			continue;
>  
> -		ret = drm_dp_mst_topology_mgr_resume(&intel_dp->mst_mgr);
> +		ret = drm_dp_mst_topology_mgr_resume(&intel_dp->mst_mgr,
> +						     true);
>  		if (ret) {
>  			intel_dp->is_mst = false;
>  			drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr,
> diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c
> index 307584107d77..e459e2a79d78 100644
> --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c
> +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c
> @@ -1309,14 +1309,14 @@ nv50_mstm_fini(struct nv50_mstm *mstm)
>  }
>  
>  static void
> -nv50_mstm_init(struct nv50_mstm *mstm)
> +nv50_mstm_init(struct nv50_mstm *mstm, bool runtime)
>  {
>  	int ret;
>  
>  	if (!mstm || !mstm->mgr.mst_state)
>  		return;
>  
> -	ret = drm_dp_mst_topology_mgr_resume(&mstm->mgr);
> +	ret = drm_dp_mst_topology_mgr_resume(&mstm->mgr, !runtime);
>  	if (ret == -1) {
>  		drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false);
>  		drm_kms_helper_hotplug_event(mstm->mgr.dev);
> @@ -2262,7 +2262,7 @@ nv50_display_init(struct drm_device *dev, bool resume, bool runtime)
>  		if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) {
>  			struct nouveau_encoder *nv_encoder =
>  				nouveau_encoder(encoder);
> -			nv50_mstm_init(nv_encoder->dp.mstm);
> +			nv50_mstm_init(nv_encoder->dp.mstm, runtime);
>  		}
>  	}
>  
> diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
> index 1efbb086f7ac..1bdee5ee6dcd 100644
> --- a/include/drm/drm_dp_mst_helper.h
> +++ b/include/drm/drm_dp_mst_helper.h
> @@ -685,7 +685,8 @@ void drm_dp_mst_dump_topology(struct seq_file *m,
>  
>  void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);
>  int __must_check
> -drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr);
> +drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr,
> +			       bool sync);
>  
>  ssize_t drm_dp_mst_dpcd_read(struct drm_dp_aux *aux,
>  			     unsigned int offset, void *buffer, size_t size);
> -- 
> 2.21.0
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 26/27] drm/dp_mst: Also print unhashed pointers for malloc/topology references
  2019-09-03 20:46 ` [PATCH v2 26/27] drm/dp_mst: Also print unhashed pointers for malloc/topology references Lyude Paul
@ 2019-09-27 14:25   ` Sean Paul
  2019-10-09 19:40     ` Lyude Paul
  0 siblings, 1 reply; 62+ messages in thread
From: Sean Paul @ 2019-09-27 14:25 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

On Tue, Sep 03, 2019 at 04:46:04PM -0400, Lyude Paul wrote:
> Currently we only print mstb/port pointer addresses in our malloc and
> topology refcount functions using the hashed-by-default %p, but
> unfortunately if you're trying to debug a use-after-free error caused by
> a refcounting error then this really isn't terribly useful. On the other
> hand though, everything in the rest of the DP MST helpers uses hashed
> pointer values as well and probably isn't useful to convert to unhashed.
> So, let's just get the best of both worlds and print both the hashed and
> unhashed pointer in our malloc/topology refcount debugging output. This
> will hopefully make it a lot easier to figure out which port/mstb is
> causing KASAN to get upset.
> 
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>

It's really too bad there isn't a CONFIG_DEBUG_SHOW_PK_ADDRESSES or even a value
of kptr_restrict value that bypasses pointer hashing. I'm sure we're not the
only ones to feel this pain. Maybe everyone just hacks vsnprintf...

As it is, I'm not totally sold on exposing the actual addresses unconditionally.
What do you think about pulling the print out into a function and only printing
px if a debug kconfig is set?

Sean

> ---
>  drivers/gpu/drm/drm_dp_mst_topology.c | 34 ++++++++++++++++-----------
>  1 file changed, 20 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
> index 2fe24e366925..5b5c0b3b3c0e 100644
> --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> @@ -1327,7 +1327,8 @@ static void
>  drm_dp_mst_get_mstb_malloc(struct drm_dp_mst_branch *mstb)
>  {
>  	kref_get(&mstb->malloc_kref);
> -	DRM_DEBUG("mstb %p (%d)\n", mstb, kref_read(&mstb->malloc_kref));
> +	DRM_DEBUG("mstb %p/%px (%d)\n",
> +		  mstb, mstb, kref_read(&mstb->malloc_kref));
>  }
>  
>  /**
> @@ -1344,7 +1345,8 @@ drm_dp_mst_get_mstb_malloc(struct drm_dp_mst_branch *mstb)
>  static void
>  drm_dp_mst_put_mstb_malloc(struct drm_dp_mst_branch *mstb)
>  {
> -	DRM_DEBUG("mstb %p (%d)\n", mstb, kref_read(&mstb->malloc_kref) - 1);
> +	DRM_DEBUG("mstb %p/%px (%d)\n",
> +		  mstb, mstb, kref_read(&mstb->malloc_kref) - 1);
>  	kref_put(&mstb->malloc_kref, drm_dp_free_mst_branch_device);
>  }
>  
> @@ -1379,7 +1381,8 @@ void
>  drm_dp_mst_get_port_malloc(struct drm_dp_mst_port *port)
>  {
>  	kref_get(&port->malloc_kref);
> -	DRM_DEBUG("port %p (%d)\n", port, kref_read(&port->malloc_kref));
> +	DRM_DEBUG("port %p/%px (%d)\n",
> +		  port, port, kref_read(&port->malloc_kref));
>  }
>  EXPORT_SYMBOL(drm_dp_mst_get_port_malloc);
>  
> @@ -1396,7 +1399,8 @@ EXPORT_SYMBOL(drm_dp_mst_get_port_malloc);
>  void
>  drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port)
>  {
> -	DRM_DEBUG("port %p (%d)\n", port, kref_read(&port->malloc_kref) - 1);
> +	DRM_DEBUG("port %p/%px (%d)\n",
> +		  port, port, kref_read(&port->malloc_kref) - 1);
>  	kref_put(&port->malloc_kref, drm_dp_free_mst_port);
>  }
>  EXPORT_SYMBOL(drm_dp_mst_put_port_malloc);
> @@ -1447,8 +1451,8 @@ drm_dp_mst_topology_try_get_mstb(struct drm_dp_mst_branch *mstb)
>  	int ret = kref_get_unless_zero(&mstb->topology_kref);
>  
>  	if (ret)
> -		DRM_DEBUG("mstb %p (%d)\n", mstb,
> -			  kref_read(&mstb->topology_kref));
> +		DRM_DEBUG("mstb %p/%px (%d)\n",
> +			  mstb, mstb, kref_read(&mstb->topology_kref));
>  
>  	return ret;
>  }
> @@ -1471,7 +1475,8 @@ static void drm_dp_mst_topology_get_mstb(struct drm_dp_mst_branch *mstb)
>  {
>  	WARN_ON(kref_read(&mstb->topology_kref) == 0);
>  	kref_get(&mstb->topology_kref);
> -	DRM_DEBUG("mstb %p (%d)\n", mstb, kref_read(&mstb->topology_kref));
> +	DRM_DEBUG("mstb %p/%px (%d)\n",
> +		  mstb, mstb, kref_read(&mstb->topology_kref));
>  }
>  
>  /**
> @@ -1489,8 +1494,8 @@ static void drm_dp_mst_topology_get_mstb(struct drm_dp_mst_branch *mstb)
>  static void
>  drm_dp_mst_topology_put_mstb(struct drm_dp_mst_branch *mstb)
>  {
> -	DRM_DEBUG("mstb %p (%d)\n",
> -		  mstb, kref_read(&mstb->topology_kref) - 1);
> +	DRM_DEBUG("mstb %p/%px (%d)\n",
> +		  mstb, mstb, kref_read(&mstb->topology_kref) - 1);
>  	kref_put(&mstb->topology_kref, drm_dp_destroy_mst_branch_device);
>  }
>  
> @@ -1546,8 +1551,8 @@ drm_dp_mst_topology_try_get_port(struct drm_dp_mst_port *port)
>  	int ret = kref_get_unless_zero(&port->topology_kref);
>  
>  	if (ret)
> -		DRM_DEBUG("port %p (%d)\n", port,
> -			  kref_read(&port->topology_kref));
> +		DRM_DEBUG("port %p/%px (%d)\n",
> +			  port, port, kref_read(&port->topology_kref));
>  
>  	return ret;
>  }
> @@ -1569,7 +1574,8 @@ static void drm_dp_mst_topology_get_port(struct drm_dp_mst_port *port)
>  {
>  	WARN_ON(kref_read(&port->topology_kref) == 0);
>  	kref_get(&port->topology_kref);
> -	DRM_DEBUG("port %p (%d)\n", port, kref_read(&port->topology_kref));
> +	DRM_DEBUG("port %p/%px (%d)\n",
> +		  port, port, kref_read(&port->topology_kref));
>  }
>  
>  /**
> @@ -1585,8 +1591,8 @@ static void drm_dp_mst_topology_get_port(struct drm_dp_mst_port *port)
>   */
>  static void drm_dp_mst_topology_put_port(struct drm_dp_mst_port *port)
>  {
> -	DRM_DEBUG("port %p (%d)\n",
> -		  port, kref_read(&port->topology_kref) - 1);
> +	DRM_DEBUG("port %p/%px (%d)\n",
> +		  port, port, kref_read(&port->topology_kref) - 1);
>  	kref_put(&port->topology_kref, drm_dp_destroy_port);
>  }
>  
> -- 
> 2.21.0
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 27/27] drm/dp_mst: Add topology ref history tracking for debugging
  2019-09-03 20:46 ` [PATCH v2 27/27] drm/dp_mst: Add topology ref history tracking for debugging Lyude Paul
@ 2019-09-27 14:51   ` Sean Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Sean Paul @ 2019-09-27 14:51 UTC (permalink / raw)
  To: Lyude Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Sean Paul, David Airlie, Daniel Vetter, linux-kernel

On Tue, Sep 03, 2019 at 04:46:05PM -0400, Lyude Paul wrote:
> For very subtle mistakes with topology refs, it can be rather difficult
> to trace them down with the debugging info that we already have. I had
> one such issue recently while trying to implement suspend/resume
> reprobing for MST, and ended up coming up with this.
> 
> Inspired by Chris Wilson's wakeref tracking for i915, this adds a very
> similar feature to the DP MST helpers, which allows for partial tracking
> of topology refs for both ports and branch devices. This is a lot less
> advanced then wakeref tracking: we merely keep a count of all of the
> spots where a topology ref has been grabbed or dropped, then dump out
> that history in chronological order when a port or branch device's
> topology refcount reaches 0. So far, I've found this incredibly useful
> for debugging topology refcount errors.
> 
> Since this has the potential to be somewhat slow and loud, we add an
> expert kernel config option to enable or disable this feature,
> CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS.
> 

Looks very useful indeed! 

My only nit is that we could probably grow the list a little more aggressively
(or start it off at some size > 1) and avoid a bunch of reallocs. That said,
I'm not sure how often it's reallocated so it might not be an issue. Either
way, 

Reviewed-by: Sean Paul <sean@poorly.run>


> Changes since v1:
> * Don't forget to destroy topology_ref_history_lock
> 
> Cc: Juston Li <juston.li@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: Harry Wentland <hwentlan@amd.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Lyude Paul <lyude@redhat.com>
> ---
>  drivers/gpu/drm/Kconfig               |  14 ++
>  drivers/gpu/drm/drm_dp_mst_topology.c | 233 +++++++++++++++++++++++++-
>  include/drm/drm_dp_mst_helper.h       |  45 +++++
>  3 files changed, 288 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index e67c194c2aca..44fc2c2a6e2c 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -93,6 +93,20 @@ config DRM_KMS_FB_HELPER
>  	help
>  	  FBDEV helpers for KMS drivers.
>  
> +config DRM_DEBUG_DP_MST_TOPOLOGY_REFS
> +        bool "Enable refcount backtrace history in the DP MST helpers"
> +        select STACKDEPOT
> +        depends on DRM_KMS_HELPER
> +        depends on DEBUG_KERNEL
> +        depends on EXPERT
> +        help
> +          Enables debug tracing for topology refs in DRM's DP MST helpers. A
> +          history of each topology reference/dereference will be printed to the
> +          kernel log once a port or branch device's topology refcount reaches 0.
> +
> +          This has the potential to use a lot of memory and print some very
> +          large kernel messages. If in doubt, say "N".
> +
>  config DRM_FBDEV_EMULATION
>  	bool "Enable legacy fbdev support for your modesetting driver"
>  	depends on DRM
> diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
> index 5b5c0b3b3c0e..18f9a02927d9 100644
> --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> @@ -28,6 +28,13 @@
>  #include <linux/sched.h>
>  #include <linux/seq_file.h>
>  
> +#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
> +#include <linux/stackdepot.h>
> +#include <linux/sort.h>
> +#include <linux/timekeeping.h>
> +#include <linux/math64.h>
> +#endif
> +
>  #include <drm/drm_atomic.h>
>  #include <drm/drm_atomic_helper.h>
>  #include <drm/drm_dp_mst_helper.h>
> @@ -1405,12 +1412,189 @@ drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port)
>  }
>  EXPORT_SYMBOL(drm_dp_mst_put_port_malloc);
>  
> +#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
> +
> +#define STACK_DEPTH 8
> +
> +static noinline void
> +__topology_ref_save(struct drm_dp_mst_topology_mgr *mgr,
> +		    struct drm_dp_mst_topology_ref_history *history,
> +		    enum drm_dp_mst_topology_ref_type type)
> +{
> +	struct drm_dp_mst_topology_ref_entry *entry = NULL;
> +	depot_stack_handle_t backtrace;
> +	ulong stack_entries[STACK_DEPTH];
> +	uint n;
> +	int i;
> +
> +	n = stack_trace_save(stack_entries, ARRAY_SIZE(stack_entries), 1);
> +	backtrace = stack_depot_save(stack_entries, n, GFP_KERNEL);
> +	if (!backtrace)
> +		goto fail_alloc;
> +
> +	/* Try to find an existing entry for this backtrace */
> +	for (i = 0; i < history->len; i++) {
> +		if (history->entries[i].backtrace == backtrace) {
> +			entry = &history->entries[i];
> +			break;
> +		}
> +	}
> +
> +	/* Otherwise add one */
> +	if (!entry) {
> +		struct drm_dp_mst_topology_ref_entry *new;
> +		int new_len = history->len + 1;
> +
> +		new = krealloc(history->entries, sizeof(*new) * new_len,
> +			       GFP_KERNEL);
> +		if (!new)
> +			goto fail_alloc;
> +
> +		entry = &new[history->len];
> +		history->len = new_len;
> +		history->entries = new;
> +
> +		entry->backtrace = backtrace;
> +		entry->type = type;
> +		entry->count = 0;
> +	}
> +	entry->count++;
> +	entry->ts_nsec = ktime_get_ns();
> +
> +	return;
> +fail_alloc:
> +	DRM_WARN_ONCE("Failed to allocate memory for topology refcount backtrace\n");
> +}
> +
> +static int
> +topology_ref_history_cmp(const void *a, const void *b)
> +{
> +	const struct drm_dp_mst_topology_ref_entry *entry_a = a, *entry_b = b;
> +
> +	if (entry_a->ts_nsec > entry_b->ts_nsec)
> +		return 1;
> +	else if (entry_a->ts_nsec < entry_b->ts_nsec)
> +		return -1;
> +	else
> +		return 0;
> +}
> +
> +static inline const char *
> +topology_ref_type_to_str(enum drm_dp_mst_topology_ref_type type)
> +{
> +	if (type == DRM_DP_MST_TOPOLOGY_REF_GET)
> +		return "get";
> +	else
> +		return "put";
> +}
> +
> +static void
> +__dump_topology_ref_history(struct drm_dp_mst_topology_ref_history *history,
> +			    void *ptr, const char *type_str)
> +{
> +	struct drm_printer p = drm_debug_printer(DBG_PREFIX);
> +	char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
> +	int i;
> +
> +	if (!buf)
> +		return;
> +
> +	if (!history->len)
> +		goto out;
> +
> +	/* First, sort the list so that it goes from oldest to newest
> +	 * reference entry
> +	 */
> +	sort(history->entries, history->len, sizeof(*history->entries),
> +	     topology_ref_history_cmp, NULL);
> +
> +	drm_printf(&p,
> +		   "%s (%p/%px) topology count reached 0, dumping history:\n",
> +		   type_str, ptr, ptr);
> +
> +	for (i = 0; i < history->len; i++) {
> +		const struct drm_dp_mst_topology_ref_entry *entry =
> +			&history->entries[i];
> +		ulong *entries;
> +		uint nr_entries;
> +		u64 ts_nsec = entry->ts_nsec;
> +		u64 rem_nsec = do_div(ts_nsec, 1000000000);
> +
> +		nr_entries = stack_depot_fetch(entry->backtrace, &entries);
> +		stack_trace_snprint(buf, PAGE_SIZE, entries, nr_entries, 4);
> +
> +		drm_printf(&p, "  %d %ss (last at %5llu.%06llu):\n%s",
> +			   entry->count,
> +			   topology_ref_type_to_str(entry->type),
> +			   ts_nsec, rem_nsec / 1000, buf);
> +	}
> +
> +	/* Now free the history, since this is the only time we expose it */
> +	kfree(history->entries);
> +out:
> +	kfree(buf);
> +}
> +
> +static __always_inline void
> +drm_dp_mst_dump_mstb_topology_history(struct drm_dp_mst_branch *mstb)
> +{
> +	__dump_topology_ref_history(&mstb->topology_ref_history, mstb,
> +				    "MSTB");
> +}
> +
> +static __always_inline void
> +drm_dp_mst_dump_port_topology_history(struct drm_dp_mst_port *port)
> +{
> +	__dump_topology_ref_history(&port->topology_ref_history, port,
> +				    "Port");
> +}
> +
> +static __always_inline void
> +save_mstb_topology_ref(struct drm_dp_mst_branch *mstb,
> +		       enum drm_dp_mst_topology_ref_type type)
> +{
> +	__topology_ref_save(mstb->mgr, &mstb->topology_ref_history, type);
> +}
> +
> +static __always_inline void
> +save_port_topology_ref(struct drm_dp_mst_port *port,
> +		       enum drm_dp_mst_topology_ref_type type)
> +{
> +	__topology_ref_save(port->mgr, &port->topology_ref_history, type);
> +}
> +
> +static inline void
> +topology_ref_history_lock(struct drm_dp_mst_topology_mgr *mgr)
> +{
> +	mutex_lock(&mgr->topology_ref_history_lock);
> +}
> +
> +static inline void
> +topology_ref_history_unlock(struct drm_dp_mst_topology_mgr *mgr)
> +{
> +	mutex_unlock(&mgr->topology_ref_history_lock);
> +}
> +#else
> +static inline void
> +topology_ref_history_lock(struct drm_dp_mst_topology_mgr *mgr) {}
> +static inline void
> +topology_ref_history_unlock(struct drm_dp_mst_topology_mgr *mgr) {}
> +static inline void
> +drm_dp_mst_dump_mstb_topology_history(struct drm_dp_mst_branch *mstb) {}
> +static inline void
> +drm_dp_mst_dump_port_topology_history(struct drm_dp_mst_port *port) {}
> +#define save_mstb_topology_ref(mstb, type)
> +#define save_port_topology_ref(port, type)
> +#endif
> +
>  static void drm_dp_destroy_mst_branch_device(struct kref *kref)
>  {
>  	struct drm_dp_mst_branch *mstb =
>  		container_of(kref, struct drm_dp_mst_branch, topology_kref);
>  	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
>  
> +	drm_dp_mst_dump_mstb_topology_history(mstb);
> +
>  	INIT_LIST_HEAD(&mstb->destroy_next);
>  
>  	/*
> @@ -1448,11 +1632,18 @@ static void drm_dp_destroy_mst_branch_device(struct kref *kref)
>  static int __must_check
>  drm_dp_mst_topology_try_get_mstb(struct drm_dp_mst_branch *mstb)
>  {
> -	int ret = kref_get_unless_zero(&mstb->topology_kref);
> +	int ret;
>  
> -	if (ret)
> +	topology_ref_history_lock(mstb->mgr);
> +	ret = kref_get_unless_zero(&mstb->topology_kref);
> +
> +	if (ret) {
>  		DRM_DEBUG("mstb %p/%px (%d)\n",
>  			  mstb, mstb, kref_read(&mstb->topology_kref));
> +		save_mstb_topology_ref(mstb, DRM_DP_MST_TOPOLOGY_REF_GET);
> +	}
> +
> +	topology_ref_history_unlock(mstb->mgr);
>  
>  	return ret;
>  }
> @@ -1473,10 +1664,15 @@ drm_dp_mst_topology_try_get_mstb(struct drm_dp_mst_branch *mstb)
>   */
>  static void drm_dp_mst_topology_get_mstb(struct drm_dp_mst_branch *mstb)
>  {
> +	topology_ref_history_lock(mstb->mgr);
> +
> +	save_mstb_topology_ref(mstb, DRM_DP_MST_TOPOLOGY_REF_GET);
>  	WARN_ON(kref_read(&mstb->topology_kref) == 0);
>  	kref_get(&mstb->topology_kref);
>  	DRM_DEBUG("mstb %p/%px (%d)\n",
>  		  mstb, mstb, kref_read(&mstb->topology_kref));
> +
> +	topology_ref_history_unlock(mstb->mgr);
>  }
>  
>  /**
> @@ -1494,9 +1690,14 @@ static void drm_dp_mst_topology_get_mstb(struct drm_dp_mst_branch *mstb)
>  static void
>  drm_dp_mst_topology_put_mstb(struct drm_dp_mst_branch *mstb)
>  {
> +	topology_ref_history_lock(mstb->mgr);
> +
>  	DRM_DEBUG("mstb %p/%px (%d)\n",
>  		  mstb, mstb, kref_read(&mstb->topology_kref) - 1);
> +	save_mstb_topology_ref(mstb, DRM_DP_MST_TOPOLOGY_REF_PUT);
>  	kref_put(&mstb->topology_kref, drm_dp_destroy_mst_branch_device);
> +
> +	topology_ref_history_unlock(mstb->mgr);
>  }
>  
>  static void drm_dp_destroy_port(struct kref *kref)
> @@ -1505,6 +1706,8 @@ static void drm_dp_destroy_port(struct kref *kref)
>  		container_of(kref, struct drm_dp_mst_port, topology_kref);
>  	struct drm_dp_mst_topology_mgr *mgr = port->mgr;
>  
> +	drm_dp_mst_dump_port_topology_history(port);
> +
>  	/* There's nothing that needs locking to destroy an input port yet */
>  	if (port->input) {
>  		drm_dp_mst_put_port_malloc(port);
> @@ -1548,12 +1751,18 @@ static void drm_dp_destroy_port(struct kref *kref)
>  static int __must_check
>  drm_dp_mst_topology_try_get_port(struct drm_dp_mst_port *port)
>  {
> -	int ret = kref_get_unless_zero(&port->topology_kref);
> +	int ret;
> +
> +	topology_ref_history_lock(port->mgr);
> +	ret = kref_get_unless_zero(&port->topology_kref);
>  
> -	if (ret)
> +	if (ret) {
>  		DRM_DEBUG("port %p/%px (%d)\n",
>  			  port, port, kref_read(&port->topology_kref));
> +		save_port_topology_ref(port, DRM_DP_MST_TOPOLOGY_REF_GET);
> +	}
>  
> +	topology_ref_history_unlock(port->mgr);
>  	return ret;
>  }
>  
> @@ -1572,10 +1781,15 @@ drm_dp_mst_topology_try_get_port(struct drm_dp_mst_port *port)
>   */
>  static void drm_dp_mst_topology_get_port(struct drm_dp_mst_port *port)
>  {
> +	topology_ref_history_lock(port->mgr);
> +
>  	WARN_ON(kref_read(&port->topology_kref) == 0);
>  	kref_get(&port->topology_kref);
>  	DRM_DEBUG("port %p/%px (%d)\n",
>  		  port, port, kref_read(&port->topology_kref));
> +	save_port_topology_ref(port, DRM_DP_MST_TOPOLOGY_REF_GET);
> +
> +	topology_ref_history_unlock(port->mgr);
>  }
>  
>  /**
> @@ -1591,9 +1805,14 @@ static void drm_dp_mst_topology_get_port(struct drm_dp_mst_port *port)
>   */
>  static void drm_dp_mst_topology_put_port(struct drm_dp_mst_port *port)
>  {
> +	topology_ref_history_lock(port->mgr);
> +
>  	DRM_DEBUG("port %p/%px (%d)\n",
>  		  port, port, kref_read(&port->topology_kref) - 1);
> +	save_port_topology_ref(port, DRM_DP_MST_TOPOLOGY_REF_PUT);
>  	kref_put(&port->topology_kref, drm_dp_destroy_port);
> +
> +	topology_ref_history_unlock(port->mgr);
>  }
>  
>  static struct drm_dp_mst_branch *
> @@ -4548,6 +4767,9 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
>  	mutex_init(&mgr->payload_lock);
>  	mutex_init(&mgr->delayed_destroy_lock);
>  	mutex_init(&mgr->up_req_lock);
> +#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
> +	mutex_init(&mgr->topology_ref_history_lock);
> +#endif
>  	INIT_LIST_HEAD(&mgr->tx_msg_downq);
>  	INIT_LIST_HEAD(&mgr->destroy_port_list);
>  	INIT_LIST_HEAD(&mgr->destroy_branch_device_list);
> @@ -4613,6 +4835,9 @@ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
>  	mutex_destroy(&mgr->qlock);
>  	mutex_destroy(&mgr->lock);
>  	mutex_destroy(&mgr->up_req_lock);
> +#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
> +	mutex_destroy(&mgr->topology_ref_history_lock);
> +#endif
>  }
>  EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);
>  
> diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
> index 1bdee5ee6dcd..75b8fba6f399 100644
> --- a/include/drm/drm_dp_mst_helper.h
> +++ b/include/drm/drm_dp_mst_helper.h
> @@ -26,6 +26,26 @@
>  #include <drm/drm_dp_helper.h>
>  #include <drm/drm_atomic.h>
>  
> +#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
> +#include <linux/stackdepot.h>
> +#include <linux/timekeeping.h>
> +
> +enum drm_dp_mst_topology_ref_type {
> +	DRM_DP_MST_TOPOLOGY_REF_GET,
> +	DRM_DP_MST_TOPOLOGY_REF_PUT,
> +};
> +
> +struct drm_dp_mst_topology_ref_history {
> +	struct drm_dp_mst_topology_ref_entry {
> +		enum drm_dp_mst_topology_ref_type type;
> +		int count;
> +		ktime_t ts_nsec;
> +		depot_stack_handle_t backtrace;
> +	} *entries;
> +	int len;
> +};
> +#endif /* IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS) */
> +
>  struct drm_dp_mst_branch;
>  
>  /**
> @@ -92,6 +112,14 @@ struct drm_dp_mst_port {
>  	 */
>  	struct kref malloc_kref;
>  
> +#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
> +	/**
> +	 * @topology_ref_history: A history of each topology
> +	 * reference/dereference. See CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS.
> +	 */
> +	struct drm_dp_mst_topology_ref_history topology_ref_history;
> +#endif
> +
>  	u8 port_num;
>  	bool input;
>  	bool mcs;
> @@ -162,6 +190,14 @@ struct drm_dp_mst_branch {
>  	 */
>  	struct kref malloc_kref;
>  
> +#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
> +	/**
> +	 * @topology_ref_history: A history of each topology
> +	 * reference/dereference. See CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS.
> +	 */
> +	struct drm_dp_mst_topology_ref_history topology_ref_history;
> +#endif
> +
>  	/**
>  	 * @destroy_next: linked-list entry used by
>  	 * drm_dp_delayed_destroy_work()
> @@ -630,6 +666,15 @@ struct drm_dp_mst_topology_mgr {
>  	 * transmissions.
>  	 */
>  	struct work_struct up_req_work;
> +
> +#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
> +	/**
> +	 * @topology_ref_history_lock: protects
> +	 * &drm_dp_mst_port.topology_ref_history and
> +	 * &drm_dp_mst_branch.topology_ref_history.
> +	 */
> +	struct mutex topology_ref_history_lock;
> +#endif
>  };
>  
>  int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
> -- 
> 2.21.0
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 03/27] drm/dp_mst: Destroy MSTBs asynchronously
  2019-09-27 13:31       ` Sean Paul
@ 2019-10-08  9:45         ` Daniel Vetter
  0 siblings, 0 replies; 62+ messages in thread
From: Daniel Vetter @ 2019-10-08  9:45 UTC (permalink / raw)
  To: Sean Paul
  Cc: Lyude Paul, dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	David Airlie, linux-kernel

On Fri, Sep 27, 2019 at 09:31:07AM -0400, Sean Paul wrote:
> On Wed, Sep 25, 2019 at 04:08:22PM -0400, Lyude Paul wrote:
> > On Wed, 2019-09-25 at 14:16 -0400, Sean Paul wrote:
> > > On Tue, Sep 03, 2019 at 04:45:41PM -0400, Lyude Paul wrote:
> > > > When reprobing an MST topology during resume, we have to account for the
> > > > fact that while we were suspended it's possible that mstbs may have been
> > > > removed from any ports in the topology. Since iterating downwards in the
> > > > topology requires that we hold &mgr->lock, destroying MSTBs from this
> > > > context would result in attempting to lock &mgr->lock a second time and
> > > > deadlocking.
> > > > 
> > > > So, fix this by first moving destruction of MSTBs into
> > > > destroy_connector_work, then rename destroy_connector_work and friends
> > > > to reflect that they now destroy both ports and mstbs.
> > > > 
> > > > Changes since v1:
> > > > * s/destroy_connector_list/destroy_port_list/
> > > >   s/connector_destroy_lock/delayed_destroy_lock/
> > > >   s/connector_destroy_work/delayed_destroy_work/
> > > >   s/drm_dp_finish_destroy_branch_device/drm_dp_delayed_destroy_mstb/
> > > >   s/drm_dp_finish_destroy_port/drm_dp_delayed_destroy_port/
> > > >   - danvet
> > > > * Use two loops in drm_dp_delayed_destroy_work() - danvet
> > > > * Better explain why we need to do this - danvet
> > > > * Use cancel_work_sync() instead of flush_work() - flush_work() doesn't
> > > >   account for work requeing
> > > > 
> > > > Cc: Juston Li <juston.li@intel.com>
> > > > Cc: Imre Deak <imre.deak@intel.com>
> > > > Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > > Cc: Harry Wentland <hwentlan@amd.com>
> > > > Cc: Daniel Vetter <daniel@ffwll.ch>
> > > > Signed-off-by: Lyude Paul <lyude@redhat.com>
> > > 
> > > Took me a while to grok this, and I'm still not 100% confident my mental
> > > model
> > > is correct, so please bear with me while I ask silly questions :)
> > > 
> > > Now that the destroy is delayed, and the port remains in the topology, is it
> > > possible we will underflow the topology kref by calling put_mstb multiple
> > > times?
> > > It looks like that would result in a WARN from refcount.c, and wouldn't call
> > > the
> > > destroy function multiple times, so that's nice :)
> > > 
> > > Similarly, is there any defense against calling get_mstb() between destroy()
> > > and
> > > the delayed destroy worker running?
> > > 
> > Good question! There's only one instance where we unconditionally grab an MSTB
> > ref, drm_dp_mst_topology_mgr_set_mst(), and in that location we're guaranteed
> > to be the only one with access to that mstb until we drop &mgr->lock.
> > Everywhere else we use drm_dp_mst_topology_try_get_mstb(), which uses
> > kref_get_unless_zero() to protect against that kind of situation (and forces
> > the caller to check with __must_check)

Imo would be good to add this to the commit message when merging as a Q&A,
just for the record. At least I like to do that with the
silly-but-no-so-silly questions that come up in review.
-Daniel

> 
> Awesome, thanks for the breakdown!
> 
> 
> Reviewed-by: Sean Paul <sean@poorly.run>
> 
> 
> > 
> > > Sean
> > > 
> > > > ---
> > > >  drivers/gpu/drm/drm_dp_mst_topology.c | 162 +++++++++++++++++---------
> > > >  include/drm/drm_dp_mst_helper.h       |  26 +++--
> > > >  2 files changed, 127 insertions(+), 61 deletions(-)
> > > > 
> > > > diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c
> > > > b/drivers/gpu/drm/drm_dp_mst_topology.c
> > > > index 3054ec622506..738f260d4b15 100644
> > > > --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> > > > +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> > > > @@ -1113,34 +1113,17 @@ static void
> > > > drm_dp_destroy_mst_branch_device(struct kref *kref)
> > > >  	struct drm_dp_mst_branch *mstb =
> > > >  		container_of(kref, struct drm_dp_mst_branch, topology_kref);
> > > >  	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
> > > > -	struct drm_dp_mst_port *port, *tmp;
> > > > -	bool wake_tx = false;
> > > >  
> > > > -	mutex_lock(&mgr->lock);
> > > > -	list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
> > > > -		list_del(&port->next);
> > > > -		drm_dp_mst_topology_put_port(port);
> > > > -	}
> > > > -	mutex_unlock(&mgr->lock);
> > > > -
> > > > -	/* drop any tx slots msg */
> > > > -	mutex_lock(&mstb->mgr->qlock);
> > > > -	if (mstb->tx_slots[0]) {
> > > > -		mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
> > > > -		mstb->tx_slots[0] = NULL;
> > > > -		wake_tx = true;
> > > > -	}
> > > > -	if (mstb->tx_slots[1]) {
> > > > -		mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
> > > > -		mstb->tx_slots[1] = NULL;
> > > > -		wake_tx = true;
> > > > -	}
> > > > -	mutex_unlock(&mstb->mgr->qlock);
> > > > +	INIT_LIST_HEAD(&mstb->destroy_next);
> > > >  
> > > > -	if (wake_tx)
> > > > -		wake_up_all(&mstb->mgr->tx_waitq);
> > > > -
> > > > -	drm_dp_mst_put_mstb_malloc(mstb);
> > > > +	/*
> > > > +	 * This can get called under mgr->mutex, so we need to perform the
> > > > +	 * actual destruction of the mstb in another worker
> > > > +	 */
> > > > +	mutex_lock(&mgr->delayed_destroy_lock);
> > > > +	list_add(&mstb->destroy_next, &mgr->destroy_branch_device_list);
> > > > +	mutex_unlock(&mgr->delayed_destroy_lock);
> > > > +	schedule_work(&mgr->delayed_destroy_work);
> > > >  }
> > > >  
> > > >  /**
> > > > @@ -1255,10 +1238,10 @@ static void drm_dp_destroy_port(struct kref *kref)
> > > >  			 * we might be holding the mode_config.mutex
> > > >  			 * from an EDID retrieval */
> > > >  
> > > > -			mutex_lock(&mgr->destroy_connector_lock);
> > > > -			list_add(&port->next, &mgr->destroy_connector_list);
> > > > -			mutex_unlock(&mgr->destroy_connector_lock);
> > > > -			schedule_work(&mgr->destroy_connector_work);
> > > > +			mutex_lock(&mgr->delayed_destroy_lock);
> > > > +			list_add(&port->next, &mgr->destroy_port_list);
> > > > +			mutex_unlock(&mgr->delayed_destroy_lock);
> > > > +			schedule_work(&mgr->delayed_destroy_work);
> > > >  			return;
> > > >  		}
> > > >  		/* no need to clean up vcpi
> > > > @@ -2792,7 +2775,7 @@ void drm_dp_mst_topology_mgr_suspend(struct
> > > > drm_dp_mst_topology_mgr *mgr)
> > > >  			   DP_MST_EN | DP_UPSTREAM_IS_SRC);
> > > >  	mutex_unlock(&mgr->lock);
> > > >  	flush_work(&mgr->work);
> > > > -	flush_work(&mgr->destroy_connector_work);
> > > > +	flush_work(&mgr->delayed_destroy_work);
> > > >  }
> > > >  EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend);
> > > >  
> > > > @@ -3740,34 +3723,104 @@ static void drm_dp_tx_work(struct work_struct
> > > > *work)
> > > >  	mutex_unlock(&mgr->qlock);
> > > >  }
> > > >  
> > > > -static void drm_dp_destroy_connector_work(struct work_struct *work)
> > > > +static inline void
> > > > +drm_dp_delayed_destroy_port(struct drm_dp_mst_port *port)
> > > >  {
> > > > -	struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct
> > > > drm_dp_mst_topology_mgr, destroy_connector_work);
> > > > -	struct drm_dp_mst_port *port;
> > > > -	bool send_hotplug = false;
> > > > +	port->mgr->cbs->destroy_connector(port->mgr, port->connector);
> > > > +
> > > > +	drm_dp_port_teardown_pdt(port, port->pdt);
> > > > +	port->pdt = DP_PEER_DEVICE_NONE;
> > > > +
> > > > +	drm_dp_mst_put_port_malloc(port);
> > > > +}
> > > > +
> > > > +static inline void
> > > > +drm_dp_delayed_destroy_mstb(struct drm_dp_mst_branch *mstb)
> > > > +{
> > > > +	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
> > > > +	struct drm_dp_mst_port *port, *tmp;
> > > > +	bool wake_tx = false;
> > > > +
> > > > +	mutex_lock(&mgr->lock);
> > > > +	list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
> > > > +		list_del(&port->next);
> > > > +		drm_dp_mst_topology_put_port(port);
> > > > +	}
> > > > +	mutex_unlock(&mgr->lock);
> > > > +
> > > > +	/* drop any tx slots msg */
> > > > +	mutex_lock(&mstb->mgr->qlock);
> > > > +	if (mstb->tx_slots[0]) {
> > > > +		mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
> > > > +		mstb->tx_slots[0] = NULL;
> > > > +		wake_tx = true;
> > > > +	}
> > > > +	if (mstb->tx_slots[1]) {
> > > > +		mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
> > > > +		mstb->tx_slots[1] = NULL;
> > > > +		wake_tx = true;
> > > > +	}
> > > > +	mutex_unlock(&mstb->mgr->qlock);
> > > > +
> > > > +	if (wake_tx)
> > > > +		wake_up_all(&mstb->mgr->tx_waitq);
> > > > +
> > > > +	drm_dp_mst_put_mstb_malloc(mstb);
> > > > +}
> > > > +
> > > > +static void drm_dp_delayed_destroy_work(struct work_struct *work)
> > > > +{
> > > > +	struct drm_dp_mst_topology_mgr *mgr =
> > > > +		container_of(work, struct drm_dp_mst_topology_mgr,
> > > > +			     delayed_destroy_work);
> > > > +	bool send_hotplug = false, go_again;
> > > > +
> > > >  	/*
> > > >  	 * Not a regular list traverse as we have to drop the destroy
> > > > -	 * connector lock before destroying the connector, to avoid AB->BA
> > > > +	 * connector lock before destroying the mstb/port, to avoid AB->BA
> > > >  	 * ordering between this lock and the config mutex.
> > > >  	 */
> > > > -	for (;;) {
> > > > -		mutex_lock(&mgr->destroy_connector_lock);
> > > > -		port = list_first_entry_or_null(&mgr->destroy_connector_list,
> > > > struct drm_dp_mst_port, next);
> > > > -		if (!port) {
> > > > -			mutex_unlock(&mgr->destroy_connector_lock);
> > > > -			break;
> > > > +	do {
> > > > +		go_again = false;
> > > > +
> > > > +		for (;;) {
> > > > +			struct drm_dp_mst_branch *mstb;
> > > > +
> > > > +			mutex_lock(&mgr->delayed_destroy_lock);
> > > > +			mstb = list_first_entry_or_null(&mgr-
> > > > >destroy_branch_device_list,
> > > > +							struct
> > > > drm_dp_mst_branch,
> > > > +							destroy_next);
> > > > +			if (mstb)
> > > > +				list_del(&mstb->destroy_next);
> > > > +			mutex_unlock(&mgr->delayed_destroy_lock);
> > > > +
> > > > +			if (!mstb)
> > > > +				break;
> > > > +
> > > > +			drm_dp_delayed_destroy_mstb(mstb);
> > > > +			go_again = true;
> > > >  		}
> > > > -		list_del(&port->next);
> > > > -		mutex_unlock(&mgr->destroy_connector_lock);
> > > >  
> > > > -		mgr->cbs->destroy_connector(mgr, port->connector);
> > > > +		for (;;) {
> > > > +			struct drm_dp_mst_port *port;
> > > >  
> > > > -		drm_dp_port_teardown_pdt(port, port->pdt);
> > > > -		port->pdt = DP_PEER_DEVICE_NONE;
> > > > +			mutex_lock(&mgr->delayed_destroy_lock);
> > > > +			port = list_first_entry_or_null(&mgr-
> > > > >destroy_port_list,
> > > > +							struct
> > > > drm_dp_mst_port,
> > > > +							next);
> > > > +			if (port)
> > > > +				list_del(&port->next);
> > > > +			mutex_unlock(&mgr->delayed_destroy_lock);
> > > > +
> > > > +			if (!port)
> > > > +				break;
> > > > +
> > > > +			drm_dp_delayed_destroy_port(port);
> > > > +			send_hotplug = true;
> > > > +			go_again = true;
> > > > +		}
> > > > +	} while (go_again);
> > > >  
> > > > -		drm_dp_mst_put_port_malloc(port);
> > > > -		send_hotplug = true;
> > > > -	}
> > > >  	if (send_hotplug)
> > > >  		drm_kms_helper_hotplug_event(mgr->dev);
> > > >  }
> > > > @@ -3957,12 +4010,13 @@ int drm_dp_mst_topology_mgr_init(struct
> > > > drm_dp_mst_topology_mgr *mgr,
> > > >  	mutex_init(&mgr->lock);
> > > >  	mutex_init(&mgr->qlock);
> > > >  	mutex_init(&mgr->payload_lock);
> > > > -	mutex_init(&mgr->destroy_connector_lock);
> > > > +	mutex_init(&mgr->delayed_destroy_lock);
> > > >  	INIT_LIST_HEAD(&mgr->tx_msg_downq);
> > > > -	INIT_LIST_HEAD(&mgr->destroy_connector_list);
> > > > +	INIT_LIST_HEAD(&mgr->destroy_port_list);
> > > > +	INIT_LIST_HEAD(&mgr->destroy_branch_device_list);
> > > >  	INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work);
> > > >  	INIT_WORK(&mgr->tx_work, drm_dp_tx_work);
> > > > -	INIT_WORK(&mgr->destroy_connector_work,
> > > > drm_dp_destroy_connector_work);
> > > > +	INIT_WORK(&mgr->delayed_destroy_work, drm_dp_delayed_destroy_work);
> > > >  	init_waitqueue_head(&mgr->tx_waitq);
> > > >  	mgr->dev = dev;
> > > >  	mgr->aux = aux;
> > > > @@ -4005,7 +4059,7 @@ void drm_dp_mst_topology_mgr_destroy(struct
> > > > drm_dp_mst_topology_mgr *mgr)
> > > >  {
> > > >  	drm_dp_mst_topology_mgr_set_mst(mgr, false);
> > > >  	flush_work(&mgr->work);
> > > > -	flush_work(&mgr->destroy_connector_work);
> > > > +	cancel_work_sync(&mgr->delayed_destroy_work);
> > > >  	mutex_lock(&mgr->payload_lock);
> > > >  	kfree(mgr->payloads);
> > > >  	mgr->payloads = NULL;
> > > > diff --git a/include/drm/drm_dp_mst_helper.h
> > > > b/include/drm/drm_dp_mst_helper.h
> > > > index fc349204a71b..4a4507fe928d 100644
> > > > --- a/include/drm/drm_dp_mst_helper.h
> > > > +++ b/include/drm/drm_dp_mst_helper.h
> > > > @@ -143,6 +143,12 @@ struct drm_dp_mst_branch {
> > > >  	 */
> > > >  	struct kref malloc_kref;
> > > >  
> > > > +	/**
> > > > +	 * @destroy_next: linked-list entry used by
> > > > +	 * drm_dp_delayed_destroy_work()
> > > > +	 */
> > > > +	struct list_head destroy_next;
> > > > +
> > > >  	u8 rad[8];
> > > >  	u8 lct;
> > > >  	int num_ports;
> > > > @@ -575,18 +581,24 @@ struct drm_dp_mst_topology_mgr {
> > > >  	struct work_struct tx_work;
> > > >  
> > > >  	/**
> > > > -	 * @destroy_connector_list: List of to be destroyed connectors.
> > > > +	 * @destroy_port_list: List of to be destroyed connectors.
> > > > +	 */
> > > > +	struct list_head destroy_port_list;
> > > > +	/**
> > > > +	 * @destroy_branch_device_list: List of to be destroyed branch
> > > > +	 * devices.
> > > >  	 */
> > > > -	struct list_head destroy_connector_list;
> > > > +	struct list_head destroy_branch_device_list;
> > > >  	/**
> > > > -	 * @destroy_connector_lock: Protects @connector_list.
> > > > +	 * @delayed_destroy_lock: Protects @destroy_port_list and
> > > > +	 * @destroy_branch_device_list.
> > > >  	 */
> > > > -	struct mutex destroy_connector_lock;
> > > > +	struct mutex delayed_destroy_lock;
> > > >  	/**
> > > > -	 * @destroy_connector_work: Work item to destroy connectors. Needed to
> > > > -	 * avoid locking inversion.
> > > > +	 * @delayed_destroy_work: Work item to destroy MST port and branch
> > > > +	 * devices, needed to avoid locking inversion.
> > > >  	 */
> > > > -	struct work_struct destroy_connector_work;
> > > > +	struct work_struct delayed_destroy_work;
> > > >  };
> > > >  
> > > >  int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
> > > > -- 
> > > > 2.21.0
> > > > 
> > -- 
> > Cheers,
> > 	Lyude Paul
> > 
> 
> -- 
> Sean Paul, Software Engineer, Google / Chromium OS

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 25/27] drm/dp_mst: Add basic topology reprobing when resuming
  2019-09-27 13:52   ` Sean Paul
@ 2019-10-09 19:06     ` Lyude Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Lyude Paul @ 2019-10-09 19:06 UTC (permalink / raw)
  To: Sean Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Harry Wentland, Leo Li,
	Alex Deucher, Christian König, David (ChunMing) Zhou,
	David Airlie, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	Jani Nikula, Joonas Lahtinen, Rodrigo Vivi, Ben Skeggs,
	Nicholas Kazlauskas, David Francis, Mario Kleiner,
	Bhawanpreet Lakha, Chris Wilson, Manasi Navare,
	Dhinakaran Pandiyan, José Roberto de Souza, Karol Herbst,
	Laurent Pinchart, Ilia Mirkin, linux-kernel, intel-gfx

On Fri, 2019-09-27 at 09:52 -0400, Sean Paul wrote:
> On Tue, Sep 03, 2019 at 04:46:03PM -0400, Lyude Paul wrote:
> > Finally! For a very long time, our MST helpers have had one very
> > annoying issue: They don't know how to reprobe the topology state when
> > coming out of suspend. This means that if a user has a machine connected
> > to an MST topology and decides to suspend their machine, we lose all
> > topology changes that happened during that period. That can be a big
> > problem if the machine was connected to a different topology on the same
> > port before resuming, as we won't bother reprobing any of the ports and
> > likely cause the user's monitors not to come back up as expected.
> > 
> > So, we start fixing this by teaching our MST helpers how to reprobe the
> > link addresses of each connected topology when resuming. As it turns
> > out, the behavior that we want here is identical to the behavior we want
> > when initially probing a newly connected MST topology, with a couple of
> > important differences:
> > 
> > - We need to be more careful about handling the potential races between
> >   events from the MST hub that could change the topology state as we're
> >   performing the link address reprobe
> > - We need to be more careful about handling unlikely state changes on
> >   ports - such as an input port turning into an output port, something
> >   that would be far more likely to happen in situations like the MST hub
> >   we're connected to being changed while we're suspend
> > 
> > Both of which have been solved by previous commits. That leaves one
> > requirement:
> > 
> > - We need to prune any MST ports in our in-memory topology state that
> >   were present when suspending, but have not appeared in the post-resume
> >   link address response from their parent branch device
> > 
> > Which we can now handle in this commit by modifying
> > drm_dp_send_link_address(). We then introduce suspend/resume reprobing
> > by introducing drm_dp_mst_topology_mgr_invalidate_mstb(), which we call
> > in drm_dp_mst_topology_mgr_suspend() to traverse the in-memory topology
> > state to indicate that each mstb needs it's link address resent and PBN
> > resources reprobed.
> > 
> > On resume, we start back up &mgr->work and have it reprobe the topology
> > in the same way we would on a hotplug, removing any leftover ports that
> > no longer appear in the topology state.
> > 
> > Cc: Juston Li <juston.li@intel.com>
> > Cc: Imre Deak <imre.deak@intel.com>
> > Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > Cc: Harry Wentland <hwentlan@amd.com>
> > Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> > Signed-off-by: Lyude Paul <lyude@redhat.com>
> > ---
> >  .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |   2 +-
> >  drivers/gpu/drm/drm_dp_mst_topology.c         | 138 +++++++++++++-----
> >  drivers/gpu/drm/i915/display/intel_dp.c       |   3 +-
> >  drivers/gpu/drm/nouveau/dispnv50/disp.c       |   6 +-
> >  include/drm/drm_dp_mst_helper.h               |   3 +-
> >  5 files changed, 112 insertions(+), 40 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> > b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> > index 4d3c8bff77da..27ee3e045b86 100644
> > --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> > +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> > @@ -973,7 +973,7 @@ static void s3_handle_mst(struct drm_device *dev, bool
> > suspend)
> >  		if (suspend) {
> >  			drm_dp_mst_topology_mgr_suspend(mgr);
> >  		} else {
> > -			ret = drm_dp_mst_topology_mgr_resume(mgr);
> > +			ret = drm_dp_mst_topology_mgr_resume(mgr, true);
> >  			if (ret < 0) {
> >  				drm_dp_mst_topology_mgr_set_mst(mgr, false);
> >  				need_hotplug = true;
> > diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c
> > b/drivers/gpu/drm/drm_dp_mst_topology.c
> > index e407aba1fbd2..2fe24e366925 100644
> > --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> > +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> > @@ -2020,6 +2020,14 @@ drm_dp_mst_handle_link_address_port(struct
> > drm_dp_mst_branch *mstb,
> >  		goto fail_unlock;
> >  	}
> >  
> > +	/*
> > +	 * If this port wasn't just created, then we're reprobing because
> > +	 * we're coming out of suspend. In this case, always resend the link
> > +	 * address if there's an MSTB on this port
> > +	 */
> > +	if (!created && port->pdt == DP_PEER_DEVICE_MST_BRANCHING)
> > +		send_link_addr = true;
> > +
> >  	if (send_link_addr) {
> >  		mutex_lock(&mgr->lock);
> >  		if (port->mstb) {
> > @@ -2530,7 +2538,8 @@ static void drm_dp_send_link_address(struct
> > drm_dp_mst_topology_mgr *mgr,
> >  {
> >  	struct drm_dp_sideband_msg_tx *txmsg;
> >  	struct drm_dp_link_address_ack_reply *reply;
> > -	int i, len, ret;
> > +	struct drm_dp_mst_port *port, *tmp;
> > +	int i, len, ret, port_mask = 0;
> >  
> >  	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
> >  	if (!txmsg)
> > @@ -2560,9 +2569,28 @@ static void drm_dp_send_link_address(struct
> > drm_dp_mst_topology_mgr *mgr,
> >  
> >  	drm_dp_check_mstb_guid(mstb, reply->guid);
> >  
> > -	for (i = 0; i < reply->nports; i++)
> > +	for (i = 0; i < reply->nports; i++) {
> > +		port_mask |= BIT(reply->ports[i].port_number);
> >  		drm_dp_mst_handle_link_address_port(mstb, mgr->dev,
> >  						    &reply->ports[i]);
> > +	}
> > +
> > +	/* Prune any ports that are currently a part of mstb in our in-memory
> > +	 * topology, but were not seen in this link address. Usually this
> > +	 * means that they were removed while the topology was out of sync,
> > +	 * e.g. during suspend/resume
> > +	 */
> > +	mutex_lock(&mgr->lock);
> > +	list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
> > +		if (port_mask & BIT(port->port_num))
> > +			continue;
> > +
> > +		DRM_DEBUG_KMS("port %d was not in link address, removing\n",
> > +			      port->port_num);
> > +		list_del(&port->next);
> > +		drm_dp_mst_topology_put_port(port);
> > +	}
> > +	mutex_unlock(&mgr->lock);
> >  
> >  	drm_kms_helper_hotplug_event(mgr->dev);
> >  
> > @@ -3191,6 +3219,23 @@ int drm_dp_mst_topology_mgr_set_mst(struct
> > drm_dp_mst_topology_mgr *mgr, bool ms
> >  }
> >  EXPORT_SYMBOL(drm_dp_mst_topology_mgr_set_mst);
> >  
> > +static void
> > +drm_dp_mst_topology_mgr_invalidate_mstb(struct drm_dp_mst_branch *mstb)
> > +{
> > +	struct drm_dp_mst_port *port;
> > +
> > +	/* The link address will need to be re-sent on resume */
> > +	mstb->link_address_sent = false;
> > +
> > +	list_for_each_entry(port, &mstb->ports, next) {
> > +		/* The PBN for each port will also need to be re-probed */
> > +		port->available_pbn = 0;
> > +
> > +		if (port->mstb)
> > +			drm_dp_mst_topology_mgr_invalidate_mstb(port->mstb);
> > +	}
> > +}
> > +
> >  /**
> >   * drm_dp_mst_topology_mgr_suspend() - suspend the MST manager
> >   * @mgr: manager to suspend
> > @@ -3207,60 +3252,85 @@ void drm_dp_mst_topology_mgr_suspend(struct
> > drm_dp_mst_topology_mgr *mgr)
> >  	flush_work(&mgr->up_req_work);
> >  	flush_work(&mgr->work);
> >  	flush_work(&mgr->delayed_destroy_work);
> > +
> > +	mutex_lock(&mgr->lock);
> > +	if (mgr->mst_state && mgr->mst_primary)
> > +		drm_dp_mst_topology_mgr_invalidate_mstb(mgr->mst_primary);
> > +	mutex_unlock(&mgr->lock);
> >  }
> >  EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend);
> >  
> >  /**
> >   * drm_dp_mst_topology_mgr_resume() - resume the MST manager
> >   * @mgr: manager to resume
> > + * @sync: whether or not to perform topology reprobing synchronously
> >   *
> >   * This will fetch DPCD and see if the device is still there,
> >   * if it is, it will rewrite the MSTM control bits, and return.
> >   *
> > - * if the device fails this returns -1, and the driver should do
> > + * If the device fails this returns -1, and the driver should do
> >   * a full MST reprobe, in case we were undocked.
> 
> nit: I don't think this sentence applies any longer since we're doing the
> reprobe.
Note that this does actually still apply, but I should be a bit more clear
about it: "reprobe" in this sense just means drm_dp_mst_topology_set_mst(mgr,
off)
> 
> > + *
> > + * During system resume (where it is assumed that the driver will be
> > calling
> > + * drm_atomic_helper_resume()) this function should be called beforehand
> > with
> > + * @sync set to true. In contexts like runtime resume where the driver is
> > not
> > + * expected to be calling drm_atomic_helper_resume(), this function
> > should be
> > + * called with @sync set to false in order to avoid deadlocking.
> > + *
> > + * Returns: -1 if the MST topology was removed while we were suspended, 0
> > + * otherwise.
> >   */
> > -int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr)
> > +int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr,
> > +				   bool sync)
> >  {
> > -	int ret = 0;
> > +	int ret;
> > +	u8 guid[16];
> >  
> >  	mutex_lock(&mgr->lock);
> > +	if (!mgr->mst_primary)
> > +		goto out_fail;
> >  
> > -	if (mgr->mst_primary) {
> > -		int sret;
> > -		u8 guid[16];
> > +	ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd,
> > +			       DP_RECEIVER_CAP_SIZE);
> > +	if (ret != DP_RECEIVER_CAP_SIZE) {
> > +		DRM_DEBUG_KMS("dpcd read failed - undocked during
> > suspend?\n");
> > +		goto out_fail;
> > +	}
> >  
> > -		sret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd,
> > DP_RECEIVER_CAP_SIZE);
> > -		if (sret != DP_RECEIVER_CAP_SIZE) {
> > -			DRM_DEBUG_KMS("dpcd read failed - undocked during
> > suspend?\n");
> > -			ret = -1;
> > -			goto out_unlock;
> > -		}
> > +	ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
> > +				 DP_MST_EN |
> > +				 DP_UP_REQ_EN |
> > +				 DP_UPSTREAM_IS_SRC);
> > +	if (ret < 0) {
> > +		DRM_DEBUG_KMS("mst write failed - undocked during
> > suspend?\n");
> > +		goto out_fail;
> > +	}
> >  
> > -		ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
> > -					 DP_MST_EN | DP_UP_REQ_EN |
> > DP_UPSTREAM_IS_SRC);
> > -		if (ret < 0) {
> > -			DRM_DEBUG_KMS("mst write failed - undocked during
> > suspend?\n");
> > -			ret = -1;
> > -			goto out_unlock;
> > -		}
> > +	/* Some hubs forget their guids after they resume */
> > +	ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16);
> > +	if (ret != 16) {
> > +		DRM_DEBUG_KMS("dpcd read failed - undocked during
> > suspend?\n");
> > +		goto out_fail;
> > +	}
> > +	drm_dp_check_mstb_guid(mgr->mst_primary, guid);
> >  
> > -		/* Some hubs forget their guids after they resume */
> > -		sret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16);
> > -		if (sret != 16) {
> > -			DRM_DEBUG_KMS("dpcd read failed - undocked during
> > suspend?\n");
> > -			ret = -1;
> > -			goto out_unlock;
> > -		}
> > -		drm_dp_check_mstb_guid(mgr->mst_primary, guid);
> > +	/* For the final step of resuming the topology, we need to bring the
> > +	 * state of our in-memory topology back into sync with reality. So,
> > +	 * restart the probing process as if we're probing a new hub
> > +	 */
> > +	queue_work(system_long_wq, &mgr->work);
> > +	mutex_unlock(&mgr->lock);
> >  
> > -		ret = 0;
> > -	} else
> > -		ret = -1;
> > +	if (sync) {
> > +		DRM_DEBUG_KMS("Waiting for link probe work to finish re-
> > syncing topology...\n");
> > +		flush_work(&mgr->work);
> > +	}
> 
> It took me longer than I'd like to admit to realize that most of the diff in
> this function is just removing the indent. Would you mind splitting that out
> into a separate patch so the reprobe change is more obvious?
> 
> With these nits fixed,
> 
> Reviewed-by: Sean Paul <sean@poorly.run>
> 
> 
> >  
> > -out_unlock:
> > +	return 0;
> > +
> > +out_fail:
> >  	mutex_unlock(&mgr->lock);
> > -	return ret;
> > +	return -1;
> >  }
> >  EXPORT_SYMBOL(drm_dp_mst_topology_mgr_resume);
> >  
> > diff --git a/drivers/gpu/drm/i915/display/intel_dp.c
> > b/drivers/gpu/drm/i915/display/intel_dp.c
> > index 5673ed75e428..b78364dcdef9 100644
> > --- a/drivers/gpu/drm/i915/display/intel_dp.c
> > +++ b/drivers/gpu/drm/i915/display/intel_dp.c
> > @@ -7400,7 +7400,8 @@ void intel_dp_mst_resume(struct drm_i915_private
> > *dev_priv)
> >  		if (!intel_dp->can_mst)
> >  			continue;
> >  
> > -		ret = drm_dp_mst_topology_mgr_resume(&intel_dp->mst_mgr);
> > +		ret = drm_dp_mst_topology_mgr_resume(&intel_dp->mst_mgr,
> > +						     true);
> >  		if (ret) {
> >  			intel_dp->is_mst = false;
> >  			drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr,
> > diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c
> > b/drivers/gpu/drm/nouveau/dispnv50/disp.c
> > index 307584107d77..e459e2a79d78 100644
> > --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c
> > +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c
> > @@ -1309,14 +1309,14 @@ nv50_mstm_fini(struct nv50_mstm *mstm)
> >  }
> >  
> >  static void
> > -nv50_mstm_init(struct nv50_mstm *mstm)
> > +nv50_mstm_init(struct nv50_mstm *mstm, bool runtime)
> >  {
> >  	int ret;
> >  
> >  	if (!mstm || !mstm->mgr.mst_state)
> >  		return;
> >  
> > -	ret = drm_dp_mst_topology_mgr_resume(&mstm->mgr);
> > +	ret = drm_dp_mst_topology_mgr_resume(&mstm->mgr, !runtime);
> >  	if (ret == -1) {
> >  		drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false);
> >  		drm_kms_helper_hotplug_event(mstm->mgr.dev);
> > @@ -2262,7 +2262,7 @@ nv50_display_init(struct drm_device *dev, bool
> > resume, bool runtime)
> >  		if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) {
> >  			struct nouveau_encoder *nv_encoder =
> >  				nouveau_encoder(encoder);
> > -			nv50_mstm_init(nv_encoder->dp.mstm);
> > +			nv50_mstm_init(nv_encoder->dp.mstm, runtime);
> >  		}
> >  	}
> >  
> > diff --git a/include/drm/drm_dp_mst_helper.h
> > b/include/drm/drm_dp_mst_helper.h
> > index 1efbb086f7ac..1bdee5ee6dcd 100644
> > --- a/include/drm/drm_dp_mst_helper.h
> > +++ b/include/drm/drm_dp_mst_helper.h
> > @@ -685,7 +685,8 @@ void drm_dp_mst_dump_topology(struct seq_file *m,
> >  
> >  void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr
> > *mgr);
> >  int __must_check
> > -drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr);
> > +drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr,
> > +			       bool sync);
> >  
> >  ssize_t drm_dp_mst_dpcd_read(struct drm_dp_aux *aux,
> >  			     unsigned int offset, void *buffer, size_t size);
> > -- 
> > 2.21.0
> > 
-- 
Cheers,
	Lyude Paul


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 26/27] drm/dp_mst: Also print unhashed pointers for malloc/topology references
  2019-09-27 14:25   ` Sean Paul
@ 2019-10-09 19:40     ` Lyude Paul
  0 siblings, 0 replies; 62+ messages in thread
From: Lyude Paul @ 2019-10-09 19:40 UTC (permalink / raw)
  To: Sean Paul
  Cc: dri-devel, nouveau, amd-gfx, Juston Li, Imre Deak,
	Ville Syrjälä,
	Harry Wentland, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
	David Airlie, Daniel Vetter, linux-kernel

Hey! Re: our discussion about this at XDC, I think I'm going to drop this
patch and just fix KASAN so it prints the hashed pointer as well, I'll cc you
on the patches for that as well

On Fri, 2019-09-27 at 10:25 -0400, Sean Paul wrote:
> On Tue, Sep 03, 2019 at 04:46:04PM -0400, Lyude Paul wrote:
> > Currently we only print mstb/port pointer addresses in our malloc and
> > topology refcount functions using the hashed-by-default %p, but
> > unfortunately if you're trying to debug a use-after-free error caused by
> > a refcounting error then this really isn't terribly useful. On the other
> > hand though, everything in the rest of the DP MST helpers uses hashed
> > pointer values as well and probably isn't useful to convert to unhashed.
> > So, let's just get the best of both worlds and print both the hashed and
> > unhashed pointer in our malloc/topology refcount debugging output. This
> > will hopefully make it a lot easier to figure out which port/mstb is
> > causing KASAN to get upset.
> > 
> > Cc: Juston Li <juston.li@intel.com>
> > Cc: Imre Deak <imre.deak@intel.com>
> > Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > Cc: Harry Wentland <hwentlan@amd.com>
> > Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> > Signed-off-by: Lyude Paul <lyude@redhat.com>
> 
> It's really too bad there isn't a CONFIG_DEBUG_SHOW_PK_ADDRESSES or even a
> value
> of kptr_restrict value that bypasses pointer hashing. I'm sure we're not the
> only ones to feel this pain. Maybe everyone just hacks vsnprintf...
> 
> As it is, I'm not totally sold on exposing the actual addresses
> unconditionally.
> What do you think about pulling the print out into a function and only
> printing
> px if a debug kconfig is set?
> 
> Sean
> 
> > ---
> >  drivers/gpu/drm/drm_dp_mst_topology.c | 34 ++++++++++++++++-----------
> >  1 file changed, 20 insertions(+), 14 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c
> > b/drivers/gpu/drm/drm_dp_mst_topology.c
> > index 2fe24e366925..5b5c0b3b3c0e 100644
> > --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> > +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> > @@ -1327,7 +1327,8 @@ static void
> >  drm_dp_mst_get_mstb_malloc(struct drm_dp_mst_branch *mstb)
> >  {
> >  	kref_get(&mstb->malloc_kref);
> > -	DRM_DEBUG("mstb %p (%d)\n", mstb, kref_read(&mstb->malloc_kref));
> > +	DRM_DEBUG("mstb %p/%px (%d)\n",
> > +		  mstb, mstb, kref_read(&mstb->malloc_kref));
> >  }
> >  
> >  /**
> > @@ -1344,7 +1345,8 @@ drm_dp_mst_get_mstb_malloc(struct drm_dp_mst_branch
> > *mstb)
> >  static void
> >  drm_dp_mst_put_mstb_malloc(struct drm_dp_mst_branch *mstb)
> >  {
> > -	DRM_DEBUG("mstb %p (%d)\n", mstb, kref_read(&mstb->malloc_kref) - 1);
> > +	DRM_DEBUG("mstb %p/%px (%d)\n",
> > +		  mstb, mstb, kref_read(&mstb->malloc_kref) - 1);
> >  	kref_put(&mstb->malloc_kref, drm_dp_free_mst_branch_device);
> >  }
> >  
> > @@ -1379,7 +1381,8 @@ void
> >  drm_dp_mst_get_port_malloc(struct drm_dp_mst_port *port)
> >  {
> >  	kref_get(&port->malloc_kref);
> > -	DRM_DEBUG("port %p (%d)\n", port, kref_read(&port->malloc_kref));
> > +	DRM_DEBUG("port %p/%px (%d)\n",
> > +		  port, port, kref_read(&port->malloc_kref));
> >  }
> >  EXPORT_SYMBOL(drm_dp_mst_get_port_malloc);
> >  
> > @@ -1396,7 +1399,8 @@ EXPORT_SYMBOL(drm_dp_mst_get_port_malloc);
> >  void
> >  drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port)
> >  {
> > -	DRM_DEBUG("port %p (%d)\n", port, kref_read(&port->malloc_kref) - 1);
> > +	DRM_DEBUG("port %p/%px (%d)\n",
> > +		  port, port, kref_read(&port->malloc_kref) - 1);
> >  	kref_put(&port->malloc_kref, drm_dp_free_mst_port);
> >  }
> >  EXPORT_SYMBOL(drm_dp_mst_put_port_malloc);
> > @@ -1447,8 +1451,8 @@ drm_dp_mst_topology_try_get_mstb(struct
> > drm_dp_mst_branch *mstb)
> >  	int ret = kref_get_unless_zero(&mstb->topology_kref);
> >  
> >  	if (ret)
> > -		DRM_DEBUG("mstb %p (%d)\n", mstb,
> > -			  kref_read(&mstb->topology_kref));
> > +		DRM_DEBUG("mstb %p/%px (%d)\n",
> > +			  mstb, mstb, kref_read(&mstb->topology_kref));
> >  
> >  	return ret;
> >  }
> > @@ -1471,7 +1475,8 @@ static void drm_dp_mst_topology_get_mstb(struct
> > drm_dp_mst_branch *mstb)
> >  {
> >  	WARN_ON(kref_read(&mstb->topology_kref) == 0);
> >  	kref_get(&mstb->topology_kref);
> > -	DRM_DEBUG("mstb %p (%d)\n", mstb, kref_read(&mstb->topology_kref));
> > +	DRM_DEBUG("mstb %p/%px (%d)\n",
> > +		  mstb, mstb, kref_read(&mstb->topology_kref));
> >  }
> >  
> >  /**
> > @@ -1489,8 +1494,8 @@ static void drm_dp_mst_topology_get_mstb(struct
> > drm_dp_mst_branch *mstb)
> >  static void
> >  drm_dp_mst_topology_put_mstb(struct drm_dp_mst_branch *mstb)
> >  {
> > -	DRM_DEBUG("mstb %p (%d)\n",
> > -		  mstb, kref_read(&mstb->topology_kref) - 1);
> > +	DRM_DEBUG("mstb %p/%px (%d)\n",
> > +		  mstb, mstb, kref_read(&mstb->topology_kref) - 1);
> >  	kref_put(&mstb->topology_kref, drm_dp_destroy_mst_branch_device);
> >  }
> >  
> > @@ -1546,8 +1551,8 @@ drm_dp_mst_topology_try_get_port(struct
> > drm_dp_mst_port *port)
> >  	int ret = kref_get_unless_zero(&port->topology_kref);
> >  
> >  	if (ret)
> > -		DRM_DEBUG("port %p (%d)\n", port,
> > -			  kref_read(&port->topology_kref));
> > +		DRM_DEBUG("port %p/%px (%d)\n",
> > +			  port, port, kref_read(&port->topology_kref));
> >  
> >  	return ret;
> >  }
> > @@ -1569,7 +1574,8 @@ static void drm_dp_mst_topology_get_port(struct
> > drm_dp_mst_port *port)
> >  {
> >  	WARN_ON(kref_read(&port->topology_kref) == 0);
> >  	kref_get(&port->topology_kref);
> > -	DRM_DEBUG("port %p (%d)\n", port, kref_read(&port->topology_kref));
> > +	DRM_DEBUG("port %p/%px (%d)\n",
> > +		  port, port, kref_read(&port->topology_kref));
> >  }
> >  
> >  /**
> > @@ -1585,8 +1591,8 @@ static void drm_dp_mst_topology_get_port(struct
> > drm_dp_mst_port *port)
> >   */
> >  static void drm_dp_mst_topology_put_port(struct drm_dp_mst_port *port)
> >  {
> > -	DRM_DEBUG("port %p (%d)\n",
> > -		  port, kref_read(&port->topology_kref) - 1);
> > +	DRM_DEBUG("port %p/%px (%d)\n",
> > +		  port, port, kref_read(&port->topology_kref) - 1);
> >  	kref_put(&port->topology_kref, drm_dp_destroy_port);
> >  }
> >  
> > -- 
> > 2.21.0
> > 
-- 
Cheers,
	Lyude Paul


^ permalink raw reply	[flat|nested] 62+ messages in thread

end of thread, other threads:[~2019-10-09 19:40 UTC | newest]

Thread overview: 62+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20190903204645.25487-1-lyude@redhat.com>
2019-09-03 20:45 ` [PATCH v2 01/27] drm/dp_mst: Move link address dumping into a function Lyude Paul
2019-09-25 17:45   ` Sean Paul
2019-09-03 20:45 ` [PATCH v2 02/27] drm/dp_mst: Get rid of list clear in destroy_connector_work Lyude Paul
2019-09-25 17:45   ` Sean Paul
2019-09-03 20:45 ` [PATCH v2 03/27] drm/dp_mst: Destroy MSTBs asynchronously Lyude Paul
2019-09-25 18:16   ` Sean Paul
2019-09-25 20:08     ` Lyude Paul
2019-09-27 13:31       ` Sean Paul
2019-10-08  9:45         ` Daniel Vetter
2019-09-03 20:45 ` [PATCH v2 04/27] drm/dp_mst: Move test_calc_pbn_mode() into an actual selftest Lyude Paul
2019-09-25 18:17   ` Sean Paul
2019-09-03 20:45 ` [PATCH v2 05/27] drm/print: Add drm_err_printer() Lyude Paul
2019-09-03 20:45 ` [PATCH v2 06/27] drm/dp_mst: Combine redundant cases in drm_dp_encode_sideband_req() Lyude Paul
2019-09-03 21:35   ` Dave Airlie
2019-09-03 21:57   ` [PATCH v3] " Lyude Paul
2019-09-03 20:45 ` [PATCH v2 07/27] drm/dp_mst: Add sideband down request tracing + selftests Lyude Paul
2019-09-10  9:01   ` Jani Nikula
2019-09-03 20:45 ` [PATCH v2 08/27] drm/dp_mst: Remove PDT teardown in drm_dp_destroy_port() and refactor Lyude Paul
2019-09-25 19:00   ` Sean Paul
2019-09-03 20:45 ` [PATCH v2 09/27] drm/dp_mst: Refactor drm_dp_send_enum_path_resources Lyude Paul
2019-09-03 20:45 ` [PATCH v2 10/27] drm/dp_mst: Remove huge conditional in drm_dp_mst_handle_up_req() Lyude Paul
2019-09-03 20:45 ` [PATCH v2 11/27] drm/dp_mst: Constify guid in drm_dp_get_mst_branch_by_guid() Lyude Paul
2019-09-03 21:41   ` Dave Airlie
2019-09-03 20:45 ` [PATCH v2 12/27] drm/dp_mst: Refactor drm_dp_mst_handle_up_req() Lyude Paul
2019-09-03 20:45 ` [PATCH v2 13/27] drm/dp_mst: Refactor drm_dp_mst_handle_down_rep() Lyude Paul
2019-09-03 20:45 ` [PATCH v2 14/27] drm/dp_mst: Destroy topology_mgr mutexes Lyude Paul
2019-09-25 19:14   ` Sean Paul
2019-09-03 20:45 ` [PATCH v2 15/27] drm/dp_mst: Cleanup drm_dp_send_link_address() a bit Lyude Paul
2019-09-03 21:42   ` Dave Airlie
2019-09-03 20:45 ` [PATCH v2 16/27] drm/dp_mst: Refactor pdt setup/teardown, add more locking Lyude Paul
2019-09-25 19:27   ` Sean Paul
2019-09-25 21:00     ` Lyude Paul
2019-09-27 13:30       ` Sean Paul
2019-09-03 20:45 ` [PATCH v2 17/27] drm/dp_mst: Rename drm_dp_add_port and drm_dp_update_port Lyude Paul
2019-09-25 19:30   ` Sean Paul
2019-09-03 20:45 ` [PATCH v2 18/27] drm/dp_mst: Remove lies in {up,down}_rep_recv documentation Lyude Paul
2019-09-25 19:32   ` Sean Paul
2019-09-03 20:45 ` [PATCH v2 19/27] drm/dp_mst: Handle UP requests asynchronously Lyude Paul
2019-09-25 19:46   ` Sean Paul
2019-09-03 20:45 ` [PATCH v2 20/27] drm/dp_mst: Protect drm_dp_mst_port members with connection_mutex Lyude Paul
2019-09-25 20:00   ` Sean Paul
2019-09-25 21:01     ` Lyude Paul
2019-09-03 20:45 ` [PATCH v2 21/27] drm/dp_mst: Don't forget to update port->input in drm_dp_mst_handle_conn_stat() Lyude Paul
2019-09-25 20:03   ` Sean Paul
2019-09-03 20:46 ` [PATCH v2 22/27] drm/nouveau: Don't grab runtime PM refs for HPD IRQs Lyude Paul
2019-09-25 20:06   ` Sean Paul
2019-09-03 20:46 ` [PATCH v2 23/27] drm/amdgpu: Iterate through DRM connectors correctly Lyude Paul
2019-09-13 20:45   ` Alex Deucher
2019-09-27 13:48     ` Alex Deucher
2019-09-03 20:46 ` [PATCH v2 24/27] drm/amdgpu/dm: Resume short HPD IRQs before resuming MST topology Lyude Paul
2019-09-13 20:46   ` Alex Deucher
2019-09-25 20:08   ` Sean Paul
2019-09-25 21:52   ` [PATCH v4] " Lyude Paul
2019-09-27 13:29     ` Alex Deucher
2019-09-03 20:46 ` [PATCH v2 25/27] drm/dp_mst: Add basic topology reprobing when resuming Lyude Paul
2019-09-27 13:52   ` Sean Paul
2019-10-09 19:06     ` Lyude Paul
2019-09-03 20:46 ` [PATCH v2 26/27] drm/dp_mst: Also print unhashed pointers for malloc/topology references Lyude Paul
2019-09-27 14:25   ` Sean Paul
2019-10-09 19:40     ` Lyude Paul
2019-09-03 20:46 ` [PATCH v2 27/27] drm/dp_mst: Add topology ref history tracking for debugging Lyude Paul
2019-09-27 14:51   ` Sean Paul

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).