aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Vetter <daniel.vetter@ffwll.ch>2014-11-19 12:38:08 -0500
committerDave Airlie <airlied@redhat.com>2014-11-19 20:35:20 -0500
commitf52b69f1ecfdd7ef6867a257620258c09e569552 (patch)
tree743137d6d11bd5a9ed8bba2484de30c5f630944c
parent6f75cea66c8dd043ced282016b21a639af176642 (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.c26
-rw-r--r--drivers/gpu/drm/drm_atomic_helper.c23
-rw-r--r--include/drm/drm_crtc.h2
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
499drm_atomic_connectors_for_crtc(struct drm_atomic_state *state, 516drm_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
249mode_fixup(struct drm_atomic_state *state) 249mode_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
485disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) 483disable_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)
553static void 550static void
554set_routing_links(struct drm_device *dev, struct drm_atomic_state *old_state) 551set_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
608crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) 604crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
609{ 605{
610 int ncrtcs = old_state->dev->mode_config.num_crtc;