Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+39 -7
+2 -1
include/linux/alarmtimer.h
··· 44 44 void alarm_init(struct alarm *alarm, enum alarmtimer_type type, 45 45 enum alarmtimer_restart (*function)(struct alarm *, ktime_t)); 46 46 void alarm_start(struct alarm *alarm, ktime_t start); 47 - void alarm_cancel(struct alarm *alarm); 47 + int alarm_try_to_cancel(struct alarm *alarm); 48 + int alarm_cancel(struct alarm *alarm); 48 49 49 50 u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval); 50 51
+37 -6
kernel/time/alarmtimer.c
··· 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 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 347 struct alarm_base *base = &alarm_bases[alarm->type]; 345 348 unsigned long flags; 346 - 349 + int ret = -1; 347 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 356 alarmtimer_remove(base, alarm); 357 + ret = 1; 358 + } else 359 + ret = 0; 360 + out: 350 361 spin_unlock_irqrestore(&base->lock, flags); 362 + return ret; 351 363 } 352 364 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 + } 353 381 354 382 355 383 u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval) ··· 538 510 if (!rtcdev) 539 511 return -ENOTSUPP; 540 512 541 - alarm_cancel(&timr->it.alarm.alarmtimer); 513 + if (alarm_try_to_cancel(&timr->it.alarm.alarmtimer) < 0) 514 + return TIMER_RETRY; 515 + 542 516 return 0; 543 517 } 544 518 ··· 564 534 alarm_timer_get(timr, old_setting); 565 535 566 536 /* If the timer was already set, cancel it */ 567 - alarm_cancel(&timr->it.alarm.alarmtimer); 537 + if (alarm_try_to_cancel(&timr->it.alarm.alarmtimer) < 0) 538 + return TIMER_RETRY; 568 539 569 540 /* start the timer */ 570 541 timr->it.alarm.interval = timespec_to_ktime(new_setting->it_interval);