|
@@ -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)
|
|
{
|
|
{
|
|
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);
|
|
|
|
- fence->channel = NULL;
|
|
|
|
|
|
+ if (nouveau_fence_signal(fence))
|
|
|
|
+ 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)
|
|
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)
|
|
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 NVIF_NOTIFY_KEEP;
|
|
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
void
|
|
@@ -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)
|
|
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
|
|
|
|
|
|
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)
|
|
|
|
- prev = f->channel;
|
|
|
|
|
|
+ if (f) {
|
|
|
|
+ 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
|
|
|
|
|
|
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)
|
|
|
|
- prev = f->channel;
|
|
|
|
|
|
+ if (f) {
|
|
|
|
+ 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)
|
|
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)
|
|
{
|
|
{
|
|
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)
|