* [PATCH v8 0/2] Pass down hot-plug CONNECTOR ID to user-space
@ 2026-04-22 12:35 Nicolas Frattaroli
2026-04-22 12:35 ` [PATCH v8 1/2] drm/connector: Fix epoch_counter docs to reflect reality Nicolas Frattaroli
2026-04-22 12:35 ` [PATCH v8 2/2] drm: Send per-connector hotplug events Nicolas Frattaroli
0 siblings, 2 replies; 4+ messages in thread
From: Nicolas Frattaroli @ 2026-04-22 12:35 UTC (permalink / raw)
To: Stanislav Lisovskiy, Ville Syrjälä, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Louis Chauvet, Haneen Mohammed, Melissa Wen, Daniel Stone,
Ian Forbes, Dmitry Baryshkov
Cc: dri-devel, linux-kernel, dri-devel, linux-kernel, kernel,
wayland-devel, Nicolas Frattaroli, Marius Vlad
This series addresses a shortcoming whereby a hot plug event is sent
without it being passed the actual connector that caused it. This takes
into consideration both the polling path and the HPD (Hot Plug Detect)
path.
The motivation is that user-space applications such as Weston would
previously receive non-connector-specific hotplug events, and then have
to figure out themselves which connector needs to have a modeset
executed on. This notably did not work when the hotplug events came in
too fast, resulting in Weston missing an on-off-on transition of a
connector, seeing that its state was unchanged from "on" so can't be the
one that was hotplugged, and skipping reinitialising it as it looks
through the other connectors that could've caused it.
The real world implication is that on setups with slightly sketchy HDMI
connections, a brief flicker in the HPD signal could result in video
output bidding farewell entirely until a manual proper re-plug was
performed.
By sending connector specific hotplug events, this ambiguity is resolved
without any change to the user-space API. Future work should however
make the kernel explicitly request a modeset from userspace through a
more robust path, either by using link-status (nb. the userspace side
may not handle it properly at present) or by exposing the epoch counter
through the uAPI such that fragile state comparison code is no longer
needed.
This change results in potentially more hotplug events being sent than
previously, as n changed connectors at a time result in n hotplug
events, rather than just a single one.
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
---
Changes in v8:
- Drop pending_hp patch
- Add patch correcting epoch_counter documentation
- Rework implementation such that hotplug events are still sent outside
the mode_config mutex, but don't iterate through the connector list
without the lock.
- Link to v7: https://patch.msgid.link/20260415-hot-plug-passup-v7-0-9a27ef5e2428@collabora.com
Changes in v7 RESEND:
- None, other than removing the leftover diffstat from the cover letter
- Link to v7: https://lore.kernel.org/r/20260217-hot-plug-passup-v7-0-f8221b2aab51@collabora.com
Changes in v7:
- Drop the two vkms patches, as I don't want them to be blocked on
review. I still think they're correct, but they're not essential and
don't need to block this series.
- Link to v6: https://lore.kernel.org/r/20260123-hot-plug-passup-v6-0-aaaf61d960bb@collabora.com
Changes in v6:
- Rewrote cover letter to explain the motivation for this series more
plainly
- Rename "status_changed" to "pending_hp"
- Set "pending_hp" in the existing path that would also affect
epoch_counter
- No longer set the boolean in drm_helper_probe_single_connector_modes,
as it does not appear to be necessary
- Reword commits to better justify the changes
- Link to v5: https://lore.kernel.org/r/20251111162338.15141-1-marius.vlad@collabora.com/
Changes in v5:
- vkms: add support for sending the CONNECTOR ID when hot-plugging through
ConfigFS - as reported by Louis, vkms can now make use of ConfigFS to
simulate connector status.
- vkms: add a small change to ignore previous/old drm connector status
when sending out hot-plug uevent.
- Link to v4: https://lore.kernel.org/r/20251103174558.7709-1-marius.vlad@collabora.com/
Changes in v4:
- removed the "This patch" bit - Dmitry
- added a short note when the flag is set and cleared - Dmitry
- address double dead-locking detected - kbot: https://lore.kernel.org/dri-devel/202509251410.fdfbcac3-lkp@intel.com/
- virtual connectors do not seem have any kind of hotplug - added
polling in vkms - as noted by Ian
- Link to v3: https://lore.kernel.org/r/20250923083636.4749-1-marius.vlad@collabora.com/
Changes in v3:
- Address comments from Dmitry:
- guard connector status write with mode_config.mutex
- avoid setting up the connector status and immediately unset it. Do the
unset in drm_kms_helper_hotplug_event/drm_kms_helper_connector_hotplug_event
- Link to v2: https://lore.kernel.org/r/20250729165708.9947-1-marius.vlad@collabora.com/
Changes in v2:
- Address comments from Daniel:
- split patch into 2, one that introduces a bool to track connector
connection status change and a patch that uses that to be able to send
hot plug events with the proper CONNECTOR ID to udev and further pass
that down to user-space
- nuke out mutex when iterating connector list
- fix typo
- Link to v1: https://lore.kernel.org/r/20250627131751.2004-1-marius.vlad@collabora.com/
---
Nicolas Frattaroli (2):
drm/connector: Fix epoch_counter docs to reflect reality
drm: Send per-connector hotplug events
drivers/gpu/drm/drm_probe_helper.c | 68 ++++++++++++++++++++++++--------------
include/drm/drm_connector.h | 5 ++-
2 files changed, 47 insertions(+), 26 deletions(-)
---
base-commit: 28247e192ca66016a4b374ff06acdf9dbb6cfe7c
change-id: 20260121-hot-plug-passup-f8ed03f7c202
Best regards,
--
Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH v8 1/2] drm/connector: Fix epoch_counter docs to reflect reality 2026-04-22 12:35 [PATCH v8 0/2] Pass down hot-plug CONNECTOR ID to user-space Nicolas Frattaroli @ 2026-04-22 12:35 ` Nicolas Frattaroli 2026-04-22 12:35 ` [PATCH v8 2/2] drm: Send per-connector hotplug events Nicolas Frattaroli 1 sibling, 0 replies; 4+ messages in thread From: Nicolas Frattaroli @ 2026-04-22 12:35 UTC (permalink / raw) To: Stanislav Lisovskiy, Ville Syrjälä, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Louis Chauvet, Haneen Mohammed, Melissa Wen, Daniel Stone, Ian Forbes, Dmitry Baryshkov Cc: dri-devel, linux-kernel, dri-devel, linux-kernel, kernel, wayland-devel, Nicolas Frattaroli Since the very day epoch_counter in drm_connector was introduced, its documentation was not accurate. It claims it's used to detect "any other changes [...] besides status", when in reality, it's used to detect changes including status, as a status change also increases the epoch counter. Adjust the documentation to rectify this discrepancy. Fixes: 5186421cbfe2 ("drm: Introduce epoch counter to drm_connector") Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com> --- include/drm/drm_connector.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 3e422a4f2e72..446385a12f4b 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -2264,7 +2264,10 @@ struct drm_connector { */ struct mutex edid_override_mutex; - /** @epoch_counter: used to detect any other changes in connector, besides status */ + /** + * @epoch_counter: Used to detect changes in connector. Increased when + * the connector, including its status, is changed. + */ u64 epoch_counter; /** -- 2.53.0 ^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v8 2/2] drm: Send per-connector hotplug events 2026-04-22 12:35 [PATCH v8 0/2] Pass down hot-plug CONNECTOR ID to user-space Nicolas Frattaroli 2026-04-22 12:35 ` [PATCH v8 1/2] drm/connector: Fix epoch_counter docs to reflect reality Nicolas Frattaroli @ 2026-04-22 12:35 ` Nicolas Frattaroli 2026-04-22 14:30 ` Ville Syrjälä 1 sibling, 1 reply; 4+ messages in thread From: Nicolas Frattaroli @ 2026-04-22 12:35 UTC (permalink / raw) To: Stanislav Lisovskiy, Ville Syrjälä, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Louis Chauvet, Haneen Mohammed, Melissa Wen, Daniel Stone, Ian Forbes, Dmitry Baryshkov Cc: dri-devel, linux-kernel, dri-devel, linux-kernel, kernel, wayland-devel, Nicolas Frattaroli, Marius Vlad Try to send per-connector hotplug events as often as possible, rather than connector-less global hotplug events. This does result in more hotplug events if multiple connectors changed at the same time, but give userspace more actionable information. Since the hotplug event needs to be sent outside of the mode_config mutex to avoid a deadlock, pointers to all the changed connectors are stored in a newly allocated array. The "changed" boolean in output_poll_execute now only serves to signal that a non-connector-specific hotplug event needs to be sent from a prior event that was delayed. So, rename it from "changed" to "delayed_hp" to avoid any confusion. Co-developed-by: Marius Vlad <marius.vlad@collabora.com> Signed-off-by: Marius Vlad <marius.vlad@collabora.com> Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com> --- drivers/gpu/drm/drm_probe_helper.c | 68 ++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index d4dc8cb45bce..3beed8aa32fe 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -757,17 +757,19 @@ static void output_poll_execute(struct work_struct *work) { struct delayed_work *delayed_work = to_delayed_work(work); struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_work); + struct drm_connector **changed_conns; struct drm_connector *connector; struct drm_connector_list_iter conn_iter; enum drm_connector_status old_status; - bool repoll = false, changed; + unsigned int num_changed = 0, i; + bool repoll = false, delayed_hp; u64 old_epoch_counter; if (!dev->mode_config.poll_enabled) return; /* Pick up any changes detected by the probe functions. */ - changed = dev->mode_config.delayed_event; + delayed_hp = dev->mode_config.delayed_event; dev->mode_config.delayed_event = false; if (!drm_kms_helper_poll) { @@ -783,6 +785,13 @@ static void output_poll_execute(struct work_struct *work) goto out; } + changed_conns = kmalloc_array(dev->mode_config.num_connector, + sizeof(*changed_conns), GFP_KERNEL); + if (!changed_conns) { + repoll = true; + goto out; + } + drm_connector_list_iter_begin(dev, &conn_iter); drm_for_each_connector_iter(connector, &conn_iter) { /* Ignore forced connectors. */ @@ -836,15 +845,23 @@ static void output_poll_execute(struct work_struct *work) connector->base.id, connector->name, old_epoch_counter, connector->epoch_counter); - changed = true; + drm_connector_get(connector); + changed_conns[num_changed++] = connector; } } drm_connector_list_iter_end(&conn_iter); mutex_unlock(&dev->mode_config.mutex); + for (i = 0; i < num_changed; i++) { + drm_kms_helper_connector_hotplug_event(changed_conns[i]); + drm_connector_put(changed_conns[i]); + } + + kfree(changed_conns); + out: - if (changed) + if (delayed_hp) drm_kms_helper_hotplug_event(dev); if (repoll) @@ -1081,39 +1098,40 @@ EXPORT_SYMBOL(drm_connector_helper_hpd_irq_event); */ bool drm_helper_hpd_irq_event(struct drm_device *dev) { - struct drm_connector *connector, *first_changed_connector = NULL; struct drm_connector_list_iter conn_iter; - int changed = 0; + struct drm_connector **changed_conns; + struct drm_connector *connector; + unsigned int changed = 0, i; if (!dev->mode_config.poll_enabled) return false; - mutex_lock(&dev->mode_config.mutex); - drm_connector_list_iter_begin(dev, &conn_iter); - drm_for_each_connector_iter(connector, &conn_iter) { - /* Only handle HPD capable connectors. */ - if (!(connector->polled & DRM_CONNECTOR_POLL_HPD)) - continue; + scoped_guard(mutex, &dev->mode_config.mutex) { + changed_conns = kmalloc_array(dev->mode_config.num_connector, + sizeof(*changed_conns), GFP_KERNEL); + if (!changed_conns) + return false; - if (check_connector_changed(connector)) { - if (!first_changed_connector) { + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + /* Only handle HPD capable connectors. */ + if (!(connector->polled & DRM_CONNECTOR_POLL_HPD)) + continue; + + if (check_connector_changed(connector)) { drm_connector_get(connector); - first_changed_connector = connector; + changed_conns[changed++] = connector; } - - changed++; } + drm_connector_list_iter_end(&conn_iter); } - drm_connector_list_iter_end(&conn_iter); - mutex_unlock(&dev->mode_config.mutex); - if (changed == 1) - drm_kms_helper_connector_hotplug_event(first_changed_connector); - else if (changed > 0) - drm_kms_helper_hotplug_event(dev); + for (i = 0; i < changed; i++) { + drm_kms_helper_connector_hotplug_event(changed_conns[i]); + drm_connector_put(changed_conns[i]); + } - if (first_changed_connector) - drm_connector_put(first_changed_connector); + kfree(changed_conns); return changed; } -- 2.53.0 ^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v8 2/2] drm: Send per-connector hotplug events 2026-04-22 12:35 ` [PATCH v8 2/2] drm: Send per-connector hotplug events Nicolas Frattaroli @ 2026-04-22 14:30 ` Ville Syrjälä 0 siblings, 0 replies; 4+ messages in thread From: Ville Syrjälä @ 2026-04-22 14:30 UTC (permalink / raw) To: Nicolas Frattaroli Cc: Stanislav Lisovskiy, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Louis Chauvet, Haneen Mohammed, Melissa Wen, Daniel Stone, Ian Forbes, Dmitry Baryshkov, dri-devel, linux-kernel, kernel, wayland-devel, Marius Vlad On Wed, Apr 22, 2026 at 02:35:32PM +0200, Nicolas Frattaroli wrote: > Try to send per-connector hotplug events as often as possible, rather > than connector-less global hotplug events. This does result in more > hotplug events if multiple connectors changed at the same time, but > give userspace more actionable information. > > Since the hotplug event needs to be sent outside of the mode_config > mutex to avoid a deadlock, pointers to all the changed connectors are > stored in a newly allocated array. > > The "changed" boolean in output_poll_execute now only serves to signal > that a non-connector-specific hotplug event needs to be sent from a > prior event that was delayed. So, rename it from "changed" to > "delayed_hp" to avoid any confusion. > > Co-developed-by: Marius Vlad <marius.vlad@collabora.com> > Signed-off-by: Marius Vlad <marius.vlad@collabora.com> > Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com> > --- > drivers/gpu/drm/drm_probe_helper.c | 68 ++++++++++++++++++++++++-------------- > 1 file changed, 43 insertions(+), 25 deletions(-) > > diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c > index d4dc8cb45bce..3beed8aa32fe 100644 > --- a/drivers/gpu/drm/drm_probe_helper.c > +++ b/drivers/gpu/drm/drm_probe_helper.c > @@ -757,17 +757,19 @@ static void output_poll_execute(struct work_struct *work) > { > struct delayed_work *delayed_work = to_delayed_work(work); > struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_work); > + struct drm_connector **changed_conns; > struct drm_connector *connector; > struct drm_connector_list_iter conn_iter; > enum drm_connector_status old_status; > - bool repoll = false, changed; > + unsigned int num_changed = 0, i; > + bool repoll = false, delayed_hp; > u64 old_epoch_counter; > > if (!dev->mode_config.poll_enabled) > return; > > /* Pick up any changes detected by the probe functions. */ > - changed = dev->mode_config.delayed_event; > + delayed_hp = dev->mode_config.delayed_event; > dev->mode_config.delayed_event = false; > > if (!drm_kms_helper_poll) { > @@ -783,6 +785,13 @@ static void output_poll_execute(struct work_struct *work) > goto out; > } > > + changed_conns = kmalloc_array(dev->mode_config.num_connector, > + sizeof(*changed_conns), GFP_KERNEL); > + if (!changed_conns) { > + repoll = true; > + goto out; > + } > + > drm_connector_list_iter_begin(dev, &conn_iter); > drm_for_each_connector_iter(connector, &conn_iter) { > /* Ignore forced connectors. */ > @@ -836,15 +845,23 @@ static void output_poll_execute(struct work_struct *work) > connector->base.id, connector->name, > old_epoch_counter, connector->epoch_counter); > > - changed = true; > + drm_connector_get(connector); > + changed_conns[num_changed++] = connector; > } > } > drm_connector_list_iter_end(&conn_iter); > > mutex_unlock(&dev->mode_config.mutex); > > + for (i = 0; i < num_changed; i++) { > + drm_kms_helper_connector_hotplug_event(changed_conns[i]); > + drm_connector_put(changed_conns[i]); > + } > + > + kfree(changed_conns); > + > out: > - if (changed) > + if (delayed_hp) > drm_kms_helper_hotplug_event(dev); > > if (repoll) > @@ -1081,39 +1098,40 @@ EXPORT_SYMBOL(drm_connector_helper_hpd_irq_event); > */ > bool drm_helper_hpd_irq_event(struct drm_device *dev) > { > - struct drm_connector *connector, *first_changed_connector = NULL; > struct drm_connector_list_iter conn_iter; > - int changed = 0; > + struct drm_connector **changed_conns; > + struct drm_connector *connector; > + unsigned int changed = 0, i; > > if (!dev->mode_config.poll_enabled) > return false; > > - mutex_lock(&dev->mode_config.mutex); > - drm_connector_list_iter_begin(dev, &conn_iter); > - drm_for_each_connector_iter(connector, &conn_iter) { > - /* Only handle HPD capable connectors. */ > - if (!(connector->polled & DRM_CONNECTOR_POLL_HPD)) > - continue; > + scoped_guard(mutex, &dev->mode_config.mutex) { > + changed_conns = kmalloc_array(dev->mode_config.num_connector, > + sizeof(*changed_conns), GFP_KERNEL); > + if (!changed_conns) > + return false; I'm thinking you could avoid all this kmalloc_array() complication by doing the drm_sysfs_connector_hotplug_event() calls directly from the loop, and only defer the drm_client_dev_hotplug() to the end (to avoid locking issues). And ideally I think drm_client_dev_hotplug() should even queue its own work and do things asynchronously, but that's a much bigger change and would need more thought. > > - if (check_connector_changed(connector)) { > - if (!first_changed_connector) { > + drm_connector_list_iter_begin(dev, &conn_iter); > + drm_for_each_connector_iter(connector, &conn_iter) { > + /* Only handle HPD capable connectors. */ > + if (!(connector->polled & DRM_CONNECTOR_POLL_HPD)) > + continue; > + > + if (check_connector_changed(connector)) { > drm_connector_get(connector); > - first_changed_connector = connector; > + changed_conns[changed++] = connector; > } > - > - changed++; > } > + drm_connector_list_iter_end(&conn_iter); > } > - drm_connector_list_iter_end(&conn_iter); > - mutex_unlock(&dev->mode_config.mutex); > > - if (changed == 1) > - drm_kms_helper_connector_hotplug_event(first_changed_connector); > - else if (changed > 0) > - drm_kms_helper_hotplug_event(dev); > + for (i = 0; i < changed; i++) { > + drm_kms_helper_connector_hotplug_event(changed_conns[i]); > + drm_connector_put(changed_conns[i]); > + } > > - if (first_changed_connector) > - drm_connector_put(first_changed_connector); > + kfree(changed_conns); > > return changed; > } > > -- > 2.53.0 -- Ville Syrjälä Intel ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-04-22 14:30 UTC | newest] Thread overview: 4+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-04-22 12:35 [PATCH v8 0/2] Pass down hot-plug CONNECTOR ID to user-space Nicolas Frattaroli 2026-04-22 12:35 ` [PATCH v8 1/2] drm/connector: Fix epoch_counter docs to reflect reality Nicolas Frattaroli 2026-04-22 12:35 ` [PATCH v8 2/2] drm: Send per-connector hotplug events Nicolas Frattaroli 2026-04-22 14:30 ` Ville Syrjälä
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox