aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDaniel Vetter <daniel.vetter@ffwll.ch>2013-06-05 18:17:25 -0400
committerDave Airlie <airlied@redhat.com>2013-06-27 06:34:16 -0400
commit160954b7bca43da7cd3cfbce310e6df919a8216e (patch)
treec10b295d21363961641e452a92bb8179e05106f0 /drivers
parent2b54f78190a2683f48f1295a4c28e98df042546a (diff)
drm: kms_helper: don't lose hotplug event
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> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c32
1 files changed, 31 insertions, 1 deletions
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index 738a4294d820..f6829ba58e86 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,
122 int count = 0; 122 int count = 0;
123 int mode_flags = 0; 123 int mode_flags = 0;
124 bool verbose_prune = true; 124 bool verbose_prune = true;
125 enum drm_connector_status old_status;
125 126
126 DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, 127 DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
127 drm_get_connector_name(connector)); 128 drm_get_connector_name(connector));
@@ -137,7 +138,32 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
137 if (connector->funcs->force) 138 if (connector->funcs->force)
138 connector->funcs->force(connector); 139 connector->funcs->force(connector);
139 } else { 140 } else {
141 old_status = connector->status;
142
140 connector->status = connector->funcs->detect(connector, true); 143 connector->status = connector->funcs->detect(connector, true);
144
145 /*
146 * Normally either the driver's hpd code or the poll loop should
147 * pick up any changes and fire the hotplug event. But if
148 * userspace sneaks in a probe, we might miss a change. Hence
149 * check here, and if anything changed start the hotplug code.
150 */
151 if (old_status != connector->status) {
152 DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n",
153 connector->base.id,
154 drm_get_connector_name(connector),
155 old_status, connector->status);
156
157 /*
158 * The hotplug event code might call into the fb
159 * helpers, and so expects that we do not hold any
160 * locks. Fire up the poll struct instead, it will
161 * disable itself again.
162 */
163 dev->mode_config.delayed_event = true;
164 schedule_delayed_work(&dev->mode_config.output_poll_work,
165 0);
166 }
141 } 167 }
142 168
143 /* Re-enable polling in case the global poll config changed. */ 169 /* Re-enable polling in case the global poll config changed. */
@@ -985,7 +1011,11 @@ static void output_poll_execute(struct work_struct *work)
985 struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_work); 1011 struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_work);
986 struct drm_connector *connector; 1012 struct drm_connector *connector;
987 enum drm_connector_status old_status; 1013 enum drm_connector_status old_status;
988 bool repoll = false, changed = false; 1014 bool repoll = false, changed;
1015
1016 /* Pick up any changes detected by the probe functions. */
1017 changed = dev->mode_config.delayed_event;
1018 dev->mode_config.delayed_event = false;
989 1019
990 if (!drm_kms_helper_poll) 1020 if (!drm_kms_helper_poll)
991 return; 1021 return;