Skip to content

Commit 39b50c6

Browse files
committed
drm/atomic_helper: Stop modesets on unregistered connectors harder
Unfortunately, it appears our fix in: commit b5d2984 ("drm/atomic_helper: Allow DPMS On<->Off changes for unregistered connectors") Which attempted to work around the problems introduced by: commit 4d80273 ("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 4d80273 ("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: b5d2984 ("drm/atomic_helper: Allow DPMS On<->Off changes for unregistered connectors") Cc: Ville Syrjälä <[email protected]> Cc: Daniel Vetter <[email protected]> Cc: Rodrigo Vivi <[email protected]> Cc: [email protected] Cc: David Airlie <[email protected]> Signed-off-by: Lyude Paul <[email protected]> Reviewed-by: Daniel Vetter <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent 20fd600 commit 39b50c6

File tree

5 files changed

+99
-33
lines changed

5 files changed

+99
-33
lines changed

drivers/gpu/drm/drm_atomic_helper.c

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,26 @@ update_connector_routing(struct drm_atomic_state *state,
308308
return 0;
309309
}
310310

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+
311331
funcs = connector->helper_private;
312332

313333
if (funcs->atomic_best_encoder)
@@ -352,7 +372,6 @@ update_connector_routing(struct drm_atomic_state *state,
352372

353373
set_best_encoder(state, new_connector_state, new_encoder);
354374

355-
crtc_state = drm_atomic_get_new_crtc_state(state, new_connector_state->crtc);
356375
crtc_state->connectors_changed = true;
357376

358377
DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d:%s]\n",

drivers/gpu/drm/drm_atomic_uapi.c

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -299,27 +299,6 @@ drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state,
299299
struct drm_connector *connector = conn_state->connector;
300300
struct drm_crtc_state *crtc_state;
301301

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-
323302
if (conn_state->crtc == crtc)
324303
return 0;
325304

drivers/gpu/drm/drm_connector.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,8 @@ void drm_connector_cleanup(struct drm_connector *connector)
379379
/* The connector should have been removed from userspace long before
380380
* it is finally destroyed.
381381
*/
382-
if (WARN_ON(connector->registered))
382+
if (WARN_ON(connector->registration_state ==
383+
DRM_CONNECTOR_REGISTERED))
383384
drm_connector_unregister(connector);
384385

385386
if (connector->tile_group) {
@@ -436,7 +437,7 @@ int drm_connector_register(struct drm_connector *connector)
436437
return 0;
437438

438439
mutex_lock(&connector->mutex);
439-
if (connector->registered)
440+
if (connector->registration_state != DRM_CONNECTOR_INITIALIZING)
440441
goto unlock;
441442

442443
ret = drm_sysfs_connector_add(connector);
@@ -456,7 +457,7 @@ int drm_connector_register(struct drm_connector *connector)
456457

457458
drm_mode_object_register(connector->dev, &connector->base);
458459

459-
connector->registered = true;
460+
connector->registration_state = DRM_CONNECTOR_REGISTERED;
460461
goto unlock;
461462

462463
err_debugfs:
@@ -478,7 +479,7 @@ EXPORT_SYMBOL(drm_connector_register);
478479
void drm_connector_unregister(struct drm_connector *connector)
479480
{
480481
mutex_lock(&connector->mutex);
481-
if (!connector->registered) {
482+
if (connector->registration_state != DRM_CONNECTOR_REGISTERED) {
482483
mutex_unlock(&connector->mutex);
483484
return;
484485
}
@@ -489,7 +490,7 @@ void drm_connector_unregister(struct drm_connector *connector)
489490
drm_sysfs_connector_remove(connector);
490491
drm_debugfs_connector_remove(connector);
491492

492-
connector->registered = false;
493+
connector->registration_state = DRM_CONNECTOR_UNREGISTERED;
493494
mutex_unlock(&connector->mutex);
494495
}
495496
EXPORT_SYMBOL(drm_connector_unregister);

drivers/gpu/drm/i915/intel_dp_mst.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
7878
pipe_config->pbn = mst_pbn;
7979

8080
/* Zombie connectors can't have VCPI slots */
81-
if (READ_ONCE(connector->registered)) {
81+
if (!drm_connector_is_unregistered(connector)) {
8282
slots = drm_dp_atomic_find_vcpi_slots(state,
8383
&intel_dp->mst_mgr,
8484
port,
@@ -314,7 +314,7 @@ static int intel_dp_mst_get_ddc_modes(struct drm_connector *connector)
314314
struct edid *edid;
315315
int ret;
316316

317-
if (!READ_ONCE(connector->registered))
317+
if (drm_connector_is_unregistered(connector))
318318
return intel_connector_update_modes(connector, NULL);
319319

320320
edid = drm_dp_mst_get_edid(connector, &intel_dp->mst_mgr, intel_connector->port);
@@ -330,7 +330,7 @@ intel_dp_mst_detect(struct drm_connector *connector, bool force)
330330
struct intel_connector *intel_connector = to_intel_connector(connector);
331331
struct intel_dp *intel_dp = intel_connector->mst_port;
332332

333-
if (!READ_ONCE(connector->registered))
333+
if (drm_connector_is_unregistered(connector))
334334
return connector_status_disconnected;
335335
return drm_dp_mst_detect_port(connector, &intel_dp->mst_mgr,
336336
intel_connector->port);
@@ -361,7 +361,7 @@ intel_dp_mst_mode_valid(struct drm_connector *connector,
361361
int bpp = 24; /* MST uses fixed bpp */
362362
int max_rate, mode_rate, max_lanes, max_link_clock;
363363

364-
if (!READ_ONCE(connector->registered))
364+
if (drm_connector_is_unregistered(connector))
365365
return MODE_ERROR;
366366

367367
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)

include/drm/drm_connector.h

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,53 @@ enum drm_connector_status {
8282
connector_status_unknown = 3,
8383
};
8484

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+
*/
93+
enum 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+
85132
enum subpixel_order {
86133
SubPixelUnknown = 0,
87134
SubPixelHorizontalRGB,
@@ -853,10 +900,12 @@ struct drm_connector {
853900
bool ycbcr_420_allowed;
854901

855902
/**
856-
* @registered: Is this connector exposed (registered) with userspace?
903+
* @registration_state: Is this connector initializing, exposed
904+
* (registered) with userspace, or unregistered?
905+
*
857906
* Protected by @mutex.
858907
*/
859-
bool registered;
908+
enum drm_connector_registration_state registration_state;
860909

861910
/**
862911
* @modes:
@@ -1166,6 +1215,24 @@ static inline void drm_connector_unreference(struct drm_connector *connector)
11661215
drm_connector_put(connector);
11671216
}
11681217

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+
*/
1229+
static inline bool
1230+
drm_connector_is_unregistered(struct drm_connector *connector)
1231+
{
1232+
return READ_ONCE(connector->registration_state) ==
1233+
DRM_CONNECTOR_UNREGISTERED;
1234+
}
1235+
11691236
const char *drm_get_connector_status_name(enum drm_connector_status status);
11701237
const char *drm_get_subpixel_order_name(enum subpixel_order order);
11711238
const char *drm_get_dpms_name(int val);

0 commit comments

Comments
 (0)