All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] drm/connector: Cache out-of-band hotplug events
@ 2026-08-21 14:55 ` Sebastian Reichel
  0 siblings, 0 replies; 9+ messages in thread
From: Sebastian Reichel @ 2026-08-21 14:55 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Heiko Stuebner, Dmitry Baryshkov
  Cc: Chaoyi Chen, Yongxing Mou, Igor Paunovic, Alexey Charkov,
	dri-devel, linux-kernel, linux-arm-kernel, linux-rockchip, kernel,
	Sebastian Reichel

When the USB-C state machine finished negotiating DP AltMode before the
DRM device has been probed, the out-of-band hotplug events fired to
early and are lost. Without replugging the display or reloading the
USB-C driver, the DRM driver assumes nothing is plugged.

Reproducing this race condition at boot time depends on kernel
configuration and exact USB-C equipment due to timing, but it can easily
be reproduced by reloading the DRM driver consuming the out-of-band
hotplug events without reloading the USB-C driver.

Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
This has been tested together with the patch series adding USB-C DP
AltMode support for Rockchip RK3588/RK3576. This is mostly independent
and should also affect other platforms, so I'm sending it separately.

I ran into this during development via the module reload path, but it
seems Heiko [0] and Igor [1] managed to hit the race condition with a
normal boot.

[0] https://lore.kernel.org/dri-devel/20767137.geO5KgaWL5@diego/
[1] https://lore.kernel.org/dri-devel/20260811211534.8618-1-royalnet026@gmail.com/
---
 drivers/gpu/drm/drm_connector.c     | 106 ++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_crtc_internal.h |   1 +
 drivers/gpu/drm/drm_drv.c           |   1 +
 3 files changed, 108 insertions(+)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 8b4baed060f3..b69e3776b153 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -33,6 +33,7 @@
 #include <drm/drm_sysfs.h>
 #include <drm/drm_utils.h>
 
+#include <linux/cleanup.h>
 #include <linux/export.h>
 #include <linux/platform_device.h>
 #include <linux/property.h>
@@ -81,6 +82,22 @@
 static DEFINE_MUTEX(connector_list_lock);
 static LIST_HEAD(connector_list);
 
+/*
+ * List of connector fwnodes with their last out-of-band hotplug status
+ * required to forward them to a connector on registration. This ensures
+ * the connector sees a HPD event, if the event arrived before the DRM
+ * driver was probed (either due to module reload, or because of bad
+ * timing during bootup).
+ */
+struct drm_oob_hotplug_state {
+	struct list_head head;
+	struct fwnode_handle *fwnode;
+	enum drm_connector_status status;
+};
+
+static DEFINE_MUTEX(oob_hotplug_list_lock);
+static LIST_HEAD(oob_hotplug_list);
+
 struct drm_conn_prop_enum_list {
 	int type;
 	const char *name;
@@ -130,6 +147,19 @@ void drm_connector_ida_destroy(void)
 		ida_destroy(&drm_connector_enum_list[i].ida);
 }
 
+void drm_connector_oob_hotplug_cleanup(void)
+{
+	struct drm_oob_hotplug_state *e, *tmp;
+
+	scoped_guard(mutex, &oob_hotplug_list_lock) {
+		list_for_each_entry_safe(e, tmp, &oob_hotplug_list, head) {
+			list_del(&e->head);
+			fwnode_handle_put(e->fwnode);
+			kfree(e);
+		}
+	}
+}
+
 /**
  * drm_get_connector_type_name - return a string for connector type
  * @type: The connector type (DRM_MODE_CONNECTOR_*)
@@ -829,6 +859,36 @@ void drm_connector_cleanup(struct drm_connector *connector)
 }
 EXPORT_SYMBOL(drm_connector_cleanup);
 
+/**
+ * drm_connector_replay_oob_hotplug_event - send cached OOB HPD event
+ * @connector: the connector that should receive the event
+ *
+ * Send the cached out-of-band hotplug as a new out-of-band hotplug event.
+ */
+static void drm_connector_replay_oob_hotplug_event(struct drm_connector *connector)
+{
+	struct fwnode_handle *fwnode = connector->fwnode;
+	enum drm_connector_status status;
+	struct drm_oob_hotplug_state *e;
+	bool found = false;
+
+	if (!fwnode || !connector->funcs->oob_hotplug_event)
+		return;
+
+	scoped_guard(mutex, &oob_hotplug_list_lock) {
+		list_for_each_entry(e, &oob_hotplug_list, head) {
+			if (e->fwnode == fwnode || fwnode->secondary == e->fwnode) {
+				status = e->status;
+				found = true;
+				break;
+			}
+		}
+	}
+
+	if (found)
+		connector->funcs->oob_hotplug_event(connector, status);
+}
+
 /**
  * drm_connector_register - register a connector
  * @connector: the connector to register
@@ -849,6 +909,7 @@ EXPORT_SYMBOL(drm_connector_cleanup);
  */
 int drm_connector_register(struct drm_connector *connector)
 {
+	bool replay_oob_hotplug = false;
 	int ret = 0;
 
 	if (!connector->dev->registered)
@@ -888,6 +949,7 @@ int drm_connector_register(struct drm_connector *connector)
 	mutex_lock(&connector_list_lock);
 	list_add_tail(&connector->global_connector_list_entry, &connector_list);
 	mutex_unlock(&connector_list_lock);
+	replay_oob_hotplug = true;
 	goto unlock;
 
 err_late_register:
@@ -898,6 +960,10 @@ int drm_connector_register(struct drm_connector *connector)
 	drm_sysfs_connector_remove(connector);
 unlock:
 	mutex_unlock(&connector->mutex);
+
+	if (replay_oob_hotplug)
+		drm_connector_replay_oob_hotplug_event(connector);
+
 	return ret;
 }
 EXPORT_SYMBOL(drm_connector_register);
@@ -3671,6 +3737,41 @@ struct drm_connector *drm_connector_find_by_fwnode(struct fwnode_handle *fwnode)
 	return found;
 }
 
+/**
+ * drm_connector_record_oob_hotplug_status - Cache OOB hotplug status
+ * @fwnode - fwnode for the DRM connector
+ * @status - out-of-band status info
+ *
+ * Cache the latest out-of-band hotplug status for a fwnode so it can be
+ * (re)played from when the DRM device is (re)registered after this event
+ * arrived.
+ */
+static void drm_connector_record_oob_hotplug_status(struct fwnode_handle *fwnode,
+						    enum drm_connector_status status)
+{
+	struct drm_oob_hotplug_state *e;
+
+	if (!fwnode)
+		return;
+
+	guard(mutex)(&oob_hotplug_list_lock);
+
+	list_for_each_entry(e, &oob_hotplug_list, head) {
+		if (e->fwnode == fwnode) {
+			e->status = status;
+			return;
+		}
+	}
+
+	e = kzalloc(sizeof(*e), GFP_KERNEL);
+	if (!e)
+		return;
+
+	e->fwnode = fwnode_handle_get(fwnode);
+	e->status = status;
+	list_add_tail(&e->head, &oob_hotplug_list);
+}
+
 /**
  * drm_connector_oob_hotplug_event - Report out-of-band hotplug event to connector
  * @connector_fwnode: fwnode_handle to report the event on
@@ -3683,12 +3784,17 @@ struct drm_connector *drm_connector_find_by_fwnode(struct fwnode_handle *fwnode)
  *
  * This function can be used to report these out-of-band events after obtaining
  * a drm_connector reference through calling drm_connector_find_by_fwnode().
+ *
+ * The last status for each fwnode is cached and replayed when a matching DRM
+ * connector device is (re)registered.
  */
 void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode,
 				     enum drm_connector_status status)
 {
 	struct drm_connector *connector;
 
+	drm_connector_record_oob_hotplug_status(connector_fwnode, status);
+
 	connector = drm_connector_find_by_fwnode(connector_fwnode);
 	if (IS_ERR(connector))
 		return;
diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h
index 83146ffef00c..c2714ea256a7 100644
--- a/drivers/gpu/drm/drm_crtc_internal.h
+++ b/drivers/gpu/drm/drm_crtc_internal.h
@@ -188,6 +188,7 @@ int drm_mode_getencoder(struct drm_device *dev,
 /* drm_connector.c */
 void drm_connector_ida_init(void);
 void drm_connector_ida_destroy(void);
+void drm_connector_oob_hotplug_cleanup(void);
 void drm_connector_unregister_all(struct drm_device *dev);
 int drm_connector_register_all(struct drm_device *dev);
 int drm_connector_set_obj_prop(struct drm_mode_object *obj,
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 3c570f9393b9..c1aeab297ff9 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -1239,6 +1239,7 @@ static void drm_core_exit(void)
 	drm_sysfs_destroy();
 	WARN_ON(!xa_empty(&drm_minors_xa));
 	drm_connector_ida_destroy();
+	drm_connector_oob_hotplug_cleanup();
 }
 
 static int __init drm_core_init(void)

---
base-commit: d3d1e0c4343385fb343a552e4f3b6b97d5762fc2
change-id: 20260821-drm-connector-oob-hotplug-cache-13386679f852

Best regards,
--  
Sebastian Reichel <sebastian.reichel@collabora.com>


_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

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

* [PATCH] drm/connector: Cache out-of-band hotplug events
@ 2026-08-21 14:55 ` Sebastian Reichel
  0 siblings, 0 replies; 9+ messages in thread
