drm/nouveau/tmr: detect stalled gpu timer and break out of waits
authorBen Skeggs <bskeggs@redhat.com>
Tue, 11 Dec 2018 04:50:02 +0000 (14:50 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Tue, 11 Dec 2018 05:37:45 +0000 (15:37 +1000)
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h
drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c

index e9b0746826ca66f9ac1765bbf8e757e1ee57a208..3693ebf371b647d0e7ba1044861f5417da6e332f 100644 (file)
@@ -28,6 +28,18 @@ struct nvkm_timer {
 u64 nvkm_timer_read(struct nvkm_timer *);
 void nvkm_timer_alarm(struct nvkm_timer *, u32 nsec, struct nvkm_alarm *);
 
+struct nvkm_timer_wait {
+       struct nvkm_timer *tmr;
+       u64 limit;
+       u64 time0;
+       u64 time1;
+       int reads;
+};
+
+void nvkm_timer_wait_init(struct nvkm_device *, u64 nsec,
+                         struct nvkm_timer_wait *);
+s64 nvkm_timer_wait_test(struct nvkm_timer_wait *);
+
 /* Delay based on GPU time (ie. PTIMER).
  *
  * Will return -ETIMEDOUT unless the loop was terminated with 'break',
@@ -38,21 +50,17 @@ void nvkm_timer_alarm(struct nvkm_timer *, u32 nsec, struct nvkm_alarm *);
  */
 #define NVKM_DELAY _warn = false;
 #define nvkm_nsec(d,n,cond...) ({                                              \
-       struct nvkm_device *_device = (d);                                     \
-       struct nvkm_timer *_tmr = _device->timer;                              \
-       u64 _nsecs = (n), _time0 = nvkm_timer_read(_tmr);                      \
-       s64 _taken = 0;                                                        \
+       struct nvkm_timer_wait _wait;                                          \
        bool _warn = true;                                                     \
+       s64 _taken = 0;                                                        \
                                                                                \
+       nvkm_timer_wait_init((d), (n), &_wait);                                \
        do {                                                                   \
                cond                                                           \
-       } while (_taken = nvkm_timer_read(_tmr) - _time0, _taken < _nsecs);    \
+       } while ((_taken = nvkm_timer_wait_test(&_wait)) >= 0);                \
                                                                                \
-       if (_taken >= _nsecs) {                                                \
-               if (_warn)                                                     \
-                       dev_WARN(_device->dev, "timeout\n");                   \
-               _taken = -ETIMEDOUT;                                           \
-       }                                                                      \
+       if (_warn && _taken < 0)                                               \
+               dev_WARN(_wait.tmr->subdev.device->dev, "timeout\n");          \
        _taken;                                                                \
 })
 #define nvkm_usec(d,u,cond...) nvkm_nsec((d), (u) * 1000, ##cond)
index 36de23d12ae460e336d1f4917434881afe26a5a5..dd922033628c236cfec38f3e91d49aa9ba886e78 100644 (file)
  */
 #include "priv.h"
 
+s64
+nvkm_timer_wait_test(struct nvkm_timer_wait *wait)
+{
+       struct nvkm_subdev *subdev = &wait->tmr->subdev;
+       u64 time = nvkm_timer_read(wait->tmr);
+
+       if (wait->reads == 0) {
+               wait->time0 = time;
+               wait->time1 = time;
+       }
+
+       if (wait->time1 == time) {
+               if (wait->reads++ == 16) {
+                       nvkm_fatal(subdev, "stalled at %016llx\n", time);
+                       return -ETIMEDOUT;
+               }
+       } else {
+               wait->time1 = time;
+               wait->reads = 1;
+       }
+
+       if (wait->time1 - wait->time0 > wait->limit)
+               return -ETIMEDOUT;
+
+       return wait->time1 - wait->time0;
+}
+
+void
+nvkm_timer_wait_init(struct nvkm_device *device, u64 nsec,
+                    struct nvkm_timer_wait *wait)
+{
+       wait->tmr = device->timer;
+       wait->limit = nsec;
+       wait->reads = 0;
+}
+
 u64
 nvkm_timer_read(struct nvkm_timer *tmr)
 {