diff options
| author | Daniel Vetter <daniel.vetter@ffwll.ch> | 2016-12-18 08:35:45 -0500 |
|---|---|---|
| committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2017-01-30 04:17:17 -0500 |
| commit | 4e5b54f127426c82dc2816340c26d951a5bb3429 (patch) | |
| tree | cc3a8f7e335cf12880bc8d1a1e2066f6cfe0a68e /drivers/gpu/drm/drm_connector.c | |
| parent | 566cf877a1fcb6d6dc0126b076aad062054c2637 (diff) | |
drm: prevent double-(un)registration for connectors
If we're unlucky then the registration from a hotplugged connector
might race with the final registration step on driver load. And since
MST topology discover is asynchronous that's even somewhat likely.
v2: Also update the kerneldoc for @registered!
v3: Review from Chris:
- Improve kerneldoc for late_register/early_unregister callbacks.
- Use mutex_destroy.
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Sean Paul <seanpaul@chromium.org>
Reported-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20161218133545.2106-1-daniel.vetter@ffwll.ch
(cherry picked from commit e73ab00e9a0f1731f34d0620a9c55f5c30c4ad4e)
Diffstat (limited to 'drivers/gpu/drm/drm_connector.c')
| -rw-r--r-- | drivers/gpu/drm/drm_connector.c | 20 |
1 files changed, 15 insertions, 5 deletions
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 5a4526289392..8be60a4576b3 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c | |||
| @@ -225,6 +225,7 @@ int drm_connector_init(struct drm_device *dev, | |||
| 225 | 225 | ||
| 226 | INIT_LIST_HEAD(&connector->probed_modes); | 226 | INIT_LIST_HEAD(&connector->probed_modes); |
| 227 | INIT_LIST_HEAD(&connector->modes); | 227 | INIT_LIST_HEAD(&connector->modes); |
| 228 | mutex_init(&connector->mutex); | ||
| 228 | connector->edid_blob_ptr = NULL; | 229 | connector->edid_blob_ptr = NULL; |
| 229 | connector->status = connector_status_unknown; | 230 | connector->status = connector_status_unknown; |
| 230 | 231 | ||
| @@ -359,6 +360,8 @@ void drm_connector_cleanup(struct drm_connector *connector) | |||
| 359 | connector->funcs->atomic_destroy_state(connector, | 360 | connector->funcs->atomic_destroy_state(connector, |
| 360 | connector->state); | 361 | connector->state); |
| 361 | 362 | ||
| 363 | mutex_destroy(&connector->mutex); | ||
| 364 | |||
| 362 | memset(connector, 0, sizeof(*connector)); | 365 | memset(connector, 0, sizeof(*connector)); |
| 363 | } | 366 | } |
| 364 | EXPORT_SYMBOL(drm_connector_cleanup); | 367 | EXPORT_SYMBOL(drm_connector_cleanup); |
| @@ -374,14 +377,15 @@ EXPORT_SYMBOL(drm_connector_cleanup); | |||
| 374 | */ | 377 | */ |
| 375 | int drm_connector_register(struct drm_connector *connector) | 378 | int drm_connector_register(struct drm_connector *connector) |
| 376 | { | 379 | { |
| 377 | int ret; | 380 | int ret = 0; |
| 378 | 381 | ||
| 382 | mutex_lock(&connector->mutex); | ||
| 379 | if (connector->registered) | 383 | if (connector->registered) |
| 380 | return 0; | 384 | goto unlock; |
| 381 | 385 | ||
| 382 | ret = drm_sysfs_connector_add(connector); | 386 | ret = drm_sysfs_connector_add(connector); |
| 383 | if (ret) | 387 | if (ret) |
| 384 | return ret; | 388 | goto unlock; |
| 385 | 389 | ||
| 386 | ret = drm_debugfs_connector_add(connector); | 390 | ret = drm_debugfs_connector_add(connector); |
| 387 | if (ret) { | 391 | if (ret) { |
| @@ -397,12 +401,14 @@ int drm_connector_register(struct drm_connector *connector) | |||
| 397 | drm_mode_object_register(connector->dev, &connector->base); | 401 | drm_mode_object_register(connector->dev, &connector->base); |
| 398 | 402 | ||
| 399 | connector->registered = true; | 403 | connector->registered = true; |
| 400 | return 0; | 404 | goto unlock; |
| 401 | 405 | ||
| 402 | err_debugfs: | 406 | err_debugfs: |
| 403 | drm_debugfs_connector_remove(connector); | 407 | drm_debugfs_connector_remove(connector); |
| 404 | err_sysfs: | 408 | err_sysfs: |
| 405 | drm_sysfs_connector_remove(connector); | 409 | drm_sysfs_connector_remove(connector); |
| 410 | unlock: | ||
| 411 | mutex_unlock(&connector->mutex); | ||
| 406 | return ret; | 412 | return ret; |
| 407 | } | 413 | } |
| 408 | EXPORT_SYMBOL(drm_connector_register); | 414 | EXPORT_SYMBOL(drm_connector_register); |
| @@ -415,8 +421,11 @@ EXPORT_SYMBOL(drm_connector_register); | |||
| 415 | */ | 421 | */ |
| 416 | void drm_connector_unregister(struct drm_connector *connector) | 422 | void drm_connector_unregister(struct drm_connector *connector) |
| 417 | { | 423 | { |
| 418 | if (!connector->registered) | 424 | mutex_lock(&connector->mutex); |
| 425 | if (!connector->registered) { | ||
| 426 | mutex_unlock(&connector->mutex); | ||
| 419 | return; | 427 | return; |
| 428 | } | ||
| 420 | 429 | ||
| 421 | if (connector->funcs->early_unregister) | 430 | if (connector->funcs->early_unregister) |
| 422 | connector->funcs->early_unregister(connector); | 431 | connector->funcs->early_unregister(connector); |
| @@ -425,6 +434,7 @@ void drm_connector_unregister(struct drm_connector *connector) | |||
| 425 | drm_debugfs_connector_remove(connector); | 434 | drm_debugfs_connector_remove(connector); |
| 426 | 435 | ||
| 427 | connector->registered = false; | 436 | connector->registered = false; |
| 437 | mutex_unlock(&connector->mutex); | ||
| 428 | } | 438 | } |
| 429 | EXPORT_SYMBOL(drm_connector_unregister); | 439 | EXPORT_SYMBOL(drm_connector_unregister); |
| 430 | 440 | ||
