diff options
| -rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_fence.c | 92 | ||||
| -rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_fence.h | 4 |
2 files changed, 67 insertions, 29 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 515cd9aebb99..f32a434724e3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c | |||
| @@ -52,20 +52,24 @@ nouveau_fctx(struct nouveau_fence *fence) | |||
| 52 | return container_of(fence->base.lock, struct nouveau_fence_chan, lock); | 52 | return container_of(fence->base.lock, struct nouveau_fence_chan, lock); |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | static void | 55 | static int |
| 56 | nouveau_fence_signal(struct nouveau_fence *fence) | 56 | nouveau_fence_signal(struct nouveau_fence *fence) |
| 57 | { | 57 | { |
| 58 | int drop = 0; | ||
| 59 | |||
| 58 | fence_signal_locked(&fence->base); | 60 | fence_signal_locked(&fence->base); |
| 59 | list_del(&fence->head); | 61 | list_del(&fence->head); |
| 62 | rcu_assign_pointer(fence->channel, NULL); | ||
| 60 | 63 | ||
| 61 | if (test_bit(FENCE_FLAG_USER_BITS, &fence->base.flags)) { | 64 | if (test_bit(FENCE_FLAG_USER_BITS, &fence->base.flags)) { |
| 62 | struct nouveau_fence_chan *fctx = nouveau_fctx(fence); | 65 | struct nouveau_fence_chan *fctx = nouveau_fctx(fence); |
| 63 | 66 | ||
| 64 | if (!--fctx->notify_ref) | 67 | if (!--fctx->notify_ref) |
| 65 | nvif_notify_put(&fctx->notify); | 68 | drop = 1; |
| 66 | } | 69 | } |
| 67 | 70 | ||
| 68 | fence_put(&fence->base); | 71 | fence_put(&fence->base); |
| 72 | return drop; | ||
| 69 | } | 73 | } |
| 70 | 74 | ||
| 71 | static struct nouveau_fence * | 75 | static struct nouveau_fence * |
| @@ -88,16 +92,23 @@ nouveau_fence_context_del(struct nouveau_fence_chan *fctx) | |||
| 88 | { | 92 | { |
| 89 | struct nouveau_fence *fence; | 93 | struct nouveau_fence *fence; |
| 90 | 94 | ||
| 91 | nvif_notify_fini(&fctx->notify); | ||
| 92 | |||
| 93 | spin_lock_irq(&fctx->lock); | 95 | spin_lock_irq(&fctx->lock); |
| 94 | while (!list_empty(&fctx->pending)) { | 96 | while (!list_empty(&fctx->pending)) { |
| 95 | fence = list_entry(fctx->pending.next, typeof(*fence), head); | 97 | fence = list_entry(fctx->pending.next, typeof(*fence), head); |
| 96 | 98 | ||
| 97 | nouveau_fence_signal(fence); | 99 | if (nouveau_fence_signal(fence)) |
| 98 | fence->channel = NULL; | 100 | nvif_notify_put(&fctx->notify); |
| 99 | } | 101 | } |
| 100 | spin_unlock_irq(&fctx->lock); | 102 | spin_unlock_irq(&fctx->lock); |
| 103 | |||
| 104 | nvif_notify_fini(&fctx->notify); | ||
| 105 | fctx->dead = 1; | ||
| 106 | |||
| 107 | /* | ||
| 108 | * Ensure that all accesses to fence->channel complete before freeing | ||
| 109 | * the channel. | ||
| 110 | */ | ||
| 111 | synchronize_rcu(); | ||
| 101 | } | 112 | } |
| 102 | 113 | ||
| 103 | static void | 114 | static void |
| @@ -112,21 +123,23 @@ nouveau_fence_context_free(struct nouveau_fence_chan *fctx) | |||
| 112 | kref_put(&fctx->fence_ref, nouveau_fence_context_put); | 123 | kref_put(&fctx->fence_ref, nouveau_fence_context_put); |
| 113 | } | 124 | } |
| 114 | 125 | ||
| 115 | static void | 126 | static int |
| 116 | nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx) | 127 | nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx) |
| 117 | { | 128 | { |
| 118 | struct nouveau_fence *fence; | 129 | struct nouveau_fence *fence; |
| 119 | 130 | int drop = 0; | |
| 120 | u32 seq = fctx->read(chan); | 131 | u32 seq = fctx->read(chan); |
| 121 | 132 | ||
| 122 | while (!list_empty(&fctx->pending)) { | 133 | while (!list_empty(&fctx->pending)) { |
| 123 | fence = list_entry(fctx->pending.next, typeof(*fence), head); | 134 | fence = list_entry(fctx->pending.next, typeof(*fence), head); |
| 124 | 135 | ||
| 125 | if ((int)(seq - fence->base.seqno) < 0) | 136 | if ((int)(seq - fence->base.seqno) < 0) |
| 126 | return; | 137 | break; |
| 127 | 138 | ||
| 128 | nouveau_fence_signal(fence); | 139 | drop |= nouveau_fence_signal(fence); |
| 129 | } | 140 | } |
| 141 | |||
| 142 | return drop; | ||
| 130 | } | 143 | } |
| 131 | 144 | ||
| 132 | static int | 145 | static int |
| @@ -135,18 +148,21 @@ nouveau_fence_wait_uevent_handler(struct nvif_notify *notify) | |||
| 135 | struct nouveau_fence_chan *fctx = | 148 | struct nouveau_fence_chan *fctx = |
| 136 | container_of(notify, typeof(*fctx), notify); | 149 | container_of(notify, typeof(*fctx), notify); |
| 137 | unsigned long flags; | 150 | unsigned long flags; |
| 151 | int ret = NVIF_NOTIFY_KEEP; | ||
| 138 | 152 | ||
| 139 | spin_lock_irqsave(&fctx->lock, flags); | 153 | spin_lock_irqsave(&fctx->lock, flags); |
| 140 | if (!list_empty(&fctx->pending)) { | 154 | if (!list_empty(&fctx->pending)) { |
| 141 | struct nouveau_fence *fence; | 155 | struct nouveau_fence *fence; |
| 156 | struct nouveau_channel *chan; | ||
| 142 | 157 | ||
| 143 | fence = list_entry(fctx->pending.next, typeof(*fence), head); | 158 | fence = list_entry(fctx->pending.next, typeof(*fence), head); |
| 144 | nouveau_fence_update(fence->channel, fctx); | 159 | chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); |
| 160 | if (nouveau_fence_update(fence->channel, fctx)) | ||
| 161 | ret = NVIF_NOTIFY_DROP; | ||
| 145 | } | 162 | } |
| 146 | spin_unlock_irqrestore(&fctx->lock, flags); | 163 | spin_unlock_irqrestore(&fctx->lock, flags); |
| 147 | 164 | ||
| 148 | /* Always return keep here. NVIF refcount is handled with nouveau_fence_update */ | 165 | return ret; |
| 149 | return NVIF_NOTIFY_KEEP; | ||
| 150 | } | 166 | } |
| 151 | 167 | ||
| 152 | void | 168 | void |
| @@ -262,7 +278,10 @@ nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan) | |||
| 262 | if (!ret) { | 278 | if (!ret) { |
| 263 | fence_get(&fence->base); | 279 | fence_get(&fence->base); |
| 264 | spin_lock_irq(&fctx->lock); | 280 | spin_lock_irq(&fctx->lock); |
| 265 | nouveau_fence_update(chan, fctx); | 281 | |
| 282 | if (nouveau_fence_update(chan, fctx)) | ||
| 283 | nvif_notify_put(&fctx->notify); | ||
| 284 | |||
| 266 | list_add_tail(&fence->head, &fctx->pending); | 285 | list_add_tail(&fence->head, &fctx->pending); |
| 267 | spin_unlock_irq(&fctx->lock); | 286 | spin_unlock_irq(&fctx->lock); |
| 268 | } | 287 | } |
| @@ -276,13 +295,16 @@ nouveau_fence_done(struct nouveau_fence *fence) | |||
| 276 | if (fence->base.ops == &nouveau_fence_ops_legacy || | 295 | if (fence->base.ops == &nouveau_fence_ops_legacy || |
| 277 | fence->base.ops == &nouveau_fence_ops_uevent) { | 296 | fence->base.ops == &nouveau_fence_ops_uevent) { |
| 278 | struct nouveau_fence_chan *fctx = nouveau_fctx(fence); | 297 | struct nouveau_fence_chan *fctx = nouveau_fctx(fence); |
| 298 | struct nouveau_channel *chan; | ||
| 279 | unsigned long flags; | 299 | unsigned long flags; |
| 280 | 300 | ||
| 281 | if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags)) | 301 | if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags)) |
| 282 | return true; | 302 | return true; |
| 283 | 303 | ||
| 284 | spin_lock_irqsave(&fctx->lock, flags); | 304 | spin_lock_irqsave(&fctx->lock, flags); |
| 285 | nouveau_fence_update(fence->channel, fctx); | 305 | chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); |
| 306 | if (chan && nouveau_fence_update(chan, fctx)) | ||
| 307 | nvif_notify_put(&fctx->notify); | ||
| 286 | spin_unlock_irqrestore(&fctx->lock, flags); | 308 | spin_unlock_irqrestore(&fctx->lock, flags); |
| 287 | } | 309 | } |
| 288 | return fence_is_signaled(&fence->base); | 310 | return fence_is_signaled(&fence->base); |
| @@ -387,12 +409,18 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool e | |||
| 387 | 409 | ||
| 388 | if (fence && (!exclusive || !fobj || !fobj->shared_count)) { | 410 | if (fence && (!exclusive || !fobj || !fobj->shared_count)) { |
| 389 | struct nouveau_channel *prev = NULL; | 411 | struct nouveau_channel *prev = NULL; |
| 412 | bool must_wait = true; | ||
| 390 | 413 | ||
| 391 | f = nouveau_local_fence(fence, chan->drm); | 414 | f = nouveau_local_fence(fence, chan->drm); |
| 392 | if (f) | 415 | if (f) { |
| 393 | prev = f->channel; | 416 | rcu_read_lock(); |
| 417 | prev = rcu_dereference(f->channel); | ||
| 418 | if (prev && (prev == chan || fctx->sync(f, prev, chan) == 0)) | ||
| 419 | must_wait = false; | ||
| 420 | rcu_read_unlock(); | ||
| 421 | } | ||
| 394 | 422 | ||
| 395 | if (!prev || (prev != chan && (ret = fctx->sync(f, prev, chan)))) | 423 | if (must_wait) |
| 396 | ret = fence_wait(fence, intr); | 424 | ret = fence_wait(fence, intr); |
| 397 | 425 | ||
| 398 | return ret; | 426 | return ret; |
| @@ -403,19 +431,22 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool e | |||
| 403 | 431 | ||
| 404 | for (i = 0; i < fobj->shared_count && !ret; ++i) { | 432 | for (i = 0; i < fobj->shared_count && !ret; ++i) { |
| 405 | struct nouveau_channel *prev = NULL; | 433 | struct nouveau_channel *prev = NULL; |
| 434 | bool must_wait = true; | ||
| 406 | 435 | ||
| 407 | fence = rcu_dereference_protected(fobj->shared[i], | 436 | fence = rcu_dereference_protected(fobj->shared[i], |
| 408 | reservation_object_held(resv)); | 437 | reservation_object_held(resv)); |
| 409 | 438 | ||
| 410 | f = nouveau_local_fence(fence, chan->drm); | 439 | f = nouveau_local_fence(fence, chan->drm); |
| 411 | if (f) | 440 | if (f) { |
| 412 | prev = f->channel; | 441 | rcu_read_lock(); |
| 442 | prev = rcu_dereference(f->channel); | ||
| 443 | if (prev && (prev == chan || fctx->sync(f, prev, chan) == 0)) | ||
| 444 | must_wait = false; | ||
| 445 | rcu_read_unlock(); | ||
| 446 | } | ||
| 413 | 447 | ||
| 414 | if (!prev || (prev != chan && (ret = fctx->sync(f, prev, chan)))) | 448 | if (must_wait) |
| 415 | ret = fence_wait(fence, intr); | 449 | ret = fence_wait(fence, intr); |
| 416 | |||
| 417 | if (ret) | ||
| 418 | break; | ||
| 419 | } | 450 | } |
| 420 | 451 | ||
| 421 | return ret; | 452 | return ret; |
| @@ -463,7 +494,7 @@ static const char *nouveau_fence_get_timeline_name(struct fence *f) | |||
| 463 | struct nouveau_fence *fence = from_fence(f); | 494 | struct nouveau_fence *fence = from_fence(f); |
| 464 | struct nouveau_fence_chan *fctx = nouveau_fctx(fence); | 495 | struct nouveau_fence_chan *fctx = nouveau_fctx(fence); |
| 465 | 496 | ||
| 466 | return fence->channel ? fctx->name : "dead channel"; | 497 | return !fctx->dead ? fctx->name : "dead channel"; |
| 467 | } | 498 | } |
| 468 | 499 | ||
| 469 | /* | 500 | /* |
| @@ -476,9 +507,16 @@ static bool nouveau_fence_is_signaled(struct fence *f) | |||
| 476 | { | 507 | { |
| 477 | struct nouveau_fence *fence = from_fence(f); | 508 | struct nouveau_fence *fence = from_fence(f); |
| 478 | struct nouveau_fence_chan *fctx = nouveau_fctx(fence); | 509 | struct nouveau_fence_chan *fctx = nouveau_fctx(fence); |
| 479 | struct nouveau_channel *chan = fence->channel; | 510 | struct nouveau_channel *chan; |
| 511 | bool ret = false; | ||
| 512 | |||
| 513 | rcu_read_lock(); | ||
| 514 | chan = rcu_dereference(fence->channel); | ||
| 515 | if (chan) | ||
| 516 | ret = (int)(fctx->read(chan) - fence->base.seqno) >= 0; | ||
| 517 | rcu_read_unlock(); | ||
| 480 | 518 | ||
| 481 | return (int)(fctx->read(chan) - fence->base.seqno) >= 0; | 519 | return ret; |
| 482 | } | 520 | } |
| 483 | 521 | ||
| 484 | static bool nouveau_fence_no_signaling(struct fence *f) | 522 | static bool nouveau_fence_no_signaling(struct fence *f) |
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.h b/drivers/gpu/drm/nouveau/nouveau_fence.h index 943b0b17b1fc..96e461c6f68f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.h +++ b/drivers/gpu/drm/nouveau/nouveau_fence.h | |||
| @@ -14,7 +14,7 @@ struct nouveau_fence { | |||
| 14 | 14 | ||
| 15 | bool sysmem; | 15 | bool sysmem; |
| 16 | 16 | ||
| 17 | struct nouveau_channel *channel; | 17 | struct nouveau_channel __rcu *channel; |
| 18 | unsigned long timeout; | 18 | unsigned long timeout; |
| 19 | }; | 19 | }; |
| 20 | 20 | ||
| @@ -47,7 +47,7 @@ struct nouveau_fence_chan { | |||
| 47 | char name[32]; | 47 | char name[32]; |
| 48 | 48 | ||
| 49 | struct nvif_notify notify; | 49 | struct nvif_notify notify; |
| 50 | int notify_ref; | 50 | int notify_ref, dead; |
| 51 | }; | 51 | }; |
| 52 | 52 | ||
| 53 | struct nouveau_fence_priv { | 53 | struct nouveau_fence_priv { |
