* [patch V7 00/21] posix-timers: Cure the SIG_IGN mess
@ 2024-11-05 8:14 Thomas Gleixner
2024-11-05 8:14 ` [patch V7 01/21] posix-cpu-timers: Correctly update timer status in posix_cpu_timer_del() Thomas Gleixner
` (20 more replies)
0 siblings, 21 replies; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
This are the remaining bits to cure the SIG_IGN mess. Version 5 can be found
here:
https://lore.kernel.org/lkml/20241001083138.922192481@linutronix.de
Last year I reread a 15 years old comment about the SIG_IGN problem:
"FIXME: What we really want, is to stop this timer completely and restart
it in case the SIG_IGN is removed. This is a non trivial change which
involves sighand locking (sigh !), which we don't want to do late in the
release cycle. ... A more complex fix which solves also another related
inconsistency is already in the pipeline."
The embarrasing part was that I put that comment in back then. So I went
back and rumaged through old notes as I completely had forgotten why our
attempts to fix this back then failed.
It turned out that the comment is about right: sighand locking and life
time issues. So I sat down with the old notes and started to wrap my head
around this again.
The problem to solve:
Posix interval timers are not rearmed automatically by the kernel for
various reasons:
1) To prevent DoS by extremly short intervals.
2) To avoid timer overhead when a signal is pending and has not
yet been delivered.
This is achieved by queueing the signal at timer expiry and rearming the
timer at signal delivery to user space. This puts the rearming basically
under scheduler control and the work happens in context of the task which
asked for the signal.
There is a problem with that vs. SIG_IGN. If a signal has SIG_IGN installed
as handler, the related signals are discarded. So in case of posix interval
timers this means that such a timer is never rearmed even when SIG_IGN is
replaced later with a real handler (including SIG_DFL).
To work around that the kernel self rearms those timers and throttles them
when the interval is smaller than a tick to prevent a DoS.
That just keeps timers ticking, which obviously has effects on power and
just creates work for nothing.
So ideally these timers should be stopped and rearmed when SIG_IGN is
replaced, which aligns with the regular handling of posix timers.
Sounds trivial, but isn't:
1) Lock ordering.
The timer lock cannot be taken with sighand lock held which is
problematic vs. the atomicity of sigaction().
2) Life time rules
The timer and the sigqueue are separate entities which requires a
lookup of the timer ID in the signal rearm code. This can be handled,
but the separate life time rules are not necessarily robust.
3) Finding the relevant timers
Obviosly it is possible to walk the posix timer list under sighand
lock and handle it from there. That can be expensive especially in the
case that there are no affected timers as the walk would just end up
doing nothing.
The following series is a new and this time actually working attempt to
solve this. It addresses it by:
1) Embedding the preallocated sigqueue into struct k_itimer, which makes
the life time rules way simpler and just needs a trivial reference
count.
2) Having a separate list in task::signal on which ignored timers are
queued.
This avoids walking a potentially large timer list for nothing on a
SIG_IGN to handler transition.
3) Requeueing the timers signal in the relevant signal queue so the timer
is rearmed when the signal is actually delivered
That turned out to be the least complicated way to address the sighand
lock vs. timer lock ordering issue.
With that timers which have their signal ignored are not longer self
rearmed and the relevant workarounds including throttling for DoS
prevention are removed.
The series is based on:
git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip timers/core
The series is also available from git:
git://git.kernel.org/pub/scm/linux/kernel/git/tglx/devel.git posixt-v7
Changes vs. V6:
- Update timer state consistently in posix_cpu_timer_del() - Frederic
- Cured the timer delete/exit issue vs. SIG_IGN - Frederic
- Use the proper list head in the exit path - Frederic
- Picked up Reviewed-by tags as appropriate
Thanks,
tglx
---
drivers/power/supply/charger-manager.c | 3
fs/proc/base.c | 4
fs/timerfd.c | 4
include/linux/alarmtimer.h | 10
include/linux/posix-timers.h | 61 +++++
include/linux/sched/signal.h | 4
include/uapi/asm-generic/siginfo.h | 2
init/init_task.c | 5
kernel/fork.c | 1
kernel/signal.c | 343 ++++++++++++++++++++++-----------
kernel/time/alarmtimer.c | 87 +-------
kernel/time/posix-cpu-timers.c | 59 +++--
kernel/time/posix-timers.c | 234 ++++++++++------------
kernel/time/posix-timers.h | 2
net/netfilter/xt_IDLETIMER.c | 4
15 files changed, 463 insertions(+), 360 deletions(-)
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 01/21] posix-cpu-timers: Correctly update timer status in posix_cpu_timer_del()
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-05 12:02 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 02/21] posix-timers: Make signal delivery consistent Thomas Gleixner
` (19 subsequent siblings)
20 siblings, 2 replies; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
If posix_cpu_timer_del() exits early due to task not found or sighand
invalid, it fails to clear the state of the timer. That's harmless but
inconsistent.
These early exits are accounted as successful delete. Move the update of
the timer state into the success return path, so all "successful" deletions
are handled.
Reported-by: Frederic Weisbecker <frederic@kernel.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
V7: New patch
---
kernel/time/posix-cpu-timers.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -493,20 +493,20 @@ 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);
}
out:
rcu_read_unlock();
- if (!ret)
- put_pid(ctmr->pid);
+ if (!ret) {
+ put_pid(ctmr->pid);
+ timer->it_status = POSIX_TIMER_DISARMED;
+ }
return ret;
}
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 02/21] posix-timers: Make signal delivery consistent
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
2024-11-05 8:14 ` [patch V7 01/21] posix-cpu-timers: Correctly update timer status in posix_cpu_timer_del() Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 03/21] posix-timers: Make signal overrun accounting sensible Thomas Gleixner
` (18 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
From: Thomas Gleixner <tglx@linutronix.de>
Signals of timers which are reprogammed, disarmed or deleted can deliver
signals related to the past. The POSIX spec is blury about this:
- "The effect of disarming or resetting a timer with pending expiration
notifications is unspecified."
- "The disposition of pending signals for the deleted timer is
unspecified."
In both cases it is reasonable to expect that pending signals are
discarded. Especially in the reprogramming case it does not make sense to
account for previous overruns or to deliver a signal for a timer which has
been disarmed. This makes the behaviour consistent and understandable.
Remove the si_sys_private check from the signal delivery code and invoke
posix_timer_deliver_signal() unconditionally for posix timer related
signals.
Change posix_timer_deliver_signal() so it controls the actual signal
delivery via the return value. It now instructs the signal code to drop the
signal when:
1) The timer does not longer exist in the hash table
2) The timer signal_seq value is not the same as the si_sys_private value
which was set when the signal was queued.
This is also a preparatory change to embed the sigqueue into the k_itimer
structure, which in turn allows to remove the si_sys_private magic.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
---
V6: Remove the sequence increment from delivery and turn the requeue
pending check into a WARN_ON_ONCE() (Frederic)
Move the sequence increment into the delete hook so that the exit
cleanup path is covered too
---
include/linux/posix-timers.h | 2 --
kernel/signal.c | 6 ++----
kernel/time/posix-cpu-timers.c | 2 +-
kernel/time/posix-timers.c | 28 ++++++++++++++++------------
4 files changed, 19 insertions(+), 19 deletions(-)
---
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -137,8 +137,6 @@ static inline void clear_posix_cputimers
static inline void posix_cputimers_init_work(void) { }
#endif
-#define REQUEUE_PENDING 1
-
/**
* struct k_itimer - POSIX.1b interval timer structure.
* @list: List head for binding the timer to signals->posix_timers
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -550,10 +550,8 @@ static void collect_signal(int sig, stru
list_del_init(&first->list);
copy_siginfo(info, &first->info);
- *resched_timer =
- (first->flags & SIGQUEUE_PREALLOC) &&
- (info->si_code == SI_TIMER) &&
- (info->si_sys_private);
+ *resched_timer = (first->flags & SIGQUEUE_PREALLOC) &&
+ (info->si_code == SI_TIMER);
__sigqueue_free(first);
} else {
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -746,7 +746,7 @@ static void __posix_cpu_timer_get(struct
* - Timers which expired, but the signal has not yet been
* delivered
*/
- if (iv && ((timer->it_signal_seq & REQUEUE_PENDING) || sigev_none))
+ if (iv && timer->it_status != POSIX_TIMER_ARMED)
expires = bump_cpu_timer(timer, now);
else
expires = cpu_timer_getexpires(&timer->it.cpu);
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -269,7 +269,10 @@ bool posixtimer_deliver_signal(struct ke
if (!timr)
goto out;
- if (timr->it_interval && timr->it_signal_seq == info->si_sys_private) {
+ if (timr->it_signal_seq != info->si_sys_private)
+ goto out_unlock;
+
+ if (timr->it_interval && !WARN_ON_ONCE(timr->it_status != POSIX_TIMER_REQUEUE_PENDING)) {
timr->kclock->timer_rearm(timr);
timr->it_status = POSIX_TIMER_ARMED;
@@ -281,6 +284,7 @@ bool posixtimer_deliver_signal(struct ke
}
ret = true;
+out_unlock:
unlock_timer(timr, flags);
out:
spin_lock(¤t->sighand->siglock);
@@ -293,19 +297,18 @@ 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;
+ int ret;
lockdep_assert_held(&timr->it_lock);
- 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);
+ ret = send_sigqueue(timr->sigq, timr->it_pid, type, timr->it_signal_seq);
/* If we failed to send the signal the timer stops. */
return ret > 0;
}
@@ -663,7 +666,7 @@ void common_timer_get(struct k_itimer *t
* is a SIGEV_NONE timer move the expiry time forward by intervals,
* so expiry is > now.
*/
- if (iv && (timr->it_signal_seq & REQUEUE_PENDING || sig_none))
+ if (iv && timr->it_status != POSIX_TIMER_ARMED)
timr->it_overrun += kc->timer_forward(timr, now);
remaining = kc->timer_remaining(timr, now);
@@ -863,8 +866,6 @@ void posix_timer_set_common(struct k_iti
else
timer->it_interval = 0;
- /* Prevent reloading in case there is a signal pending */
- timer->it_signal_seq = (timer->it_signal_seq + 2) & ~REQUEUE_PENDING;
/* Reset overrun accounting */
timer->it_overrun_last = 0;
timer->it_overrun = -1LL;
@@ -882,8 +883,6 @@ int common_timer_set(struct k_itimer *ti
if (old_setting)
common_timer_get(timr, old_setting);
- /* Prevent rearming by clearing the interval */
- timr->it_interval = 0;
/*
* Careful here. On SMP systems the timer expiry function could be
* active and spinning on timr->it_lock.
@@ -933,6 +932,9 @@ static int do_timer_settime(timer_t time
if (old_spec64)
old_spec64->it_interval = ktime_to_timespec64(timr->it_interval);
+ /* Prevent signal delivery and rearming. */
+ timr->it_signal_seq++;
+
kc = timr->kclock;
if (WARN_ON_ONCE(!kc || !kc->timer_set))
error = -EINVAL;
@@ -1001,7 +1003,6 @@ int common_timer_del(struct k_itimer *ti
{
const struct k_clock *kc = timer->kclock;
- timer->it_interval = 0;
if (kc->timer_try_to_cancel(timer) < 0)
return TIMER_RETRY;
timer->it_status = POSIX_TIMER_DISARMED;
@@ -1012,6 +1013,9 @@ static inline int timer_delete_hook(stru
{
const struct k_clock *kc = timer->kclock;
+ /* Prevent signal delivery and rearming. */
+ timer->it_signal_seq++;
+
if (WARN_ON_ONCE(!kc || !kc->timer_del))
return -EINVAL;
return kc->timer_del(timer);
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 03/21] posix-timers: Make signal overrun accounting sensible
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
2024-11-05 8:14 ` [patch V7 01/21] posix-cpu-timers: Correctly update timer status in posix_cpu_timer_del() Thomas Gleixner
2024-11-05 8:14 ` [patch V7 02/21] posix-timers: Make signal delivery consistent Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 04/21] posix-cpu-timers: Cleanup the firing logic Thomas Gleixner
` (17 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
The handling of the timer overrun in the signal code is inconsistent as it
takes previous overruns into account. This is just wrong as after the
reprogramming of a timer the overrun count starts over from a clean state,
i.e. 0.
Don't touch info::si_overrun in send_sigqueue() and only store the overrun
value at signal delivery time, which is computed from the timer itself
relative to the expiry time.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
V6: Fold the timer_overrun_to_int() cleanup from Frederic and remove all
overrun fiddling from the signal path.
---
kernel/signal.c | 6 ------
kernel/time/posix-timers.c | 11 ++++++-----
2 files changed, 6 insertions(+), 11 deletions(-)
---
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1968,15 +1968,9 @@ int send_sigqueue(struct sigqueue *q, st
ret = 0;
if (unlikely(!list_empty(&q->list))) {
- /*
- * If an SI_TIMER entry is already queue just increment
- * the overrun count.
- */
- q->info.si_overrun++;
result = TRACE_SIGNAL_ALREADY_PENDING;
goto out;
}
- q->info.si_overrun = 0;
signalfd_notify(t, sig);
pending = (type != PIDTYPE_PID) ? &t->signal->shared_pending : &t->pending;
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -233,11 +233,12 @@ static __init int init_posix_timers(void
* The siginfo si_overrun field and the return value of timer_getoverrun(2)
* are of type int. Clamp the overrun value to INT_MAX
*/
-static inline int timer_overrun_to_int(struct k_itimer *timr, int baseval)
+static inline int timer_overrun_to_int(struct k_itimer *timr)
{
- s64 sum = timr->it_overrun_last + (s64)baseval;
+ if (timr->it_overrun_last > (s64)INT_MAX)
+ return INT_MAX;
- return sum > (s64)INT_MAX ? INT_MAX : (int)sum;
+ return (int)timr->it_overrun_last;
}
static void common_hrtimer_rearm(struct k_itimer *timr)
@@ -280,7 +281,7 @@ bool posixtimer_deliver_signal(struct ke
timr->it_overrun = -1LL;
++timr->it_signal_seq;
- info->si_overrun = timer_overrun_to_int(timr, info->si_overrun);
+ info->si_overrun = timer_overrun_to_int(timr);
}
ret = true;
@@ -774,7 +775,7 @@ SYSCALL_DEFINE1(timer_getoverrun, timer_
if (!timr)
return -EINVAL;
- overrun = timer_overrun_to_int(timr, 0);
+ overrun = timer_overrun_to_int(timr);
unlock_timer(timr, flags);
return overrun;
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 04/21] posix-cpu-timers: Cleanup the firing logic
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
` (2 preceding siblings ...)
2024-11-05 8:14 ` [patch V7 03/21] posix-timers: Make signal overrun accounting sensible Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 05/21] posix-cpu-timers: Use dedicated flag for CPU timer nanosleep Thomas Gleixner
` (16 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
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 <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
---
V6: New patch after detecting the tristate mismatch vs. bool from the
patch which introduced the nanosleep flag.
---
include/linux/posix-timers.h | 2 +-
kernel/time/posix-cpu-timers.c | 34 ++++++++++++++++++++++++----------
2 files changed, 25 insertions(+), 11 deletions(-)
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -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;
};
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -493,10 +493,18 @@ 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) {
+ /*
+ * 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);
}
@@ -668,7 +676,13 @@ static int posix_cpu_timer_set(struct k_
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);
@@ -809,7 +823,7 @@ static u64 collect_timerqueue(struct tim
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);
@@ -1364,7 +1378,7 @@ static void handle_posix_cpu_timers(stru
* 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
@@ -1376,13 +1390,13 @@ static void handle_posix_cpu_timers(stru
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);
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 05/21] posix-cpu-timers: Use dedicated flag for CPU timer nanosleep
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
` (3 preceding siblings ...)
2024-11-05 8:14 ` [patch V7 04/21] posix-cpu-timers: Cleanup the firing logic Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 06/21] posix-timers: Add a refcount to struct k_itimer Thomas Gleixner
` (15 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
POSIX CPU timer nanosleep creates a k_itimer on stack and uses the sigq
pointer to detect the nanosleep case in the expiry function.
Prepare for embedding sigqueue into struct k_itimer by using a dedicated
flag for nanosleep.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
include/linux/posix-timers.h | 2 ++
kernel/time/posix-cpu-timers.c | 3 ++-
2 files changed, 4 insertions(+), 1 deletion(-)
---
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -42,6 +42,7 @@ static inline int clockid_to_fd(const cl
* @pid: Pointer to target task PID
* @elist: List head for the expiry list
* @firing: Timer is currently firing
+ * @nanosleep: Timer is used for nanosleep and is not a regular posix-timer
* @handling: Pointer to the task which handles expiry
*/
struct cpu_timer {
@@ -50,6 +51,7 @@ struct cpu_timer {
struct pid *pid;
struct list_head elist;
bool firing;
+ bool nanosleep;
struct task_struct __rcu *handling;
};
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -596,7 +596,7 @@ static void cpu_timer_fire(struct k_itim
timer->it_status = POSIX_TIMER_DISARMED;
- if (unlikely(timer->sigq == NULL)) {
+ if (unlikely(ctmr->nanosleep)) {
/*
* This a special case for clock_nanosleep,
* not a normal timer from sys_timer_create.
@@ -1493,6 +1493,7 @@ static int do_cpu_nanosleep(const clocki
timer.it_overrun = -1;
error = posix_cpu_timer_create(&timer);
timer.it_process = current;
+ timer.it.cpu.nanosleep = true;
if (!error) {
static struct itimerspec64 zero_it;
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 06/21] posix-timers: Add a refcount to struct k_itimer
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
` (4 preceding siblings ...)
2024-11-05 8:14 ` [patch V7 05/21] posix-cpu-timers: Use dedicated flag for CPU timer nanosleep Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 07/21] signal: Split up __sigqueue_alloc() Thomas Gleixner
` (14 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
To cure the SIG_IGN handling for posix interval timers, the preallocated
sigqueue needs to be embedded into struct k_itimer to prevent life time
races of all sorts.
To make that work correctly it needs reference counting so that timer
deletion does not free the timer prematuraly when there is a signal queued
or delivered concurrently.
Add a rcuref to the posix timer part.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
include/linux/posix-timers.h | 14 ++++++++++++++
kernel/time/posix-timers.c | 7 ++++---
2 files changed, 18 insertions(+), 3 deletions(-)
---
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -6,11 +6,13 @@
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/posix-timers_types.h>
+#include <linux/rcuref.h>
#include <linux/spinlock.h>
#include <linux/timerqueue.h>
struct kernel_siginfo;
struct task_struct;
+struct k_itimer;
static inline clockid_t make_process_cpuclock(const unsigned int pid,
const clockid_t clock)
@@ -105,6 +107,7 @@ static inline void posix_cputimers_rt_wa
void posixtimer_rearm_itimer(struct task_struct *p);
bool posixtimer_deliver_signal(struct kernel_siginfo *info);
+void posixtimer_free_timer(struct k_itimer *timer);
/* Init task static initializer */
#define INIT_CPU_TIMERBASE(b) { \
@@ -129,6 +132,7 @@ static inline void posix_cputimers_group
u64 cpu_limit) { }
static inline void posixtimer_rearm_itimer(struct task_struct *p) { }
static inline bool posixtimer_deliver_signal(struct kernel_siginfo *info) { return false; }
+static inline void posixtimer_free_timer(struct k_itimer *timer) { }
#endif
#ifdef CONFIG_POSIX_CPU_TIMERS_TASK_WORK
@@ -156,6 +160,7 @@ static inline void posix_cputimers_init_
* @it_signal: Pointer to the creators signal struct
* @it_pid: The pid of the process/task targeted by the signal
* @it_process: The task to wakeup on clock_nanosleep (CPU timers)
+ * @rcuref: Reference count for life time management
* @sigq: Pointer to preallocated sigqueue
* @it: Union representing the various posix timer type
* internals.
@@ -180,6 +185,7 @@ struct k_itimer {
struct task_struct *it_process;
};
struct sigqueue *sigq;
+ rcuref_t rcuref;
union {
struct {
struct hrtimer timer;
@@ -200,4 +206,12 @@ void set_process_cpu_timer(struct task_s
int update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new);
+#ifdef CONFIG_POSIX_TIMERS
+static inline void posixtimer_putref(struct k_itimer *tmr)
+{
+ if (rcuref_put(&tmr->rcuref))
+ posixtimer_free_timer(tmr);
+}
+#endif /* !CONFIG_POSIX_TIMERS */
+
#endif
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -417,10 +417,11 @@ static struct k_itimer * alloc_posix_tim
return NULL;
}
clear_siginfo(&tmr->sigq->info);
+ rcuref_init(&tmr->rcuref, 1);
return tmr;
}
-static void posix_timer_free(struct k_itimer *tmr)
+void posixtimer_free_timer(struct k_itimer *tmr)
{
put_pid(tmr->it_pid);
sigqueue_free(tmr->sigq);
@@ -432,7 +433,7 @@ static void posix_timer_unhash_and_free(
spin_lock(&hash_lock);
hlist_del_rcu(&tmr->t_hash);
spin_unlock(&hash_lock);
- posix_timer_free(tmr);
+ posixtimer_putref(tmr);
}
static int common_timer_create(struct k_itimer *new_timer)
@@ -467,7 +468,7 @@ static int do_timer_create(clockid_t whi
*/
new_timer_id = posix_timer_add(new_timer);
if (new_timer_id < 0) {
- posix_timer_free(new_timer);
+ posixtimer_free_timer(new_timer);
return new_timer_id;
}
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 07/21] signal: Split up __sigqueue_alloc()
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
` (5 preceding siblings ...)
2024-11-05 8:14 ` [patch V7 06/21] posix-timers: Add a refcount to struct k_itimer Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 08/21] signal: Provide posixtimer_sigqueue_init() Thomas Gleixner
` (13 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
To cure the SIG_IGN handling for posix interval timers, the preallocated
sigqueue needs to be embedded into struct k_itimer to prevent life time
races of all sorts.
Reorganize __sigqueue_alloc() so the ucounts retrieval and the
initialization can be used independently.
No functional change.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
kernel/signal.c | 52 +++++++++++++++++++++++++++++++++++-----------------
1 file changed, 35 insertions(+), 17 deletions(-)
---
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -396,16 +396,9 @@ void task_join_group_stop(struct task_st
task_set_jobctl_pending(task, mask | JOBCTL_STOP_PENDING);
}
-/*
- * allocate a new signal queue record
- * - this may be called without locks if and only if t == current, otherwise an
- * appropriate lock must be held to stop the target task from exiting
- */
-static struct sigqueue *
-__sigqueue_alloc(int sig, struct task_struct *t, gfp_t gfp_flags,
- int override_rlimit, const unsigned int sigqueue_flags)
+static struct ucounts *sig_get_ucounts(struct task_struct *t, int sig,
+ int override_rlimit)
{
- struct sigqueue *q = NULL;
struct ucounts *ucounts;
long sigpending;
@@ -424,19 +417,44 @@ static struct sigqueue *
if (!sigpending)
return NULL;
- if (override_rlimit || likely(sigpending <= task_rlimit(t, RLIMIT_SIGPENDING))) {
- q = kmem_cache_alloc(sigqueue_cachep, gfp_flags);
- } else {
+ if (unlikely(!override_rlimit && sigpending > task_rlimit(t, RLIMIT_SIGPENDING))) {
+ dec_rlimit_put_ucounts(ucounts, UCOUNT_RLIMIT_SIGPENDING);
print_dropped_signal(sig);
+ return NULL;
}
- if (unlikely(q == NULL)) {
+ return ucounts;
+}
+
+static void __sigqueue_init(struct sigqueue *q, struct ucounts *ucounts,
+ const unsigned int sigqueue_flags)
+{
+ INIT_LIST_HEAD(&q->list);
+ q->flags = sigqueue_flags;
+ q->ucounts = ucounts;
+}
+
+/*
+ * allocate a new signal queue record
+ * - this may be called without locks if and only if t == current, otherwise an
+ * appropriate lock must be held to stop the target task from exiting
+ */
+static struct sigqueue *__sigqueue_alloc(int sig, struct task_struct *t, gfp_t gfp_flags,
+ int override_rlimit, const unsigned int sigqueue_flags)
+{
+ struct ucounts *ucounts = sig_get_ucounts(t, sig, override_rlimit);
+ struct sigqueue *q;
+
+ if (!ucounts)
+ return NULL;
+
+ q = kmem_cache_alloc(sigqueue_cachep, gfp_flags);
+ if (!q) {
dec_rlimit_put_ucounts(ucounts, UCOUNT_RLIMIT_SIGPENDING);
- } else {
- INIT_LIST_HEAD(&q->list);
- q->flags = sigqueue_flags;
- q->ucounts = ucounts;
+ return NULL;
}
+
+ __sigqueue_init(q, ucounts, sigqueue_flags);
return q;
}
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 08/21] signal: Provide posixtimer_sigqueue_init()
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
` (6 preceding siblings ...)
2024-11-05 8:14 ` [patch V7 07/21] signal: Split up __sigqueue_alloc() Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 09/21] posix-timers: Store PID type in the timer Thomas Gleixner
` (12 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
To cure the SIG_IGN handling for posix interval timers, the preallocated
sigqueue needs to be embedded into struct k_itimer to prevent life time
races of all sorts.
Provide a new function to initialize the embedded sigqueue to prepare for
that.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
include/linux/posix-timers.h | 2 ++
kernel/signal.c | 11 +++++++++++
2 files changed, 13 insertions(+)
---
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -12,6 +12,7 @@
struct kernel_siginfo;
struct task_struct;
+struct sigqueue;
struct k_itimer;
static inline clockid_t make_process_cpuclock(const unsigned int pid,
@@ -106,6 +107,7 @@ static inline void posix_cputimers_rt_wa
}
void posixtimer_rearm_itimer(struct task_struct *p);
+bool posixtimer_init_sigqueue(struct sigqueue *q);
bool posixtimer_deliver_signal(struct kernel_siginfo *info);
void posixtimer_free_timer(struct k_itimer *timer);
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1905,6 +1905,17 @@ void flush_itimer_signals(void)
__flush_itimer_signals(&tsk->signal->shared_pending);
}
+bool posixtimer_init_sigqueue(struct sigqueue *q)
+{
+ struct ucounts *ucounts = sig_get_ucounts(current, -1, 0);
+
+ if (!ucounts)
+ return false;
+ clear_siginfo(&q->info);
+ __sigqueue_init(q, ucounts, SIGQUEUE_PREALLOC);
+ return true;
+}
+
struct sigqueue *sigqueue_alloc(void)
{
return __sigqueue_alloc(-1, current, GFP_KERNEL, 0, SIGQUEUE_PREALLOC);
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 09/21] posix-timers: Store PID type in the timer
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
` (7 preceding siblings ...)
2024-11-05 8:14 ` [patch V7 08/21] signal: Provide posixtimer_sigqueue_init() Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 10/21] signal: Refactor send_sigqueue() Thomas Gleixner
` (11 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
instead of re-evaluating the signal delivery mode everywhere.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
include/linux/posix-timers.h | 2 ++
kernel/time/posix-timers.c | 9 ++++++---
2 files changed, 8 insertions(+), 3 deletions(-)
---
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -5,6 +5,7 @@
#include <linux/alarmtimer.h>
#include <linux/list.h>
#include <linux/mutex.h>
+#include <linux/pid.h>
#include <linux/posix-timers_types.h>
#include <linux/rcuref.h>
#include <linux/spinlock.h>
@@ -180,6 +181,7 @@ struct k_itimer {
s64 it_overrun_last;
unsigned int it_signal_seq;
int it_sigev_notify;
+ enum pid_type it_pid_type;
ktime_t it_interval;
struct signal_struct *it_signal;
union {
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -298,7 +298,6 @@ bool posixtimer_deliver_signal(struct ke
int posix_timer_queue_signal(struct k_itimer *timr)
{
enum posix_timer_state state = POSIX_TIMER_DISARMED;
- enum pid_type type;
int ret;
lockdep_assert_held(&timr->it_lock);
@@ -308,8 +307,7 @@ int posix_timer_queue_signal(struct k_it
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, timr->it_signal_seq);
+ ret = send_sigqueue(timr->sigq, timr->it_pid, timr->it_pid_type, timr->it_signal_seq);
/* If we failed to send the signal the timer stops. */
return ret > 0;
}
@@ -496,6 +494,11 @@ static int do_timer_create(clockid_t whi
new_timer->it_pid = get_pid(task_tgid(current));
}
+ if (new_timer->it_sigev_notify & SIGEV_THREAD_ID)
+ new_timer->it_pid_type = PIDTYPE_PID;
+ else
+ new_timer->it_pid_type = PIDTYPE_TGID;
+
new_timer->sigq->info.si_tid = new_timer->it_id;
new_timer->sigq->info.si_code = SI_TIMER;
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 10/21] signal: Refactor send_sigqueue()
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
` (8 preceding siblings ...)
2024-11-05 8:14 ` [patch V7 09/21] posix-timers: Store PID type in the timer Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 11/21] signal: Replace resched_timer logic Thomas Gleixner
` (10 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
To handle posix timers which have their signal ignored via SIG_IGN properly
it is required to requeue a ignored signal for delivery when SIG_IGN is
lifted so the timer gets rearmed.
Split the required code out of send_sigqueue() so it can be reused in
context of sigaction().
While at it rename send_sigqueue() to posixtimer_send_sigqueue() so its
clear what this is about.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
include/linux/posix-timers.h | 1
include/linux/sched/signal.h | 1
kernel/signal.c | 82 +++++++++++++++++++++++--------------------
kernel/time/posix-timers.c | 2 -
4 files changed, 47 insertions(+), 39 deletions(-)
---
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -109,6 +109,7 @@ static inline void posix_cputimers_rt_wa
void posixtimer_rearm_itimer(struct task_struct *p);
bool posixtimer_init_sigqueue(struct sigqueue *q);
+int posixtimer_send_sigqueue(struct k_itimer *tmr);
bool posixtimer_deliver_signal(struct kernel_siginfo *info);
void posixtimer_free_timer(struct k_itimer *timer);
--- a/include/linux/sched/signal.h
+++ b/include/linux/sched/signal.h
@@ -340,7 +340,6 @@ extern int send_sig(int, struct task_str
extern int zap_other_threads(struct task_struct *p);
extern struct sigqueue *sigqueue_alloc(void);
extern void sigqueue_free(struct sigqueue *);
-extern int send_sigqueue(struct sigqueue *, struct pid *, enum pid_type, int si_private);
extern int do_sigaction(int, struct k_sigaction *, struct k_sigaction *);
static inline void clear_notify_signal(void)
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1947,40 +1947,54 @@ void sigqueue_free(struct sigqueue *q)
__sigqueue_free(q);
}
-int send_sigqueue(struct sigqueue *q, struct pid *pid, enum pid_type type, int si_private)
+static void posixtimer_queue_sigqueue(struct sigqueue *q, struct task_struct *t, enum pid_type type)
{
- int sig = q->info.si_signo;
struct sigpending *pending;
+ int sig = q->info.si_signo;
+
+ signalfd_notify(t, sig);
+ pending = (type != PIDTYPE_PID) ? &t->signal->shared_pending : &t->pending;
+ list_add_tail(&q->list, &pending->list);
+ sigaddset(&pending->signal, sig);
+ complete_signal(sig, t, type);
+}
+
+/*
+ * This function is used by POSIX timers to deliver a timer signal.
+ * Where type is PIDTYPE_PID (such as for timers with SIGEV_THREAD_ID
+ * set), the signal must be delivered to the specific thread (queues
+ * into t->pending).
+ *
+ * Where type is not PIDTYPE_PID, signals must be delivered to the
+ * process. In this case, prefer to deliver to current if it is in
+ * the same thread group as the target process, which avoids
+ * unnecessarily waking up a potentially idle task.
+ */
+static inline struct task_struct *posixtimer_get_target(struct k_itimer *tmr)
+{
+ struct task_struct *t = pid_task(tmr->it_pid, tmr->it_pid_type);
+
+ if (t && tmr->it_pid_type != PIDTYPE_PID && same_thread_group(t, current))
+ t = current;
+ return t;
+}
+
+int posixtimer_send_sigqueue(struct k_itimer *tmr)
+{
+ struct sigqueue *q = tmr->sigq;
+ int sig = q->info.si_signo;
struct task_struct *t;
unsigned long flags;
int ret, result;
- if (WARN_ON_ONCE(!(q->flags & SIGQUEUE_PREALLOC)))
- return 0;
- if (WARN_ON_ONCE(q->info.si_code != SI_TIMER))
- return 0;
+ guard(rcu)();
- ret = -1;
- rcu_read_lock();
-
- /*
- * This function is used by POSIX timers to deliver a timer signal.
- * Where type is PIDTYPE_PID (such as for timers with SIGEV_THREAD_ID
- * set), the signal must be delivered to the specific thread (queues
- * into t->pending).
- *
- * Where type is not PIDTYPE_PID, signals must be delivered to the
- * process. In this case, prefer to deliver to current if it is in
- * the same thread group as the target process, which avoids
- * unnecessarily waking up a potentially idle task.
- */
- t = pid_task(pid, type);
+ t = posixtimer_get_target(tmr);
if (!t)
- goto ret;
- if (type != PIDTYPE_PID && same_thread_group(t, current))
- t = current;
+ return -1;
+
if (!likely(lock_task_sighand(t, &flags)))
- goto ret;
+ return -1;
/*
* Update @q::info::si_sys_private for posix timer signals with
@@ -1988,30 +2002,24 @@ int send_sigqueue(struct sigqueue *q, st
* decides based on si_sys_private whether to invoke
* posixtimer_rearm() or not.
*/
- q->info.si_sys_private = si_private;
+ q->info.si_sys_private = tmr->it_signal_seq;
ret = 1; /* the signal is ignored */
- result = TRACE_SIGNAL_IGNORED;
- if (!prepare_signal(sig, t, false))
+ if (!prepare_signal(sig, t, false)) {
+ result = TRACE_SIGNAL_IGNORED;
goto out;
+ }
ret = 0;
if (unlikely(!list_empty(&q->list))) {
result = TRACE_SIGNAL_ALREADY_PENDING;
goto out;
}
-
- signalfd_notify(t, sig);
- pending = (type != PIDTYPE_PID) ? &t->signal->shared_pending : &t->pending;
- list_add_tail(&q->list, &pending->list);
- sigaddset(&pending->signal, sig);
- complete_signal(sig, t, type);
+ posixtimer_queue_sigqueue(q, t, tmr->it_pid_type);
result = TRACE_SIGNAL_DELIVERED;
out:
- trace_signal_generate(sig, &q->info, t, type != PIDTYPE_PID, result);
+ trace_signal_generate(sig, &q->info, t, tmr->it_pid_type != PIDTYPE_PID, result);
unlock_task_sighand(t, &flags);
-ret:
- rcu_read_unlock();
return ret;
}
#endif /* CONFIG_POSIX_TIMERS */
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -307,7 +307,7 @@ int posix_timer_queue_signal(struct k_it
timr->it_status = state;
- ret = send_sigqueue(timr->sigq, timr->it_pid, timr->it_pid_type, timr->it_signal_seq);
+ ret = posixtimer_send_sigqueue(timr);
/* If we failed to send the signal the timer stops. */
return ret > 0;
}
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 11/21] signal: Replace resched_timer logic
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
` (9 preceding siblings ...)
2024-11-05 8:14 ` [patch V7 10/21] signal: Refactor send_sigqueue() Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-05 12:08 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 12/21] posix-timers: Embed sigqueue in struct k_itimer Thomas Gleixner
` (9 subsequent siblings)
20 siblings, 2 replies; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
From: Thomas Gleixner <tglx@linutronix.de>
In preparation for handling ignored posix timer signals correctly and
embedding the sigqueue struct into struct k_itimer, hand down a pointer to
the sigqueue struct into posix_timer_deliver_signal() instead of just
having a boolean flag.
No functional change.
Suggested-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: "Eric W. Biederman" <ebiederm@xmission.com>
---
V6: NULLify timer_sigq after again: in dequeue_signal() - Frederic
V5: New patch
---
include/linux/posix-timers.h | 5 +++--
kernel/signal.c | 32 ++++++++++++++++++++------------
kernel/time/posix-timers.c | 2 +-
3 files changed, 24 insertions(+), 15 deletions(-)
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -110,7 +110,7 @@ static inline void posix_cputimers_rt_wa
void posixtimer_rearm_itimer(struct task_struct *p);
bool posixtimer_init_sigqueue(struct sigqueue *q);
int posixtimer_send_sigqueue(struct k_itimer *tmr);
-bool posixtimer_deliver_signal(struct kernel_siginfo *info);
+bool posixtimer_deliver_signal(struct kernel_siginfo *info, struct sigqueue *timer_sigq);
void posixtimer_free_timer(struct k_itimer *timer);
/* Init task static initializer */
@@ -135,7 +135,8 @@ static inline void posix_cputimers_init(
static inline void posix_cputimers_group_init(struct posix_cputimers *pct,
u64 cpu_limit) { }
static inline void posixtimer_rearm_itimer(struct task_struct *p) { }
-static inline bool posixtimer_deliver_signal(struct kernel_siginfo *info) { return false; }
+static inline bool posixtimer_deliver_signal(struct kernel_siginfo *info,
+ struct sigqueue *timer_sigq) { return false; }
static inline void posixtimer_free_timer(struct k_itimer *timer) { }
#endif
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -545,7 +545,7 @@ bool unhandled_signal(struct task_struct
}
static void collect_signal(int sig, struct sigpending *list, kernel_siginfo_t *info,
- bool *resched_timer)
+ struct sigqueue **timer_sigq)
{
struct sigqueue *q, *first = NULL;
@@ -568,10 +568,17 @@ static void collect_signal(int sig, stru
list_del_init(&first->list);
copy_siginfo(info, &first->info);
- *resched_timer = (first->flags & SIGQUEUE_PREALLOC) &&
- (info->si_code == SI_TIMER);
-
- __sigqueue_free(first);
+ /*
+ * posix-timer signals are preallocated and freed when the
+ * timer goes away. Either directly or by clearing
+ * SIGQUEUE_PREALLOC so that the next delivery will free
+ * them. Spare the extra round through __sigqueue_free()
+ * which is ignoring preallocated signals.
+ */
+ if (unlikely((first->flags & SIGQUEUE_PREALLOC) && (info->si_code == SI_TIMER)))
+ *timer_sigq = first;
+ else
+ __sigqueue_free(first);
} else {
/*
* Ok, it wasn't in the queue. This must be
@@ -588,12 +595,12 @@ static void collect_signal(int sig, stru
}
static int __dequeue_signal(struct sigpending *pending, sigset_t *mask,
- kernel_siginfo_t *info, bool *resched_timer)
+ kernel_siginfo_t *info, struct sigqueue **timer_sigq)
{
int sig = next_signal(pending, mask);
if (sig)
- collect_signal(sig, pending, info, resched_timer);
+ collect_signal(sig, pending, info, timer_sigq);
return sig;
}
@@ -605,18 +612,19 @@ static int __dequeue_signal(struct sigpe
int dequeue_signal(sigset_t *mask, kernel_siginfo_t *info, enum pid_type *type)
{
struct task_struct *tsk = current;
- bool resched_timer = false;
+ struct sigqueue *timer_sigq;
int signr;
lockdep_assert_held(&tsk->sighand->siglock);
again:
*type = PIDTYPE_PID;
- signr = __dequeue_signal(&tsk->pending, mask, info, &resched_timer);
+ timer_sigq = NULL;
+ signr = __dequeue_signal(&tsk->pending, mask, info, &timer_sigq);
if (!signr) {
*type = PIDTYPE_TGID;
signr = __dequeue_signal(&tsk->signal->shared_pending,
- mask, info, &resched_timer);
+ mask, info, &timer_sigq);
if (unlikely(signr == SIGALRM))
posixtimer_rearm_itimer(tsk);
@@ -642,8 +650,8 @@ int dequeue_signal(sigset_t *mask, kerne
current->jobctl |= JOBCTL_STOP_DEQUEUED;
}
- if (IS_ENABLED(CONFIG_POSIX_TIMERS) && unlikely(resched_timer)) {
- if (!posixtimer_deliver_signal(info))
+ if (IS_ENABLED(CONFIG_POSIX_TIMERS) && unlikely(timer_sigq)) {
+ if (!posixtimer_deliver_signal(info, timer_sigq))
goto again;
}
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -254,7 +254,7 @@ static void common_hrtimer_rearm(struct
* This function is called from the signal delivery code. It decides
* whether the signal should be dropped and rearms interval timers.
*/
-bool posixtimer_deliver_signal(struct kernel_siginfo *info)
+bool posixtimer_deliver_signal(struct kernel_siginfo *info, struct sigqueue *timer_sigq)
{
struct k_itimer *timr;
unsigned long flags;
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 12/21] posix-timers: Embed sigqueue in struct k_itimer
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
` (10 preceding siblings ...)
2024-11-05 8:14 ` [patch V7 11/21] signal: Replace resched_timer logic Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 13/21] signal: Cleanup unused posix-timer leftovers Thomas Gleixner
` (8 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
From: Thomas Gleixner <tglx@linutronix.de>
To cure the SIG_IGN handling for posix interval timers, the preallocated
sigqueue needs to be embedded into struct k_itimer to prevent life time
races of all sorts.
Now that the prerequisites are in place, embed the sigqueue into struct
k_itimer and fixup the relevant usage sites.
Aside of preparing for proper SIG_IGN handling, this spares an extra
allocation.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
V6: Split out the timer related part from the delivery function to
use guard() and avoid gotos
---
fs/proc/base.c | 4 -
include/linux/posix-timers.h | 23 ++++++++++-
kernel/signal.c | 19 +++++----
kernel/time/posix-timers.c | 88 +++++++++++++++++++++++++------------------
4 files changed, 87 insertions(+), 47 deletions(-)
---
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2553,8 +2553,8 @@ static int show_timer(struct seq_file *m
seq_printf(m, "ID: %d\n", timer->it_id);
seq_printf(m, "signal: %d/%px\n",
- timer->sigq->info.si_signo,
- timer->sigq->info.si_value.sival_ptr);
+ timer->sigq.info.si_signo,
+ timer->sigq.info.si_value.sival_ptr);
seq_printf(m, "notify: %s/%s.%d\n",
nstr[notify & ~SIGEV_THREAD_ID],
(notify & SIGEV_THREAD_ID) ? "tid" : "pid",
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -39,6 +39,8 @@ static inline int clockid_to_fd(const cl
#ifdef CONFIG_POSIX_TIMERS
+#include <linux/signal_types.h>
+
/**
* cpu_timer - Posix CPU timer representation for k_itimer
* @node: timerqueue node to queue in the task/sig
@@ -166,7 +168,7 @@ static inline void posix_cputimers_init_
* @it_pid: The pid of the process/task targeted by the signal
* @it_process: The task to wakeup on clock_nanosleep (CPU timers)
* @rcuref: Reference count for life time management
- * @sigq: Pointer to preallocated sigqueue
+ * @sigq: Embedded sigqueue
* @it: Union representing the various posix timer type
* internals.
* @rcu: RCU head for freeing the timer.
@@ -190,7 +192,7 @@ struct k_itimer {
struct pid *it_pid;
struct task_struct *it_process;
};
- struct sigqueue *sigq;
+ struct sigqueue sigq;
rcuref_t rcuref;
union {
struct {
@@ -218,6 +220,23 @@ static inline void posixtimer_putref(str
if (rcuref_put(&tmr->rcuref))
posixtimer_free_timer(tmr);
}
+
+static inline void posixtimer_sigqueue_getref(struct sigqueue *q)
+{
+ struct k_itimer *tmr = container_of(q, struct k_itimer, sigq);
+
+ WARN_ON_ONCE(!rcuref_get(&tmr->rcuref));
+}
+
+static inline void posixtimer_sigqueue_putref(struct sigqueue *q)
+{
+ struct k_itimer *tmr = container_of(q, struct k_itimer, sigq);
+
+ posixtimer_putref(tmr);
+}
+#else /* CONFIG_POSIX_TIMERS */
+static inline void posixtimer_sigqueue_getref(struct sigqueue *q) { }
+static inline void posixtimer_sigqueue_putref(struct sigqueue *q) { }
#endif /* !CONFIG_POSIX_TIMERS */
#endif
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -460,8 +460,10 @@ static struct sigqueue *__sigqueue_alloc
static void __sigqueue_free(struct sigqueue *q)
{
- if (q->flags & SIGQUEUE_PREALLOC)
+ if (q->flags & SIGQUEUE_PREALLOC) {
+ posixtimer_sigqueue_putref(q);
return;
+ }
if (q->ucounts) {
dec_rlimit_put_ucounts(q->ucounts, UCOUNT_RLIMIT_SIGPENDING);
q->ucounts = NULL;
@@ -569,11 +571,11 @@ static void collect_signal(int sig, stru
copy_siginfo(info, &first->info);
/*
- * posix-timer signals are preallocated and freed when the
- * timer goes away. Either directly or by clearing
- * SIGQUEUE_PREALLOC so that the next delivery will free
- * them. Spare the extra round through __sigqueue_free()
- * which is ignoring preallocated signals.
+ * posix-timer signals are preallocated and freed when the last
+ * reference count is dropped in posixtimer_deliver_signal() or
+ * immediately on timer deletion when the signal is not pending.
+ * Spare the extra round through __sigqueue_free() which is
+ * ignoring preallocated signals.
*/
if (unlikely((first->flags & SIGQUEUE_PREALLOC) && (info->si_code == SI_TIMER)))
*timer_sigq = first;
@@ -1989,7 +1991,7 @@ static inline struct task_struct *posixt
int posixtimer_send_sigqueue(struct k_itimer *tmr)
{
- struct sigqueue *q = tmr->sigq;
+ struct sigqueue *q = &tmr->sigq;
int sig = q->info.si_signo;
struct task_struct *t;
unsigned long flags;
@@ -2020,9 +2022,12 @@ int posixtimer_send_sigqueue(struct k_it
ret = 0;
if (unlikely(!list_empty(&q->list))) {
+ /* This holds a reference count already */
result = TRACE_SIGNAL_ALREADY_PENDING;
goto out;
}
+
+ posixtimer_sigqueue_getref(q);
posixtimer_queue_sigqueue(q, t, tmr->it_pid_type);
result = TRACE_SIGNAL_DELIVERED;
out:
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -250,15 +250,40 @@ static void common_hrtimer_rearm(struct
hrtimer_restart(timer);
}
+static bool __posixtimer_deliver_signal(struct kernel_siginfo *info, struct k_itimer *timr)
+{
+ guard(spinlock)(&timr->it_lock);
+
+ /*
+ * Check if the timer is still alive or whether it got modified
+ * since the signal was queued. In either case, don't rearm and
+ * drop the signal.
+ */
+ if (timr->it_signal_seq != info->si_sys_private || WARN_ON_ONCE(!timr->it_signal))
+ return false;
+
+ if (!timr->it_interval || WARN_ON_ONCE(timr->it_status != POSIX_TIMER_REQUEUE_PENDING))
+ return true;
+
+ timr->kclock->timer_rearm(timr);
+ timr->it_status = POSIX_TIMER_ARMED;
+ timr->it_overrun_last = timr->it_overrun;
+ timr->it_overrun = -1LL;
+ ++timr->it_signal_seq;
+ info->si_overrun = timer_overrun_to_int(timr);
+ return true;
+}
+
/*
* This function is called from the signal delivery code. It decides
- * whether the signal should be dropped and rearms interval timers.
+ * whether the signal should be dropped and rearms interval timers. The
+ * timer can be unconditionally accessed as there is a reference held on
+ * it.
*/
bool posixtimer_deliver_signal(struct kernel_siginfo *info, struct sigqueue *timer_sigq)
{
- struct k_itimer *timr;
- unsigned long flags;
- bool ret = false;
+ struct k_itimer *timr = container_of(timer_sigq, struct k_itimer, sigq);
+ bool ret;
/*
* Release siglock to ensure proper locking order versus
@@ -266,28 +291,11 @@ bool posixtimer_deliver_signal(struct ke
*/
spin_unlock(¤t->sighand->siglock);
- timr = lock_timer(info->si_tid, &flags);
- if (!timr)
- goto out;
-
- if (timr->it_signal_seq != info->si_sys_private)
- goto out_unlock;
-
- if (timr->it_interval && !WARN_ON_ONCE(timr->it_status != POSIX_TIMER_REQUEUE_PENDING)) {
- timr->kclock->timer_rearm(timr);
+ ret = __posixtimer_deliver_signal(info, timr);
- timr->it_status = POSIX_TIMER_ARMED;
- timr->it_overrun_last = timr->it_overrun;
- timr->it_overrun = -1LL;
- ++timr->it_signal_seq;
-
- info->si_overrun = timer_overrun_to_int(timr);
- }
- ret = true;
+ /* Drop the reference which was acquired when the signal was queued */
+ posixtimer_putref(timr);
-out_unlock:
- unlock_timer(timr, flags);
-out:
spin_lock(¤t->sighand->siglock);
/* Don't expose the si_sys_private value to userspace */
@@ -404,17 +412,17 @@ static struct pid *good_sigevent(sigeven
}
}
-static struct k_itimer * alloc_posix_timer(void)
+static struct k_itimer *alloc_posix_timer(void)
{
struct k_itimer *tmr = kmem_cache_zalloc(posix_timers_cache, GFP_KERNEL);
if (!tmr)
return tmr;
- if (unlikely(!(tmr->sigq = sigqueue_alloc()))) {
+
+ if (unlikely(!posixtimer_init_sigqueue(&tmr->sigq))) {
kmem_cache_free(posix_timers_cache, tmr);
return NULL;
}
- clear_siginfo(&tmr->sigq->info);
rcuref_init(&tmr->rcuref, 1);
return tmr;
}
@@ -422,7 +430,8 @@ static struct k_itimer * alloc_posix_tim
void posixtimer_free_timer(struct k_itimer *tmr)
{
put_pid(tmr->it_pid);
- sigqueue_free(tmr->sigq);
+ if (tmr->sigq.ucounts)
+ dec_rlimit_put_ucounts(tmr->sigq.ucounts, UCOUNT_RLIMIT_SIGPENDING);
kfree_rcu(tmr, rcu);
}
@@ -484,13 +493,13 @@ static int do_timer_create(clockid_t whi
goto out;
}
new_timer->it_sigev_notify = event->sigev_notify;
- new_timer->sigq->info.si_signo = event->sigev_signo;
- new_timer->sigq->info.si_value = event->sigev_value;
+ new_timer->sigq.info.si_signo = event->sigev_signo;
+ new_timer->sigq.info.si_value = event->sigev_value;
} else {
new_timer->it_sigev_notify = SIGEV_SIGNAL;
- new_timer->sigq->info.si_signo = SIGALRM;
- memset(&new_timer->sigq->info.si_value, 0, sizeof(sigval_t));
- new_timer->sigq->info.si_value.sival_int = new_timer->it_id;
+ new_timer->sigq.info.si_signo = SIGALRM;
+ memset(&new_timer->sigq.info.si_value, 0, sizeof(sigval_t));
+ new_timer->sigq.info.si_value.sival_int = new_timer->it_id;
new_timer->it_pid = get_pid(task_tgid(current));
}
@@ -499,8 +508,8 @@ static int do_timer_create(clockid_t whi
else
new_timer->it_pid_type = PIDTYPE_TGID;
- new_timer->sigq->info.si_tid = new_timer->it_id;
- new_timer->sigq->info.si_code = SI_TIMER;
+ new_timer->sigq.info.si_tid = new_timer->it_id;
+ new_timer->sigq.info.si_code = SI_TIMER;
if (copy_to_user(created_timer_id, &new_timer_id, sizeof (new_timer_id))) {
error = -EFAULT;
@@ -584,7 +593,14 @@ static struct k_itimer *__lock_timer(tim
* 1) Set timr::it_signal to NULL with timr::it_lock held
* 2) Release timr::it_lock
* 3) Remove from the hash under hash_lock
- * 4) Call RCU for removal after the grace period
+ * 4) Put the reference count.
+ *
+ * The reference count might not drop to zero if timr::sigq is
+ * queued. In that case the signal delivery or flush will put the
+ * last reference count.
+ *
+ * When the reference count reaches zero, the timer is scheduled
+ * for RCU removal after the grace period.
*
* Holding rcu_read_lock() accross the lookup ensures that
* the timer cannot be freed.
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 13/21] signal: Cleanup unused posix-timer leftovers
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
` (11 preceding siblings ...)
2024-11-05 8:14 ` [patch V7 12/21] posix-timers: Embed sigqueue in struct k_itimer Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 14/21] posix-timers: Move sequence logic into struct k_itimer Thomas Gleixner
` (7 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
From: Thomas Gleixner <tglx@linutronix.de>
Remove the leftovers of sigqueue preallocation as it's not longer used.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
include/linux/sched/signal.h | 2 --
kernel/signal.c | 39 ++++-----------------------------------
2 files changed, 4 insertions(+), 37 deletions(-)
---
--- a/include/linux/sched/signal.h
+++ b/include/linux/sched/signal.h
@@ -338,8 +338,6 @@ extern void force_fatal_sig(int);
extern void force_exit_sig(int);
extern int send_sig(int, struct task_struct *, int);
extern int zap_other_threads(struct task_struct *p);
-extern struct sigqueue *sigqueue_alloc(void);
-extern void sigqueue_free(struct sigqueue *);
extern int do_sigaction(int, struct k_sigaction *, struct k_sigaction *);
static inline void clear_notify_signal(void)
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -439,8 +439,8 @@ static void __sigqueue_init(struct sigqu
* - this may be called without locks if and only if t == current, otherwise an
* appropriate lock must be held to stop the target task from exiting
*/
-static struct sigqueue *__sigqueue_alloc(int sig, struct task_struct *t, gfp_t gfp_flags,
- int override_rlimit, const unsigned int sigqueue_flags)
+static struct sigqueue *sigqueue_alloc(int sig, struct task_struct *t, gfp_t gfp_flags,
+ int override_rlimit)
{
struct ucounts *ucounts = sig_get_ucounts(t, sig, override_rlimit);
struct sigqueue *q;
@@ -454,7 +454,7 @@ static struct sigqueue *__sigqueue_alloc
return NULL;
}
- __sigqueue_init(q, ucounts, sigqueue_flags);
+ __sigqueue_init(q, ucounts, 0);
return q;
}
@@ -1070,7 +1070,7 @@ static int __send_signal_locked(int sig,
else
override_rlimit = 0;
- q = __sigqueue_alloc(sig, t, GFP_ATOMIC, override_rlimit, 0);
+ q = sigqueue_alloc(sig, t, GFP_ATOMIC, override_rlimit);
if (q) {
list_add_tail(&q->list, &pending->list);
@@ -1926,37 +1926,6 @@ bool posixtimer_init_sigqueue(struct sig
return true;
}
-struct sigqueue *sigqueue_alloc(void)
-{
- return __sigqueue_alloc(-1, current, GFP_KERNEL, 0, SIGQUEUE_PREALLOC);
-}
-
-void sigqueue_free(struct sigqueue *q)
-{
- spinlock_t *lock = ¤t->sighand->siglock;
- unsigned long flags;
-
- if (WARN_ON_ONCE(!(q->flags & SIGQUEUE_PREALLOC)))
- return;
- /*
- * We must hold ->siglock while testing q->list
- * to serialize with collect_signal() or with
- * __exit_signal()->flush_sigqueue().
- */
- spin_lock_irqsave(lock, flags);
- q->flags &= ~SIGQUEUE_PREALLOC;
- /*
- * If it is queued it will be freed when dequeued,
- * like the "regular" sigqueue.
- */
- if (!list_empty(&q->list))
- q = NULL;
- spin_unlock_irqrestore(lock, flags);
-
- if (q)
- __sigqueue_free(q);
-}
-
static void posixtimer_queue_sigqueue(struct sigqueue *q, struct task_struct *t, enum pid_type type)
{
struct sigpending *pending;
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 14/21] posix-timers: Move sequence logic into struct k_itimer
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
` (12 preceding siblings ...)
2024-11-05 8:14 ` [patch V7 13/21] signal: Cleanup unused posix-timer leftovers Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 15/21] signal: Provide ignored_posix_timers list Thomas Gleixner
` (6 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
From: Thomas Gleixner <tglx@linutronix.de>
The posix timer signal handling uses siginfo::si_sys_private for handling
the sequence counter check. That indirection is not longer required and the
sequence count value at signal queueing time can be stored in struct
k_itimer itself.
This removes the requirement of treating siginfo::si_sys_private special as
it's now always zero as the kernel does not touch it anymore.
Suggested-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: "Eric W. Biederman" <ebiederm@xmission.com>
---
V6: Update the comment in siginfo.h
V5: New patch
---
include/linux/posix-timers.h | 2 ++
include/uapi/asm-generic/siginfo.h | 2 +-
kernel/signal.c | 8 +++-----
kernel/time/posix-timers.c | 5 +----
4 files changed, 7 insertions(+), 10 deletions(-)
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -162,6 +162,7 @@ static inline void posix_cputimers_init_
* @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
+ * @it_sigqueue_seq: The sequence count at the point where the signal was queued
* @it_sigev_notify: The notify word of sigevent struct for signal delivery
* @it_interval: The interval for periodic timers
* @it_signal: Pointer to the creators signal struct
@@ -184,6 +185,7 @@ struct k_itimer {
s64 it_overrun;
s64 it_overrun_last;
unsigned int it_signal_seq;
+ unsigned int it_sigqueue_seq;
int it_sigev_notify;
enum pid_type it_pid_type;
ktime_t it_interval;
--- a/include/uapi/asm-generic/siginfo.h
+++ b/include/uapi/asm-generic/siginfo.h
@@ -46,7 +46,7 @@ union __sifields {
__kernel_timer_t _tid; /* timer id */
int _overrun; /* overrun count */
sigval_t _sigval; /* same as below */
- int _sys_private; /* not to be passed to user */
+ int _sys_private; /* Not used by the kernel. Historic leftover. Always 0. */
} _timer;
/* POSIX.1b signals */
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1976,12 +1976,10 @@ int posixtimer_send_sigqueue(struct k_it
return -1;
/*
- * Update @q::info::si_sys_private for posix timer signals with
- * sighand locked to prevent a race against dequeue_signal() which
- * decides based on si_sys_private whether to invoke
- * posixtimer_rearm() or not.
+ * Update @tmr::sigqueue_seq for posix timer signals with sighand
+ * locked to prevent a race against dequeue_signal().
*/
- q->info.si_sys_private = tmr->it_signal_seq;
+ tmr->it_sigqueue_seq = tmr->it_signal_seq;
ret = 1; /* the signal is ignored */
if (!prepare_signal(sig, t, false)) {
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -259,7 +259,7 @@ static bool __posixtimer_deliver_signal(
* since the signal was queued. In either case, don't rearm and
* drop the signal.
*/
- if (timr->it_signal_seq != info->si_sys_private || WARN_ON_ONCE(!timr->it_signal))
+ if (timr->it_signal_seq != timr->it_sigqueue_seq || WARN_ON_ONCE(!timr->it_signal))
return false;
if (!timr->it_interval || WARN_ON_ONCE(timr->it_status != POSIX_TIMER_REQUEUE_PENDING))
@@ -297,9 +297,6 @@ bool posixtimer_deliver_signal(struct ke
posixtimer_putref(timr);
spin_lock(¤t->sighand->siglock);
-
- /* Don't expose the si_sys_private value to userspace */
- info->si_sys_private = 0;
return ret;
}
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 15/21] signal: Provide ignored_posix_timers list
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
` (13 preceding siblings ...)
2024-11-05 8:14 ` [patch V7 14/21] posix-timers: Move sequence logic into struct k_itimer Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 16/21] posix-timers: Handle ignored list on delete and exit Thomas Gleixner
` (5 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
From: Thomas Gleixner <tglx@linutronix.de>
To prepare for handling posix timer signals on sigaction(SIG_IGN) properly,
add a list to task::signal.
This list will be used to queue posix timers so their signal can be
requeued when SIG_IGN is lifted later.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
include/linux/sched/signal.h | 1 +
init/init_task.c | 5 +++--
kernel/fork.c | 1 +
3 files changed, 5 insertions(+), 2 deletions(-)
---
--- a/include/linux/sched/signal.h
+++ b/include/linux/sched/signal.h
@@ -138,6 +138,7 @@ struct signal_struct {
/* POSIX.1b Interval Timers */
unsigned int next_posix_timer_id;
struct hlist_head posix_timers;
+ struct hlist_head ignored_posix_timers;
/* ITIMER_REAL timer for the process */
struct hrtimer real_timer;
--- a/init/init_task.c
+++ b/init/init_task.c
@@ -30,8 +30,9 @@ static struct signal_struct init_signals
.cred_guard_mutex = __MUTEX_INITIALIZER(init_signals.cred_guard_mutex),
.exec_update_lock = __RWSEM_INITIALIZER(init_signals.exec_update_lock),
#ifdef CONFIG_POSIX_TIMERS
- .posix_timers = HLIST_HEAD_INIT,
- .cputimer = {
+ .posix_timers = HLIST_HEAD_INIT,
+ .ignored_posix_timers = HLIST_HEAD_INIT,
+ .cputimer = {
.cputime_atomic = INIT_CPUTIME_ATOMIC,
},
#endif
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1864,6 +1864,7 @@ static int copy_signal(unsigned long clo
#ifdef CONFIG_POSIX_TIMERS
INIT_HLIST_HEAD(&sig->posix_timers);
+ INIT_HLIST_HEAD(&sig->ignored_posix_timers);
hrtimer_init(&sig->real_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
sig->real_timer.function = it_real_fn;
#endif
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 16/21] posix-timers: Handle ignored list on delete and exit
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
` (14 preceding siblings ...)
2024-11-05 8:14 ` [patch V7 15/21] signal: Provide ignored_posix_timers list Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-05 13:08 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 17/21] signal: Handle ignored signals in do_sigaction(action != SIG_IGN) Thomas Gleixner
` (4 subsequent siblings)
20 siblings, 2 replies; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
From: Thomas Gleixner <tglx@linutronix.de>
To handle posix timer signals on sigaction(SIG_IGN) properly, the timers
will be queued on a separate ignored list.
Add the necessary cleanup code for timer_delete() and exit_itimers().
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
V7: Use the proper struct member for the ignored list failure case - Frederic
V6: Warn when the ignored list is not empty after deleting all timers in
exit_itimers()
---
include/linux/posix-timers.h | 4 +++-
kernel/time/posix-timers.c | 28 ++++++++++++++++++++++++++++
2 files changed, 31 insertions(+), 1 deletion(-)
---
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -152,7 +152,8 @@ static inline void posix_cputimers_init_
/**
* struct k_itimer - POSIX.1b interval timer structure.
- * @list: List head for binding the timer to signals->posix_timers
+ * @list: List node for binding the timer to tsk::signal::posix_timers
+ * @ignored_list: List node for tracking ignored timers in tsk::signal::ignored_posix_timers
* @t_hash: Entry in the posix timer hash table
* @it_lock: Lock protecting the timer
* @kclock: Pointer to the k_clock struct handling this timer
@@ -176,6 +177,7 @@ static inline void posix_cputimers_init_
*/
struct k_itimer {
struct hlist_node list;
+ struct hlist_node ignored_list;
struct hlist_node t_hash;
spinlock_t it_lock;
const struct k_clock *kclock;
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -1027,6 +1027,18 @@ int common_timer_del(struct k_itimer *ti
return 0;
}
+/*
+ * If the deleted timer is on the ignored list, remove it and
+ * drop the associated reference.
+ */
+static inline void posix_timer_cleanup_ignored(struct k_itimer *tmr)
+{
+ if (!hlist_unhashed(&tmr->ignored_list)) {
+ hlist_del_init(&tmr->ignored_list);
+ posixtimer_putref(tmr);
+ }
+}
+
static inline int timer_delete_hook(struct k_itimer *timer)
{
const struct k_clock *kc = timer->kclock;
@@ -1059,6 +1071,7 @@ SYSCALL_DEFINE1(timer_delete, timer_t, t
spin_lock(¤t->sighand->siglock);
hlist_del(&timer->list);
+ posix_timer_cleanup_ignored(timer);
spin_unlock(¤t->sighand->siglock);
/*
* A concurrent lookup could check timer::it_signal lockless. It
@@ -1110,6 +1123,8 @@ static void itimer_delete(struct k_itime
}
hlist_del(&timer->list);
+ posix_timer_cleanup_ignored(timer);
+
/*
* Setting timer::it_signal to NULL is technically not required
* here as nothing can access the timer anymore legitimately via
@@ -1142,6 +1157,19 @@ void exit_itimers(struct task_struct *ts
/* The timers are not longer accessible via tsk::signal */
while (!hlist_empty(&timers))
itimer_delete(hlist_entry(timers.first, struct k_itimer, list));
+
+ /*
+ * There should be no timers on the ignored list. itimer_delete() has
+ * mopped them up.
+ */
+ if (!WARN_ON_ONCE(!hlist_empty(&tsk->signal->ignored_posix_timers)))
+ return;
+
+ hlist_move_list(&tsk->signal->ignored_posix_timers, &timers);
+ while (!hlist_empty(&timers)) {
+ posix_timer_cleanup_ignored(hlist_entry(timers.first, struct k_itimer,
+ ignored_list));
+ }
}
SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock,
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 17/21] signal: Handle ignored signals in do_sigaction(action != SIG_IGN)
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
` (15 preceding siblings ...)
2024-11-05 8:14 ` [patch V7 16/21] posix-timers: Handle ignored list on delete and exit Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 18/21] signal: Queue ignored posixtimers on ignore list Thomas Gleixner
` (3 subsequent siblings)
20 siblings, 1 reply; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
From: Thomas Gleixner <tglx@linutronix.de>
When a real handler (including SIG_DFL) is installed for a signal, which
had previously SIG_IGN set, then the list of ignored posix timers has to be
checked for timers which are affected by this change.
Add a list walk function which checks for the matching signal number and if
found requeues the timers signal, so the timer is rearmed on signal
delivery.
Rearming the timer right away is not possible because that requires to drop
sighand lock.
No functional change as the counter part which queues the timers on the
ignored list is still missing.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
kernel/signal.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 52 insertions(+), 1 deletion(-)
---
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -2002,7 +2002,54 @@ int posixtimer_send_sigqueue(struct k_it
unlock_task_sighand(t, &flags);
return ret;
}
-#endif /* CONFIG_POSIX_TIMERS */
+
+static void posixtimer_sig_unignore(struct task_struct *tsk, int sig)
+{
+ struct hlist_head *head = &tsk->signal->ignored_posix_timers;
+ struct hlist_node *tmp;
+ struct k_itimer *tmr;
+
+ if (likely(hlist_empty(head)))
+ return;
+
+ /*
+ * Rearming a timer with sighand lock held is not possible due to
+ * lock ordering vs. tmr::it_lock. Just stick the sigqueue back and
+ * let the signal delivery path deal with it whether it needs to be
+ * rearmed or not. This cannot be decided here w/o dropping sighand
+ * lock and creating a loop retry horror show.
+ */
+ hlist_for_each_entry_safe(tmr, tmp , head, ignored_list) {
+ struct task_struct *target;
+
+ /*
+ * tmr::sigq.info.si_signo is immutable, so accessing it
+ * without holding tmr::it_lock is safe.
+ */
+ if (tmr->sigq.info.si_signo != sig)
+ continue;
+
+ hlist_del_init(&tmr->ignored_list);
+
+ /* This should never happen and leaks a reference count */
+ if (WARN_ON_ONCE(!list_empty(&tmr->sigq.list)))
+ continue;
+
+ /*
+ * Get the target for the signal. If target is a thread and
+ * has exited by now, drop the reference count.
+ */
+ guard(rcu)();
+ target = posixtimer_get_target(tmr);
+ if (target)
+ posixtimer_queue_sigqueue(&tmr->sigq, target, tmr->it_pid_type);
+ else
+ posixtimer_putref(tmr);
+ }
+}
+#else /* CONFIG_POSIX_TIMERS */
+static inline void posixtimer_sig_unignore(struct task_struct *tsk, int sig) { }
+#endif /* !CONFIG_POSIX_TIMERS */
void do_notify_pidfd(struct task_struct *task)
{
@@ -4180,6 +4227,8 @@ int do_sigaction(int sig, struct k_sigac
sigaction_compat_abi(act, oact);
if (act) {
+ bool was_ignored = k->sa.sa_handler == SIG_IGN;
+
sigdelsetmask(&act->sa.sa_mask,
sigmask(SIGKILL) | sigmask(SIGSTOP));
*k = *act;
@@ -4200,6 +4249,8 @@ int do_sigaction(int sig, struct k_sigac
flush_sigqueue_mask(p, &mask, &p->signal->shared_pending);
for_each_thread(p, t)
flush_sigqueue_mask(p, &mask, &t->pending);
+ } else if (was_ignored) {
+ posixtimer_sig_unignore(p, sig);
}
}
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 18/21] signal: Queue ignored posixtimers on ignore list
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
` (16 preceding siblings ...)
2024-11-05 8:14 ` [patch V7 17/21] signal: Handle ignored signals in do_sigaction(action != SIG_IGN) Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-05 14:02 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 19/21] posix-timers: Cleanup SIG_IGN workaround leftovers Thomas Gleixner
` (2 subsequent siblings)
20 siblings, 2 replies; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
From: Thomas Gleixner <tglx@linutronix.de>
Queue posixtimers which have their signal ignored on the ignored list:
1) When the timer fires and the signal has SIG_IGN set
2) When SIG_IGN is installed via sigaction() and a timer signal
is already queued
This only happens when the signal is for a valid timer, which delivered the
signal in periodic mode. One-shot timer signals are correctly dropped.
Due to the lock order constraints (sighand::siglock nests inside
timer::lock) the signal code cannot access any of the timer fields which
are relevant to make this decision, e.g. timer::it_status.
This is addressed by establishing a protection scheme which requires to
lock both locks on the timer side for modifying decision fields in the
timer struct and therefore makes it possible for the signal delivery to
evaluate with only sighand:siglock being held:
1) Move the NULLification of timer->it_signal into the sighand::siglock
protected section of timer_delete() and check timer::it_signal in the
code path which determines whether the signal is dropped or queued on
the ignore list.
This ensures that a deleted timer cannot be moved onto the ignore
list, which would prevent it from being freed on exit() as it is not
longer in the process' posix timer list.
If the timer got moved to the ignored list before deletion then it is
removed from the ignored list under sighand lock in timer_delete().
2) Provide a new timer::it_sig_periodic flag, which gets set in the
signal queue path with both timer and sighand locks held if the timer
is actually in periodic mode at expiry time.
The ignore list code checks this flag under sighand::siglock and drops
the signal when it is not set.
If it is set, then the signal is moved to the ignored list independent
of the actual state of the timer.
When the signal is un-ignored later then the signal is moved back to
the signal queue. On signal delivery the posix timer side decides
about dropping the signal if the timer was re-armed, dis-armed or
deleted based on the signal sequence counter check.
If the thread/process exits then not yet delivered signals are
discarded which means the reference of the timer containing the
sigqueue is dropped and frees the timer.
This is way cheaper than requiring all code paths to lock
sighand::siglock of the target thread/process on any modification of
timer::it_status or going all the way and removing pending signals
from the signal queues on every rearm, disarm or delete operation.
So the protection scheme here is that on the timer side both timer::lock
and sighand::siglock have to be held for modifying
timer::it_signal
timer::it_sig_periodic
which means that on the signal side holding sighand::siglock is enough to
evaluate these fields.
In posixtimer_deliver_signal() holding timer::lock is sufficient to do the
sequence validation against timer::it_signal_seq because a concurrent
expiry is waiting on timer::lock to be released.
This completes the SIG_IGN handling and such timers are not longer self
rearmed which avoids pointless wakeups.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
V7: Fix exit/delete race conditions and use a protected field for the
periodic detection - Frederic
---
include/linux/posix-timers.h | 2 +
kernel/signal.c | 80 ++++++++++++++++++++++++++++++++++++++++---
kernel/time/posix-timers.c | 7 +++
3 files changed, 83 insertions(+), 6 deletions(-)
---
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -160,6 +160,7 @@ static inline void posix_cputimers_init_
* @it_clock: The posix timer clock id
* @it_id: The posix timer id for identifying the timer
* @it_status: The status of the timer
+ * @it_sig_periodic: The periodic status at signal delivery
* @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
@@ -184,6 +185,7 @@ struct k_itimer {
clockid_t it_clock;
timer_t it_id;
int it_status;
+ bool it_sig_periodic;
s64 it_overrun;
s64 it_overrun_last;
unsigned int it_signal_seq;
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -59,6 +59,8 @@
#include <asm/cacheflush.h>
#include <asm/syscall.h> /* for syscall_get_* */
+#include "time/posix-timers.h"
+
/*
* SLAB caches for signal bits.
*/
@@ -731,6 +733,16 @@ void signal_wake_up_state(struct task_st
kick_process(t);
}
+static inline void posixtimer_sig_ignore(struct task_struct *tsk, struct sigqueue *q);
+
+static void sigqueue_free_ignored(struct task_struct *tsk, struct sigqueue *q)
+{
+ if (likely(!(q->flags & SIGQUEUE_PREALLOC) || q->info.si_code != SI_TIMER))
+ __sigqueue_free(q);
+ else
+ posixtimer_sig_ignore(tsk, q);
+}
+
/* Remove signals in mask from the pending set and queue. */
static void flush_sigqueue_mask(struct task_struct *p, sigset_t *mask, struct sigpending *s)
{
@@ -747,7 +759,7 @@ static void flush_sigqueue_mask(struct t
list_for_each_entry_safe(q, n, &s->list, list) {
if (sigismember(mask, q->info.si_signo)) {
list_del_init(&q->list);
- __sigqueue_free(q);
+ sigqueue_free_ignored(p, q);
}
}
}
@@ -1964,7 +1976,7 @@ int posixtimer_send_sigqueue(struct k_it
int sig = q->info.si_signo;
struct task_struct *t;
unsigned long flags;
- int ret, result;
+ int result;
guard(rcu)();
@@ -1981,13 +1993,55 @@ int posixtimer_send_sigqueue(struct k_it
*/
tmr->it_sigqueue_seq = tmr->it_signal_seq;
- ret = 1; /* the signal is ignored */
+ /*
+ * Set the signal delivery status under sighand lock, so that the
+ * ignored signal handling can distinguish between a periodic and a
+ * non-periodic timer.
+ */
+ tmr->it_sig_periodic = tmr->it_status == POSIX_TIMER_REQUEUE_PENDING;
+
if (!prepare_signal(sig, t, false)) {
result = TRACE_SIGNAL_IGNORED;
+
+ /* Paranoia check. Try to survive. */
+ if (WARN_ON_ONCE(!list_empty(&q->list)))
+ goto out;
+
+ /* Periodic timers with SIG_IGN are queued on the ignored list */
+ if (tmr->it_sig_periodic) {
+ /*
+ * Already queued means the timer was rearmed after
+ * the previous expiry got it on the ignore list.
+ * Nothing to do for that case.
+ */
+ if (hlist_unhashed(&tmr->ignored_list)) {
+ /*
+ * Take a signal reference and queue it on
+ * the ignored list.
+ */
+ posixtimer_sigqueue_getref(q);
+ posixtimer_sig_ignore(t, q);
+ }
+ } else if (!hlist_unhashed(&tmr->ignored_list)) {
+ /*
+ * Covers the case where a timer was periodic and
+ * then the signal was ignored. Later it was rearmed
+ * as oneshot timer. The previous signal is invalid
+ * now, and this oneshot signal has to be dropped.
+ * Remove it from the ignored list and drop the
+ * reference count as the signal is not longer
+ * queued.
+ */
+ hlist_del_init(&tmr->ignored_list);
+ posixtimer_putref(tmr);
+ }
goto out;
}
- ret = 0;
+ /* This should never happen and leaks a reference count */
+ if (WARN_ON_ONCE(!hlist_unhashed(&tmr->ignored_list)))
+ hlist_del_init(&tmr->ignored_list);
+
if (unlikely(!list_empty(&q->list))) {
/* This holds a reference count already */
result = TRACE_SIGNAL_ALREADY_PENDING;
@@ -2000,7 +2054,22 @@ int posixtimer_send_sigqueue(struct k_it
out:
trace_signal_generate(sig, &q->info, t, tmr->it_pid_type != PIDTYPE_PID, result);
unlock_task_sighand(t, &flags);
- return ret;
+ return 0;
+}
+
+static inline void posixtimer_sig_ignore(struct task_struct *tsk, struct sigqueue *q)
+{
+ struct k_itimer *tmr = container_of(q, struct k_itimer, sigq);
+
+ /*
+ * If the timer is marked deleted already or the signal originates
+ * from a non-periodic timer, then just drop the reference
+ * count. Otherwise queue it on the ignored list.
+ */
+ if (tmr->it_signal && tmr->it_sig_periodic)
+ hlist_add_head(&tmr->ignored_list, &tsk->signal->ignored_posix_timers);
+ else
+ posixtimer_putref(tmr);
}
static void posixtimer_sig_unignore(struct task_struct *tsk, int sig)
@@ -2048,6 +2117,7 @@ static void posixtimer_sig_unignore(stru
}
}
#else /* CONFIG_POSIX_TIMERS */
+static inline void posixtimer_sig_ignore(struct task_struct *tsk, struct sigqueue *q) { }
static inline void posixtimer_sig_unignore(struct task_struct *tsk, int sig) { }
#endif /* !CONFIG_POSIX_TIMERS */
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -1072,12 +1072,17 @@ SYSCALL_DEFINE1(timer_delete, timer_t, t
spin_lock(¤t->sighand->siglock);
hlist_del(&timer->list);
posix_timer_cleanup_ignored(timer);
- spin_unlock(¤t->sighand->siglock);
/*
* A concurrent lookup could check timer::it_signal lockless. It
* will reevaluate with timer::it_lock held and observe the NULL.
+ *
+ * It must be written with siglock held so that the signal code
+ * observes timer->it_signal == NULL in do_sigaction(SIG_IGN),
+ * which prevents it from moving a pending signal of a deleted
+ * timer to the ignore list.
*/
WRITE_ONCE(timer->it_signal, NULL);
+ spin_unlock(¤t->sighand->siglock);
unlock_timer(timer, flags);
posix_timer_unhash_and_free(timer);
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 19/21] posix-timers: Cleanup SIG_IGN workaround leftovers
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
` (17 preceding siblings ...)
2024-11-05 8:14 ` [patch V7 18/21] signal: Queue ignored posixtimers on ignore list Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-05 14:26 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 20/21] alarmtimers: Remove the throttle mechanism from alarm_forward_now() Thomas Gleixner
2024-11-05 8:14 ` [patch V7 21/21] alarmtimers: Remove return value from alarm functions Thomas Gleixner
20 siblings, 2 replies; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
From: Thomas Gleixner <tglx@linutronix.de>
Now that ignored posix timer signals are requeued and the timers are
rearmed on signal delivery the workaround to keep such timers alive and
self rearm them is not longer required.
Remove the relevant hacks and the not longer required return values from
the related functions. The alarm timer workarounds will be cleaned up in a
separate step.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
include/linux/posix-timers.h | 2 -
kernel/signal.c | 7 +--
kernel/time/alarmtimer.c | 47 +++++---------------------
kernel/time/posix-cpu-timers.c | 18 ++--------
kernel/time/posix-timers.c | 73 +++--------------------------------------
kernel/time/posix-timers.h | 2 -
6 files changed, 24 insertions(+), 125 deletions(-)
---
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -111,7 +111,7 @@ static inline void posix_cputimers_rt_wa
void posixtimer_rearm_itimer(struct task_struct *p);
bool posixtimer_init_sigqueue(struct sigqueue *q);
-int posixtimer_send_sigqueue(struct k_itimer *tmr);
+void posixtimer_send_sigqueue(struct k_itimer *tmr);
bool posixtimer_deliver_signal(struct kernel_siginfo *info, struct sigqueue *timer_sigq);
void posixtimer_free_timer(struct k_itimer *timer);
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1970,7 +1970,7 @@ static inline struct task_struct *posixt
return t;
}
-int posixtimer_send_sigqueue(struct k_itimer *tmr)
+void posixtimer_send_sigqueue(struct k_itimer *tmr)
{
struct sigqueue *q = &tmr->sigq;
int sig = q->info.si_signo;
@@ -1982,10 +1982,10 @@ int posixtimer_send_sigqueue(struct k_it
t = posixtimer_get_target(tmr);
if (!t)
- return -1;
+ return;
if (!likely(lock_task_sighand(t, &flags)))
- return -1;
+ return;
/*
* Update @tmr::sigqueue_seq for posix timer signals with sighand
@@ -2054,7 +2054,6 @@ int posixtimer_send_sigqueue(struct k_it
out:
trace_signal_generate(sig, &q->info, t, tmr->it_pid_type != PIDTYPE_PID, result);
unlock_task_sighand(t, &flags);
- return 0;
}
static inline void posixtimer_sig_ignore(struct task_struct *tsk, struct sigqueue *q)
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -197,28 +197,15 @@ static enum hrtimer_restart alarmtimer_f
{
struct alarm *alarm = container_of(timer, struct alarm, timer);
struct alarm_base *base = &alarm_bases[alarm->type];
- unsigned long flags;
- int ret = HRTIMER_NORESTART;
- int restart = ALARMTIMER_NORESTART;
- spin_lock_irqsave(&base->lock, flags);
- alarmtimer_dequeue(base, alarm);
- spin_unlock_irqrestore(&base->lock, flags);
+ scoped_guard (spinlock_irqsave, &base->lock)
+ alarmtimer_dequeue(base, alarm);
if (alarm->function)
- restart = alarm->function(alarm, base->get_ktime());
-
- spin_lock_irqsave(&base->lock, flags);
- if (restart != ALARMTIMER_NORESTART) {
- hrtimer_set_expires(&alarm->timer, alarm->node.expires);
- alarmtimer_enqueue(base, alarm);
- ret = HRTIMER_RESTART;
- }
- spin_unlock_irqrestore(&base->lock, flags);
+ alarm->function(alarm, base->get_ktime());
trace_alarmtimer_fired(alarm, base->get_ktime());
- return ret;
-
+ return HRTIMER_NORESTART;
}
ktime_t alarm_expires_remaining(const struct alarm *alarm)
@@ -567,30 +554,14 @@ static enum alarmtimer_type clock2alarm(
*
* Return: whether the timer is to be restarted
*/
-static enum alarmtimer_restart alarm_handle_timer(struct alarm *alarm,
- ktime_t now)
+static enum alarmtimer_restart alarm_handle_timer(struct alarm *alarm, ktime_t now)
{
- struct k_itimer *ptr = container_of(alarm, struct k_itimer,
- it.alarm.alarmtimer);
- enum alarmtimer_restart result = ALARMTIMER_NORESTART;
- unsigned long flags;
-
- spin_lock_irqsave(&ptr->it_lock, flags);
+ struct k_itimer *ptr = container_of(alarm, struct k_itimer, it.alarm.alarmtimer);
- if (posix_timer_queue_signal(ptr) && ptr->it_interval) {
- /*
- * Handle ignored signals and rearm the timer. This will go
- * away once we handle ignored signals proper. Ensure that
- * small intervals cannot starve the system.
- */
- ptr->it_overrun += __alarm_forward_now(alarm, ptr->it_interval, true);
- ++ptr->it_signal_seq;
- ptr->it_status = POSIX_TIMER_ARMED;
- result = ALARMTIMER_RESTART;
- }
- spin_unlock_irqrestore(&ptr->it_lock, flags);
+ guard(spinlock_irqsave)(&ptr->it_lock);
+ posix_timer_queue_signal(ptr);
- return result;
+ return ALARMTIMER_NORESTART;
}
/**
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -603,21 +603,11 @@ static void cpu_timer_fire(struct k_itim
*/
wake_up_process(timer->it_process);
cpu_timer_setexpires(ctmr, 0);
- } else if (!timer->it_interval) {
- /*
- * One-shot timer. Clear it as soon as it's fired.
- */
+ } else {
posix_timer_queue_signal(timer);
- cpu_timer_setexpires(ctmr, 0);
- } else if (posix_timer_queue_signal(timer)) {
- /*
- * The signal did not get queued because the signal
- * was ignored, so we won't get any callback to
- * reload the timer. But we need to keep it
- * ticking in case the signal is deliverable next time.
- */
- posix_cpu_timer_rearm(timer);
- ++timer->it_signal_seq;
+ /* Disable oneshot timers */
+ if (!timer->it_interval)
+ cpu_timer_setexpires(ctmr, 0);
}
}
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -301,21 +301,12 @@ bool posixtimer_deliver_signal(struct ke
return ret;
}
-int posix_timer_queue_signal(struct k_itimer *timr)
+void posix_timer_queue_signal(struct k_itimer *timr)
{
- enum posix_timer_state state = POSIX_TIMER_DISARMED;
- int ret;
-
lockdep_assert_held(&timr->it_lock);
- if (timr->it_interval)
- state = POSIX_TIMER_REQUEUE_PENDING;
-
- timr->it_status = state;
-
- ret = posixtimer_send_sigqueue(timr);
- /* If we failed to send the signal the timer stops. */
- return ret > 0;
+ timr->it_status = timr->it_interval ? POSIX_TIMER_REQUEUE_PENDING : POSIX_TIMER_DISARMED;
+ posixtimer_send_sigqueue(timr);
}
/*
@@ -328,62 +319,10 @@ int posix_timer_queue_signal(struct k_it
static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
{
struct k_itimer *timr = container_of(timer, struct k_itimer, it.real.timer);
- enum hrtimer_restart ret = HRTIMER_NORESTART;
- unsigned long flags;
-
- spin_lock_irqsave(&timr->it_lock, flags);
-
- if (posix_timer_queue_signal(timr)) {
- /*
- * The signal was not queued due to SIG_IGN. As a
- * consequence the timer is not going to be rearmed from
- * the signal delivery path. But as a real signal handler
- * can be installed later the timer must be rearmed here.
- */
- if (timr->it_interval != 0) {
- ktime_t now = hrtimer_cb_get_time(timer);
- /*
- * FIXME: What we really want, is to stop this
- * timer completely and restart it in case the
- * SIG_IGN is removed. This is a non trivial
- * change to the signal handling code.
- *
- * For now let timers with an interval less than a
- * jiffy expire every jiffy and recheck for a
- * valid signal handler.
- *
- * This avoids interrupt starvation in case of a
- * very small interval, which would expire the
- * timer immediately again.
- *
- * Moving now ahead of time by one jiffy tricks
- * hrtimer_forward() to expire the timer later,
- * while it still maintains the overrun accuracy
- * for the price of a slight inconsistency in the
- * timer_gettime() case. This is at least better
- * than a timer storm.
- *
- * Only required when high resolution timers are
- * enabled as the periodic tick based timers are
- * automatically aligned to the next tick.
- */
- if (IS_ENABLED(CONFIG_HIGH_RES_TIMERS)) {
- ktime_t kj = TICK_NSEC;
-
- if (timr->it_interval < kj)
- now = ktime_add(now, kj);
- }
-
- timr->it_overrun += hrtimer_forward(timer, now, timr->it_interval);
- ret = HRTIMER_RESTART;
- ++timr->it_signal_seq;
- timr->it_status = POSIX_TIMER_ARMED;
- }
- }
-
- unlock_timer(timr, flags);
- return ret;
+ guard(spinlock_irqsave)(&timr->it_lock);
+ posix_timer_queue_signal(timr);
+ return HRTIMER_NORESTART;
}
static struct pid *good_sigevent(sigevent_t * event)
--- a/kernel/time/posix-timers.h
+++ b/kernel/time/posix-timers.h
@@ -42,7 +42,7 @@ extern const struct k_clock clock_proces
extern const struct k_clock clock_thread;
extern const struct k_clock alarm_clock;
-int posix_timer_queue_signal(struct k_itimer *timr);
+void posix_timer_queue_signal(struct k_itimer *timr);
void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting);
int common_timer_set(struct k_itimer *timr, int flags,
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 20/21] alarmtimers: Remove the throttle mechanism from alarm_forward_now()
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
` (18 preceding siblings ...)
2024-11-05 8:14 ` [patch V7 19/21] posix-timers: Cleanup SIG_IGN workaround leftovers Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-05 14:29 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 21/21] alarmtimers: Remove return value from alarm functions Thomas Gleixner
20 siblings, 2 replies; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
From: Thomas Gleixner <tglx@linutronix.de>
Now that ignored posix timer signals are requeued and the timers are
rearmed on signal delivery the workaround to keep such timers alive and
self rearm them is not longer required.
Remove the unused alarm timer parts.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
kernel/time/alarmtimer.c | 28 ++--------------------------
1 file changed, 2 insertions(+), 26 deletions(-)
---
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -467,35 +467,11 @@ u64 alarm_forward(struct alarm *alarm, k
}
EXPORT_SYMBOL_GPL(alarm_forward);
-static u64 __alarm_forward_now(struct alarm *alarm, ktime_t interval, bool throttle)
+u64 alarm_forward_now(struct alarm *alarm, ktime_t interval)
{
struct alarm_base *base = &alarm_bases[alarm->type];
- ktime_t now = base->get_ktime();
-
- if (IS_ENABLED(CONFIG_HIGH_RES_TIMERS) && throttle) {
- /*
- * Same issue as with posix_timer_fn(). Timers which are
- * periodic but the signal is ignored can starve the system
- * with a very small interval. The real fix which was
- * promised in the context of posix_timer_fn() never
- * materialized, but someone should really work on it.
- *
- * To prevent DOS fake @now to be 1 jiffy out which keeps
- * the overrun accounting correct but creates an
- * inconsistency vs. timer_gettime(2).
- */
- ktime_t kj = NSEC_PER_SEC / HZ;
- if (interval < kj)
- now = ktime_add(now, kj);
- }
-
- return alarm_forward(alarm, now, interval);
-}
-
-u64 alarm_forward_now(struct alarm *alarm, ktime_t interval)
-{
- return __alarm_forward_now(alarm, interval, false);
+ return alarm_forward(alarm, base->get_ktime(), interval);
}
EXPORT_SYMBOL_GPL(alarm_forward_now);
^ permalink raw reply [flat|nested] 50+ messages in thread
* [patch V7 21/21] alarmtimers: Remove return value from alarm functions
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
` (19 preceding siblings ...)
2024-11-05 8:14 ` [patch V7 20/21] alarmtimers: Remove the throttle mechanism from alarm_forward_now() Thomas Gleixner
@ 2024-11-05 8:14 ` Thomas Gleixner
2024-11-05 14:34 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
20 siblings, 2 replies; 50+ messages in thread
From: Thomas Gleixner @ 2024-11-05 8:14 UTC (permalink / raw)
To: LKML
Cc: Anna-Maria Behnsen, Frederic Weisbecker, John Stultz,
Peter Zijlstra, Ingo Molnar, Stephen Boyd, Eric Biederman,
Oleg Nesterov
From: Thomas Gleixner <tglx@linutronix.de>
Now that the SIG_IGN problem is solved in the core code, the alarmtimer
callbacks do not require a return value anymore.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
drivers/power/supply/charger-manager.c | 3 +--
fs/timerfd.c | 4 +---
include/linux/alarmtimer.h | 10 ++--------
kernel/time/alarmtimer.c | 16 +++++-----------
net/netfilter/xt_IDLETIMER.c | 4 +---
5 files changed, 10 insertions(+), 27 deletions(-)
---
--- a/drivers/power/supply/charger-manager.c
+++ b/drivers/power/supply/charger-manager.c
@@ -1412,10 +1412,9 @@ static inline struct charger_desc *cm_ge
return dev_get_platdata(&pdev->dev);
}
-static enum alarmtimer_restart cm_timer_func(struct alarm *alarm, ktime_t now)
+static void cm_timer_func(struct alarm *alarm, ktime_t now)
{
cm_timer_set = false;
- return ALARMTIMER_NORESTART;
}
static int charger_manager_probe(struct platform_device *pdev)
--- a/fs/timerfd.c
+++ b/fs/timerfd.c
@@ -79,13 +79,11 @@ static enum hrtimer_restart timerfd_tmrp
return HRTIMER_NORESTART;
}
-static enum alarmtimer_restart timerfd_alarmproc(struct alarm *alarm,
- ktime_t now)
+static void timerfd_alarmproc(struct alarm *alarm, ktime_t now)
{
struct timerfd_ctx *ctx = container_of(alarm, struct timerfd_ctx,
t.alarm);
timerfd_triggered(ctx);
- return ALARMTIMER_NORESTART;
}
/*
--- a/include/linux/alarmtimer.h
+++ b/include/linux/alarmtimer.h
@@ -20,12 +20,6 @@ enum alarmtimer_type {
ALARM_BOOTTIME_FREEZER,
};
-enum alarmtimer_restart {
- ALARMTIMER_NORESTART,
- ALARMTIMER_RESTART,
-};
-
-
#define ALARMTIMER_STATE_INACTIVE 0x00
#define ALARMTIMER_STATE_ENQUEUED 0x01
@@ -42,14 +36,14 @@ enum alarmtimer_restart {
struct alarm {
struct timerqueue_node node;
struct hrtimer timer;
- enum alarmtimer_restart (*function)(struct alarm *, ktime_t now);
+ void (*function)(struct alarm *, ktime_t now);
enum alarmtimer_type type;
int state;
void *data;
};
void alarm_init(struct alarm *alarm, enum alarmtimer_type type,
- enum alarmtimer_restart (*function)(struct alarm *, ktime_t));
+ void (*function)(struct alarm *, ktime_t));
void alarm_start(struct alarm *alarm, ktime_t start);
void alarm_start_relative(struct alarm *alarm, ktime_t start);
void alarm_restart(struct alarm *alarm);
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -321,7 +321,7 @@ static int alarmtimer_resume(struct devi
static void
__alarm_init(struct alarm *alarm, enum alarmtimer_type type,
- enum alarmtimer_restart (*function)(struct alarm *, ktime_t))
+ void (*function)(struct alarm *, ktime_t))
{
timerqueue_init(&alarm->node);
alarm->timer.function = alarmtimer_fired;
@@ -337,7 +337,7 @@ static void
* @function: callback that is run when the alarm fires
*/
void alarm_init(struct alarm *alarm, enum alarmtimer_type type,
- enum alarmtimer_restart (*function)(struct alarm *, ktime_t))
+ void (*function)(struct alarm *, ktime_t))
{
hrtimer_init(&alarm->timer, alarm_bases[type].base_clockid,
HRTIMER_MODE_ABS);
@@ -530,14 +530,12 @@ static enum alarmtimer_type clock2alarm(
*
* Return: whether the timer is to be restarted
*/
-static enum alarmtimer_restart alarm_handle_timer(struct alarm *alarm, ktime_t now)
+static void alarm_handle_timer(struct alarm *alarm, ktime_t now)
{
struct k_itimer *ptr = container_of(alarm, struct k_itimer, it.alarm.alarmtimer);
guard(spinlock_irqsave)(&ptr->it_lock);
posix_timer_queue_signal(ptr);
-
- return ALARMTIMER_NORESTART;
}
/**
@@ -698,18 +696,14 @@ static int alarm_timer_create(struct k_i
* @now: time at the timer expiration
*
* Wakes up the task that set the alarmtimer
- *
- * Return: ALARMTIMER_NORESTART
*/
-static enum alarmtimer_restart alarmtimer_nsleep_wakeup(struct alarm *alarm,
- ktime_t now)
+static void alarmtimer_nsleep_wakeup(struct alarm *alarm, ktime_t now)
{
struct task_struct *task = alarm->data;
alarm->data = NULL;
if (task)
wake_up_process(task);
- return ALARMTIMER_NORESTART;
}
/**
@@ -761,7 +755,7 @@ static int alarmtimer_do_nsleep(struct a
static void
alarm_init_on_stack(struct alarm *alarm, enum alarmtimer_type type,
- enum alarmtimer_restart (*function)(struct alarm *, ktime_t))
+ void (*function)(struct alarm *, ktime_t))
{
hrtimer_init_on_stack(&alarm->timer, alarm_bases[type].base_clockid,
HRTIMER_MODE_ABS);
--- a/net/netfilter/xt_IDLETIMER.c
+++ b/net/netfilter/xt_IDLETIMER.c
@@ -107,14 +107,12 @@ static void idletimer_tg_expired(struct
schedule_work(&timer->work);
}
-static enum alarmtimer_restart idletimer_tg_alarmproc(struct alarm *alarm,
- ktime_t now)
+static void idletimer_tg_alarmproc(struct alarm *alarm, ktime_t now)
{
struct idletimer_tg *timer = alarm->data;
pr_debug("alarm %s expired\n", timer->attr.attr.name);
schedule_work(&timer->work);
- return ALARMTIMER_NORESTART;
}
static int idletimer_check_sysfs_name(const char *name, unsigned int size)
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [patch V7 01/21] posix-cpu-timers: Correctly update timer status in posix_cpu_timer_del()
2024-11-05 8:14 ` [patch V7 01/21] posix-cpu-timers: Correctly update timer status in posix_cpu_timer_del() Thomas Gleixner
@ 2024-11-05 12:02 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
1 sibling, 0 replies; 50+ messages in thread
From: Frederic Weisbecker @ 2024-11-05 12:02 UTC (permalink / raw)
To: Thomas Gleixner
Cc: LKML, Anna-Maria Behnsen, John Stultz, Peter Zijlstra,
Ingo Molnar, Stephen Boyd, Eric Biederman, Oleg Nesterov
Le Tue, Nov 05, 2024 at 09:14:29AM +0100, Thomas Gleixner a écrit :
> If posix_cpu_timer_del() exits early due to task not found or sighand
> invalid, it fails to clear the state of the timer. That's harmless but
> inconsistent.
>
> These early exits are accounted as successful delete. Move the update of
> the timer state into the success return path, so all "successful" deletions
> are handled.
>
> Reported-by: Frederic Weisbecker <frederic@kernel.org>
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [patch V7 11/21] signal: Replace resched_timer logic
2024-11-05 8:14 ` [patch V7 11/21] signal: Replace resched_timer logic Thomas Gleixner
@ 2024-11-05 12:08 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
1 sibling, 0 replies; 50+ messages in thread
From: Frederic Weisbecker @ 2024-11-05 12:08 UTC (permalink / raw)
To: Thomas Gleixner
Cc: LKML, Anna-Maria Behnsen, John Stultz, Peter Zijlstra,
Ingo Molnar, Stephen Boyd, Eric Biederman, Oleg Nesterov
Le Tue, Nov 05, 2024 at 09:14:43AM +0100, Thomas Gleixner a écrit :
> From: Thomas Gleixner <tglx@linutronix.de>
>
> In preparation for handling ignored posix timer signals correctly and
> embedding the sigqueue struct into struct k_itimer, hand down a pointer to
> the sigqueue struct into posix_timer_deliver_signal() instead of just
> having a boolean flag.
>
> No functional change.
>
> Suggested-by: Eric W. Biederman <ebiederm@xmission.com>
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> Acked-by: "Eric W. Biederman" <ebiederm@xmission.com>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [patch V7 16/21] posix-timers: Handle ignored list on delete and exit
2024-11-05 8:14 ` [patch V7 16/21] posix-timers: Handle ignored list on delete and exit Thomas Gleixner
@ 2024-11-05 13:08 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
1 sibling, 0 replies; 50+ messages in thread
From: Frederic Weisbecker @ 2024-11-05 13:08 UTC (permalink / raw)
To: Thomas Gleixner
Cc: LKML, Anna-Maria Behnsen, John Stultz, Peter Zijlstra,
Ingo Molnar, Stephen Boyd, Eric Biederman, Oleg Nesterov
Le Tue, Nov 05, 2024 at 09:14:51AM +0100, Thomas Gleixner a écrit :
> From: Thomas Gleixner <tglx@linutronix.de>
>
> To handle posix timer signals on sigaction(SIG_IGN) properly, the timers
> will be queued on a separate ignored list.
>
> Add the necessary cleanup code for timer_delete() and exit_itimers().
>
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [patch V7 18/21] signal: Queue ignored posixtimers on ignore list
2024-11-05 8:14 ` [patch V7 18/21] signal: Queue ignored posixtimers on ignore list Thomas Gleixner
@ 2024-11-05 14:02 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
1 sibling, 0 replies; 50+ messages in thread
From: Frederic Weisbecker @ 2024-11-05 14:02 UTC (permalink / raw)
To: Thomas Gleixner
Cc: LKML, Anna-Maria Behnsen, John Stultz, Peter Zijlstra,
Ingo Molnar, Stephen Boyd, Eric Biederman, Oleg Nesterov
Le Tue, Nov 05, 2024 at 09:14:54AM +0100, Thomas Gleixner a écrit :
> From: Thomas Gleixner <tglx@linutronix.de>
>
> Queue posixtimers which have their signal ignored on the ignored list:
>
> 1) When the timer fires and the signal has SIG_IGN set
>
> 2) When SIG_IGN is installed via sigaction() and a timer signal
> is already queued
>
> This only happens when the signal is for a valid timer, which delivered the
> signal in periodic mode. One-shot timer signals are correctly dropped.
>
> Due to the lock order constraints (sighand::siglock nests inside
> timer::lock) the signal code cannot access any of the timer fields which
> are relevant to make this decision, e.g. timer::it_status.
>
> This is addressed by establishing a protection scheme which requires to
> lock both locks on the timer side for modifying decision fields in the
> timer struct and therefore makes it possible for the signal delivery to
> evaluate with only sighand:siglock being held:
>
> 1) Move the NULLification of timer->it_signal into the sighand::siglock
> protected section of timer_delete() and check timer::it_signal in the
> code path which determines whether the signal is dropped or queued on
> the ignore list.
>
> This ensures that a deleted timer cannot be moved onto the ignore
> list, which would prevent it from being freed on exit() as it is not
> longer in the process' posix timer list.
>
> If the timer got moved to the ignored list before deletion then it is
> removed from the ignored list under sighand lock in timer_delete().
>
> 2) Provide a new timer::it_sig_periodic flag, which gets set in the
> signal queue path with both timer and sighand locks held if the timer
> is actually in periodic mode at expiry time.
>
> The ignore list code checks this flag under sighand::siglock and drops
> the signal when it is not set.
>
> If it is set, then the signal is moved to the ignored list independent
> of the actual state of the timer.
>
> When the signal is un-ignored later then the signal is moved back to
> the signal queue. On signal delivery the posix timer side decides
> about dropping the signal if the timer was re-armed, dis-armed or
> deleted based on the signal sequence counter check.
>
> If the thread/process exits then not yet delivered signals are
> discarded which means the reference of the timer containing the
> sigqueue is dropped and frees the timer.
>
> This is way cheaper than requiring all code paths to lock
> sighand::siglock of the target thread/process on any modification of
> timer::it_status or going all the way and removing pending signals
> from the signal queues on every rearm, disarm or delete operation.
>
> So the protection scheme here is that on the timer side both timer::lock
> and sighand::siglock have to be held for modifying
>
> timer::it_signal
> timer::it_sig_periodic
>
> which means that on the signal side holding sighand::siglock is enough to
> evaluate these fields.
>
> In posixtimer_deliver_signal() holding timer::lock is sufficient to do the
> sequence validation against timer::it_signal_seq because a concurrent
> expiry is waiting on timer::lock to be released.
>
> This completes the SIG_IGN handling and such timers are not longer self
> rearmed which avoids pointless wakeups.
>
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> ---
> V7: Fix exit/delete race conditions and use a protected field for the
> periodic detection - Frederic
>
> ---
> include/linux/posix-timers.h | 2 +
> kernel/signal.c | 80 ++++++++++++++++++++++++++++++++++++++++---
> kernel/time/posix-timers.c | 7 +++
> 3 files changed, 83 insertions(+), 6 deletions(-)
> ---
>
> --- a/include/linux/posix-timers.h
> +++ b/include/linux/posix-timers.h
> @@ -160,6 +160,7 @@ static inline void posix_cputimers_init_
> * @it_clock: The posix timer clock id
> * @it_id: The posix timer id for identifying the timer
> * @it_status: The status of the timer
> + * @it_sig_periodic: The periodic status at signal delivery
> * @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
> @@ -184,6 +185,7 @@ struct k_itimer {
> clockid_t it_clock;
> timer_t it_id;
> int it_status;
> + bool it_sig_periodic;
> s64 it_overrun;
> s64 it_overrun_last;
> unsigned int it_signal_seq;
> --- a/kernel/signal.c
> +++ b/kernel/signal.c
> @@ -59,6 +59,8 @@
> #include <asm/cacheflush.h>
> #include <asm/syscall.h> /* for syscall_get_* */
>
> +#include "time/posix-timers.h"
> +
> /*
> * SLAB caches for signal bits.
> */
> @@ -731,6 +733,16 @@ void signal_wake_up_state(struct task_st
> kick_process(t);
> }
>
> +static inline void posixtimer_sig_ignore(struct task_struct *tsk, struct sigqueue *q);
> +
> +static void sigqueue_free_ignored(struct task_struct *tsk, struct sigqueue *q)
> +{
> + if (likely(!(q->flags & SIGQUEUE_PREALLOC) || q->info.si_code != SI_TIMER))
> + __sigqueue_free(q);
> + else
> + posixtimer_sig_ignore(tsk, q);
> +}
> +
> /* Remove signals in mask from the pending set and queue. */
> static void flush_sigqueue_mask(struct task_struct *p, sigset_t *mask, struct sigpending *s)
> {
> @@ -747,7 +759,7 @@ static void flush_sigqueue_mask(struct t
> list_for_each_entry_safe(q, n, &s->list, list) {
> if (sigismember(mask, q->info.si_signo)) {
> list_del_init(&q->list);
> - __sigqueue_free(q);
> + sigqueue_free_ignored(p, q);
> }
> }
> }
> @@ -1964,7 +1976,7 @@ int posixtimer_send_sigqueue(struct k_it
> int sig = q->info.si_signo;
> struct task_struct *t;
> unsigned long flags;
> - int ret, result;
> + int result;
>
> guard(rcu)();
>
> @@ -1981,13 +1993,55 @@ int posixtimer_send_sigqueue(struct k_it
> */
> tmr->it_sigqueue_seq = tmr->it_signal_seq;
>
> - ret = 1; /* the signal is ignored */
> + /*
> + * Set the signal delivery status under sighand lock, so that the
> + * ignored signal handling can distinguish between a periodic and a
> + * non-periodic timer.
> + */
> + tmr->it_sig_periodic = tmr->it_status == POSIX_TIMER_REQUEUE_PENDING;
It much clearer with this, thanks!
I'm not yet completely confident with the SIGCONT/SIGSTOP part but since
the previous behaviour wasn't too self-confident either:
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [patch V7 19/21] posix-timers: Cleanup SIG_IGN workaround leftovers
2024-11-05 8:14 ` [patch V7 19/21] posix-timers: Cleanup SIG_IGN workaround leftovers Thomas Gleixner
@ 2024-11-05 14:26 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
1 sibling, 0 replies; 50+ messages in thread
From: Frederic Weisbecker @ 2024-11-05 14:26 UTC (permalink / raw)
To: Thomas Gleixner
Cc: LKML, Anna-Maria Behnsen, John Stultz, Peter Zijlstra,
Ingo Molnar, Stephen Boyd, Eric Biederman, Oleg Nesterov
Le Tue, Nov 05, 2024 at 09:14:55AM +0100, Thomas Gleixner a écrit :
> From: Thomas Gleixner <tglx@linutronix.de>
>
> Now that ignored posix timer signals are requeued and the timers are
> rearmed on signal delivery the workaround to keep such timers alive and
> self rearm them is not longer required.
>
> Remove the relevant hacks and the not longer required return values from
> the related functions. The alarm timer workarounds will be cleaned up in a
> separate step.
>
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [patch V7 20/21] alarmtimers: Remove the throttle mechanism from alarm_forward_now()
2024-11-05 8:14 ` [patch V7 20/21] alarmtimers: Remove the throttle mechanism from alarm_forward_now() Thomas Gleixner
@ 2024-11-05 14:29 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
1 sibling, 0 replies; 50+ messages in thread
From: Frederic Weisbecker @ 2024-11-05 14:29 UTC (permalink / raw)
To: Thomas Gleixner
Cc: LKML, Anna-Maria Behnsen, John Stultz, Peter Zijlstra,
Ingo Molnar, Stephen Boyd, Eric Biederman, Oleg Nesterov
Le Tue, Nov 05, 2024 at 09:14:56AM +0100, Thomas Gleixner a écrit :
> From: Thomas Gleixner <tglx@linutronix.de>
>
> Now that ignored posix timer signals are requeued and the timers are
> rearmed on signal delivery the workaround to keep such timers alive and
> self rearm them is not longer required.
>
> Remove the unused alarm timer parts.
>
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [patch V7 21/21] alarmtimers: Remove return value from alarm functions
2024-11-05 8:14 ` [patch V7 21/21] alarmtimers: Remove return value from alarm functions Thomas Gleixner
@ 2024-11-05 14:34 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
1 sibling, 0 replies; 50+ messages in thread
From: Frederic Weisbecker @ 2024-11-05 14:34 UTC (permalink / raw)
To: Thomas Gleixner
Cc: LKML, Anna-Maria Behnsen, John Stultz, Peter Zijlstra,
Ingo Molnar, Stephen Boyd, Eric Biederman, Oleg Nesterov
Le Tue, Nov 05, 2024 at 09:14:58AM +0100, Thomas Gleixner a écrit :
> From: Thomas Gleixner <tglx@linutronix.de>
>
> Now that the SIG_IGN problem is solved in the core code, the alarmtimer
> callbacks do not require a return value anymore.
>
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Frederic Weisbecker <frederic@kernel.org>
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] alarmtimers: Remove return value from alarm functions
2024-11-05 8:14 ` [patch V7 21/21] alarmtimers: Remove return value from alarm functions Thomas Gleixner
2024-11-05 14:34 ` Frederic Weisbecker
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
1 sibling, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Thomas Gleixner, Peter Zijlstra (Intel),
Frederic Weisbecker, x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 2634303f8773b0c602069887565cd412440be15d
Gitweb: https://git.kernel.org/tip/2634303f8773b0c602069887565cd412440be15d
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:58 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:46 +01:00
alarmtimers: Remove return value from alarm functions
Now that the SIG_IGN problem is solved in the core code, the alarmtimer
callbacks do not require a return value anymore.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Frederic Weisbecker <frederic@kernel.org>
Link: https://lore.kernel.org/all/20241105064214.318837272@linutronix.de
---
drivers/power/supply/charger-manager.c | 3 +--
fs/timerfd.c | 4 +---
include/linux/alarmtimer.h | 10 ++--------
kernel/time/alarmtimer.c | 16 +++++-----------
net/netfilter/xt_IDLETIMER.c | 4 +---
5 files changed, 10 insertions(+), 27 deletions(-)
diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c
index 96f0a7f..09ec0ec 100644
--- a/drivers/power/supply/charger-manager.c
+++ b/drivers/power/supply/charger-manager.c
@@ -1412,10 +1412,9 @@ static inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev)
return dev_get_platdata(&pdev->dev);
}
-static enum alarmtimer_restart cm_timer_func(struct alarm *alarm, ktime_t now)
+static void cm_timer_func(struct alarm *alarm, ktime_t now)
{
cm_timer_set = false;
- return ALARMTIMER_NORESTART;
}
static int charger_manager_probe(struct platform_device *pdev)
diff --git a/fs/timerfd.c b/fs/timerfd.c
index 137523e..f10c99a 100644
--- a/fs/timerfd.c
+++ b/fs/timerfd.c
@@ -79,13 +79,11 @@ static enum hrtimer_restart timerfd_tmrproc(struct hrtimer *htmr)
return HRTIMER_NORESTART;
}
-static enum alarmtimer_restart timerfd_alarmproc(struct alarm *alarm,
- ktime_t now)
+static void timerfd_alarmproc(struct alarm *alarm, ktime_t now)
{
struct timerfd_ctx *ctx = container_of(alarm, struct timerfd_ctx,
t.alarm);
timerfd_triggered(ctx);
- return ALARMTIMER_NORESTART;
}
/*
diff --git a/include/linux/alarmtimer.h b/include/linux/alarmtimer.h
index 05e758b..3ffa534 100644
--- a/include/linux/alarmtimer.h
+++ b/include/linux/alarmtimer.h
@@ -20,12 +20,6 @@ enum alarmtimer_type {
ALARM_BOOTTIME_FREEZER,
};
-enum alarmtimer_restart {
- ALARMTIMER_NORESTART,
- ALARMTIMER_RESTART,
-};
-
-
#define ALARMTIMER_STATE_INACTIVE 0x00
#define ALARMTIMER_STATE_ENQUEUED 0x01
@@ -42,14 +36,14 @@ enum alarmtimer_restart {
struct alarm {
struct timerqueue_node node;
struct hrtimer timer;
- enum alarmtimer_restart (*function)(struct alarm *, ktime_t now);
+ void (*function)(struct alarm *, ktime_t now);
enum alarmtimer_type type;
int state;
void *data;
};
void alarm_init(struct alarm *alarm, enum alarmtimer_type type,
- enum alarmtimer_restart (*function)(struct alarm *, ktime_t));
+ void (*function)(struct alarm *, ktime_t));
void alarm_start(struct alarm *alarm, ktime_t start);
void alarm_start_relative(struct alarm *alarm, ktime_t start);
void alarm_restart(struct alarm *alarm);
diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c
index 593e7d5..37d2d79 100644
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -321,7 +321,7 @@ static int alarmtimer_resume(struct device *dev)
static void
__alarm_init(struct alarm *alarm, enum alarmtimer_type type,
- enum alarmtimer_restart (*function)(struct alarm *, ktime_t))
+ void (*function)(struct alarm *, ktime_t))
{
timerqueue_init(&alarm->node);
alarm->timer.function = alarmtimer_fired;
@@ -337,7 +337,7 @@ __alarm_init(struct alarm *alarm, enum alarmtimer_type type,
* @function: callback that is run when the alarm fires
*/
void alarm_init(struct alarm *alarm, enum alarmtimer_type type,
- enum alarmtimer_restart (*function)(struct alarm *, ktime_t))
+ void (*function)(struct alarm *, ktime_t))
{
hrtimer_init(&alarm->timer, alarm_bases[type].base_clockid,
HRTIMER_MODE_ABS);
@@ -530,14 +530,12 @@ static enum alarmtimer_type clock2alarm(clockid_t clockid)
*
* Return: whether the timer is to be restarted
*/
-static enum alarmtimer_restart alarm_handle_timer(struct alarm *alarm, ktime_t now)
+static void alarm_handle_timer(struct alarm *alarm, ktime_t now)
{
struct k_itimer *ptr = container_of(alarm, struct k_itimer, it.alarm.alarmtimer);
guard(spinlock_irqsave)(&ptr->it_lock);
posix_timer_queue_signal(ptr);
-
- return ALARMTIMER_NORESTART;
}
/**
@@ -698,18 +696,14 @@ static int alarm_timer_create(struct k_itimer *new_timer)
* @now: time at the timer expiration
*
* Wakes up the task that set the alarmtimer
- *
- * Return: ALARMTIMER_NORESTART
*/
-static enum alarmtimer_restart alarmtimer_nsleep_wakeup(struct alarm *alarm,
- ktime_t now)
+static void alarmtimer_nsleep_wakeup(struct alarm *alarm, ktime_t now)
{
struct task_struct *task = alarm->data;
alarm->data = NULL;
if (task)
wake_up_process(task);
- return ALARMTIMER_NORESTART;
}
/**
@@ -761,7 +755,7 @@ static int alarmtimer_do_nsleep(struct alarm *alarm, ktime_t absexp,
static void
alarm_init_on_stack(struct alarm *alarm, enum alarmtimer_type type,
- enum alarmtimer_restart (*function)(struct alarm *, ktime_t))
+ void (*function)(struct alarm *, ktime_t))
{
hrtimer_init_on_stack(&alarm->timer, alarm_bases[type].base_clockid,
HRTIMER_MODE_ABS);
diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c
index db720ef..5514600 100644
--- a/net/netfilter/xt_IDLETIMER.c
+++ b/net/netfilter/xt_IDLETIMER.c
@@ -107,14 +107,12 @@ static void idletimer_tg_expired(struct timer_list *t)
schedule_work(&timer->work);
}
-static enum alarmtimer_restart idletimer_tg_alarmproc(struct alarm *alarm,
- ktime_t now)
+static void idletimer_tg_alarmproc(struct alarm *alarm, ktime_t now)
{
struct idletimer_tg *timer = alarm->data;
pr_debug("alarm %s expired\n", timer->attr.attr.name);
schedule_work(&timer->work);
- return ALARMTIMER_NORESTART;
}
static int idletimer_check_sysfs_name(const char *name, unsigned int size)
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] alarmtimers: Remove the throttle mechanism from alarm_forward_now()
2024-11-05 8:14 ` [patch V7 20/21] alarmtimers: Remove the throttle mechanism from alarm_forward_now() Thomas Gleixner
2024-11-05 14:29 ` Frederic Weisbecker
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
1 sibling, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Thomas Gleixner, Frederic Weisbecker, Peter Zijlstra (Intel),
x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 6b0aa145786dab25c6b8e79ad70ac3382c381596
Gitweb: https://git.kernel.org/tip/6b0aa145786dab25c6b8e79ad70ac3382c381596
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:56 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:45 +01:00
alarmtimers: Remove the throttle mechanism from alarm_forward_now()
Now that ignored posix timer signals are requeued and the timers are
rearmed on signal delivery the workaround to keep such timers alive and
self rearm them is not longer required.
Remove the unused alarm timer parts.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241105064214.252443020@linutronix.de
---
kernel/time/alarmtimer.c | 28 ++--------------------------
1 file changed, 2 insertions(+), 26 deletions(-)
diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c
index 8543d7f..593e7d5 100644
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -467,35 +467,11 @@ u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval)
}
EXPORT_SYMBOL_GPL(alarm_forward);
-static u64 __alarm_forward_now(struct alarm *alarm, ktime_t interval, bool throttle)
+u64 alarm_forward_now(struct alarm *alarm, ktime_t interval)
{
struct alarm_base *base = &alarm_bases[alarm->type];
- ktime_t now = base->get_ktime();
-
- if (IS_ENABLED(CONFIG_HIGH_RES_TIMERS) && throttle) {
- /*
- * Same issue as with posix_timer_fn(). Timers which are
- * periodic but the signal is ignored can starve the system
- * with a very small interval. The real fix which was
- * promised in the context of posix_timer_fn() never
- * materialized, but someone should really work on it.
- *
- * To prevent DOS fake @now to be 1 jiffy out which keeps
- * the overrun accounting correct but creates an
- * inconsistency vs. timer_gettime(2).
- */
- ktime_t kj = NSEC_PER_SEC / HZ;
-
- if (interval < kj)
- now = ktime_add(now, kj);
- }
-
- return alarm_forward(alarm, now, interval);
-}
-u64 alarm_forward_now(struct alarm *alarm, ktime_t interval)
-{
- return __alarm_forward_now(alarm, interval, false);
+ return alarm_forward(alarm, base->get_ktime(), interval);
}
EXPORT_SYMBOL_GPL(alarm_forward_now);
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] posix-timers: Cleanup SIG_IGN workaround leftovers
2024-11-05 8:14 ` [patch V7 19/21] posix-timers: Cleanup SIG_IGN workaround leftovers Thomas Gleixner
2024-11-05 14:26 ` Frederic Weisbecker
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
1 sibling, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Thomas Gleixner, Frederic Weisbecker, Peter Zijlstra (Intel),
x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 7a66f72b09bb0762360274b1fb677b3433dbaa06
Gitweb: https://git.kernel.org/tip/7a66f72b09bb0762360274b1fb677b3433dbaa06
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:55 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:45 +01:00
posix-timers: Cleanup SIG_IGN workaround leftovers
Now that ignored posix timer signals are requeued and the timers are
rearmed on signal delivery the workaround to keep such timers alive and
self rearm them is not longer required.
Remove the relevant hacks and the not longer required return values from
the related functions. The alarm timer workarounds will be cleaned up in a
separate step.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241105064214.187239060@linutronix.de
---
include/linux/posix-timers.h | 2 +-
kernel/signal.c | 7 +--
kernel/time/alarmtimer.c | 47 ++++-----------------
kernel/time/posix-cpu-timers.c | 18 +-------
kernel/time/posix-timers.c | 73 ++-------------------------------
kernel/time/posix-timers.h | 2 +-
6 files changed, 24 insertions(+), 125 deletions(-)
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 43ea6e7..f11f10c 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -111,7 +111,7 @@ static inline void posix_cputimers_rt_watchdog(struct posix_cputimers *pct,
void posixtimer_rearm_itimer(struct task_struct *p);
bool posixtimer_init_sigqueue(struct sigqueue *q);
-int posixtimer_send_sigqueue(struct k_itimer *tmr);
+void posixtimer_send_sigqueue(struct k_itimer *tmr);
bool posixtimer_deliver_signal(struct kernel_siginfo *info, struct sigqueue *timer_sigq);
void posixtimer_free_timer(struct k_itimer *timer);
diff --git a/kernel/signal.c b/kernel/signal.c
index 9b098a7..cbf70c8 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1970,7 +1970,7 @@ static inline struct task_struct *posixtimer_get_target(struct k_itimer *tmr)
return t;
}
-int posixtimer_send_sigqueue(struct k_itimer *tmr)
+void posixtimer_send_sigqueue(struct k_itimer *tmr)
{
struct sigqueue *q = &tmr->sigq;
int sig = q->info.si_signo;
@@ -1982,10 +1982,10 @@ int posixtimer_send_sigqueue(struct k_itimer *tmr)
t = posixtimer_get_target(tmr);
if (!t)
- return -1;
+ return;
if (!likely(lock_task_sighand(t, &flags)))
- return -1;
+ return;
/*
* Update @tmr::sigqueue_seq for posix timer signals with sighand
@@ -2054,7 +2054,6 @@ int posixtimer_send_sigqueue(struct k_itimer *tmr)
out:
trace_signal_generate(sig, &q->info, t, tmr->it_pid_type != PIDTYPE_PID, result);
unlock_task_sighand(t, &flags);
- return 0;
}
static inline void posixtimer_sig_ignore(struct task_struct *tsk, struct sigqueue *q)
diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c
index 452d8aa..8543d7f 100644
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -197,28 +197,15 @@ static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer)
{
struct alarm *alarm = container_of(timer, struct alarm, timer);
struct alarm_base *base = &alarm_bases[alarm->type];
- unsigned long flags;
- int ret = HRTIMER_NORESTART;
- int restart = ALARMTIMER_NORESTART;
- spin_lock_irqsave(&base->lock, flags);
- alarmtimer_dequeue(base, alarm);
- spin_unlock_irqrestore(&base->lock, flags);
+ scoped_guard (spinlock_irqsave, &base->lock)
+ alarmtimer_dequeue(base, alarm);
if (alarm->function)
- restart = alarm->function(alarm, base->get_ktime());
-
- spin_lock_irqsave(&base->lock, flags);
- if (restart != ALARMTIMER_NORESTART) {
- hrtimer_set_expires(&alarm->timer, alarm->node.expires);
- alarmtimer_enqueue(base, alarm);
- ret = HRTIMER_RESTART;
- }
- spin_unlock_irqrestore(&base->lock, flags);
+ alarm->function(alarm, base->get_ktime());
trace_alarmtimer_fired(alarm, base->get_ktime());
- return ret;
-
+ return HRTIMER_NORESTART;
}
ktime_t alarm_expires_remaining(const struct alarm *alarm)
@@ -567,30 +554,14 @@ static enum alarmtimer_type clock2alarm(clockid_t clockid)
*
* Return: whether the timer is to be restarted
*/
-static enum alarmtimer_restart alarm_handle_timer(struct alarm *alarm,
- ktime_t now)
+static enum alarmtimer_restart alarm_handle_timer(struct alarm *alarm, ktime_t now)
{
- struct k_itimer *ptr = container_of(alarm, struct k_itimer,
- it.alarm.alarmtimer);
- enum alarmtimer_restart result = ALARMTIMER_NORESTART;
- unsigned long flags;
-
- spin_lock_irqsave(&ptr->it_lock, flags);
+ struct k_itimer *ptr = container_of(alarm, struct k_itimer, it.alarm.alarmtimer);
- if (posix_timer_queue_signal(ptr) && ptr->it_interval) {
- /*
- * Handle ignored signals and rearm the timer. This will go
- * away once we handle ignored signals proper. Ensure that
- * small intervals cannot starve the system.
- */
- ptr->it_overrun += __alarm_forward_now(alarm, ptr->it_interval, true);
- ++ptr->it_signal_seq;
- ptr->it_status = POSIX_TIMER_ARMED;
- result = ALARMTIMER_RESTART;
- }
- spin_unlock_irqrestore(&ptr->it_lock, flags);
+ guard(spinlock_irqsave)(&ptr->it_lock);
+ posix_timer_queue_signal(ptr);
- return result;
+ return ALARMTIMER_NORESTART;
}
/**
diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
index 0c441d8..50e8d04 100644
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -603,21 +603,11 @@ static void cpu_timer_fire(struct k_itimer *timer)
*/
wake_up_process(timer->it_process);
cpu_timer_setexpires(ctmr, 0);
- } else if (!timer->it_interval) {
- /*
- * One-shot timer. Clear it as soon as it's fired.
- */
+ } else {
posix_timer_queue_signal(timer);
- cpu_timer_setexpires(ctmr, 0);
- } else if (posix_timer_queue_signal(timer)) {
- /*
- * The signal did not get queued because the signal
- * was ignored, so we won't get any callback to
- * reload the timer. But we need to keep it
- * ticking in case the signal is deliverable next time.
- */
- posix_cpu_timer_rearm(timer);
- ++timer->it_signal_seq;
+ /* Disable oneshot timers */
+ if (!timer->it_interval)
+ cpu_timer_setexpires(ctmr, 0);
}
}
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index ea72db3..881a9ce 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -300,21 +300,12 @@ bool posixtimer_deliver_signal(struct kernel_siginfo *info, struct sigqueue *tim
return ret;
}
-int posix_timer_queue_signal(struct k_itimer *timr)
+void posix_timer_queue_signal(struct k_itimer *timr)
{
- enum posix_timer_state state = POSIX_TIMER_DISARMED;
- int ret;
-
lockdep_assert_held(&timr->it_lock);
- if (timr->it_interval)
- state = POSIX_TIMER_REQUEUE_PENDING;
-
- timr->it_status = state;
-
- ret = posixtimer_send_sigqueue(timr);
- /* If we failed to send the signal the timer stops. */
- return ret > 0;
+ timr->it_status = timr->it_interval ? POSIX_TIMER_REQUEUE_PENDING : POSIX_TIMER_DISARMED;
+ posixtimer_send_sigqueue(timr);
}
/*
@@ -327,62 +318,10 @@ int posix_timer_queue_signal(struct k_itimer *timr)
static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
{
struct k_itimer *timr = container_of(timer, struct k_itimer, it.real.timer);
- enum hrtimer_restart ret = HRTIMER_NORESTART;
- unsigned long flags;
-
- spin_lock_irqsave(&timr->it_lock, flags);
-
- if (posix_timer_queue_signal(timr)) {
- /*
- * The signal was not queued due to SIG_IGN. As a
- * consequence the timer is not going to be rearmed from
- * the signal delivery path. But as a real signal handler
- * can be installed later the timer must be rearmed here.
- */
- if (timr->it_interval != 0) {
- ktime_t now = hrtimer_cb_get_time(timer);
-
- /*
- * FIXME: What we really want, is to stop this
- * timer completely and restart it in case the
- * SIG_IGN is removed. This is a non trivial
- * change to the signal handling code.
- *
- * For now let timers with an interval less than a
- * jiffy expire every jiffy and recheck for a
- * valid signal handler.
- *
- * This avoids interrupt starvation in case of a
- * very small interval, which would expire the
- * timer immediately again.
- *
- * Moving now ahead of time by one jiffy tricks
- * hrtimer_forward() to expire the timer later,
- * while it still maintains the overrun accuracy
- * for the price of a slight inconsistency in the
- * timer_gettime() case. This is at least better
- * than a timer storm.
- *
- * Only required when high resolution timers are
- * enabled as the periodic tick based timers are
- * automatically aligned to the next tick.
- */
- if (IS_ENABLED(CONFIG_HIGH_RES_TIMERS)) {
- ktime_t kj = TICK_NSEC;
-
- if (timr->it_interval < kj)
- now = ktime_add(now, kj);
- }
-
- timr->it_overrun += hrtimer_forward(timer, now, timr->it_interval);
- ret = HRTIMER_RESTART;
- ++timr->it_signal_seq;
- timr->it_status = POSIX_TIMER_ARMED;
- }
- }
- unlock_timer(timr, flags);
- return ret;
+ guard(spinlock_irqsave)(&timr->it_lock);
+ posix_timer_queue_signal(timr);
+ return HRTIMER_NORESTART;
}
static struct pid *good_sigevent(sigevent_t * event)
diff --git a/kernel/time/posix-timers.h b/kernel/time/posix-timers.h
index 4d09677..61906f0 100644
--- a/kernel/time/posix-timers.h
+++ b/kernel/time/posix-timers.h
@@ -42,7 +42,7 @@ extern const struct k_clock clock_process;
extern const struct k_clock clock_thread;
extern const struct k_clock alarm_clock;
-int posix_timer_queue_signal(struct k_itimer *timr);
+void posix_timer_queue_signal(struct k_itimer *timr);
void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting);
int common_timer_set(struct k_itimer *timr, int flags,
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] signal: Queue ignored posixtimers on ignore list
2024-11-05 8:14 ` [patch V7 18/21] signal: Queue ignored posixtimers on ignore list Thomas Gleixner
2024-11-05 14:02 ` Frederic Weisbecker
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
1 sibling, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Thomas Gleixner, Frederic Weisbecker, Peter Zijlstra (Intel),
x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: df7a996b4dab03c889fa86d849447b716f07b069
Gitweb: https://git.kernel.org/tip/df7a996b4dab03c889fa86d849447b716f07b069
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:54 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:45 +01:00
signal: Queue ignored posixtimers on ignore list
Queue posixtimers which have their signal ignored on the ignored list:
1) When the timer fires and the signal has SIG_IGN set
2) When SIG_IGN is installed via sigaction() and a timer signal
is already queued
This only happens when the signal is for a valid timer, which delivered the
signal in periodic mode. One-shot timer signals are correctly dropped.
Due to the lock order constraints (sighand::siglock nests inside
timer::lock) the signal code cannot access any of the timer fields which
are relevant to make this decision, e.g. timer::it_status.
This is addressed by establishing a protection scheme which requires to
lock both locks on the timer side for modifying decision fields in the
timer struct and therefore makes it possible for the signal delivery to
evaluate with only sighand:siglock being held:
1) Move the NULLification of timer->it_signal into the sighand::siglock
protected section of timer_delete() and check timer::it_signal in the
code path which determines whether the signal is dropped or queued on
the ignore list.
This ensures that a deleted timer cannot be moved onto the ignore
list, which would prevent it from being freed on exit() as it is not
longer in the process' posix timer list.
If the timer got moved to the ignored list before deletion then it is
removed from the ignored list under sighand lock in timer_delete().
2) Provide a new timer::it_sig_periodic flag, which gets set in the
signal queue path with both timer and sighand locks held if the timer
is actually in periodic mode at expiry time.
The ignore list code checks this flag under sighand::siglock and drops
the signal when it is not set.
If it is set, then the signal is moved to the ignored list independent
of the actual state of the timer.
When the signal is un-ignored later then the signal is moved back to
the signal queue. On signal delivery the posix timer side decides
about dropping the signal if the timer was re-armed, dis-armed or
deleted based on the signal sequence counter check.
If the thread/process exits then not yet delivered signals are
discarded which means the reference of the timer containing the
sigqueue is dropped and frees the timer.
This is way cheaper than requiring all code paths to lock
sighand::siglock of the target thread/process on any modification of
timer::it_status or going all the way and removing pending signals
from the signal queues on every rearm, disarm or delete operation.
So the protection scheme here is that on the timer side both timer::lock
and sighand::siglock have to be held for modifying
timer::it_signal
timer::it_sig_periodic
which means that on the signal side holding sighand::siglock is enough to
evaluate these fields.
In posixtimer_deliver_signal() holding timer::lock is sufficient to do the
sequence validation against timer::it_signal_seq because a concurrent
expiry is waiting on timer::lock to be released.
This completes the SIG_IGN handling and such timers are not longer self
rearmed which avoids pointless wakeups.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241105064214.120756416@linutronix.de
---
include/linux/posix-timers.h | 2 +-
kernel/signal.c | 80 ++++++++++++++++++++++++++++++++---
kernel/time/posix-timers.c | 7 ++-
3 files changed, 83 insertions(+), 6 deletions(-)
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 1608b52..43ea6e7 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -160,6 +160,7 @@ static inline void posix_cputimers_init_work(void) { }
* @it_clock: The posix timer clock id
* @it_id: The posix timer id for identifying the timer
* @it_status: The status of the timer
+ * @it_sig_periodic: The periodic status at signal delivery
* @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
@@ -184,6 +185,7 @@ struct k_itimer {
clockid_t it_clock;
timer_t it_id;
int it_status;
+ bool it_sig_periodic;
s64 it_overrun;
s64 it_overrun_last;
unsigned int it_signal_seq;
diff --git a/kernel/signal.c b/kernel/signal.c
index 908b49c..9b098a7 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -59,6 +59,8 @@
#include <asm/cacheflush.h>
#include <asm/syscall.h> /* for syscall_get_* */
+#include "time/posix-timers.h"
+
/*
* SLAB caches for signal bits.
*/
@@ -731,6 +733,16 @@ void signal_wake_up_state(struct task_struct *t, unsigned int state)
kick_process(t);
}
+static inline void posixtimer_sig_ignore(struct task_struct *tsk, struct sigqueue *q);
+
+static void sigqueue_free_ignored(struct task_struct *tsk, struct sigqueue *q)
+{
+ if (likely(!(q->flags & SIGQUEUE_PREALLOC) || q->info.si_code != SI_TIMER))
+ __sigqueue_free(q);
+ else
+ posixtimer_sig_ignore(tsk, q);
+}
+
/* Remove signals in mask from the pending set and queue. */
static void flush_sigqueue_mask(struct task_struct *p, sigset_t *mask, struct sigpending *s)
{
@@ -747,7 +759,7 @@ static void flush_sigqueue_mask(struct task_struct *p, sigset_t *mask, struct si
list_for_each_entry_safe(q, n, &s->list, list) {
if (sigismember(mask, q->info.si_signo)) {
list_del_init(&q->list);
- __sigqueue_free(q);
+ sigqueue_free_ignored(p, q);
}
}
}
@@ -1964,7 +1976,7 @@ int posixtimer_send_sigqueue(struct k_itimer *tmr)
int sig = q->info.si_signo;
struct task_struct *t;
unsigned long flags;
- int ret, result;
+ int result;
guard(rcu)();
@@ -1981,13 +1993,55 @@ int posixtimer_send_sigqueue(struct k_itimer *tmr)
*/
tmr->it_sigqueue_seq = tmr->it_signal_seq;
- ret = 1; /* the signal is ignored */
+ /*
+ * Set the signal delivery status under sighand lock, so that the
+ * ignored signal handling can distinguish between a periodic and a
+ * non-periodic timer.
+ */
+ tmr->it_sig_periodic = tmr->it_status == POSIX_TIMER_REQUEUE_PENDING;
+
if (!prepare_signal(sig, t, false)) {
result = TRACE_SIGNAL_IGNORED;
+
+ /* Paranoia check. Try to survive. */
+ if (WARN_ON_ONCE(!list_empty(&q->list)))
+ goto out;
+
+ /* Periodic timers with SIG_IGN are queued on the ignored list */
+ if (tmr->it_sig_periodic) {
+ /*
+ * Already queued means the timer was rearmed after
+ * the previous expiry got it on the ignore list.
+ * Nothing to do for that case.
+ */
+ if (hlist_unhashed(&tmr->ignored_list)) {
+ /*
+ * Take a signal reference and queue it on
+ * the ignored list.
+ */
+ posixtimer_sigqueue_getref(q);
+ posixtimer_sig_ignore(t, q);
+ }
+ } else if (!hlist_unhashed(&tmr->ignored_list)) {
+ /*
+ * Covers the case where a timer was periodic and
+ * then the signal was ignored. Later it was rearmed
+ * as oneshot timer. The previous signal is invalid
+ * now, and this oneshot signal has to be dropped.
+ * Remove it from the ignored list and drop the
+ * reference count as the signal is not longer
+ * queued.
+ */
+ hlist_del_init(&tmr->ignored_list);
+ posixtimer_putref(tmr);
+ }
goto out;
}
- ret = 0;
+ /* This should never happen and leaks a reference count */
+ if (WARN_ON_ONCE(!hlist_unhashed(&tmr->ignored_list)))
+ hlist_del_init(&tmr->ignored_list);
+
if (unlikely(!list_empty(&q->list))) {
/* This holds a reference count already */
result = TRACE_SIGNAL_ALREADY_PENDING;
@@ -2000,7 +2054,22 @@ int posixtimer_send_sigqueue(struct k_itimer *tmr)
out:
trace_signal_generate(sig, &q->info, t, tmr->it_pid_type != PIDTYPE_PID, result);
unlock_task_sighand(t, &flags);
- return ret;
+ return 0;
+}
+
+static inline void posixtimer_sig_ignore(struct task_struct *tsk, struct sigqueue *q)
+{
+ struct k_itimer *tmr = container_of(q, struct k_itimer, sigq);
+
+ /*
+ * If the timer is marked deleted already or the signal originates
+ * from a non-periodic timer, then just drop the reference
+ * count. Otherwise queue it on the ignored list.
+ */
+ if (tmr->it_signal && tmr->it_sig_periodic)
+ hlist_add_head(&tmr->ignored_list, &tsk->signal->ignored_posix_timers);
+ else
+ posixtimer_putref(tmr);
}
static void posixtimer_sig_unignore(struct task_struct *tsk, int sig)
@@ -2048,6 +2117,7 @@ static void posixtimer_sig_unignore(struct task_struct *tsk, int sig)
}
}
#else /* CONFIG_POSIX_TIMERS */
+static inline void posixtimer_sig_ignore(struct task_struct *tsk, struct sigqueue *q) { }
static inline void posixtimer_sig_unignore(struct task_struct *tsk, int sig) { }
#endif /* !CONFIG_POSIX_TIMERS */
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index 2b88fb4..ea72db3 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -1072,12 +1072,17 @@ retry_delete:
spin_lock(¤t->sighand->siglock);
hlist_del(&timer->list);
posix_timer_cleanup_ignored(timer);
- spin_unlock(¤t->sighand->siglock);
/*
* A concurrent lookup could check timer::it_signal lockless. It
* will reevaluate with timer::it_lock held and observe the NULL.
+ *
+ * It must be written with siglock held so that the signal code
+ * observes timer->it_signal == NULL in do_sigaction(SIG_IGN),
+ * which prevents it from moving a pending signal of a deleted
+ * timer to the ignore list.
*/
WRITE_ONCE(timer->it_signal, NULL);
+ spin_unlock(¤t->sighand->siglock);
unlock_timer(timer, flags);
posix_timer_unhash_and_free(timer);
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] signal: Handle ignored signals in do_sigaction(action != SIG_IGN)
2024-11-05 8:14 ` [patch V7 17/21] signal: Handle ignored signals in do_sigaction(action != SIG_IGN) Thomas Gleixner
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
0 siblings, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Thomas Gleixner, Frederic Weisbecker, Peter Zijlstra (Intel),
x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: caf77435dd8a52cb39c602bdf67d35d6f782f553
Gitweb: https://git.kernel.org/tip/caf77435dd8a52cb39c602bdf67d35d6f782f553
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:52 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:45 +01:00
signal: Handle ignored signals in do_sigaction(action != SIG_IGN)
When a real handler (including SIG_DFL) is installed for a signal, which
had previously SIG_IGN set, then the list of ignored posix timers has to be
checked for timers which are affected by this change.
Add a list walk function which checks for the matching signal number and if
found requeues the timers signal, so the timer is rearmed on signal
delivery.
Rearming the timer right away is not possible because that requires to drop
sighand lock.
No functional change as the counter part which queues the timers on the
ignored list is still missing.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241105064214.054091076@linutronix.de
---
kernel/signal.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 52 insertions(+), 1 deletion(-)
diff --git a/kernel/signal.c b/kernel/signal.c
index d2734dc..908b49c 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -2002,7 +2002,54 @@ out:
unlock_task_sighand(t, &flags);
return ret;
}
-#endif /* CONFIG_POSIX_TIMERS */
+
+static void posixtimer_sig_unignore(struct task_struct *tsk, int sig)
+{
+ struct hlist_head *head = &tsk->signal->ignored_posix_timers;
+ struct hlist_node *tmp;
+ struct k_itimer *tmr;
+
+ if (likely(hlist_empty(head)))
+ return;
+
+ /*
+ * Rearming a timer with sighand lock held is not possible due to
+ * lock ordering vs. tmr::it_lock. Just stick the sigqueue back and
+ * let the signal delivery path deal with it whether it needs to be
+ * rearmed or not. This cannot be decided here w/o dropping sighand
+ * lock and creating a loop retry horror show.
+ */
+ hlist_for_each_entry_safe(tmr, tmp , head, ignored_list) {
+ struct task_struct *target;
+
+ /*
+ * tmr::sigq.info.si_signo is immutable, so accessing it
+ * without holding tmr::it_lock is safe.
+ */
+ if (tmr->sigq.info.si_signo != sig)
+ continue;
+
+ hlist_del_init(&tmr->ignored_list);
+
+ /* This should never happen and leaks a reference count */
+ if (WARN_ON_ONCE(!list_empty(&tmr->sigq.list)))
+ continue;
+
+ /*
+ * Get the target for the signal. If target is a thread and
+ * has exited by now, drop the reference count.
+ */
+ guard(rcu)();
+ target = posixtimer_get_target(tmr);
+ if (target)
+ posixtimer_queue_sigqueue(&tmr->sigq, target, tmr->it_pid_type);
+ else
+ posixtimer_putref(tmr);
+ }
+}
+#else /* CONFIG_POSIX_TIMERS */
+static inline void posixtimer_sig_unignore(struct task_struct *tsk, int sig) { }
+#endif /* !CONFIG_POSIX_TIMERS */
void do_notify_pidfd(struct task_struct *task)
{
@@ -4180,6 +4227,8 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
sigaction_compat_abi(act, oact);
if (act) {
+ bool was_ignored = k->sa.sa_handler == SIG_IGN;
+
sigdelsetmask(&act->sa.sa_mask,
sigmask(SIGKILL) | sigmask(SIGSTOP));
*k = *act;
@@ -4200,6 +4249,8 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
flush_sigqueue_mask(p, &mask, &p->signal->shared_pending);
for_each_thread(p, t)
flush_sigqueue_mask(p, &mask, &t->pending);
+ } else if (was_ignored) {
+ posixtimer_sig_unignore(p, sig);
}
}
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] posix-timers: Handle ignored list on delete and exit
2024-11-05 8:14 ` [patch V7 16/21] posix-timers: Handle ignored list on delete and exit Thomas Gleixner
2024-11-05 13:08 ` Frederic Weisbecker
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
1 sibling, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Thomas Gleixner, Frederic Weisbecker, Peter Zijlstra (Intel),
x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 0e20cd33acc7a173b23900550331ee82a23e9f00
Gitweb: https://git.kernel.org/tip/0e20cd33acc7a173b23900550331ee82a23e9f00
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:51 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:45 +01:00
posix-timers: Handle ignored list on delete and exit
To handle posix timer signals on sigaction(SIG_IGN) properly, the timers
will be queued on a separate ignored list.
Add the necessary cleanup code for timer_delete() and exit_itimers().
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241105064213.987530588@linutronix.de
---
include/linux/posix-timers.h | 4 +++-
kernel/time/posix-timers.c | 28 ++++++++++++++++++++++++++++
2 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 49a8961..1608b52 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -152,7 +152,8 @@ static inline void posix_cputimers_init_work(void) { }
/**
* struct k_itimer - POSIX.1b interval timer structure.
- * @list: List head for binding the timer to signals->posix_timers
+ * @list: List node for binding the timer to tsk::signal::posix_timers
+ * @ignored_list: List node for tracking ignored timers in tsk::signal::ignored_posix_timers
* @t_hash: Entry in the posix timer hash table
* @it_lock: Lock protecting the timer
* @kclock: Pointer to the k_clock struct handling this timer
@@ -176,6 +177,7 @@ static inline void posix_cputimers_init_work(void) { }
*/
struct k_itimer {
struct hlist_node list;
+ struct hlist_node ignored_list;
struct hlist_node t_hash;
spinlock_t it_lock;
const struct k_clock *kclock;
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index f20c06d..2b88fb4 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -1027,6 +1027,18 @@ int common_timer_del(struct k_itimer *timer)
return 0;
}
+/*
+ * If the deleted timer is on the ignored list, remove it and
+ * drop the associated reference.
+ */
+static inline void posix_timer_cleanup_ignored(struct k_itimer *tmr)
+{
+ if (!hlist_unhashed(&tmr->ignored_list)) {
+ hlist_del_init(&tmr->ignored_list);
+ posixtimer_putref(tmr);
+ }
+}
+
static inline int timer_delete_hook(struct k_itimer *timer)
{
const struct k_clock *kc = timer->kclock;
@@ -1059,6 +1071,7 @@ retry_delete:
spin_lock(¤t->sighand->siglock);
hlist_del(&timer->list);
+ posix_timer_cleanup_ignored(timer);
spin_unlock(¤t->sighand->siglock);
/*
* A concurrent lookup could check timer::it_signal lockless. It
@@ -1110,6 +1123,8 @@ retry_delete:
}
hlist_del(&timer->list);
+ posix_timer_cleanup_ignored(timer);
+
/*
* Setting timer::it_signal to NULL is technically not required
* here as nothing can access the timer anymore legitimately via
@@ -1142,6 +1157,19 @@ void exit_itimers(struct task_struct *tsk)
/* The timers are not longer accessible via tsk::signal */
while (!hlist_empty(&timers))
itimer_delete(hlist_entry(timers.first, struct k_itimer, list));
+
+ /*
+ * There should be no timers on the ignored list. itimer_delete() has
+ * mopped them up.
+ */
+ if (!WARN_ON_ONCE(!hlist_empty(&tsk->signal->ignored_posix_timers)))
+ return;
+
+ hlist_move_list(&tsk->signal->ignored_posix_timers, &timers);
+ while (!hlist_empty(&timers)) {
+ posix_timer_cleanup_ignored(hlist_entry(timers.first, struct k_itimer,
+ ignored_list));
+ }
}
SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock,
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] signal: Provide ignored_posix_timers list
2024-11-05 8:14 ` [patch V7 15/21] signal: Provide ignored_posix_timers list Thomas Gleixner
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
0 siblings, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Thomas Gleixner, Frederic Weisbecker, Peter Zijlstra (Intel),
x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 69f032c92cf883ea74a4b69ba3d91317aa6f174e
Gitweb: https://git.kernel.org/tip/69f032c92cf883ea74a4b69ba3d91317aa6f174e
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:49 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:45 +01:00
signal: Provide ignored_posix_timers list
To prepare for handling posix timer signals on sigaction(SIG_IGN) properly,
add a list to task::signal.
This list will be used to queue posix timers so their signal can be
requeued when SIG_IGN is lifted later.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241105064213.920101900@linutronix.de
---
include/linux/sched/signal.h | 1 +
init/init_task.c | 5 +++--
kernel/fork.c | 1 +
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h
index 02972fd..d5d03d9 100644
--- a/include/linux/sched/signal.h
+++ b/include/linux/sched/signal.h
@@ -138,6 +138,7 @@ struct signal_struct {
/* POSIX.1b Interval Timers */
unsigned int next_posix_timer_id;
struct hlist_head posix_timers;
+ struct hlist_head ignored_posix_timers;
/* ITIMER_REAL timer for the process */
struct hrtimer real_timer;
diff --git a/init/init_task.c b/init/init_task.c
index 136a823..e557f62 100644
--- a/init/init_task.c
+++ b/init/init_task.c
@@ -30,8 +30,9 @@ static struct signal_struct init_signals = {
.cred_guard_mutex = __MUTEX_INITIALIZER(init_signals.cred_guard_mutex),
.exec_update_lock = __RWSEM_INITIALIZER(init_signals.exec_update_lock),
#ifdef CONFIG_POSIX_TIMERS
- .posix_timers = HLIST_HEAD_INIT,
- .cputimer = {
+ .posix_timers = HLIST_HEAD_INIT,
+ .ignored_posix_timers = HLIST_HEAD_INIT,
+ .cputimer = {
.cputime_atomic = INIT_CPUTIME_ATOMIC,
},
#endif
diff --git a/kernel/fork.c b/kernel/fork.c
index 60c0b48..c2bd836 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1864,6 +1864,7 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
#ifdef CONFIG_POSIX_TIMERS
INIT_HLIST_HEAD(&sig->posix_timers);
+ INIT_HLIST_HEAD(&sig->ignored_posix_timers);
hrtimer_init(&sig->real_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
sig->real_timer.function = it_real_fn;
#endif
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] posix-timers: Move sequence logic into struct k_itimer
2024-11-05 8:14 ` [patch V7 14/21] posix-timers: Move sequence logic into struct k_itimer Thomas Gleixner
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
0 siblings, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Eric W. Biederman, Thomas Gleixner, Frederic Weisbecker, x86,
linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 647da5f709f112319c0d51e06f330d8afecb1940
Gitweb: https://git.kernel.org/tip/647da5f709f112319c0d51e06f330d8afecb1940
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:48 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:45 +01:00
posix-timers: Move sequence logic into struct k_itimer
The posix timer signal handling uses siginfo::si_sys_private for handling
the sequence counter check. That indirection is not longer required and the
sequence count value at signal queueing time can be stored in struct
k_itimer itself.
This removes the requirement of treating siginfo::si_sys_private special as
it's now always zero as the kernel does not touch it anymore.
Suggested-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: "Eric W. Biederman" <ebiederm@xmission.com>
Link: https://lore.kernel.org/all/20241105064213.852619866@linutronix.de
---
include/linux/posix-timers.h | 2 ++
include/uapi/asm-generic/siginfo.h | 2 +-
kernel/signal.c | 8 +++-----
kernel/time/posix-timers.c | 5 +----
4 files changed, 7 insertions(+), 10 deletions(-)
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 28c0a30..49a8961 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -162,6 +162,7 @@ static inline void posix_cputimers_init_work(void) { }
* @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
+ * @it_sigqueue_seq: The sequence count at the point where the signal was queued
* @it_sigev_notify: The notify word of sigevent struct for signal delivery
* @it_interval: The interval for periodic timers
* @it_signal: Pointer to the creators signal struct
@@ -184,6 +185,7 @@ struct k_itimer {
s64 it_overrun;
s64 it_overrun_last;
unsigned int it_signal_seq;
+ unsigned int it_sigqueue_seq;
int it_sigev_notify;
enum pid_type it_pid_type;
ktime_t it_interval;
diff --git a/include/uapi/asm-generic/siginfo.h b/include/uapi/asm-generic/siginfo.h
index b7bc545..5a1ca43 100644
--- a/include/uapi/asm-generic/siginfo.h
+++ b/include/uapi/asm-generic/siginfo.h
@@ -46,7 +46,7 @@ union __sifields {
__kernel_timer_t _tid; /* timer id */
int _overrun; /* overrun count */
sigval_t _sigval; /* same as below */
- int _sys_private; /* not to be passed to user */
+ int _sys_private; /* Not used by the kernel. Historic leftover. Always 0. */
} _timer;
/* POSIX.1b signals */
diff --git a/kernel/signal.c b/kernel/signal.c
index d267a2c..d2734dc 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1976,12 +1976,10 @@ int posixtimer_send_sigqueue(struct k_itimer *tmr)
return -1;
/*
- * Update @q::info::si_sys_private for posix timer signals with
- * sighand locked to prevent a race against dequeue_signal() which
- * decides based on si_sys_private whether to invoke
- * posixtimer_rearm() or not.
+ * Update @tmr::sigqueue_seq for posix timer signals with sighand
+ * locked to prevent a race against dequeue_signal().
*/
- q->info.si_sys_private = tmr->it_signal_seq;
+ tmr->it_sigqueue_seq = tmr->it_signal_seq;
ret = 1; /* the signal is ignored */
if (!prepare_signal(sig, t, false)) {
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index 2e2c0ed..f20c06d 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -259,7 +259,7 @@ static bool __posixtimer_deliver_signal(struct kernel_siginfo *info, struct k_it
* since the signal was queued. In either case, don't rearm and
* drop the signal.
*/
- if (timr->it_signal_seq != info->si_sys_private || WARN_ON_ONCE(!timr->it_signal))
+ if (timr->it_signal_seq != timr->it_sigqueue_seq || WARN_ON_ONCE(!timr->it_signal))
return false;
if (!timr->it_interval || WARN_ON_ONCE(timr->it_status != POSIX_TIMER_REQUEUE_PENDING))
@@ -297,9 +297,6 @@ bool posixtimer_deliver_signal(struct kernel_siginfo *info, struct sigqueue *tim
posixtimer_putref(timr);
spin_lock(¤t->sighand->siglock);
-
- /* Don't expose the si_sys_private value to userspace */
- info->si_sys_private = 0;
return ret;
}
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] signal: Cleanup unused posix-timer leftovers
2024-11-05 8:14 ` [patch V7 13/21] signal: Cleanup unused posix-timer leftovers Thomas Gleixner
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
0 siblings, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Thomas Gleixner, Frederic Weisbecker, Peter Zijlstra (Intel),
x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: c2a4796a154bb952be1106911841aab2c8c17c4d
Gitweb: https://git.kernel.org/tip/c2a4796a154bb952be1106911841aab2c8c17c4d
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:46 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:44 +01:00
signal: Cleanup unused posix-timer leftovers
Remove the leftovers of sigqueue preallocation as it's not longer used.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241105064213.786506636@linutronix.de
---
include/linux/sched/signal.h | 2 +--
kernel/signal.c | 39 +++--------------------------------
2 files changed, 4 insertions(+), 37 deletions(-)
diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h
index 36283c1..02972fd 100644
--- a/include/linux/sched/signal.h
+++ b/include/linux/sched/signal.h
@@ -338,8 +338,6 @@ extern void force_fatal_sig(int);
extern void force_exit_sig(int);
extern int send_sig(int, struct task_struct *, int);
extern int zap_other_threads(struct task_struct *p);
-extern struct sigqueue *sigqueue_alloc(void);
-extern void sigqueue_free(struct sigqueue *);
extern int do_sigaction(int, struct k_sigaction *, struct k_sigaction *);
static inline void clear_notify_signal(void)
diff --git a/kernel/signal.c b/kernel/signal.c
index 2d74cd5..d267a2c 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -439,8 +439,8 @@ static void __sigqueue_init(struct sigqueue *q, struct ucounts *ucounts,
* - this may be called without locks if and only if t == current, otherwise an
* appropriate lock must be held to stop the target task from exiting
*/
-static struct sigqueue *__sigqueue_alloc(int sig, struct task_struct *t, gfp_t gfp_flags,
- int override_rlimit, const unsigned int sigqueue_flags)
+static struct sigqueue *sigqueue_alloc(int sig, struct task_struct *t, gfp_t gfp_flags,
+ int override_rlimit)
{
struct ucounts *ucounts = sig_get_ucounts(t, sig, override_rlimit);
struct sigqueue *q;
@@ -454,7 +454,7 @@ static struct sigqueue *__sigqueue_alloc(int sig, struct task_struct *t, gfp_t g
return NULL;
}
- __sigqueue_init(q, ucounts, sigqueue_flags);
+ __sigqueue_init(q, ucounts, 0);
return q;
}
@@ -1070,7 +1070,7 @@ static int __send_signal_locked(int sig, struct kernel_siginfo *info,
else
override_rlimit = 0;
- q = __sigqueue_alloc(sig, t, GFP_ATOMIC, override_rlimit, 0);
+ q = sigqueue_alloc(sig, t, GFP_ATOMIC, override_rlimit);
if (q) {
list_add_tail(&q->list, &pending->list);
@@ -1926,37 +1926,6 @@ bool posixtimer_init_sigqueue(struct sigqueue *q)
return true;
}
-struct sigqueue *sigqueue_alloc(void)
-{
- return __sigqueue_alloc(-1, current, GFP_KERNEL, 0, SIGQUEUE_PREALLOC);
-}
-
-void sigqueue_free(struct sigqueue *q)
-{
- spinlock_t *lock = ¤t->sighand->siglock;
- unsigned long flags;
-
- if (WARN_ON_ONCE(!(q->flags & SIGQUEUE_PREALLOC)))
- return;
- /*
- * We must hold ->siglock while testing q->list
- * to serialize with collect_signal() or with
- * __exit_signal()->flush_sigqueue().
- */
- spin_lock_irqsave(lock, flags);
- q->flags &= ~SIGQUEUE_PREALLOC;
- /*
- * If it is queued it will be freed when dequeued,
- * like the "regular" sigqueue.
- */
- if (!list_empty(&q->list))
- q = NULL;
- spin_unlock_irqrestore(lock, flags);
-
- if (q)
- __sigqueue_free(q);
-}
-
static void posixtimer_queue_sigqueue(struct sigqueue *q, struct task_struct *t, enum pid_type type)
{
struct sigpending *pending;
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] posix-timers: Embed sigqueue in struct k_itimer
2024-11-05 8:14 ` [patch V7 12/21] posix-timers: Embed sigqueue in struct k_itimer Thomas Gleixner
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
0 siblings, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Thomas Gleixner, Frederic Weisbecker, Peter Zijlstra (Intel),
x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 6017a158beb13b412e55a451379798aae5876514
Gitweb: https://git.kernel.org/tip/6017a158beb13b412e55a451379798aae5876514
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:45 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:44 +01:00
posix-timers: Embed sigqueue in struct k_itimer
To cure the SIG_IGN handling for posix interval timers, the preallocated
sigqueue needs to be embedded into struct k_itimer to prevent life time
races of all sorts.
Now that the prerequisites are in place, embed the sigqueue into struct
k_itimer and fixup the relevant usage sites.
Aside of preparing for proper SIG_IGN handling, this spares an extra
allocation.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241105064213.719695194@linutronix.de
---
fs/proc/base.c | 4 +-
include/linux/posix-timers.h | 23 ++++++++-
kernel/signal.c | 19 +++++---
kernel/time/posix-timers.c | 88 ++++++++++++++++++++---------------
4 files changed, 87 insertions(+), 47 deletions(-)
diff --git a/fs/proc/base.c b/fs/proc/base.c
index b31283d..6a37a43 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2553,8 +2553,8 @@ static int show_timer(struct seq_file *m, void *v)
seq_printf(m, "ID: %d\n", timer->it_id);
seq_printf(m, "signal: %d/%px\n",
- timer->sigq->info.si_signo,
- timer->sigq->info.si_value.sival_ptr);
+ timer->sigq.info.si_signo,
+ timer->sigq.info.si_value.sival_ptr);
seq_printf(m, "notify: %s/%s.%d\n",
nstr[notify & ~SIGEV_THREAD_ID],
(notify & SIGEV_THREAD_ID) ? "tid" : "pid",
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 39f1db7..28c0a30 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -39,6 +39,8 @@ static inline int clockid_to_fd(const clockid_t clk)
#ifdef CONFIG_POSIX_TIMERS
+#include <linux/signal_types.h>
+
/**
* cpu_timer - Posix CPU timer representation for k_itimer
* @node: timerqueue node to queue in the task/sig
@@ -166,7 +168,7 @@ static inline void posix_cputimers_init_work(void) { }
* @it_pid: The pid of the process/task targeted by the signal
* @it_process: The task to wakeup on clock_nanosleep (CPU timers)
* @rcuref: Reference count for life time management
- * @sigq: Pointer to preallocated sigqueue
+ * @sigq: Embedded sigqueue
* @it: Union representing the various posix timer type
* internals.
* @rcu: RCU head for freeing the timer.
@@ -190,7 +192,7 @@ struct k_itimer {
struct pid *it_pid;
struct task_struct *it_process;
};
- struct sigqueue *sigq;
+ struct sigqueue sigq;
rcuref_t rcuref;
union {
struct {
@@ -218,6 +220,23 @@ static inline void posixtimer_putref(struct k_itimer *tmr)
if (rcuref_put(&tmr->rcuref))
posixtimer_free_timer(tmr);
}
+
+static inline void posixtimer_sigqueue_getref(struct sigqueue *q)
+{
+ struct k_itimer *tmr = container_of(q, struct k_itimer, sigq);
+
+ WARN_ON_ONCE(!rcuref_get(&tmr->rcuref));
+}
+
+static inline void posixtimer_sigqueue_putref(struct sigqueue *q)
+{
+ struct k_itimer *tmr = container_of(q, struct k_itimer, sigq);
+
+ posixtimer_putref(tmr);
+}
+#else /* CONFIG_POSIX_TIMERS */
+static inline void posixtimer_sigqueue_getref(struct sigqueue *q) { }
+static inline void posixtimer_sigqueue_putref(struct sigqueue *q) { }
#endif /* !CONFIG_POSIX_TIMERS */
#endif
diff --git a/kernel/signal.c b/kernel/signal.c
index 0ddb5dd..2d74cd5 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -460,8 +460,10 @@ static struct sigqueue *__sigqueue_alloc(int sig, struct task_struct *t, gfp_t g
static void __sigqueue_free(struct sigqueue *q)
{
- if (q->flags & SIGQUEUE_PREALLOC)
+ if (q->flags & SIGQUEUE_PREALLOC) {
+ posixtimer_sigqueue_putref(q);
return;
+ }
if (q->ucounts) {
dec_rlimit_put_ucounts(q->ucounts, UCOUNT_RLIMIT_SIGPENDING);
q->ucounts = NULL;
@@ -569,11 +571,11 @@ still_pending:
copy_siginfo(info, &first->info);
/*
- * posix-timer signals are preallocated and freed when the
- * timer goes away. Either directly or by clearing
- * SIGQUEUE_PREALLOC so that the next delivery will free
- * them. Spare the extra round through __sigqueue_free()
- * which is ignoring preallocated signals.
+ * posix-timer signals are preallocated and freed when the last
+ * reference count is dropped in posixtimer_deliver_signal() or
+ * immediately on timer deletion when the signal is not pending.
+ * Spare the extra round through __sigqueue_free() which is
+ * ignoring preallocated signals.
*/
if (unlikely((first->flags & SIGQUEUE_PREALLOC) && (info->si_code == SI_TIMER)))
*timer_sigq = first;
@@ -1989,7 +1991,7 @@ static inline struct task_struct *posixtimer_get_target(struct k_itimer *tmr)
int posixtimer_send_sigqueue(struct k_itimer *tmr)
{
- struct sigqueue *q = tmr->sigq;
+ struct sigqueue *q = &tmr->sigq;
int sig = q->info.si_signo;
struct task_struct *t;
unsigned long flags;
@@ -2020,9 +2022,12 @@ int posixtimer_send_sigqueue(struct k_itimer *tmr)
ret = 0;
if (unlikely(!list_empty(&q->list))) {
+ /* This holds a reference count already */
result = TRACE_SIGNAL_ALREADY_PENDING;
goto out;
}
+
+ posixtimer_sigqueue_getref(q);
posixtimer_queue_sigqueue(q, t, tmr->it_pid_type);
result = TRACE_SIGNAL_DELIVERED;
out:
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index d6fef06..2e2c0ed 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -250,15 +250,40 @@ static void common_hrtimer_rearm(struct k_itimer *timr)
hrtimer_restart(timer);
}
+static bool __posixtimer_deliver_signal(struct kernel_siginfo *info, struct k_itimer *timr)
+{
+ guard(spinlock)(&timr->it_lock);
+
+ /*
+ * Check if the timer is still alive or whether it got modified
+ * since the signal was queued. In either case, don't rearm and
+ * drop the signal.
+ */
+ if (timr->it_signal_seq != info->si_sys_private || WARN_ON_ONCE(!timr->it_signal))
+ return false;
+
+ if (!timr->it_interval || WARN_ON_ONCE(timr->it_status != POSIX_TIMER_REQUEUE_PENDING))
+ return true;
+
+ timr->kclock->timer_rearm(timr);
+ timr->it_status = POSIX_TIMER_ARMED;
+ timr->it_overrun_last = timr->it_overrun;
+ timr->it_overrun = -1LL;
+ ++timr->it_signal_seq;
+ info->si_overrun = timer_overrun_to_int(timr);
+ return true;
+}
+
/*
* This function is called from the signal delivery code. It decides
- * whether the signal should be dropped and rearms interval timers.
+ * whether the signal should be dropped and rearms interval timers. The
+ * timer can be unconditionally accessed as there is a reference held on
+ * it.
*/
bool posixtimer_deliver_signal(struct kernel_siginfo *info, struct sigqueue *timer_sigq)
{
- struct k_itimer *timr;
- unsigned long flags;
- bool ret = false;
+ struct k_itimer *timr = container_of(timer_sigq, struct k_itimer, sigq);
+ bool ret;
/*
* Release siglock to ensure proper locking order versus
@@ -266,28 +291,11 @@ bool posixtimer_deliver_signal(struct kernel_siginfo *info, struct sigqueue *tim
*/
spin_unlock(¤t->sighand->siglock);
- timr = lock_timer(info->si_tid, &flags);
- if (!timr)
- goto out;
-
- if (timr->it_signal_seq != info->si_sys_private)
- goto out_unlock;
-
- if (timr->it_interval && !WARN_ON_ONCE(timr->it_status != POSIX_TIMER_REQUEUE_PENDING)) {
- timr->kclock->timer_rearm(timr);
+ ret = __posixtimer_deliver_signal(info, timr);
- timr->it_status = POSIX_TIMER_ARMED;
- timr->it_overrun_last = timr->it_overrun;
- timr->it_overrun = -1LL;
- ++timr->it_signal_seq;
-
- info->si_overrun = timer_overrun_to_int(timr);
- }
- ret = true;
+ /* Drop the reference which was acquired when the signal was queued */
+ posixtimer_putref(timr);
-out_unlock:
- unlock_timer(timr, flags);
-out:
spin_lock(¤t->sighand->siglock);
/* Don't expose the si_sys_private value to userspace */
@@ -404,17 +412,17 @@ static struct pid *good_sigevent(sigevent_t * event)
}
}
-static struct k_itimer * alloc_posix_timer(void)
+static struct k_itimer *alloc_posix_timer(void)
{
struct k_itimer *tmr = kmem_cache_zalloc(posix_timers_cache, GFP_KERNEL);
if (!tmr)
return tmr;
- if (unlikely(!(tmr->sigq = sigqueue_alloc()))) {
+
+ if (unlikely(!posixtimer_init_sigqueue(&tmr->sigq))) {
kmem_cache_free(posix_timers_cache, tmr);
return NULL;
}
- clear_siginfo(&tmr->sigq->info);
rcuref_init(&tmr->rcuref, 1);
return tmr;
}
@@ -422,7 +430,8 @@ static struct k_itimer * alloc_posix_timer(void)
void posixtimer_free_timer(struct k_itimer *tmr)
{
put_pid(tmr->it_pid);
- sigqueue_free(tmr->sigq);
+ if (tmr->sigq.ucounts)
+ dec_rlimit_put_ucounts(tmr->sigq.ucounts, UCOUNT_RLIMIT_SIGPENDING);
kfree_rcu(tmr, rcu);
}
@@ -484,13 +493,13 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
goto out;
}
new_timer->it_sigev_notify = event->sigev_notify;
- new_timer->sigq->info.si_signo = event->sigev_signo;
- new_timer->sigq->info.si_value = event->sigev_value;
+ new_timer->sigq.info.si_signo = event->sigev_signo;
+ new_timer->sigq.info.si_value = event->sigev_value;
} else {
new_timer->it_sigev_notify = SIGEV_SIGNAL;
- new_timer->sigq->info.si_signo = SIGALRM;
- memset(&new_timer->sigq->info.si_value, 0, sizeof(sigval_t));
- new_timer->sigq->info.si_value.sival_int = new_timer->it_id;
+ new_timer->sigq.info.si_signo = SIGALRM;
+ memset(&new_timer->sigq.info.si_value, 0, sizeof(sigval_t));
+ new_timer->sigq.info.si_value.sival_int = new_timer->it_id;
new_timer->it_pid = get_pid(task_tgid(current));
}
@@ -499,8 +508,8 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
else
new_timer->it_pid_type = PIDTYPE_TGID;
- new_timer->sigq->info.si_tid = new_timer->it_id;
- new_timer->sigq->info.si_code = SI_TIMER;
+ new_timer->sigq.info.si_tid = new_timer->it_id;
+ new_timer->sigq.info.si_code = SI_TIMER;
if (copy_to_user(created_timer_id, &new_timer_id, sizeof (new_timer_id))) {
error = -EFAULT;
@@ -584,7 +593,14 @@ static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags)
* 1) Set timr::it_signal to NULL with timr::it_lock held
* 2) Release timr::it_lock
* 3) Remove from the hash under hash_lock
- * 4) Call RCU for removal after the grace period
+ * 4) Put the reference count.
+ *
+ * The reference count might not drop to zero if timr::sigq is
+ * queued. In that case the signal delivery or flush will put the
+ * last reference count.
+ *
+ * When the reference count reaches zero, the timer is scheduled
+ * for RCU removal after the grace period.
*
* Holding rcu_read_lock() accross the lookup ensures that
* the timer cannot be freed.
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] signal: Replace resched_timer logic
2024-11-05 8:14 ` [patch V7 11/21] signal: Replace resched_timer logic Thomas Gleixner
2024-11-05 12:08 ` Frederic Weisbecker
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
1 sibling, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Eric W. Biederman, Thomas Gleixner, Frederic Weisbecker, x86,
linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 11629b9808e5900d675fd469d19932ea48060de3
Gitweb: https://git.kernel.org/tip/11629b9808e5900d675fd469d19932ea48060de3
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:43 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:44 +01:00
signal: Replace resched_timer logic
In preparation for handling ignored posix timer signals correctly and
embedding the sigqueue struct into struct k_itimer, hand down a pointer to
the sigqueue struct into posix_timer_deliver_signal() instead of just
having a boolean flag.
No functional change.
Suggested-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: "Eric W. Biederman" <ebiederm@xmission.com>
Link: https://lore.kernel.org/all/20241105064213.652658158@linutronix.de
---
include/linux/posix-timers.h | 5 +++--
kernel/signal.c | 32 ++++++++++++++++++++------------
kernel/time/posix-timers.c | 2 +-
3 files changed, 24 insertions(+), 15 deletions(-)
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 52611ea..39f1db7 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -110,7 +110,7 @@ static inline void posix_cputimers_rt_watchdog(struct posix_cputimers *pct,
void posixtimer_rearm_itimer(struct task_struct *p);
bool posixtimer_init_sigqueue(struct sigqueue *q);
int posixtimer_send_sigqueue(struct k_itimer *tmr);
-bool posixtimer_deliver_signal(struct kernel_siginfo *info);
+bool posixtimer_deliver_signal(struct kernel_siginfo *info, struct sigqueue *timer_sigq);
void posixtimer_free_timer(struct k_itimer *timer);
/* Init task static initializer */
@@ -135,7 +135,8 @@ static inline void posix_cputimers_init(struct posix_cputimers *pct) { }
static inline void posix_cputimers_group_init(struct posix_cputimers *pct,
u64 cpu_limit) { }
static inline void posixtimer_rearm_itimer(struct task_struct *p) { }
-static inline bool posixtimer_deliver_signal(struct kernel_siginfo *info) { return false; }
+static inline bool posixtimer_deliver_signal(struct kernel_siginfo *info,
+ struct sigqueue *timer_sigq) { return false; }
static inline void posixtimer_free_timer(struct k_itimer *timer) { }
#endif
diff --git a/kernel/signal.c b/kernel/signal.c
index 5b71e26..0ddb5dd 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -545,7 +545,7 @@ bool unhandled_signal(struct task_struct *tsk, int sig)
}
static void collect_signal(int sig, struct sigpending *list, kernel_siginfo_t *info,
- bool *resched_timer)
+ struct sigqueue **timer_sigq)
{
struct sigqueue *q, *first = NULL;
@@ -568,10 +568,17 @@ still_pending:
list_del_init(&first->list);
copy_siginfo(info, &first->info);
- *resched_timer = (first->flags & SIGQUEUE_PREALLOC) &&
- (info->si_code == SI_TIMER);
-
- __sigqueue_free(first);
+ /*
+ * posix-timer signals are preallocated and freed when the
+ * timer goes away. Either directly or by clearing
+ * SIGQUEUE_PREALLOC so that the next delivery will free
+ * them. Spare the extra round through __sigqueue_free()
+ * which is ignoring preallocated signals.
+ */
+ if (unlikely((first->flags & SIGQUEUE_PREALLOC) && (info->si_code == SI_TIMER)))
+ *timer_sigq = first;
+ else
+ __sigqueue_free(first);
} else {
/*
* Ok, it wasn't in the queue. This must be
@@ -588,12 +595,12 @@ still_pending:
}
static int __dequeue_signal(struct sigpending *pending, sigset_t *mask,
- kernel_siginfo_t *info, bool *resched_timer)
+ kernel_siginfo_t *info, struct sigqueue **timer_sigq)
{
int sig = next_signal(pending, mask);
if (sig)
- collect_signal(sig, pending, info, resched_timer);
+ collect_signal(sig, pending, info, timer_sigq);
return sig;
}
@@ -605,18 +612,19 @@ static int __dequeue_signal(struct sigpending *pending, sigset_t *mask,
int dequeue_signal(sigset_t *mask, kernel_siginfo_t *info, enum pid_type *type)
{
struct task_struct *tsk = current;
- bool resched_timer = false;
+ struct sigqueue *timer_sigq;
int signr;
lockdep_assert_held(&tsk->sighand->siglock);
again:
*type = PIDTYPE_PID;
- signr = __dequeue_signal(&tsk->pending, mask, info, &resched_timer);
+ timer_sigq = NULL;
+ signr = __dequeue_signal(&tsk->pending, mask, info, &timer_sigq);
if (!signr) {
*type = PIDTYPE_TGID;
signr = __dequeue_signal(&tsk->signal->shared_pending,
- mask, info, &resched_timer);
+ mask, info, &timer_sigq);
if (unlikely(signr == SIGALRM))
posixtimer_rearm_itimer(tsk);
@@ -642,8 +650,8 @@ again:
current->jobctl |= JOBCTL_STOP_DEQUEUED;
}
- if (IS_ENABLED(CONFIG_POSIX_TIMERS) && unlikely(resched_timer)) {
- if (!posixtimer_deliver_signal(info))
+ if (IS_ENABLED(CONFIG_POSIX_TIMERS) && unlikely(timer_sigq)) {
+ if (!posixtimer_deliver_signal(info, timer_sigq))
goto again;
}
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index 0901ed9..d6fef06 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -254,7 +254,7 @@ static void common_hrtimer_rearm(struct k_itimer *timr)
* This function is called from the signal delivery code. It decides
* whether the signal should be dropped and rearms interval timers.
*/
-bool posixtimer_deliver_signal(struct kernel_siginfo *info)
+bool posixtimer_deliver_signal(struct kernel_siginfo *info, struct sigqueue *timer_sigq)
{
struct k_itimer *timr;
unsigned long flags;
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] signal: Refactor send_sigqueue()
2024-11-05 8:14 ` [patch V7 10/21] signal: Refactor send_sigqueue() Thomas Gleixner
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
0 siblings, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Thomas Gleixner, Frederic Weisbecker, Peter Zijlstra (Intel),
x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 0360ed14d9826678a50fa2b873e522a24cd3c018
Gitweb: https://git.kernel.org/tip/0360ed14d9826678a50fa2b873e522a24cd3c018
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:42 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:44 +01:00
signal: Refactor send_sigqueue()
To handle posix timers which have their signal ignored via SIG_IGN properly
it is required to requeue a ignored signal for delivery when SIG_IGN is
lifted so the timer gets rearmed.
Split the required code out of send_sigqueue() so it can be reused in
context of sigaction().
While at it rename send_sigqueue() to posixtimer_send_sigqueue() so its
clear what this is about.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241105064213.586453412@linutronix.de
---
include/linux/posix-timers.h | 1 +-
include/linux/sched/signal.h | 1 +-
kernel/signal.c | 82 +++++++++++++++++++----------------
kernel/time/posix-timers.c | 2 +-
4 files changed, 47 insertions(+), 39 deletions(-)
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 9471765..52611ea 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -109,6 +109,7 @@ static inline void posix_cputimers_rt_watchdog(struct posix_cputimers *pct,
void posixtimer_rearm_itimer(struct task_struct *p);
bool posixtimer_init_sigqueue(struct sigqueue *q);
+int posixtimer_send_sigqueue(struct k_itimer *tmr);
bool posixtimer_deliver_signal(struct kernel_siginfo *info);
void posixtimer_free_timer(struct k_itimer *timer);
diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h
index bd9f569..36283c1 100644
--- a/include/linux/sched/signal.h
+++ b/include/linux/sched/signal.h
@@ -340,7 +340,6 @@ extern int send_sig(int, struct task_struct *, int);
extern int zap_other_threads(struct task_struct *p);
extern struct sigqueue *sigqueue_alloc(void);
extern void sigqueue_free(struct sigqueue *);
-extern int send_sigqueue(struct sigqueue *, struct pid *, enum pid_type, int si_private);
extern int do_sigaction(int, struct k_sigaction *, struct k_sigaction *);
static inline void clear_notify_signal(void)
diff --git a/kernel/signal.c b/kernel/signal.c
index 911ed3a..5b71e26 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1947,40 +1947,54 @@ void sigqueue_free(struct sigqueue *q)
__sigqueue_free(q);
}
-int send_sigqueue(struct sigqueue *q, struct pid *pid, enum pid_type type, int si_private)
+static void posixtimer_queue_sigqueue(struct sigqueue *q, struct task_struct *t, enum pid_type type)
{
- int sig = q->info.si_signo;
struct sigpending *pending;
+ int sig = q->info.si_signo;
+
+ signalfd_notify(t, sig);
+ pending = (type != PIDTYPE_PID) ? &t->signal->shared_pending : &t->pending;
+ list_add_tail(&q->list, &pending->list);
+ sigaddset(&pending->signal, sig);
+ complete_signal(sig, t, type);
+}
+
+/*
+ * This function is used by POSIX timers to deliver a timer signal.
+ * Where type is PIDTYPE_PID (such as for timers with SIGEV_THREAD_ID
+ * set), the signal must be delivered to the specific thread (queues
+ * into t->pending).
+ *
+ * Where type is not PIDTYPE_PID, signals must be delivered to the
+ * process. In this case, prefer to deliver to current if it is in
+ * the same thread group as the target process, which avoids
+ * unnecessarily waking up a potentially idle task.
+ */
+static inline struct task_struct *posixtimer_get_target(struct k_itimer *tmr)
+{
+ struct task_struct *t = pid_task(tmr->it_pid, tmr->it_pid_type);
+
+ if (t && tmr->it_pid_type != PIDTYPE_PID && same_thread_group(t, current))
+ t = current;
+ return t;
+}
+
+int posixtimer_send_sigqueue(struct k_itimer *tmr)
+{
+ struct sigqueue *q = tmr->sigq;
+ int sig = q->info.si_signo;
struct task_struct *t;
unsigned long flags;
int ret, result;
- if (WARN_ON_ONCE(!(q->flags & SIGQUEUE_PREALLOC)))
- return 0;
- if (WARN_ON_ONCE(q->info.si_code != SI_TIMER))
- return 0;
-
- ret = -1;
- rcu_read_lock();
+ guard(rcu)();
- /*
- * This function is used by POSIX timers to deliver a timer signal.
- * Where type is PIDTYPE_PID (such as for timers with SIGEV_THREAD_ID
- * set), the signal must be delivered to the specific thread (queues
- * into t->pending).
- *
- * Where type is not PIDTYPE_PID, signals must be delivered to the
- * process. In this case, prefer to deliver to current if it is in
- * the same thread group as the target process, which avoids
- * unnecessarily waking up a potentially idle task.
- */
- t = pid_task(pid, type);
+ t = posixtimer_get_target(tmr);
if (!t)
- goto ret;
- if (type != PIDTYPE_PID && same_thread_group(t, current))
- t = current;
+ return -1;
+
if (!likely(lock_task_sighand(t, &flags)))
- goto ret;
+ return -1;
/*
* Update @q::info::si_sys_private for posix timer signals with
@@ -1988,30 +2002,24 @@ int send_sigqueue(struct sigqueue *q, struct pid *pid, enum pid_type type, int s
* decides based on si_sys_private whether to invoke
* posixtimer_rearm() or not.
*/
- q->info.si_sys_private = si_private;
+ q->info.si_sys_private = tmr->it_signal_seq;
ret = 1; /* the signal is ignored */
- result = TRACE_SIGNAL_IGNORED;
- if (!prepare_signal(sig, t, false))
+ if (!prepare_signal(sig, t, false)) {
+ result = TRACE_SIGNAL_IGNORED;
goto out;
+ }
ret = 0;
if (unlikely(!list_empty(&q->list))) {
result = TRACE_SIGNAL_ALREADY_PENDING;
goto out;
}
-
- signalfd_notify(t, sig);
- pending = (type != PIDTYPE_PID) ? &t->signal->shared_pending : &t->pending;
- list_add_tail(&q->list, &pending->list);
- sigaddset(&pending->signal, sig);
- complete_signal(sig, t, type);
+ posixtimer_queue_sigqueue(q, t, tmr->it_pid_type);
result = TRACE_SIGNAL_DELIVERED;
out:
- trace_signal_generate(sig, &q->info, t, type != PIDTYPE_PID, result);
+ trace_signal_generate(sig, &q->info, t, tmr->it_pid_type != PIDTYPE_PID, result);
unlock_task_sighand(t, &flags);
-ret:
- rcu_read_unlock();
return ret;
}
#endif /* CONFIG_POSIX_TIMERS */
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index f18d64c..0901ed9 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -307,7 +307,7 @@ int posix_timer_queue_signal(struct k_itimer *timr)
timr->it_status = state;
- ret = send_sigqueue(timr->sigq, timr->it_pid, timr->it_pid_type, timr->it_signal_seq);
+ ret = posixtimer_send_sigqueue(timr);
/* If we failed to send the signal the timer stops. */
return ret > 0;
}
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] posix-timers: Store PID type in the timer
2024-11-05 8:14 ` [patch V7 09/21] posix-timers: Store PID type in the timer Thomas Gleixner
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
0 siblings, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Thomas Gleixner, Frederic Weisbecker, Peter Zijlstra (Intel),
x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: ef1c5bcd6daa674392bdf89b8ae889aafd73f956
Gitweb: https://git.kernel.org/tip/ef1c5bcd6daa674392bdf89b8ae889aafd73f956
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:41 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:44 +01:00
posix-timers: Store PID type in the timer
instead of re-evaluating the signal delivery mode everywhere.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241105064213.519086500@linutronix.de
---
include/linux/posix-timers.h | 2 ++
kernel/time/posix-timers.c | 9 ++++++---
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 200098d..9471765 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -5,6 +5,7 @@
#include <linux/alarmtimer.h>
#include <linux/list.h>
#include <linux/mutex.h>
+#include <linux/pid.h>
#include <linux/posix-timers_types.h>
#include <linux/rcuref.h>
#include <linux/spinlock.h>
@@ -180,6 +181,7 @@ struct k_itimer {
s64 it_overrun_last;
unsigned int it_signal_seq;
int it_sigev_notify;
+ enum pid_type it_pid_type;
ktime_t it_interval;
struct signal_struct *it_signal;
union {
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index 53bd3c4..f18d64c 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -298,7 +298,6 @@ out:
int posix_timer_queue_signal(struct k_itimer *timr)
{
enum posix_timer_state state = POSIX_TIMER_DISARMED;
- enum pid_type type;
int ret;
lockdep_assert_held(&timr->it_lock);
@@ -308,8 +307,7 @@ int posix_timer_queue_signal(struct k_itimer *timr)
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, timr->it_signal_seq);
+ ret = send_sigqueue(timr->sigq, timr->it_pid, timr->it_pid_type, timr->it_signal_seq);
/* If we failed to send the signal the timer stops. */
return ret > 0;
}
@@ -496,6 +494,11 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
new_timer->it_pid = get_pid(task_tgid(current));
}
+ if (new_timer->it_sigev_notify & SIGEV_THREAD_ID)
+ new_timer->it_pid_type = PIDTYPE_PID;
+ else
+ new_timer->it_pid_type = PIDTYPE_TGID;
+
new_timer->sigq->info.si_tid = new_timer->it_id;
new_timer->sigq->info.si_code = SI_TIMER;
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] signal: Provide posixtimer_sigqueue_init()
2024-11-05 8:14 ` [patch V7 08/21] signal: Provide posixtimer_sigqueue_init() Thomas Gleixner
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
0 siblings, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Thomas Gleixner, Frederic Weisbecker, Peter Zijlstra (Intel),
x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 54f1dd642fd088ba969206f09e7afffad7d9db2c
Gitweb: https://git.kernel.org/tip/54f1dd642fd088ba969206f09e7afffad7d9db2c
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:39 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:44 +01:00
signal: Provide posixtimer_sigqueue_init()
To cure the SIG_IGN handling for posix interval timers, the preallocated
sigqueue needs to be embedded into struct k_itimer to prevent life time
races of all sorts.
Provide a new function to initialize the embedded sigqueue to prepare for
that.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241105064213.450427515@linutronix.de
---
include/linux/posix-timers.h | 2 ++
kernel/signal.c | 11 +++++++++++
2 files changed, 13 insertions(+)
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 9740fd0..200098d 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -12,6 +12,7 @@
struct kernel_siginfo;
struct task_struct;
+struct sigqueue;
struct k_itimer;
static inline clockid_t make_process_cpuclock(const unsigned int pid,
@@ -106,6 +107,7 @@ static inline void posix_cputimers_rt_watchdog(struct posix_cputimers *pct,
}
void posixtimer_rearm_itimer(struct task_struct *p);
+bool posixtimer_init_sigqueue(struct sigqueue *q);
bool posixtimer_deliver_signal(struct kernel_siginfo *info);
void posixtimer_free_timer(struct k_itimer *timer);
diff --git a/kernel/signal.c b/kernel/signal.c
index dbd4247..911ed3a 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1905,6 +1905,17 @@ void flush_itimer_signals(void)
__flush_itimer_signals(&tsk->signal->shared_pending);
}
+bool posixtimer_init_sigqueue(struct sigqueue *q)
+{
+ struct ucounts *ucounts = sig_get_ucounts(current, -1, 0);
+
+ if (!ucounts)
+ return false;
+ clear_siginfo(&q->info);
+ __sigqueue_init(q, ucounts, SIGQUEUE_PREALLOC);
+ return true;
+}
+
struct sigqueue *sigqueue_alloc(void)
{
return __sigqueue_alloc(-1, current, GFP_KERNEL, 0, SIGQUEUE_PREALLOC);
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] signal: Split up __sigqueue_alloc()
2024-11-05 8:14 ` [patch V7 07/21] signal: Split up __sigqueue_alloc() Thomas Gleixner
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
0 siblings, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Thomas Gleixner, Frederic Weisbecker, Peter Zijlstra (Intel),
x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 5cac427f7971b0619ebbfc131ef81fcf229c3c01
Gitweb: https://git.kernel.org/tip/5cac427f7971b0619ebbfc131ef81fcf229c3c01
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:38 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:44 +01:00
signal: Split up __sigqueue_alloc()
To cure the SIG_IGN handling for posix interval timers, the preallocated
sigqueue needs to be embedded into struct k_itimer to prevent life time
races of all sorts.
Reorganize __sigqueue_alloc() so the ucounts retrieval and the
initialization can be used independently.
No functional change.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241105064213.371410037@linutronix.de
---
kernel/signal.c | 52 ++++++++++++++++++++++++++++++++----------------
1 file changed, 35 insertions(+), 17 deletions(-)
diff --git a/kernel/signal.c b/kernel/signal.c
index ba7159b..dbd4247 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -396,16 +396,9 @@ void task_join_group_stop(struct task_struct *task)
task_set_jobctl_pending(task, mask | JOBCTL_STOP_PENDING);
}
-/*
- * allocate a new signal queue record
- * - this may be called without locks if and only if t == current, otherwise an
- * appropriate lock must be held to stop the target task from exiting
- */
-static struct sigqueue *
-__sigqueue_alloc(int sig, struct task_struct *t, gfp_t gfp_flags,
- int override_rlimit, const unsigned int sigqueue_flags)
+static struct ucounts *sig_get_ucounts(struct task_struct *t, int sig,
+ int override_rlimit)
{
- struct sigqueue *q = NULL;
struct ucounts *ucounts;
long sigpending;
@@ -424,19 +417,44 @@ __sigqueue_alloc(int sig, struct task_struct *t, gfp_t gfp_flags,
if (!sigpending)
return NULL;
- if (override_rlimit || likely(sigpending <= task_rlimit(t, RLIMIT_SIGPENDING))) {
- q = kmem_cache_alloc(sigqueue_cachep, gfp_flags);
- } else {
+ if (unlikely(!override_rlimit && sigpending > task_rlimit(t, RLIMIT_SIGPENDING))) {
+ dec_rlimit_put_ucounts(ucounts, UCOUNT_RLIMIT_SIGPENDING);
print_dropped_signal(sig);
+ return NULL;
}
- if (unlikely(q == NULL)) {
+ return ucounts;
+}
+
+static void __sigqueue_init(struct sigqueue *q, struct ucounts *ucounts,
+ const unsigned int sigqueue_flags)
+{
+ INIT_LIST_HEAD(&q->list);
+ q->flags = sigqueue_flags;
+ q->ucounts = ucounts;
+}
+
+/*
+ * allocate a new signal queue record
+ * - this may be called without locks if and only if t == current, otherwise an
+ * appropriate lock must be held to stop the target task from exiting
+ */
+static struct sigqueue *__sigqueue_alloc(int sig, struct task_struct *t, gfp_t gfp_flags,
+ int override_rlimit, const unsigned int sigqueue_flags)
+{
+ struct ucounts *ucounts = sig_get_ucounts(t, sig, override_rlimit);
+ struct sigqueue *q;
+
+ if (!ucounts)
+ return NULL;
+
+ q = kmem_cache_alloc(sigqueue_cachep, gfp_flags);
+ if (!q) {
dec_rlimit_put_ucounts(ucounts, UCOUNT_RLIMIT_SIGPENDING);
- } else {
- INIT_LIST_HEAD(&q->list);
- q->flags = sigqueue_flags;
- q->ucounts = ucounts;
+ return NULL;
}
+
+ __sigqueue_init(q, ucounts, sigqueue_flags);
return q;
}
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] posix-cpu-timers: Use dedicated flag for CPU timer nanosleep
2024-11-05 8:14 ` [patch V7 05/21] posix-cpu-timers: Use dedicated flag for CPU timer nanosleep Thomas Gleixner
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
0 siblings, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Thomas Gleixner, Frederic Weisbecker, Peter Zijlstra (Intel),
x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 4cf7bf2a2f1a8ace4a49a1138c8123fdb5990093
Gitweb: https://git.kernel.org/tip/4cf7bf2a2f1a8ace4a49a1138c8123fdb5990093
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:35 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:43 +01:00
posix-cpu-timers: Use dedicated flag for CPU timer nanosleep
POSIX CPU timer nanosleep creates a k_itimer on stack and uses the sigq
pointer to detect the nanosleep case in the expiry function.
Prepare for embedding sigqueue into struct k_itimer by using a dedicated
flag for nanosleep.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241105064213.238550394@linutronix.de
---
include/linux/posix-timers.h | 2 ++
kernel/time/posix-cpu-timers.c | 3 ++-
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index b1de217..bcd0120 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -42,6 +42,7 @@ static inline int clockid_to_fd(const clockid_t clk)
* @pid: Pointer to target task PID
* @elist: List head for the expiry list
* @firing: Timer is currently firing
+ * @nanosleep: Timer is used for nanosleep and is not a regular posix-timer
* @handling: Pointer to the task which handles expiry
*/
struct cpu_timer {
@@ -50,6 +51,7 @@ struct cpu_timer {
struct pid *pid;
struct list_head elist;
bool firing;
+ bool nanosleep;
struct task_struct __rcu *handling;
};
diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
index a282a3c..0c441d8 100644
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -596,7 +596,7 @@ static void cpu_timer_fire(struct k_itimer *timer)
timer->it_status = POSIX_TIMER_DISARMED;
- if (unlikely(timer->sigq == NULL)) {
+ if (unlikely(ctmr->nanosleep)) {
/*
* This a special case for clock_nanosleep,
* not a normal timer from sys_timer_create.
@@ -1493,6 +1493,7 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags,
timer.it_overrun = -1;
error = posix_cpu_timer_create(&timer);
timer.it_process = current;
+ timer.it.cpu.nanosleep = true;
if (!error) {
static struct itimerspec64 zero_it;
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] posix-timers: Add a refcount to struct k_itimer
2024-11-05 8:14 ` [patch V7 06/21] posix-timers: Add a refcount to struct k_itimer Thomas Gleixner
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
0 siblings, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Thomas Gleixner, Frederic Weisbecker, Peter Zijlstra (Intel),
x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 5d916a0988eed5217c103932ff4887c9ae83c89c
Gitweb: https://git.kernel.org/tip/5d916a0988eed5217c103932ff4887c9ae83c89c
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:36 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:43 +01:00
posix-timers: Add a refcount to struct k_itimer
To cure the SIG_IGN handling for posix interval timers, the preallocated
sigqueue needs to be embedded into struct k_itimer to prevent life time
races of all sorts.
To make that work correctly it needs reference counting so that timer
deletion does not free the timer prematuraly when there is a signal queued
or delivered concurrently.
Add a rcuref to the posix timer part.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241105064213.304756440@linutronix.de
---
include/linux/posix-timers.h | 14 ++++++++++++++
kernel/time/posix-timers.c | 7 ++++---
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index bcd0120..9740fd0 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -6,11 +6,13 @@
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/posix-timers_types.h>
+#include <linux/rcuref.h>
#include <linux/spinlock.h>
#include <linux/timerqueue.h>
struct kernel_siginfo;
struct task_struct;
+struct k_itimer;
static inline clockid_t make_process_cpuclock(const unsigned int pid,
const clockid_t clock)
@@ -105,6 +107,7 @@ static inline void posix_cputimers_rt_watchdog(struct posix_cputimers *pct,
void posixtimer_rearm_itimer(struct task_struct *p);
bool posixtimer_deliver_signal(struct kernel_siginfo *info);
+void posixtimer_free_timer(struct k_itimer *timer);
/* Init task static initializer */
#define INIT_CPU_TIMERBASE(b) { \
@@ -129,6 +132,7 @@ static inline void posix_cputimers_group_init(struct posix_cputimers *pct,
u64 cpu_limit) { }
static inline void posixtimer_rearm_itimer(struct task_struct *p) { }
static inline bool posixtimer_deliver_signal(struct kernel_siginfo *info) { return false; }
+static inline void posixtimer_free_timer(struct k_itimer *timer) { }
#endif
#ifdef CONFIG_POSIX_CPU_TIMERS_TASK_WORK
@@ -156,6 +160,7 @@ static inline void posix_cputimers_init_work(void) { }
* @it_signal: Pointer to the creators signal struct
* @it_pid: The pid of the process/task targeted by the signal
* @it_process: The task to wakeup on clock_nanosleep (CPU timers)
+ * @rcuref: Reference count for life time management
* @sigq: Pointer to preallocated sigqueue
* @it: Union representing the various posix timer type
* internals.
@@ -180,6 +185,7 @@ struct k_itimer {
struct task_struct *it_process;
};
struct sigqueue *sigq;
+ rcuref_t rcuref;
union {
struct {
struct hrtimer timer;
@@ -200,4 +206,12 @@ void set_process_cpu_timer(struct task_struct *task, unsigned int clock_idx,
int update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new);
+#ifdef CONFIG_POSIX_TIMERS
+static inline void posixtimer_putref(struct k_itimer *tmr)
+{
+ if (rcuref_put(&tmr->rcuref))
+ posixtimer_free_timer(tmr);
+}
+#endif /* !CONFIG_POSIX_TIMERS */
+
#endif
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index 66ed49e..53bd3c4 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -417,10 +417,11 @@ static struct k_itimer * alloc_posix_timer(void)
return NULL;
}
clear_siginfo(&tmr->sigq->info);
+ rcuref_init(&tmr->rcuref, 1);
return tmr;
}
-static void posix_timer_free(struct k_itimer *tmr)
+void posixtimer_free_timer(struct k_itimer *tmr)
{
put_pid(tmr->it_pid);
sigqueue_free(tmr->sigq);
@@ -432,7 +433,7 @@ static void posix_timer_unhash_and_free(struct k_itimer *tmr)
spin_lock(&hash_lock);
hlist_del_rcu(&tmr->t_hash);
spin_unlock(&hash_lock);
- posix_timer_free(tmr);
+ posixtimer_putref(tmr);
}
static int common_timer_create(struct k_itimer *new_timer)
@@ -467,7 +468,7 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
*/
new_timer_id = posix_timer_add(new_timer);
if (new_timer_id < 0) {
- posix_timer_free(new_timer);
+ posixtimer_free_timer(new_timer);
return new_timer_id;
}
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] posix-cpu-timers: Cleanup the firing logic
2024-11-05 8:14 ` [patch V7 04/21] posix-cpu-timers: Cleanup the firing logic Thomas Gleixner
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
0 siblings, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits; +Cc: Thomas Gleixner, Frederic Weisbecker, x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: bf635681c906ad056d1fda325de8d1c12c9f8201
Gitweb: https://git.kernel.org/tip/bf635681c906ad056d1fda325de8d1c12c9f8201
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:33 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:43 +01:00
posix-cpu-timers: Cleanup the firing logic
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 <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Link: https://lore.kernel.org/all/20241105064213.172848618@linutronix.de
---
include/linux/posix-timers.h | 2 +-
kernel/time/posix-cpu-timers.c | 34 +++++++++++++++++++++++----------
2 files changed, 25 insertions(+), 11 deletions(-)
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 8c6d974..b1de217 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -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;
};
diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
index 4305c00..a282a3c 100644
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -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);
}
@@ -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);
@@ -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);
@@ -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
@@ -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);
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] posix-timers: Make signal overrun accounting sensible
2024-11-05 8:14 ` [patch V7 03/21] posix-timers: Make signal overrun accounting sensible Thomas Gleixner
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
0 siblings, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Thomas Gleixner, Frederic Weisbecker, Peter Zijlstra (Intel),
x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: b06b0345fff3678517acd0f1837d52477ba30944
Gitweb: https://git.kernel.org/tip/b06b0345fff3678517acd0f1837d52477ba30944
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:32 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:43 +01:00
posix-timers: Make signal overrun accounting sensible
The handling of the timer overrun in the signal code is inconsistent as it
takes previous overruns into account. This is just wrong as after the
reprogramming of a timer the overrun count starts over from a clean state,
i.e. 0.
Don't touch info::si_overrun in send_sigqueue() and only store the overrun
value at signal delivery time, which is computed from the timer itself
relative to the expiry time.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241105064213.106738193@linutronix.de
---
kernel/signal.c | 6 ------
kernel/time/posix-timers.c | 11 ++++++-----
2 files changed, 6 insertions(+), 11 deletions(-)
diff --git a/kernel/signal.c b/kernel/signal.c
index 68e6bc7..ba7159b 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1968,15 +1968,9 @@ int send_sigqueue(struct sigqueue *q, struct pid *pid, enum pid_type type, int s
ret = 0;
if (unlikely(!list_empty(&q->list))) {
- /*
- * If an SI_TIMER entry is already queue just increment
- * the overrun count.
- */
- q->info.si_overrun++;
result = TRACE_SIGNAL_ALREADY_PENDING;
goto out;
}
- q->info.si_overrun = 0;
signalfd_notify(t, sig);
pending = (type != PIDTYPE_PID) ? &t->signal->shared_pending : &t->pending;
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index b380e25..66ed49e 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -233,11 +233,12 @@ __initcall(init_posix_timers);
* The siginfo si_overrun field and the return value of timer_getoverrun(2)
* are of type int. Clamp the overrun value to INT_MAX
*/
-static inline int timer_overrun_to_int(struct k_itimer *timr, int baseval)
+static inline int timer_overrun_to_int(struct k_itimer *timr)
{
- s64 sum = timr->it_overrun_last + (s64)baseval;
+ if (timr->it_overrun_last > (s64)INT_MAX)
+ return INT_MAX;
- return sum > (s64)INT_MAX ? INT_MAX : (int)sum;
+ return (int)timr->it_overrun_last;
}
static void common_hrtimer_rearm(struct k_itimer *timr)
@@ -280,7 +281,7 @@ bool posixtimer_deliver_signal(struct kernel_siginfo *info)
timr->it_overrun = -1LL;
++timr->it_signal_seq;
- info->si_overrun = timer_overrun_to_int(timr, info->si_overrun);
+ info->si_overrun = timer_overrun_to_int(timr);
}
ret = true;
@@ -774,7 +775,7 @@ SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id)
if (!timr)
return -EINVAL;
- overrun = timer_overrun_to_int(timr, 0);
+ overrun = timer_overrun_to_int(timr);
unlock_timer(timr, flags);
return overrun;
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] posix-cpu-timers: Correctly update timer status in posix_cpu_timer_del()
2024-11-05 8:14 ` [patch V7 01/21] posix-cpu-timers: Correctly update timer status in posix_cpu_timer_del() Thomas Gleixner
2024-11-05 12:02 ` Frederic Weisbecker
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
1 sibling, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits; +Cc: Frederic Weisbecker, Thomas Gleixner, x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 15cbfb92efee5c7f09e531a331e19759dbe0ac3c
Gitweb: https://git.kernel.org/tip/15cbfb92efee5c7f09e531a331e19759dbe0ac3c
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:29 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:43 +01:00
posix-cpu-timers: Correctly update timer status in posix_cpu_timer_del()
If posix_cpu_timer_del() exits early due to task not found or sighand
invalid, it fails to clear the state of the timer. That's harmless but
inconsistent.
These early exits are accounted as successful delete. Move the update of
the timer state into the success return path, so all "successful" deletions
are handled.
Reported-by: Frederic Weisbecker <frederic@kernel.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Link: https://lore.kernel.org/all/20241105064212.974053438@linutronix.de
---
kernel/time/posix-cpu-timers.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
index 12f828d..5f444e3 100644
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -493,20 +493,20 @@ 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);
}
out:
rcu_read_unlock();
- if (!ret)
- put_pid(ctmr->pid);
+ if (!ret) {
+ put_pid(ctmr->pid);
+ timer->it_status = POSIX_TIMER_DISARMED;
+ }
return ret;
}
^ permalink raw reply [flat|nested] 50+ messages in thread
* [tip: timers/core] posix-timers: Make signal delivery consistent
2024-11-05 8:14 ` [patch V7 02/21] posix-timers: Make signal delivery consistent Thomas Gleixner
@ 2024-11-07 1:31 ` tip-bot2 for Thomas Gleixner
0 siblings, 0 replies; 50+ messages in thread
From: tip-bot2 for Thomas Gleixner @ 2024-11-07 1:31 UTC (permalink / raw)
To: linux-tip-commits; +Cc: Thomas Gleixner, Frederic Weisbecker, x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 513793bc6ab331b947111e8efaf8fcef33fb83e5
Gitweb: https://git.kernel.org/tip/513793bc6ab331b947111e8efaf8fcef33fb83e5
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Tue, 05 Nov 2024 09:14:31 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Thu, 07 Nov 2024 02:14:43 +01:00
posix-timers: Make signal delivery consistent
Signals of timers which are reprogammed, disarmed or deleted can deliver
signals related to the past. The POSIX spec is blury about this:
- "The effect of disarming or resetting a timer with pending expiration
notifications is unspecified."
- "The disposition of pending signals for the deleted timer is
unspecified."
In both cases it is reasonable to expect that pending signals are
discarded. Especially in the reprogramming case it does not make sense to
account for previous overruns or to deliver a signal for a timer which has
been disarmed. This makes the behaviour consistent and understandable.
Remove the si_sys_private check from the signal delivery code and invoke
posix_timer_deliver_signal() unconditionally for posix timer related
signals.
Change posix_timer_deliver_signal() so it controls the actual signal
delivery via the return value. It now instructs the signal code to drop the
signal when:
1) The timer does not longer exist in the hash table
2) The timer signal_seq value is not the same as the si_sys_private value
which was set when the signal was queued.
This is also a preparatory change to embed the sigqueue into the k_itimer
structure, which in turn allows to remove the si_sys_private magic.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Link: https://lore.kernel.org/all/20241105064213.040348644@linutronix.de
---
include/linux/posix-timers.h | 2 --
kernel/signal.c | 6 ++----
kernel/time/posix-cpu-timers.c | 2 +-
kernel/time/posix-timers.c | 28 ++++++++++++++++------------
4 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 02afbb4..8c6d974 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -137,8 +137,6 @@ static inline void clear_posix_cputimers_work(struct task_struct *p) { }
static inline void posix_cputimers_init_work(void) { }
#endif
-#define REQUEUE_PENDING 1
-
/**
* struct k_itimer - POSIX.1b interval timer structure.
* @list: List head for binding the timer to signals->posix_timers
diff --git a/kernel/signal.c b/kernel/signal.c
index df34aa4..68e6bc7 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -550,10 +550,8 @@ still_pending:
list_del_init(&first->list);
copy_siginfo(info, &first->info);
- *resched_timer =
- (first->flags & SIGQUEUE_PREALLOC) &&
- (info->si_code == SI_TIMER) &&
- (info->si_sys_private);
+ *resched_timer = (first->flags & SIGQUEUE_PREALLOC) &&
+ (info->si_code == SI_TIMER);
__sigqueue_free(first);
} else {
diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
index 5f444e3..4305c00 100644
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -746,7 +746,7 @@ static void __posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *i
* - Timers which expired, but the signal has not yet been
* delivered
*/
- if (iv && ((timer->it_signal_seq & REQUEUE_PENDING) || sigev_none))
+ if (iv && timer->it_status != POSIX_TIMER_ARMED)
expires = bump_cpu_timer(timer, now);
else
expires = cpu_timer_getexpires(&timer->it.cpu);
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index dd72b8e..b380e25 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -269,7 +269,10 @@ bool posixtimer_deliver_signal(struct kernel_siginfo *info)
if (!timr)
goto out;
- if (timr->it_interval && timr->it_signal_seq == info->si_sys_private) {
+ if (timr->it_signal_seq != info->si_sys_private)
+ goto out_unlock;
+
+ if (timr->it_interval && !WARN_ON_ONCE(timr->it_status != POSIX_TIMER_REQUEUE_PENDING)) {
timr->kclock->timer_rearm(timr);
timr->it_status = POSIX_TIMER_ARMED;
@@ -281,6 +284,7 @@ bool posixtimer_deliver_signal(struct kernel_siginfo *info)
}
ret = true;
+out_unlock:
unlock_timer(timr, flags);
out:
spin_lock(¤t->sighand->siglock);
@@ -293,19 +297,18 @@ out:
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;
+ int ret;
lockdep_assert_held(&timr->it_lock);
- 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);
+ ret = send_sigqueue(timr->sigq, timr->it_pid, type, timr->it_signal_seq);
/* If we failed to send the signal the timer stops. */
return ret > 0;
}
@@ -663,7 +666,7 @@ void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting)
* is a SIGEV_NONE timer move the expiry time forward by intervals,
* so expiry is > now.
*/
- if (iv && (timr->it_signal_seq & REQUEUE_PENDING || sig_none))
+ if (iv && timr->it_status != POSIX_TIMER_ARMED)
timr->it_overrun += kc->timer_forward(timr, now);
remaining = kc->timer_remaining(timr, now);
@@ -863,8 +866,6 @@ void posix_timer_set_common(struct k_itimer *timer, struct itimerspec64 *new_set
else
timer->it_interval = 0;
- /* Prevent reloading in case there is a signal pending */
- timer->it_signal_seq = (timer->it_signal_seq + 2) & ~REQUEUE_PENDING;
/* Reset overrun accounting */
timer->it_overrun_last = 0;
timer->it_overrun = -1LL;
@@ -882,8 +883,6 @@ int common_timer_set(struct k_itimer *timr, int flags,
if (old_setting)
common_timer_get(timr, old_setting);
- /* Prevent rearming by clearing the interval */
- timr->it_interval = 0;
/*
* Careful here. On SMP systems the timer expiry function could be
* active and spinning on timr->it_lock.
@@ -933,6 +932,9 @@ retry:
if (old_spec64)
old_spec64->it_interval = ktime_to_timespec64(timr->it_interval);
+ /* Prevent signal delivery and rearming. */
+ timr->it_signal_seq++;
+
kc = timr->kclock;
if (WARN_ON_ONCE(!kc || !kc->timer_set))
error = -EINVAL;
@@ -1001,7 +1003,6 @@ int common_timer_del(struct k_itimer *timer)
{
const struct k_clock *kc = timer->kclock;
- timer->it_interval = 0;
if (kc->timer_try_to_cancel(timer) < 0)
return TIMER_RETRY;
timer->it_status = POSIX_TIMER_DISARMED;
@@ -1012,6 +1013,9 @@ static inline int timer_delete_hook(struct k_itimer *timer)
{
const struct k_clock *kc = timer->kclock;
+ /* Prevent signal delivery and rearming. */
+ timer->it_signal_seq++;
+
if (WARN_ON_ONCE(!kc || !kc->timer_del))
return -EINVAL;
return kc->timer_del(timer);
^ permalink raw reply [flat|nested] 50+ messages in thread
end of thread, other threads:[~2024-11-07 1:31 UTC | newest]
Thread overview: 50+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-11-05 8:14 [patch V7 00/21] posix-timers: Cure the SIG_IGN mess Thomas Gleixner
2024-11-05 8:14 ` [patch V7 01/21] posix-cpu-timers: Correctly update timer status in posix_cpu_timer_del() Thomas Gleixner
2024-11-05 12:02 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 02/21] posix-timers: Make signal delivery consistent Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 03/21] posix-timers: Make signal overrun accounting sensible Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 04/21] posix-cpu-timers: Cleanup the firing logic Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 05/21] posix-cpu-timers: Use dedicated flag for CPU timer nanosleep Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 06/21] posix-timers: Add a refcount to struct k_itimer Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 07/21] signal: Split up __sigqueue_alloc() Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 08/21] signal: Provide posixtimer_sigqueue_init() Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 09/21] posix-timers: Store PID type in the timer Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 10/21] signal: Refactor send_sigqueue() Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 11/21] signal: Replace resched_timer logic Thomas Gleixner
2024-11-05 12:08 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 12/21] posix-timers: Embed sigqueue in struct k_itimer Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 13/21] signal: Cleanup unused posix-timer leftovers Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 14/21] posix-timers: Move sequence logic into struct k_itimer Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 15/21] signal: Provide ignored_posix_timers list Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 16/21] posix-timers: Handle ignored list on delete and exit Thomas Gleixner
2024-11-05 13:08 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 17/21] signal: Handle ignored signals in do_sigaction(action != SIG_IGN) Thomas Gleixner
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 18/21] signal: Queue ignored posixtimers on ignore list Thomas Gleixner
2024-11-05 14:02 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 19/21] posix-timers: Cleanup SIG_IGN workaround leftovers Thomas Gleixner
2024-11-05 14:26 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 20/21] alarmtimers: Remove the throttle mechanism from alarm_forward_now() Thomas Gleixner
2024-11-05 14:29 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2024-11-05 8:14 ` [patch V7 21/21] alarmtimers: Remove return value from alarm functions Thomas Gleixner
2024-11-05 14:34 ` Frederic Weisbecker
2024-11-07 1:31 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®