aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLyude Paul <lyude@redhat.com>2018-10-16 16:39:46 -0400
committerJoonas Lahtinen <joonas.lahtinen@linux.intel.com>2018-10-19 04:46:46 -0400
commitde9f8eea5a44b0b756d3d6345af7f8e630a3c8c0 (patch)
tree577b8e84fa95e6e61e7a8d16aee507e944def5cb
parent34ca26a98ad67edd6e4870fe2d4aa047d41a51dd (diff)
drm/atomic_helper: Stop modesets on unregistered connectors harder
Unfortunately, it appears our fix in: commit b5d29843d8ef ("drm/atomic_helper: Allow DPMS On<->Off changes for unregistered connectors") Which attempted to work around the problems introduced by: commit 4d80273976bf ("drm/atomic_helper: Disallow new modesets on unregistered connectors") Is still not the right solution, as modesets can still be triggered outside of drm_atomic_set_crtc_for_connector(). So in order to fix this, while still being careful that we don't break modesets that a driver may perform before being registered with userspace, we replace connector->registered with a tristate member, connector->registration_state. This allows us to keep track of whether or not a connector is still initializing and hasn't been exposed to userspace, is currently registered and exposed to userspace, or has been legitimately removed from the system after having once been present. Using this info, we can prevent userspace from performing new modesets on unregistered connectors while still allowing the driver to perform modesets on unregistered connectors before the driver has finished being registered. Changes since v1: - Fix WARN_ON() in drm_connector_cleanup() that CI caught with this patchset in igt@drv_module_reload@basic-reload-inject and igt@drv_module_reload@basic-reload by checking if the connector is registered instead of unregistered, as calling drm_connector_cleanup() on a connector that hasn't been registered with userspace yet should stay valid. - Remove unregistered_connector_check(), and just go back to what we were doing before in commit 4d80273976bf ("drm/atomic_helper: Disallow new modesets on unregistered connectors") except replacing READ_ONCE(connector->registered) with drm_connector_is_unregistered(). This gets rid of the behavior of allowing DPMS On<->Off, but that should be fine as it's more consistent with the UAPI we had before - danvet - s/drm_connector_unregistered/drm_connector_is_unregistered/ - danvet - Update documentation, fix some typos. Fixes: b5d29843d8ef ("drm/atomic_helper: Allow DPMS On<->Off changes for unregistered connectors") Cc: Ville Syrjälä <ville.syrjala@linux.intel.com> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: Rodrigo Vivi <rodrigo.vivi@intel.com> Cc: stable@vger.kernel.org Cc: David Airlie <airlied@linux.ie> Signed-off-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> Link: https://patchwork.freedesktop.org/patch/msgid/20181016203946.9601-1-lyude@redhat.com (cherry picked from commit 39b50c603878f4f8ae541ac4088a805d588abc79) Fixes: e96550956fbc ("drm/atomic_helper: Disallow new modesets on unregistered connectors") Fixes: 34ca26a98ad6 ("drm/atomic_helper: Allow DPMS On<->Off changes for unregistered connectors") Cc: stable@vger.kernel.org Signed-off-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
-rw-r--r--drivers/gpu/drm/drm_atomic_helper.c21
-rw-r--r--drivers/gpu/drm/drm_atomic_uapi.c21
-rw-r--r--drivers/gpu/drm/drm_connector.c11
-rw-r--r--drivers/gpu/drm/i915/intel_dp_mst.c8
-rw-r--r--include/drm/drm_connector.h71
5 files changed, 99 insertions, 33 deletions
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index e49b22381048..1cc3a045ec2f 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -308,6 +308,26 @@ update_connector_routing(struct drm_atomic_state *state,
308 return 0; 308 return 0;
309 } 309 }
310 310
311 crtc_state = drm_atomic_get_new_crtc_state(state,
312 new_connector_state->crtc);
313 /*
314 * For compatibility with legacy users, we want to make sure that
315 * we allow DPMS On->Off modesets on unregistered connectors. Modesets
316 * which would result in anything else must be considered invalid, to
317 * avoid turning on new displays on dead connectors.
318 *
319 * Since the connector can be unregistered at any point during an
320 * atomic check or commit, this is racy. But that's OK: all we care
321 * about is ensuring that userspace can't do anything but shut off the
322 * display on a connector that was destroyed after its been notified,
323 * not before.
324 */
325 if (drm_connector_is_unregistered(connector) && crtc_state->active) {
326 DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] is not registered\n",
327 connector->base.id, connector->name);
328 return -EINVAL;
329 }
330
311 funcs = connector->helper_private; 331 funcs = connector->helper_private;
312 332
313 if (funcs->atomic_best_encoder) 333 if (funcs->atomic_best_encoder)
@@ -352,7 +372,6 @@ update_connector_routing(struct drm_atomic_state *state,
352 372
353 set_best_encoder(state, new_connector_state, new_encoder); 373 set_best_encoder(state, new_connector_state, new_encoder);
354 374
355 crtc_state = drm_atomic_get_new_crtc_state(state, new_connector_state->crtc);
356 crtc_state->connectors_changed = true; 375 crtc_state->connectors_changed = true;
357 376
358 DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d:%s]\n", 377 DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d:%s]\n",
diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
index a22d6f269b07..d5b7f315098c 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -299,27 +299,6 @@ drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state,
299 struct drm_connector *connector = conn_state->connector; 299 struct drm_connector *connector = conn_state->connector;
300 struct drm_crtc_state *crtc_state; 300 struct drm_crtc_state *crtc_state;
301 301
302 /*
303 * For compatibility with legacy users, we want to make sure that
304 * we allow DPMS On<->Off modesets on unregistered connectors, since
305 * legacy modesetting users will not be expecting these to fail. We do
306 * not however, want to allow legacy users to assign a connector
307 * that's been unregistered from sysfs to another CRTC, since doing
308 * this with a now non-existent connector could potentially leave us
309 * in an invalid state.
310 *
311 * Since the connector can be unregistered at any point during an
312 * atomic check or commit, this is racy. But that's OK: all we care
313 * about is ensuring that userspace can't use this connector for new
314 * configurations after it's been notified that the connector is no
315 * longer present.
316 */
317 if (!READ_ONCE(connector->registered) && crtc) {
318 DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] is not registered\n",
319 connector->base.id, connector->name);
320 return -EINVAL;
321 }
322
323 if (conn_state->crtc == crtc) 302 if (conn_state->crtc == crtc)
324 return 0; 303 return 0;
325 304
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 1e40e5decbe9..4943cef178be 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -379,7 +379,8 @@ void drm_connector_cleanup(struct drm_connector *connector)
379 /* The connector should have been removed from userspace long before 379 /* The connector should have been removed from userspace long before
380 * it is finally destroyed. 380 * it is finally destroyed.
381 */ 381 */
382 if (WARN_ON(connector->registered)) 382 if (WARN_ON(connector->registration_state ==
383 DRM_CONNECTOR_REGISTERED))
383 drm_connector_unregister(connector); 384 drm_connector_unregister(connector);
384 385
385 if (connector->tile_group) { 386 if (connector->tile_group) {
@@ -436,7 +437,7 @@ int drm_connector_register(struct drm_connector *connector)
436 return 0; 437 return 0;
437 438
438 mutex_lock(&connector->mutex); 439 mutex_lock(&connector->mutex);
439 if (connector->registered) 440 if (connector->registration_state != DRM_CONNECTOR_INITIALIZING)
440 goto unlock; 441 goto unlock;
441 442
442 ret = drm_sysfs_connector_add(connector); 443 ret = drm_sysfs_connector_add(connector);
@@ -456,7 +457,7 @@ int drm_connector_register(struct drm_connector *connector)
456 457
457 drm_mode_object_register(connector->dev, &connector->base); 458 drm_mode_object_register(connector->dev, &connector->base);
458 459
459 connector->registered = true; 460 connector->registration_state = DRM_CONNECTOR_REGISTERED;
460 goto unlock; 461 goto unlock;
461 462
462err_debugfs: 463err_debugfs:
@@ -478,7 +479,7 @@ EXPORT_SYMBOL(drm_connector_register);
478void drm_connector_unregister(struct drm_connector *connector) 479void drm_connector_unregister(struct drm_connector *connector)
479{ 480{
480 mutex_lock(&connector->mutex); 481 mutex_lock(&connector->mutex);
481 if (!connector->registered) { 482 if (connector->registration_state != DRM_CONNECTOR_REGISTERED) {
482 mutex_unlock(&connector->mutex); 483 mutex_unlock(&connector->mutex);
483 return; 484 return;
484 } 485 }
@@ -489,7 +490,7 @@ void drm_connector_unregister(struct drm_connector *connector)
489 drm_sysfs_connector_remove(connector); 490 drm_sysfs_connector_remove(connector);
490 drm_debugfs_connector_remove(connector); 491 drm_debugfs_connector_remove(connector);
491 492
492 connector->registered = false; 493 connector->registration_state = DRM_CONNECTOR_UNREGISTERED;
493 mutex_unlock(&connector->mutex); 494 mutex_unlock(&connector->mutex);
494} 495}
495EXPORT_SYMBOL(drm_connector_unregister); 496EXPORT_SYMBOL(drm_connector_unregister);
diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c
index 7f155b4f1a7d..1b00f8ea145b 100644
--- a/drivers/gpu/drm/i915/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/intel_dp_mst.c
@@ -77,7 +77,7 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
77 pipe_config->pbn = mst_pbn; 77 pipe_config->pbn = mst_pbn;
78 78
79 /* Zombie connectors can't have VCPI slots */ 79 /* Zombie connectors can't have VCPI slots */
80 if (READ_ONCE(connector->registered)) { 80 if (!drm_connector_is_unregistered(connector)) {
81 slots = drm_dp_atomic_find_vcpi_slots(state, 81 slots = drm_dp_atomic_find_vcpi_slots(state,
82 &intel_dp->mst_mgr, 82 &intel_dp->mst_mgr,
83 port, 83 port,
@@ -313,7 +313,7 @@ static int intel_dp_mst_get_ddc_modes(struct drm_connector *connector)
313 struct edid *edid; 313 struct edid *edid;
314 int ret; 314 int ret;
315 315
316 if (!READ_ONCE(connector->registered)) 316 if (drm_connector_is_unregistered(connector))
317 return intel_connector_update_modes(connector, NULL); 317 return intel_connector_update_modes(connector, NULL);
318 318
319 edid = drm_dp_mst_get_edid(connector, &intel_dp->mst_mgr, intel_connector->port); 319 edid = drm_dp_mst_get_edid(connector, &intel_dp->mst_mgr, intel_connector->port);
@@ -329,7 +329,7 @@ intel_dp_mst_detect(struct drm_connector *connector, bool force)
329 struct intel_connector *intel_connector = to_intel_connector(connector); 329 struct intel_connector *intel_connector = to_intel_connector(connector);
330 struct intel_dp *intel_dp = intel_connector->mst_port; 330 struct intel_dp *intel_dp = intel_connector->mst_port;
331 331
332 if (!READ_ONCE(connector->registered)) 332 if (drm_connector_is_unregistered(connector))
333 return connector_status_disconnected; 333 return connector_status_disconnected;
334 return drm_dp_mst_detect_port(connector, &intel_dp->mst_mgr, 334 return drm_dp_mst_detect_port(connector, &intel_dp->mst_mgr,
335 intel_connector->port); 335 intel_connector->port);
@@ -372,7 +372,7 @@ intel_dp_mst_mode_valid(struct drm_connector *connector,
372 int bpp = 24; /* MST uses fixed bpp */ 372 int bpp = 24; /* MST uses fixed bpp */
373 int max_rate, mode_rate, max_lanes, max_link_clock; 373 int max_rate, mode_rate, max_lanes, max_link_clock;
374 374
375 if (!READ_ONCE(connector->registered)) 375 if (drm_connector_is_unregistered(connector))
376 return MODE_ERROR; 376 return MODE_ERROR;
377 377
378 if (mode->flags & DRM_MODE_FLAG_DBLSCAN) 378 if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 91a877fa00cb..9ccad6b062f2 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -82,6 +82,53 @@ enum drm_connector_status {
82 connector_status_unknown = 3, 82 connector_status_unknown = 3,
83}; 83};
84 84
85/**
86 * enum drm_connector_registration_status - userspace registration status for
87 * a &drm_connector
88 *
89 * This enum is used to track the status of initializing a connector and
90 * registering it with userspace, so that DRM can prevent bogus modesets on
91 * connectors that no longer exist.
92 */
93enum drm_connector_registration_state {
94 /**
95 * @DRM_CONNECTOR_INITIALIZING: The connector has just been created,
96 * but has yet to be exposed to userspace. There should be no
97 * additional restrictions to how the state of this connector may be
98 * modified.
99 */
100 DRM_CONNECTOR_INITIALIZING = 0,
101
102 /**
103 * @DRM_CONNECTOR_REGISTERED: The connector has been fully initialized
104 * and registered with sysfs, as such it has been exposed to
105 * userspace. There should be no additional restrictions to how the
106 * state of this connector may be modified.
107 */
108 DRM_CONNECTOR_REGISTERED = 1,
109
110 /**
111 * @DRM_CONNECTOR_UNREGISTERED: The connector has either been exposed
112 * to userspace and has since been unregistered and removed from
113 * userspace, or the connector was unregistered before it had a chance
114 * to be exposed to userspace (e.g. still in the
115 * @DRM_CONNECTOR_INITIALIZING state). When a connector is
116 * unregistered, there are additional restrictions to how its state
117 * may be modified:
118 *
119 * - An unregistered connector may only have its DPMS changed from
120 * On->Off. Once DPMS is changed to Off, it may not be switched back
121 * to On.
122 * - Modesets are not allowed on unregistered connectors, unless they
123 * would result in disabling its assigned CRTCs. This means
124 * disabling a CRTC on an unregistered connector is OK, but enabling
125 * one is not.
126 * - Removing a CRTC from an unregistered connector is OK, but new
127 * CRTCs may never be assigned to an unregistered connector.
128 */
129 DRM_CONNECTOR_UNREGISTERED = 2,
130};
131
85enum subpixel_order { 132enum subpixel_order {
86 SubPixelUnknown = 0, 133 SubPixelUnknown = 0,
87 SubPixelHorizontalRGB, 134 SubPixelHorizontalRGB,
@@ -853,10 +900,12 @@ struct drm_connector {
853 bool ycbcr_420_allowed; 900 bool ycbcr_420_allowed;
854 901
855 /** 902 /**
856 * @registered: Is this connector exposed (registered) with userspace? 903 * @registration_state: Is this connector initializing, exposed
904 * (registered) with userspace, or unregistered?
905 *
857 * Protected by @mutex. 906 * Protected by @mutex.
858 */ 907 */
859 bool registered; 908 enum drm_connector_registration_state registration_state;
860 909
861 /** 910 /**
862 * @modes: 911 * @modes:
@@ -1166,6 +1215,24 @@ static inline void drm_connector_unreference(struct drm_connector *connector)
1166 drm_connector_put(connector); 1215 drm_connector_put(connector);
1167} 1216}
1168 1217
1218/**
1219 * drm_connector_is_unregistered - has the connector been unregistered from
1220 * userspace?
1221 * @connector: DRM connector
1222 *
1223 * Checks whether or not @connector has been unregistered from userspace.
1224 *
1225 * Returns:
1226 * True if the connector was unregistered, false if the connector is
1227 * registered or has not yet been registered with userspace.
1228 */
1229static inline bool
1230drm_connector_is_unregistered(struct drm_connector *connector)
1231{
1232 return READ_ONCE(connector->registration_state) ==
1233 DRM_CONNECTOR_UNREGISTERED;
1234}
1235
1169const char *drm_get_connector_status_name(enum drm_connector_status status); 1236const char *drm_get_connector_status_name(enum drm_connector_status status);
1170const char *drm_get_subpixel_order_name(enum subpixel_order order); 1237const char *drm_get_subpixel_order_name(enum subpixel_order order);
1171const char *drm_get_dpms_name(int val); 1238const char *drm_get_dpms_name(int val);