alarmtimers: Remove interval cap limit hack
[deliverable/linux.git] / kernel / time / alarmtimer.c
index 59f369f98a04311f5bfa49d01e706d4b12ca97c4..a522c007e6fda479f16f9bd2ea8a9459b16f21ef 100644 (file)
@@ -174,6 +174,7 @@ static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer)
        unsigned long flags;
        ktime_t now;
        int ret = HRTIMER_NORESTART;
+       int restart = ALARMTIMER_NORESTART;
 
        spin_lock_irqsave(&base->lock, flags);
        now = base->gettime();
@@ -188,16 +189,16 @@ static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer)
 
                timerqueue_del(&base->timerqueue, &alarm->node);
                alarm->enabled = 0;
-               /* Re-add periodic timers */
-               if (alarm->period.tv64) {
-                       alarm->node.expires = ktime_add(expired, alarm->period);
-                       timerqueue_add(&base->timerqueue, &alarm->node);
-                       alarm->enabled = 1;
-               }
+
                spin_unlock_irqrestore(&base->lock, flags);
                if (alarm->function)
-                       alarm->function(alarm);
+                       restart = alarm->function(alarm, now);
                spin_lock_irqsave(&base->lock, flags);
+
+               if (restart != ALARMTIMER_NORESTART) {
+                       timerqueue_add(&base->timerqueue, &alarm->node);
+                       alarm->enabled = 1;
+               }
        }
 
        if (next) {
@@ -299,7 +300,7 @@ static void alarmtimer_freezerset(ktime_t absexp, enum alarmtimer_type type)
  * @function: callback that is run when the alarm fires
  */
 void alarm_init(struct alarm *alarm, enum alarmtimer_type type,
-               void (*function)(struct alarm *))
+               enum alarmtimer_restart (*function)(struct alarm *, ktime_t))
 {
        timerqueue_init(&alarm->node);
        alarm->period = ktime_set(0, 0);
@@ -346,6 +347,41 @@ void alarm_cancel(struct alarm *alarm)
 }
 
 
+
+u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval)
+{
+       u64 overrun = 1;
+       ktime_t delta;
+
+       delta = ktime_sub(now, alarm->node.expires);
+
+       if (delta.tv64 < 0)
+               return 0;
+
+       if (unlikely(delta.tv64 >= interval.tv64)) {
+               s64 incr = ktime_to_ns(interval);
+
+               overrun = ktime_divns(delta, incr);
+
+               alarm->node.expires = ktime_add_ns(alarm->node.expires,
+                                                       incr*overrun);
+
+               if (alarm->node.expires.tv64 > now.tv64)
+                       return overrun;
+               /*
+                * This (and the ktime_add() below) is the
+                * correction for exact:
+                */
+               overrun++;
+       }
+
+       alarm->node.expires = ktime_add(alarm->node.expires, interval);
+       return overrun;
+}
+
+
+
+
 /**
  * clock2alarm - helper that converts from clockid to alarmtypes
  * @clockid: clockid.
@@ -365,12 +401,20 @@ static enum alarmtimer_type clock2alarm(clockid_t clockid)
  *
  * Posix timer callback for expired alarm timers.
  */
-static void alarm_handle_timer(struct alarm *alarm)
+static enum alarmtimer_restart alarm_handle_timer(struct alarm *alarm,
+                                                       ktime_t now)
 {
        struct k_itimer *ptr = container_of(alarm, struct k_itimer,
                                                it.alarmtimer);
        if (posix_timer_event(ptr, 0) != 0)
                ptr->it_overrun++;
+
+       /* Re-add periodic timers */
+       if (alarm->period.tv64) {
+               ptr->it_overrun += alarm_forward(alarm, now, alarm->period);
+               return ALARMTIMER_RESTART;
+       }
+       return ALARMTIMER_NORESTART;
 }
 
 /**
@@ -441,6 +485,8 @@ static int alarm_timer_create(struct k_itimer *new_timer)
 static void alarm_timer_get(struct k_itimer *timr,
                                struct itimerspec *cur_setting)
 {
+       memset(cur_setting, 0, sizeof(struct itimerspec));
+
        cur_setting->it_interval =
                        ktime_to_timespec(timr->it.alarmtimer.period);
        cur_setting->it_value =
@@ -479,11 +525,8 @@ static int alarm_timer_set(struct k_itimer *timr, int flags,
        if (!rtcdev)
                return -ENOTSUPP;
 
-       /* Save old values */
-       old_setting->it_interval =
-                       ktime_to_timespec(timr->it.alarmtimer.period);
-       old_setting->it_value =
-                       ktime_to_timespec(timr->it.alarmtimer.node.expires);
+       if (old_setting)
+               alarm_timer_get(timr, old_setting);
 
        /* If the timer was already set, cancel it */
        alarm_cancel(&timr->it.alarmtimer);
@@ -501,13 +544,15 @@ static int alarm_timer_set(struct k_itimer *timr, int flags,
  *
  * Wakes up the task that set the alarmtimer
  */
-static void alarmtimer_nsleep_wakeup(struct alarm *alarm)
+static enum alarmtimer_restart alarmtimer_nsleep_wakeup(struct alarm *alarm,
+                                                               ktime_t now)
 {
        struct task_struct *task = (struct task_struct *)alarm->data;
 
        alarm->data = NULL;
        if (task)
                wake_up_process(task);
+       return ALARMTIMER_NORESTART;
 }
 
 /**
This page took 0.035823 seconds and 5 git commands to generate.