clockevents: prevent endless loop lockup
[firefly-linux-kernel-4.4.55.git] / kernel / time / tick-oneshot.c
index 06595c64b0c99ac6869c02eebe9da8e317bf5204..2e35501e61ddaa5b65a4275875931bc1fb438395 100644 (file)
 /**
  * tick_program_event internal worker function
  */
-static int __tick_program_event(struct clock_event_device *dev,
-                               ktime_t expires, int force)
+int tick_dev_program_event(struct clock_event_device *dev, ktime_t expires,
+                          int force)
 {
        ktime_t now = ktime_get();
+       int i;
 
-       while (1) {
+       for (i = 0;;) {
                int ret = clockevents_program_event(dev, expires, now);
 
                if (!ret || !force)
                        return ret;
+
+               /*
+                * We tried 2 times to program the device with the given
+                * min_delta_ns. If that's not working then we double it
+                * and emit a warning.
+                */
+               if (++i > 2) {
+                       printk(KERN_WARNING "CE: __tick_program_event of %s is "
+                              "stuck %llx %llx\n", dev->name ? dev->name : "?",
+                              now.tv64, expires.tv64);
+                       printk(KERN_WARNING
+                              "CE: increasing min_delta_ns %ld to %ld nsec\n",
+                              dev->min_delta_ns, dev->min_delta_ns << 1);
+                       WARN_ON(1);
+
+                       /* Double the min. delta and try again */
+                       if (!dev->min_delta_ns)
+                               dev->min_delta_ns = 5000;
+                       else
+                               dev->min_delta_ns <<= 1;
+                       i = 0;
+               }
+
                now = ktime_get();
-               expires = ktime_add(now, ktime_set(0, dev->min_delta_ns));
+               expires = ktime_add_ns(now, dev->min_delta_ns);
        }
 }
 
@@ -47,7 +71,7 @@ int tick_program_event(ktime_t expires, int force)
 {
        struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev;
 
-       return __tick_program_event(dev, expires, force);
+       return tick_dev_program_event(dev, expires, force);
 }
 
 /**
@@ -71,7 +95,7 @@ void tick_setup_oneshot(struct clock_event_device *newdev,
 {
        newdev->event_handler = handler;
        clockevents_set_mode(newdev, CLOCK_EVT_MODE_ONESHOT);
-       __tick_program_event(newdev, next_event, 1);
+       tick_dev_program_event(newdev, next_event, 1);
 }
 
 /**