From: Sebastian Reichel @ 2026-08-21 14:55 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Heiko Stuebner, Dmitry Baryshkov
  Cc: Chaoyi Chen, Yongxing Mou, Igor Paunovic, Alexey Charkov,
	dri-devel, linux-kernel, linux-arm-kernel, linux-rockchip, kernel,
	Sebastian Reichel

When the USB-C state machine finished negotiating DP AltMode before the
DRM device has been probed, the out-of-band hotplug events fired to
early and are lost. Without replugging the display or reloading the
USB-C driver, the DRM driver assumes nothing is plugged.

Reproducing this race condition at boot time depends on kernel
configuration and exact USB-C equipment due to timing, but it can easily
be reproduced by reloading the DRM driver consuming the out-of-band
hotplug events without reloading the USB-C driver.

Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
This has been tested together with the patch series adding USB-C DP
AltMode support for Rockchip RK3588/RK3576. This is mostly independent
and should also affect other platforms, so I'm sending it separately.

I ran into this during development via the module reload path, but it
seems Heiko [0] and Igor [1] managed to hit the race condition with a
normal boot.

[0] https://lore.kernel.org/dri-devel/20767137.geO5KgaWL5@diego/
[1] https://lore.kernel.org/dri-devel/20260811211534.8618-1-royalnet026@gmail.com/
---
 drivers/gpu/drm/drm_connector.c     | 106 ++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_crtc_internal.h |   1 +
 drivers/gpu/drm/drm_drv.c           |   1 +
 3 files changed, 108 insertions(+)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 8b4baed060f3..b69e3776b153 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -33,6 +33,7 @@
 #include <drm/drm_sysfs.h>
 #include <drm/drm_utils.h>
 
+#include <linux/cleanup.h>
 #include <linux/export.h>
 #include <linux/platform_device.h>
 #include <linux/property.h>
@@ -81,6 +82,22 @@
 static DEFINE_MUTEX(connector_list_lock);
 static LIST_HEAD(connector_list);
 
+/*
+ * List of connector fwnodes with their last out-of-band hotplug status
+ * required to forward them to a connector on registration. This ensures
+ * the connector sees a HPD event, if the event arrived before the DRM
+ * driver was probed (either due to module reload, or because of bad
+ * timing during bootup).
+ */
+struct drm_oob_hotplug_state {
+	struct list_head head;
+	struct fwnode_handle *fwnode;
+	enum drm_connector_status status;
+};
+
+static DEFINE_MUTEX(oob_hotplug_list_lock);
+static LIST_HEAD(oob_hotplug_list);
+
 struct drm_conn_prop_enum_list {
 	int type;
 	const char *name;
@@ -130,6 +147,19 @@ void drm_connector_ida_destroy(void)
 		ida_destroy(&drm_connector_enum_list[i].ida);
 }
 
