alarmtimers: Remove period from alarm structure
[pandora-kernel.git] / kernel / time / alarmtimer.c
index 59f369f..9093559 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,10 +300,9 @@ 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);
        alarm->function = function;
        alarm->type = type;
        alarm->enabled = 0;
@@ -312,9 +312,8 @@ void alarm_init(struct alarm *alarm, enum alarmtimer_type type,
  * alarm_start - Sets an alarm to fire
  * @alarm: ptr to alarm to set
  * @start: time to run the alarm
- * @period: period at which the alarm will recur
  */
-void alarm_start(struct alarm *alarm, ktime_t start, ktime_t period)
+void alarm_start(struct alarm *alarm, ktime_t start)
 {
        struct alarm_base *base = &alarm_bases[alarm->type];
        unsigned long flags;
@@ -323,7 +322,6 @@ void alarm_start(struct alarm *alarm, ktime_t start, ktime_t period)
        if (alarm->enabled)
                alarmtimer_remove(base, alarm);
        alarm->node.expires = start;
-       alarm->period = period;
        alarmtimer_enqueue(base, alarm);
        alarm->enabled = 1;
        spin_unlock_irqrestore(&base->lock, flags);
@@ -346,6 +344,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 +398,21 @@ 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);
+                                               it.alarm.alarmtimer);
        if (posix_timer_event(ptr, 0) != 0)
                ptr->it_overrun++;
+
+       /* Re-add periodic timers */
+       if (ptr->it.alarm.interval.tv64) {
+               ptr->it_overrun += alarm_forward(alarm, now,
+                                               ptr->it.alarm.interval);
+               return ALARMTIMER_RESTART;
+       }
+       return ALARMTIMER_NORESTART;
 }
 
 /**
@@ -427,7 +469,7 @@ static int alarm_timer_create(struct k_itimer *new_timer)
 
        type = clock2alarm(new_timer->it_clock);
        base = &alarm_bases[type];
-       alarm_init(&new_timer->it.alarmtimer, type, alarm_handle_timer);
+       alarm_init(&new_timer->it.alarm.alarmtimer, type, alarm_handle_timer);
        return 0;
 }
 
@@ -441,10 +483,12 @@ 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);
+                       ktime_to_timespec(timr->it.alarm.interval);
        cur_setting->it_value =
-                       ktime_to_timespec(timr->it.alarmtimer.node.expires);
+               ktime_to_timespec(timr->it.alarm.alarmtimer.node.expires);
        return;
 }
 
@@ -459,7 +503,7 @@ static int alarm_timer_del(struct k_itimer *timr)
        if (!rtcdev)
                return -ENOTSUPP;
 
-       alarm_cancel(&timr->it.alarmtimer);
+       alarm_cancel(&timr->it.alarm.alarmtimer);
        return 0;
 }
 
@@ -479,19 +523,16 @@ 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);
+       alarm_cancel(&timr->it.alarm.alarmtimer);
 
        /* start the timer */
-       alarm_start(&timr->it.alarmtimer,
-                       timespec_to_ktime(new_setting->it_value),
-                       timespec_to_ktime(new_setting->it_interval));
+       timr->it.alarm.interval = timespec_to_ktime(new_setting->it_interval);
+       alarm_start(&timr->it.alarm.alarmtimer,
+                       timespec_to_ktime(new_setting->it_value));
        return 0;
 }
 
@@ -501,13 +542,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;
 }
 
 /**
@@ -522,7 +565,7 @@ static int alarmtimer_do_nsleep(struct alarm *alarm, ktime_t absexp)
        alarm->data = (void *)current;
        do {
                set_current_state(TASK_INTERRUPTIBLE);
-               alarm_start(alarm, absexp, ktime_set(0, 0));
+               alarm_start(alarm, absexp);
                if (likely(alarm->data))
                        schedule();