diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2013-07-08 00:15:51 -0400 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2013-07-09 20:47:12 -0400 |
commit | 060810d7abaabcab282e062c595871d661561400 (patch) | |
tree | d27009af2a9255c10eeccf3f72246515dca11b38 | |
parent | 06d5a24f08831e167fae42a64ef2083a89f8e617 (diff) |
drm/nouveau: fix locking issues in page flipping paths
b580c9e2b7ba5030a795aa2fb73b796523d65a78 introduced additional problems
while trying to solve issues that became apparent while porting to the
new reservation stuff.
The major problem was that the the previously mentioned patch took the
client mutex earlier than previously, but the pinning of new_bo can
can potentially cause a buffer move, which would result in attempting to
acquire the same mutex again.
This commit attempts to fix that "fix".
Thanks to Maarten for the tips on keeping lockdep happy and cooking :)
Reported-by: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Acked-by: Maarten Lankhorst <maarten.lankhorst@canonical.com>
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_bo.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_display.c | 53 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drm.c | 3 |
3 files changed, 22 insertions, 36 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 4b1afb131380..85fed108d7e4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c | |||
@@ -973,7 +973,7 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr, | |||
973 | struct ttm_mem_reg *old_mem = &bo->mem; | 973 | struct ttm_mem_reg *old_mem = &bo->mem; |
974 | int ret; | 974 | int ret; |
975 | 975 | ||
976 | mutex_lock(&chan->cli->mutex); | 976 | mutex_lock_nested(&chan->cli->mutex, SINGLE_DEPTH_NESTING); |
977 | 977 | ||
978 | /* create temporary vmas for the transfer and attach them to the | 978 | /* create temporary vmas for the transfer and attach them to the |
979 | * old nouveau_mem node, these will get cleaned up after ttm has | 979 | * old nouveau_mem node, these will get cleaned up after ttm has |
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 708b2d1c0037..61fdef8eac49 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c | |||
@@ -524,9 +524,12 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, | |||
524 | struct nouveau_page_flip_state *s; | 524 | struct nouveau_page_flip_state *s; |
525 | struct nouveau_channel *chan = NULL; | 525 | struct nouveau_channel *chan = NULL; |
526 | struct nouveau_fence *fence; | 526 | struct nouveau_fence *fence; |
527 | struct list_head res; | 527 | struct ttm_validate_buffer resv[2] = { |
528 | struct ttm_validate_buffer res_val[2]; | 528 | { .bo = &old_bo->bo }, |
529 | { .bo = &new_bo->bo }, | ||
530 | }; | ||
529 | struct ww_acquire_ctx ticket; | 531 | struct ww_acquire_ctx ticket; |
532 | LIST_HEAD(res); | ||
530 | int ret; | 533 | int ret; |
531 | 534 | ||
532 | if (!drm->channel) | 535 | if (!drm->channel) |
@@ -545,27 +548,19 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, | |||
545 | chan = drm->channel; | 548 | chan = drm->channel; |
546 | spin_unlock(&old_bo->bo.bdev->fence_lock); | 549 | spin_unlock(&old_bo->bo.bdev->fence_lock); |
547 | 550 | ||
548 | mutex_lock(&chan->cli->mutex); | ||
549 | |||
550 | if (new_bo != old_bo) { | 551 | if (new_bo != old_bo) { |
551 | ret = nouveau_bo_pin(new_bo, TTM_PL_FLAG_VRAM); | 552 | ret = nouveau_bo_pin(new_bo, TTM_PL_FLAG_VRAM); |
552 | if (likely(!ret)) { | 553 | if (ret) |
553 | res_val[0].bo = &old_bo->bo; | 554 | goto fail_free; |
554 | res_val[1].bo = &new_bo->bo; | ||
555 | INIT_LIST_HEAD(&res); | ||
556 | list_add_tail(&res_val[0].head, &res); | ||
557 | list_add_tail(&res_val[1].head, &res); | ||
558 | ret = ttm_eu_reserve_buffers(&ticket, &res); | ||
559 | if (ret) | ||
560 | nouveau_bo_unpin(new_bo); | ||
561 | } | ||
562 | } else | ||
563 | ret = ttm_bo_reserve(&new_bo->bo, false, false, false, 0); | ||
564 | 555 | ||
565 | if (ret) { | 556 | list_add(&resv[1].head, &res); |
566 | mutex_unlock(&chan->cli->mutex); | ||
567 | goto fail_free; | ||
568 | } | 557 | } |
558 | list_add(&resv[0].head, &res); | ||
559 | |||
560 | mutex_lock(&chan->cli->mutex); | ||
561 | ret = ttm_eu_reserve_buffers(&ticket, &res); | ||
562 | if (ret) | ||
563 | goto fail_unpin; | ||
569 | 564 | ||
570 | /* Initialize a page flip struct */ | 565 | /* Initialize a page flip struct */ |
571 | *s = (struct nouveau_page_flip_state) | 566 | *s = (struct nouveau_page_flip_state) |
@@ -576,10 +571,8 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, | |||
576 | /* Emit a page flip */ | 571 | /* Emit a page flip */ |
577 | if (nv_device(drm->device)->card_type >= NV_50) { | 572 | if (nv_device(drm->device)->card_type >= NV_50) { |
578 | ret = nv50_display_flip_next(crtc, fb, chan, 0); | 573 | ret = nv50_display_flip_next(crtc, fb, chan, 0); |
579 | if (ret) { | 574 | if (ret) |
580 | mutex_unlock(&chan->cli->mutex); | ||
581 | goto fail_unreserve; | 575 | goto fail_unreserve; |
582 | } | ||
583 | } | 576 | } |
584 | 577 | ||
585 | ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence); | 578 | ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence); |
@@ -590,22 +583,18 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, | |||
590 | /* Update the crtc struct and cleanup */ | 583 | /* Update the crtc struct and cleanup */ |
591 | crtc->fb = fb; | 584 | crtc->fb = fb; |
592 | 585 | ||
593 | if (old_bo != new_bo) { | 586 | ttm_eu_fence_buffer_objects(&ticket, &res, fence); |
594 | ttm_eu_fence_buffer_objects(&ticket, &res, fence); | 587 | if (old_bo != new_bo) |
595 | nouveau_bo_unpin(old_bo); | 588 | nouveau_bo_unpin(old_bo); |
596 | } else { | ||
597 | nouveau_bo_fence(new_bo, fence); | ||
598 | ttm_bo_unreserve(&new_bo->bo); | ||
599 | } | ||
600 | nouveau_fence_unref(&fence); | 589 | nouveau_fence_unref(&fence); |
601 | return 0; | 590 | return 0; |
602 | 591 | ||
603 | fail_unreserve: | 592 | fail_unreserve: |
604 | if (old_bo != new_bo) { | 593 | ttm_eu_backoff_reservation(&ticket, &res); |
605 | ttm_eu_backoff_reservation(&ticket, &res); | 594 | fail_unpin: |
595 | mutex_unlock(&chan->cli->mutex); | ||
596 | if (old_bo != new_bo) | ||
606 | nouveau_bo_unpin(new_bo); | 597 | nouveau_bo_unpin(new_bo); |
607 | } else | ||
608 | ttm_bo_unreserve(&new_bo->bo); | ||
609 | fail_free: | 598 | fail_free: |
610 | kfree(s); | 599 | kfree(s); |
611 | return ret; | 600 | return ret; |
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 218a4b522fe5..4eca52b8d573 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c | |||
@@ -284,8 +284,6 @@ static int nouveau_drm_probe(struct pci_dev *pdev, | |||
284 | return 0; | 284 | return 0; |
285 | } | 285 | } |
286 | 286 | ||
287 | static struct lock_class_key drm_client_lock_class_key; | ||
288 | |||
289 | static int | 287 | static int |
290 | nouveau_drm_load(struct drm_device *dev, unsigned long flags) | 288 | nouveau_drm_load(struct drm_device *dev, unsigned long flags) |
291 | { | 289 | { |
@@ -297,7 +295,6 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags) | |||
297 | ret = nouveau_cli_create(pdev, "DRM", sizeof(*drm), (void**)&drm); | 295 | ret = nouveau_cli_create(pdev, "DRM", sizeof(*drm), (void**)&drm); |
298 | if (ret) | 296 | if (ret) |
299 | return ret; | 297 | return ret; |
300 | lockdep_set_class(&drm->client.mutex, &drm_client_lock_class_key); | ||
301 | 298 | ||
302 | dev->dev_private = drm; | 299 | dev->dev_private = drm; |
303 | drm->dev = dev; | 300 | drm->dev = dev; |