Skip to content

Commit

Permalink
alarmtimer: Use hrtimer per-alarm instead of per-base
Browse files Browse the repository at this point in the history
Arve Hjønnevåg reported numerous crashes from the
"BUG_ON(timer->state != HRTIMER_STATE_CALLBACK)" check
in __run_hrtimer after it called alarmtimer_fired.

It ends up the alarmtimer code was not properly handling
possible failures of hrtimer_try_to_cancel, and because
these faulres occur when the underlying base hrtimer is
being run, this limits the ability to properly handle
modifications to any alarmtimers on that base.

Because much of the logic duplicates the hrtimer logic,
it seems that we might as well have a per-alarmtimer
hrtimer, and avoid the extra complextity of trying to
multiplex many alarmtimers off of one hrtimer.

Thus this patch moves the hrtimer to the alarm structure
and simplifies the management logic.

Changelog:
v2:
* Includes a fix for double alarm_start calls found by
  Arve

Cc: Arve Hjønnevåg <arve@android.com>
Cc: Colin Cross <ccross@android.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Reported-by: Arve Hjønnevåg <arve@android.com>
Tested-by: Arve Hjønnevåg <arve@android.com>
Signed-off-by: John Stultz <john.stultz@linaro.org>
  • Loading branch information
John Stultz committed Sep 24, 2012
1 parent 59a93c2 commit dae373b
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 68 deletions.
3 changes: 2 additions & 1 deletion include/linux/alarmtimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ enum alarmtimer_restart {
*/
struct alarm {
struct timerqueue_node node;
struct hrtimer timer;
enum alarmtimer_restart (*function)(struct alarm *, ktime_t now);
enum alarmtimer_type type;
int state;
Expand All @@ -43,7 +44,7 @@ struct alarm {

void alarm_init(struct alarm *alarm, enum alarmtimer_type type,
enum alarmtimer_restart (*function)(struct alarm *, ktime_t));
void alarm_start(struct alarm *alarm, ktime_t start);
int alarm_start(struct alarm *alarm, ktime_t start);
int alarm_try_to_cancel(struct alarm *alarm);
int alarm_cancel(struct alarm *alarm);

Expand Down
94 changes: 27 additions & 67 deletions kernel/time/alarmtimer.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
static struct alarm_base {
spinlock_t lock;
struct timerqueue_head timerqueue;
struct hrtimer timer;
ktime_t (*gettime)(void);
clockid_t base_clockid;
} alarm_bases[ALARM_NUMTYPE];
Expand Down Expand Up @@ -132,50 +131,35 @@ static inline void alarmtimer_rtc_timer_init(void) { }
* @base: pointer to the base where the timer is being run
* @alarm: pointer to alarm being enqueued.
*
* Adds alarm to a alarm_base timerqueue and if necessary sets
* an hrtimer to run.
* Adds alarm to a alarm_base timerqueue
*
* Must hold base->lock when calling.
*/
static void alarmtimer_enqueue(struct alarm_base *base, struct alarm *alarm)
{
if (alarm->state & ALARMTIMER_STATE_ENQUEUED)
timerqueue_del(&base->timerqueue, &alarm->node);

timerqueue_add(&base->timerqueue, &alarm->node);
alarm->state |= ALARMTIMER_STATE_ENQUEUED;

if (&alarm->node == timerqueue_getnext(&base->timerqueue)) {
hrtimer_try_to_cancel(&base->timer);
hrtimer_start(&base->timer, alarm->node.expires,
HRTIMER_MODE_ABS);
}
}

/**
* alarmtimer_remove - Removes an alarm timer from an alarm_base timerqueue
* @base: pointer to the base where the timer is running
* @alarm: pointer to alarm being removed
*
* Removes alarm to a alarm_base timerqueue and if necessary sets
* a new timer to run.
* Removes alarm to a alarm_base timerqueue
*
* Must hold base->lock when calling.
*/
static void alarmtimer_remove(struct alarm_base *base, struct alarm *alarm)
{
struct timerqueue_node *next = timerqueue_getnext(&base->timerqueue);

if (!(alarm->state & ALARMTIMER_STATE_ENQUEUED))
return;

timerqueue_del(&base->timerqueue, &alarm->node);
alarm->state &= ~ALARMTIMER_STATE_ENQUEUED;

if (next == &alarm->node) {
hrtimer_try_to_cancel(&base->timer);
next = timerqueue_getnext(&base->timerqueue);
if (!next)
return;
hrtimer_start(&base->timer, next->expires, HRTIMER_MODE_ABS);
}
}


Expand All @@ -190,42 +174,23 @@ static void alarmtimer_remove(struct alarm_base *base, struct alarm *alarm)
*/
static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer)
{
struct alarm_base *base = container_of(timer, struct alarm_base, timer);
struct timerqueue_node *next;
struct alarm *alarm = container_of(timer, struct alarm, timer);
struct alarm_base *base = &alarm_bases[alarm->type];
unsigned long flags;
ktime_t now;
int ret = HRTIMER_NORESTART;
int restart = ALARMTIMER_NORESTART;

spin_lock_irqsave(&base->lock, flags);
now = base->gettime();
while ((next = timerqueue_getnext(&base->timerqueue))) {
struct alarm *alarm;
ktime_t expired = next->expires;

if (expired.tv64 > now.tv64)
break;

alarm = container_of(next, struct alarm, node);

timerqueue_del(&base->timerqueue, &alarm->node);
alarm->state &= ~ALARMTIMER_STATE_ENQUEUED;

alarm->state |= ALARMTIMER_STATE_CALLBACK;
spin_unlock_irqrestore(&base->lock, flags);
if (alarm->function)
restart = alarm->function(alarm, now);
spin_lock_irqsave(&base->lock, flags);
alarm->state &= ~ALARMTIMER_STATE_CALLBACK;
alarmtimer_remove(base, alarm);
spin_unlock_irqrestore(&base->lock, flags);

if (restart != ALARMTIMER_NORESTART) {
timerqueue_add(&base->timerqueue, &alarm->node);
alarm->state |= ALARMTIMER_STATE_ENQUEUED;
}
}
if (alarm->function)
restart = alarm->function(alarm, base->gettime());

if (next) {
hrtimer_set_expires(&base->timer, next->expires);
spin_lock_irqsave(&base->lock, flags);
if (restart != ALARMTIMER_NORESTART) {
hrtimer_set_expires(&alarm->timer, alarm->node.expires);
alarmtimer_enqueue(base, alarm);
ret = HRTIMER_RESTART;
}
spin_unlock_irqrestore(&base->lock, flags);
Expand Down Expand Up @@ -331,6 +296,9 @@ void alarm_init(struct alarm *alarm, enum alarmtimer_type type,
enum alarmtimer_restart (*function)(struct alarm *, ktime_t))
{
timerqueue_init(&alarm->node);
hrtimer_init(&alarm->timer, alarm_bases[type].base_clockid,
HRTIMER_MODE_ABS);
alarm->timer.function = alarmtimer_fired;
alarm->function = function;
alarm->type = type;
alarm->state = ALARMTIMER_STATE_INACTIVE;
Expand All @@ -341,17 +309,19 @@ void alarm_init(struct alarm *alarm, enum alarmtimer_type type,
* @alarm: ptr to alarm to set
* @start: time to run the alarm
*/
void alarm_start(struct alarm *alarm, ktime_t start)
int alarm_start(struct alarm *alarm, ktime_t start)
{
struct alarm_base *base = &alarm_bases[alarm->type];
unsigned long flags;
int ret;

spin_lock_irqsave(&base->lock, flags);
if (alarmtimer_active(alarm))
alarmtimer_remove(base, alarm);
alarm->node.expires = start;
alarmtimer_enqueue(base, alarm);
ret = hrtimer_start(&alarm->timer, alarm->node.expires,
HRTIMER_MODE_ABS);
spin_unlock_irqrestore(&base->lock, flags);
return ret;
}

/**
Expand All @@ -365,18 +335,12 @@ int alarm_try_to_cancel(struct alarm *alarm)
{
struct alarm_base *base = &alarm_bases[alarm->type];
unsigned long flags;
int ret = -1;
spin_lock_irqsave(&base->lock, flags);

if (alarmtimer_callback_running(alarm))
goto out;
int ret;

if (alarmtimer_is_queued(alarm)) {
spin_lock_irqsave(&base->lock, flags);
ret = hrtimer_try_to_cancel(&alarm->timer);
if (ret >= 0)
alarmtimer_remove(base, alarm);
ret = 1;
} else
ret = 0;
out:
spin_unlock_irqrestore(&base->lock, flags);
return ret;
}
Expand Down Expand Up @@ -809,10 +773,6 @@ static int __init alarmtimer_init(void)
for (i = 0; i < ALARM_NUMTYPE; i++) {
timerqueue_init_head(&alarm_bases[i].timerqueue);
spin_lock_init(&alarm_bases[i].lock);
hrtimer_init(&alarm_bases[i].timer,
alarm_bases[i].base_clockid,
HRTIMER_MODE_ABS);
alarm_bases[i].timer.function = alarmtimer_fired;
}

error = alarmtimer_rtc_interface_setup();
Expand Down

0 comments on commit dae373b

Please sign in to comment.