Commit 1550dde8 authored by Thomas Gleixner's avatar Thomas Gleixner
Browse files

posix-timers: Add proper state tracking



Right now the state tracking is done by two struct members:

 - it_active:
     A boolean which tracks armed/disarmed state

 - it_signal_seq:
     A sequence counter which is used to invalidate settings
     and prevent rearming

Replace it_active with it_status and keep properly track about the states
in one place.

This allows to reuse it_signal_seq to track reprogramming, disarm and
delete operations in order to drop signals which are related to the state
previous of those operations.

Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Reviewed-by: default avatarFrederic Weisbecker <frederic@kernel.org>
Acked-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241001083835.670337048@linutronix.de
parent cd1e93ae
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -147,7 +147,7 @@ static inline void posix_cputimers_init_work(void) { }
 * @kclock:		Pointer to the k_clock struct handling this timer
 * @it_clock:		The posix timer clock id
 * @it_id:		The posix timer id for identifying the timer
 * @it_active:		Marker that timer is active
 * @it_status:		The status of the timer
 * @it_overrun:		The overrun counter for pending signals
 * @it_overrun_last:	The overrun at the time of the last delivered signal
 * @it_signal_seq:	Sequence count to control signal delivery
@@ -168,7 +168,7 @@ struct k_itimer {
	const struct k_clock	*kclock;
	clockid_t		it_clock;
	timer_t			it_id;
	int			it_active;
	int			it_status;
	s64			it_overrun;
	s64			it_overrun_last;
	unsigned int		it_signal_seq;
+1 −1
Original line number Diff line number Diff line
@@ -585,7 +585,7 @@ static enum alarmtimer_restart alarm_handle_timer(struct alarm *alarm,
		 */
		ptr->it_overrun += __alarm_forward_now(alarm, ptr->it_interval, true);
		++ptr->it_signal_seq;
		ptr->it_active = 1;
		ptr->it_status = POSIX_TIMER_ARMED;
		result = ALARMTIMER_RESTART;
	}
	spin_unlock_irqrestore(&ptr->it_lock, flags);
+8 −7
Original line number Diff line number Diff line
@@ -453,7 +453,6 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
	struct cpu_timer *ctmr = &timer->it.cpu;
	struct posix_cputimer_base *base;

	timer->it_active = 0;
	if (!cpu_timer_dequeue(ctmr))
		return;

@@ -494,11 +493,12 @@ static int posix_cpu_timer_del(struct k_itimer *timer)
		 */
		WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
	} else {
		if (timer->it.cpu.firing)
		if (timer->it.cpu.firing) {
			ret = TIMER_RETRY;
		else
		} else {
			disarm_timer(timer, p);

			timer->it_status = POSIX_TIMER_DISARMED;
		}
		unlock_task_sighand(p, &flags);
	}

@@ -560,7 +560,7 @@ static void arm_timer(struct k_itimer *timer, struct task_struct *p)
	struct cpu_timer *ctmr = &timer->it.cpu;
	u64 newexp = cpu_timer_getexpires(ctmr);

	timer->it_active = 1;
	timer->it_status = POSIX_TIMER_ARMED;
	if (!cpu_timer_enqueue(&base->tqhead, ctmr))
		return;

@@ -586,7 +586,8 @@ static void cpu_timer_fire(struct k_itimer *timer)
{
	struct cpu_timer *ctmr = &timer->it.cpu;

	timer->it_active = 0;
	timer->it_status = POSIX_TIMER_DISARMED;

	if (unlikely(timer->sigq == NULL)) {
		/*
		 * This a special case for clock_nanosleep,
@@ -671,7 +672,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
		ret = TIMER_RETRY;
	} else {
		cpu_timer_dequeue(ctmr);
		timer->it_active = 0;
		timer->it_status = POSIX_TIMER_DISARMED;
	}

	/*
+13 −9
Original line number Diff line number Diff line
@@ -272,7 +272,7 @@ bool posixtimer_deliver_signal(struct kernel_siginfo *info)
	if (timr->it_interval && timr->it_signal_seq == info->si_sys_private) {
		timr->kclock->timer_rearm(timr);

		timr->it_active = 1;
		timr->it_status = POSIX_TIMER_ARMED;
		timr->it_overrun_last = timr->it_overrun;
		timr->it_overrun = -1LL;
		++timr->it_signal_seq;
@@ -292,14 +292,17 @@ bool posixtimer_deliver_signal(struct kernel_siginfo *info)

int posix_timer_queue_signal(struct k_itimer *timr)
{
	enum posix_timer_state state = POSIX_TIMER_DISARMED;
	int ret, si_private = 0;
	enum pid_type type;

	lockdep_assert_held(&timr->it_lock);

	timr->it_active = 0;
	if (timr->it_interval)
	if (timr->it_interval) {
		state = POSIX_TIMER_REQUEUE_PENDING;
		si_private = ++timr->it_signal_seq;
	}
	timr->it_status = state;

	type = !(timr->it_sigev_notify & SIGEV_THREAD_ID) ? PIDTYPE_TGID : PIDTYPE_PID;
	ret = send_sigqueue(timr->sigq, timr->it_pid, type, si_private);
@@ -367,7 +370,7 @@ static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
			timr->it_overrun += hrtimer_forward(timer, now, timr->it_interval);
			ret = HRTIMER_RESTART;
			++timr->it_signal_seq;
			timr->it_active = 1;
			timr->it_status = POSIX_TIMER_ARMED;
		}
	}

@@ -640,10 +643,10 @@ void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting)
	/* interval timer ? */
	if (iv) {
		cur_setting->it_interval = ktime_to_timespec64(iv);
	} else if (!timr->it_active) {
	} else if (timr->it_status == POSIX_TIMER_DISARMED) {
		/*
		 * SIGEV_NONE oneshot timers are never queued and therefore
		 * timr->it_active is always false. The check below
		 * timr->it_status is always DISARMED. The check below
		 * vs. remaining time will handle this case.
		 *
		 * For all other timers there is nothing to update here, so
@@ -888,7 +891,7 @@ int common_timer_set(struct k_itimer *timr, int flags,
	if (kc->timer_try_to_cancel(timr) < 0)
		return TIMER_RETRY;

	timr->it_active = 0;
	timr->it_status = POSIX_TIMER_DISARMED;
	posix_timer_set_common(timr, new_setting);

	/* Keep timer disarmed when it_value is zero */
@@ -901,7 +904,8 @@ int common_timer_set(struct k_itimer *timr, int flags,
	sigev_none = timr->it_sigev_notify == SIGEV_NONE;

	kc->timer_arm(timr, expires, flags & TIMER_ABSTIME, sigev_none);
	timr->it_active = !sigev_none;
	if (!sigev_none)
		timr->it_status = POSIX_TIMER_ARMED;
	return 0;
}

@@ -1000,7 +1004,7 @@ int common_timer_del(struct k_itimer *timer)
	timer->it_interval = 0;
	if (kc->timer_try_to_cancel(timer) < 0)
		return TIMER_RETRY;
	timer->it_active = 0;
	timer->it_status = POSIX_TIMER_DISARMED;
	return 0;
}

+6 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
#define TIMER_RETRY 1

enum posix_timer_state {
	POSIX_TIMER_DISARMED,
	POSIX_TIMER_ARMED,
	POSIX_TIMER_REQUEUE_PENDING,
};

struct k_clock {
	int	(*clock_getres)(const clockid_t which_clock,
				struct timespec64 *tp);