[PATCH] clockevents: Fix suspend/resume to disk hangs
[powerpc.git] / kernel / time / tick-common.c
index 4500e34..bfda3f7 100644 (file)
@@ -77,6 +77,7 @@ static void tick_periodic(int cpu)
 void tick_handle_periodic(struct clock_event_device *dev)
 {
        int cpu = smp_processor_id();
+       ktime_t next;
 
        tick_periodic(cpu);
 
@@ -86,12 +87,12 @@ void tick_handle_periodic(struct clock_event_device *dev)
         * Setup the next period for devices, which do not have
         * periodic mode:
         */
+       next = ktime_add(dev->next_event, tick_period);
        for (;;) {
-               ktime_t next = ktime_add(dev->next_event, tick_period);
-
                if (!clockevents_program_event(dev, next, ktime_get()))
                        return;
                tick_periodic(cpu);
+               next = ktime_add(next, tick_period);
        }
 }
 
@@ -297,6 +298,29 @@ static void tick_shutdown(unsigned int *cpup)
        spin_unlock_irqrestore(&tick_device_lock, flags);
 }
 
+static void tick_suspend(void)
+{
+       struct tick_device *td = &__get_cpu_var(tick_cpu_device);
+       unsigned long flags;
+
+       spin_lock_irqsave(&tick_device_lock, flags);
+       clockevents_set_mode(td->evtdev, CLOCK_EVT_MODE_SHUTDOWN);
+       spin_unlock_irqrestore(&tick_device_lock, flags);
+}
+
+static void tick_resume(void)
+{
+       struct tick_device *td = &__get_cpu_var(tick_cpu_device);
+       unsigned long flags;
+
+       spin_lock_irqsave(&tick_device_lock, flags);
+       if (td->mode == TICKDEV_MODE_PERIODIC)
+               tick_setup_periodic(td->evtdev, 0);
+       else
+               tick_resume_oneshot();
+       spin_unlock_irqrestore(&tick_device_lock, flags);
+}
+
 /*
  * Notification about clock event devices
  */
@@ -324,6 +348,16 @@ static int tick_notify(struct notifier_block *nb, unsigned long reason,
                tick_shutdown(dev);
                break;
 
+       case CLOCK_EVT_NOTIFY_SUSPEND:
+               tick_suspend();
+               tick_suspend_broadcast();
+               break;
+
+       case CLOCK_EVT_NOTIFY_RESUME:
+               if (!tick_resume_broadcast())
+                       tick_resume();
+               break;
+
        default:
                break;
        }