+void drm_connector_oob_hotplug_cleanup(void)
+{
+	struct drm_oob_hotplug_state *e, *tmp;
+
+	scoped_guard(mutex, &oob_hotplug_list_lock) {
+		list_for_each_entry_safe(e, tmp, &oob_hotplug_list, head) {
+			list_del(&e->head);
+			fwnode_handle_put(e->fwnode);
+			kfree(e);
+		}
+	}
+}
+
 /**
  * drm_get_connector_type_name - return a string for connector type
  * @type: The connector type (DRM_MODE_CONNECTOR_*)
@@ -829,6 +859,36 @@ void drm_connector_cleanup(struct drm_connector *connector)
 }
 EXPORT_SYMBOL(drm_connector_cleanup);
 
+/**
+ * drm_connector_replay_oob_hotplug_event - send cached OOB HPD event
+ * @connector: the connector that should receive the event
+ *
+ * Send the cached out-of-band hotplug as a new out-of-band hotplug event.
+ */
+static void drm_connector_replay_oob_hotplug_event(struct drm_connector *connector)
+{
+	struct fwnode_handle *fwnode = connector->fwnode;
+	enum drm_connector_status status;
+	struct drm_oob_hotplug_state *e;
+	bool found = false;
+
+	if (!fwnode || !connector->funcs->oob_hotplug_event)
+		return;
+
+	scoped_guard(mutex, &oob_hotplug_list_lock) {
+		list_for_each_entry(e, &oob_hotplug_list, head) {
+			if (e->fwnode == fwnode || fwnode->secondary == e->fwnode) {
+				status = e->status;
+				found = true;
+				break;
+			}
+		}
+	}
+
+	if (found)
+		connector->funcs->oob_hotplug_event(connector, status);
+}
+
 /**
  * drm_connector_register - register a connector
  * @connector: the connector to register
@@ -849,6 +909,7 @@ EXPORT_SYMBOL(drm_connector_cleanup);
  */
 int drm_connector_register(struct drm_connector *connector)
 {
+	bool replay_oob_hotplug = false;
 	int ret = 0;
 
 	if (!connector->dev->registered)
@@ -888,6 +949,7 @@ int drm_connector_register(struct drm_connector *connector)
 	mutex_lock(&connector_list_lock);
 	list_add_tail(&connector->global_connector_list_entry, &connector_list);
 	mutex_unlock(&connector_list_lock);
+	replay_oob_hotplug = true;
 	goto unlock;
 
 err_late_register:
@@ -898,6 +960,10 @@ int drm_connector_register(struct drm_connector *connector)
 	drm_sysfs_connector_remove(connector);
 unlock:
 	mutex_unlock(&connector->mutex);
+
+	if (replay_oob_hotplug)
+		drm_connector_replay_oob_hotplug_event(connector);
+
 	return ret;
 }
 EXPORT_SYMBOL(drm_connector_register);
@@ -3671,6 +3737,41 @@ struct drm_connector *drm_connector_find_by_fwnode(struct fwnode_handle *fwnode)
 	return found;
 }
 
