diff options
author | John Stultz <john.stultz@linaro.org> | 2011-08-10 15:41:36 -0400 |
---|---|---|
committer | John Stultz <john.stultz@linaro.org> | 2011-08-10 17:55:29 -0400 |
commit | 9082c465a5403f4a98734193e078552991a2e283 (patch) | |
tree | 387e564f326488abe9ee84b0c723294ea573de1e /kernel/time | |
parent | a28cde81ab13cc251748a4c4ef06883dd09a10ea (diff) |
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: John Stultz <john.stultz@linaro.org>
Diffstat (limited to 'kernel/time')
-rw-r--r-- | kernel/time/alarmtimer.c | 43 |
1 files changed, 37 insertions, 6 deletions
diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index 5b14cc29b6a6..bdb7342b6896 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c | |||
@@ -336,21 +336,49 @@ void alarm_start(struct alarm *alarm, ktime_t start) | |||
336 | } | 336 | } |
337 | 337 | ||
338 | /** | 338 | /** |
339 | * alarm_cancel - Tries to cancel an alarm timer | 339 | * alarm_try_to_cancel - Tries to cancel an alarm timer |
340 | * @alarm: ptr to alarm to be canceled | 340 | * @alarm: ptr to alarm to be canceled |
341 | * | ||
342 | * Returns 1 if the timer was canceled, 0 if it was not running, | ||
343 | * and -1 if the callback was running | ||
341 | */ | 344 | */ |
342 | void alarm_cancel(struct alarm *alarm) | 345 | int alarm_try_to_cancel(struct alarm *alarm) |
343 | { | 346 | { |
344 | struct alarm_base *base = &alarm_bases[alarm->type]; | 347 | struct alarm_base *base = &alarm_bases[alarm->type]; |
345 | unsigned long flags; | 348 | unsigned long flags; |
346 | 349 | int ret = -1; | |
347 | spin_lock_irqsave(&base->lock, flags); | 350 | spin_lock_irqsave(&base->lock, flags); |
348 | if (alarmtimer_is_queued(alarm)) | 351 | |
352 | if (alarmtimer_callback_running(alarm)) | ||
353 | goto out; | ||
354 | |||
355 | if (alarmtimer_is_queued(alarm)) { | ||
349 | alarmtimer_remove(base, alarm); | 356 | alarmtimer_remove(base, alarm); |
357 | ret = 1; | ||
358 | } else | ||
359 | ret = 0; | ||
360 | out: | ||
350 | spin_unlock_irqrestore(&base->lock, flags); | 361 | spin_unlock_irqrestore(&base->lock, flags); |
362 | return ret; | ||
351 | } | 363 | } |
352 | 364 | ||
353 | 365 | ||
366 | /** | ||
367 | * alarm_cancel - Spins trying to cancel an alarm timer until it is done | ||
368 | * @alarm: ptr to alarm to be canceled | ||
369 | * | ||
370 | * Returns 1 if the timer was canceled, 0 if it was not active. | ||
371 | */ | ||
372 | int alarm_cancel(struct alarm *alarm) | ||
373 | { | ||
374 | for (;;) { | ||
375 | int ret = alarm_try_to_cancel(alarm); | ||
376 | if (ret >= 0) | ||
377 | return ret; | ||
378 | cpu_relax(); | ||
379 | } | ||
380 | } | ||
381 | |||
354 | 382 | ||
355 | u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval) | 383 | u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval) |
356 | { | 384 | { |
@@ -510,7 +538,9 @@ static int alarm_timer_del(struct k_itimer *timr) | |||
510 | if (!rtcdev) | 538 | if (!rtcdev) |
511 | return -ENOTSUPP; | 539 | return -ENOTSUPP; |
512 | 540 | ||
513 | alarm_cancel(&timr->it.alarm.alarmtimer); | 541 | if (alarm_try_to_cancel(&timr->it.alarm.alarmtimer) < 0) |
542 | return TIMER_RETRY; | ||
543 | |||
514 | return 0; | 544 | return 0; |
515 | } | 545 | } |
516 | 546 | ||
@@ -534,7 +564,8 @@ static int alarm_timer_set(struct k_itimer *timr, int flags, | |||
534 | alarm_timer_get(timr, old_setting); | 564 | alarm_timer_get(timr, old_setting); |
535 | 565 | ||
536 | /* If the timer was already set, cancel it */ | 566 | /* If the timer was already set, cancel it */ |
537 | alarm_cancel(&timr->it.alarm.alarmtimer); | 567 | if (alarm_try_to_cancel(&timr->it.alarm.alarmtimer) < 0) |
568 | return TIMER_RETRY; | ||
538 | 569 | ||
539 | /* start the timer */ | 570 | /* start the timer */ |
540 | timr->it.alarm.interval = timespec_to_ktime(new_setting->it_interval); | 571 | timr->it.alarm.interval = timespec_to_ktime(new_setting->it_interval); |