[patch 27/45] posix-timers: Add proper state tracking

From: Thomas Gleixner
Date: Tue Jun 06 2023 - 10:40:31 EST


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: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
---
include/linux/posix-timers.h | 4 ++--
kernel/time/alarmtimer.c | 2 +-
kernel/time/posix-cpu-timers.c | 14 +++++++-------
kernel/time/posix-timers.c | 22 +++++++++++++---------
kernel/time/posix-timers.h | 6 ++++++
5 files changed, 29 insertions(+), 19 deletions(-)

--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -210,7 +210,7 @@ static inline void posix_cputimers_init_
* @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
@@ -231,7 +231,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;
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -574,7 +574,7 @@ static enum alarmtimer_restart alarm_han
*/
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);
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -453,7 +453,6 @@ static void disarm_timer(struct k_itimer
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_
*/
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 *t
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;

@@ -670,7 +670,7 @@ static int posix_cpu_timer_set(struct k_
ret = TIMER_RETRY;
} else {
cpu_timer_dequeue(ctmr);
- timer->it_active = 0;
+ timer->it_status = POSIX_TIMER_DISARMED;
}

/*
@@ -804,7 +804,7 @@ static u64 collect_timerqueue(struct tim
rcu_assign_pointer(ctmr->handling, current);
cpu_timer_dequeue(ctmr);
ktmr = container_of(ctmr, struct k_itimer, it.cpu);
- ktmr->it_active = 0;
+ ktmr->it_status = POSIX_TIMER_DISARMED;
list_add_tail(&ctmr->elist, firing);
}

--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -272,7 +272,7 @@ bool posixtimer_deliver_signal(struct ke
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 ke

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_
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;
}
}

@@ -642,10 +645,10 @@ void common_timer_get(struct k_itimer *t
bool iv = !!timr->it_interval;
ktime_t now, remaining;

- if (!iv && !timr->it_active) {
+ if (!iv && 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
@@ -890,7 +893,7 @@ int common_timer_set(struct k_itimer *ti
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 */
@@ -903,7 +906,8 @@ int common_timer_set(struct k_itimer *ti
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;
}

@@ -1002,7 +1006,7 @@ int common_timer_del(struct k_itimer *ti
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;
}

--- a/kernel/time/posix-timers.h
+++ b/kernel/time/posix-timers.h
@@ -1,6 +1,12 @@
/* 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);