+/**
+ * drm_connector_record_oob_hotplug_status - Cache OOB hotplug status
+ * @fwnode - fwnode for the DRM connector
+ * @status - out-of-band status info
+ *
+ * Cache the latest out-of-band hotplug status for a fwnode so it can be
+ * (re)played from when the DRM device is (re)registered after this event
+ * arrived.
+ */
+static void drm_connector_record_oob_hotplug_status(struct fwnode_handle *fwnode,
+						    enum drm_connector_status status)
+{
+	struct drm_oob_hotplug_state *e;
+
+	if (!fwnode)
+		return;
+
+	guard(mutex)(&oob_hotplug_list_lock);
+
+	list_for_each_entry(e, &oob_hotplug_list, head) {
+		if (e->fwnode == fwnode) {
+			e->status = status;
+			return;
+		}
+	}
+
+	e = kzalloc(sizeof(*e), GFP_KERNEL);
+	if (!e)
+		return;
+
+	e->fwnode = fwnode_handle_get(fwnode);
+	e->status = status;
+	list_add_tail(&e->head, &oob_hotplug_list);
+}
+
 /**
  * drm_connector_oob_hotplug_event - Report out-of-band hotplug event to connector
  * @connector_fwnode: fwnode_handle to report the event on
@@ -3683,12 +3784,17 @@ struct drm_connector *drm_connector_find_by_fwnode(struct fwnode_handle *fwnode)
  *
  * This function can be used to report these out-of-band events after obtaining
  * a drm_connector reference through calling drm_connector_find_by_fwnode().
+ *
+ * The last status for each fwnode is cached and replayed when a matching DRM
+ * connector device is (re)registered.
  */
 void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode,
 				     enum drm_connector_status status)
 {
 	struct drm_connector *connector;
 
+	drm_connector_record_oob_hotplug_status(connector_fwnode, status);
+
 	connector = drm_connector_find_by_fwnode(connector_fwnode);
 	if (IS_ERR(connector))
 		return;
diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h
index 83146ffef00c..c2714ea256a7 100644
--- a/drivers/gpu/drm/drm_crtc_internal.h
+++ b/drivers/gpu/drm/drm_crtc_internal.h
@@ -188,6 +188,7 @@ int drm_mode_getencoder(struct drm_device *dev,
 /* drm_connector.c */
 void drm_connector_ida_init(void);
 void drm_connector_ida_destroy(void);
+void drm_connector_oob_hotplug_cleanup(void);
 void drm_connector_unregister_all(struct drm_device *dev);
 int drm_connector_register_all(struct drm_device *dev);
 int drm_connector_set_obj_prop(struct drm_mode_object *obj,
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 3c570f9393b9..c1aeab297ff9 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -1239,6 +1239,7 @@ static void drm_core_exit(void)
 	drm_sysfs_destroy();
 	WARN_ON(!xa_empty(&drm_minors_xa));
 	drm_connector_ida_destroy();
+	drm_connector_oob_hotplug_cleanup();
 }
 
 static int __init drm_core_init(void)

---
base-commit: d3d1e0c4343385fb343a552e4f3b6b97d5762fc2
change-id: 20260821-drm-connector-oob-hotplug-cache-13386679f852

Best regards,
--  
Sebastian Reichel <sebastian.reichel@collabora.com>


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

* Re: [PATCH] drm/connector: Cache out-of-band hotplug events
  2026-08-21 14:55 ` Sebastian Reichel
  (?)
@ 2026-08-21 15:06 ` sashiko-bot
  -1 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-08-21 15:06 UTC (permalink / raw)
  To: Sebastian Reichel; +Cc: dri-devel

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Out-of-order delivery of OOB hotplug events due to a Time-of-Check to Time-of-Use (TOCTOU) race condition during connector registration.
- [Medium] Permanent memory leak of fwnode references and drm_oob_hotplug_state structs for dynamically generated fwnodes.
--

--- Patch [1]: [PATCH] drm/connector: Cache out-of-band hotplug events ---
commit 4d3add94a455defa7c034e56e4bb755bf2de93af
Author: Sebastian Reichel <sebastian.reichel@collabora.com>

drm/connector: Cache out-of-band hotplug events

When the USB-C state machine finished negotiating DP AltMode before the
DRM device has been probed, the out-of-band hotplug events fired to
early and are lost. Without replugging the display or reloading the
USB-C driver, the DRM driver assumes nothing is plugged.

Link: https://lore.kernel.org/dri-devel/20767137.geO5KgaWL5@diego/
Link: https://lore.kernel.org/dri-devel/20260811211534.8618-1-royalnet026@gmail.com/

> diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
> index 8b4baed060f3a..b69e3776b1539 100644
> --- a/drivers/gpu/drm/drm_connector.c
> +++ b/drivers/gpu/drm/drm_connector.c

[ ... ]

> @@ -829,6 +859,36 @@ void drm_connector_cleanup(struct drm_connector *connector)
>  }
>  EXPORT_SYMBOL(drm_connector_cleanup);
>  
> +/**
> + * drm_connector_replay_oob_hotplug_event - send cached OOB HPD event
> + * @connector: the connector that should receive the event
> + *
> + * Send the cached out-of-band hotplug as a new out-of-band hotplug event.
> + */
> +static void drm_connector_replay_oob_hotplug_event(struct drm_connector *connector)
> +{
> +	struct fwnode_handle *fwnode = connector->fwnode;
> +	enum drm_connector_status status;
> +	struct drm_oob_hotplug_state *e;
> +	bool found = false;
> +
> +	if (!fwnode || !connector->funcs->oob_hotplug_event)
> +		return;
> +
> +	scoped_guard(mutex, &oob_hotplug_list_lock) {
> +		list_for_each_entry(e, &oob_hotplug_list, head) {
> +			if (e->fwnode == fwnode || fwnode->secondary == e->fwnode) {
> +				status = e->status;
> +				found = true;
> +				break;
> +			}
> +		}
> +	}
> +
> +	if (found)
> +		connector->funcs->oob_hotplug_event(connector, status);

[Severity: High]
Does this create a race condition that could result in out-of-order event
delivery?

When this function drops the oob_hotplug_list_lock after reading the state,
a concurrent hardware hotplug event could be processed by
drm_connector_oob_hotplug_event() in another thread.

If that thread updates the cache and fires the connector callback with a
new connected status, would this function then execute its callback with
the older, stale cached status, permanently desynchronizing the state?

> +}
> +

[ ... ]

> @@ -3671,6 +3737,41 @@ struct drm_connector *drm_connector_find_by_fwnode(struct fwnode_handle *fwnode)
>  	return found;
>  }
>  
> +/**
> + * drm_connector_record_oob_hotplug_status - Cache OOB hotplug status
> + * @fwnode - fwnode for the DRM connector
> + * @status - out-of-band status info
> + *
> + * Cache the latest out-of-band hotplug status for a fwnode so it can be
> + * (re)played from when the DRM device is (re)registered after this event
> + * arrived.
> + */
> +static void drm_connector_record_oob_hotplug_status(struct fwnode_handle *fwnode,
> +						    enum drm_connector_status status)
> +{
> +	struct drm_oob_hotplug_state *e;
> +
> +	if (!fwnode)
> +		return;
> +
> +	guard(mutex)(&oob_hotplug_list_lock);
> +
> +	list_for_each_entry(e, &oob_hotplug_list, head) {
> +		if (e->fwnode == fwnode) {
> +			e->status = status;
> +			return;
> +		}
> +	}
> +
> +	e = kzalloc(sizeof(*e), GFP_KERNEL);
> +	if (!e)
> +		return;
> +
> +	e->fwnode = fwnode_handle_get(fwnode);
> +	e->status = status;
> +	list_add_tail(&e->head, &oob_hotplug_list);

[Severity: Medium]
Will this leak memory and fwnode references for dynamically generated fwnodes?

It appears that a new state object is allocated and appended to the global
oob_hotplug_list, and a reference to the fwnode is taken. However, there
does not seem to be any eviction mechanism to remove these items when a
connector is destroyed.

If dynamic fwnodes are continually added and removed, could this result in
unbounded growth of the list and eventual resource exhaustion?

> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260821-drm-connector-oob-hotplug-cache-v1-1-e92e146fdc7b@collabora.com?part=1

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

* Re: [PATCH] drm/connector: Cache out-of-band hotplug events
  2026-08-21 14:55 ` Sebastian Reichel
@ 2026-08-24 20:33   ` kernel test robot
  -1 siblings, 0 replies; 9+ messages in thread
From: kernel test robot @ 2026-08-24 20:33 UTC (permalink / raw)
  To: Sebastian Reichel, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Heiko Stuebner,
	Dmitry Baryshkov
  Cc: oe-kbuild-all, Chaoyi Chen, Yongxing Mou, Igor Paunovic,
	Alexey Charkov, dri-devel, linux-kernel, linux-arm-kernel,
	linux-rockchip, kernel, Sebastian Reichel

Hi Sebastian,

kernel test robot noticed the following build warnings:

[auto build test WARNING on d3d1e0c4343385fb343a552e4f3b6b97d5762fc2]

url:    https://github.com/intel-lab-lkp/linux/commits/Sebastian-Reichel/drm-connector-Cache-out-of-band-hotplug-events/20260821-165525
base:   d3d1e0c4343385fb343a552e4f3b6b97d5762fc2
patch link:    https://lore.kernel.org/r/20260821-drm-connector-oob-hotplug-cache-v1-1-e92e146fdc7b%40collabora.com
patch subject: [PATCH] drm/connector: Cache out-of-band hotplug events
config: parisc-defconfig (https://download.01.org/0day-ci/archive/20260825/202608250407.a5fV5DFE-lkp@intel.com/config)
compiler: hppa-linux-gcc (GCC) 16.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260825/202608250407.a5fV5DFE-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202608250407.a5fV5DFE-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> Warning: drivers/gpu/drm/drm_connector.c:3750 function parameter 'fwnode' not described in 'drm_connector_record_oob_hotplug_status'
>> Warning: drivers/gpu/drm/drm_connector.c:3750 function parameter 'status' not described in 'drm_connector_record_oob_hotplug_status'
>> Warning: drivers/gpu/drm/drm_connector.c:3750 function parameter 'fwnode' not described in 'drm_connector_record_oob_hotplug_status'
>> Warning: drivers/gpu/drm/drm_connector.c:3750 function parameter 'status' not described in 'drm_connector_record_oob_hotplug_status'

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* Re: [PATCH] drm/connector: Cache out-of-band hotplug events
@ 2026-08-24 20:33   ` kernel test robot
  0 siblings, 0 replies; 9+ messages in thread
From: kernel test robot @ 2026-08-24 20:33 UTC (permalink / raw)
  To: Sebastian Reichel, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Heiko Stuebner,
	Dmitry Baryshkov
  Cc: oe-kbuild-all, Chaoyi Chen, Yongxing Mou, Igor Paunovic,
	Alexey Charkov, dri-devel, linux-kernel, linux-arm-kernel,
	linux-rockchip, kernel, Sebastian Reichel

Hi Sebastian,

kernel test robot noticed the following build warnings:

[auto build test WARNING on d3d1e0c4343385fb343a552e4f3b6b97d5762fc2]

url:    https://github.com/intel-lab-lkp/linux/commits/Sebastian-Reichel/drm-connector-Cache-out-of-band-hotplug-events/20260821-165525
base:   d3d1e0c4343385fb343a552e4f3b6b97d5762fc2
patch link:    https://lore.kernel.org/r/20260821-drm-connector-oob-hotplug-cache-v1-1-e92e146fdc7b%40collabora.com
patch subject: [PATCH] drm/connector: Cache out-of-band hotplug events
config: parisc-defconfig (https://download.01.org/0day-ci/archive/20260825/202608250407.a5fV5DFE-lkp@intel.com/config)
compiler: hppa-linux-gcc (GCC) 16.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260825/202608250407.a5fV5DFE-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202608250407.a5fV5DFE-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> Warning: drivers/gpu/drm/drm_connector.c:3750 function parameter 'fwnode' not described in 'drm_connector_record_oob_hotplug_status'
>> Warning: drivers/gpu/drm/drm_connector.c:3750 function parameter 'status' not described in 'drm_connector_record_oob_hotplug_status'
>> Warning: drivers/gpu/drm/drm_connector.c:3750 function parameter 'fwnode' not described in 'drm_connector_record_oob_hotplug_status'
>> Warning: drivers/gpu/drm/drm_connector.c:3750 function parameter 'status' not described in 'drm_connector_record_oob_hotplug_status'

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

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

* Re: [PATCH] drm/connector: Cache out-of-band hotplug events
  2026-08-21 14:55 ` Sebastian Reichel
@ 2026-09-10 16:25   ` Igor Paunovic
  -1 siblings, 0 replies; 9+ messages in thread
From: Igor Paunovic @ 2026-09-10 16:25 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: Igor Paunovic, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Heiko Stuebner,
	Dmitry Baryshkov, Chaoyi Chen, Yongxing Mou, Alexey Charkov,
	dri-devel, linux-kernel, linux-arm-kernel, linux-rockchip, kernel

Hi Sebastian,

On 8/21/26 16:55, Sebastian Reichel wrote:
> When the USB-C state machine finished negotiating DP AltMode before the
> DRM device has been probed, the out-of-band hotplug events fired to
> early and are lost. Without replugging the display or reloading the
> USB-C driver, the DRM driver assumes nothing is plugged.

This is the boot race I reported in [1], and the patch fixes it here.

Orange Pi 5 Plus (RK3588), a Samsung monitor on USB-C DisplayPort Alt
Mode behind fusb302, on drm-misc-next plus the dw-dp and Rockchip USBDP
PHY series.  The same tree also carries my pending vop2 AXI clock patch
and the pending dw-hdmi-qp audio guard; without those two the board
does not survive the boot for reasons unrelated to this patch.

Without the patch (one boot on that tree, two more on my daily kernel)
the DP connector reads "disconnected" from the moment dw-dp binds until
a boot-time service of mine unbinds and rebinds fusb302 some 45-70 s
later; only then does the link come up.

With the patch the display comes up on its own: the same service,
running 40 s after dw-dp bound, finds the connector already "connected"
and does nothing.  No warnings over the session.

Tested-by: Igor Paunovic <royalnet026@gmail.com> # Orange Pi 5 Plus, USB-C DP Alt Mode

[1] https://lore.kernel.org/all/20260811211534.8618-1-royalnet026@gmail.com/

Thanks,
Igor


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

* Re: [PATCH] drm/connector: Cache out-of-band hotplug events
@ 2026-09-10 16:25   ` Igor Paunovic
  0 siblings, 0 replies; 9+ messages in thread
From: Igor Paunovic @ 2026-09-10 16:25 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: Igor Paunovic, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Heiko Stuebner,
	Dmitry Baryshkov, Chaoyi Chen, Yongxing Mou, Alexey Charkov,
	dri-devel, linux-kernel, linux-arm-kernel, linux-rockchip, kernel

Hi Sebastian,

On 8/21/26 16:55, Sebastian Reichel wrote:
> When the USB-C state machine finished negotiating DP AltMode before the
> DRM device has been probed, the out-of-band hotplug events fired to
> early and are lost. Without replugging the display or reloading the
> USB-C driver, the DRM driver assumes nothing is plugged.

This is the boot race I reported in [1], and the patch fixes it here.

Orange Pi 5 Plus (RK3588), a Samsung monitor on USB-C DisplayPort Alt
Mode behind fusb302, on drm-misc-next plus the dw-dp and Rockchip USBDP
PHY series.  The same tree also carries my pending vop2 AXI clock patch
and the pending dw-hdmi-qp audio guard; without those two the board
does not survive the boot for reasons unrelated to this patch.

Without the patch (one boot on that tree, two more on my daily kernel)
the DP connector reads "disconnected" from the moment dw-dp binds until
a boot-time service of mine unbinds and rebinds fusb302 some 45-70 s
later; only then does the link come up.

With the patch the display comes up on its own: the same service,
running 40 s after dw-dp bound, finds the connector already "connected"
and does nothing.  No warnings over the session.

Tested-by: Igor Paunovic <royalnet026@gmail.com> # Orange Pi 5 Plus, USB-C DP Alt Mode

[1] https://lore.kernel.org/all/20260811211534.8618-1-royalnet026@gmail.com/

Thanks,
Igor

_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

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

* Re: [PATCH] drm/connector: Cache out-of-band hotplug events
  2026-08-21 14:55 ` Sebastian Reichel
@ 2026-09-11  1:53   ` Chaoyi Chen
  -1 siblings, 0 replies; 9+ messages in thread
From: Chaoyi Chen @ 2026-09-11  1:53 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: Yongxing Mou, Igor Paunovic, Alexey Charkov, dri-devel,
	linux-kernel, linux-arm-kernel, linux-rockchip, kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Heiko Stuebner, Dmitry Baryshkov

Hello Sebastian,

On 8/21/2026 10:55 PM, Sebastian Reichel wrote:
> When the USB-C state machine finished negotiating DP AltMode before the
> DRM device has been probed, the out-of-band hotplug events fired to
> early and are lost. Without replugging the display or reloading the
> USB-C driver, the DRM driver assumes nothing is plugged.
> 
> Reproducing this race condition at boot time depends on kernel
> configuration and exact USB-C equipment due to timing, but it can easily
> be reproduced by reloading the DRM driver consuming the out-of-band
> hotplug events without reloading the USB-C driver.
> 
> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
> ---
> This has been tested together with the patch series adding USB-C DP
> AltMode support for Rockchip RK3588/RK3576. This is mostly independent
> and should also affect other platforms, so I'm sending it separately.
> 
> I ran into this during development via the module reload path, but it
> seems Heiko [0] and Igor [1] managed to hit the race condition with a
> normal boot.
> 
> [0] https://lore.kernel.org/dri-devel/20767137.geO5KgaWL5@diego/
> [1] https://lore.kernel.org/dri-devel/20260811211534.8618-1-royalnet026@gmail.com/
> ---
>  drivers/gpu/drm/drm_connector.c     | 106 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/drm_crtc_internal.h |   1 +
>  drivers/gpu/drm/drm_drv.c           |   1 +
>  3 files changed, 108 insertions(+)
> 
> diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
> index 8b4baed060f3..b69e3776b153 100644
> --- a/drivers/gpu/drm/drm_connector.c
> +++ b/drivers/gpu/drm/drm_connector.c
> @@ -33,6 +33,7 @@
>  #include <drm/drm_sysfs.h>
>  #include <drm/drm_utils.h>
>  
> +#include <linux/cleanup.h>
>  #include <linux/export.h>
>  #include <linux/platform_device.h>
>  #include <linux/property.h>
> @@ -81,6 +82,22 @@
>  static DEFINE_MUTEX(connector_list_lock);
>  static LIST_HEAD(connector_list);
>  
> +/*
> + * List of connector fwnodes with their last out-of-band hotplug status
> + * required to forward them to a connector on registration. This ensures
> + * the connector sees a HPD event, if the event arrived before the DRM
> + * driver was probed (either due to module reload, or because of bad
> + * timing during bootup).
> + */
> +struct drm_oob_hotplug_state {
> +	struct list_head head;
> +	struct fwnode_handle *fwnode;
> +	enum drm_connector_status status;
> +};
> +
> +static DEFINE_MUTEX(oob_hotplug_list_lock);
> +static LIST_HEAD(oob_hotplug_list);
> +
>  struct drm_conn_prop_enum_list {
>  	int type;
>  	const char *name;
> @@ -130,6 +147,19 @@ void drm_connector_ida_destroy(void)
>  		ida_destroy(&drm_connector_enum_list[i].ida);
>  }
>  
> +void drm_connector_oob_hotplug_cleanup(void)
> +{
> +	struct drm_oob_hotplug_state *e, *tmp;
> +
> +	scoped_guard(mutex, &oob_hotplug_list_lock) {
> +		list_for_each_entry_safe(e, tmp, &oob_hotplug_list, head) {
> +			list_del(&e->head);
> +			fwnode_handle_put(e->fwnode);
> +			kfree(e);
> +		}
> +	}
> +}
> +
>  /**
>   * drm_get_connector_type_name - return a string for connector type
>   * @type: The connector type (DRM_MODE_CONNECTOR_*)
> @@ -829,6 +859,36 @@ void drm_connector_cleanup(struct drm_connector *connector)
>  }
>  EXPORT_SYMBOL(drm_connector_cleanup);
>  
> +/**
> + * drm_connector_replay_oob_hotplug_event - send cached OOB HPD event
> + * @connector: the connector that should receive the event
> + *
> + * Send the cached out-of-band hotplug as a new out-of-band hotplug event.
> + */
> +static void drm_connector_replay_oob_hotplug_event(struct drm_connector *connector)
> +{
> +	struct fwnode_handle *fwnode = connector->fwnode;
> +	enum drm_connector_status status;
> +	struct drm_oob_hotplug_state *e;
> +	bool found = false;
> +
> +	if (!fwnode || !connector->funcs->oob_hotplug_event)
> +		return;
> +
> +	scoped_guard(mutex, &oob_hotplug_list_lock) {
> +		list_for_each_entry(e, &oob_hotplug_list, head) {
> +			if (e->fwnode == fwnode || fwnode->secondary == e->fwnode) {
> +				status = e->status;
> +				found = true;
> +				break;
> +			}
> +		}
> +	}
> +
> +	if (found)
> +		connector->funcs->oob_hotplug_event(connector, status);
> +}
> +
>  /**
>   * drm_connector_register - register a connector
>   * @connector: the connector to register
> @@ -849,6 +909,7 @@ EXPORT_SYMBOL(drm_connector_cleanup);
>   */
>  int drm_connector_register(struct drm_connector *connector)
>  {
> +	bool replay_oob_hotplug = false;
>  	int ret = 0;
>  
>  	if (!connector->dev->registered)
> @@ -888,6 +949,7 @@ int drm_connector_register(struct drm_connector *connector)
>  	mutex_lock(&connector_list_lock);
>  	list_add_tail(&connector->global_connector_list_entry, &connector_list);
>  	mutex_unlock(&connector_list_lock);
> +	replay_oob_hotplug = true;
>  	goto unlock;
>  
>  err_late_register:
> @@ -898,6 +960,10 @@ int drm_connector_register(struct drm_connector *connector)
>  	drm_sysfs_connector_remove(connector);
>  unlock:
>  	mutex_unlock(&connector->mutex);
> +
> +	if (replay_oob_hotplug)
> +		drm_connector_replay_oob_hotplug_event(connector);
> +
>  	return ret;
>  }
>  EXPORT_SYMBOL(drm_connector_register);
> @@ -3671,6 +3737,41 @@ struct drm_connector *drm_connector_find_by_fwnode(struct fwnode_handle *fwnode)
>  	return found;
>  }
>  
> +/**
> + * drm_connector_record_oob_hotplug_status - Cache OOB hotplug status
> + * @fwnode - fwnode for the DRM connector
> + * @status - out-of-band status info
> + *
> + * Cache the latest out-of-band hotplug status for a fwnode so it can be
> + * (re)played from when the DRM device is (re)registered after this event
> + * arrived.
> + */
> +static void drm_connector_record_oob_hotplug_status(struct fwnode_handle *fwnode,
> +						    enum drm_connector_status status)
> +{
> +	struct drm_oob_hotplug_state *e;
> +
> +	if (!fwnode)
> +		return;
> +
> +	guard(mutex)(&oob_hotplug_list_lock);
> +
> +	list_for_each_entry(e, &oob_hotplug_list, head) {
> +		if (e->fwnode == fwnode) {
> +			e->status = status;
> +			return;
> +		}
> +	}
> +
> +	e = kzalloc(sizeof(*e), GFP_KERNEL);
> +	if (!e)
> +		return;
> +
> +	e->fwnode = fwnode_handle_get(fwnode);
> +	e->status = status;
> +	list_add_tail(&e->head, &oob_hotplug_list);
> +}
> +
>  /**
>   * drm_connector_oob_hotplug_event - Report out-of-band hotplug event to connector
>   * @connector_fwnode: fwnode_handle to report the event on
> @@ -3683,12 +3784,17 @@ struct drm_connector *drm_connector_find_by_fwnode(struct fwnode_handle *fwnode)
>   *
>   * This function can be used to report these out-of-band events after obtaining
>   * a drm_connector reference through calling drm_connector_find_by_fwnode().
> + *
> + * The last status for each fwnode is cached and replayed when a matching DRM
> + * connector device is (re)registered.
>   */
>  void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode,
>  				     enum drm_connector_status status)
>  {
>  	struct drm_connector *connector;
>  
> +	drm_connector_record_oob_hotplug_status(connector_fwnode, status);
> +
>  	connector = drm_connector_find_by_fwnode(connector_fwnode);
>  	if (IS_ERR(connector))
>  		return;


Sorry for late reply.

Should we call drm_connector_record_oob_hotplug_status() only when 
drm_connector_find_by_fwnode() can't find the connector?

> diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h
> index 83146ffef00c..c2714ea256a7 100644
> --- a/drivers/gpu/drm/drm_crtc_internal.h
> +++ b/drivers/gpu/drm/drm_crtc_internal.h
> @@ -188,6 +188,7 @@ int drm_mode_getencoder(struct drm_device *dev,
>  /* drm_connector.c */
>  void drm_connector_ida_init(void);
>  void drm_connector_ida_destroy(void);
> +void drm_connector_oob_hotplug_cleanup(void);
>  void drm_connector_unregister_all(struct drm_device *dev);
>  int drm_connector_register_all(struct drm_device *dev);
>  int drm_connector_set_obj_prop(struct drm_mode_object *obj,
> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> index 3c570f9393b9..c1aeab297ff9 100644
> --- a/drivers/gpu/drm/drm_drv.c
> +++ b/drivers/gpu/drm/drm_drv.c
> @@ -1239,6 +1239,7 @@ static void drm_core_exit(void)
>  	drm_sysfs_destroy();
>  	WARN_ON(!xa_empty(&drm_minors_xa));
>  	drm_connector_ida_destroy();
> +	drm_connector_oob_hotplug_cleanup();
>  }
>  
>  static int __init drm_core_init(void)
> 
> ---
> base-commit: d3d1e0c4343385fb343a552e4f3b6b97d5762fc2
> change-id: 20260821-drm-connector-oob-hotplug-cache-13386679f852
> 
> Best regards,
> --  
> Sebastian Reichel <sebastian.reichel@collabora.com>

-- 
Best, 
Chaoyi

_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

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

* Re: [PATCH] drm/connector: Cache out-of-band hotplug events
@ 2026-09-11  1:53   ` Chaoyi Chen
  0 siblings, 0 replies; 9+ messages in thread
From: Chaoyi Chen @ 2026-09-11  1:53 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: Yongxing Mou, Igor Paunovic, Alexey Charkov, dri-devel,
	linux-kernel, linux-arm-kernel, linux-rockchip, kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Heiko Stuebner, Dmitry Baryshkov

Hello Sebastian,

On 8/21/2026 10:55 PM, Sebastian Reichel wrote:
> When the USB-C state machine finished negotiating DP AltMode before the
> DRM device has been probed, the out-of-band hotplug events fired to
> early and are lost. Without replugging the display or reloading the
> USB-C driver, the DRM driver assumes nothing is plugged.
> 
> Reproducing this race condition at boot time depends on kernel
> configuration and exact USB-C equipment due to timing, but it can easily
> be reproduced by reloading the DRM driver consuming the out-of-band
> hotplug events without reloading the USB-C driver.
> 
> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
> ---
> This has been tested together with the patch series adding USB-C DP
> AltMode support for Rockchip RK3588/RK3576. This is mostly independent
> and should also affect other platforms, so I'm sending it separately.
> 
> I ran into this during development via the module reload path, but it
> seems Heiko [0] and Igor [1] managed to hit the race condition with a
> normal boot.
> 
> [0] https://lore.kernel.org/dri-devel/20767137.geO5KgaWL5@diego/
> [1] https://lore.kernel.org/dri-devel/20260811211534.8618-1-royalnet026@gmail.com/
> ---
>  drivers/gpu/drm/drm_connector.c     | 106 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/drm_crtc_internal.h |   1 +
>  drivers/gpu/drm/drm_drv.c           |   1 +
>  3 files changed, 108 insertions(+)
> 
> diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
> index 8b4baed060f3..b69e3776b153 100644
> --- a/drivers/gpu/drm/drm_connector.c
> +++ b/drivers/gpu/drm/drm_connector.c
> @@ -33,6 +33,7 @@
>  #include <drm/drm_sysfs.h>
>  #include <drm/drm_utils.h>
>  
> +#include <linux/cleanup.h>
>  #include <linux/export.h>
>  #include <linux/platform_device.h>
>  #include <linux/property.h>
> @@ -81,6 +82,22 @@
>  static DEFINE_MUTEX(connector_list_lock);
>  static LIST_HEAD(connector_list);
>  
> +/*
> + * List of connector fwnodes with their last out-of-band hotplug status
> + * required to forward them to a connector on registration. This ensures
> + * the connector sees a HPD event, if the event arrived before the DRM
> + * driver was probed (either due to module reload, or because of bad
> + * timing during bootup).
> + */
> +struct drm_oob_hotplug_state {
> +	struct list_head head;
> +	struct fwnode_handle *fwnode;
> +	enum drm_connector_status status;
> +};
> +
> +static DEFINE_MUTEX(oob_hotplug_list_lock);
> +static LIST_HEAD(oob_hotplug_list);
> +
>  struct drm_conn_prop_enum_list {
>  	int type;
>  	const char *name;
> @@ -130,6 +147,19 @@ void drm_connector_ida_destroy(void)
>  		ida_destroy(&drm_connector_enum_list[i].ida);
>  }
>  
> +void drm_connector_oob_hotplug_cleanup(void)
> +{
> +	struct drm_oob_hotplug_state *e, *tmp;
> +
> +	scoped_guard(mutex, &oob_hotplug_list_lock) {
> +		list_for_each_entry_safe(e, tmp, &oob_hotplug_list, head) {
> +			list_del(&e->head);
> +			fwnode_handle_put(e->fwnode);
> +			kfree(e);
> +		}
> +	}
> +}
> +
>  /**
>   * drm_get_connector_type_name - return a string for connector type
>   * @type: The connector type (DRM_MODE_CONNECTOR_*)
> @@ -829,6 +859,36 @@ void drm_connector_cleanup(struct drm_connector *connector)
>  }
>  EXPORT_SYMBOL(drm_connector_cleanup);
>  
> +/**
> + * drm_connector_replay_oob_hotplug_event - send cached OOB HPD event
> + * @connector: the connector that should receive the event
> + *
> + * Send the cached out-of-band hotplug as a new out-of-band hotplug event.
> + */
> +static void drm_connector_replay_oob_hotplug_event(struct drm_connector *connector)
> +{
> +	struct fwnode_handle *fwnode = connector->fwnode;
> +	enum drm_connector_status status;
> +	struct drm_oob_hotplug_state *e;
> +	bool found = false;
> +
> +	if (!fwnode || !connector->funcs->oob_hotplug_event)
> +		return;
> +
> +	scoped_guard(mutex, &oob_hotplug_list_lock) {
> +		list_for_each_entry(e, &oob_hotplug_list, head) {
> +			if (e->fwnode == fwnode || fwnode->secondary == e->fwnode) {
> +				status = e->status;
> +				found = true;
> +				break;
> +			}
> +		}
> +	}
> +
> +	if (found)
> +		connector->funcs->oob_hotplug_event(connector, status);
> +}
> +
>  /**
>   * drm_connector_register - register a connector
>   * @connector: the connector to register
> @@ -849,6 +909,7 @@ EXPORT_SYMBOL(drm_connector_cleanup);
>   */
>  int drm_connector_register(struct drm_connector *connector)
>  {
> +	bool replay_oob_hotplug = false;
>  	int ret = 0;
>  
>  	if (!connector->dev->registered)
> @@ -888,6 +949,7 @@ int drm_connector_register(struct drm_connector *connector)
>  	mutex_lock(&connector_list_lock);
>  	list_add_tail(&connector->global_connector_list_entry, &connector_list);
>  	mutex_unlock(&connector_list_lock);
> +	replay_oob_hotplug = true;
>  	goto unlock;
>  
>  err_late_register:
> @@ -898,6 +960,10 @@ int drm_connector_register(struct drm_connector *connector)
>  	drm_sysfs_connector_remove(connector);
>  unlock:
>  	mutex_unlock(&connector->mutex);
> +
> +	if (replay_oob_hotplug)
> +		drm_connector_replay_oob_hotplug_event(connector);
> +
>  	return ret;
>  }
>  EXPORT_SYMBOL(drm_connector_register);
> @@ -3671,6 +3737,41 @@ struct drm_connector *drm_connector_find_by_fwnode(struct fwnode_handle *fwnode)
>  	return found;
>  }
>  
> +/**
> + * drm_connector_record_oob_hotplug_status - Cache OOB hotplug status
> + * @fwnode - fwnode for the DRM connector
> + * @status - out-of-band status info
> + *
> + * Cache the latest out-of-band hotplug status for a fwnode so it can be
> + * (re)played from when the DRM device is (re)registered after this event
> + * arrived.
> + */
> +static void drm_connector_record_oob_hotplug_status(struct fwnode_handle *fwnode,
> +						    enum drm_connector_status status)
> +{
> +	struct drm_oob_hotplug_state *e;
> +
> +	if (!fwnode)
> +		return;
> +
> +	guard(mutex)(&oob_hotplug_list_lock);
> +
> +	list_for_each_entry(e, &oob_hotplug_list, head) {
> +		if (e->fwnode == fwnode) {
> +			e->status = status;
> +			return;
> +		}
> +	}
> +
> +	e = kzalloc(sizeof(*e), GFP_KERNEL);
> +	if (!e)
> +		return;
> +
> +	e->fwnode = fwnode_handle_get(fwnode);
> +	e->status = status;
> +	list_add_tail(&e->head, &oob_hotplug_list);
> +}
> +
>  /**
>   * drm_connector_oob_hotplug_event - Report out-of-band hotplug event to connector
>   * @connector_fwnode: fwnode_handle to report the event on
> @@ -3683,12 +3784,17 @@ struct drm_connector *drm_connector_find_by_fwnode(struct fwnode_handle *fwnode)
>   *
>   * This function can be used to report these out-of-band events after obtaining
>   * a drm_connector reference through calling drm_connector_find_by_fwnode().
> + *
> + * The last status for each fwnode is cached and replayed when a matching DRM
> + * connector device is (re)registered.
>   */
>  void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode,
>  				     enum drm_connector_status status)
>  {
>  	struct drm_connector *connector;
>  
> +	drm_connector_record_oob_hotplug_status(connector_fwnode, status);
> +
>  	connector = drm_connector_find_by_fwnode(connector_fwnode);
>  	if (IS_ERR(connector))
>  		return;


Sorry for late reply.

Should we call drm_connector_record_oob_hotplug_status() only when 
drm_connector_find_by_fwnode() can't find the connector?

> diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h
> index 83146ffef00c..c2714ea256a7 100644
> --- a/drivers/gpu/drm/drm_crtc_internal.h
> +++ b/drivers/gpu/drm/drm_crtc_internal.h
> @@ -188,6 +188,7 @@ int drm_mode_getencoder(struct drm_device *dev,
>  /* drm_connector.c */
>  void drm_connector_ida_init(void);
>  void drm_connector_ida_destroy(void);
> +void drm_connector_oob_hotplug_cleanup(void);
>  void drm_connector_unregister_all(struct drm_device *dev);
>  int drm_connector_register_all(struct drm_device *dev);
>  int drm_connector_set_obj_prop(struct drm_mode_object *obj,
> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> index 3c570f9393b9..c1aeab297ff9 100644
> --- a/drivers/gpu/drm/drm_drv.c
> +++ b/drivers/gpu/drm/drm_drv.c
> @@ -1239,6 +1239,7 @@ static void drm_core_exit(void)
>  	drm_sysfs_destroy();
>  	WARN_ON(!xa_empty(&drm_minors_xa));
>  	drm_connector_ida_destroy();
> +	drm_connector_oob_hotplug_cleanup();
>  }
>  
>  static int __init drm_core_init(void)
> 
> ---
> base-commit: d3d1e0c4343385fb343a552e4f3b6b97d5762fc2
> change-id: 20260821-drm-connector-oob-hotplug-cache-13386679f852
> 
> Best regards,
> --  
> Sebastian Reichel <sebastian.reichel@collabora.com>

-- 
Best, 
Chaoyi

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

end of thread, other threads:[~2026-09-11  1:53 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-21 14:55 [PATCH] drm/connector: Cache out-of-band hotplug events Sebastian Reichel
2026-08-21 14:55 ` Sebastian Reichel
2026-08-21 15:06 ` sashiko-bot
2026-08-24 20:33 ` kernel test robot
2026-08-24 20:33   ` kernel test robot
2026-09-10 16:25 ` Igor Paunovic
2026-09-10 16:25   ` Igor Paunovic
2026-09-11  1:53 ` Chaoyi Chen
2026-09-11  1:53   ` Chaoyi Chen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.