diff options
| author | Daniel Vetter <daniel.vetter@ffwll.ch> | 2014-11-19 12:38:08 -0500 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2014-11-19 20:35:20 -0500 |
| commit | f52b69f1ecfdd7ef6867a257620258c09e569552 (patch) | |
| tree | 743137d6d11bd5a9ed8bba2484de30c5f630944c | |
| parent | 6f75cea66c8dd043ced282016b21a639af176642 (diff) | |
drm/atomic: Don't overrun the connector array when hotplugging
Yet another fallout from not considering DP MST hotplug. With the
previous patches we have stable indices, but it might still happen
that a connector gets added between when we allocate the array and
when we actually add a connector. Especially when we back off due to
ww mutex contention or similar issues.
So store the sizes of the arrays in struct drm_atomic_state and double
check them. We don't really care about races except that we want to
use a consistent value, so ACCESS_ONCE is all we need. And if we
indeed notice that we'd overrun the array then just give up and
restart the entire ioctl.
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Reviewed-by: Rob Clark <robdclark@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
| -rw-r--r-- | drivers/gpu/drm/drm_atomic.c | 26 | ||||
| -rw-r--r-- | drivers/gpu/drm/drm_atomic_helper.c | 23 | ||||
| -rw-r--r-- | include/drm/drm_crtc.h | 2 |
3 files changed, 31 insertions, 20 deletions
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 67c1dc894bd9..3624632084e2 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c | |||
| @@ -56,6 +56,8 @@ drm_atomic_state_alloc(struct drm_device *dev) | |||
| 56 | if (!state) | 56 | if (!state) |
| 57 | return NULL; | 57 | return NULL; |
| 58 | 58 | ||
| 59 | state->num_connector = ACCESS_ONCE(dev->mode_config.num_connector); | ||
| 60 | |||
| 59 | state->crtcs = kcalloc(dev->mode_config.num_crtc, | 61 | state->crtcs = kcalloc(dev->mode_config.num_crtc, |
| 60 | sizeof(*state->crtcs), GFP_KERNEL); | 62 | sizeof(*state->crtcs), GFP_KERNEL); |
| 61 | if (!state->crtcs) | 63 | if (!state->crtcs) |
| @@ -72,12 +74,12 @@ drm_atomic_state_alloc(struct drm_device *dev) | |||
| 72 | sizeof(*state->plane_states), GFP_KERNEL); | 74 | sizeof(*state->plane_states), GFP_KERNEL); |
| 73 | if (!state->plane_states) | 75 | if (!state->plane_states) |
| 74 | goto fail; | 76 | goto fail; |
| 75 | state->connectors = kcalloc(dev->mode_config.num_connector, | 77 | state->connectors = kcalloc(state->num_connector, |
| 76 | sizeof(*state->connectors), | 78 | sizeof(*state->connectors), |
| 77 | GFP_KERNEL); | 79 | GFP_KERNEL); |
| 78 | if (!state->connectors) | 80 | if (!state->connectors) |
| 79 | goto fail; | 81 | goto fail; |
| 80 | state->connector_states = kcalloc(dev->mode_config.num_connector, | 82 | state->connector_states = kcalloc(state->num_connector, |
| 81 | sizeof(*state->connector_states), | 83 | sizeof(*state->connector_states), |
| 82 | GFP_KERNEL); | 84 | GFP_KERNEL); |
| 83 | if (!state->connector_states) | 85 | if (!state->connector_states) |
| @@ -117,7 +119,7 @@ void drm_atomic_state_clear(struct drm_atomic_state *state) | |||
| 117 | 119 | ||
| 118 | DRM_DEBUG_KMS("Clearing atomic state %p\n", state); | 120 | DRM_DEBUG_KMS("Clearing atomic state %p\n", state); |
| 119 | 121 | ||
| 120 | for (i = 0; i < config->num_connector; i++) { | 122 | for (i = 0; i < state->num_connector; i++) { |
| 121 | struct drm_connector *connector = state->connectors[i]; | 123 | struct drm_connector *connector = state->connectors[i]; |
| 122 | 124 | ||
| 123 | if (!connector) | 125 | if (!connector) |
| @@ -304,6 +306,21 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, | |||
| 304 | 306 | ||
| 305 | index = drm_connector_index(connector); | 307 | index = drm_connector_index(connector); |
| 306 | 308 | ||
| 309 | /* | ||
| 310 | * Construction of atomic state updates can race with a connector | ||
| 311 | * hot-add which might overflow. In this case flip the table and just | ||
| 312 | * restart the entire ioctl - no one is fast enough to livelock a cpu | ||
| 313 | * with physical hotplug events anyway. | ||
| 314 | * | ||
| 315 | * Note that we only grab the indexes once we have the right lock to | ||
| 316 | * prevent hotplug/unplugging of connectors. So removal is no problem, | ||
| 317 | * at most the array is a bit too large. | ||
| 318 | */ | ||
| 319 | if (index >= state->num_connector) { | ||
| 320 | DRM_DEBUG_KMS("Hot-added connector would overflow state array, restarting\n"); | ||
| 321 | return -EAGAIN; | ||
| 322 | } | ||
| 323 | |||
| 307 | if (state->connector_states[index]) | 324 | if (state->connector_states[index]) |
| 308 | return state->connector_states[index]; | 325 | return state->connector_states[index]; |
| 309 | 326 | ||
| @@ -499,10 +516,9 @@ int | |||
| 499 | drm_atomic_connectors_for_crtc(struct drm_atomic_state *state, | 516 | drm_atomic_connectors_for_crtc(struct drm_atomic_state *state, |
| 500 | struct drm_crtc *crtc) | 517 | struct drm_crtc *crtc) |
| 501 | { | 518 | { |
| 502 | int nconnectors = state->dev->mode_config.num_connector; | ||
| 503 | int i, num_connected_connectors = 0; | 519 | int i, num_connected_connectors = 0; |
| 504 | 520 | ||
| 505 | for (i = 0; i < nconnectors; i++) { | 521 | for (i = 0; i < state->num_connector; i++) { |
| 506 | struct drm_connector_state *conn_state; | 522 | struct drm_connector_state *conn_state; |
| 507 | 523 | ||
| 508 | conn_state = state->connector_states[i]; | 524 | conn_state = state->connector_states[i]; |
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 0cd054615920..99095ef147ef 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c | |||
| @@ -249,7 +249,6 @@ static int | |||
| 249 | mode_fixup(struct drm_atomic_state *state) | 249 | mode_fixup(struct drm_atomic_state *state) |
| 250 | { | 250 | { |
| 251 | int ncrtcs = state->dev->mode_config.num_crtc; | 251 | int ncrtcs = state->dev->mode_config.num_crtc; |
| 252 | int nconnectors = state->dev->mode_config.num_connector; | ||
| 253 | struct drm_crtc_state *crtc_state; | 252 | struct drm_crtc_state *crtc_state; |
| 254 | struct drm_connector_state *conn_state; | 253 | struct drm_connector_state *conn_state; |
| 255 | int i; | 254 | int i; |
| @@ -264,7 +263,7 @@ mode_fixup(struct drm_atomic_state *state) | |||
| 264 | drm_mode_copy(&crtc_state->adjusted_mode, &crtc_state->mode); | 263 | drm_mode_copy(&crtc_state->adjusted_mode, &crtc_state->mode); |
| 265 | } | 264 | } |
| 266 | 265 | ||
| 267 | for (i = 0; i < nconnectors; i++) { | 266 | for (i = 0; i < state->num_connector; i++) { |
| 268 | struct drm_encoder_helper_funcs *funcs; | 267 | struct drm_encoder_helper_funcs *funcs; |
| 269 | struct drm_encoder *encoder; | 268 | struct drm_encoder *encoder; |
| 270 | 269 | ||
| @@ -336,7 +335,6 @@ drm_atomic_helper_check_prepare(struct drm_device *dev, | |||
| 336 | struct drm_atomic_state *state) | 335 | struct drm_atomic_state *state) |
| 337 | { | 336 | { |
| 338 | int ncrtcs = dev->mode_config.num_crtc; | 337 | int ncrtcs = dev->mode_config.num_crtc; |
| 339 | int nconnectors = dev->mode_config.num_connector; | ||
| 340 | struct drm_crtc *crtc; | 338 | struct drm_crtc *crtc; |
| 341 | struct drm_crtc_state *crtc_state; | 339 | struct drm_crtc_state *crtc_state; |
| 342 | int i, ret; | 340 | int i, ret; |
| @@ -361,7 +359,7 @@ drm_atomic_helper_check_prepare(struct drm_device *dev, | |||
| 361 | } | 359 | } |
| 362 | } | 360 | } |
| 363 | 361 | ||
| 364 | for (i = 0; i < nconnectors; i++) { | 362 | for (i = 0; i < state->num_connector; i++) { |
| 365 | /* | 363 | /* |
| 366 | * This only sets crtc->mode_changed for routing changes, | 364 | * This only sets crtc->mode_changed for routing changes, |
| 367 | * drivers must set crtc->mode_changed themselves when connector | 365 | * drivers must set crtc->mode_changed themselves when connector |
| @@ -485,10 +483,9 @@ static void | |||
| 485 | disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) | 483 | disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) |
| 486 | { | 484 | { |
| 487 | int ncrtcs = old_state->dev->mode_config.num_crtc; | 485 | int ncrtcs = old_state->dev->mode_config.num_crtc; |
| 488 | int nconnectors = old_state->dev->mode_config.num_connector; | ||
| 489 | int i; | 486 | int i; |
| 490 | 487 | ||
| 491 | for (i = 0; i < nconnectors; i++) { | 488 | for (i = 0; i < old_state->num_connector; i++) { |
| 492 | struct drm_connector_state *old_conn_state; | 489 | struct drm_connector_state *old_conn_state; |
| 493 | struct drm_connector *connector; | 490 | struct drm_connector *connector; |
| 494 | struct drm_encoder_helper_funcs *funcs; | 491 | struct drm_encoder_helper_funcs *funcs; |
| @@ -553,12 +550,11 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) | |||
| 553 | static void | 550 | static void |
| 554 | set_routing_links(struct drm_device *dev, struct drm_atomic_state *old_state) | 551 | set_routing_links(struct drm_device *dev, struct drm_atomic_state *old_state) |
| 555 | { | 552 | { |
| 556 | int nconnectors = dev->mode_config.num_connector; | ||
| 557 | int ncrtcs = old_state->dev->mode_config.num_crtc; | 553 | int ncrtcs = old_state->dev->mode_config.num_crtc; |
| 558 | int i; | 554 | int i; |
| 559 | 555 | ||
| 560 | /* clear out existing links */ | 556 | /* clear out existing links */ |
| 561 | for (i = 0; i < nconnectors; i++) { | 557 | for (i = 0; i < old_state->num_connector; i++) { |
| 562 | struct drm_connector *connector; | 558 | struct drm_connector *connector; |
| 563 | 559 | ||
| 564 | connector = old_state->connectors[i]; | 560 | connector = old_state->connectors[i]; |
| @@ -573,7 +569,7 @@ set_routing_links(struct drm_device *dev, struct drm_atomic_state *old_state) | |||
| 573 | } | 569 | } |
| 574 | 570 | ||
| 575 | /* set new links */ | 571 | /* set new links */ |
| 576 | for (i = 0; i < nconnectors; i++) { | 572 | for (i = 0; i < old_state->num_connector; i++) { |
| 577 | struct drm_connector *connector; | 573 | struct drm_connector *connector; |
| 578 | 574 | ||
| 579 | connector = old_state->connectors[i]; | 575 | connector = old_state->connectors[i]; |
| @@ -608,7 +604,6 @@ static void | |||
| 608 | crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) | 604 | crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) |
| 609 | { | 605 | { |
| 610 | int ncrtcs = old_state->dev->mode_config.num_crtc; | ||
