]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/gpu/drm/radeon/radeon_fence.c
Merge branch 'linus' into perf/urgent
[karo-tx-linux.git] / drivers / gpu / drm / radeon / radeon_fence.c
index 5bb78bf547ea6eed5aa4d46543f9e524b0700631..11f5f402d22cb331deed24dcb3b671e92a54892e 100644 (file)
@@ -63,75 +63,82 @@ static u32 radeon_fence_read(struct radeon_device *rdev, int ring)
 
 int radeon_fence_emit(struct radeon_device *rdev, struct radeon_fence *fence)
 {
-       unsigned long irq_flags;
-
-       write_lock_irqsave(&rdev->fence_lock, irq_flags);
-       if (fence->emitted) {
-               write_unlock_irqrestore(&rdev->fence_lock, irq_flags);
+       /* we are protected by the ring emission mutex */
+       if (fence->seq && fence->seq < RADEON_FENCE_NOTEMITED_SEQ) {
                return 0;
        }
-       fence->seq = atomic_add_return(1, &rdev->fence_drv[fence->ring].seq);
+       fence->seq = ++rdev->fence_drv[fence->ring].seq;
        radeon_fence_ring_emit(rdev, fence->ring, fence);
        trace_radeon_fence_emit(rdev->ddev, fence->seq);
-       fence->emitted = true;
-       /* are we the first fence on a previusly idle ring? */
-       if (list_empty(&rdev->fence_drv[fence->ring].emitted)) {
-               rdev->fence_drv[fence->ring].last_activity = jiffies;
-       }
-       list_move_tail(&fence->list, &rdev->fence_drv[fence->ring].emitted);
-       write_unlock_irqrestore(&rdev->fence_lock, irq_flags);
        return 0;
 }
 
-static bool radeon_fence_poll_locked(struct radeon_device *rdev, int ring)
+void radeon_fence_process(struct radeon_device *rdev, int ring)
 {
-       struct radeon_fence *fence;
-       struct list_head *i, *n;
-       uint32_t seq;
+       uint64_t seq, last_seq;
+       unsigned count_loop = 0;
        bool wake = false;
 
-       seq = radeon_fence_read(rdev, ring);
-       if (seq == rdev->fence_drv[ring].last_seq)
-               return false;
-
-       rdev->fence_drv[ring].last_seq = seq;
-       rdev->fence_drv[ring].last_activity = jiffies;
+       /* Note there is a scenario here for an infinite loop but it's
+        * very unlikely to happen. For it to happen, the current polling
+        * process need to be interrupted by another process and another
+        * process needs to update the last_seq btw the atomic read and
+        * xchg of the current process.
+        *
+        * More over for this to go in infinite loop there need to be
+        * continuously new fence signaled ie radeon_fence_read needs
+        * to return a different value each time for both the currently
+        * polling process and the other process that xchg the last_seq
+        * btw atomic read and xchg of the current process. And the
+        * value the other process set as last seq must be higher than
+        * the seq value we just read. Which means that current process
+        * need to be interrupted after radeon_fence_read and before
+        * atomic xchg.
+        *
+        * To be even more safe we count the number of time we loop and
+        * we bail after 10 loop just accepting the fact that we might
+        * have temporarly set the last_seq not to the true real last
+        * seq but to an older one.
+        */
+       last_seq = atomic64_read(&rdev->fence_drv[ring].last_seq);
+       do {
+               seq = radeon_fence_read(rdev, ring);
+               seq |= last_seq & 0xffffffff00000000LL;
+               if (seq < last_seq) {
+                       seq += 0x100000000LL;
+               }
 
-       n = NULL;
-       list_for_each(i, &rdev->fence_drv[ring].emitted) {
-               fence = list_entry(i, struct radeon_fence, list);
-               if (fence->seq == seq) {
-                       n = i;
+               if (seq == last_seq) {
                        break;
                }
-       }
-       /* all fence previous to this one are considered as signaled */
-       if (n) {
-               i = n;
-               do {
-                       n = i->prev;
-                       list_move_tail(i, &rdev->fence_drv[ring].signaled);
-                       fence = list_entry(i, struct radeon_fence, list);
-                       fence->signaled = true;
-                       i = n;
-               } while (i != &rdev->fence_drv[ring].emitted);
+               /* If we loop over we don't want to return without
+                * checking if a fence is signaled as it means that the
+                * seq we just read is different from the previous on.
+                */
                wake = true;
+               last_seq = seq;
+               if ((count_loop++) > 10) {
+                       /* We looped over too many time leave with the
+                        * fact that we might have set an older fence
+                        * seq then the current real last seq as signaled
+                        * by the hw.
+                        */
+                       break;
+               }
+       } while (atomic64_xchg(&rdev->fence_drv[ring].last_seq, seq) > seq);
+
+       if (wake) {
+               rdev->fence_drv[ring].last_activity = jiffies;
+               wake_up_all(&rdev->fence_queue);
        }
-       return wake;
 }
 
 static void radeon_fence_destroy(struct kref *kref)
 {
-       unsigned long irq_flags;
-        struct radeon_fence *fence;
+       struct radeon_fence *fence;
 
        fence = container_of(kref, struct radeon_fence, kref);
-       write_lock_irqsave(&fence->rdev->fence_lock, irq_flags);
-       list_del(&fence->list);
-       fence->emitted = false;
-       write_unlock_irqrestore(&fence->rdev->fence_lock, irq_flags);
-       if (fence->semaphore)
-               radeon_semaphore_free(fence->rdev, fence->semaphore);
+       fence->seq = RADEON_FENCE_NOTEMITED_SEQ;
        kfree(fence);
 }
 
@@ -145,83 +152,84 @@ int radeon_fence_create(struct radeon_device *rdev,
        }
        kref_init(&((*fence)->kref));
        (*fence)->rdev = rdev;
-       (*fence)->emitted = false;
-       (*fence)->signaled = false;
-       (*fence)->seq = 0;
+       (*fence)->seq = RADEON_FENCE_NOTEMITED_SEQ;
        (*fence)->ring = ring;
-       (*fence)->semaphore = NULL;
-       INIT_LIST_HEAD(&(*fence)->list);
        return 0;
 }
 
-bool radeon_fence_signaled(struct radeon_fence *fence)
+static bool radeon_fence_seq_signaled(struct radeon_device *rdev,
+                                     u64 seq, unsigned ring)
 {
-       unsigned long irq_flags;
-       bool signaled = false;
-
-       if (!fence)
+       if (atomic64_read(&rdev->fence_drv[ring].last_seq) >= seq) {
+               return true;
+       }
+       /* poll new last sequence at least once */
+       radeon_fence_process(rdev, ring);
+       if (atomic64_read(&rdev->fence_drv[ring].last_seq) >= seq) {
                return true;
+       }
+       return false;
+}
 
-       write_lock_irqsave(&fence->rdev->fence_lock, irq_flags);
-       signaled = fence->signaled;
-       /* if we are shuting down report all fence as signaled */
-       if (fence->rdev->shutdown) {
-               signaled = true;
+bool radeon_fence_signaled(struct radeon_fence *fence)
+{
+       if (!fence) {
+               return true;
        }
-       if (!fence->emitted) {
+       if (fence->seq == RADEON_FENCE_NOTEMITED_SEQ) {
                WARN(1, "Querying an unemitted fence : %p !\n", fence);
-               signaled = true;
+               return true;
        }
-       if (!signaled) {
-               radeon_fence_poll_locked(fence->rdev, fence->ring);
-               signaled = fence->signaled;
+       if (fence->seq == RADEON_FENCE_SIGNALED_SEQ) {
+               return true;
+       }
+       if (radeon_fence_seq_signaled(fence->rdev, fence->seq, fence->ring)) {
+               fence->seq = RADEON_FENCE_SIGNALED_SEQ;
+               return true;
        }
-       write_unlock_irqrestore(&fence->rdev->fence_lock, irq_flags);
-       return signaled;
+       return false;
 }
 
-int radeon_fence_wait(struct radeon_fence *fence, bool intr)
+static int radeon_fence_wait_seq(struct radeon_device *rdev, u64 target_seq,
+                                unsigned ring, bool intr, bool lock_ring)
 {
-       struct radeon_device *rdev;
-       unsigned long irq_flags, timeout;
-       u32 seq;
-       int i, r;
+       unsigned long timeout, last_activity;
+       uint64_t seq;
+       unsigned i;
        bool signaled;
+       int r;
 
-       if (fence == NULL) {
-               WARN(1, "Querying an invalid fence : %p !\n", fence);
-               return -EINVAL;
-       }
+       while (target_seq > atomic64_read(&rdev->fence_drv[ring].last_seq)) {
+               if (!rdev->ring[ring].ready) {
+                       return -EBUSY;
+               }
 
-       rdev = fence->rdev;
-       signaled = radeon_fence_signaled(fence);
-       while (!signaled) {
-               read_lock_irqsave(&rdev->fence_lock, irq_flags);
                timeout = jiffies - RADEON_FENCE_JIFFIES_TIMEOUT;
-               if (time_after(rdev->fence_drv[fence->ring].last_activity, timeout)) {
+               if (time_after(rdev->fence_drv[ring].last_activity, timeout)) {
                        /* the normal case, timeout is somewhere before last_activity */
-                       timeout = rdev->fence_drv[fence->ring].last_activity - timeout;
+                       timeout = rdev->fence_drv[ring].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 */
+                        * anyway we will just wait for the minimum amount and then check for a lockup
+                        */
                        timeout = 1;
                }
-               /* save current sequence value used to check for GPU lockups */
-               seq = rdev->fence_drv[fence->ring].last_seq;
-               read_unlock_irqrestore(&rdev->fence_lock, irq_flags);
+               seq = atomic64_read(&rdev->fence_drv[ring].last_seq);
+               /* Save current last activity valuee, used to check for GPU lockups */
+               last_activity = rdev->fence_drv[ring].last_activity;
 
                trace_radeon_fence_wait_begin(rdev->ddev, seq);
-               radeon_irq_kms_sw_irq_get(rdev, fence->ring);
+               radeon_irq_kms_sw_irq_get(rdev, ring);
                if (intr) {
-                       r = wait_event_interruptible_timeout(
-                               rdev->fence_drv[fence->ring].queue,
-                               (signaled = radeon_fence_signaled(fence)), timeout);
-               } else {
-                       r = wait_event_timeout(
-                               rdev->fence_drv[fence->ring].queue,
-                               (signaled = radeon_fence_signaled(fence)), timeout);
+                       r = wait_event_interruptible_timeout(rdev->fence_queue,
+                               (signaled = radeon_fence_seq_signaled(rdev, target_seq, ring)),
+                               timeout);
+                } else {
+                       r = wait_event_timeout(rdev->fence_queue,
+                               (signaled = radeon_fence_seq_signaled(rdev, target_seq, ring)),
+                               timeout);
                }
-               radeon_irq_kms_sw_irq_put(rdev, fence->ring);
+               radeon_irq_kms_sw_irq_put(rdev, ring);
                if (unlikely(r < 0)) {
                        return r;
                }
@@ -234,81 +242,246 @@ int radeon_fence_wait(struct radeon_fence *fence, bool intr)
                                continue;
                        }
 
-                       write_lock_irqsave(&rdev->fence_lock, irq_flags);
                        /* check if sequence value has changed since last_activity */
-                       if (seq != rdev->fence_drv[fence->ring].last_seq) {
-                               write_unlock_irqrestore(&rdev->fence_lock, irq_flags);
+                       if (seq != atomic64_read(&rdev->fence_drv[ring].last_seq)) {
                                continue;
                        }
 
-                       /* change sequence value on all rings, so nobody else things there is a lockup */
-                       for (i = 0; i < RADEON_NUM_RINGS; ++i)
-                               rdev->fence_drv[i].last_seq -= 0x10000;
-
-                       rdev->fence_drv[fence->ring].last_activity = jiffies;
-                       write_unlock_irqrestore(&rdev->fence_lock, irq_flags);
+                       if (lock_ring) {
+                               mutex_lock(&rdev->ring_lock);
+                       }
 
-                       if (radeon_ring_is_lockup(rdev, fence->ring, &rdev->ring[fence->ring])) {
+                       /* test if somebody else has already decided that this is a lockup */
+                       if (last_activity != rdev->fence_drv[ring].last_activity) {
+                               if (lock_ring) {
+                                       mutex_unlock(&rdev->ring_lock);
+                               }
+                               continue;
+                       }
 
+                       if (radeon_ring_is_lockup(rdev, ring, &rdev->ring[ring])) {
                                /* good news we believe it's a lockup */
-                               printk(KERN_WARNING "GPU lockup (waiting for 0x%08X last fence id 0x%08X)\n",
-                                    fence->seq, seq);
+                               dev_warn(rdev->dev, "GPU lockup (waiting for 0x%016llx last fence id 0x%016llx)\n",
+                                        target_seq, seq);
+
+                               /* 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[fence->ring].ready = false;
+                               rdev->ring[ring].ready = false;
+                               if (lock_ring) {
+                                       mutex_unlock(&rdev->ring_lock);
+                               }
                                return -EDEADLK;
                        }
+
+                       if (lock_ring) {
+                               mutex_unlock(&rdev->ring_lock);
+                       }
                }
        }
        return 0;
 }
 
-int radeon_fence_wait_next(struct radeon_device *rdev, int ring)
+int radeon_fence_wait(struct radeon_fence *fence, bool intr)
 {
-       unsigned long irq_flags;
-       struct radeon_fence *fence;
        int r;
 
-       write_lock_irqsave(&rdev->fence_lock, irq_flags);
-       if (!rdev->ring[ring].ready) {
-               write_unlock_irqrestore(&rdev->fence_lock, irq_flags);
-               return -EBUSY;
+       if (fence == NULL) {
+               WARN(1, "Querying an invalid fence : %p !\n", fence);
+               return -EINVAL;
        }
-       if (list_empty(&rdev->fence_drv[ring].emitted)) {
-               write_unlock_irqrestore(&rdev->fence_lock, irq_flags);
-               return -ENOENT;
+
+       r = radeon_fence_wait_seq(fence->rdev, fence->seq,
+                                 fence->ring, intr, true);
+       if (r) {
+               return r;
        }
-       fence = list_entry(rdev->fence_drv[ring].emitted.next,
-                          struct radeon_fence, list);
-       radeon_fence_ref(fence);
-       write_unlock_irqrestore(&rdev->fence_lock, irq_flags);
-       r = radeon_fence_wait(fence, false);
-       radeon_fence_unref(&fence);
-       return r;
+       fence->seq = RADEON_FENCE_SIGNALED_SEQ;
+       return 0;
 }
 
-int radeon_fence_wait_empty(struct radeon_device *rdev, int ring)
+bool radeon_fence_any_seq_signaled(struct radeon_device *rdev, u64 *seq)
 {
-       unsigned long irq_flags;
-       struct radeon_fence *fence;
+       unsigned i;
+
+       for (i = 0; i < RADEON_NUM_RINGS; ++i) {
+               if (seq[i] && radeon_fence_seq_signaled(rdev, seq[i], i)) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+static int radeon_fence_wait_any_seq(struct radeon_device *rdev,
+                                    u64 *target_seq, bool intr)
+{
+       unsigned long timeout, last_activity, tmp;
+       unsigned i, ring = RADEON_NUM_RINGS;
+       bool signaled;
        int r;
 
-       write_lock_irqsave(&rdev->fence_lock, irq_flags);
-       if (!rdev->ring[ring].ready) {
-               write_unlock_irqrestore(&rdev->fence_lock, irq_flags);
-               return -EBUSY;
+       for (i = 0, last_activity = 0; i < RADEON_NUM_RINGS; ++i) {
+               if (!target_seq[i]) {
+                       continue;
+               }
+
+               /* use the most recent one as indicator */
+               if (time_after(rdev->fence_drv[i].last_activity, last_activity)) {
+                       last_activity = rdev->fence_drv[i].last_activity;
+               }
+
+               /* For lockup detection just pick the lowest ring we are
+                * actively waiting for
+                */
+               if (i < ring) {
+                       ring = i;
+               }
        }
-       if (list_empty(&rdev->fence_drv[ring].emitted)) {
-               write_unlock_irqrestore(&rdev->fence_lock, irq_flags);
+
+       /* nothing to wait for ? */
+       if (ring == RADEON_NUM_RINGS) {
                return 0;
        }
-       fence = list_entry(rdev->fence_drv[ring].emitted.prev,
-                          struct radeon_fence, list);
-       radeon_fence_ref(fence);
-       write_unlock_irqrestore(&rdev->fence_lock, irq_flags);
-       r = radeon_fence_wait(fence, false);
-       radeon_fence_unref(&fence);
-       return r;
+
+       while (!radeon_fence_any_seq_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_fence_any_seq_signaled(rdev, target_seq)),
+                               timeout);
+               } else {
+                       r = wait_event_timeout(rdev->fence_queue,
+                               (signaled = radeon_fence_any_seq_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;
+       int r;
+
+       for (i = 0; i < RADEON_NUM_RINGS; ++i) {
+               seq[i] = 0;
+
+               if (!fences[i]) {
+                       continue;
+               }
+
+               if (fences[i]->seq == RADEON_FENCE_SIGNALED_SEQ) {
+                       /* something was allready signaled */
+                       return 0;
+               }
+
+               if (fences[i]->seq < RADEON_FENCE_NOTEMITED_SEQ) {
+                       seq[i] = fences[i]->seq;
+               }
+       }
+
+       r = radeon_fence_wait_any_seq(rdev, seq, intr);
+       if (r) {
+               return r;
+       }
+       return 0;
+}
+
+int radeon_fence_wait_next_locked(struct radeon_device *rdev, int ring)
+{
+       uint64_t seq;
+
+       /* We are not protected by ring lock when reading current seq but
+        * it's ok as worst case is we return to early while we could have
+        * wait.
+        */
+       seq = atomic64_read(&rdev->fence_drv[ring].last_seq) + 1ULL;
+       if (seq >= rdev->fence_drv[ring].seq) {
+               /* nothing to wait for, last_seq is
+                  already the last emited fence */
+               return -ENOENT;
+       }
+       return radeon_fence_wait_seq(rdev, seq, ring, false, false);
+}
+
+int radeon_fence_wait_empty_locked(struct radeon_device *rdev, int ring)
+{
+       /* We are not protected by ring lock when reading current seq
+        * but it's ok as wait empty is call from place where no more
+        * activity can be scheduled so there won't be concurrent access
+        * to seq value.
+        */
+       return radeon_fence_wait_seq(rdev, rdev->fence_drv[ring].seq,
+                                    ring, false, false);
 }
 
 struct radeon_fence *radeon_fence_ref(struct radeon_fence *fence)
@@ -327,49 +500,27 @@ void radeon_fence_unref(struct radeon_fence **fence)
        }
 }
 
-void radeon_fence_process(struct radeon_device *rdev, int ring)
-{
-       unsigned long irq_flags;
-       bool wake;
-
-       write_lock_irqsave(&rdev->fence_lock, irq_flags);
-       wake = radeon_fence_poll_locked(rdev, ring);
-       write_unlock_irqrestore(&rdev->fence_lock, irq_flags);
-       if (wake) {
-               wake_up_all(&rdev->fence_drv[ring].queue);
-       }
-}
-
-int radeon_fence_count_emitted(struct radeon_device *rdev, int ring)
+unsigned radeon_fence_count_emitted(struct radeon_device *rdev, int ring)
 {
-       unsigned long irq_flags;
-       int not_processed = 0;
-
-       read_lock_irqsave(&rdev->fence_lock, irq_flags);
-       if (!rdev->fence_drv[ring].initialized) {
-               read_unlock_irqrestore(&rdev->fence_lock, irq_flags);
-               return 0;
+       uint64_t emitted;
+
+       /* We are not protected by ring lock when reading the last sequence
+        * but it's ok to report slightly wrong fence count here.
+        */
+       radeon_fence_process(rdev, ring);
+       emitted = rdev->fence_drv[ring].seq - atomic64_read(&rdev->fence_drv[ring].last_seq);
+       /* to avoid 32bits warp around */
+       if (emitted > 0x10000000) {
+               emitted = 0x10000000;
        }
-
-       if (!list_empty(&rdev->fence_drv[ring].emitted)) {
-               struct list_head *ptr;
-               list_for_each(ptr, &rdev->fence_drv[ring].emitted) {
-                       /* count up to 3, that's enought info */
-                       if (++not_processed >= 3)
-                               break;
-               }
-       }
-       read_unlock_irqrestore(&rdev->fence_lock, irq_flags);
-       return not_processed;
+       return (unsigned)emitted;
 }
 
 int radeon_fence_driver_start_ring(struct radeon_device *rdev, int ring)
 {
-       unsigned long irq_flags;
        uint64_t index;
        int r;
 
-       write_lock_irqsave(&rdev->fence_lock, irq_flags);
        radeon_scratch_free(rdev, rdev->fence_drv[ring].scratch_reg);
        if (rdev->wb.use_event) {
                rdev->fence_drv[ring].scratch_reg = 0;
@@ -378,7 +529,6 @@ int radeon_fence_driver_start_ring(struct radeon_device *rdev, int ring)
                r = radeon_scratch_get(rdev, &rdev->fence_drv[ring].scratch_reg);
                if (r) {
                        dev_err(rdev->dev, "fence failed to get scratch register\n");
-                       write_unlock_irqrestore(&rdev->fence_lock, irq_flags);
                        return r;
                }
                index = RADEON_WB_SCRATCH_OFFSET +
@@ -387,11 +537,10 @@ int radeon_fence_driver_start_ring(struct radeon_device *rdev, int ring)
        }
        rdev->fence_drv[ring].cpu_addr = &rdev->wb.wb[index/4];
        rdev->fence_drv[ring].gpu_addr = rdev->wb.gpu_addr + index;
-       radeon_fence_write(rdev, atomic_read(&rdev->fence_drv[ring].seq), ring);
+       radeon_fence_write(rdev, rdev->fence_drv[ring].seq, ring);
        rdev->fence_drv[ring].initialized = true;
-       DRM_INFO("fence driver on ring %d use gpu addr 0x%08Lx and cpu addr 0x%p\n",
+       dev_info(rdev->dev, "fence driver on ring %d use gpu addr 0x%016llx and cpu addr 0x%p\n",
                 ring, rdev->fence_drv[ring].gpu_addr, rdev->fence_drv[ring].cpu_addr);
-       write_unlock_irqrestore(&rdev->fence_lock, irq_flags);
        return 0;
 }
 
@@ -400,23 +549,20 @@ static void radeon_fence_driver_init_ring(struct radeon_device *rdev, int ring)
        rdev->fence_drv[ring].scratch_reg = -1;
        rdev->fence_drv[ring].cpu_addr = NULL;
        rdev->fence_drv[ring].gpu_addr = 0;
-       atomic_set(&rdev->fence_drv[ring].seq, 0);
-       INIT_LIST_HEAD(&rdev->fence_drv[ring].emitted);
-       INIT_LIST_HEAD(&rdev->fence_drv[ring].signaled);
-       init_waitqueue_head(&rdev->fence_drv[ring].queue);
+       rdev->fence_drv[ring].seq = 0;
+       atomic64_set(&rdev->fence_drv[ring].last_seq, 0);
+       rdev->fence_drv[ring].last_activity = jiffies;
        rdev->fence_drv[ring].initialized = false;
 }
 
 int radeon_fence_driver_init(struct radeon_device *rdev)
 {
-       unsigned long irq_flags;
        int ring;
 
-       write_lock_irqsave(&rdev->fence_lock, irq_flags);
+       init_waitqueue_head(&rdev->fence_queue);
        for (ring = 0; ring < RADEON_NUM_RINGS; ring++) {
                radeon_fence_driver_init_ring(rdev, ring);
        }
-       write_unlock_irqrestore(&rdev->fence_lock, irq_flags);
        if (radeon_debugfs_fence_init(rdev)) {
                dev_err(rdev->dev, "fence debugfs file creation failed\n");
        }
@@ -425,19 +571,18 @@ int radeon_fence_driver_init(struct radeon_device *rdev)
 
 void radeon_fence_driver_fini(struct radeon_device *rdev)
 {
-       unsigned long irq_flags;
        int ring;
 
+       mutex_lock(&rdev->ring_lock);
        for (ring = 0; ring < RADEON_NUM_RINGS; ring++) {
                if (!rdev->fence_drv[ring].initialized)
                        continue;
-               radeon_fence_wait_empty(rdev, ring);
-               wake_up_all(&rdev->fence_drv[ring].queue);
-               write_lock_irqsave(&rdev->fence_lock, irq_flags);
+               radeon_fence_wait_empty_locked(rdev, ring);
+               wake_up_all(&rdev->fence_queue);
                radeon_scratch_free(rdev, rdev->fence_drv[ring].scratch_reg);
-               write_unlock_irqrestore(&rdev->fence_lock, irq_flags);
                rdev->fence_drv[ring].initialized = false;
        }
+       mutex_unlock(&rdev->ring_lock);
 }
 
 
@@ -450,7 +595,6 @@ static int radeon_debugfs_fence_info(struct seq_file *m, void *data)
        struct drm_info_node *node = (struct drm_info_node *)m->private;
        struct drm_device *dev = node->minor->dev;
        struct radeon_device *rdev = dev->dev_private;
-       struct radeon_fence *fence;
        int i;
 
        for (i = 0; i < RADEON_NUM_RINGS; ++i) {
@@ -458,14 +602,10 @@ static int radeon_debugfs_fence_info(struct seq_file *m, void *data)
                        continue;
 
                seq_printf(m, "--- ring %d ---\n", i);
-               seq_printf(m, "Last signaled fence 0x%08X\n",
-                          radeon_fence_read(rdev, i));
-               if (!list_empty(&rdev->fence_drv[i].emitted)) {
-                       fence = list_entry(rdev->fence_drv[i].emitted.prev,
-                                          struct radeon_fence, list);
-                       seq_printf(m, "Last emitted fence %p with 0x%08X\n",
-                                  fence,  fence->seq);
-               }
+               seq_printf(m, "Last signaled fence 0x%016llx\n",
+                          (unsigned long long)atomic64_read(&rdev->fence_drv[i].last_seq));
+               seq_printf(m, "Last emitted  0x%016llx\n",
+                          rdev->fence_drv[i].seq);
        }
        return 0;
 }