Skip to content

Commit

Permalink
posix-cpu-timers: Cleanup the firing logic
Browse files Browse the repository at this point in the history
The firing flag of a posix CPU timer is tristate:

  0: when the timer is not about to deliver a signal

  1: when the timer has expired, but the signal has not been delivered yet

 -1: when the timer was queued for signal delivery and a rearm operation
     raced against it and supressed the signal delivery.

This is a pointless exercise as this can be simply expressed with a
boolean. Only if set, the signal is delivered. This makes delete and rearm
consistent with the rest of the posix timers.

Convert firing to bool and fixup the usage sites accordingly and add
comments why the timer cannot be dequeued right away.

Signed-off-by: Thomas Gleixner <[email protected]>
Reviewed-by: Frederic Weisbecker <[email protected]>
Link: https://lore.kernel.org/all/[email protected]
  • Loading branch information
KAGA-KOKO committed Nov 7, 2024
1 parent b06b034 commit bf63568
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 11 deletions.
2 changes: 1 addition & 1 deletion include/linux/posix-timers.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ struct cpu_timer {
struct timerqueue_head *head;
struct pid *pid;
struct list_head elist;
int firing;
bool firing;
struct task_struct __rcu *handling;
};

Expand Down
34 changes: 24 additions & 10 deletions kernel/time/posix-cpu-timers.c
Original file line number Diff line number Diff line change
Expand Up @@ -493,10 +493,18 @@ 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) {
/*
* Prevent signal delivery. The timer cannot be dequeued
* because it is on the firing list which is not protected
* by sighand->lock. The delivery path is waiting for
* the timer lock. So go back, unlock and retry.
*/
timer->it.cpu.firing = false;
ret = TIMER_RETRY;
else
} else {
disarm_timer(timer, p);
}
unlock_task_sighand(p, &flags);
}

Expand Down Expand Up @@ -668,7 +676,13 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
old_expires = cpu_timer_getexpires(ctmr);

if (unlikely(timer->it.cpu.firing)) {
timer->it.cpu.firing = -1;
/*
* Prevent signal delivery. The timer cannot be dequeued
* because it is on the firing list which is not protected
* by sighand->lock. The delivery path is waiting for
* the timer lock. So go back, unlock and retry.
*/
timer->it.cpu.firing = false;
ret = TIMER_RETRY;
} else {
cpu_timer_dequeue(ctmr);
Expand Down Expand Up @@ -809,7 +823,7 @@ static u64 collect_timerqueue(struct timerqueue_head *head,
if (++i == MAX_COLLECTED || now < expires)
return expires;

ctmr->firing = 1;
ctmr->firing = true;
/* See posix_cpu_timer_wait_running() */
rcu_assign_pointer(ctmr->handling, current);
cpu_timer_dequeue(ctmr);
Expand Down Expand Up @@ -1364,7 +1378,7 @@ static void handle_posix_cpu_timers(struct task_struct *tsk)
* timer call will interfere.
*/
list_for_each_entry_safe(timer, next, &firing, it.cpu.elist) {
int cpu_firing;
bool cpu_firing;

/*
* spin_lock() is sufficient here even independent of the
Expand All @@ -1376,13 +1390,13 @@ static void handle_posix_cpu_timers(struct task_struct *tsk)
spin_lock(&timer->it_lock);
list_del_init(&timer->it.cpu.elist);
cpu_firing = timer->it.cpu.firing;
timer->it.cpu.firing = 0;
timer->it.cpu.firing = false;
/*
* The firing flag is -1 if we collided with a reset
* of the timer, which already reported this
* almost-firing as an overrun. So don't generate an event.
* If the firing flag is cleared then this raced with a
* timer rearm/delete operation. So don't generate an
* event.
*/
if (likely(cpu_firing >= 0))
if (likely(cpu_firing))
cpu_timer_fire(timer);
/* See posix_cpu_timer_wait_running() */
rcu_assign_pointer(timer->it.cpu.handling, NULL);
Expand Down

0 comments on commit bf63568

Please sign in to comment.