Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -44,7 +44,8 @@ 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);
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);

+37 −6
Original line number Diff line number Diff line
@@ -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
 *
 * 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];
	unsigned long flags;

	int ret = -1;
	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);
		ret = 1;
	} else
		ret = 0;
out:
	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)
{
@@ -510,7 +538,9 @@ static int alarm_timer_del(struct k_itimer *timr)
	if (!rtcdev)
		return -ENOTSUPP;

	alarm_cancel(&timr->it.alarm.alarmtimer);
	if (alarm_try_to_cancel(&timr->it.alarm.alarmtimer) < 0)
		return TIMER_RETRY;

	return 0;
}

@@ -534,7 +564,8 @@ static int alarm_timer_set(struct k_itimer *timr, int flags,
		alarm_timer_get(timr, old_setting);

	/* 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 */
	timr->it.alarm.interval = timespec_to_ktime(new_setting->it_interval);