Commit 0ec5f02f authored by Maarten Lankhorst's avatar Maarten Lankhorst Committed by Ben Skeggs

drm/nouveau: prevent stale fence->channel pointers, and protect with rcu

Tested-by: default avatarAlexandre Courbot <acourbot@nvidia.com>
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 19a10828
...@@ -52,20 +52,24 @@ nouveau_fctx(struct nouveau_fence *fence) ...@@ -52,20 +52,24 @@ nouveau_fctx(struct nouveau_fence *fence)
return container_of(fence->base.lock, struct nouveau_fence_chan, lock); return container_of(fence->base.lock, struct nouveau_fence_chan, lock);
} }
static void static int
nouveau_fence_signal(struct nouveau_fence *fence) nouveau_fence_signal(struct nouveau_fence *fence)
{ {
int drop = 0;
fence_signal_locked(&fence->base); fence_signal_locked(&fence->base);
list_del(&fence->head); list_del(&fence->head);
rcu_assign_pointer(fence->channel, NULL);
if (test_bit(FENCE_FLAG_USER_BITS, &fence->base.flags)) { if (test_bit(FENCE_FLAG_USER_BITS, &fence->base.flags)) {
struct nouveau_fence_chan *fctx = nouveau_fctx(fence); struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
if (!--fctx->notify_ref) if (!--fctx->notify_ref)
nvif_notify_put(&fctx->notify); drop = 1;
} }
fence_put(&fence->base); fence_put(&fence->base);
return drop;
} }
static struct nouveau_fence * static struct nouveau_fence *
...@@ -88,16 +92,23 @@ nouveau_fence_context_del(struct nouveau_fence_chan *fctx) ...@@ -88,16 +92,23 @@ nouveau_fence_context_del(struct nouveau_fence_chan *fctx)
{ {
struct nouveau_fence *fence; struct nouveau_fence *fence;
nvif_notify_fini(&fctx->notify);
spin_lock_irq(&fctx->lock); spin_lock_irq(&fctx->lock);
while (!list_empty(&fctx->pending)) { while (!list_empty(&fctx->pending)) {
fence = list_entry(fctx->pending.next, typeof(*fence), head); fence = list_entry(fctx->pending.next, typeof(*fence), head);
nouveau_fence_signal(fence); if (nouveau_fence_signal(fence))
fence->channel = NULL; nvif_notify_put(&fctx->notify);
} }
spin_unlock_irq(&fctx->lock); spin_unlock_irq(&fctx->lock);
nvif_notify_fini(&fctx->notify);
fctx->dead = 1;
/*
* Ensure that all accesses to fence->channel complete before freeing
* the channel.
*/
synchronize_rcu();
} }
static void static void
...@@ -112,21 +123,23 @@ nouveau_fence_context_free(struct nouveau_fence_chan *fctx) ...@@ -112,21 +123,23 @@ nouveau_fence_context_free(struct nouveau_fence_chan *fctx)
kref_put(&fctx->fence_ref, nouveau_fence_context_put); kref_put(&fctx->fence_ref, nouveau_fence_context_put);
} }
static void static int
nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx) nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx)
{ {
struct nouveau_fence *fence; struct nouveau_fence *fence;
int drop = 0;
u32 seq = fctx->read(chan); u32 seq = fctx->read(chan);
while (!list_empty(&fctx->pending)) { while (!list_empty(&fctx->pending)) {
fence = list_entry(fctx->pending.next, typeof(*fence), head); fence = list_entry(fctx->pending.next, typeof(*fence), head);
if ((int)(seq - fence->base.seqno) < 0) if ((int)(seq - fence->base.seqno) < 0)
return; break;
nouveau_fence_signal(fence); drop |= nouveau_fence_signal(fence);
} }
return drop;
} }
static int static int
...@@ -135,18 +148,21 @@ nouveau_fence_wait_uevent_handler(struct nvif_notify *notify) ...@@ -135,18 +148,21 @@ nouveau_fence_wait_uevent_handler(struct nvif_notify *notify)
struct nouveau_fence_chan *fctx = struct nouveau_fence_chan *fctx =
container_of(notify, typeof(*fctx), notify); container_of(notify, typeof(*fctx), notify);
unsigned long flags; unsigned long flags;
int ret = NVIF_NOTIFY_KEEP;
spin_lock_irqsave(&fctx->lock, flags); spin_lock_irqsave(&fctx->lock, flags);
if (!list_empty(&fctx->pending)) { if (!list_empty(&fctx->pending)) {
struct nouveau_fence *fence; struct nouveau_fence *fence;
struct nouveau_channel *chan;
fence = list_entry(fctx->pending.next, typeof(*fence), head); fence = list_entry(fctx->pending.next, typeof(*fence), head);
nouveau_fence_update(fence->channel, fctx); chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock));
if (nouveau_fence_update(fence->channel, fctx))
ret = NVIF_NOTIFY_DROP;
} }
spin_unlock_irqrestore(&fctx->lock, flags); spin_unlock_irqrestore(&fctx->lock, flags);
/* Always return keep here. NVIF refcount is handled with nouveau_fence_update */ return ret;
return NVIF_NOTIFY_KEEP;
} }
void void
...@@ -262,7 +278,10 @@ nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan) ...@@ -262,7 +278,10 @@ nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan)
if (!ret) { if (!ret) {
fence_get(&fence->base); fence_get(&fence->base);
spin_lock_irq(&fctx->lock); spin_lock_irq(&fctx->lock);
nouveau_fence_update(chan, fctx);
if (nouveau_fence_update(chan, fctx))
nvif_notify_put(&fctx->notify);
list_add_tail(&fence->head, &fctx->pending); list_add_tail(&fence->head, &fctx->pending);
spin_unlock_irq(&fctx->lock); spin_unlock_irq(&fctx->lock);
} }
...@@ -276,13 +295,16 @@ nouveau_fence_done(struct nouveau_fence *fence) ...@@ -276,13 +295,16 @@ nouveau_fence_done(struct nouveau_fence *fence)
if (fence->base.ops == &nouveau_fence_ops_legacy || if (fence->base.ops == &nouveau_fence_ops_legacy ||
fence->base.ops == &nouveau_fence_ops_uevent) { fence->base.ops == &nouveau_fence_ops_uevent) {
struct nouveau_fence_chan *fctx = nouveau_fctx(fence); struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
struct nouveau_channel *chan;
unsigned long flags; unsigned long flags;
if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags)) if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags))
return true; return true;
spin_lock_irqsave(&fctx->lock, flags); spin_lock_irqsave(&fctx->lock, flags);
nouveau_fence_update(fence->channel, fctx); chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock));
if (chan && nouveau_fence_update(chan, fctx))
nvif_notify_put(&fctx->notify);
spin_unlock_irqrestore(&fctx->lock, flags); spin_unlock_irqrestore(&fctx->lock, flags);
} }
return fence_is_signaled(&fence->base); return fence_is_signaled(&fence->base);
...@@ -387,12 +409,18 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool e ...@@ -387,12 +409,18 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool e
if (fence && (!exclusive || !fobj || !fobj->shared_count)) { if (fence && (!exclusive || !fobj || !fobj->shared_count)) {
struct nouveau_channel *prev = NULL; struct nouveau_channel *prev = NULL;
bool must_wait = true;
f = nouveau_local_fence(fence, chan->drm); f = nouveau_local_fence(fence, chan->drm);
if (f) if (f) {
prev = f->channel; rcu_read_lock();
prev = rcu_dereference(f->channel);
if (prev && (prev == chan || fctx->sync(f, prev, chan) == 0))
must_wait = false;
rcu_read_unlock();
}
if (!prev || (prev != chan && (ret = fctx->sync(f, prev, chan)))) if (must_wait)
ret = fence_wait(fence, intr); ret = fence_wait(fence, intr);
return ret; return ret;
...@@ -403,19 +431,22 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool e ...@@ -403,19 +431,22 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool e
for (i = 0; i < fobj->shared_count && !ret; ++i) { for (i = 0; i < fobj->shared_count && !ret; ++i) {
struct nouveau_channel *prev = NULL; struct nouveau_channel *prev = NULL;
bool must_wait = true;
fence = rcu_dereference_protected(fobj->shared[i], fence = rcu_dereference_protected(fobj->shared[i],
reservation_object_held(resv)); reservation_object_held(resv));
f = nouveau_local_fence(fence, chan->drm); f = nouveau_local_fence(fence, chan->drm);
if (f) if (f) {
prev = f->channel; rcu_read_lock();
prev = rcu_dereference(f->channel);
if (prev && (prev == chan || fctx->sync(f, prev, chan) == 0))
must_wait = false;
rcu_read_unlock();
}
if (!prev || (prev != chan && (ret = fctx->sync(f, prev, chan)))) if (must_wait)
ret = fence_wait(fence, intr); ret = fence_wait(fence, intr);
if (ret)
break;
} }
return ret; return ret;
...@@ -463,7 +494,7 @@ static const char *nouveau_fence_get_timeline_name(struct fence *f) ...@@ -463,7 +494,7 @@ static const char *nouveau_fence_get_timeline_name(struct fence *f)
struct nouveau_fence *fence = from_fence(f); struct nouveau_fence *fence = from_fence(f);
struct nouveau_fence_chan *fctx = nouveau_fctx(fence); struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
return fence->channel ? fctx->name : "dead channel"; return !fctx->dead ? fctx->name : "dead channel";
} }
/* /*
...@@ -476,9 +507,16 @@ static bool nouveau_fence_is_signaled(struct fence *f) ...@@ -476,9 +507,16 @@ static bool nouveau_fence_is_signaled(struct fence *f)
{ {
struct nouveau_fence *fence = from_fence(f); struct nouveau_fence *fence = from_fence(f);
struct nouveau_fence_chan *fctx = nouveau_fctx(fence); struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
struct nouveau_channel *chan = fence->channel; struct nouveau_channel *chan;
bool ret = false;
rcu_read_lock();
chan = rcu_dereference(fence->channel);
if (chan)
ret = (int)(fctx->read(chan) - fence->base.seqno) >= 0;
rcu_read_unlock();
return (int)(fctx->read(chan) - fence->base.seqno) >= 0; return ret;
} }
static bool nouveau_fence_no_signaling(struct fence *f) static bool nouveau_fence_no_signaling(struct fence *f)
......
...@@ -14,7 +14,7 @@ struct nouveau_fence { ...@@ -14,7 +14,7 @@ struct nouveau_fence {
bool sysmem; bool sysmem;
struct nouveau_channel *channel; struct nouveau_channel __rcu *channel;
unsigned long timeout; unsigned long timeout;
}; };
...@@ -47,7 +47,7 @@ struct nouveau_fence_chan { ...@@ -47,7 +47,7 @@ struct nouveau_fence_chan {
char name[32]; char name[32];
struct nvif_notify notify; struct nvif_notify notify;
int notify_ref; int notify_ref, dead;
}; };
struct nouveau_fence_priv { struct nouveau_fence_priv {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment