alarmtimer: Use hrtimer per-alarm instead of per-base
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>
This commit is contained in:
parent
59a93c27c4
commit
dae373be9f
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
@ -132,21 +131,17 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -154,28 +149,17 @@ static void alarmtimer_enqueue(struct alarm_base *base, struct alarm *alarm)
|
|||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
alarmtimer_remove(base, alarm);
|
||||
spin_unlock_irqrestore(&base->lock, flags);
|
||||
|
||||
if (expired.tv64 > now.tv64)
|
||||
break;
|
||||
if (alarm->function)
|
||||
restart = alarm->function(alarm, base->gettime());
|
||||
|
||||
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;
|
||||
|
||||
if (restart != ALARMTIMER_NORESTART) {
|
||||
timerqueue_add(&base->timerqueue, &alarm->node);
|
||||
alarm->state |= ALARMTIMER_STATE_ENQUEUED;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&base->lock, flags);
|
||||
|
||||
if (alarmtimer_callback_running(alarm))
|
||||
goto out;
|
||||
|
||||
if (alarmtimer_is_queued(alarm)) {
|
||||
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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue