From: Jerome Glisse <jglisse@xxxxxxxxxx> Use one wait queue for all rings. When one ring progress, other likely does to and we are not expecting to have a lot of waiter anyway. Also add a fence_wait_any that will wait until the first fence in the fence array (one fence per ring) is signaled. This allow to wait on all rings. Signed-off-by: Christian König <deathsimple@xxxxxxxxxxx> Signed-off-by: Jerome Glisse <jglisse@xxxxxxxxxx> --- drivers/gpu/drm/radeon/radeon.h | 5 +- drivers/gpu/drm/radeon/radeon_fence.c | 151 +++++++++++++++++++++++++++++++- 2 files changed, 150 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index c78f15b..6f8d0f5 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -262,7 +262,6 @@ struct radeon_fence_driver { uint64_t seq; atomic64_t last_seq; unsigned long last_activity; - wait_queue_head_t queue; bool initialized; }; @@ -286,6 +285,9 @@ bool radeon_fence_signaled(struct radeon_fence *fence); int radeon_fence_wait(struct radeon_fence *fence, bool interruptible); int radeon_fence_wait_next(struct radeon_device *rdev, int ring); int radeon_fence_wait_empty(struct radeon_device *rdev, int ring); +int radeon_fence_wait_any(struct radeon_device *rdev, + struct radeon_fence **fences, + bool intr); struct radeon_fence *radeon_fence_ref(struct radeon_fence *fence); void radeon_fence_unref(struct radeon_fence **fence); unsigned radeon_fence_count_emitted(struct radeon_device *rdev, int ring); @@ -1516,6 +1518,7 @@ struct radeon_device { struct radeon_scratch scratch; struct radeon_mman mman; struct radeon_fence_driver fence_drv[RADEON_NUM_RINGS]; + wait_queue_head_t fence_queue; struct radeon_semaphore_driver semaphore_drv; struct mutex ring_lock; struct radeon_ring ring[RADEON_NUM_RINGS]; diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c index 99c31b2..9c2c1b3 100644 --- a/drivers/gpu/drm/radeon/radeon_fence.c +++ b/drivers/gpu/drm/radeon/radeon_fence.c @@ -233,11 +233,11 @@ static int radeon_wait_seq(struct radeon_device *rdev, u64 target_seq, trace_radeon_fence_wait_begin(rdev->ddev, seq); radeon_irq_kms_sw_irq_get(rdev, ring); if (intr) { - r = wait_event_interruptible_timeout(rdev->fence_drv[ring].queue, + r = wait_event_interruptible_timeout(rdev->fence_queue, (signaled = radeon_seq_signaled(rdev, target_seq, ring)), timeout); } else { - r = wait_event_timeout(rdev->fence_drv[ring].queue, + r = wait_event_timeout(rdev->fence_queue, (signaled = radeon_seq_signaled(rdev, target_seq, ring)), timeout); } @@ -304,6 +304,147 @@ int radeon_fence_wait(struct radeon_fence *fence, bool intr) return 0; } +bool radeon_seq_any_signaled(struct radeon_device *rdev, u64 *seq) +{ + unsigned i, j; + + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + /* poll new last sequence at least once */ + for (j = 0; j < 2; j++) { + if (atomic64_read(&rdev->fence_drv[i].last_seq) >= seq[i]) { + return true; + } + radeon_fence_poll(rdev, i); + } + } + return false; +} + +static int radeon_wait_seq_any(struct radeon_device *rdev, u64 *target_seq, + bool intr) +{ + unsigned long timeout, last_activity, tmp; + unsigned i, ring = 0; + bool signaled; + int r; + + /* use the most recent one as indicator */ + for (i = 0, last_activity = 0; i < RADEON_NUM_RINGS; ++i) { + if (time_after(rdev->fence_drv[i].last_activity, last_activity)) { + last_activity = rdev->fence_drv[i].last_activity; + } + /* For lockup detection we just pick one of the ring we are + * actively waiting for + */ + if (target_seq[i]) { + ring = i; + } + } + while (!radeon_seq_any_signaled(rdev, target_seq)) { + timeout = jiffies - RADEON_FENCE_JIFFIES_TIMEOUT; + if (time_after(last_activity, timeout)) { + /* the normal case, timeout is somewhere before last_activity */ + timeout = last_activity - timeout; + } else { + /* either jiffies wrapped around, or no fence was signaled in the last 500ms + * anyway we will just wait for the minimum amount and then check for a lockup + */ + timeout = 1; + } + + trace_radeon_fence_wait_begin(rdev->ddev, target_seq[ring]); + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + if (target_seq[i]) { + radeon_irq_kms_sw_irq_get(rdev, i); + } + } + if (intr) { + r = wait_event_interruptible_timeout(rdev->fence_queue, + (signaled = radeon_seq_any_signaled(rdev, target_seq)), + timeout); + } else { + r = wait_event_timeout(rdev->fence_queue, + (signaled = radeon_seq_any_signaled(rdev, target_seq)), + timeout); + } + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + if (target_seq[i]) { + radeon_irq_kms_sw_irq_put(rdev, i); + } + } + if (unlikely(r < 0)) { + return r; + } + trace_radeon_fence_wait_end(rdev->ddev, target_seq[ring]); + + if (unlikely(!signaled)) { + /* we were interrupted for some reason and fence + * isn't signaled yet, resume waiting */ + if (r) { + continue; + } + + mutex_lock(&rdev->ring_lock); + for (i = 0, tmp = 0; i < RADEON_NUM_RINGS; ++i) { + if (time_after(rdev->fence_drv[i].last_activity, tmp)) { + tmp = rdev->fence_drv[i].last_activity; + } + } + /* test if somebody else has already decided that this is a lockup */ + if (last_activity != tmp) { + last_activity = tmp; + mutex_unlock(&rdev->ring_lock); + continue; + } + + if (radeon_ring_is_lockup(rdev, ring, &rdev->ring[ring])) { + /* good news we believe it's a lockup */ + dev_warn(rdev->dev, "GPU lockup (waiting for 0x%016llx)\n", + target_seq[ring]); + + /* change last activity so nobody else think there is a lockup */ + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + rdev->fence_drv[i].last_activity = jiffies; + } + + /* mark the ring as not ready any more */ + rdev->ring[ring].ready = false; + mutex_unlock(&rdev->ring_lock); + return -EDEADLK; + } + mutex_unlock(&rdev->ring_lock); + } + } + return 0; +} + +int radeon_fence_wait_any(struct radeon_device *rdev, + struct radeon_fence **fences, + bool intr) +{ + uint64_t seq[RADEON_NUM_RINGS]; + unsigned i, c; + int r; + + for (i = 0, c = 0; i < RADEON_NUM_RINGS; ++i) { + seq[i] = 0; + if (fences[i] && fences[i]->seq < RADEON_FENCE_NOTEMITED_SEQ) { + seq[i] = fences[i]->seq; + c++; + } + } + if (!c) { + /* nothing to wait for */ + return 0; + } + + r = radeon_wait_seq_any(rdev, seq, intr); + if (r) { + return r; + } + return 0; +} + int radeon_fence_wait_next(struct radeon_device *rdev, int ring) { uint64_t seq; @@ -352,7 +493,7 @@ void radeon_fence_process(struct radeon_device *rdev, int ring) wake = radeon_fence_poll(rdev, ring); if (wake) { - wake_up_all(&rdev->fence_drv[ring].queue); + wake_up_all(&rdev->fence_queue); } } @@ -409,7 +550,6 @@ static void radeon_fence_driver_init_ring(struct radeon_device *rdev, int ring) rdev->fence_drv[ring].seq = 1; atomic64_set(&rdev->fence_drv[ring].last_seq, 0); rdev->fence_drv[ring].last_activity = jiffies; - init_waitqueue_head(&rdev->fence_drv[ring].queue); rdev->fence_drv[ring].initialized = false; } @@ -417,6 +557,7 @@ int radeon_fence_driver_init(struct radeon_device *rdev) { int ring; + init_waitqueue_head(&rdev->fence_queue); for (ring = 0; ring < RADEON_NUM_RINGS; ring++) { radeon_fence_driver_init_ring(rdev, ring); } @@ -434,7 +575,7 @@ void radeon_fence_driver_fini(struct radeon_device *rdev) if (!rdev->fence_drv[ring].initialized) continue; radeon_fence_wait_empty(rdev, ring); - wake_up_all(&rdev->fence_drv[ring].queue); + wake_up_all(&rdev->fence_queue); radeon_scratch_free(rdev, rdev->fence_drv[ring].scratch_reg); rdev->fence_drv[ring].initialized = false; } -- 1.7.7.6 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel