There's a race window (small for hpd, 10s large for polled outputs) where userspace could sneak in with an unrelated connnector probe ioctl call and eat the hotplug event (since neither the hpd nor the poll code see a state change).
To avoid this, check whether the connector state changes in all other ->detect calls (in the current helper code that's only probe_single) and if that's the case, fire off a hotplug event. Note that we can't directly call the hotplug event handler, since that expects that no locks are held (due to reentrancy with the fb code to update the kms console).
Also, this requires that drivers using the probe_single helper function set up the poll work. All current drivers do that already, and with the reworked hpd handling there'll be no downside to unconditionally setting up the poll work any more.
Reviewed-by: Chris Wilson chris@chris-wilson.co.uk Signed-off-by: Daniel Vetter daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_crtc_helper.c | 32 +++++++++++++++++++++++++++++++- include/drm/drm_crtc.h | 1 + 2 files changed, 32 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index ed1334e..1c0c0bc 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -122,6 +122,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, int count = 0; int mode_flags = 0; bool verbose_prune = true; + enum drm_connector_status old_status;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, drm_get_connector_name(connector)); @@ -137,7 +138,32 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, if (connector->funcs->force) connector->funcs->force(connector); } else { + old_status = connector->status; + connector->status = connector->funcs->detect(connector, true); + + /* + * Normally either the driver's hpd code or the poll loop should + * pick up any changes and fire the hotplug event. But if + * userspace sneaks in a probe, we might miss a change. Hence + * check here, and if anything changed start the hotplug code. + */ + if (old_status != connector->status) { + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n", + connector->base.id, + drm_get_connector_name(connector), + old_status, connector->status); + + /* + * The hotplug event code might call into the fb + * helpers, and so expects that we do not hold any + * locks. Fire up the poll struct instead, it will + * disable itself again. + */ + dev->mode_config.delayed_event = true; + schedule_delayed_work(&dev->mode_config.output_poll_work, + 0); + } }
/* Re-enable polling in case the global poll config changed. */ @@ -980,7 +1006,11 @@ static void output_poll_execute(struct work_struct *work) struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_work); struct drm_connector *connector; enum drm_connector_status old_status; - bool repoll = false, changed = false; + bool repoll = false, changed; + + /* Pick up any changes detected by the probe functions. */ + changed = dev->mode_config.delayed_event; + dev->mode_config.delayed_event = false;
if (!drm_kms_helper_poll) return; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index adb3f9b..91c8568 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -815,6 +815,7 @@ struct drm_mode_config { /* output poll support */ bool poll_enabled; bool poll_running; + bool delayed_event; struct delayed_work output_poll_work;
/* pointers to standard properties */
On some chipset we try to avoid possibly invasive output detection methods (like load detect which can cause flickering elsewhere) in the output poll work. Drivers could hence return unknown when a previous full ->detect call returned a different state.
This change will generate a hotplug event, forcing userspace to do a full scan. This in turn updates the connector->status field so that we will _again_ get a state change when the hotplug work re-runs in 10 seconds.
To avoid this ping-pong loop detect this situation and clamp the connector state to the old value.
Patch is inspired by a patch from Knut Peterson. Knut's patch completely ignored connector state changes if either the old or new status was unknown, which seemed to be a bit too agressive to me.
References: http://lists.freedesktop.org/archives/dri-devel/2012-August/025975.html Cc: Knut Petersen Knut_Petersen@t-online.de Cc: Alex Deucher alexander.deucher@amd.com Signed-off-by: Daniel Vetter daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_crtc_helper.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 1c0c0bc..9985a17 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -1040,6 +1040,24 @@ static void output_poll_execute(struct work_struct *work) if (old_status != connector->status) { const char *old, *new;
+ /* + * The poll work sets force=false when calling detect so + * that drivers can avoid to do disruptive tests (e.g. + * when load detect cycles could cause flickering on + * other, running displays). This bears the risk that we + * flip-flop between unknown here in the poll work and + * the real state when userspace forces a full detect + * call after receiving a hotplug event due to this + * change. + * + * Hence clamp an unknown detect status to the old + * value. + */ + if (connector->status == connector_status_unknown) { + connector->status = old_status; + continue; + } + old = drm_get_connector_status_name(old_status); new = drm_get_connector_status_name(connector->status);
On Thu, Jun 06, 2013 at 12:17:26AM +0200, Daniel Vetter wrote:
On some chipset we try to avoid possibly invasive output detection methods (like load detect which can cause flickering elsewhere) in the output poll work. Drivers could hence return unknown when a previous full ->detect call returned a different state.
This change will generate a hotplug event, forcing userspace to do a full scan. This in turn updates the connector->status field so that we will _again_ get a state change when the hotplug work re-runs in 10 seconds.
To avoid this ping-pong loop detect this situation and clamp the connector state to the old value.
Patch is inspired by a patch from Knut Peterson. Knut's patch completely ignored connector state changes if either the old or new status was unknown, which seemed to be a bit too agressive to me.
References: http://lists.freedesktop.org/archives/dri-devel/2012-August/025975.html Cc: Knut Petersen Knut_Petersen@t-online.de Cc: Alex Deucher alexander.deucher@amd.com Signed-off-by: Daniel Vetter daniel.vetter@ffwll.ch
Also Reviewed-by: Chris Wilson chris@chris-wilson.co.uk
I don't think this has any effect for i915, the circumstances where it might we return connector->status rather than Unknown, so we need some input from nouveau/radeon/architect as to our sanity. -Chris
On Thu, Jun 6, 2013 at 9:49 AM, Chris Wilson chris@chris-wilson.co.uk wrote:
On Thu, Jun 06, 2013 at 12:17:26AM +0200, Daniel Vetter wrote:
On some chipset we try to avoid possibly invasive output detection methods (like load detect which can cause flickering elsewhere) in the output poll work. Drivers could hence return unknown when a previous full ->detect call returned a different state.
This change will generate a hotplug event, forcing userspace to do a full scan. This in turn updates the connector->status field so that we will _again_ get a state change when the hotplug work re-runs in 10 seconds.
To avoid this ping-pong loop detect this situation and clamp the connector state to the old value.
Patch is inspired by a patch from Knut Peterson. Knut's patch completely ignored connector state changes if either the old or new status was unknown, which seemed to be a bit too agressive to me.
References: http://lists.freedesktop.org/archives/dri-devel/2012-August/025975.html Cc: Knut Petersen Knut_Petersen@t-online.de Cc: Alex Deucher alexander.deucher@amd.com Signed-off-by: Daniel Vetter daniel.vetter@ffwll.ch
Also Reviewed-by: Chris Wilson chris@chris-wilson.co.uk
I don't think this has any effect for i915, the circumstances where it might we return connector->status rather than Unknown, so we need some input from nouveau/radeon/architect as to our sanity.
Hm, now I'm confused. The case I've had in mind indeed already works on i915.ko, but Knut's bug report was on a i915gm. The only place where we return unknown is when force==true and we can't get a load detect pipe. That shouldn't be able to ping-pong ...
Knut, can you please shed some clue on me here? -Daniel -- Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Thu, Jun 6, 2013 at 3:49 AM, Chris Wilson chris@chris-wilson.co.uk wrote:
On Thu, Jun 06, 2013 at 12:17:26AM +0200, Daniel Vetter wrote:
On some chipset we try to avoid possibly invasive output detection methods (like load detect which can cause flickering elsewhere) in the output poll work. Drivers could hence return unknown when a previous full ->detect call returned a different state.
This change will generate a hotplug event, forcing userspace to do a full scan. This in turn updates the connector->status field so that we will _again_ get a state change when the hotplug work re-runs in 10 seconds.
To avoid this ping-pong loop detect this situation and clamp the connector state to the old value.
Patch is inspired by a patch from Knut Peterson. Knut's patch completely ignored connector state changes if either the old or new status was unknown, which seemed to be a bit too agressive to me.
References: http://lists.freedesktop.org/archives/dri-devel/2012-August/025975.html Cc: Knut Petersen Knut_Petersen@t-online.de Cc: Alex Deucher alexander.deucher@amd.com Signed-off-by: Daniel Vetter daniel.vetter@ffwll.ch
Also Reviewed-by: Chris Wilson chris@chris-wilson.co.uk
I don't think this has any effect for i915, the circumstances where it might we return connector->status rather than Unknown, so we need some input from nouveau/radeon/architect as to our sanity.
It shouldn't affect radeon at all. We shouldn't ever see the unknown status in practice. The only time we return unknown is as a sanity check in our load detection functions (e.g., if the vbios is missing the load detect command table or if we somehow end up in dac load detect on a digital encoder). So, FWIW,
Acked-by: Alex Deucher alexander.deucher@amd.com
Alex
dri-devel@lists.freedesktop.org