Commit 9082c465 authored by John Stultz's avatar John Stultz

alarmtimers: Add try_to_cancel functionality

There's a number of edge cases when cancelling a alarm, so
to be sure we accurately do so, introduce try_to_cancel, which
returns proper failure errors if it cannot. Also modify cancel
to spin until the alarm is properly disabled.

CC: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarJohn Stultz <john.stultz@linaro.org>
parent a28cde81
...@@ -44,7 +44,8 @@ struct alarm { ...@@ -44,7 +44,8 @@ struct alarm {
void alarm_init(struct alarm *alarm, enum alarmtimer_type type, void alarm_init(struct alarm *alarm, enum alarmtimer_type type,
enum alarmtimer_restart (*function)(struct alarm *, ktime_t)); enum alarmtimer_restart (*function)(struct alarm *, ktime_t));
void alarm_start(struct alarm *alarm, ktime_t start); void alarm_start(struct alarm *alarm, ktime_t start);
void alarm_cancel(struct alarm *alarm); int alarm_try_to_cancel(struct alarm *alarm);
int alarm_cancel(struct alarm *alarm);
u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval); u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval);
......
...@@ -336,21 +336,49 @@ void alarm_start(struct alarm *alarm, ktime_t start) ...@@ -336,21 +336,49 @@ void alarm_start(struct alarm *alarm, ktime_t start)
} }
/** /**
* alarm_cancel - Tries to cancel an alarm timer * alarm_try_to_cancel - Tries to cancel an alarm timer
* @alarm: ptr to alarm to be canceled * @alarm: ptr to alarm to be canceled
*
* Returns 1 if the timer was canceled, 0 if it was not running,
* and -1 if the callback was running
*/ */
void alarm_cancel(struct alarm *alarm) int alarm_try_to_cancel(struct alarm *alarm)
{ {
struct alarm_base *base = &alarm_bases[alarm->type]; struct alarm_base *base = &alarm_bases[alarm->type];
unsigned long flags; unsigned long flags;
int ret = -1;
spin_lock_irqsave(&base->lock, flags); spin_lock_irqsave(&base->lock, flags);
if (alarmtimer_is_queued(alarm))
if (alarmtimer_callback_running(alarm))
goto out;
if (alarmtimer_is_queued(alarm)) {
alarmtimer_remove(base, alarm); alarmtimer_remove(base, alarm);
ret = 1;
} else
ret = 0;
out:
spin_unlock_irqrestore(&base->lock, flags); spin_unlock_irqrestore(&base->lock, flags);
return ret;
} }
/**
* alarm_cancel - Spins trying to cancel an alarm timer until it is done
* @alarm: ptr to alarm to be canceled
*
* Returns 1 if the timer was canceled, 0 if it was not active.
*/
int alarm_cancel(struct alarm *alarm)
{
for (;;) {
int ret = alarm_try_to_cancel(alarm);
if (ret >= 0)
return ret;
cpu_relax();
}
}
u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval) u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval)
{ {
...@@ -510,7 +538,9 @@ static int alarm_timer_del(struct k_itimer *timr) ...@@ -510,7 +538,9 @@ static int alarm_timer_del(struct k_itimer *timr)
if (!rtcdev) if (!rtcdev)
return -ENOTSUPP; return -ENOTSUPP;
alarm_cancel(&timr->it.alarm.alarmtimer); if (alarm_try_to_cancel(&timr->it.alarm.alarmtimer) < 0)
return TIMER_RETRY;
return 0; return 0;
} }
...@@ -534,7 +564,8 @@ static int alarm_timer_set(struct k_itimer *timr, int flags, ...@@ -534,7 +564,8 @@ static int alarm_timer_set(struct k_itimer *timr, int flags,
alarm_timer_get(timr, old_setting); alarm_timer_get(timr, old_setting);
/* If the timer was already set, cancel it */ /* If the timer was already set, cancel it */
alarm_cancel(&timr->it.alarm.alarmtimer); if (alarm_try_to_cancel(&timr->it.alarm.alarmtimer) < 0)
return TIMER_RETRY;
/* start the timer */ /* start the timer */
timr->it.alarm.interval = timespec_to_ktime(new_setting->it_interval); timr->it.alarm.interval = timespec_to_ktime(new_setting->it_interval);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment