Skip to contentSkip to conversation

posix-timers: Fixes and cleanups

PATCH Applied Thomas Gleixner 8 participants 122 messages Apr 25, 2023 – Jun 18, 2023 Posix clocks and timers

lore
Share & tools
lore
#0 lore raw

cover posix-timers: Fixes and cleanups

Hi!

A recent syzcaller/KCSAN report about a data race and an actually broken loop termination due to that race made me look deeper into the posix timer code.

Aside of the loop termination issue this unearthed another issue in the exit() path where timer deletion might livelock on RT enabled kernels.

While going through the code with a fine comb, I stumbled over another place which needs READ/WRITE_ONCE() annotations, tons of outdated and/or uncomprehensible comments and a bunch of silly code.

The series has therefore a larger set of cleanups on top of the two fixes.

It is based on

git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers/core

and also availble from git:

git://git.kernel.org/pub/scm/linux/kernel/git/tglx/devel.git timers/posix

Thanks,

tglx

---
 include/linux/sched/signal.h |    2 
 kernel/time/posix-timers.c   |  523 ++++++++++++++++++++++++++-----------------
 2 files changed, 321 insertions(+), 204 deletions(-)
#1 lore raw

PATCH 1/20 posix-timers: Prevent RT livelock in itimer_delete()

itimer_delete() has a retry loop when the timer is concurrently expired. On non-RT kernels this just spin-waits until the timer callback has completed. On RT kernels this is a potential livelock when the exiting task preempted the hrtimer soft interrupt.

This only affects hrtimer based timers as Posix CPU timers cannot be concurrently expired. For CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y this is obviously impossible as the task cannot run task work and exit at the same time. The CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n (only non-RT) is prevented because interrupts are disabled.

Replace spin_unlock() with an invocation of timer_wait_running() to handle it the same way as the other retry loops in the posix timer code.

Fixes ec8f954a40da ("posix-timers: Use a callback for cancel synchronization on PREEMPT_RT")
Cc Sebastian Siewior <[email protected]>
---
 kernel/time/posix-timers.c |   50 +++++++++++++++++++++++++++++++++++++--------
 1 file changed, 42 insertions(+), 8 deletions(-)
kernel/time/posix-timers.c +42 -8
@@ -1037,27 +1037,59 @@ SYSCALL_DEFINE1(timer_delete, timer_t, t
 }
 
 /*
- * return timer owned by the process, used by exit_itimers
+ * Delete a timer if it is armed, remove it from the hash and schedule it
+ * for RCU freeing.
  */
 static void itimer_delete(struct k_itimer *timer)
 {
-retry_delete:
-	spin_lock_irq(&timer->it_lock);
+	unsigned long flags;
 
+retry_delete:
+	/*
+	 * irqsave is required to make timer_wait_running() work.
+	 */
+	spin_lock_irqsave(&timer->it_lock, flags);
+
+	/*
+	 * Even if the timer is not longer accessible from other tasks
+	 * it still might be armed and queued in the underlying timer
+	 * mechanism. Worse, that timer mechanism might run the expiry
+	 * function concurrently.
+	 */
 	if (timer_delete_hook(timer) == TIMER_RETRY) {
-		spin_unlock_irq(&timer->it_lock);
+		/*
+		 * Timer is expired concurrently, prevent livelocks
+		 * and pointless spinning on RT.
+		 *
+		 * The CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y case is
+		 * irrelevant here because obviously the exiting task
+		 * cannot be expiring timer in task work concurrently.
+		 * Ditto for CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n as the
+		 * tick interrupt cannot run on this CPU because the above
+		 * spin_lock disabled interrupts.
+		 *
+		 * timer_wait_running() drops timer::it_lock, which opens
+		 * the possibility for another task to delete the timer.
+		 *
+		 * That's not possible here because this is invoked from
+		 * do_exit() only for the last thread of the thread group.
+		 * So no other task can access that timer.
+		 */
+		if (WARN_ON_ONCE(timer_wait_running(timer, &flags) != timer))
+			return;
+
 		goto retry_delete;
 	}
 	list_del(&timer->list);
 
-	spin_unlock_irq(&timer->it_lock);
+	spin_unlock_irqrestore(&timer->it_lock, flags);
 	release_posix_timer(timer, IT_ID_SET);
 }
 
 /*
- * This is called by do_exit or de_thread, only when nobody else can
- * modify the signal->posix_timers list. Yet we need sighand->siglock
- * to prevent the race with /proc/pid/timers.
+ * Invoked from do_exit() when the last thread of a thread group exits.
+ * At that point no other task can access the timers of the dying
+ * task anymore.
  */
 void exit_itimers(struct task_struct *tsk)
 {
@@ -1067,10 +1099,12 @@ void exit_itimers(struct task_struct *ts
 	if (list_empty(&tsk->signal->posix_timers))
 		return;
 
+	/* Protect against concurrent read via /proc/$PID/timers */
 	spin_lock_irq(&tsk->sighand->siglock);
 	list_replace_init(&tsk->signal->posix_timers, &timers);
 	spin_unlock_irq(&tsk->sighand->siglock);
 
+	/* The timers are not longer accessible via tsk::signal */
 	while (!list_empty(&timers)) {
 		tmr = list_first_entry(&timers, struct k_itimer, list);
 		itimer_delete(tmr);
#2 lore raw

Re: [patch 01/20] posix-timers: Prevent RT livelock in itimer_delete()

Quoted 10 lines · Thomas Gleixner

itimer_delete() has a retry loop when the timer is concurrently expired. On non-RT kernels this just spin-waits until the timer callback has completed. On RT kernels this is a potential livelock when the exiting task preempted the hrtimer soft interrupt.

This only affects hrtimer based timers as Posix CPU timers cannot be concurrently expired. For CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y this is obviously impossible as the task cannot run task work and exit at the same time. The CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n (only non-RT) is prevented because interrupts are disabled.

But the owner of the timer is not the same as the target of the timer, right?

Though I seem to remember that we forbid setting a timer to a target outside the current process, in which case the owner and the target are the same at this exit stage. But I can't remember what enforces that permission in pid_for_clock()...

Thanks.

#3 lore raw

Re: [patch 01/20] posix-timers: Prevent RT livelock in itimer_delete()

Quoted 12 lines · Frederic Weisbecker
Quoted 10 lines · Thomas Gleixner

itimer_delete() has a retry loop when the timer is concurrently expired. On non-RT kernels this just spin-waits until the timer callback has completed. On RT kernels this is a potential livelock when the exiting task preempted the hrtimer soft interrupt.

This only affects hrtimer based timers as Posix CPU timers cannot be concurrently expired. For CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y this is obviously impossible as the task cannot run task work and exit at the same time. The CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n (only non-RT) is prevented because interrupts are disabled.

Quoted 6 lines

But the owner of the timer is not the same as the target of the timer, right?

Though I seem to remember that we forbid setting a timer to a target outside the current process, in which case the owner and the target are the same at this exit stage. But I can't remember what enforces that permission in pid_for_clock()..

The owner of the timer is always the one which needs to find the entity to synchronize on, whether that's the right hrtimer base or the task which runs the expiry code.

wait_for_running_timer() is taking that into account:

- The hrtimer timer based posix timers lock the hrtimer base expiry lock on the base to which the timer is currently associated

- Posix CPU timers can be armed on a differnet process (only per thread timers are restricted to currents threadgroup) but the wait_for_running() callback "knows" how to find that process:

When the timer is moved to the expiry list it gets:

cputimer->firing = 1; rcu_assign_pointer(ctmr->handling, current);

and the wait for running side does:

       rcu_read_lock()
       tsk = rcu_dereference(timr->it.cpu.handling);
       ....
       mutex_lock(&tsk->posix_cputimers_work.mutex);

See collect_timerqueue(), handle_posix_cpu_timers() and posix_cpu_timer_wait_running() for details.

commit f7abf14f0001 ("posix-cpu-timers: Implement the missing timer_wait_running callback") has quite some prose in the changelog.

Thanks,

tglx

#4 lore raw

Re: [patch 01/20] posix-timers: Prevent RT livelock in itimer_delete()

Quoted 45 lines · Thomas Gleixner
Quoted 12 lines · Frederic Weisbecker
Quoted 10 lines · Thomas Gleixner

itimer_delete() has a retry loop when the timer is concurrently expired. On non-RT kernels this just spin-waits until the timer callback has completed. On RT kernels this is a potential livelock when the exiting task preempted the hrtimer soft interrupt.

This only affects hrtimer based timers as Posix CPU timers cannot be concurrently expired. For CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y this is obviously impossible as the task cannot run task work and exit at the same time. The CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n (only non-RT) is prevented because interrupts are disabled.

Quoted 6 lines

But the owner of the timer is not the same as the target of the timer, right?

Though I seem to remember that we forbid setting a timer to a target outside the current process, in which case the owner and the target are the same at this exit stage. But I can't remember what enforces that permission in pid_for_clock()..

The owner of the timer is always the one which needs to find the entity to synchronize on, whether that's the right hrtimer base or the task which runs the expiry code.

wait_for_running_timer() is taking that into account:

- The hrtimer timer based posix timers lock the hrtimer base expiry lock on the base to which the timer is currently associated

- Posix CPU timers can be armed on a differnet process (only per thread timers are restricted to currents threadgroup) but the wait_for_running() callback "knows" how to find that process:

When the timer is moved to the expiry list it gets:

cputimer->firing = 1; rcu_assign_pointer(ctmr->handling, current);

and the wait for running side does:

       rcu_read_lock()
       tsk = rcu_dereference(timr->it.cpu.handling);
       ....
       mutex_lock(&tsk->posix_cputimers_work.mutex);
Quoted 5 lines

See collect_timerqueue(), handle_posix_cpu_timers() and posix_cpu_timer_wait_running() for details.

commit f7abf14f0001 ("posix-cpu-timers: Implement the missing timer_wait_running callback") has quite some prose in the changelog.

But you have a point. The comment I added in itimer_delete() vs. CPU timers is wrong for timers which are armed on a different process. Needs to be removed.

Thanks,

tglx

#5 lore raw

PATCH v2 1/20 posix-timers: Prevent RT livelock in itimer_delete()

itimer_delete() has a retry loop when the timer is concurrently expired. On non-RT kernels this just spin-waits until the timer callback has completed. On RT kernels this is a potential livelock when the exiting task preempted the hrtimer soft interrupt.

Replace spin_unlock() with an invocation of timer_wait_running() to handle it the same way as the other retry loops in the posix timer code.

Fixes ec8f954a40da ("posix-timers: Use a callback for cancel synchronization on PREEMPT_RT")

---
V2: Remove the bogus claims about posix CPU timers - Frederic

---
 kernel/time/posix-timers.c |   50 +++++++++++++++++++++++++++++++++++++--------
 1 file changed, 42 insertions(+), 8 deletions(-)
kernel/time/posix-timers.c +42 -8
@@ -1037,27 +1037,59 @@ SYSCALL_DEFINE1(timer_delete, timer_t, t
 }
 
 /*
- * return timer owned by the process, used by exit_itimers
+ * Delete a timer if it is armed, remove it from the hash and schedule it
+ * for RCU freeing.
  */
 static void itimer_delete(struct k_itimer *timer)
 {
-retry_delete:
-	spin_lock_irq(&timer->it_lock);
+	unsigned long flags;
 
+retry_delete:
+	/*
+	 * irqsave is required to make timer_wait_running() work.
+	 */
+	spin_lock_irqsave(&timer->it_lock, flags);
+
+	/*
+	 * Even if the timer is not longer accessible from other tasks
+	 * it still might be armed and queued in the underlying timer
+	 * mechanism. Worse, that timer mechanism might run the expiry
+	 * function concurrently.
+	 */
 	if (timer_delete_hook(timer) == TIMER_RETRY) {
-		spin_unlock_irq(&timer->it_lock);
+		/*
+		 * Timer is expired concurrently, prevent livelocks
+		 * and pointless spinning on RT.
+		 *
+		 * The CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y case is
+		 * irrelevant here because obviously the exiting task
+		 * cannot be expiring timer in task work concurrently.
+		 * Ditto for CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n as the
+		 * tick interrupt cannot run on this CPU because the above
+		 * spin_lock disabled interrupts.
+		 *
+		 * timer_wait_running() drops timer::it_lock, which opens
+		 * the possibility for another task to delete the timer.
+		 *
+		 * That's not possible here because this is invoked from
+		 * do_exit() only for the last thread of the thread group.
+		 * So no other task can access that timer.
+		 */
+		if (WARN_ON_ONCE(timer_wait_running(timer, &flags) != timer))
+			return;
+
 		goto retry_delete;
 	}
 	list_del(&timer->list);
 
-	spin_unlock_irq(&timer->it_lock);
+	spin_unlock_irqrestore(&timer->it_lock, flags);
 	release_posix_timer(timer, IT_ID_SET);
 }
 
 /*
- * This is called by do_exit or de_thread, only when nobody else can
- * modify the signal->posix_timers list. Yet we need sighand->siglock
- * to prevent the race with /proc/pid/timers.
+ * Invoked from do_exit() when the last thread of a thread group exits.
+ * At that point no other task can access the timers of the dying
+ * task anymore.
  */
 void exit_itimers(struct task_struct *tsk)
 {
@@ -1067,10 +1099,12 @@ void exit_itimers(struct task_struct *ts
 	if (list_empty(&tsk->signal->posix_timers))
 		return;
 
+	/* Protect against concurrent read via /proc/$PID/timers */
 	spin_lock_irq(&tsk->sighand->siglock);
 	list_replace_init(&tsk->signal->posix_timers, &timers);
 	spin_unlock_irq(&tsk->sighand->siglock);
 
+	/* The timers are not longer accessible via tsk::signal */
 	while (!list_empty(&timers)) {
 		tmr = list_first_entry(&timers, struct k_itimer, list);
 		itimer_delete(tmr);
#6 lore raw

PATCH 1/20 posix-timers: Prevent RT livelock in itimer_delete()

itimer_delete() has a retry loop when the timer is concurrently expired. On non-RT kernels this just spin-waits until the timer callback has completed. On RT kernels this is a potential livelock when the exiting task preempted the hrtimer soft interrupt.

Replace spin_unlock() with an invocation of timer_wait_running() to handle it the same way as the other retry loops in the posix timer code.

Fixes ec8f954a40da ("posix-timers: Use a callback for cancel synchronization on PREEMPT_RT")

---

V2:  Remove bogus comments vs. posix CPU timers - Frederic
V2a: Send the real fixed up version
---
 kernel/time/posix-timers.c |   43 +++++++++++++++++++++++++++++++++++--------
 1 file changed, 35 insertions(+), 8 deletions(-)
kernel/time/posix-timers.c +35 -8
@@ -1037,27 +1037,52 @@ SYSCALL_DEFINE1(timer_delete, timer_t, t
 }
 
 /*
- * return timer owned by the process, used by exit_itimers
+ * Delete a timer if it is armed, remove it from the hash and schedule it
+ * for RCU freeing.
  */
 static void itimer_delete(struct k_itimer *timer)
 {
-retry_delete:
-	spin_lock_irq(&timer->it_lock);
+	unsigned long flags;
 
+retry_delete:
+	/*
+	 * irqsave is required to make timer_wait_running() work.
+	 */
+	spin_lock_irqsave(&timer->it_lock, flags);
+
+	/*
+	 * Even if the timer is not longer accessible from other tasks
+	 * it still might be armed and queued in the underlying timer
+	 * mechanism. Worse, that timer mechanism might run the expiry
+	 * function concurrently.
+	 */
 	if (timer_delete_hook(timer) == TIMER_RETRY) {
-		spin_unlock_irq(&timer->it_lock);
+		/*
+		 * Timer is expired concurrently, prevent livelocks
+		 * and pointless spinning on RT.
+		 *
+		 * timer_wait_running() drops timer::it_lock, which opens
+		 * the possibility for another task to delete the timer.
+		 *
+		 * That's not possible here because this is invoked from
+		 * do_exit() only for the last thread of the thread group.
+		 * So no other task can access and delete that timer.
+		 */
+		if (WARN_ON_ONCE(timer_wait_running(timer, &flags) != timer))
+			return;
+
 		goto retry_delete;
 	}
 	list_del(&timer->list);
 
-	spin_unlock_irq(&timer->it_lock);
+	spin_unlock_irqrestore(&timer->it_lock, flags);
 	release_posix_timer(timer, IT_ID_SET);
 }
 
 /*
- * This is called by do_exit or de_thread, only when nobody else can
- * modify the signal->posix_timers list. Yet we need sighand->siglock
- * to prevent the race with /proc/pid/timers.
+ * Invoked from do_exit() when the last thread of a thread group exits.
+ * At that point no other task can access the timers of the dying
+ * task anymore.
  */
 void exit_itimers(struct task_struct *tsk)
 {
@@ -1067,10 +1092,12 @@ void exit_itimers(struct task_struct *ts
 	if (list_empty(&tsk->signal->posix_timers))
 		return;
 
+	/* Protect against concurrent read via /proc/$PID/timers */
 	spin_lock_irq(&tsk->sighand->siglock);
 	list_replace_init(&tsk->signal->posix_timers, &timers);
 	spin_unlock_irq(&tsk->sighand->siglock);
 
+	/* The timers are not longer accessible via tsk::signal */
 	while (!list_empty(&timers)) {
 		tmr = list_first_entry(&timers, struct k_itimer, list);
 		itimer_delete(tmr);
maintainer Reviewed-by on v1 1/20
#7 lore raw

Re: [patch v2a 01/20] posix-timers: Prevent RT livelock in itimer_delete()

Quoted 4 lines · Thomas Gleixner

itimer_delete() has a retry loop when the timer is concurrently expired. On non-RT kernels this just spin-waits until the timer callback has completed. On RT kernels this is a potential livelock when the exiting task preempted the hrtimer soft interrupt.

It's not just RT but also archs supporting HAVE_POSIX_CPU_TIMERS_TASK_WORK

Quoted 36 lines

Replace spin_unlock() with an invocation of timer_wait_running() to handle it the same way as the other retry loops in the posix timer code.

Fixes ec8f954a40da ("posix-timers: Use a callback for cancel synchronization on PREEMPT_RT")

---

V2:  Remove bogus comments vs. posix CPU timers - Frederic
V2a: Send the real fixed up version
---
 kernel/time/posix-timers.c |   43 +++++++++++++++++++++++++++++++++++--------
 1 file changed, 35 insertions(+), 8 deletions(-)
kernel/time/posix-timers.c +10 -3
@@ -1037,27 +1037,52 @@ SYSCALL_DEFINE1(timer_delete, timer_t, t
 }
 
 /*
- * return timer owned by the process, used by exit_itimers
+ * Delete a timer if it is armed, remove it from the hash and schedule it
+ * for RCU freeing.
  */
 static void itimer_delete(struct k_itimer *timer)
 {
-retry_delete:
-	spin_lock_irq(&timer->it_lock);
+	unsigned long flags;
 
+retry_delete:
+	/*
+	 * irqsave is required to make timer_wait_running() work.
+	 */
+	spin_lock_irqsave(&timer->it_lock, flags);
+
+	/*

kernel/time/posix-timers.c SYSCALL_DEFINE1(timer_delete, timer_t, t · line 1062

Quoted 10 lines
kernel/time/posix-timers.c +8 -1
+	 * Even if the timer is not longer accessible from other tasks
+	 * it still might be armed and queued in the underlying timer
+	 * mechanism. Worse, that timer mechanism might run the expiry
+	 * function concurrently.
+	 */
 	if (timer_delete_hook(timer) == TIMER_RETRY) {
-		spin_unlock_irq(&timer->it_lock);
+		/*
+		 * Timer is expired concurrently, prevent livelocks
+		 * and pointless spinning on RT.

Ditto.

Thanks.

#8 lore raw

[tip: timers/core] posix-timers: Prevent RT livelock in itimer_delete()

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     1b59b2577582f9cf3d0f17245675a76859175cc1
Gitweb:        https://git.kernel.org/tip/1b59b2577582f9cf3d0f17245675a76859175cc1
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Thu, 01 Jun 2023 22:16:34 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:36 +02:00

posix-timers: Prevent RT livelock in itimer_delete()

itimer_delete() has a retry loop when the timer is concurrently expired. On non-RT kernels this just spin-waits until the timer callback has completed, except for posix CPU timers which have HAVE_POSIX_CPU_TIMERS_TASK_WORK enabled.

In that case and on RT kernels the existing task could live lock when preempting the task which does the timer delivery.

Replace spin_unlock() with an invocation of timer_wait_running() to handle it the same way as the other retry loops in the posix timer code.

Fixes ec8f954a40da ("posix-timers: Use a callback for cancel synchronization on PREEMPT_RT")
---
 kernel/time/posix-timers.c | 41 ++++++++++++++++++++++++++++++-------
 1 file changed, 34 insertions(+), 7 deletions(-)
kernel/time/posix-timers.c +34 -7
index 808a247..2d835c2 100644
@@ -1037,27 +1037,52 @@ retry_delete:
 }
 
 /*
- * return timer owned by the process, used by exit_itimers
+ * Delete a timer if it is armed, remove it from the hash and schedule it
+ * for RCU freeing.
  */
 static void itimer_delete(struct k_itimer *timer)
 {
+	unsigned long flags;
+
 retry_delete:
-	spin_lock_irq(&timer->it_lock);
+	/*
+	 * irqsave is required to make timer_wait_running() work.
+	 */
+	spin_lock_irqsave(&timer->it_lock, flags);
 
+	/*
+	 * Even if the timer is not longer accessible from other tasks
+	 * it still might be armed and queued in the underlying timer
+	 * mechanism. Worse, that timer mechanism might run the expiry
+	 * function concurrently.
+	 */
 	if (timer_delete_hook(timer) == TIMER_RETRY) {
-		spin_unlock_irq(&timer->it_lock);
+		/*
+		 * Timer is expired concurrently, prevent livelocks
+		 * and pointless spinning on RT.
+		 *
+		 * timer_wait_running() drops timer::it_lock, which opens
+		 * the possibility for another task to delete the timer.
+		 *
+		 * That's not possible here because this is invoked from
+		 * do_exit() only for the last thread of the thread group.
+		 * So no other task can access and delete that timer.
+		 */
+		if (WARN_ON_ONCE(timer_wait_running(timer, &flags) != timer))
+			return;
+
 		goto retry_delete;
 	}
 	list_del(&timer->list);
 
-	spin_unlock_irq(&timer->it_lock);
+	spin_unlock_irqrestore(&timer->it_lock, flags);
 	release_posix_timer(timer, IT_ID_SET);
 }
 
 /*
- * This is called by do_exit or de_thread, only when nobody else can
- * modify the signal->posix_timers list. Yet we need sighand->siglock
- * to prevent the race with /proc/pid/timers.
+ * Invoked from do_exit() when the last thread of a thread group exits.
+ * At that point no other task can access the timers of the dying
+ * task anymore.
  */
 void exit_itimers(struct task_struct *tsk)
 {
@@ -1067,10 +1092,12 @@ void exit_itimers(struct task_struct *tsk)
 	if (list_empty(&tsk->signal->posix_timers))
 		return;
 
+	/* Protect against concurrent read via /proc/$PID/timers */
 	spin_lock_irq(&tsk->sighand->siglock);
 	list_replace_init(&tsk->signal->posix_timers, &timers);
 	spin_unlock_irq(&tsk->sighand->siglock);
 
+	/* The timers are not longer accessible via tsk::signal */
 	while (!list_empty(&timers)) {
 		tmr = list_first_entry(&timers, struct k_itimer, list);
 		itimer_delete(tmr);
#9 lore raw

[tip: timers/core] posix-timers: Prevent RT livelock in itimer_delete()

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     9d9e522010eb5685d8b53e8a24320653d9d4cbbf
Gitweb:        https://git.kernel.org/tip/9d9e522010eb5685d8b53e8a24320653d9d4cbbf
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Thu, 01 Jun 2023 22:16:34 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:40:42 +02:00

posix-timers: Prevent RT livelock in itimer_delete()

itimer_delete() has a retry loop when the timer is concurrently expired. On non-RT kernels this just spin-waits until the timer callback has completed, except for posix CPU timers which have HAVE_POSIX_CPU_TIMERS_TASK_WORK enabled.

In that case and on RT kernels the existing task could live lock when preempting the task which does the timer delivery.

Replace spin_unlock() with an invocation of timer_wait_running() to handle it the same way as the other retry loops in the posix timer code.

Fixes ec8f954a40da ("posix-timers: Use a callback for cancel synchronization on PREEMPT_RT")
---
 kernel/time/posix-timers.c | 43 ++++++++++++++++++++++++++++++-------
 1 file changed, 35 insertions(+), 8 deletions(-)
kernel/time/posix-timers.c +35 -8
index 808a247..ed3c4a9 100644
@@ -1037,27 +1037,52 @@ retry_delete:
 }
 
 /*
- * return timer owned by the process, used by exit_itimers
+ * Delete a timer if it is armed, remove it from the hash and schedule it
+ * for RCU freeing.
  */
 static void itimer_delete(struct k_itimer *timer)
 {
-retry_delete:
-	spin_lock_irq(&timer->it_lock);
+	unsigned long flags;
+
+	/*
+	 * irqsave is required to make timer_wait_running() work.
+	 */
+	spin_lock_irqsave(&timer->it_lock, flags);
 
+retry_delete:
+	/*
+	 * Even if the timer is not longer accessible from other tasks
+	 * it still might be armed and queued in the underlying timer
+	 * mechanism. Worse, that timer mechanism might run the expiry
+	 * function concurrently.
+	 */
 	if (timer_delete_hook(timer) == TIMER_RETRY) {
-		spin_unlock_irq(&timer->it_lock);
+		/*
+		 * Timer is expired concurrently, prevent livelocks
+		 * and pointless spinning on RT.
+		 *
+		 * timer_wait_running() drops timer::it_lock, which opens
+		 * the possibility for another task to delete the timer.
+		 *
+		 * That's not possible here because this is invoked from
+		 * do_exit() only for the last thread of the thread group.
+		 * So no other task can access and delete that timer.
+		 */
+		if (WARN_ON_ONCE(timer_wait_running(timer, &flags) != timer))
+			return;
+
 		goto retry_delete;
 	}
 	list_del(&timer->list);
 
-	spin_unlock_irq(&timer->it_lock);
+	spin_unlock_irqrestore(&timer->it_lock, flags);
 	release_posix_timer(timer, IT_ID_SET);
 }
 
 /*
- * This is called by do_exit or de_thread, only when nobody else can
- * modify the signal->posix_timers list. Yet we need sighand->siglock
- * to prevent the race with /proc/pid/timers.
+ * Invoked from do_exit() when the last thread of a thread group exits.
+ * At that point no other task can access the timers of the dying
+ * task anymore.
  */
 void exit_itimers(struct task_struct *tsk)
 {
@@ -1067,10 +1092,12 @@ void exit_itimers(struct task_struct *tsk)
 	if (list_empty(&tsk->signal->posix_timers))
 		return;
 
+	/* Protect against concurrent read via /proc/$PID/timers */
 	spin_lock_irq(&tsk->sighand->siglock);
 	list_replace_init(&tsk->signal->posix_timers, &timers);
 	spin_unlock_irq(&tsk->sighand->siglock);
 
+	/* The timers are not longer accessible via tsk::signal */
 	while (!list_empty(&timers)) {
 		tmr = list_first_entry(&timers, struct k_itimer, list);
 		itimer_delete(tmr);
#10 lore raw

PATCH 2/20 posix-timers: Ensure timer ID search-loop limit is valid

In reply to Thomas Gleixner

posix_timer_add() tries to allocate a posix timer ID by starting from the cached ID which was stored by the last successful allocation.

This is done in a loop searching the ID space for a free slot one by one. The loop has to terminate when the search wrapped around to the starting point.

But that's racy vs. establishing the starting point. That is read out lockless, which leads to the following problem:

CPU0 CPU1
posix_timer_add()
start = sig->posix_timer_id;
lock(hash_lock);
... posix_timer_add()
if (++sig->posix_timer_id < 0)

      			             start = sig->posix_timer_id;
     sig->posix_timer_id = 0;

So CPU1 can observe a negative start value, i.e. -1, and the loop break never happens because the condition can never be true:

if (sig->posix_timer_id == start)

     break;

While this is unlikely to ever turn into an endless loop as the ID space is huge (INT_MAX), the racy read of the start value caught the attention of KCSAN and Dmitry unearthed that incorrectness.

Rewrite it so that the start condition can never observe the negative value and annotate the read and the write with READ_ONCE()/WRITE_ONCE().

---
 include/linux/sched/signal.h |    2 +-
 kernel/time/posix-timers.c   |   30 +++++++++++++++++-------------
 2 files changed, 18 insertions(+), 14 deletions(-)
include/linux/sched/signal.h +1 -1
@@ -135,7 +135,7 @@ struct signal_struct {
 #ifdef CONFIG_POSIX_TIMERS
 
 	/* POSIX.1b Interval Timers */
-	int			posix_timer_id;
+	unsigned int		next_posix_timer_id;
 	struct list_head	posix_timers;
 
 	/* ITIMER_REAL timer for the process */
kernel/time/posix-timers.c +17 -13
@@ -140,25 +140,29 @@ static struct k_itimer *posix_timer_by_i
 static int posix_timer_add(struct k_itimer *timer)
 {
 	struct signal_struct *sig = current->signal;
-	int first_free_id = sig->posix_timer_id;
 	struct hlist_head *head;
-	int ret = -ENOENT;
+	unsigned int start, id;
 
-	do {
+	/* Can be written by a different task concurrently in the loop below */
+	start = READ_ONCE(sig->next_posix_timer_id);
+
+	for (id = ~start; start != id; id++) {
 		spin_lock(&hash_lock);
-		head = &posix_timers_hashtable[hash(sig, sig->posix_timer_id)];
-		if (!__posix_timers_find(head, sig, sig->posix_timer_id)) {
+		id = sig->next_posix_timer_id;
+
+		/* Write the next ID back. Clamp it to the positive space */
+		WRITE_ONCE(sig->next_posix_timer_id, (id + 1) & INT_MAX);
+
+		head = &posix_timers_hashtable[hash(sig, id)];
+		if (!__posix_timers_find(head, sig, id)) {
 			hlist_add_head_rcu(&timer->t_hash, head);
-			ret = sig->posix_timer_id;
+			spin_unlock(&hash_lock);
+			return id;
 		}
-		if (++sig->posix_timer_id < 0)
-			sig->posix_timer_id = 0;
-		if ((sig->posix_timer_id == first_free_id) && (ret == -ENOENT))
-			/* Loop over all possible ids completed */
-			ret = -EAGAIN;
 		spin_unlock(&hash_lock);
-	} while (ret == -ENOENT);
-	return ret;
+	}
+	/* POSIX return code when no timer ID could be allocated */
+	return -EAGAIN;
 }
 
 static inline void unlock_timer(struct k_itimer *timr, unsigned long flags)
#11 lore raw

Re: [patch 02/20] posix-timers: Ensure timer ID search-loop limit is valid

Quoted 64 lines · Thomas Gleixner

posix_timer_add() tries to allocate a posix timer ID by starting from the cached ID which was stored by the last successful allocation.

This is done in a loop searching the ID space for a free slot one by one. The loop has to terminate when the search wrapped around to the starting point.

But that's racy vs. establishing the starting point. That is read out lockless, which leads to the following problem:

CPU0 CPU1
posix_timer_add()
start = sig->posix_timer_id;
lock(hash_lock);
... posix_timer_add()
if (++sig->posix_timer_id < 0)

      			             start = sig->posix_timer_id;
     sig->posix_timer_id = 0;

So CPU1 can observe a negative start value, i.e. -1, and the loop break never happens because the condition can never be true:

if (sig->posix_timer_id == start)

     break;

While this is unlikely to ever turn into an endless loop as the ID space is huge (INT_MAX), the racy read of the start value caught the attention of KCSAN and Dmitry unearthed that incorrectness.

Rewrite it so that the start condition can never observe the negative value and annotate the read and the write with READ_ONCE()/WRITE_ONCE().

---
 include/linux/sched/signal.h |    2 +-
 kernel/time/posix-timers.c   |   30 +++++++++++++++++-------------
 2 files changed, 18 insertions(+), 14 deletions(-)
include/linux/sched/signal.h +1 -1
@@ -135,7 +135,7 @@ struct signal_struct {
 #ifdef CONFIG_POSIX_TIMERS
 
 	/* POSIX.1b Interval Timers */
-	int			posix_timer_id;
+	unsigned int		next_posix_timer_id;
 	struct list_head	posix_timers;
 
 	/* ITIMER_REAL timer for the process */
kernel/time/posix-timers.c +2 -3
@@ -140,25 +140,29 @@ static struct k_itimer *posix_timer_by_i
 static int posix_timer_add(struct k_itimer *timer)
 {
 	struct signal_struct *sig = current->signal;
-	int first_free_id = sig->posix_timer_id;
 	struct hlist_head *head;
-	int ret = -ENOENT;
+	unsigned int start, id;
 
-	do {
+	/* Can be written by a different task concurrently in the loop below */

kernel/time/posix-timers.c static struct k_itimer *posix_timer_by_i · line 154

Quoted 10 lines
kernel/time/posix-timers.c +7 -2
+	start = READ_ONCE(sig->next_posix_timer_id);
+
+	for (id = ~start; start != id; id++) {
 		spin_lock(&hash_lock);
-		head = &posix_timers_hashtable[hash(sig, sig->posix_timer_id)];
-		if (!__posix_timers_find(head, sig, sig->posix_timer_id)) {
+		id = sig->next_posix_timer_id;
+
+		/* Write the next ID back. Clamp it to the positive space */
+		WRITE_ONCE(sig->next_posix_timer_id, (id + 1) & INT_MAX);

Isn't that looping forever?

Thanks.

#12 lore raw

Re: [patch 02/20] posix-timers: Ensure timer ID search-loop limit is valid

Quoted 16 lines · Frederic Weisbecker

kernel/time/posix-timers.c static struct k_itimer *posix_timer_by_i · line 154

Quoted 13 lines · Thomas Gleixner
kernel/time/posix-timers.c +8 -3
-	do {
+	/* Can be written by a different task concurrently in the loop below */
+	start = READ_ONCE(sig->next_posix_timer_id);
+
+	for (id = ~start; start != id; id++) {
 		spin_lock(&hash_lock);
-		head = &posix_timers_hashtable[hash(sig, sig->posix_timer_id)];
-		if (!__posix_timers_find(head, sig, sig->posix_timer_id)) {
+		id = sig->next_posix_timer_id;
+
+		/* Write the next ID back. Clamp it to the positive space */
+		WRITE_ONCE(sig->next_posix_timer_id, (id + 1) & INT_MAX);

Isn't that looping forever?

No. The loop breaks when @id reaches the locklessly read out @start value again.

I admit that the 'id = ~start' part in the for() expression is confusing without a comment. That initial @id value is in the invalid space to make sure that the termination condition 'start != id' does not trigger right away. But that value gets immediately overwritten after acquiring hash_lock by the real sig->next_posix_timer_id value.

The clamp to the positive space has nothing to do with that. That's required because the ID must be positive as a negative value would be an error when returned, right?

So the whole thing works like this:

   start = READ_LOCKLESS(sig->next_id);

// Enfore that id and start are different to not terminate right away id = ~start;

loop:
if (id == start)
goto fail;
lock()

        id = sig->next_id;                      <-- stable readout
        sig->next_id = (id + 1) & INT_MAX;      <-- prevent going negative

        if (unused_id(id)) {
           add_timer_to_hash(timer, id);
           unlock();
           return id;
        }

id++;
unlock();
goto loop;

As the initial lockless readout is guaranteed to be in the positive space, how is that supposed to be looping forever?

Admittedly this can be written less obscure, but not tonight :)

Thanks,

tglx

#13 lore raw

Re: [patch 02/20] posix-timers: Ensure timer ID search-loop limit is valid

Quoted 24 lines · Thomas Gleixner

On Fri, May 05 2023 at 16:50, Frederic Weisbecker wrote:
So the whole thing works like this:

   start = READ_LOCKLESS(sig->next_id);
   // Enfore that id and start are different to not terminate right away
   id = ~start;

loop:

   if (id == start)
   	goto fail;
   lock()
        id = sig->next_id;                      <-- stable readout
        sig->next_id = (id + 1) & INT_MAX;      <-- prevent going negative

        if (unused_id(id)) {
           add_timer_to_hash(timer, id);
           unlock();
           return id;
        }
   id++;
   unlock();
   goto loop;
Quoted 2 lines

As the initial lockless readout is guaranteed to be in the positive space, how is that supposed to be looping forever?

Unless you think about the theoretical case of an unlimited number of threads sharing the signal_struct which all concurrently try to allocate a timer id and then releasing it immediately again (to avoid resource limit exhaustion). Theoretically possible, but is this a real concern with a timer ID space of 2G?

I'm sure that it's incredibly hard to exploit this, but what's really bothering me is the hash table itself. The only reason why we have that is CRIU.

The only alternative solution I could come up with is a paritioned xarray where the index space would be segmented for each TGID, i.e.

segment.start = TGID * MAX_TIMERS_PER_PROCESS
segment.end = segment.start + MAX_TIMERS_PER_PROCESS - 1

where MAX_TIMERS_PER_PROCESS could be a copius 2^16 which would work for both 32bit and 64bit TID limits.

That would avoid the hash table lookups and the related issues, but OTH it would require to allocate one extra page per TGID if the application uses a single posix timer.

Not sure whether that's worth it though.

Thanks,

tglx

#14 lore raw

Re: [patch 02/20] posix-timers: Ensure timer ID search-loop limit is valid

Quoted 10 lines · Thomas Gleixner
Quoted 3 lines · Thomas Gleixner

On Fri, May 05 2023 at 16:50, Frederic Weisbecker wrote:
As the initial lockless readout is guaranteed to be in the positive space, how is that supposed to be looping forever?

Unless you think about the theoretical case of an unlimited number of threads sharing the signal_struct which all concurrently try to allocate a timer id and then releasing it immediately again (to avoid resource limit exhaustion). Theoretically possible, but is this a real concern with a timer ID space of 2G?

It only hurts the process which does this and does not inflict any latencies by holding hash_lock over the full search for a free spot.

Quoted 6 lines

The only alternative solution I could come up with is a paritioned xarray where the index space would be segmented for each TGID, i.e.

segment.start = TGID * MAX_TIMERS_PER_PROCESS
segment.end = segment.start + MAX_TIMERS_PER_PROCESS - 1

Quoted 8 lines

where MAX_TIMERS_PER_PROCESS could be a copius 2^16 which would work for both 32bit and 64bit TID limits.

That would avoid the hash table lookups and the related issues, but OTH it would require to allocate one extra page per TGID if the application uses a single posix timer.

Not sure whether that's worth it though.

More thoughts on this. If we go there and accept the extra page of memory then we can just go all the way and make the xarray per process, actually per signal.

That would also require another change, namely making the preallocated sigqueue part of struct k_itimer, which in turn would not be the worst of all ideas as it gets rid of the lookup in posixtimer_rearm() and would also allow for some clever handling of the nasty SIG_IGN issues.

Though that's separate from the problem at hand.

Thanks,

tglx

#15 lore raw

Re: [patch 02/20] posix-timers: Ensure timer ID search-loop limit is valid

Quoted 14 lines · Thomas Gleixner
Quoted 13 lines · Thomas Gleixner

The only alternative solution I could come up with is a paritioned xarray where the index space would be segmented for each TGID, i.e.

segment.start = TGID * MAX_TIMERS_PER_PROCESS
segment.end = segment.start + MAX_TIMERS_PER_PROCESS - 1

where MAX_TIMERS_PER_PROCESS could be a copius 2^16 which would work for both 32bit and 64bit TID limits.

That would avoid the hash table lookups and the related issues, but OTH it would require to allocate one extra page per TGID if the application uses a single posix timer.

Quoted 5 lines
Quoted 1 line

Not sure whether that's worth it though.

More thoughts on this. If we go there and accept the extra page of memory then we can just go all the way and make the xarray per process, actually per signal.

Thinking more about it. The current scheme how timer ID allocation works is really interesting vs. CRIU.

Assume a process creates/deletes timers frequently. It's not hard to move the next ID close to INT_MAX, i.e. 2G

Now checkpoint that thing and restore it which means to do the create/delete dance to move next ID up to the last one-1. Will only take a couple of hours....

Thanks,

tglx

#16 lore raw

Re: [patch 02/20] posix-timers: Ensure timer ID search-loop limit is valid

Quoted 8 lines · Thomas Gleixner
Quoted 3 lines · Thomas Gleixner

More thoughts on this. If we go there and accept the extra page of memory then we can just go all the way and make the xarray per process, actually per signal.

Thinking more about it. The current scheme how timer ID allocation works is really interesting vs. CRIU.

Quoted 6 lines

Assume a process creates/deletes timers frequently. It's not hard to move the next ID close to INT_MAX, i.e. 2G

Now checkpoint that thing and restore it which means to do the create/delete dance to move next ID up to the last one-1. Will only take a couple of hours....

I'm cursing myself for overlooking this back then when the CRIU changes to the timer ID management were made. Why?

Because that created an ABI which CRIU relies on.

The proper solution for this would be to make it possible to create a timer with a given ID. That's not rocket science, but we need buy in from the CRIU folks. Otherwise we are up the regression creek without a paddle.

Thanks,

tglx

#17 lore raw

[RFD] posix-timers: CRIU woes

Hi!

This is a summary of several mails so that the CRIU people have the full picture.

A recent syzbot report made me look at the timer ID management, which was modified 7 years ago to accomodate CRIU:

5ed67f05f66c ("posix timers: Allocate timer id per process (v2)")

and introduced that reported issue along with a bogus loop termination problem. See

    https://lore.kernel.org/lkml/[email protected]/
    https://lore.kernel.org/lkml/[email protected]

for details.

The intent to make the timer IDs per process is definitely correct, but the implementation is beyond suboptimal. I really regret that I did not catch this back then when picking those changes up.

The way it works is that each process keeps a 'next_timer_id' which it uses to allocate the next timer. That allows CRIU to reconstruct timers with the same ID by walking the ID space via

  do {
     timer_create(&timer, ...., &id);
     if (id == original_id)
        goto success;
     timer_delete(&timer);
  } while (idspace_not_exhausted());

That works by some definition of works, but it is problematic in two ways:

1) As the timer ID space is up to INT_MAX, a process which creates and deletes timers frequently, can easily move up close to the INT_MAX id space over time.

If such a process is checkpointed and restored, then the above loop will run for at least an hour to restore a single timer.

And no, this is not only a hypothetical issue. There are legitimate scenarios where threads are created and the control thread arms a posix CPU timer on them. Such threads can be torn down on a regular base due to thread pool consolidations. As CRIU does not happen every 5 minutes it's not completely unlikely that such a process surives quite some time on a particular host and thereby approaches the ID space limit.

Sure we can restrict the ID space to a way smaller number so the search wraps around earlier, but what's a sensible limit?

Though restricting the ID space has its own issue vs. backwards compability. A process which created a timer on an older kernel with an ID larger than the newer kernels ID limit cannot longer be restored on that newer kernel.

Aside of that it does not solve the other problem this created:

2) That change created an user space ABI, which means that the kernel side has to stick with this next ID search mechanism forever.

That prevents to get rid of that global lock and hash table by sticking an xarray into task::signal which makes a lot of sense in terms of cache locality and gets rid of the extra timer list management in task::signal. Making this change would be very useful to address some other issues in the posix-timer code without creating yet more duct tape horrors.

Such a change obviously has to aim for a dense ID space to keep the memory overhead low, but that breaks existing CRIU userspace because dense ID space and next ID search does not fit together.

Next ID search is obviously creating non-recoverable holes in the case that timers are deleted afterwards.

A dense ID space approach can create holes too, but they are recoverable and well within the resource limits, because the process has to be able to create enough timers in the first place in order to release those in the middle.

With the next ID search brute force recovery is not possible on a kernel with dense ID as there is no way to create all intermediate timers first before reaching the one at the far end due to resource limits.

So because of that half thought out user space ABI we are now up the regression creek without a paddle, unless CRIU can accomodate to a different restore mechanism to lift this restriction from the kernel.

Thoughts?

Thanks,

tglx

#18 lore raw

Re: [RFD] posix-timers: CRIU woes

Quoted 87 lines · Thomas Gleixner

Hi!

This is a summary of several mails so that the CRIU people have the full picture.

A recent syzbot report made me look at the timer ID management, which was modified 7 years ago to accomodate CRIU:

5ed67f05f66c ("posix timers: Allocate timer id per process (v2)")

and introduced that reported issue along with a bogus loop termination problem. See

     https://lore.kernel.org/lkml/[email protected]/
     https://lore.kernel.org/lkml/[email protected]

for details.

The intent to make the timer IDs per process is definitely correct, but the implementation is beyond suboptimal. I really regret that I did not catch this back then when picking those changes up.

The way it works is that each process keeps a 'next_timer_id' which it uses to allocate the next timer. That allows CRIU to reconstruct timers with the same ID by walking the ID space via

   do {
      timer_create(&timer, ...., &id);
      if (id == original_id)
         goto success;
      timer_delete(&timer);
   } while (idspace_not_exhausted());

That works by some definition of works, but it is problematic in two ways:

1) As the timer ID space is up to INT_MAX, a process which creates and deletes timers frequently, can easily move up close to the INT_MAX id space over time.

If such a process is checkpointed and restored, then the above loop will run for at least an hour to restore a single timer.

And no, this is not only a hypothetical issue. There are legitimate scenarios where threads are created and the control thread arms a posix CPU timer on them. Such threads can be torn down on a regular base due to thread pool consolidations. As CRIU does not happen every 5 minutes it's not completely unlikely that such a process surives quite some time on a particular host and thereby approaches the ID space limit.

Sure we can restrict the ID space to a way smaller number so the search wraps around earlier, but what's a sensible limit?

Though restricting the ID space has its own issue vs. backwards compability. A process which created a timer on an older kernel with an ID larger than the newer kernels ID limit cannot longer be restored on that newer kernel.

Aside of that it does not solve the other problem this created:

2) That change created an user space ABI, which means that the kernel side has to stick with this next ID search mechanism forever.

That prevents to get rid of that global lock and hash table by sticking an xarray into task::signal which makes a lot of sense in terms of cache locality and gets rid of the extra timer list management in task::signal. Making this change would be very useful to address some other issues in the posix-timer code without creating yet more duct tape horrors.

Such a change obviously has to aim for a dense ID space to keep the memory overhead low, but that breaks existing CRIU userspace because dense ID space and next ID search does not fit together.

Next ID search is obviously creating non-recoverable holes in the case that timers are deleted afterwards.

A dense ID space approach can create holes too, but they are recoverable and well within the resource limits, because the process has to be able to create enough timers in the first place in order to release those in the middle.

With the next ID search brute force recovery is not possible on a kernel with dense ID as there is no way to create all intermediate timers first before reaching the one at the far end due to resource limits.

Quoted 5 lines

So because of that half thought out user space ABI we are now up the regression creek without a paddle, unless CRIU can accomodate to a different restore mechanism to lift this restriction from the kernel.

Thoughts?

Maybe we can do something similar to /proc/sys/kernel/ns_last_pid? Switch to per-(process->signal) idr based approach with idr_set_cursor to set next id for next posix timer from new sysctl?

Quoted 6 lines

Thanks,

tglx

Signature · 2 lines
Best regards, Tikhomirov Pavel
Senior Software Developer, Virtuozzo.
#19 lore raw

Re: [RFD] posix-timers: CRIU woes

Pavel!

Quoted 10 lines · Pavel Tikhomirov
Quoted 5 lines · Thomas Gleixner

So because of that half thought out user space ABI we are now up the regression creek without a paddle, unless CRIU can accomodate to a different restore mechanism to lift this restriction from the kernel.

Thoughts?

Maybe we can do something similar to /proc/sys/kernel/ns_last_pid? Switch to per-(process->signal) idr based approach with idr_set_cursor to set next id for next posix timer from new sysctl?

I'm not a fan of such sysctls. We have already too many of them and that particular one does not buy much.

We can simply let timer_create() or a new syscall create a timer at a given ID.

That allows CRIU to restore any checkpointed process no matter which kernel version it came from without doing this insane create/delete dance.

The downside is that this allows to create stupidly sparse timer IDs even for the non CRIU case, which increases per process kernel memory consumption and creates slightly more overhead in the signal delivery path. The latter is a burden on the process owning the timer and not affecting expiry, which is a context stealing operation. The memory part needs eventually some thoughts vs. accounting.

If the 'explicit at ID' option is not used then the ID mechanism is optimzied for dense IDs by using the first available ID in a bottom up search, which recovers holes created by a timer_delete() operation.

Thanks,

tglx

#20 lore raw

Re: [RFD] posix-timers: CRIU woes

Quoted 8 lines · Thomas Gleixner

Pavel!

Quoted 5 lines · Pavel Tikhomirov
Quoted 4 lines · Thomas Gleixner

So because of that half thought out user space ABI we are now up the regression creek without a paddle, unless CRIU can accomodate to a different restore mechanism to lift this restriction from the kernel.

Quoted 8 lines
Quoted 5 lines
Quoted 1 line

Thoughts?

Maybe we can do something similar to /proc/sys/kernel/ns_last_pid? Switch to per-(process->signal) idr based approach with idr_set_cursor to set next id for next posix timer from new sysctl?

I'm not a fan of such sysctls. We have already too many of them and that particular one does not buy much.

Sorry, it was a bad idea, what you suggest below is much better.

Quoted 3 lines

We can simply let timer_create() or a new syscall create a timer at a given ID.

Yes this would work for CRIU. (note: in neighbor thread Andrei writes about adding a bit to sigevent.sigev_notify to request a timer with a specified id, new syscall is also a good option)

Quoted 4 lines

That allows CRIU to restore any checkpointed process no matter which kernel version it came from without doing this insane create/delete dance.

Yes, for CRIU this kind of API change is a big improvement.

Quoted 11 lines

The downside is that this allows to create stupidly sparse timer IDs even for the non CRIU case, which increases per process kernel memory consumption and creates slightly more overhead in the signal delivery path. The latter is a burden on the process owning the timer and not affecting expiry, which is a context stealing operation. The memory part needs eventually some thoughts vs. accounting.

If the 'explicit at ID' option is not used then the ID mechanism is optimzied for dense IDs by using the first available ID in a bottom up search, which recovers holes created by a timer_delete() operation.

Not sure how kernel memory consumption increases with sparse timer IDs, global hashtable (posix_timers_hashtable) is the same size anyway, entries in hlists can be distributed differently as hash depends on id directly but we have same number of entries. Probably I miss something, why do we need dense IDs?

Quoted 4 lines

Thanks,

tglx

Signature · 2 lines
Best regards, Tikhomirov Pavel
Senior Software Developer, Virtuozzo.
#21 lore raw

Re: [RFD] posix-timers: CRIU woes

Quoted 5 lines · Pavel Tikhomirov

Not sure how kernel memory consumption increases with sparse timer IDs, global hashtable (posix_timers_hashtable) is the same size anyway, entries in hlists can be distributed differently as hash depends on id directly but we have same number of entries. Probably I miss something, why do we need dense IDs?

The proposal was to remove the global hash and use a signal_struct based xarray instead.

#22 lore raw

Re: [RFD] posix-timers: CRIU woes

In reply to Pavel Tikhomirov
Quoted 12 lines · Pavel Tikhomirov
Quoted 10 lines · Thomas Gleixner

The downside is that this allows to create stupidly sparse timer IDs even for the non CRIU case, which increases per process kernel memory consumption and creates slightly more overhead in the signal delivery path. The latter is a burden on the process owning the timer and not affecting expiry, which is a context stealing operation. The memory part needs eventually some thoughts vs. accounting.

If the 'explicit at ID' option is not used then the ID mechanism is optimzied for dense IDs by using the first available ID in a bottom up search, which recovers holes created by a timer_delete() operation.

Quoted 5 lines

Not sure how kernel memory consumption increases with sparse timer IDs, global hashtable (posix_timers_hashtable) is the same size anyway, entries in hlists can be distributed differently as hash depends on id directly but we have same number of entries. Probably I miss something, why do we need dense IDs?

Because I want to get rid of the global hash table as I explained in my summary mail.

Thanks,

tglx

#23 lore raw

RE: [RFD] posix-timers: CRIU woes

In reply to Pavel Tikhomirov

From: Pavel Tikhomirov

Quoted 1 line

Sent: 10 May 2023 05:37

...

Quoted 4 lines
Quoted 4 lines

A dense ID space approach can create holes too, but they are recoverable and well within the resource limits, because the process has to be able to create enough timers in the first place in order to release those in the middle.

While it doesn't help at all for creating items with fixed ids, my 'favourite' scheme for allocating ids it to allocate a number that will be a perfect hash onto an empty hash table slot.

The lookup check is then just array[id & mask].id == id. A FIFO freelist can be run through the free entries and the high bits incremented each time a slot is used. So allocation is usually fixed cost.

If the table is full it's size can easily be doubled. If the number of unused entries is doubled each time the table size is doubled then the you (more or less) guarantee that an id won't get reused any time soon after it is freed.

This would be ok for restoring ids allocated by the same scheme. But would need a fallback for restoring pathological list of ids.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

#24 lore raw

Re: [RFD] posix-timers: CRIU woes

In reply to Thomas Gleixner
Quoted 88 lines · Thomas Gleixner

Hi!

This is a summary of several mails so that the CRIU people have the full picture.

A recent syzbot report made me look at the timer ID management, which was modified 7 years ago to accomodate CRIU:

5ed67f05f66c ("posix timers: Allocate timer id per process (v2)")

and introduced that reported issue along with a bogus loop termination problem. See

    https://lore.kernel.org/lkml/[email protected]/
    https://lore.kernel.org/lkml/[email protected]

for details.

The intent to make the timer IDs per process is definitely correct, but the implementation is beyond suboptimal. I really regret that I did not catch this back then when picking those changes up.

The way it works is that each process keeps a 'next_timer_id' which it uses to allocate the next timer. That allows CRIU to reconstruct timers with the same ID by walking the ID space via

  do {
     timer_create(&timer, ...., &id);
     if (id == original_id)
        goto success;
     timer_delete(&timer);
  } while (idspace_not_exhausted());

That works by some definition of works, but it is problematic in two ways:

1) As the timer ID space is up to INT_MAX, a process which creates and deletes timers frequently, can easily move up close to the INT_MAX id space over time.

If such a process is checkpointed and restored, then the above loop will run for at least an hour to restore a single timer.

And no, this is not only a hypothetical issue. There are legitimate scenarios where threads are created and the control thread arms a posix CPU timer on them. Such threads can be torn down on a regular base due to thread pool consolidations. As CRIU does not happen every 5 minutes it's not completely unlikely that such a process surives quite some time on a particular host and thereby approaches the ID space limit.

Sure we can restrict the ID space to a way smaller number so the search wraps around earlier, but what's a sensible limit?

Though restricting the ID space has its own issue vs. backwards compability. A process which created a timer on an older kernel with an ID larger than the newer kernels ID limit cannot longer be restored on that newer kernel.

Aside of that it does not solve the other problem this created:

2) That change created an user space ABI, which means that the kernel side has to stick with this next ID search mechanism forever.

That prevents to get rid of that global lock and hash table by sticking an xarray into task::signal which makes a lot of sense in terms of cache locality and gets rid of the extra timer list management in task::signal. Making this change would be very useful to address some other issues in the posix-timer code without creating yet more duct tape horrors.

Such a change obviously has to aim for a dense ID space to keep the memory overhead low, but that breaks existing CRIU userspace because dense ID space and next ID search does not fit together.

Next ID search is obviously creating non-recoverable holes in the case that timers are deleted afterwards.

A dense ID space approach can create holes too, but they are recoverable and well within the resource limits, because the process has to be able to create enough timers in the first place in order to release those in the middle.

With the next ID search brute force recovery is not possible on a kernel with dense ID as there is no way to create all intermediate timers first before reaching the one at the far end due to resource limits.

Quoted 5 lines

So because of that half thought out user space ABI we are now up the regression creek without a paddle, unless CRIU can accomodate to a different restore mechanism to lift this restriction from the kernel.

Thoughts?

Hi Thomas,

If you give us a new API to create timers with specified id-s, we will figure out how to live with it. It isn't good to ask users to update CRIU to work on new kernels, but here are reasons and event improvements for CRIU, so I think it's worth it.

As for API, we can use one bit of sigevent.sigev_notify to request a timer with a specified id.

Thanks,
Andrei

#25 lore raw

Re: [RFD] posix-timers: CRIU woes

Quoted 93 lines · Andrey Vagin
Quoted 92 lines · Thomas Gleixner

Hi!

This is a summary of several mails so that the CRIU people have the full picture.

A recent syzbot report made me look at the timer ID management, which was modified 7 years ago to accomodate CRIU:

5ed67f05f66c ("posix timers: Allocate timer id per process (v2)")

and introduced that reported issue along with a bogus loop termination problem. See

     https://lore.kernel.org/lkml/[email protected]/
     https://lore.kernel.org/lkml/[email protected]

for details.

The intent to make the timer IDs per process is definitely correct, but the implementation is beyond suboptimal. I really regret that I did not catch this back then when picking those changes up.

The way it works is that each process keeps a 'next_timer_id' which it uses to allocate the next timer. That allows CRIU to reconstruct timers with the same ID by walking the ID space via

   do {
      timer_create(&timer, ...., &id);
      if (id == original_id)
         goto success;
      timer_delete(&timer);
   } while (idspace_not_exhausted());

That works by some definition of works, but it is problematic in two ways:

1) As the timer ID space is up to INT_MAX, a process which creates and deletes timers frequently, can easily move up close to the INT_MAX id space over time.

If such a process is checkpointed and restored, then the above loop will run for at least an hour to restore a single timer.

And no, this is not only a hypothetical issue. There are legitimate scenarios where threads are created and the control thread arms a posix CPU timer on them. Such threads can be torn down on a regular base due to thread pool consolidations. As CRIU does not happen every 5 minutes it's not completely unlikely that such a process surives quite some time on a particular host and thereby approaches the ID space limit.

Sure we can restrict the ID space to a way smaller number so the search wraps around earlier, but what's a sensible limit?

Though restricting the ID space has its own issue vs. backwards compability. A process which created a timer on an older kernel with an ID larger than the newer kernels ID limit cannot longer be restored on that newer kernel.

Aside of that it does not solve the other problem this created:

2) That change created an user space ABI, which means that the kernel side has to stick with this next ID search mechanism forever.

That prevents to get rid of that global lock and hash table by sticking an xarray into task::signal which makes a lot of sense in terms of cache locality and gets rid of the extra timer list management in task::signal. Making this change would be very useful to address some other issues in the posix-timer code without creating yet more duct tape horrors.

Such a change obviously has to aim for a dense ID space to keep the memory overhead low, but that breaks existing CRIU userspace because dense ID space and next ID search does not fit together.

Next ID search is obviously creating non-recoverable holes in the case that timers are deleted afterwards.

A dense ID space approach can create holes too, but they are recoverable and well within the resource limits, because the process has to be able to create enough timers in the first place in order to release those in the middle.

With the next ID search brute force recovery is not possible on a kernel with dense ID as there is no way to create all intermediate timers first before reaching the one at the far end due to resource limits.

So because of that half thought out user space ABI we are now up the regression creek without a paddle, unless CRIU can accomodate to a different restore mechanism to lift this restriction from the kernel.

Quoted 8 lines
Quoted 1 line

Thoughts?

Hi Thomas,

If you give us a new API to create timers with specified id-s, we will figure out how to live with it. It isn't good to ask users to update CRIU to work on new kernels, but here are reasons and event improvements for CRIU, so I think it's worth it.

I agree, any API to create timers with specified id-s would work for new CRIU versions.

Quoted 3 lines

As for API, we can use one bit of sigevent.sigev_notify to request a timer with a specified id.

Yes, I agree, this kind of API is a good option.

Quoted 3 lines

Thanks,
Andrei

Signature · 2 lines
Best regards, Tikhomirov Pavel
Senior Software Developer, Virtuozzo.
#26 lore raw

Re: [RFD] posix-timers: CRIU woes

Quoted 6 lines · Pavel Tikhomirov
Quoted 5 lines · Andrey Vagin
Quoted 5 lines

So because of that half thought out user space ABI we are now up the regression creek without a paddle, unless CRIU can accomodate to a different restore mechanism to lift this restriction from the kernel.

Quoted 7 lines
Quoted 4 lines

If you give us a new API to create timers with specified id-s, we will figure out how to live with it. It isn't good to ask users to update CRIU to work on new kernels, but here are reasons and event improvements for CRIU, so I think it's worth it.

I agree, any API to create timers with specified id-s would work for new CRIU versions.

The real question is whether this will cause any upheaval when a new kernel meets a non-updated CRIU stack.

You know the UABI regression rules of the kernel...

Thanks,

tglx

#27 lore raw

Re: [RFD] posix-timers: CRIU woes

Quoted 12 lines · Thomas Gleixner
Quoted 11 lines · Pavel Tikhomirov
Quoted 9 lines · Andrey Vagin
Quoted 5 lines

So because of that half thought out user space ABI we are now up the regression creek without a paddle, unless CRIU can accomodate to a different restore mechanism to lift this restriction from the kernel.

If you give us a new API to create timers with specified id-s, we will figure out how to live with it. It isn't good to ask users to update CRIU to work on new kernels, but here are reasons and event improvements for CRIU, so I think it's worth it.

Quoted 5 lines
Quoted 2 lines

I agree, any API to create timers with specified id-s would work for new CRIU versions.

The real question is whether this will cause any upheaval when a new kernel meets a non-updated CRIU stack.

Creation of posix timer would hang forever in this loop https://github.com/checkpoint-restore/criu/blob/33dd66c6fc93c47213aaa0447a94d97ba1fa56ba/criu/pie/restorer.c#L1185 if old criu is run on new kernel (without consecutive id allocation) AFAICS.

Quoted 6 lines

You know the UABI regression rules of the kernel...

Thanks,

tglx

Signature · 2 lines
Best regards, Tikhomirov Pavel
Senior Software Developer, Virtuozzo.
#28 lore raw

Re: [RFD] posix-timers: CRIU woes

Quoted 16 lines · Pavel Tikhomirov
Quoted 15 lines · Thomas Gleixner
Quoted 13 lines · Pavel Tikhomirov
Quoted 9 lines · Andrey Vagin
Quoted 5 lines

So because of that half thought out user space ABI we are now up the regression creek without a paddle, unless CRIU can accomodate to a different restore mechanism to lift this restriction from the kernel.

If you give us a new API to create timers with specified id-s, we will figure out how to live with it. It isn't good to ask users to update CRIU to work on new kernels, but here are reasons and event improvements for CRIU, so I think it's worth it.

I agree, any API to create timers with specified id-s would work for new CRIU versions.

Quoted 6 lines
Quoted 2 lines

The real question is whether this will cause any upheaval when a new kernel meets a non-updated CRIU stack.

Creation of posix timer would hang forever in this loop
https://github.com/checkpoint-restore/criu/blob/33dd66c6fc93c47213aaa0447a94d97ba1fa56ba/criu/pie/restorer.c#L1185
if old criu is run on new kernel (without consecutive id allocation) AFAICS.

Yes, because that "sanity" check

     if ((long)next_id > args->posix_timers[i].spt.it_id)

which tries to establish whether the kernel provides timer IDs in strict increasing order does not work for that case.

It "works" to detect the IDR case on older kernels by chance, but not under all circumstances. Assume the following case:

Global IDR has a free slot at index 1

Restore tries to create a timer for index 2

That will also loop forever, unless some other process creates a timer and occupies the free slot at index 1, right?

So this needs a fix anyway, which should be done so that the new kernel case is at least properly detected.

But even then there is still the problem of "it worked before I upgraded the kernel".

IOW, we are still up a creek without a paddle, unless you would be willing to utilize the existing CRIU bug to distribute the 'deal with new kernel' mechanics as a bug bounty :)

Fix for the loop termination below.

Thanks,

tglx

---
 criu/pie/restorer.c |   24 +++++++++++++-----------
 1 file changed, 13 insertions(+), 11 deletions(-)
criu/pie/restorer.c +13 -11
@@ -1169,10 +1169,10 @@ static int timerfd_arm(struct task_resto
 static int create_posix_timers(struct task_restore_args *args)
 {
 	int ret, i;
-	kernel_timer_t next_id;
+	kernel_timer_t next_id, timer_id;
 	struct sigevent sev;
 
-	for (i = 0; i < args->posix_timers_n; i++) {
+	for (i = 0, next_id = 0; i < args->posix_timers_n; i++) {
 		sev.sigev_notify = args->posix_timers[i].spt.it_sigev_notify;
 		sev.sigev_signo = args->posix_timers[i].spt.si_signo;
 #ifdef __GLIBC__
@@ -1183,25 +1183,27 @@ static int create_posix_timers(struct ta
 		sev.sigev_value.sival_ptr = args->posix_timers[i].spt.sival_ptr;
 
 		while (1) {
-			ret = sys_timer_create(args->posix_timers[i].spt.clock_id, &sev, &next_id);
+			ret = sys_timer_create(args->posix_timers[i].spt.clock_id, &sev, &timer_id);
 			if (ret < 0) {
 				pr_err("Can't create posix timer - %d\n", i);
 				return ret;
 			}
 
-			if (next_id == args->posix_timers[i].spt.it_id)
+			if (timer_id != next_id) {
+				pr_err("Can't create timers, kernel don't give them consequently\n");
+				return -1;
+			}
+
+			next_id++;
+
+			if (timer_id == args->posix_timers[i].spt.it_id)
 				break;
 
-			ret = sys_timer_delete(next_id);
+			ret = sys_timer_delete(timer_id);
 			if (ret < 0) {
-				pr_err("Can't remove temporaty posix timer 0x%x\n", next_id);
+				pr_err("Can't remove temporaty posix timer 0x%x\n", timer_id);
 				return ret;
 			}
-
-			if ((long)next_id > args->posix_timers[i].spt.it_id) {
-				pr_err("Can't create timers, kernel don't give them consequently\n");
-				return -1;
-			}
 		}
 	}
 
#29 lore raw

Re: [RFD] posix-timers: CRIU woes

Quoted 24 lines · Thomas Gleixner
Quoted 16 lines · Pavel Tikhomirov
Quoted 15 lines · Thomas Gleixner
Quoted 13 lines · Pavel Tikhomirov
Quoted 9 lines · Andrey Vagin
Quoted 5 lines

So because of that half thought out user space ABI we are now up the regression creek without a paddle, unless CRIU can accomodate to a different restore mechanism to lift this restriction from the kernel.

If you give us a new API to create timers with specified id-s, we will figure out how to live with it. It isn't good to ask users to update CRIU to work on new kernels, but here are reasons and event improvements for CRIU, so I think it's worth it.

I agree, any API to create timers with specified id-s would work for new CRIU versions.

Quoted 6 lines
Quoted 2 lines

The real question is whether this will cause any upheaval when a new kernel meets a non-updated CRIU stack.

Creation of posix timer would hang forever in this loop
https://github.com/checkpoint-restore/criu/blob/33dd66c6fc93c47213aaa0447a94d97ba1fa56ba/criu/pie/restorer.c#L1185
if old criu is run on new kernel (without consecutive id allocation) AFAICS.

Quoted 6 lines

Yes, because that "sanity" check

      if ((long)next_id > args->posix_timers[i].spt.it_id)

which tries to establish whether the kernel provides timer IDs in strict increasing order does not work for that case.

Yes, this check is not perfect, but it does at least something: It detects that posix timer creation missed needed id (if you start from 0 and increase by 1 each time you can not reach number > N before reaching N).

Quoted 10 lines

It "works" to detect the IDR case on older kernels by chance, but not under all circumstances. Assume the following case:

Global IDR has a free slot at index 1

Restore tries to create a timer for index 2

That will also loop forever, unless some other process creates a timer and occupies the free slot at index 1, right?

Yes on old-old kernel, where there were no ids increasing on create/delete, CRIU is broken, but CRIU does not support kernels earlier than 3.11 (https://criu.org/Check_the_kernel#Basic) so probably we are fine.

git describe --contains 5ed67f05f66c
v3.10-rc1~171^2~9

Quoted 11 lines

So this needs a fix anyway, which should be done so that the new kernel case is at least properly detected.

But even then there is still the problem of "it worked before I upgraded the kernel".

IOW, we are still up a creek without a paddle, unless you would be willing to utilize the existing CRIU bug to distribute the 'deal with new kernel' mechanics as a bug bounty :) >
Fix for the loop termination below.

It fixes the loop for new kernel, I agree.

Quoted 62 lines

Thanks,

tglx

---
  criu/pie/restorer.c |   24 +++++++++++++-----------
  1 file changed, 13 insertions(+), 11 deletions(-)
criu/pie/restorer.c +13 -11
@@ -1169,10 +1169,10 @@ static int timerfd_arm(struct task_resto
  static int create_posix_timers(struct task_restore_args *args)
  {
  	int ret, i;
-	kernel_timer_t next_id;
+	kernel_timer_t next_id, timer_id;
  	struct sigevent sev;
 
-	for (i = 0; i < args->posix_timers_n; i++) {
+	for (i = 0, next_id = 0; i < args->posix_timers_n; i++) {
  		sev.sigev_notify = args->posix_timers[i].spt.it_sigev_notify;
  		sev.sigev_signo = args->posix_timers[i].spt.si_signo;
  #ifdef __GLIBC__
@@ -1183,25 +1183,27 @@ static int create_posix_timers(struct ta
  		sev.sigev_value.sival_ptr = args->posix_timers[i].spt.sival_ptr;
 
  		while (1) {
-			ret = sys_timer_create(args->posix_timers[i].spt.clock_id, &sev, &next_id);
+			ret = sys_timer_create(args->posix_timers[i].spt.clock_id, &sev, &timer_id);
  			if (ret < 0) {
  				pr_err("Can't create posix timer - %d\n", i);
  				return ret;
  			}
 
-			if (next_id == args->posix_timers[i].spt.it_id)
+			if (timer_id != next_id) {
+				pr_err("Can't create timers, kernel don't give them consequently\n");
+				return -1;
+			}
+
+			next_id++;
+
+			if (timer_id == args->posix_timers[i].spt.it_id)
  				break;
 
-			ret = sys_timer_delete(next_id);
+			ret = sys_timer_delete(timer_id);
  			if (ret < 0) {
-				pr_err("Can't remove temporaty posix timer 0x%x\n", next_id);
+				pr_err("Can't remove temporaty posix timer 0x%x\n", timer_id);
  				return ret;
  			}
-
-			if ((long)next_id > args->posix_timers[i].spt.it_id) {
-				pr_err("Can't create timers, kernel don't give them consequently\n");
-				return -1;
-			}
  		}
  	}
 
Signature · 2 lines
Best regards, Tikhomirov Pavel
Senior Software Developer, Virtuozzo.
#30 lore raw

Re: [RFD] posix-timers: CRIU woes

In reply to Thomas Gleixner
Quoted 44 lines · Thomas Gleixner
Quoted 16 lines · Pavel Tikhomirov
Quoted 15 lines · Thomas Gleixner
Quoted 13 lines · Pavel Tikhomirov
Quoted 9 lines · Andrey Vagin
Quoted 5 lines

So because of that half thought out user space ABI we are now up the regression creek without a paddle, unless CRIU can accomodate to a different restore mechanism to lift this restriction from the kernel.

If you give us a new API to create timers with specified id-s, we will figure out how to live with it. It isn't good to ask users to update CRIU to work on new kernels, but here are reasons and event improvements for CRIU, so I think it's worth it.

I agree, any API to create timers with specified id-s would work for new CRIU versions.

Quoted 6 lines
Quoted 2 lines

The real question is whether this will cause any upheaval when a new kernel meets a non-updated CRIU stack.

Creation of posix timer would hang forever in this loop
https://github.com/checkpoint-restore/criu/blob/33dd66c6fc93c47213aaa0447a94d97ba1fa56ba/criu/pie/restorer.c#L1185
if old criu is run on new kernel (without consecutive id allocation) AFAICS.

Yes, because that "sanity" check

      if ((long)next_id > args->posix_timers[i].spt.it_id)

which tries to establish whether the kernel provides timer IDs in strict increasing order does not work for that case.

It "works" to detect the IDR case on older kernels by chance, but not under all circumstances. Assume the following case:

Global IDR has a free slot at index 1

Restore tries to create a timer for index 2

That will also loop forever, unless some other process creates a timer and occupies the free slot at index 1, right?

So this needs a fix anyway, which should be done so that the new kernel case is at least properly detected.

Quoted 8 lines

But even then there is still the problem of "it worked before I upgraded the kernel".

IOW, we are still up a creek without a paddle, unless you would be willing to utilize the existing CRIU bug to distribute the 'deal with new kernel' mechanics as a bug bounty :)

Fix for the loop termination below.

I've prepared a PR with your patch (with minimal change) and added you Signed-off-by:, hope it's ok:
https://github.com/checkpoint-restore/criu/pull/2174

Quoted 62 lines

Thanks,

tglx

---
  criu/pie/restorer.c |   24 +++++++++++++-----------
  1 file changed, 13 insertions(+), 11 deletions(-)
criu/pie/restorer.c +13 -11
@@ -1169,10 +1169,10 @@ static int timerfd_arm(struct task_resto
  static int create_posix_timers(struct task_restore_args *args)
  {
  	int ret, i;
-	kernel_timer_t next_id;
+	kernel_timer_t next_id, timer_id;
  	struct sigevent sev;
 
-	for (i = 0; i < args->posix_timers_n; i++) {
+	for (i = 0, next_id = 0; i < args->posix_timers_n; i++) {
  		sev.sigev_notify = args->posix_timers[i].spt.it_sigev_notify;
  		sev.sigev_signo = args->posix_timers[i].spt.si_signo;
  #ifdef __GLIBC__
@@ -1183,25 +1183,27 @@ static int create_posix_timers(struct ta
  		sev.sigev_value.sival_ptr = args->posix_timers[i].spt.sival_ptr;
 
  		while (1) {
-			ret = sys_timer_create(args->posix_timers[i].spt.clock_id, &sev, &next_id);
+			ret = sys_timer_create(args->posix_timers[i].spt.clock_id, &sev, &timer_id);
  			if (ret < 0) {
  				pr_err("Can't create posix timer - %d\n", i);
  				return ret;
  			}
 
-			if (next_id == args->posix_timers[i].spt.it_id)
+			if (timer_id != next_id) {
+				pr_err("Can't create timers, kernel don't give them consequently\n");
+				return -1;
+			}
+
+			next_id++;
+
+			if (timer_id == args->posix_timers[i].spt.it_id)
  				break;
 
-			ret = sys_timer_delete(next_id);
+			ret = sys_timer_delete(timer_id);
  			if (ret < 0) {
-				pr_err("Can't remove temporaty posix timer 0x%x\n", next_id);
+				pr_err("Can't remove temporaty posix timer 0x%x\n", timer_id);
  				return ret;
  			}
-
-			if ((long)next_id > args->posix_timers[i].spt.it_id) {
-				pr_err("Can't create timers, kernel don't give them consequently\n");
-				return -1;
-			}
  		}
  	}
 
Signature · 2 lines
Best regards, Tikhomirov Pavel
Senior Software Developer, Virtuozzo.
#31 lore raw

Re: [RFD] posix-timers: CRIU woes

In reply to Thomas Gleixner
Quoted 13 lines · Thomas Gleixner
Quoted 11 lines · Pavel Tikhomirov
Quoted 9 lines · Andrey Vagin
Quoted 5 lines

So because of that half thought out user space ABI we are now up the regression creek without a paddle, unless CRIU can accomodate to a different restore mechanism to lift this restriction from the kernel.

If you give us a new API to create timers with specified id-s, we will figure out how to live with it. It isn't good to ask users to update CRIU to work on new kernels, but here are reasons and event improvements for CRIU, so I think it's worth it.

Quoted 5 lines
Quoted 2 lines

I agree, any API to create timers with specified id-s would work for new CRIU versions.

The real question is whether this will cause any upheaval when a new kernel meets a non-updated CRIU stack.

It depends on what you mean by upheaval. We found that CRIU can be stuck in a busy loop with the new changes. I suggest thinking about how to work around this case and make sure that CRIU reports an error. The error should minimize the time that users will need to spend to find the reason and ways to resolve the problem.

One of the ways to fix the problem is to return indexes in a backward direction from INT_MAX to zero. But in the kernel, user indices can be converted back to "normal" values:
kernel_timer_id = INT_MAX - user_timer_id;

I have one idea of how to make these changes without breaking CRIU. CRIU does a few special things. First, it does all timer operations from a thread leader. Second, it calls timer_settime only after creating all timers. Third, it calls timer_delete for the last timer only. Any of these events can be a trigger to switch to the new algo of allocating timer id-s, but new processes allocate indices according to old rules. It seems unfortunate that a real application will create a set of very sparse indices without triggering one of these events.
I don't think that it is worth doing something like this, but if we want to strictly follow the rules, it is the choice.

Quoted 2 lines

You know the UABI regression rules of the kernel...

There is no rule without exceptions... With all pros and cons, we may consider this case as an exception. From our side, we will try to make everything to minimize the impact. Here are steps off the top of my head:
* releasing the criu fix before the kernel release.
* update packages in Linux distros (Debian, Ubuntu, Fedora, and others that we will find).
* send an announcement to the criu mailing list and to users that we know.
* add the error to FAQ.
* create a GitHub issue with a full description.

Thanks,
Andrei

#32 lore raw

Re: [RFD] posix-timers: CRIU woes

Andrey!

Quoted 4 lines · Andrey Vagin
Quoted 2 lines · Thomas Gleixner

You know the UABI regression rules of the kernel...

Quoted 10 lines

There is no rule without exceptions... With all pros and cons, we may consider this case as an exception. From our side, we will try to make everything to minimize the impact. Here are steps off the top of my head:
* releasing the criu fix before the kernel release.
* update packages in Linux distros (Debian, Ubuntu, Fedora, and others that we will find).
* send an announcement to the criu mailing list and to users that we know.
* add the error to FAQ.
* create a GitHub issue with a full description.

Thanks for this plan. After digging deeper I managed to resolve the actual problem I was chasing without changing that ID generator at all.

The main pain point of having to do that lookup from the signal delivery path is gone, which made it trivial to do the fix for the SIG_IGN mess w/o these global lookups too.

Addressing this global ID issues I pointed out becomes therefore an orthogonal issue which we can handle completely independent of the kernel internal problems I'm trying to address.

I still think we should do that for sanity sake, but we can stage that properly without dependencies outside of this particular ABI problem. That makes me way more comfortable as that's something which can be in the worst case reverted without doing any other damage.

Thanks,

tglx

#33 lore raw

Re: [RFD] posix-timers: CRIU woes

In reply to Andrey Vagin

On Wed, May 10, 2023 at 01:16:26AM -0700, Andrey Vagin wrote:
...

Quoted 9 lines

Hi Thomas,

If you give us a new API to create timers with specified id-s, we will figure out how to live with it. It isn't good to ask users to update CRIU to work on new kernels, but here are reasons and event improvements for CRIU, so I think it's worth it.

As for API, we can use one bit of sigevent.sigev_notify to request a timer with a specified id.

Which will do the trick but would look somehow strange I think, since signals are not some how related to timer's ID. Another option might be to use output created_timer_id parameter as an input cookie.

Say we describe input as

struct {
	u32 magic;
	timer_t timer_id;
};

Then if magic doesn't match we use created_timer_id for output only, and otherwise we read timer_id from input and use it. Of course there is a chance that some unitialized memory passed with existing old programs but i think false positive gonna be very-very low if ever. Just IMHO.

Cyrill

#34 lore raw

Re: [patch 02/20] posix-timers: Ensure timer ID search-loop limit is valid

In reply to Thomas Gleixner
Quoted 20 lines · Thomas Gleixner
Quoted 8 lines · Thomas Gleixner
Quoted 3 lines · Thomas Gleixner

More thoughts on this. If we go there and accept the extra page of memory then we can just go all the way and make the xarray per process, actually per signal.

Thinking more about it. The current scheme how timer ID allocation works is really interesting vs. CRIU.

Quoted 6 lines

Assume a process creates/deletes timers frequently. It's not hard to move the next ID close to INT_MAX, i.e. 2G

Now checkpoint that thing and restore it which means to do the create/delete dance to move next ID up to the last one-1. Will only take a couple of hours....

I'm cursing myself for overlooking this back then when the CRIU changes to the timer ID management were made. Why?

Quoted 6 lines

Because that created an ABI which CRIU relies on.

The proper solution for this would be to make it possible to create a timer with a given ID. That's not rocket science, but we need buy in from the CRIU folks. Otherwise we are up the regression creek without a paddle.

Let's go with the proper solution. I will prepare CRIU changes when kernel patches are ready.

Thanks,
Andrei

#35 lore raw

Re: [patch 02/20] posix-timers: Ensure timer ID search-loop limit is valid

In reply to Thomas Gleixner
Quoted 25 lines · Thomas Gleixner
Quoted 24 lines · Thomas Gleixner

On Fri, May 05 2023 at 16:50, Frederic Weisbecker wrote:
So the whole thing works like this:

   start = READ_LOCKLESS(sig->next_id);
   // Enfore that id and start are different to not terminate right away
   id = ~start;

loop:

   if (id == start)
   	goto fail;
   lock()
        id = sig->next_id;                      <-- stable readout
        sig->next_id = (id + 1) & INT_MAX;      <-- prevent going negative

        if (unused_id(id)) {
           add_timer_to_hash(timer, id);
           unlock();
           return id;
        }
   id++;
   unlock();
   goto loop;
Quoted 8 lines
Quoted 2 lines

As the initial lockless readout is guaranteed to be in the positive space, how is that supposed to be looping forever?

Unless you think about the theoretical case of an unlimited number of threads sharing the signal_struct which all concurrently try to allocate a timer id and then releasing it immediately again (to avoid resource limit exhaustion). Theoretically possible, but is this a real concern with a timer ID space of 2G?

I didn't go that far actually, it was just me misunderstanding that loop and especially the (id =~start) part. Now I got it.

I guess the for statement can just be:

for (; start != id; id++)

Quoted 11 lines

I'm sure that it's incredibly hard to exploit this, but what's really bothering me is the hash table itself. The only reason why we have that is CRIU.

The only alternative solution I could come up with is a paritioned xarray where the index space would be segmented for each TGID, i.e.

segment.start = TGID * MAX_TIMERS_PER_PROCESS
segment.end = segment.start + MAX_TIMERS_PER_PROCESS - 1

Quoted 8 lines

where MAX_TIMERS_PER_PROCESS could be a copius 2^16 which would work for both 32bit and 64bit TID limits.

That would avoid the hash table lookups and the related issues, but OTH it would require to allocate one extra page per TGID if the application uses a single posix timer.

Not sure whether that's worth it though.

Not sure either...

Thanks.

#36 lore raw

Re: [patch 02/20] posix-timers: Ensure timer ID search-loop limit is valid

Quoted 8 lines · Frederic Weisbecker
Quoted 5 lines · Thomas Gleixner

That would avoid the hash table lookups and the related issues, but OTH it would require to allocate one extra page per TGID if the application uses a single posix timer.

Not sure whether that's worth it though.

Not sure either...

See my other reply on that...

#37 lore raw

Re: [patch 02/20] posix-timers: Ensure timer ID search-loop limit is valid

In reply to Frederic Weisbecker
Quoted 7 lines · Frederic Weisbecker
Quoted 5 lines · Thomas Gleixner

Unless you think about the theoretical case of an unlimited number of threads sharing the signal_struct which all concurrently try to allocate a timer id and then releasing it immediately again (to avoid resource limit exhaustion). Theoretically possible, but is this a real concern with a timer ID space of 2G?

Quoted 6 lines

I didn't go that far actually, it was just me misunderstanding that loop and especially the (id =~start) part. Now I got it.

I guess the for statement can just be:

for (; start != id; id++)

My brain based compiler complains about uninitialized usage of @id. I'm pretty sure it's rightfully complaining and a real compiler would agree, no?

Thanks,

tglx

#38 lore raw

Re: [patch 02/20] posix-timers: Ensure timer ID search-loop limit is valid

Quoted 11 lines · Thomas Gleixner
Quoted 10 lines · Frederic Weisbecker
Quoted 5 lines · Thomas Gleixner

Unless you think about the theoretical case of an unlimited number of threads sharing the signal_struct which all concurrently try to allocate a timer id and then releasing it immediately again (to avoid resource limit exhaustion). Theoretically possible, but is this a real concern with a timer ID space of 2G?

I didn't go that far actually, it was just me misunderstanding that loop and especially the (id =~start) part. Now I got it.

Quoted 7 lines
Quoted 3 lines

I guess the for statement can just be:

for (; start != id; id++)

My brain based compiler complains about uninitialized usage of @id. I'm pretty sure it's rightfully complaining and a real compiler would agree, no?

*sigh* I should think more before pressing answer these days :)

#39 lore raw

PATCH v2 2/20 posix-timers: Ensure timer ID search-loop limit is valid

posix_timer_add() tries to allocate a posix timer ID by starting from the cached ID which was stored by the last successful allocation.

This is done in a loop searching the ID space for a free slot one by one. The loop has to terminate when the search wrapped around to the starting point.

But that's racy vs. establishing the starting point. That is read out lockless, which leads to the following problem:

CPU0 CPU1
posix_timer_add()
start = sig->posix_timer_id;
lock(hash_lock);
... posix_timer_add()
if (++sig->posix_timer_id < 0)

      			             start = sig->posix_timer_id;
     sig->posix_timer_id = 0;

So CPU1 can observe a negative start value, i.e. -1, and the loop break never happens because the condition can never be true:

if (sig->posix_timer_id == start)

     break;

While this is unlikely to ever turn into an endless loop as the ID space is huge (INT_MAX), the racy read of the start value caught the attention of KCSAN and Dmitry unearthed that incorrectness.

Rewrite it so that all id operations are under the hash lock.

---
V2: Make the loop less hideous.

---
 include/linux/sched/signal.h |    2 +-
 kernel/time/posix-timers.c   |   31 ++++++++++++++++++-------------
 2 files changed, 19 insertions(+), 14 deletions(-)
include/linux/sched/signal.h +1 -1
@@ -135,7 +135,7 @@ struct signal_struct {
 #ifdef CONFIG_POSIX_TIMERS
 
 	/* POSIX.1b Interval Timers */
-	int			posix_timer_id;
+	unsigned int		next_posix_timer_id;
 	struct list_head	posix_timers;
 
 	/* ITIMER_REAL timer for the process */
kernel/time/posix-timers.c +18 -13
@@ -140,25 +140,30 @@ static struct k_itimer *posix_timer_by_i
 static int posix_timer_add(struct k_itimer *timer)
 {
 	struct signal_struct *sig = current->signal;
-	int first_free_id = sig->posix_timer_id;
 	struct hlist_head *head;
-	int ret = -ENOENT;
+	unsigned int cnt, id;
 
-	do {
+	/*
+	 * FIXME: Replace this by a per signal struct xarray once there is
+	 * a plan to handle the resulting CRIU regression gracefully.
+	 */
+	for (cnt = 0; cnt <= INT_MAX; cnt++) {
 		spin_lock(&hash_lock);
-		head = &posix_timers_hashtable[hash(sig, sig->posix_timer_id)];
-		if (!__posix_timers_find(head, sig, sig->posix_timer_id)) {
+		id = sig->next_posix_timer_id;
+
+		/* Write the next ID back. Clamp it to the positive space */
+		sig->next_posix_timer_id = (id + 1) & INT_MAX;
+
+		head = &posix_timers_hashtable[hash(sig, id)];
+		if (!__posix_timers_find(head, sig, id)) {
 			hlist_add_head_rcu(&timer->t_hash, head);
-			ret = sig->posix_timer_id;
+			spin_unlock(&hash_lock);
+			return id;
 		}
-		if (++sig->posix_timer_id < 0)
-			sig->posix_timer_id = 0;
-		if ((sig->posix_timer_id == first_free_id) && (ret == -ENOENT))
-			/* Loop over all possible ids completed */
-			ret = -EAGAIN;
 		spin_unlock(&hash_lock);
-	} while (ret == -ENOENT);
-	return ret;
+	}
+	/* POSIX return code when no timer ID could be allocated */
+	return -EAGAIN;
 }
 
 static inline void unlock_timer(struct k_itimer *timr, unsigned long flags)
maintainer Reviewed-by on v2 2/20
#40 lore raw

Re: [patch v2 02/20] posix-timers: Ensure timer ID search-loop limit is valid

Quoted 29 lines · Thomas Gleixner

posix_timer_add() tries to allocate a posix timer ID by starting from the cached ID which was stored by the last successful allocation.

This is done in a loop searching the ID space for a free slot one by one. The loop has to terminate when the search wrapped around to the starting point.

But that's racy vs. establishing the starting point. That is read out lockless, which leads to the following problem:

CPU0 CPU1
posix_timer_add()
start = sig->posix_timer_id;
lock(hash_lock);
... posix_timer_add()
if (++sig->posix_timer_id < 0)

      			             start = sig->posix_timer_id;
     sig->posix_timer_id = 0;

So CPU1 can observe a negative start value, i.e. -1, and the loop break never happens because the condition can never be true:

if (sig->posix_timer_id == start)

     break;

While this is unlikely to ever turn into an endless loop as the ID space is huge (INT_MAX), the racy read of the start value caught the attention of KCSAN and Dmitry unearthed that incorrectness.

Quoted 5 lines

Rewrite it so that all id operations are under the hash lock.

#41 lore raw

[tip: timers/core] posix-timers: Ensure timer ID search-loop limit is valid

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     cec58cad4614f44b76624f8d62ef771c8725c483
Gitweb:        https://git.kernel.org/tip/cec58cad4614f44b76624f8d62ef771c8725c483
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Thu, 01 Jun 2023 20:58:47 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:36 +02:00

posix-timers: Ensure timer ID search-loop limit is valid

posix_timer_add() tries to allocate a posix timer ID by starting from the cached ID which was stored by the last successful allocation.

This is done in a loop searching the ID space for a free slot one by one. The loop has to terminate when the search wrapped around to the starting point.

But that's racy vs. establishing the starting point. That is read out lockless, which leads to the following problem:

CPU0 CPU1
posix_timer_add()
start = sig->posix_timer_id;
lock(hash_lock);
... posix_timer_add()
if (++sig->posix_timer_id < 0)

      			             start = sig->posix_timer_id;
     sig->posix_timer_id = 0;

So CPU1 can observe a negative start value, i.e. -1, and the loop break never happens because the condition can never be true:

if (sig->posix_timer_id == start)

     break;

While this is unlikely to ever turn into an endless loop as the ID space is huge (INT_MAX), the racy read of the start value caught the attention of KCSAN and Dmitry unearthed that incorrectness.

Rewrite it so that all id operations are under the hash lock.

---
 include/linux/sched/signal.h |  2 +-
 kernel/time/posix-timers.c   | 31 ++++++++++++++++++-------------
 2 files changed, 19 insertions(+), 14 deletions(-)
include/linux/sched/signal.h +1 -1
index 2009926..669e8cf 100644
@@ -135,7 +135,7 @@ struct signal_struct {
 #ifdef CONFIG_POSIX_TIMERS
 
 	/* POSIX.1b Interval Timers */
-	int			posix_timer_id;
+	unsigned int		next_posix_timer_id;
 	struct list_head	posix_timers;
 
 	/* ITIMER_REAL timer for the process */
kernel/time/posix-timers.c +18 -13
index 2d835c2..2c5daeb 100644
@@ -140,25 +140,30 @@ static struct k_itimer *posix_timer_by_id(timer_t id)
 static int posix_timer_add(struct k_itimer *timer)
 {
 	struct signal_struct *sig = current->signal;
-	int first_free_id = sig->posix_timer_id;
 	struct hlist_head *head;
-	int ret = -ENOENT;
+	unsigned int cnt, id;
 
-	do {
+	/*
+	 * FIXME: Replace this by a per signal struct xarray once there is
+	 * a plan to handle the resulting CRIU regression gracefully.
+	 */
+	for (cnt = 0; cnt <= INT_MAX; cnt++) {
 		spin_lock(&hash_lock);
-		head = &posix_timers_hashtable[hash(sig, sig->posix_timer_id)];
-		if (!__posix_timers_find(head, sig, sig->posix_timer_id)) {
+		id = sig->next_posix_timer_id;
+
+		/* Write the next ID back. Clamp it to the positive space */
+		sig->next_posix_timer_id = (id + 1) & INT_MAX;
+
+		head = &posix_timers_hashtable[hash(sig, id)];
+		if (!__posix_timers_find(head, sig, id)) {
 			hlist_add_head_rcu(&timer->t_hash, head);
-			ret = sig->posix_timer_id;
+			spin_unlock(&hash_lock);
+			return id;
 		}
-		if (++sig->posix_timer_id < 0)
-			sig->posix_timer_id = 0;
-		if ((sig->posix_timer_id == first_free_id) && (ret == -ENOENT))
-			/* Loop over all possible ids completed */
-			ret = -EAGAIN;
 		spin_unlock(&hash_lock);
-	} while (ret == -ENOENT);
-	return ret;
+	}
+	/* POSIX return code when no timer ID could be allocated */
+	return -EAGAIN;
 }
 
 static inline void unlock_timer(struct k_itimer *timr, unsigned long flags)
#42 lore raw

[tip: timers/core] posix-timers: Ensure timer ID search-loop limit is valid

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     8ce8849dd1e78dadcee0ec9acbd259d239b7069f
Gitweb:        https://git.kernel.org/tip/8ce8849dd1e78dadcee0ec9acbd259d239b7069f
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Thu, 01 Jun 2023 20:58:47 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:48 +02:00

posix-timers: Ensure timer ID search-loop limit is valid

posix_timer_add() tries to allocate a posix timer ID by starting from the cached ID which was stored by the last successful allocation.

This is done in a loop searching the ID space for a free slot one by one. The loop has to terminate when the search wrapped around to the starting point.

But that's racy vs. establishing the starting point. That is read out lockless, which leads to the following problem:

CPU0 CPU1
posix_timer_add()
start = sig->posix_timer_id;
lock(hash_lock);
... posix_timer_add()
if (++sig->posix_timer_id < 0)

      			             start = sig->posix_timer_id;
     sig->posix_timer_id = 0;

So CPU1 can observe a negative start value, i.e. -1, and the loop break never happens because the condition can never be true:

if (sig->posix_timer_id == start)

     break;

While this is unlikely to ever turn into an endless loop as the ID space is huge (INT_MAX), the racy read of the start value caught the attention of KCSAN and Dmitry unearthed that incorrectness.

Rewrite it so that all id operations are under the hash lock.

---
 include/linux/sched/signal.h |  2 +-
 kernel/time/posix-timers.c   | 31 ++++++++++++++++++-------------
 2 files changed, 19 insertions(+), 14 deletions(-)
include/linux/sched/signal.h +1 -1
index 2009926..669e8cf 100644
@@ -135,7 +135,7 @@ struct signal_struct {
 #ifdef CONFIG_POSIX_TIMERS
 
 	/* POSIX.1b Interval Timers */
-	int			posix_timer_id;
+	unsigned int		next_posix_timer_id;
 	struct list_head	posix_timers;
 
 	/* ITIMER_REAL timer for the process */
kernel/time/posix-timers.c +18 -13
index ed3c4a9..2d6cf93 100644
@@ -140,25 +140,30 @@ static struct k_itimer *posix_timer_by_id(timer_t id)
 static int posix_timer_add(struct k_itimer *timer)
 {
 	struct signal_struct *sig = current->signal;
-	int first_free_id = sig->posix_timer_id;
 	struct hlist_head *head;
-	int ret = -ENOENT;
+	unsigned int cnt, id;
 
-	do {
+	/*
+	 * FIXME: Replace this by a per signal struct xarray once there is
+	 * a plan to handle the resulting CRIU regression gracefully.
+	 */
+	for (cnt = 0; cnt <= INT_MAX; cnt++) {
 		spin_lock(&hash_lock);
-		head = &posix_timers_hashtable[hash(sig, sig->posix_timer_id)];
-		if (!__posix_timers_find(head, sig, sig->posix_timer_id)) {
+		id = sig->next_posix_timer_id;
+
+		/* Write the next ID back. Clamp it to the positive space */
+		sig->next_posix_timer_id = (id + 1) & INT_MAX;
+
+		head = &posix_timers_hashtable[hash(sig, id)];
+		if (!__posix_timers_find(head, sig, id)) {
 			hlist_add_head_rcu(&timer->t_hash, head);
-			ret = sig->posix_timer_id;
+			spin_unlock(&hash_lock);
+			return id;
 		}
-		if (++sig->posix_timer_id < 0)
-			sig->posix_timer_id = 0;
-		if ((sig->posix_timer_id == first_free_id) && (ret == -ENOENT))
-			/* Loop over all possible ids completed */
-			ret = -EAGAIN;
 		spin_unlock(&hash_lock);
-	} while (ret == -ENOENT);
-	return ret;
+	}
+	/* POSIX return code when no timer ID could be allocated */
+	return -EAGAIN;
 }
 
 static inline void unlock_timer(struct k_itimer *timr, unsigned long flags)
#43 lore raw

PATCH 3/20 posix-timers: Clarify timer_wait_running() comment

In reply to Thomas Gleixner

Explain it better and add the CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y aspect for completeness.

---
 kernel/time/posix-timers.c |   16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)
kernel/time/posix-timers.c +12 -4
@@ -835,10 +835,18 @@ static void common_timer_wait_running(st
 }
 
 /*
- * On PREEMPT_RT this prevent priority inversion against softirq kthread in
- * case it gets preempted while executing a timer callback. See comments in
- * hrtimer_cancel_wait_running. For PREEMPT_RT=n this just results in a
- * cpu_relax().
+ * On PREEMPT_RT this prevents priority inversion and a potential livelock
+ * against the ksoftirqd thread in case that ksoftirqd gets preempted while
+ * executing a hrtimer callback.
+ *
+ * See the comments in hrtimer_cancel_wait_running(). For PREEMPT_RT=n this
+ * just results in a cpu_relax().
+ *
+ * For POSIX CPU timers with CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n this is
+ * just a cpu_relax(). With CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y this
+ * prevents spinning on an eventually scheduled out task and a livelock
+ * when the task which tries to delete or disarm the timer has preempted
+ * the task which runs the expiry in task work context.
  */
 static struct k_itimer *timer_wait_running(struct k_itimer *timer,
 					   unsigned long *flags)
#45 lore raw

[tip: timers/core] posix-timers: Clarify timer_wait_running() comment

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     07ca14d623d8b0e6c4a0d3de4ca8a8ea35a85470
Gitweb:        https://git.kernel.org/tip/07ca14d623d8b0e6c4a0d3de4ca8a8ea35a85470
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:00 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:37 +02:00

posix-timers: Clarify timer_wait_running() comment

Explain it better and add the CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y aspect for completeness.

---
 kernel/time/posix-timers.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)
kernel/time/posix-timers.c +12 -4
index 2c5daeb..4017533 100644
@@ -836,10 +836,18 @@ static void common_timer_wait_running(struct k_itimer *timer)
 }
 
 /*
- * On PREEMPT_RT this prevent priority inversion against softirq kthread in
- * case it gets preempted while executing a timer callback. See comments in
- * hrtimer_cancel_wait_running. For PREEMPT_RT=n this just results in a
- * cpu_relax().
+ * On PREEMPT_RT this prevents priority inversion and a potential livelock
+ * against the ksoftirqd thread in case that ksoftirqd gets preempted while
+ * executing a hrtimer callback.
+ *
+ * See the comments in hrtimer_cancel_wait_running(). For PREEMPT_RT=n this
+ * just results in a cpu_relax().
+ *
+ * For POSIX CPU timers with CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n this is
+ * just a cpu_relax(). With CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y this
+ * prevents spinning on an eventually scheduled out task and a livelock
+ * when the task which tries to delete or disarm the timer has preempted
+ * the task which runs the expiry in task work context.
  */
 static struct k_itimer *timer_wait_running(struct k_itimer *timer,
 					   unsigned long *flags)
#46 lore raw

[tip: timers/core] posix-timers: Clarify timer_wait_running() comment

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     7d9909026645064cd31b20cee5939a0f72282261
Gitweb:        https://git.kernel.org/tip/7d9909026645064cd31b20cee5939a0f72282261
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:00 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:48 +02:00

posix-timers: Clarify timer_wait_running() comment

Explain it better and add the CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y aspect for completeness.

---
 kernel/time/posix-timers.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)
kernel/time/posix-timers.c +12 -4
index 2d6cf93..0c61f29 100644
@@ -836,10 +836,18 @@ static void common_timer_wait_running(struct k_itimer *timer)
 }
 
 /*
- * On PREEMPT_RT this prevent priority inversion against softirq kthread in
- * case it gets preempted while executing a timer callback. See comments in
- * hrtimer_cancel_wait_running. For PREEMPT_RT=n this just results in a
- * cpu_relax().
+ * On PREEMPT_RT this prevents priority inversion and a potential livelock
+ * against the ksoftirqd thread in case that ksoftirqd gets preempted while
+ * executing a hrtimer callback.
+ *
+ * See the comments in hrtimer_cancel_wait_running(). For PREEMPT_RT=n this
+ * just results in a cpu_relax().
+ *
+ * For POSIX CPU timers with CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n this is
+ * just a cpu_relax(). With CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y this
+ * prevents spinning on an eventually scheduled out task and a livelock
+ * when the task which tries to delete or disarm the timer has preempted
+ * the task which runs the expiry in task work context.
  */
 static struct k_itimer *timer_wait_running(struct k_itimer *timer,
 					   unsigned long *flags)
#47 lore raw

PATCH 4/20 posix-timers: Cleanup comments about timer ID tracking

In reply to Thomas Gleixner

Describe the hash table properly and remove the IDR leftover comments.

---
 kernel/time/posix-timers.c |   28 ++++++++--------------------
 1 file changed, 8 insertions(+), 20 deletions(-)
kernel/time/posix-timers.c +8 -20
@@ -35,20 +35,17 @@
 #include "timekeeping.h"
 #include "posix-timers.h"
 
-/*
- * Management arrays for POSIX timers. Timers are now kept in static hash table
- * with 512 entries.
- * Timer ids are allocated by local routine, which selects proper hash head by
- * key, constructed from current->signal address and per signal struct counter.
- * This keeps timer ids unique per process, but now they can intersect between
- * processes.
- */
+static struct kmem_cache *posix_timers_cache;
 
 /*
- * Lets keep our timers in a slab cache :-)
+ * Timers are managed in a hash table for lockless lookup. The hash key is
+ * constructed from current::signal and the timer ID and the timer is
+ * matched against current::signal and the timer ID when walking the hash
+ * bucket list.
+ *
+ * This allows checkpoint/restore to reconstruct the exact timer IDs for
+ * a process.
  */
-static struct kmem_cache *posix_timers_cache;
-
 static DEFINE_HASHTABLE(posix_timers_hashtable, 9);
 static DEFINE_SPINLOCK(hash_lock);
 
@@ -66,15 +63,6 @@ static const struct k_clock clock_realti
 #endif
 
 /*
- * The timer ID is turned into a timer address by idr_find().
- * Verifying a valid ID consists of:
- *
- * a) checking that idr_find() returns other than -1.
- * b) checking that the timer id matches the one in the timer itself.
- * c) that the timer owner is in the callers thread group.
- */
-
-/*
  * CLOCKs: The POSIX standard calls for a couple of clocks and allows us
  *	    to implement others.  This structure defines the various
  *	    clocks.
#49 lore raw

[tip: timers/core] posix-timers: Cleanup comments about timer ID tracking

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     10338fd15894827fefc2bf28ed75c81ba1dbfceb
Gitweb:        https://git.kernel.org/tip/10338fd15894827fefc2bf28ed75c81ba1dbfceb
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:01 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:37 +02:00

posix-timers: Cleanup comments about timer ID tracking

Describe the hash table properly and remove the IDR leftover comments.

---
 kernel/time/posix-timers.c | 28 ++++++++--------------------
 1 file changed, 8 insertions(+), 20 deletions(-)
kernel/time/posix-timers.c +8 -20
index 4017533..355c6f4 100644
@@ -35,20 +35,17 @@
 #include "timekeeping.h"
 #include "posix-timers.h"
 
-/*
- * Management arrays for POSIX timers. Timers are now kept in static hash table
- * with 512 entries.
- * Timer ids are allocated by local routine, which selects proper hash head by
- * key, constructed from current->signal address and per signal struct counter.
- * This keeps timer ids unique per process, but now they can intersect between
- * processes.
- */
+static struct kmem_cache *posix_timers_cache;
 
 /*
- * Lets keep our timers in a slab cache :-)
+ * Timers are managed in a hash table for lockless lookup. The hash key is
+ * constructed from current::signal and the timer ID and the timer is
+ * matched against current::signal and the timer ID when walking the hash
+ * bucket list.
+ *
+ * This allows checkpoint/restore to reconstruct the exact timer IDs for
+ * a process.
  */
-static struct kmem_cache *posix_timers_cache;
-
 static DEFINE_HASHTABLE(posix_timers_hashtable, 9);
 static DEFINE_SPINLOCK(hash_lock);
 
@@ -66,15 +63,6 @@ static const struct k_clock clock_realtime, clock_monotonic;
 #endif
 
 /*
- * The timer ID is turned into a timer address by idr_find().
- * Verifying a valid ID consists of:
- *
- * a) checking that idr_find() returns other than -1.
- * b) checking that the timer id matches the one in the timer itself.
- * c) that the timer owner is in the callers thread group.
- */
-
-/*
  * CLOCKs: The POSIX standard calls for a couple of clocks and allows us
  *	    to implement others.  This structure defines the various
  *	    clocks.
#50 lore raw

[tip: timers/core] posix-timers: Cleanup comments about timer ID tracking

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     8d44b958a1a088195524c884f0437660e0522380
Gitweb:        https://git.kernel.org/tip/8d44b958a1a088195524c884f0437660e0522380
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:01 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:48 +02:00

posix-timers: Cleanup comments about timer ID tracking

Describe the hash table properly and remove the IDR leftover comments.

---
 kernel/time/posix-timers.c | 28 ++++++++--------------------
 1 file changed, 8 insertions(+), 20 deletions(-)
kernel/time/posix-timers.c +8 -20
index 0c61f29..79909a2 100644
@@ -35,20 +35,17 @@
 #include "timekeeping.h"
 #include "posix-timers.h"
 
-/*
- * Management arrays for POSIX timers. Timers are now kept in static hash table
- * with 512 entries.
- * Timer ids are allocated by local routine, which selects proper hash head by
- * key, constructed from current->signal address and per signal struct counter.
- * This keeps timer ids unique per process, but now they can intersect between
- * processes.
- */
+static struct kmem_cache *posix_timers_cache;
 
 /*
- * Lets keep our timers in a slab cache :-)
+ * Timers are managed in a hash table for lockless lookup. The hash key is
+ * constructed from current::signal and the timer ID and the timer is
+ * matched against current::signal and the timer ID when walking the hash
+ * bucket list.
+ *
+ * This allows checkpoint/restore to reconstruct the exact timer IDs for
+ * a process.
  */
-static struct kmem_cache *posix_timers_cache;
-
 static DEFINE_HASHTABLE(posix_timers_hashtable, 9);
 static DEFINE_SPINLOCK(hash_lock);
 
@@ -66,15 +63,6 @@ static const struct k_clock clock_realtime, clock_monotonic;
 #endif
 
 /*
- * The timer ID is turned into a timer address by idr_find().
- * Verifying a valid ID consists of:
- *
- * a) checking that idr_find() returns other than -1.
- * b) checking that the timer id matches the one in the timer itself.
- * c) that the timer owner is in the callers thread group.
- */
-
-/*
  * CLOCKs: The POSIX standard calls for a couple of clocks and allows us
  *	    to implement others.  This structure defines the various
  *	    clocks.
#51 lore raw

PATCH 5/20 posix-timers: Add comments about timer lookup

In reply to Thomas Gleixner

Document how the timer ID validation in the hash table works.

---
 kernel/time/posix-timers.c |   39 ++++++++++++++++++++++++++++++++-------
 1 file changed, 32 insertions(+), 7 deletions(-)
kernel/time/posix-timers.c +32 -7
@@ -505,6 +505,12 @@ static int do_timer_create(clockid_t whi
 		return -EAGAIN;
 
 	spin_lock_init(&new_timer->it_lock);
+
+	/*
+	 * Add the timer to the hash table. The timer is not yet valid
+	 * because new_timer::it_signal is still NULL. The timer id is also
+	 * not yet visible to user space.
+	 */
 	new_timer_id = posix_timer_add(new_timer);
 	if (new_timer_id < 0) {
 		error = new_timer_id;
@@ -550,6 +556,7 @@ static int do_timer_create(clockid_t whi
 		goto out;
 
 	spin_lock_irq(&current->sighand->siglock);
+	/* This makes the timer valid in the hash table */
 	new_timer->it_signal = current->signal;
 	list_add(&new_timer->list, &current->signal->posix_timers);
 	spin_unlock_irq(&current->sighand->siglock);
@@ -596,13 +603,6 @@ COMPAT_SYSCALL_DEFINE3(timer_create, clo
 }
 #endif
 
-/*
- * Locking issues: We need to protect the result of the id look up until
- * we get the timer locked down so it is not deleted under us.  The
- * removal is done under the idr spinlock so we use that here to bridge
- * the find to the timer lock.  To avoid a dead lock, the timer id MUST
- * be release with out holding the timer lock.
- */
 static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags)
 {
 	struct k_itimer *timr;
@@ -614,10 +614,35 @@ static struct k_itimer *__lock_timer(tim
 	if ((unsigned long long)timer_id > INT_MAX)
 		return NULL;
 
+	/*
+	 * The hash lookup and the timers are RCU protected.
+	 *
+	 * Timers are added to the hash in invalid state where
+	 * timr::it_signal == NULL. timer::it_signal is only set after the
+	 * rest of the initialization succeeded.
+	 *
+	 * Timer destruction happens in steps:
+	 *  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
+	 *
+	 * Holding rcu_read_lock() accross the lookup ensures that
+	 * the timer cannot be freed.
+	 *
+	 * The lookup validates locklessly that timr::it_signal ==
+	 * current::it_signal and timr::it_id == @timer_id. timr::it_id
+	 * can't change, but timr::it_signal becomes NULL during
+	 * destruction.
+	 */
 	rcu_read_lock();
 	timr = posix_timer_by_id(timer_id);
 	if (timr) {
 		spin_lock_irqsave(&timr->it_lock, *flags);
+		/*
+		 * Validate under timr::it_lock that timr::it_signal is
+		 * still valid. Pairs with #1 above.
+		 */
 		if (timr->it_signal == current->signal) {
 			rcu_read_unlock();
 			return timr;
#53 lore raw

[tip: timers/core] posix-timers: Add comments about timer lookup

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     46c7863e061f64c96745003f699912667c2a1bd6
Gitweb:        https://git.kernel.org/tip/46c7863e061f64c96745003f699912667c2a1bd6
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:03 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:37 +02:00

posix-timers: Add comments about timer lookup

Document how the timer ID validation in the hash table works.

---
 kernel/time/posix-timers.c | 39 ++++++++++++++++++++++++++++++-------
 1 file changed, 32 insertions(+), 7 deletions(-)
kernel/time/posix-timers.c +32 -7
index 355c6f4..f6650c1 100644
@@ -506,6 +506,12 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
 		return -EAGAIN;
 
 	spin_lock_init(&new_timer->it_lock);
+
+	/*
+	 * Add the timer to the hash table. The timer is not yet valid
+	 * because new_timer::it_signal is still NULL. The timer id is also
+	 * not yet visible to user space.
+	 */
 	new_timer_id = posix_timer_add(new_timer);
 	if (new_timer_id < 0) {
 		error = new_timer_id;
@@ -551,6 +557,7 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
 		goto out;
 
 	spin_lock_irq(&current->sighand->siglock);
+	/* This makes the timer valid in the hash table */
 	new_timer->it_signal = current->signal;
 	list_add(&new_timer->list, &current->signal->posix_timers);
 	spin_unlock_irq(&current->sighand->siglock);
@@ -597,13 +604,6 @@ COMPAT_SYSCALL_DEFINE3(timer_create, clockid_t, which_clock,
 }
 #endif
 
-/*
- * Locking issues: We need to protect the result of the id look up until
- * we get the timer locked down so it is not deleted under us.  The
- * removal is done under the idr spinlock so we use that here to bridge
- * the find to the timer lock.  To avoid a dead lock, the timer id MUST
- * be release with out holding the timer lock.
- */
 static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags)
 {
 	struct k_itimer *timr;
@@ -615,10 +615,35 @@ static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags)
 	if ((unsigned long long)timer_id > INT_MAX)
 		return NULL;
 
+	/*
+	 * The hash lookup and the timers are RCU protected.
+	 *
+	 * Timers are added to the hash in invalid state where
+	 * timr::it_signal == NULL. timer::it_signal is only set after the
+	 * rest of the initialization succeeded.
+	 *
+	 * Timer destruction happens in steps:
+	 *  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
+	 *
+	 * Holding rcu_read_lock() accross the lookup ensures that
+	 * the timer cannot be freed.
+	 *
+	 * The lookup validates locklessly that timr::it_signal ==
+	 * current::it_signal and timr::it_id == @timer_id. timr::it_id
+	 * can't change, but timr::it_signal becomes NULL during
+	 * destruction.
+	 */
 	rcu_read_lock();
 	timr = posix_timer_by_id(timer_id);
 	if (timr) {
 		spin_lock_irqsave(&timr->it_lock, *flags);
+		/*
+		 * Validate under timr::it_lock that timr::it_signal is
+		 * still valid. Pairs with #1 above.
+		 */
 		if (timr->it_signal == current->signal) {
 			rcu_read_unlock();
 			return timr;
#54 lore raw

[tip: timers/core] posix-timers: Add comments about timer lookup

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     ae88967d71f1b4ffb6e48043993d37a106da8109
Gitweb:        https://git.kernel.org/tip/ae88967d71f1b4ffb6e48043993d37a106da8109
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:03 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:49 +02:00

posix-timers: Add comments about timer lookup

Document how the timer ID validation in the hash table works.

---
 kernel/time/posix-timers.c | 39 ++++++++++++++++++++++++++++++-------
 1 file changed, 32 insertions(+), 7 deletions(-)
kernel/time/posix-timers.c +32 -7
index 79909a2..d7890ac 100644
@@ -506,6 +506,12 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
 		return -EAGAIN;
 
 	spin_lock_init(&new_timer->it_lock);
+
+	/*
+	 * Add the timer to the hash table. The timer is not yet valid
+	 * because new_timer::it_signal is still NULL. The timer id is also
+	 * not yet visible to user space.
+	 */
 	new_timer_id = posix_timer_add(new_timer);
 	if (new_timer_id < 0) {
 		error = new_timer_id;
@@ -551,6 +557,7 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
 		goto out;
 
 	spin_lock_irq(&current->sighand->siglock);
+	/* This makes the timer valid in the hash table */
 	new_timer->it_signal = current->signal;
 	list_add(&new_timer->list, &current->signal->posix_timers);
 	spin_unlock_irq(&current->sighand->siglock);
@@ -597,13 +604,6 @@ COMPAT_SYSCALL_DEFINE3(timer_create, clockid_t, which_clock,
 }
 #endif
 
-/*
- * Locking issues: We need to protect the result of the id look up until
- * we get the timer locked down so it is not deleted under us.  The
- * removal is done under the idr spinlock so we use that here to bridge
- * the find to the timer lock.  To avoid a dead lock, the timer id MUST
- * be release with out holding the timer lock.
- */
 static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags)
 {
 	struct k_itimer *timr;
@@ -615,10 +615,35 @@ static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags)
 	if ((unsigned long long)timer_id > INT_MAX)
 		return NULL;
 
+	/*
+	 * The hash lookup and the timers are RCU protected.
+	 *
+	 * Timers are added to the hash in invalid state where
+	 * timr::it_signal == NULL. timer::it_signal is only set after the
+	 * rest of the initialization succeeded.
+	 *
+	 * Timer destruction happens in steps:
+	 *  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
+	 *
+	 * Holding rcu_read_lock() accross the lookup ensures that
+	 * the timer cannot be freed.
+	 *
+	 * The lookup validates locklessly that timr::it_signal ==
+	 * current::it_signal and timr::it_id == @timer_id. timr::it_id
+	 * can't change, but timr::it_signal becomes NULL during
+	 * destruction.
+	 */
 	rcu_read_lock();
 	timr = posix_timer_by_id(timer_id);
 	if (timr) {
 		spin_lock_irqsave(&timr->it_lock, *flags);
+		/*
+		 * Validate under timr::it_lock that timr::it_signal is
+		 * still valid. Pairs with #1 above.
+		 */
 		if (timr->it_signal == current->signal) {
 			rcu_read_unlock();
 			return timr;
#55 lore raw

PATCH 6/20 posix-timers: Annotate concurrent access to k_itimer::it_signal

In reply to Thomas Gleixner

k_itimer::it_signal is read lockless in the RCU protected hash lookup, but it can be written concurrently in the timer_create() and timer_delete() path. Annotate these places with READ_ONCE() and WRITE_ONCE()

---
 kernel/time/posix-timers.c |   14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
kernel/time/posix-timers.c +7 -7
@@ -109,9 +109,9 @@ static struct k_itimer *__posix_timers_f
 {
 	struct k_itimer *timer;
 
-	hlist_for_each_entry_rcu(timer, head, t_hash,
-				 lockdep_is_held(&hash_lock)) {
-		if ((timer->it_signal == sig) && (timer->it_id == id))
+	hlist_for_each_entry_rcu(timer, head, t_hash, lockdep_is_held(&hash_lock)) {
+		/* timer->it_signal can be set concurrently */
+		if ((READ_ONCE(timer->it_signal) == sig) && (timer->it_id == id))
 			return timer;
 	}
 	return NULL;
@@ -557,7 +557,7 @@ static int do_timer_create(clockid_t whi
 
 	spin_lock_irq(&current->sighand->siglock);
 	/* This makes the timer valid in the hash table */
-	new_timer->it_signal = current->signal;
+	WRITE_ONCE(new_timer->it_signal, current->signal);
 	list_add(&new_timer->list, &current->signal->posix_timers);
 	spin_unlock_irq(&current->sighand->siglock);
 
@@ -1051,10 +1051,10 @@ SYSCALL_DEFINE1(timer_delete, timer_t, t
 	list_del(&timer->list);
 	spin_unlock(&current->sighand->siglock);
 	/*
-	 * This keeps any tasks waiting on the spin lock from thinking
-	 * they got something (see the lock code above).
+	 * A concurrent lookup could check timer::it_signal lockless. It
+	 * will reevaluate with timer::it_lock held and observe the NULL.
 	 */
-	timer->it_signal = NULL;
+	WRITE_ONCE(timer->it_signal, NULL);
 
 	unlock_timer(timer, flags);
 	release_posix_timer(timer, IT_ID_SET);
maintainer Reviewed-by on v1 6/20
#56 lore raw

Re: [patch 06/20] posix-timers: Annotate concurrent access to k_itimer::it_signal

Quoted 5 lines · Thomas Gleixner

k_itimer::it_signal is read lockless in the RCU protected hash lookup, but it can be written concurrently in the timer_create() and timer_delete() path. Annotate these places with READ_ONCE() and WRITE_ONCE()

#57 lore raw

[tip: timers/core] posix-timers: Annotate concurrent access to k_itimer:: It_signal

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     d69e873ee50459e3b77019263cf95236f063a97b
Gitweb:        https://git.kernel.org/tip/d69e873ee50459e3b77019263cf95236f063a97b
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:05 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:37 +02:00

posix-timers: Annotate concurrent access to k_itimer:: It_signal

k_itimer::it_signal is read lockless in the RCU protected hash lookup, but it can be written concurrently in the timer_create() and timer_delete() path. Annotate these places with READ_ONCE() and WRITE_ONCE()

---
 kernel/time/posix-timers.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
kernel/time/posix-timers.c +7 -7
index f6650c1..4333334 100644
@@ -109,9 +109,9 @@ static struct k_itimer *__posix_timers_find(struct hlist_head *head,
 {
 	struct k_itimer *timer;
 
-	hlist_for_each_entry_rcu(timer, head, t_hash,
-				 lockdep_is_held(&hash_lock)) {
-		if ((timer->it_signal == sig) && (timer->it_id == id))
+	hlist_for_each_entry_rcu(timer, head, t_hash, lockdep_is_held(&hash_lock)) {
+		/* timer->it_signal can be set concurrently */
+		if ((READ_ONCE(timer->it_signal) == sig) && (timer->it_id == id))
 			return timer;
 	}
 	return NULL;
@@ -558,7 +558,7 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
 
 	spin_lock_irq(&current->sighand->siglock);
 	/* This makes the timer valid in the hash table */
-	new_timer->it_signal = current->signal;
+	WRITE_ONCE(new_timer->it_signal, current->signal);
 	list_add(&new_timer->list, &current->signal->posix_timers);
 	spin_unlock_irq(&current->sighand->siglock);
 
@@ -1052,10 +1052,10 @@ retry_delete:
 	list_del(&timer->list);
 	spin_unlock(&current->sighand->siglock);
 	/*
-	 * This keeps any tasks waiting on the spin lock from thinking
-	 * they got something (see the lock code above).
+	 * A concurrent lookup could check timer::it_signal lockless. It
+	 * will reevaluate with timer::it_lock held and observe the NULL.
 	 */
-	timer->it_signal = NULL;
+	WRITE_ONCE(timer->it_signal, NULL);
 
 	unlock_timer(timer, flags);
 	release_posix_timer(timer, IT_ID_SET);
#58 lore raw

[tip: timers/core] posix-timers: Annotate concurrent access to k_itimer:: It_signal

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     028cf5eaa12846c4e32104132ff70ca1cd6f5943
Gitweb:        https://git.kernel.org/tip/028cf5eaa12846c4e32104132ff70ca1cd6f5943
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:05 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:49 +02:00

posix-timers: Annotate concurrent access to k_itimer:: It_signal

k_itimer::it_signal is read lockless in the RCU protected hash lookup, but it can be written concurrently in the timer_create() and timer_delete() path. Annotate these places with READ_ONCE() and WRITE_ONCE()

---
 kernel/time/posix-timers.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
kernel/time/posix-timers.c +7 -7
index d7890ac..de3fca8 100644
@@ -109,9 +109,9 @@ static struct k_itimer *__posix_timers_find(struct hlist_head *head,
 {
 	struct k_itimer *timer;
 
-	hlist_for_each_entry_rcu(timer, head, t_hash,
-				 lockdep_is_held(&hash_lock)) {
-		if ((timer->it_signal == sig) && (timer->it_id == id))
+	hlist_for_each_entry_rcu(timer, head, t_hash, lockdep_is_held(&hash_lock)) {
+		/* timer->it_signal can be set concurrently */
+		if ((READ_ONCE(timer->it_signal) == sig) && (timer->it_id == id))
 			return timer;
 	}
 	return NULL;
@@ -558,7 +558,7 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
 
 	spin_lock_irq(&current->sighand->siglock);
 	/* This makes the timer valid in the hash table */
-	new_timer->it_signal = current->signal;
+	WRITE_ONCE(new_timer->it_signal, current->signal);
 	list_add(&new_timer->list, &current->signal->posix_timers);
 	spin_unlock_irq(&current->sighand->siglock);
 
@@ -1052,10 +1052,10 @@ retry_delete:
 	list_del(&timer->list);
 	spin_unlock(&current->sighand->siglock);
 	/*
-	 * This keeps any tasks waiting on the spin lock from thinking
-	 * they got something (see the lock code above).
+	 * A concurrent lookup could check timer::it_signal lockless. It
+	 * will reevaluate with timer::it_lock held and observe the NULL.
 	 */
-	timer->it_signal = NULL;
+	WRITE_ONCE(timer->it_signal, NULL);
 
 	unlock_timer(timer, flags);
 	release_posix_timer(timer, IT_ID_SET);
#59 lore raw

PATCH 7/20 posix-timers: Set k_itimer::it_signal to NULL on exit()

In reply to Thomas Gleixner

Technically it's not required to set k_itimer::it_signal to NULL on exit() because there is no other thread anymore which could lookup the timer concurrently.

Set it to NULL for consistency sake and add a comment to that effect.

---
 kernel/time/posix-timers.c |    8 ++++++++
 1 file changed, 8 insertions(+)
kernel/time/posix-timers.c +8 -0
@@ -1107,6 +1107,14 @@ static void itimer_delete(struct k_itime
 	}
 	list_del(&timer->list);
 
+	/*
+	 * Setting timer::it_signal to NULL is technically not required
+	 * here as nothing can access the timer anymore legitimately via
+	 * the hash table. Set it to NULL nevertheless so that all deletion
+	 * paths are consistent.
+	 */
+	WRITE_ONCE(timer->it_signal, NULL);
+
 	spin_unlock_irqrestore(&timer->it_lock, flags);
 	release_posix_timer(timer, IT_ID_SET);
 }
maintainer Reviewed-by on v1 7/20
#60 lore raw

Re: [patch 07/20] posix-timers: Set k_itimer::it_signal to NULL on exit()

Quoted 7 lines · Thomas Gleixner

Technically it's not required to set k_itimer::it_signal to NULL on exit() because there is no other thread anymore which could lookup the timer concurrently.

Set it to NULL for consistency sake and add a comment to that effect.

#61 lore raw

[tip: timers/core] posix-timers: Set k_itimer:: It_signal to NULL on exit()

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     515eeda177b797d9a7d0c496739f0bdc02c73093
Gitweb:        https://git.kernel.org/tip/515eeda177b797d9a7d0c496739f0bdc02c73093
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:06 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:37 +02:00

posix-timers: Set k_itimer:: It_signal to NULL on exit()

Technically it's not required to set k_itimer::it_signal to NULL on exit() because there is no other thread anymore which could lookup the timer concurrently.

Set it to NULL for consistency sake and add a comment to that effect.

---
 kernel/time/posix-timers.c | 8 ++++++++
 1 file changed, 8 insertions(+)
kernel/time/posix-timers.c +8 -0
index 4333334..e8d877f 100644
@@ -1101,6 +1101,14 @@ retry_delete:
 	}
 	list_del(&timer->list);
 
+	/*
+	 * Setting timer::it_signal to NULL is technically not required
+	 * here as nothing can access the timer anymore legitimately via
+	 * the hash table. Set it to NULL nevertheless so that all deletion
+	 * paths are consistent.
+	 */
+	WRITE_ONCE(timer->it_signal, NULL);
+
 	spin_unlock_irqrestore(&timer->it_lock, flags);
 	release_posix_timer(timer, IT_ID_SET);
 }
#62 lore raw

[tip: timers/core] posix-timers: Set k_itimer:: It_signal to NULL on exit()

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     72786ff23d5acb7bf3e2535831b2f1dc55c7f44e
Gitweb:        https://git.kernel.org/tip/72786ff23d5acb7bf3e2535831b2f1dc55c7f44e
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:06 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:49 +02:00

posix-timers: Set k_itimer:: It_signal to NULL on exit()

Technically it's not required to set k_itimer::it_signal to NULL on exit() because there is no other thread anymore which could lookup the timer concurrently.

Set it to NULL for consistency sake and add a comment to that effect.

---
 kernel/time/posix-timers.c | 8 ++++++++
 1 file changed, 8 insertions(+)
kernel/time/posix-timers.c +8 -0
index de3fca8..c1b77c5 100644
@@ -1101,6 +1101,14 @@ retry_delete:
 	}
 	list_del(&timer->list);
 
+	/*
+	 * Setting timer::it_signal to NULL is technically not required
+	 * here as nothing can access the timer anymore legitimately via
+	 * the hash table. Set it to NULL nevertheless so that all deletion
+	 * paths are consistent.
+	 */
+	WRITE_ONCE(timer->it_signal, NULL);
+
 	spin_unlock_irqrestore(&timer->it_lock, flags);
 	release_posix_timer(timer, IT_ID_SET);
 }
#63 lore raw

PATCH 8/20 posix-timers: Remove pointless irqsafe from hash_lock

In reply to Thomas Gleixner

All usage of hash_lock is in thread context. No point in using spin_lock_irqsave()/irqrestore() for a single usage site.

---
 kernel/time/posix-timers.c |    5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
kernel/time/posix-timers.c +2 -3
@@ -470,10 +470,9 @@ static void k_itimer_rcu_free(struct rcu
 static void release_posix_timer(struct k_itimer *tmr, int it_id_set)
 {
 	if (it_id_set) {
-		unsigned long flags;
-		spin_lock_irqsave(&hash_lock, flags);
+		spin_lock(&hash_lock, flags);
 		hlist_del_rcu(&tmr->t_hash);
-		spin_unlock_irqrestore(&hash_lock, flags);
+		spin_unlock(&hash_lock, flags);
 	}
 	put_pid(tmr->it_pid);
 	sigqueue_free(tmr->sigq);
#65 lore raw

[tip: timers/core] posix-timers: Remove pointless irqsafe from hash_lock

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     032c960ac9f3d64487423e0e01a0fb7327509595
Gitweb:        https://git.kernel.org/tip/032c960ac9f3d64487423e0e01a0fb7327509595
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:08 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:37 +02:00

posix-timers: Remove pointless irqsafe from hash_lock

All usage of hash_lock is in thread context. No point in using spin_lock_irqsave()/irqrestore() for a single usage site.

---
 kernel/time/posix-timers.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
kernel/time/posix-timers.c +2 -3
index e8d877f..d8d4cdf 100644
@@ -471,10 +471,9 @@ static void k_itimer_rcu_free(struct rcu_head *head)
 static void release_posix_timer(struct k_itimer *tmr, int it_id_set)
 {
 	if (it_id_set) {
-		unsigned long flags;
-		spin_lock_irqsave(&hash_lock, flags);
+		spin_lock(&hash_lock, flags);
 		hlist_del_rcu(&tmr->t_hash);
-		spin_unlock_irqrestore(&hash_lock, flags);
+		spin_unlock(&hash_lock, flags);
 	}
 	put_pid(tmr->it_pid);
 	sigqueue_free(tmr->sigq);
#66 lore raw

[tip: timers/core] posix-timers: Remove pointless irqsafe from hash_lock

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     11fbe6cd41210c7b5173257158a22e11e225622d
Gitweb:        https://git.kernel.org/tip/11fbe6cd41210c7b5173257158a22e11e225622d
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:08 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:49 +02:00

posix-timers: Remove pointless irqsafe from hash_lock

All usage of hash_lock is in thread context. No point in using spin_lock_irqsave()/irqrestore() for a single usage site.

---
 kernel/time/posix-timers.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
kernel/time/posix-timers.c +2 -3
index c1b77c5..ed7d260 100644
@@ -471,10 +471,9 @@ static void k_itimer_rcu_free(struct rcu_head *head)
 static void release_posix_timer(struct k_itimer *tmr, int it_id_set)
 {
 	if (it_id_set) {
-		unsigned long flags;
-		spin_lock_irqsave(&hash_lock, flags);
+		spin_lock(&hash_lock, flags);
 		hlist_del_rcu(&tmr->t_hash);
-		spin_unlock_irqrestore(&hash_lock, flags);
+		spin_unlock(&hash_lock, flags);
 	}
 	put_pid(tmr->it_pid);
 	sigqueue_free(tmr->sigq);
#67 lore raw

PATCH 9/20 posix-timers: Split release_posix_timers()

In reply to Thomas Gleixner

release_posix_timers() is called for cleaning up both hashed and unhashed timers. The cases are differentiated by an argument and the usage is hideous.

Seperate the actual free path out and use it for unhashed timers. Provide a function for hashed timers.

No functional change.

---
 kernel/time/posix-timers.c |   31 +++++++++++++++----------------
 1 file changed, 15 insertions(+), 16 deletions(-)
kernel/time/posix-timers.c +15 -16
@@ -465,20 +465,21 @@ static void k_itimer_rcu_free(struct rcu
 	kmem_cache_free(posix_timers_cache, tmr);
 }
 
-#define IT_ID_SET	1
-#define IT_ID_NOT_SET	0
-static void release_posix_timer(struct k_itimer *tmr, int it_id_set)
-{
-	if (it_id_set) {
-		spin_lock(&hash_lock, flags);
-		hlist_del_rcu(&tmr->t_hash);
-		spin_unlock(&hash_lock, flags);
-	}
+static void posix_timer_free(struct k_itimer *tmr)
+{
 	put_pid(tmr->it_pid);
 	sigqueue_free(tmr->sigq);
 	call_rcu(&tmr->rcu, k_itimer_rcu_free);
 }
 
+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);
+}
+
 static int common_timer_create(struct k_itimer *new_timer)
 {
 	hrtimer_init(&new_timer->it.real.timer, new_timer->it_clock, 0);
@@ -492,7 +493,6 @@ static int do_timer_create(clockid_t whi
 	const struct k_clock *kc = clockid_to_kclock(which_clock);
 	struct k_itimer *new_timer;
 	int error, new_timer_id;
-	int it_id_set = IT_ID_NOT_SET;
 
 	if (!kc)
 		return -EINVAL;
@@ -512,11 +512,10 @@ static int do_timer_create(clockid_t whi
 	 */
 	new_timer_id = posix_timer_add(new_timer);
 	if (new_timer_id < 0) {
-		error = new_timer_id;
-		goto out;
+		posix_timer_free(new_timer);
+		return new_timer_id;
 	}
 
-	it_id_set = IT_ID_SET;
 	new_timer->it_id = (timer_t) new_timer_id;
 	new_timer->it_clock = which_clock;
 	new_timer->kclock = kc;
@@ -568,7 +567,7 @@ static int do_timer_create(clockid_t whi
 	 * new_timer after the unlock call.
 	 */
 out:
-	release_posix_timer(new_timer, it_id_set);
+	posix_timer_unhash_and_free(new_timer);
 	return error;
 }
 
@@ -1056,7 +1055,7 @@ SYSCALL_DEFINE1(timer_delete, timer_t, t
 	WRITE_ONCE(timer->it_signal, NULL);
 
 	unlock_timer(timer, flags);
-	release_posix_timer(timer, IT_ID_SET);
+	posix_timer_unhash_and_free(timer);
 	return 0;
 }
 
@@ -1115,7 +1114,7 @@ static void itimer_delete(struct k_itime
 	WRITE_ONCE(timer->it_signal, NULL);
 
 	spin_unlock_irqrestore(&timer->it_lock, flags);
-	release_posix_timer(timer, IT_ID_SET);
+	posix_timer_unhash_and_free(timer);
 }
 
 /*
maintainer Reviewed-by on v1 9/20
#68 lore raw

Re: [patch 09/20] posix-timers: Split release_posix_timers()

Quoted 10 lines · Thomas Gleixner

release_posix_timers() is called for cleaning up both hashed and unhashed timers. The cases are differentiated by an argument and the usage is hideous.

Seperate the actual free path out and use it for unhashed timers. Provide a function for hashed timers.

No functional change.

#69 lore raw

[tip: timers/core] posix-timers: Split release_posix_timers()

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     e7441aa344046cbdcb402ac173fb163613471097
Gitweb:        https://git.kernel.org/tip/e7441aa344046cbdcb402ac173fb163613471097
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:09 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:37 +02:00

posix-timers: Split release_posix_timers()

release_posix_timers() is called for cleaning up both hashed and unhashed timers. The cases are differentiated by an argument and the usage is hideous.

Seperate the actual free path out and use it for unhashed timers. Provide a function for hashed timers.

No functional change.

---
 kernel/time/posix-timers.c | 31 +++++++++++++++----------------
 1 file changed, 15 insertions(+), 16 deletions(-)
kernel/time/posix-timers.c +15 -16
index d8d4cdf..8153374 100644
@@ -466,20 +466,21 @@ static void k_itimer_rcu_free(struct rcu_head *head)
 	kmem_cache_free(posix_timers_cache, tmr);
 }
 
-#define IT_ID_SET	1
-#define IT_ID_NOT_SET	0
-static void release_posix_timer(struct k_itimer *tmr, int it_id_set)
-{
-	if (it_id_set) {
-		spin_lock(&hash_lock, flags);
-		hlist_del_rcu(&tmr->t_hash);
-		spin_unlock(&hash_lock, flags);
-	}
+static void posix_timer_free(struct k_itimer *tmr)
+{
 	put_pid(tmr->it_pid);
 	sigqueue_free(tmr->sigq);
 	call_rcu(&tmr->rcu, k_itimer_rcu_free);
 }
 
+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);
+}
+
 static int common_timer_create(struct k_itimer *new_timer)
 {
 	hrtimer_init(&new_timer->it.real.timer, new_timer->it_clock, 0);
@@ -493,7 +494,6 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
 	const struct k_clock *kc = clockid_to_kclock(which_clock);
 	struct k_itimer *new_timer;
 	int error, new_timer_id;
-	int it_id_set = IT_ID_NOT_SET;
 
 	if (!kc)
 		return -EINVAL;
@@ -513,11 +513,10 @@ 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) {
-		error = new_timer_id;
-		goto out;
+		posix_timer_free(new_timer);
+		return new_timer_id;
 	}
 
-	it_id_set = IT_ID_SET;
 	new_timer->it_id = (timer_t) new_timer_id;
 	new_timer->it_clock = which_clock;
 	new_timer->kclock = kc;
@@ -569,7 +568,7 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
 	 * new_timer after the unlock call.
 	 */
 out:
-	release_posix_timer(new_timer, it_id_set);
+	posix_timer_unhash_and_free(new_timer);
 	return error;
 }
 
@@ -1057,7 +1056,7 @@ retry_delete:
 	WRITE_ONCE(timer->it_signal, NULL);
 
 	unlock_timer(timer, flags);
-	release_posix_timer(timer, IT_ID_SET);
+	posix_timer_unhash_and_free(timer);
 	return 0;
 }
 
@@ -1109,7 +1108,7 @@ retry_delete:
 	WRITE_ONCE(timer->it_signal, NULL);
 
 	spin_unlock_irqrestore(&timer->it_lock, flags);
-	release_posix_timer(timer, IT_ID_SET);
+	posix_timer_unhash_and_free(timer);
 }
 
 /*
#70 lore raw

[tip: timers/core] posix-timers: Split release_posix_timers()

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     8cc96ca2c75f6da59de41321797c87562703c9e1
Gitweb:        https://git.kernel.org/tip/8cc96ca2c75f6da59de41321797c87562703c9e1
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:09 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:50 +02:00

posix-timers: Split release_posix_timers()

release_posix_timers() is called for cleaning up both hashed and unhashed timers. The cases are differentiated by an argument and the usage is hideous.

Seperate the actual free path out and use it for unhashed timers. Provide a function for hashed timers.

No functional change.

---
 kernel/time/posix-timers.c | 31 +++++++++++++++----------------
 1 file changed, 15 insertions(+), 16 deletions(-)
kernel/time/posix-timers.c +15 -16
index ed7d260..6ac6933 100644
@@ -466,20 +466,21 @@ static void k_itimer_rcu_free(struct rcu_head *head)
 	kmem_cache_free(posix_timers_cache, tmr);
 }
 
-#define IT_ID_SET	1
-#define IT_ID_NOT_SET	0
-static void release_posix_timer(struct k_itimer *tmr, int it_id_set)
-{
-	if (it_id_set) {
-		spin_lock(&hash_lock, flags);
-		hlist_del_rcu(&tmr->t_hash);
-		spin_unlock(&hash_lock, flags);
-	}
+static void posix_timer_free(struct k_itimer *tmr)
+{
 	put_pid(tmr->it_pid);
 	sigqueue_free(tmr->sigq);
 	call_rcu(&tmr->rcu, k_itimer_rcu_free);
 }
 
+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);
+}
+
 static int common_timer_create(struct k_itimer *new_timer)
 {
 	hrtimer_init(&new_timer->it.real.timer, new_timer->it_clock, 0);
@@ -493,7 +494,6 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
 	const struct k_clock *kc = clockid_to_kclock(which_clock);
 	struct k_itimer *new_timer;
 	int error, new_timer_id;
-	int it_id_set = IT_ID_NOT_SET;
 
 	if (!kc)
 		return -EINVAL;
@@ -513,11 +513,10 @@ 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) {
-		error = new_timer_id;
-		goto out;
+		posix_timer_free(new_timer);
+		return new_timer_id;
 	}
 
-	it_id_set = IT_ID_SET;
 	new_timer->it_id = (timer_t) new_timer_id;
 	new_timer->it_clock = which_clock;
 	new_timer->kclock = kc;
@@ -569,7 +568,7 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
 	 * new_timer after the unlock call.
 	 */
 out:
-	release_posix_timer(new_timer, it_id_set);
+	posix_timer_unhash_and_free(new_timer);
 	return error;
 }
 
@@ -1057,7 +1056,7 @@ retry_delete:
 	WRITE_ONCE(timer->it_signal, NULL);
 
 	unlock_timer(timer, flags);
-	release_posix_timer(timer, IT_ID_SET);
+	posix_timer_unhash_and_free(timer);
 	return 0;
 }
 
@@ -1109,7 +1108,7 @@ retry_delete:
 	WRITE_ONCE(timer->it_signal, NULL);
 
 	spin_unlock_irqrestore(&timer->it_lock, flags);
-	release_posix_timer(timer, IT_ID_SET);
+	posix_timer_unhash_and_free(timer);
 }
 
 /*
#71 lore raw

PATCH 10/20 posix-timers: Document sys_clock_getres() correctly

In reply to Thomas Gleixner

The decades old comment about Posix clock resolution is confusing at best.

Remove it and add a proper explanation to sys_clock_getres().

---
 kernel/time/posix-timers.c |   81 ++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 73 insertions(+), 8 deletions(-)
kernel/time/posix-timers.c +73 -8
@@ -67,14 +67,6 @@ static const struct k_clock clock_realti
  *	    to implement others.  This structure defines the various
  *	    clocks.
  *
- * RESOLUTION: Clock resolution is used to round up timer and interval
- *	    times, NOT to report clock times, which are reported with as
- *	    much resolution as the system can muster.  In some cases this
- *	    resolution may depend on the underlying clock hardware and
- *	    may not be quantifiable until run time, and only then is the
- *	    necessary code is written.	The standard says we should say
- *	    something about this issue in the documentation...
- *
  * FUNCTIONS: The CLOCKs structure defines possible functions to
  *	    handle various clock functions.
  *
@@ -1204,6 +1196,79 @@ SYSCALL_DEFINE2(clock_adjtime, const clo
 	return err;
 }
 
+/**
+ * sys_clock_getres - Get the resolution of a clock
+ * @which_clock:	The clock to get the resolution for
+ * @tp:			Pointer to a a user space timespec64 for storage
+ *
+ * POSIX defines:
+ *
+ * "The clock_getres() function shall return the resolution of any
+ * clock. Clock resolutions are implementation-defined and cannot be set by
+ * a process. If the argument res is not NULL, the resolution of the
+ * specified clock shall be stored in the location pointed to by res. If
+ * res is NULL, the clock resolution is not returned. If the time argument
+ * of clock_settime() is not a multiple of res, then the value is truncated
+ * to a multiple of res."
+ *
+ * Due to the various hardware constraints the real resolution can vary
+ * wildly and even change during runtime when the underlying devices are
+ * replaced. The kernel also can use hardware devices with different
+ * resolutions for reading the time and for arming timers.
+ *
+ * The kernel therefore deviates from the POSIX spec in various aspects:
+ *
+ * 1) The resolution returned to user space
+ *
+ *    For CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_BOOTTIME, CLOCK_TAI,
+ *    CLOCK_REALTIME_ALARM, CLOCK_BOOTTIME_ALAREM and CLOCK_MONOTONIC_RAW
+ *    the kernel differentiates only two cases:
+ *
+ *    I)  Low resolution mode:
+ *
+ *	  When high resolution timers are disabled at compile or runtime
+ *	  the resolution returned is nanoseconds per tick, which represents
+ *	  the precision at which timers expire.
+ *
+ *    II) High resolution mode:
+ *
+ *	  When high resolution timers are enabled the resolution returned
+ *	  is always one nanosecond independent of the actual resolution of
+ *	  the underlying hardware devices.
+ *
+ *	  For CLOCK_*_ALARM the actual resolution depends on system
+ *	  state. When system is running the resolution is the same as the
+ *	  resolution of the other clocks. During suspend the actual
+ *	  resolution is the resolution of the underlying RTC device which
+ *	  might be way less precise than the clockevent device used during
+ *	  running state.
+ *
+ *   For CLOCK_REALTIME_COARSE and CLOCK_MONOTONIC_COARSE the resolution
+ *   returned is always nanoseconds per tick.
+ *
+ *   For CLOCK_PROCESS_CPUTIME and CLOCK_THREAD_CPUTIME the resolution
+ *   returned is always one nanosecond under the assumption that the
+ *   underlying scheduler clock has a better resolution than nanoseconds
+ *   per tick.
+ *
+ *   For dynamic POSIX clocks (PTP devices) the resolution returned is
+ *   always one nanosecond.
+ *
+ * 2) Affect on sys_clock_settime()
+ *
+ *    The kernel does not truncate the time which is handed in to
+ *    sys_clock_settime(). The kernel internal timekeeping is always using
+ *    nanoseconds precision independent of the clocksource device which is
+ *    used to read the time from. The resolution of that device only
+ *    affects the presicion of the time returned by sys_clock_gettime().
+ *
+ * Returns:
+ *	0		Success. @tp contains the resolution
+ *	-EINVAL		@which_clock is not a valid clock ID
+ *	-EFAULT		Copying the resolution to @tp faulted
+ *	-ENODEV		Dynamic POSIX clock is not backed by a device
+ *	-EOPNOTSUPP	Dynamic POSIX clock does not support getres()
+ */
 SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock,
 		struct __kernel_timespec __user *, tp)
 {
maintainer Reviewed-by on v1 10/20
#72 lore raw

Re: [patch 10/20] posix-timers: Document sys_clock_getres() correctly

Quoted 4 lines · Thomas Gleixner

+/**
+ * sys_clock_getres - Get the resolution of a clock
+ * @which_clock: The clock to get the resolution for
+ * @tp: Pointer to a a user space timespec64 for storage

                                           ^^^

a_a

#73 lore raw

[tip: timers/core] posix-timers: Document sys_clock_getres() correctly

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     4898e5d912108c8a865ffb3fc0230559b40ca361
Gitweb:        https://git.kernel.org/tip/4898e5d912108c8a865ffb3fc0230559b40ca361
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:11 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:37 +02:00

posix-timers: Document sys_clock_getres() correctly

The decades old comment about Posix clock resolution is confusing at best.

Remove it and add a proper explanation to sys_clock_getres().

---
 kernel/time/posix-timers.c | 81 +++++++++++++++++++++++++++++++++----
 1 file changed, 73 insertions(+), 8 deletions(-)
kernel/time/posix-timers.c +73 -8
index 8153374..c9896ff 100644
@@ -67,14 +67,6 @@ static const struct k_clock clock_realtime, clock_monotonic;
  *	    to implement others.  This structure defines the various
  *	    clocks.
  *
- * RESOLUTION: Clock resolution is used to round up timer and interval
- *	    times, NOT to report clock times, which are reported with as
- *	    much resolution as the system can muster.  In some cases this
- *	    resolution may depend on the underlying clock hardware and
- *	    may not be quantifiable until run time, and only then is the
- *	    necessary code is written.	The standard says we should say
- *	    something about this issue in the documentation...
- *
  * FUNCTIONS: The CLOCKs structure defines possible functions to
  *	    handle various clock functions.
  *
@@ -1198,6 +1190,79 @@ SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock,
 	return err;
 }
 
+/**
+ * sys_clock_getres - Get the resolution of a clock
+ * @which_clock:	The clock to get the resolution for
+ * @tp:			Pointer to a a user space timespec64 for storage
+ *
+ * POSIX defines:
+ *
+ * "The clock_getres() function shall return the resolution of any
+ * clock. Clock resolutions are implementation-defined and cannot be set by
+ * a process. If the argument res is not NULL, the resolution of the
+ * specified clock shall be stored in the location pointed to by res. If
+ * res is NULL, the clock resolution is not returned. If the time argument
+ * of clock_settime() is not a multiple of res, then the value is truncated
+ * to a multiple of res."
+ *
+ * Due to the various hardware constraints the real resolution can vary
+ * wildly and even change during runtime when the underlying devices are
+ * replaced. The kernel also can use hardware devices with different
+ * resolutions for reading the time and for arming timers.
+ *
+ * The kernel therefore deviates from the POSIX spec in various aspects:
+ *
+ * 1) The resolution returned to user space
+ *
+ *    For CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_BOOTTIME, CLOCK_TAI,
+ *    CLOCK_REALTIME_ALARM, CLOCK_BOOTTIME_ALAREM and CLOCK_MONOTONIC_RAW
+ *    the kernel differentiates only two cases:
+ *
+ *    I)  Low resolution mode:
+ *
+ *	  When high resolution timers are disabled at compile or runtime
+ *	  the resolution returned is nanoseconds per tick, which represents
+ *	  the precision at which timers expire.
+ *
+ *    II) High resolution mode:
+ *
+ *	  When high resolution timers are enabled the resolution returned
+ *	  is always one nanosecond independent of the actual resolution of
+ *	  the underlying hardware devices.
+ *
+ *	  For CLOCK_*_ALARM the actual resolution depends on system
+ *	  state. When system is running the resolution is the same as the
+ *	  resolution of the other clocks. During suspend the actual
+ *	  resolution is the resolution of the underlying RTC device which
+ *	  might be way less precise than the clockevent device used during
+ *	  running state.
+ *
+ *   For CLOCK_REALTIME_COARSE and CLOCK_MONOTONIC_COARSE the resolution
+ *   returned is always nanoseconds per tick.
+ *
+ *   For CLOCK_PROCESS_CPUTIME and CLOCK_THREAD_CPUTIME the resolution
+ *   returned is always one nanosecond under the assumption that the
+ *   underlying scheduler clock has a better resolution than nanoseconds
+ *   per tick.
+ *
+ *   For dynamic POSIX clocks (PTP devices) the resolution returned is
+ *   always one nanosecond.
+ *
+ * 2) Affect on sys_clock_settime()
+ *
+ *    The kernel does not truncate the time which is handed in to
+ *    sys_clock_settime(). The kernel internal timekeeping is always using
+ *    nanoseconds precision independent of the clocksource device which is
+ *    used to read the time from. The resolution of that device only
+ *    affects the presicion of the time returned by sys_clock_gettime().
+ *
+ * Returns:
+ *	0		Success. @tp contains the resolution
+ *	-EINVAL		@which_clock is not a valid clock ID
+ *	-EFAULT		Copying the resolution to @tp faulted
+ *	-ENODEV		Dynamic POSIX clock is not backed by a device
+ *	-EOPNOTSUPP	Dynamic POSIX clock does not support getres()
+ */
 SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock,
 		struct __kernel_timespec __user *, tp)
 {
#74 lore raw

[tip: timers/core] posix-timers: Document sys_clock_getres() correctly

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     01679b5db7172b898be325ff272e10aebd412911
Gitweb:        https://git.kernel.org/tip/01679b5db7172b898be325ff272e10aebd412911
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:11 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:50 +02:00

posix-timers: Document sys_clock_getres() correctly

The decades old comment about Posix clock resolution is confusing at best.

Remove it and add a proper explanation to sys_clock_getres().

---
 kernel/time/posix-timers.c | 81 +++++++++++++++++++++++++++++++++----
 1 file changed, 73 insertions(+), 8 deletions(-)
kernel/time/posix-timers.c +73 -8
index 6ac6933..1acdd04 100644
@@ -67,14 +67,6 @@ static const struct k_clock clock_realtime, clock_monotonic;
  *	    to implement others.  This structure defines the various
  *	    clocks.
  *
- * RESOLUTION: Clock resolution is used to round up timer and interval
- *	    times, NOT to report clock times, which are reported with as
- *	    much resolution as the system can muster.  In some cases this
- *	    resolution may depend on the underlying clock hardware and
- *	    may not be quantifiable until run time, and only then is the
- *	    necessary code is written.	The standard says we should say
- *	    something about this issue in the documentation...
- *
  * FUNCTIONS: The CLOCKs structure defines possible functions to
  *	    handle various clock functions.
  *
@@ -1198,6 +1190,79 @@ SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock,
 	return err;
 }
 
+/**
+ * sys_clock_getres - Get the resolution of a clock
+ * @which_clock:	The clock to get the resolution for
+ * @tp:			Pointer to a a user space timespec64 for storage
+ *
+ * POSIX defines:
+ *
+ * "The clock_getres() function shall return the resolution of any
+ * clock. Clock resolutions are implementation-defined and cannot be set by
+ * a process. If the argument res is not NULL, the resolution of the
+ * specified clock shall be stored in the location pointed to by res. If
+ * res is NULL, the clock resolution is not returned. If the time argument
+ * of clock_settime() is not a multiple of res, then the value is truncated
+ * to a multiple of res."
+ *
+ * Due to the various hardware constraints the real resolution can vary
+ * wildly and even change during runtime when the underlying devices are
+ * replaced. The kernel also can use hardware devices with different
+ * resolutions for reading the time and for arming timers.
+ *
+ * The kernel therefore deviates from the POSIX spec in various aspects:
+ *
+ * 1) The resolution returned to user space
+ *
+ *    For CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_BOOTTIME, CLOCK_TAI,
+ *    CLOCK_REALTIME_ALARM, CLOCK_BOOTTIME_ALAREM and CLOCK_MONOTONIC_RAW
+ *    the kernel differentiates only two cases:
+ *
+ *    I)  Low resolution mode:
+ *
+ *	  When high resolution timers are disabled at compile or runtime
+ *	  the resolution returned is nanoseconds per tick, which represents
+ *	  the precision at which timers expire.
+ *
+ *    II) High resolution mode:
+ *
+ *	  When high resolution timers are enabled the resolution returned
+ *	  is always one nanosecond independent of the actual resolution of
+ *	  the underlying hardware devices.
+ *
+ *	  For CLOCK_*_ALARM the actual resolution depends on system
+ *	  state. When system is running the resolution is the same as the
+ *	  resolution of the other clocks. During suspend the actual
+ *	  resolution is the resolution of the underlying RTC device which
+ *	  might be way less precise than the clockevent device used during
+ *	  running state.
+ *
+ *   For CLOCK_REALTIME_COARSE and CLOCK_MONOTONIC_COARSE the resolution
+ *   returned is always nanoseconds per tick.
+ *
+ *   For CLOCK_PROCESS_CPUTIME and CLOCK_THREAD_CPUTIME the resolution
+ *   returned is always one nanosecond under the assumption that the
+ *   underlying scheduler clock has a better resolution than nanoseconds
+ *   per tick.
+ *
+ *   For dynamic POSIX clocks (PTP devices) the resolution returned is
+ *   always one nanosecond.
+ *
+ * 2) Affect on sys_clock_settime()
+ *
+ *    The kernel does not truncate the time which is handed in to
+ *    sys_clock_settime(). The kernel internal timekeeping is always using
+ *    nanoseconds precision independent of the clocksource device which is
+ *    used to read the time from. The resolution of that device only
+ *    affects the presicion of the time returned by sys_clock_gettime().
+ *
+ * Returns:
+ *	0		Success. @tp contains the resolution
+ *	-EINVAL		@which_clock is not a valid clock ID
+ *	-EFAULT		Copying the resolution to @tp faulted
+ *	-ENODEV		Dynamic POSIX clock is not backed by a device
+ *	-EOPNOTSUPP	Dynamic POSIX clock does not support getres()
+ */
 SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock,
 		struct __kernel_timespec __user *, tp)
 {
#75 lore raw

PATCH 11/20 posix-timers: Document common_clock_get() correctly

In reply to Thomas Gleixner

Replace another confusing and inaccurate set of comments.

---
 kernel/time/posix-timers.c |   50 +++++++++++++++++++++++++++------------------
 1 file changed, 30 insertions(+), 20 deletions(-)
kernel/time/posix-timers.c +30 -20
@@ -659,20 +659,16 @@ static s64 common_hrtimer_forward(struct
 }
 
 /*
- * Get the time remaining on a POSIX.1b interval timer.  This function
- * is ALWAYS called with spin_lock_irq on the timer, thus it must not
- * mess with irq.
+ * Get the time remaining on a POSIX.1b interval timer.
  *
- * We have a couple of messes to clean up here.  First there is the case
- * of a timer that has a requeue pending.  These timers should appear to
- * be in the timer list with an expiry as if we were to requeue them
- * now.
+ * Two issues to handle here:
  *
- * The second issue is the SIGEV_NONE timer which may be active but is
- * not really ever put in the timer list (to save system resources).
- * This timer may be expired, and if so, we will do it here.  Otherwise
- * it is the same as a requeue pending timer WRT to what we should
- * report.
+ *  1) The timer has a requeue pending. The return value must appear as
+ *     if the timer has been requeued right now.
+ *
+ *  2) The timer is a SIGEV_NONE timer. These timers are never enqueued
+ *     into the hrtimer queue and therefore never expired. Emulate expiry
+ *     here taking #1 into account.
  */
 void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting)
 {
@@ -688,8 +684,12 @@ void common_timer_get(struct k_itimer *t
 		cur_setting->it_interval = ktime_to_timespec64(iv);
 	} else if (!timr->it_active) {
 		/*
-		 * SIGEV_NONE oneshot timers are never queued. Check them
-		 * below.
+		 * SIGEV_NONE oneshot timers are never queued and therefore
+		 * timr->it_active is always false. The check below
+		 * vs. remaining time will handle this case.
+		 *
+		 * For all other timers there is nothing to update here, so
+		 * return.
 		 */
 		if (!sig_none)
 			return;
@@ -698,18 +698,29 @@ void common_timer_get(struct k_itimer *t
 	now = kc->clock_get_ktime(timr->it_clock);
 
 	/*
-	 * When a requeue is pending or this is a SIGEV_NONE timer move the
-	 * expiry time forward by intervals, so expiry is > now.
+	 * If this is an interval timer and either has requeue pending or
+	 * is a SIGEV_NONE timer move the expiry time forward by intervals,
+	 * so expiry is > now.
 	 */
 	if (iv && (timr->it_requeue_pending & REQUEUE_PENDING || sig_none))
 		timr->it_overrun += kc->timer_forward(timr, now);
 
 	remaining = kc->timer_remaining(timr, now);
-	/* Return 0 only, when the timer is expired and not pending */
+	/*
+	 * As @now is retrieved before a possible timer_forward() and
+	 * cannot be reevaluated by the compiler @remaining is based on the
+	 * same @now value. Therefore @remaining is consistent vs. @now.
+	 *
+	 * Consequently all interval timers, i.e. @iv > 0, cannot have a
+	 * remaining time <= 0 because timer_forward() guarantees to move
+	 * them forward so that the next timer expiry is > @now.
+	 */
 	if (remaining <= 0) {
 		/*
-		 * A single shot SIGEV_NONE timer must return 0, when
-		 * it is expired !
+		 * A single shot SIGEV_NONE timer must return 0, when it is
+		 * expired! Timers which have a real signal delivery mode
+		 * must return a remaining time greater than 0 because the
+		 * signal has not yet been delivered.
 		 */
 		if (!sig_none)
 			cur_setting->it_value.tv_nsec = 1;
@@ -718,7 +729,6 @@ void common_timer_get(struct k_itimer *t
 	}
 }
 
-/* Get the time remaining on a POSIX.1b interval timer. */
 static int do_timer_gettime(timer_t timer_id,  struct itimerspec64 *setting)
 {
 	struct k_itimer *timr;
#77 lore raw

[tip: timers/core] posix-timers: Document common_clock_get() correctly

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     13a5dd88bb4c8aec6c3f808f75b73b1fe4705244
Gitweb:        https://git.kernel.org/tip/13a5dd88bb4c8aec6c3f808f75b73b1fe4705244
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:12 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:38 +02:00

posix-timers: Document common_clock_get() correctly

Replace another confusing and inaccurate set of comments.

---
 kernel/time/posix-timers.c | 50 ++++++++++++++++++++++---------------
 1 file changed, 30 insertions(+), 20 deletions(-)
kernel/time/posix-timers.c +30 -20
index c9896ff..663d3c6 100644
@@ -660,20 +660,16 @@ static s64 common_hrtimer_forward(struct k_itimer *timr, ktime_t now)
 }
 
 /*
- * Get the time remaining on a POSIX.1b interval timer.  This function
- * is ALWAYS called with spin_lock_irq on the timer, thus it must not
- * mess with irq.
+ * Get the time remaining on a POSIX.1b interval timer.
  *
- * We have a couple of messes to clean up here.  First there is the case
- * of a timer that has a requeue pending.  These timers should appear to
- * be in the timer list with an expiry as if we were to requeue them
- * now.
+ * Two issues to handle here:
  *
- * The second issue is the SIGEV_NONE timer which may be active but is
- * not really ever put in the timer list (to save system resources).
- * This timer may be expired, and if so, we will do it here.  Otherwise
- * it is the same as a requeue pending timer WRT to what we should
- * report.
+ *  1) The timer has a requeue pending. The return value must appear as
+ *     if the timer has been requeued right now.
+ *
+ *  2) The timer is a SIGEV_NONE timer. These timers are never enqueued
+ *     into the hrtimer queue and therefore never expired. Emulate expiry
+ *     here taking #1 into account.
  */
 void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting)
 {
@@ -689,8 +685,12 @@ void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting)
 		cur_setting->it_interval = ktime_to_timespec64(iv);
 	} else if (!timr->it_active) {
 		/*
-		 * SIGEV_NONE oneshot timers are never queued. Check them
-		 * below.
+		 * SIGEV_NONE oneshot timers are never queued and therefore
+		 * timr->it_active is always false. The check below
+		 * vs. remaining time will handle this case.
+		 *
+		 * For all other timers there is nothing to update here, so
+		 * return.
 		 */
 		if (!sig_none)
 			return;
@@ -699,18 +699,29 @@ void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting)
 	now = kc->clock_get_ktime(timr->it_clock);
 
 	/*
-	 * When a requeue is pending or this is a SIGEV_NONE timer move the
-	 * expiry time forward by intervals, so expiry is > now.
+	 * If this is an interval timer and either has requeue pending or
+	 * is a SIGEV_NONE timer move the expiry time forward by intervals,
+	 * so expiry is > now.
 	 */
 	if (iv && (timr->it_requeue_pending & REQUEUE_PENDING || sig_none))
 		timr->it_overrun += kc->timer_forward(timr, now);
 
 	remaining = kc->timer_remaining(timr, now);
-	/* Return 0 only, when the timer is expired and not pending */
+	/*
+	 * As @now is retrieved before a possible timer_forward() and
+	 * cannot be reevaluated by the compiler @remaining is based on the
+	 * same @now value. Therefore @remaining is consistent vs. @now.
+	 *
+	 * Consequently all interval timers, i.e. @iv > 0, cannot have a
+	 * remaining time <= 0 because timer_forward() guarantees to move
+	 * them forward so that the next timer expiry is > @now.
+	 */
 	if (remaining <= 0) {
 		/*
-		 * A single shot SIGEV_NONE timer must return 0, when
-		 * it is expired !
+		 * A single shot SIGEV_NONE timer must return 0, when it is
+		 * expired! Timers which have a real signal delivery mode
+		 * must return a remaining time greater than 0 because the
+		 * signal has not yet been delivered.
 		 */
 		if (!sig_none)
 			cur_setting->it_value.tv_nsec = 1;
@@ -719,7 +730,6 @@ void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting)
 	}
 }
 
-/* Get the time remaining on a POSIX.1b interval timer. */
 static int do_timer_gettime(timer_t timer_id,  struct itimerspec64 *setting)
 {
 	struct k_itimer *timr;
#78 lore raw

[tip: timers/core] posix-timers: Document common_clock_get() correctly

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     a86e9284336729a8f79a65371eacd0c1c7fae142
Gitweb:        https://git.kernel.org/tip/a86e9284336729a8f79a65371eacd0c1c7fae142
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:12 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:50 +02:00

posix-timers: Document common_clock_get() correctly

Replace another confusing and inaccurate set of comments.

---
 kernel/time/posix-timers.c | 50 ++++++++++++++++++++++---------------
 1 file changed, 30 insertions(+), 20 deletions(-)
kernel/time/posix-timers.c +30 -20
index 1acdd04..e1af74c 100644
@@ -660,20 +660,16 @@ static s64 common_hrtimer_forward(struct k_itimer *timr, ktime_t now)
 }
 
 /*
- * Get the time remaining on a POSIX.1b interval timer.  This function
- * is ALWAYS called with spin_lock_irq on the timer, thus it must not
- * mess with irq.
+ * Get the time remaining on a POSIX.1b interval timer.
  *
- * We have a couple of messes to clean up here.  First there is the case
- * of a timer that has a requeue pending.  These timers should appear to
- * be in the timer list with an expiry as if we were to requeue them
- * now.
+ * Two issues to handle here:
  *
- * The second issue is the SIGEV_NONE timer which may be active but is
- * not really ever put in the timer list (to save system resources).
- * This timer may be expired, and if so, we will do it here.  Otherwise
- * it is the same as a requeue pending timer WRT to what we should
- * report.
+ *  1) The timer has a requeue pending. The return value must appear as
+ *     if the timer has been requeued right now.
+ *
+ *  2) The timer is a SIGEV_NONE timer. These timers are never enqueued
+ *     into the hrtimer queue and therefore never expired. Emulate expiry
+ *     here taking #1 into account.
  */
 void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting)
 {
@@ -689,8 +685,12 @@ void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting)
 		cur_setting->it_interval = ktime_to_timespec64(iv);
 	} else if (!timr->it_active) {
 		/*
-		 * SIGEV_NONE oneshot timers are never queued. Check them
-		 * below.
+		 * SIGEV_NONE oneshot timers are never queued and therefore
+		 * timr->it_active is always false. The check below
+		 * vs. remaining time will handle this case.
+		 *
+		 * For all other timers there is nothing to update here, so
+		 * return.
 		 */
 		if (!sig_none)
 			return;
@@ -699,18 +699,29 @@ void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting)
 	now = kc->clock_get_ktime(timr->it_clock);
 
 	/*
-	 * When a requeue is pending or this is a SIGEV_NONE timer move the
-	 * expiry time forward by intervals, so expiry is > now.
+	 * If this is an interval timer and either has requeue pending or
+	 * is a SIGEV_NONE timer move the expiry time forward by intervals,
+	 * so expiry is > now.
 	 */
 	if (iv && (timr->it_requeue_pending & REQUEUE_PENDING || sig_none))
 		timr->it_overrun += kc->timer_forward(timr, now);
 
 	remaining = kc->timer_remaining(timr, now);
-	/* Return 0 only, when the timer is expired and not pending */
+	/*
+	 * As @now is retrieved before a possible timer_forward() and
+	 * cannot be reevaluated by the compiler @remaining is based on the
+	 * same @now value. Therefore @remaining is consistent vs. @now.
+	 *
+	 * Consequently all interval timers, i.e. @iv > 0, cannot have a
+	 * remaining time <= 0 because timer_forward() guarantees to move
+	 * them forward so that the next timer expiry is > @now.
+	 */
 	if (remaining <= 0) {
 		/*
-		 * A single shot SIGEV_NONE timer must return 0, when
-		 * it is expired !
+		 * A single shot SIGEV_NONE timer must return 0, when it is
+		 * expired! Timers which have a real signal delivery mode
+		 * must return a remaining time greater than 0 because the
+		 * signal has not yet been delivered.
 		 */
 		if (!sig_none)
 			cur_setting->it_value.tv_nsec = 1;
@@ -719,7 +730,6 @@ void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting)
 	}
 }
 
-/* Get the time remaining on a POSIX.1b interval timer. */
 static int do_timer_gettime(timer_t timer_id,  struct itimerspec64 *setting)
 {
 	struct k_itimer *timr;
#79 lore raw

PATCH 12/20 posix-timers: Document sys_clock_getoverrun()

In reply to Thomas Gleixner

Document the syscall in detail and with coherent sentences.

---
 kernel/time/posix-timers.c |   25 +++++++++++++++++--------
 1 file changed, 17 insertions(+), 8 deletions(-)
kernel/time/posix-timers.c +17 -8
@@ -782,14 +782,23 @@ SYSCALL_DEFINE2(timer_gettime32, timer_t
 
 #endif
 
-/*
- * Get the number of overruns of a POSIX.1b interval timer.  This is to
- * be the overrun of the timer last delivered.  At the same time we are
- * accumulating overruns on the next timer.  The overrun is frozen when
- * the signal is delivered, either at the notify time (if the info block
- * is not queued) or at the actual delivery time (as we are informed by
- * the call back to posixtimer_rearm().  So all we need to do is
- * to pick up the frozen overrun.
+/**
+ * sys_timer_getoverrun - Get the number of overruns of a POSIX.1b interval timer
+ * @timer_id:	The timer ID which identifies the timer
+ *
+ * The "overrun count" of a timer is one plus the number of expiration
+ * intervals which have elapsed between the first expiry, which queues the
+ * signal and the actual signal delivery. On signal delivery the "overrun
+ * count" is calculated and cached, so it can be returned directly here.
+ *
+ * As this is relative to the last queued signal the returned overrun count
+ * is meaningless outside of the signal delivery path and even there it
+ * does not accurately reflect the current state when user space evaluates
+ * it.
+ *
+ * Returns:
+ *	-EINVAL		@timer_id is invalid
+ *	1..INT_MAX	The number of overruns related to the last delivered signal
  */
 SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id)
 {
#81 lore raw

[tip: timers/core] posix-timers: Document sys_clock_getoverrun()

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     1adc8de86737c3d134c2da5ed8e56c2de4729923
Gitweb:        https://git.kernel.org/tip/1adc8de86737c3d134c2da5ed8e56c2de4729923
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:14 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:38 +02:00

posix-timers: Document sys_clock_getoverrun()

Document the syscall in detail and with coherent sentences.

---
 kernel/time/posix-timers.c | 25 +++++++++++++++++--------
 1 file changed, 17 insertions(+), 8 deletions(-)
kernel/time/posix-timers.c +17 -8
index 663d3c6..67c1d4d 100644
@@ -783,14 +783,23 @@ SYSCALL_DEFINE2(timer_gettime32, timer_t, timer_id,
 
 #endif
 
-/*
- * Get the number of overruns of a POSIX.1b interval timer.  This is to
- * be the overrun of the timer last delivered.  At the same time we are
- * accumulating overruns on the next timer.  The overrun is frozen when
- * the signal is delivered, either at the notify time (if the info block
- * is not queued) or at the actual delivery time (as we are informed by
- * the call back to posixtimer_rearm().  So all we need to do is
- * to pick up the frozen overrun.
+/**
+ * sys_timer_getoverrun - Get the number of overruns of a POSIX.1b interval timer
+ * @timer_id:	The timer ID which identifies the timer
+ *
+ * The "overrun count" of a timer is one plus the number of expiration
+ * intervals which have elapsed between the first expiry, which queues the
+ * signal and the actual signal delivery. On signal delivery the "overrun
+ * count" is calculated and cached, so it can be returned directly here.
+ *
+ * As this is relative to the last queued signal the returned overrun count
+ * is meaningless outside of the signal delivery path and even there it
+ * does not accurately reflect the current state when user space evaluates
+ * it.
+ *
+ * Returns:
+ *	-EINVAL		@timer_id is invalid
+ *	1..INT_MAX	The number of overruns related to the last delivered signal
  */
 SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id)
 {
#82 lore raw

[tip: timers/core] posix-timers: Document sys_clock_getoverrun()

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     65cade468dee537885b51897ba41ba05a4dcf6ca
Gitweb:        https://git.kernel.org/tip/65cade468dee537885b51897ba41ba05a4dcf6ca
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:14 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:50 +02:00

posix-timers: Document sys_clock_getoverrun()

Document the syscall in detail and with coherent sentences.

---
 kernel/time/posix-timers.c | 25 +++++++++++++++++--------
 1 file changed, 17 insertions(+), 8 deletions(-)
kernel/time/posix-timers.c +17 -8
index e1af74c..191ecf5 100644
@@ -783,14 +783,23 @@ SYSCALL_DEFINE2(timer_gettime32, timer_t, timer_id,
 
 #endif
 
-/*
- * Get the number of overruns of a POSIX.1b interval timer.  This is to
- * be the overrun of the timer last delivered.  At the same time we are
- * accumulating overruns on the next timer.  The overrun is frozen when
- * the signal is delivered, either at the notify time (if the info block
- * is not queued) or at the actual delivery time (as we are informed by
- * the call back to posixtimer_rearm().  So all we need to do is
- * to pick up the frozen overrun.
+/**
+ * sys_timer_getoverrun - Get the number of overruns of a POSIX.1b interval timer
+ * @timer_id:	The timer ID which identifies the timer
+ *
+ * The "overrun count" of a timer is one plus the number of expiration
+ * intervals which have elapsed between the first expiry, which queues the
+ * signal and the actual signal delivery. On signal delivery the "overrun
+ * count" is calculated and cached, so it can be returned directly here.
+ *
+ * As this is relative to the last queued signal the returned overrun count
+ * is meaningless outside of the signal delivery path and even there it
+ * does not accurately reflect the current state when user space evaluates
+ * it.
+ *
+ * Returns:
+ *	-EINVAL		@timer_id is invalid
+ *	1..INT_MAX	The number of overruns related to the last delivered signal
  */
 SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id)
 {
#83 lore raw

PATCH 13/20 posix-timers: Document sys_clock_settime() permissions in place

In reply to Thomas Gleixner

The documentation of sys_clock_settime() permissions is at a random place and mostly word salad.

Remove it and add a concise comment into sys_clock_settime().

---
 kernel/time/posix-timers.c |   11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)
kernel/time/posix-timers.c +4 -7
@@ -74,13 +74,6 @@ static const struct k_clock clock_realti
  *	    following: 1.) The k_itimer struct (sched.h) is used for
  *	    the timer.  2.) The list, it_lock, it_clock, it_id and
  *	    it_pid fields are not modified by timer code.
- *
- * Permissions: It is assumed that the clock_settime() function defined
- *	    for each clock will take care of permission checks.	 Some
- *	    clocks may be set able by any user (i.e. local process
- *	    clocks) others not.	 Currently the only set able clock we
- *	    have is CLOCK_REALTIME and its high res counter part, both of
- *	    which we beg off on and pass to do_sys_settimeofday().
  */
 static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags);
 
@@ -1165,6 +1158,10 @@ SYSCALL_DEFINE2(clock_settime, const clo
 	if (get_timespec64(&new_tp, tp))
 		return -EFAULT;
 
+	/*
+	 * Permission checks have to be done inside the clock specific
+	 * setter callback.
+	 */
 	return kc->clock_set(which_clock, &new_tp);
 }
 
#85 lore raw

[tip: timers/core] posix-timers: Document sys_clock_settime() permissions in place

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     6a0fc851449cdcad42f96e00854af4ac36a4b357
Gitweb:        https://git.kernel.org/tip/6a0fc851449cdcad42f96e00854af4ac36a4b357
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:16 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:38 +02:00

posix-timers: Document sys_clock_settime() permissions in place

The documentation of sys_clock_settime() permissions is at a random place and mostly word salad.

Remove it and add a concise comment into sys_clock_settime().

---
 kernel/time/posix-timers.c | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)
kernel/time/posix-timers.c +4 -7
index 67c1d4d..9d99d4b 100644
@@ -74,13 +74,6 @@ static const struct k_clock clock_realtime, clock_monotonic;
  *	    following: 1.) The k_itimer struct (sched.h) is used for
  *	    the timer.  2.) The list, it_lock, it_clock, it_id and
  *	    it_pid fields are not modified by timer code.
- *
- * Permissions: It is assumed that the clock_settime() function defined
- *	    for each clock will take care of permission checks.	 Some
- *	    clocks may be set able by any user (i.e. local process
- *	    clocks) others not.	 Currently the only set able clock we
- *	    have is CLOCK_REALTIME and its high res counter part, both of
- *	    which we beg off on and pass to do_sys_settimeofday().
  */
 static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags);
 
@@ -1159,6 +1152,10 @@ SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock,
 	if (get_timespec64(&new_tp, tp))
 		return -EFAULT;
 
+	/*
+	 * Permission checks have to be done inside the clock specific
+	 * setter callback.
+	 */
 	return kc->clock_set(which_clock, &new_tp);
 }
 
#86 lore raw

[tip: timers/core] posix-timers: Document sys_clock_settime() permissions in place

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     3561fcb402b7ab7fdb4c1746dae4995889506605
Gitweb:        https://git.kernel.org/tip/3561fcb402b7ab7fdb4c1746dae4995889506605
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:16 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:51 +02:00

posix-timers: Document sys_clock_settime() permissions in place

The documentation of sys_clock_settime() permissions is at a random place and mostly word salad.

Remove it and add a concise comment into sys_clock_settime().

---
 kernel/time/posix-timers.c | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)
kernel/time/posix-timers.c +4 -7
index 191ecf5..03ef6af 100644
@@ -74,13 +74,6 @@ static const struct k_clock clock_realtime, clock_monotonic;
  *	    following: 1.) The k_itimer struct (sched.h) is used for
  *	    the timer.  2.) The list, it_lock, it_clock, it_id and
  *	    it_pid fields are not modified by timer code.
- *
- * Permissions: It is assumed that the clock_settime() function defined
- *	    for each clock will take care of permission checks.	 Some
- *	    clocks may be set able by any user (i.e. local process
- *	    clocks) others not.	 Currently the only set able clock we
- *	    have is CLOCK_REALTIME and its high res counter part, both of
- *	    which we beg off on and pass to do_sys_settimeofday().
  */
 static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags);
 
@@ -1159,6 +1152,10 @@ SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock,
 	if (get_timespec64(&new_tp, tp))
 		return -EFAULT;
 
+	/*
+	 * Permission checks have to be done inside the clock specific
+	 * setter callback.
+	 */
 	return kc->clock_set(which_clock, &new_tp);
 }
 
#87 lore raw

PATCH 14/20 posix-timers: Document nanosleep() details

In reply to Thomas Gleixner

The descriptions for common_nsleep() is wrong and common_nsleep_timens() lacks any form of comment.

---
 kernel/time/posix-timers.c |    9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
kernel/time/posix-timers.c +7 -2
@@ -1376,7 +1376,7 @@ SYSCALL_DEFINE2(clock_getres_time32, clo
 #endif
 
 /*
- * nanosleep for monotonic and realtime clocks
+ * sys_clock_nanosleep() for CLOCK_REALTIME and CLOCK_TAI
  */
 static int common_nsleep(const clockid_t which_clock, int flags,
 			 const struct timespec64 *rqtp)
@@ -1388,8 +1388,13 @@ static int common_nsleep(const clockid_t
 				 which_clock);
 }
 
+/*
+ * sys_clock_nanosleep() for CLOCK_MONOTONIC and CLOCK_BOOTTIME
+ *
+ * Absolute nanosleeps for these clocks are time-namespace adjusted.
+ */
 static int common_nsleep_timens(const clockid_t which_clock, int flags,
-			 const struct timespec64 *rqtp)
+				const struct timespec64 *rqtp)
 {
 	ktime_t texp = timespec64_to_ktime(*rqtp);
 
#89 lore raw

[tip: timers/core] posix-timers: Document nanosleep() details

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     13da885685d6ae339f2924d3c8a1f4fe16a904cc
Gitweb:        https://git.kernel.org/tip/13da885685d6ae339f2924d3c8a1f4fe16a904cc
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:17 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:38 +02:00

posix-timers: Document nanosleep() details

The descriptions for common_nsleep() is wrong and common_nsleep_timens() lacks any form of comment.

---
 kernel/time/posix-timers.c |  9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
kernel/time/posix-timers.c +7 -2
index 9d99d4b..9ce13c9 100644
@@ -1370,7 +1370,7 @@ SYSCALL_DEFINE2(clock_getres_time32, clockid_t, which_clock,
 #endif
 
 /*
- * nanosleep for monotonic and realtime clocks
+ * sys_clock_nanosleep() for CLOCK_REALTIME and CLOCK_TAI
  */
 static int common_nsleep(const clockid_t which_clock, int flags,
 			 const struct timespec64 *rqtp)
@@ -1382,8 +1382,13 @@ static int common_nsleep(const clockid_t which_clock, int flags,
 				 which_clock);
 }
 
+/*
+ * sys_clock_nanosleep() for CLOCK_MONOTONIC and CLOCK_BOOTTIME
+ *
+ * Absolute nanosleeps for these clocks are time-namespace adjusted.
+ */
 static int common_nsleep_timens(const clockid_t which_clock, int flags,
-			 const struct timespec64 *rqtp)
+				const struct timespec64 *rqtp)
 {
 	ktime_t texp = timespec64_to_ktime(*rqtp);
 
#90 lore raw

[tip: timers/core] posix-timers: Document nanosleep() details

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     640fe745d7d4b6e47f3b455cb5de99a08c6b6d23
Gitweb:        https://git.kernel.org/tip/640fe745d7d4b6e47f3b455cb5de99a08c6b6d23
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:17 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:51 +02:00

posix-timers: Document nanosleep() details

The descriptions for common_nsleep() is wrong and common_nsleep_timens() lacks any form of comment.

---
 kernel/time/posix-timers.c |  9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
kernel/time/posix-timers.c +7 -2
index 03ef6af..54adb4c 100644
@@ -1370,7 +1370,7 @@ SYSCALL_DEFINE2(clock_getres_time32, clockid_t, which_clock,
 #endif
 
 /*
- * nanosleep for monotonic and realtime clocks
+ * sys_clock_nanosleep() for CLOCK_REALTIME and CLOCK_TAI
  */
 static int common_nsleep(const clockid_t which_clock, int flags,
 			 const struct timespec64 *rqtp)
@@ -1382,8 +1382,13 @@ static int common_nsleep(const clockid_t which_clock, int flags,
 				 which_clock);
 }
 
+/*
+ * sys_clock_nanosleep() for CLOCK_MONOTONIC and CLOCK_BOOTTIME
+ *
+ * Absolute nanosleeps for these clocks are time-namespace adjusted.
+ */
 static int common_nsleep_timens(const clockid_t which_clock, int flags,
-			 const struct timespec64 *rqtp)
+				const struct timespec64 *rqtp)
 {
 	ktime_t texp = timespec64_to_ktime(*rqtp);
 
#91 lore raw

PATCH 15/20 posix-timers: Add proper comments in do_timer_create()

In reply to Thomas Gleixner

The comment about timer lifetime at the end of the function is misplaced and uncomprehensible.

Make it understandable and put it at the right place. Add a new comment about the visibility of the new timer ID to user space.

---
 kernel/time/posix-timers.c |   20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)
kernel/time/posix-timers.c +11 -9
@@ -528,12 +528,17 @@ static int do_timer_create(clockid_t whi
 	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))) {
+	if (copy_to_user(created_timer_id, &new_timer_id, sizeof (new_timer_id))) {
 		error = -EFAULT;
 		goto out;
 	}
-
+	/*
+	 * After succesful copy out, the timer ID is visible to user space
+	 * now but not yet valid because new_timer::signal is still NULL.
+	 *
+	 * Complete the initialization with the clock specific create
+	 * callback.
+	 */
 	error = kc->timer_create(new_timer);
 	if (error)
 		goto out;
@@ -543,14 +548,11 @@ static int do_timer_create(clockid_t whi
 	WRITE_ONCE(new_timer->it_signal, current->signal);
 	list_add(&new_timer->list, &current->signal->posix_timers);
 	spin_unlock_irq(&current->sighand->siglock);
-
-	return 0;
 	/*
-	 * In the case of the timer belonging to another task, after
-	 * the task is unlocked, the timer is owned by the other task
-	 * and may cease to exist at any time.  Don't use or modify
-	 * new_timer after the unlock call.
+	 * After unlocking sighand::siglock @new_timer is subject to
+	 * concurrent removal and cannot be touched anymore
 	 */
+	return 0;
 out:
 	posix_timer_unhash_and_free(new_timer);
 	return error;
maintainer Reviewed-by on v1 15/20
#92 lore raw

Re: [patch 15/20] posix-timers: Add proper comments in do_timer_create()

Quoted 7 lines · Thomas Gleixner

The comment about timer lifetime at the end of the function is misplaced and uncomprehensible.

Make it understandable and put it at the right place. Add a new comment about the visibility of the new timer ID to user space.

#93 lore raw

[tip: timers/core] posix-timers: Add proper comments in do_timer_create()

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     2ef6cf6d46b462a1ff7fb70ad8050a2b35077e19
Gitweb:        https://git.kernel.org/tip/2ef6cf6d46b462a1ff7fb70ad8050a2b35077e19
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:19 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:38 +02:00

posix-timers: Add proper comments in do_timer_create()

The comment about timer lifetime at the end of the function is misplaced and uncomprehensible.

Make it understandable and put it at the right place. Add a new comment about the visibility of the new timer ID to user space.

---
 kernel/time/posix-timers.c | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)
kernel/time/posix-timers.c +11 -9
index 9ce13c9..5cfd09c 100644
@@ -529,12 +529,17 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
 	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))) {
+	if (copy_to_user(created_timer_id, &new_timer_id, sizeof (new_timer_id))) {
 		error = -EFAULT;
 		goto out;
 	}
-
+	/*
+	 * After succesful copy out, the timer ID is visible to user space
+	 * now but not yet valid because new_timer::signal is still NULL.
+	 *
+	 * Complete the initialization with the clock specific create
+	 * callback.
+	 */
 	error = kc->timer_create(new_timer);
 	if (error)
 		goto out;
@@ -544,14 +549,11 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
 	WRITE_ONCE(new_timer->it_signal, current->signal);
 	list_add(&new_timer->list, &current->signal->posix_timers);
 	spin_unlock_irq(&current->sighand->siglock);
-
-	return 0;
 	/*
-	 * In the case of the timer belonging to another task, after
-	 * the task is unlocked, the timer is owned by the other task
-	 * and may cease to exist at any time.  Don't use or modify
-	 * new_timer after the unlock call.
+	 * After unlocking sighand::siglock @new_timer is subject to
+	 * concurrent removal and cannot be touched anymore
 	 */
+	return 0;
 out:
 	posix_timer_unhash_and_free(new_timer);
 	return error;
#94 lore raw

[tip: timers/core] posix-timers: Add proper comments in do_timer_create()

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     52f090b164b59c06a4da87c0599424809a6bba16
Gitweb:        https://git.kernel.org/tip/52f090b164b59c06a4da87c0599424809a6bba16
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:19 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:51 +02:00

posix-timers: Add proper comments in do_timer_create()

The comment about timer lifetime at the end of the function is misplaced and uncomprehensible.

Make it understandable and put it at the right place. Add a new comment about the visibility of the new timer ID to user space.

---
 kernel/time/posix-timers.c | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)
kernel/time/posix-timers.c +11 -9
index 54adb4c..20d3b99 100644
@@ -529,12 +529,17 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
 	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))) {
+	if (copy_to_user(created_timer_id, &new_timer_id, sizeof (new_timer_id))) {
 		error = -EFAULT;
 		goto out;
 	}
-
+	/*
+	 * After succesful copy out, the timer ID is visible to user space
+	 * now but not yet valid because new_timer::signal is still NULL.
+	 *
+	 * Complete the initialization with the clock specific create
+	 * callback.
+	 */
 	error = kc->timer_create(new_timer);
 	if (error)
 		goto out;
@@ -544,14 +549,11 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
 	WRITE_ONCE(new_timer->it_signal, current->signal);
 	list_add(&new_timer->list, &current->signal->posix_timers);
 	spin_unlock_irq(&current->sighand->siglock);
-
-	return 0;
 	/*
-	 * In the case of the timer belonging to another task, after
-	 * the task is unlocked, the timer is owned by the other task
-	 * and may cease to exist at any time.  Don't use or modify
-	 * new_timer after the unlock call.
+	 * After unlocking sighand::siglock @new_timer is subject to
+	 * concurrent removal and cannot be touched anymore
 	 */
+	return 0;
 out:
 	posix_timer_unhash_and_free(new_timer);
 	return error;
#95 lore raw

PATCH 16/20 posix-timers: Comment SIGEV_THREAD_ID properly

In reply to Thomas Gleixner

Replace the word salad.

---
 kernel/time/posix-timers.c |    7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)
kernel/time/posix-timers.c +2 -5
@@ -53,12 +53,9 @@ static const struct k_clock * const posi
 static const struct k_clock *clockid_to_kclock(const clockid_t id);
 static const struct k_clock clock_realtime, clock_monotonic;
 
-/*
- * we assume that the new SIGEV_THREAD_ID shares no bits with the other
- * SIGEV values.  Here we put out an error if this assumption fails.
- */
+/* SIGEV_THREAD_ID cannot share a bit with the other SIGEV values. */
 #if SIGEV_THREAD_ID != (SIGEV_THREAD_ID & \
-                       ~(SIGEV_SIGNAL | SIGEV_NONE | SIGEV_THREAD))
+			~(SIGEV_SIGNAL | SIGEV_NONE | SIGEV_THREAD))
 #error "SIGEV_THREAD_ID must not share bit with other SIGEV values!"
 #endif
 
#97 lore raw

[tip: timers/core] posix-timers: Comment SIGEV_THREAD_ID properly

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     b73b3beb88c26d3d6c644920fa83460ec5f6a559
Gitweb:        https://git.kernel.org/tip/b73b3beb88c26d3d6c644920fa83460ec5f6a559
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:20 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:38 +02:00

posix-timers: Comment SIGEV_THREAD_ID properly

Replace the word salad.

---
 kernel/time/posix-timers.c | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)
kernel/time/posix-timers.c +2 -5
index 5cfd09c..fb92036 100644
@@ -53,12 +53,9 @@ static const struct k_clock * const posix_clocks[];
 static const struct k_clock *clockid_to_kclock(const clockid_t id);
 static const struct k_clock clock_realtime, clock_monotonic;
 
-/*
- * we assume that the new SIGEV_THREAD_ID shares no bits with the other
- * SIGEV values.  Here we put out an error if this assumption fails.
- */
+/* SIGEV_THREAD_ID cannot share a bit with the other SIGEV values. */
 #if SIGEV_THREAD_ID != (SIGEV_THREAD_ID & \
-                       ~(SIGEV_SIGNAL | SIGEV_NONE | SIGEV_THREAD))
+			~(SIGEV_SIGNAL | SIGEV_NONE | SIGEV_THREAD))
 #error "SIGEV_THREAD_ID must not share bit with other SIGEV values!"
 #endif
 
#98 lore raw

[tip: timers/core] posix-timers: Comment SIGEV_THREAD_ID properly

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     c575689d66378611bb04137b96e67ebb3ac8482b
Gitweb:        https://git.kernel.org/tip/c575689d66378611bb04137b96e67ebb3ac8482b
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:20 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:51 +02:00

posix-timers: Comment SIGEV_THREAD_ID properly

Replace the word salad.

---
 kernel/time/posix-timers.c | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)
kernel/time/posix-timers.c +2 -5
index 20d3b99..c8b0f52 100644
@@ -53,12 +53,9 @@ static const struct k_clock * const posix_clocks[];
 static const struct k_clock *clockid_to_kclock(const clockid_t id);
 static const struct k_clock clock_realtime, clock_monotonic;
 
-/*
- * we assume that the new SIGEV_THREAD_ID shares no bits with the other
- * SIGEV values.  Here we put out an error if this assumption fails.
- */
+/* SIGEV_THREAD_ID cannot share a bit with the other SIGEV values. */
 #if SIGEV_THREAD_ID != (SIGEV_THREAD_ID & \
-                       ~(SIGEV_SIGNAL | SIGEV_NONE | SIGEV_THREAD))
+			~(SIGEV_SIGNAL | SIGEV_NONE | SIGEV_THREAD))
 #error "SIGEV_THREAD_ID must not share bit with other SIGEV values!"
 #endif
 
#99 lore raw

PATCH 17/20 posix-timers: Clarify posix_timer_rearm() comment

In reply to Thomas Gleixner

Yet another incomprehensible piece of art.

---
 kernel/time/posix-timers.c |   12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)
kernel/time/posix-timers.c +3 -9
@@ -274,15 +274,9 @@ static void common_hrtimer_rearm(struct
 }
 
 /*
- * This function is exported for use by the signal deliver code.  It is
- * called just prior to the info block being released and passes that
- * block to us.  It's function is to update the overrun entry AND to
- * restart the timer.  It should only be called if the timer is to be
- * restarted (i.e. we have flagged this in the sys_private entry of the
- * info block).
- *
- * To protect against the timer going away while the interrupt is queued,
- * we require that the it_requeue_pending flag be set.
+ * This function is called from the signal delivery code if
+ * info->si_sys_private is not zero, which indicates that the timer has to
+ * be rearmed. Restart the timer and update info::si_overrun.
  */
 void posixtimer_rearm(struct kernel_siginfo *info)
 {
#101 lore raw

[tip: timers/core] posix-timers: Clarify posix_timer_rearm() comment

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     93a62291070d2836b7cbaf20febadae6ae33934a
Gitweb:        https://git.kernel.org/tip/93a62291070d2836b7cbaf20febadae6ae33934a
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:22 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:38 +02:00

posix-timers: Clarify posix_timer_rearm() comment

Yet another incomprehensible piece of art.

---
 kernel/time/posix-timers.c | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)
kernel/time/posix-timers.c +3 -9
index fb92036..f1a7c62 100644
@@ -275,15 +275,9 @@ static void common_hrtimer_rearm(struct k_itimer *timr)
 }
 
 /*
- * This function is exported for use by the signal deliver code.  It is
- * called just prior to the info block being released and passes that
- * block to us.  It's function is to update the overrun entry AND to
- * restart the timer.  It should only be called if the timer is to be
- * restarted (i.e. we have flagged this in the sys_private entry of the
- * info block).
- *
- * To protect against the timer going away while the interrupt is queued,
- * we require that the it_requeue_pending flag be set.
+ * This function is called from the signal delivery code if
+ * info->si_sys_private is not zero, which indicates that the timer has to
+ * be rearmed. Restart the timer and update info::si_overrun.
  */
 void posixtimer_rearm(struct kernel_siginfo *info)
 {
#102 lore raw

[tip: timers/core] posix-timers: Clarify posix_timer_rearm() comment

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     02972d7955341b711d4c392f14faa9f9cd69d551
Gitweb:        https://git.kernel.org/tip/02972d7955341b711d4c392f14faa9f9cd69d551
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:22 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:52 +02:00

posix-timers: Clarify posix_timer_rearm() comment

Yet another incomprehensible piece of art.

---
 kernel/time/posix-timers.c | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)
kernel/time/posix-timers.c +3 -9
index c8b0f52..d8d2169 100644
@@ -275,15 +275,9 @@ static void common_hrtimer_rearm(struct k_itimer *timr)
 }
 
 /*
- * This function is exported for use by the signal deliver code.  It is
- * called just prior to the info block being released and passes that
- * block to us.  It's function is to update the overrun entry AND to
- * restart the timer.  It should only be called if the timer is to be
- * restarted (i.e. we have flagged this in the sys_private entry of the
- * info block).
- *
- * To protect against the timer going away while the interrupt is queued,
- * we require that the it_requeue_pending flag be set.
+ * This function is called from the signal delivery code if
+ * info->si_sys_private is not zero, which indicates that the timer has to
+ * be rearmed. Restart the timer and update info::si_overrun.
  */
 void posixtimer_rearm(struct kernel_siginfo *info)
 {
#103 lore raw

PATCH 18/20 posix-timers: Clarify posix_timer_fn() comments

In reply to Thomas Gleixner

Make the issues vs. SIG_IGN understandable and remove the 15 years old promise that a proper solution is already on the horizon.

---
 kernel/time/posix-timers.c |   56 +++++++++++++++++++++------------------------
 1 file changed, 27 insertions(+), 29 deletions(-)
kernel/time/posix-timers.c +27 -29
@@ -325,11 +325,11 @@ int posix_timer_event(struct k_itimer *t
 }
 
 /*
- * This function gets called when a POSIX.1b interval timer expires.  It
- * is used as a callback from the kernel internal timer.  The
- * run_timer_list code ALWAYS calls with interrupts on.
-
- * This code is for CLOCK_REALTIME* and CLOCK_MONOTONIC* timers.
+ * This function gets called when a POSIX.1b interval timer expires from
+ * the HRTIMER soft interrupt with interrupts enabled.
+ *
+ * Handles CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_BOOTTIME and CLOCK_TAI
+ * based timers.
  */
 static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
 {
@@ -347,9 +347,10 @@ static enum hrtimer_restart posix_timer_
 
 	if (posix_timer_event(timr, si_private)) {
 		/*
-		 * signal was not sent because of sig_ignor
-		 * we will not get a call back to restart it AND
-		 * it should be restarted.
+		 * 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);
@@ -358,34 +359,31 @@ static enum hrtimer_restart posix_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 which involves sighand locking
-			 * (sigh !), which we don't want to do late in
-			 * the release cycle.
+			 * change to the signal handling code.
+			 *
+			 * For now let timers with an interval less than a
+			 * jiffie expire every jiffie to avoid softirq
+			 * starvation in case of SIG_IGN and a very small
+			 * interval, which would put the timer right back
+			 * on the softirq pending list. Moving now ahead of
+			 * time tricks hrtimer_forward() to expire the
+			 * timer later, while it still maintains the
+			 * overrun accuracy for the price of a slightly
+			 * inconsistency in the timer_gettime() case. This
+			 * is at least better than a starved softirq.
 			 *
-			 * For now we just let timers with an interval
-			 * less than a jiffie expire every jiffie to
-			 * avoid softirq starvation in case of SIG_IGN
-			 * and a very small interval, which would put
-			 * the timer right back on the softirq pending
-			 * list. By moving now ahead of time we trick
-			 * hrtimer_forward() to expire the timer
-			 * later, while we still maintain the overrun
-			 * accuracy, but have some inconsistency in
-			 * the timer_gettime() case. This is at least
-			 * better than a starved softirq. A more
-			 * complex fix which solves also another related
-			 * inconsistency is already in the pipeline.
+			 * Only required when high resolution timers are
+			 * enabled as the periodic tick based timers are
+			 * automatically aligned to the next tick.
 			 */
-#ifdef CONFIG_HIGH_RES_TIMERS
-			{
+			if (IS_ENABLED(CONFIG_HIGHRES_TIMERS)) {
 				ktime_t kj = NSEC_PER_SEC / HZ;
 
 				if (timr->it_interval < kj)
 					now = ktime_add(now, kj);
 			}
-#endif
-			timr->it_overrun += hrtimer_forward(timer, now,
-							    timr->it_interval);
+
+			timr->it_overrun += hrtimer_forward(timer, now, timr->it_interval);
 			ret = HRTIMER_RESTART;
 			++timr->it_requeue_pending;
 			timr->it_active = 1;
#104 lore raw

Re: [patch 18/20] posix-timers: Clarify posix_timer_fn() comments

Quoted 11 lines · Thomas Gleixner

Make the issues vs. SIG_IGN understandable and remove the 15 years old promise that a proper solution is already on the horizon.

---
 kernel/time/posix-timers.c |   56 +++++++++++++++++++++------------------------
 1 file changed, 27 insertions(+), 29 deletions(-)

kernel/time/posix-timers.c int posix_timer_event(struct k_itimer *t · line 329

Quoted 10 lines
kernel/time/posix-timers.c +2 -5
@@ -325,11 +325,11 @@ int posix_timer_event(struct k_itimer *t
 }
 
 /*
- * This function gets called when a POSIX.1b interval timer expires.  It
- * is used as a callback from the kernel internal timer.  The
- * run_timer_list code ALWAYS calls with interrupts on.
-
- * This code is for CLOCK_REALTIME* and CLOCK_MONOTONIC* timers.
+ * This function gets called when a POSIX.1b interval timer expires from
+ * the HRTIMER soft interrupt with interrupts enabled.

BTW, what arranges for this to be called in softirq with interrupts enabled? The modes I see used here are HRTIMER_MODE_ABS or HRTIMER_MODE_REL and not their _SOFT counterparts.

Quoted 7 lines
kernel/time/posix-timers.c +3 -0
+ *
+ * Handles CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_BOOTTIME and CLOCK_TAI
+ * based timers.
  */
 static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
 {

kernel/time/posix-timers.c static enum hrtimer_restart posix_timer_ · line 365

Quoted 10 lines
kernel/time/posix-timers.c +4 -3
@@ -358,34 +359,31 @@ static enum hrtimer_restart posix_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 which involves sighand locking
-			 * (sigh !), which we don't want to do late in
-			 * the release cycle.
+			 * change to the signal handling code.
+			 *
+			 * For now let timers with an interval less than a
+			 * jiffie expire every jiffie to avoid softirq

Or rather at least to the next jiffie, right? Because then in the next jiffie it gets re-evaluated in case a real signal handler might have been set in-between.

Or it could be:

+ * For now let timers with an interval less than a
+ * jiffie expire every jiffie (until a real sig handler
+ * is found set) to avoid softirq...

Quoted 8 lines
kernel/time/posix-timers.c +8 -0
+			 * starvation in case of SIG_IGN and a very small
+			 * interval, which would put the timer right back
+			 * on the softirq pending list. Moving now ahead of
+			 * time tricks hrtimer_forward() to expire the
+			 * timer later, while it still maintains the
+			 * overrun accuracy for the price of a slightly
+			 * inconsistency in the timer_gettime() case. This
+			 * is at least better than a starved softirq.

[...]

kernel/time/posix-timers.c static enum hrtimer_restart posix_timer_ · line 380

Quoted 5 lines
kernel/time/posix-timers.c +1 -2
 			 */
-#ifdef CONFIG_HIGH_RES_TIMERS
-			{
+			if (IS_ENABLED(CONFIG_HIGHRES_TIMERS)) {
 				ktime_t kj = NSEC_PER_SEC / HZ;

Could be TICK_NSECS?

Thanks!

Quoted 13 lines
kernel/time/posix-timers.c +2 -3
 				if (timr->it_interval < kj)
 					now = ktime_add(now, kj);
 			}
-#endif
-			timr->it_overrun += hrtimer_forward(timer, now,
-							    timr->it_interval);
+
+			timr->it_overrun += hrtimer_forward(timer, now, timr->it_interval);
 			ret = HRTIMER_RESTART;
 			++timr->it_requeue_pending;
 			timr->it_active = 1;
#105 lore raw

Re: [patch 18/20] posix-timers: Clarify posix_timer_fn() comments

Quoted 14 lines · Frederic Weisbecker
Quoted 13 lines · Thomas Gleixner

Make the issues vs. SIG_IGN understandable and remove the 15 years old promise that a proper solution is already on the horizon.

---
 kernel/time/posix-timers.c |   56 +++++++++++++++++++++------------------------
 1 file changed, 27 insertions(+), 29 deletions(-)
kernel/time/posix-timers.c +0 -0
@@ -325,11 +325,11 @@ int posix_timer_event(struct k_itimer *t
 }
 
Quoted 12 lines

kernel/time/posix-timers.c int posix_timer_event(struct k_itimer *t · line 329

Quoted 8 lines
kernel/time/posix-timers.c +2 -5
 /*
- * This function gets called when a POSIX.1b interval timer expires.  It
- * is used as a callback from the kernel internal timer.  The
- * run_timer_list code ALWAYS calls with interrupts on.
-
- * This code is for CLOCK_REALTIME* and CLOCK_MONOTONIC* timers.
+ * This function gets called when a POSIX.1b interval timer expires from
+ * the HRTIMER soft interrupt with interrupts enabled.

BTW, what arranges for this to be called in softirq with interrupts enabled? The modes I see used here are HRTIMER_MODE_ABS or HRTIMER_MODE_REL and not their _SOFT counterparts.

Duh. My RT biased brain tricked me.

Quoted 22 lines
Quoted 7 lines
kernel/time/posix-timers.c +3 -0
+ *
+ * Handles CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_BOOTTIME and CLOCK_TAI
+ * based timers.
  */
 static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
 {

kernel/time/posix-timers.c static enum hrtimer_restart posix_timer_ · line 365

Quoted 10 lines
kernel/time/posix-timers.c +4 -3
@@ -358,34 +359,31 @@ static enum hrtimer_restart posix_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 which involves sighand locking
-			 * (sigh !), which we don't want to do late in
-			 * the release cycle.
+			 * change to the signal handling code.
+			 *
+			 * For now let timers with an interval less than a
+			 * jiffie expire every jiffie to avoid softirq

Or rather at least to the next jiffie, right? Because then in the next jiffie it gets re-evaluated in case a real signal handler might have been set in-between.

Quoted 5 lines

Or it could be:

+ * For now let timers with an interval less than a
+ * jiffie expire every jiffie (until a real sig handler
+ * is found set) to avoid softirq...

Let me rephrase that.

Quoted 9 lines
Quoted 8 lines
kernel/time/posix-timers.c +8 -0
+			 * starvation in case of SIG_IGN and a very small
+			 * interval, which would put the timer right back
+			 * on the softirq pending list. Moving now ahead of
+			 * time tricks hrtimer_forward() to expire the
+			 * timer later, while it still maintains the
+			 * overrun accuracy for the price of a slightly
+			 * inconsistency in the timer_gettime() case. This
+			 * is at least better than a starved softirq.

[...]

Quoted 7 lines

kernel/time/posix-timers.c static enum hrtimer_restart posix_timer_ · line 380

Quoted 5 lines
kernel/time/posix-timers.c +1 -2
 			 */
-#ifdef CONFIG_HIGH_RES_TIMERS
-			{
+			if (IS_ENABLED(CONFIG_HIGHRES_TIMERS)) {
 				ktime_t kj = NSEC_PER_SEC / HZ;

Could be TICK_NSECS?

Yep. Fixed it up.

#106 lore raw

Re: [patch 18/20] posix-timers: Clarify posix_timer_fn() comments

In reply to Frederic Weisbecker

Make the issues vs. SIG_IGN understandable and remove the 15 years old promise that a proper solution is already on the horizon.

---
V2: Clarify comments and use TICK_NSECS - Frederic

---
 kernel/time/posix-timers.c |   62 +++++++++++++++++++++++----------------------
 1 file changed, 32 insertions(+), 30 deletions(-)
kernel/time/posix-timers.c +32 -30
@@ -326,11 +326,11 @@ int posix_timer_event(struct k_itimer *t
 }
 
 /*
- * This function gets called when a POSIX.1b interval timer expires.  It
- * is used as a callback from the kernel internal timer.  The
- * run_timer_list code ALWAYS calls with interrupts on.
-
- * This code is for CLOCK_REALTIME* and CLOCK_MONOTONIC* timers.
+ * This function gets called when a POSIX.1b interval timer expires from
+ * the HRTIMER interrupt (soft interrupt on RT kernels).
+ *
+ * Handles CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_BOOTTIME and CLOCK_TAI
+ * based timers.
  */
 static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
 {
@@ -348,9 +348,10 @@ static enum hrtimer_restart posix_timer_
 
 	if (posix_timer_event(timr, si_private)) {
 		/*
-		 * signal was not sent because of sig_ignor
-		 * we will not get a call back to restart it AND
-		 * it should be restarted.
+		 * 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);
@@ -359,34 +360,35 @@ static enum hrtimer_restart posix_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 which involves sighand locking
-			 * (sigh !), which we don't want to do late in
-			 * the release cycle.
+			 * change to the signal handling code.
+			 *
+			 * For now let timers with an interval less than a
+			 * jiffie expire every jiffie 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.
 			 *
-			 * For now we just let timers with an interval
-			 * less than a jiffie expire every jiffie to
-			 * avoid softirq starvation in case of SIG_IGN
-			 * and a very small interval, which would put
-			 * the timer right back on the softirq pending
-			 * list. By moving now ahead of time we trick
-			 * hrtimer_forward() to expire the timer
-			 * later, while we still maintain the overrun
-			 * accuracy, but have some inconsistency in
-			 * the timer_gettime() case. This is at least
-			 * better than a starved softirq. A more
-			 * complex fix which solves also another related
-			 * inconsistency is already in the pipeline.
+			 * Moving now ahead of time by one jiffie 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 starved softirq.
+			 *
+			 * Only required when high resolution timers are
+			 * enabled as the periodic tick based timers are
+			 * automatically aligned to the next tick.
 			 */
-#ifdef CONFIG_HIGH_RES_TIMERS
-			{
-				ktime_t kj = NSEC_PER_SEC / HZ;
+			if (IS_ENABLED(CONFIG_HIGHRES_TIMERS)) {
+				ktime_t kj = TICK_NSECS;
 
 				if (timr->it_interval < kj)
 					now = ktime_add(now, kj);
 			}
-#endif
-			timr->it_overrun += hrtimer_forward(timer, now,
-							    timr->it_interval);
+
+			timr->it_overrun += hrtimer_forward(timer, now, timr->it_interval);
 			ret = HRTIMER_RESTART;
 			++timr->it_requeue_pending;
 			timr->it_active = 1;
maintainer Reviewed-by
#107 lore raw

Re: [patch 18/20] posix-timers: Clarify posix_timer_fn() comments

Quoted 26 lines · Thomas Gleixner
kernel/time/posix-timers.c +9 -12
@@ -359,34 +360,35 @@ static enum hrtimer_restart posix_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 which involves sighand locking
-			 * (sigh !), which we don't want to do late in
-			 * the release cycle.
+			 * change to the signal handling code.
+			 *
+			 * For now let timers with an interval less than a
+			 * jiffie expire every jiffie 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.
 			 *
-			 * For now we just let timers with an interval
-			 * less than a jiffie expire every jiffie to
-			 * avoid softirq starvation in case of SIG_IGN
-			 * and a very small interval, which would put
-			 * the timer right back on the softirq pending
-			 * list. By moving now ahead of time we trick
-			 * hrtimer_forward() to expire the timer
-			 * later, while we still maintain the overrun
-			 * accuracy, but have some inconsistency in

kernel/time/posix-timers.c static enum hrtimer_restart posix_timer_ · line 378

Quoted 10 lines
kernel/time/posix-timers.c +6 -4
-			 * the timer_gettime() case. This is at least
-			 * better than a starved softirq. A more
-			 * complex fix which solves also another related
-			 * inconsistency is already in the pipeline.
+			 * Moving now ahead of time by one jiffie 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 starved softirq.

Could be hardirq. How about:

"This is at least better than a timer storm."

#108 lore raw

[tip: timers/core] posix-timers: Clarify posix_timer_fn() comments

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     63dede13d09850a8ace210f8e4227ac5a6b309ae
Gitweb:        https://git.kernel.org/tip/63dede13d09850a8ace210f8e4227ac5a6b309ae
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Thu, 01 Jun 2023 21:07:37 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:38 +02:00

posix-timers: Clarify posix_timer_fn() comments

Make the issues vs. SIG_IGN understandable and remove the 15 years old promise that a proper solution is already on the horizon.

---
 kernel/time/posix-timers.c | 62 +++++++++++++++++++------------------
 1 file changed, 32 insertions(+), 30 deletions(-)
kernel/time/posix-timers.c +32 -30
index f1a7c62..a22c183 100644
@@ -326,11 +326,11 @@ int posix_timer_event(struct k_itimer *timr, int si_private)
 }
 
 /*
- * This function gets called when a POSIX.1b interval timer expires.  It
- * is used as a callback from the kernel internal timer.  The
- * run_timer_list code ALWAYS calls with interrupts on.
-
- * This code is for CLOCK_REALTIME* and CLOCK_MONOTONIC* timers.
+ * This function gets called when a POSIX.1b interval timer expires from
+ * the HRTIMER interrupt (soft interrupt on RT kernels).
+ *
+ * Handles CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_BOOTTIME and CLOCK_TAI
+ * based timers.
  */
 static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
 {
@@ -348,9 +348,10 @@ static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
 
 	if (posix_timer_event(timr, si_private)) {
 		/*
-		 * signal was not sent because of sig_ignor
-		 * we will not get a call back to restart it AND
-		 * it should be restarted.
+		 * 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);
@@ -359,34 +360,35 @@ static enum hrtimer_restart posix_timer_fn(struct hrtimer *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 which involves sighand locking
-			 * (sigh !), which we don't want to do late in
-			 * the release cycle.
+			 * change to the signal handling code.
+			 *
+			 * For now let timers with an interval less than a
+			 * jiffie expire every jiffie 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.
 			 *
-			 * For now we just let timers with an interval
-			 * less than a jiffie expire every jiffie to
-			 * avoid softirq starvation in case of SIG_IGN
-			 * and a very small interval, which would put
-			 * the timer right back on the softirq pending
-			 * list. By moving now ahead of time we trick
-			 * hrtimer_forward() to expire the timer
-			 * later, while we still maintain the overrun
-			 * accuracy, but have some inconsistency in
-			 * the timer_gettime() case. This is at least
-			 * better than a starved softirq. A more
-			 * complex fix which solves also another related
-			 * inconsistency is already in the pipeline.
+			 * Moving now ahead of time by one jiffie 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.
 			 */
-#ifdef CONFIG_HIGH_RES_TIMERS
-			{
-				ktime_t kj = NSEC_PER_SEC / HZ;
+			if (IS_ENABLED(CONFIG_HIGHRES_TIMERS)) {
+				ktime_t kj = TICK_NSECS;
 
 				if (timr->it_interval < kj)
 					now = ktime_add(now, kj);
 			}
-#endif
-			timr->it_overrun += hrtimer_forward(timer, now,
-							    timr->it_interval);
+
+			timr->it_overrun += hrtimer_forward(timer, now, timr->it_interval);
 			ret = HRTIMER_RESTART;
 			++timr->it_requeue_pending;
 			timr->it_active = 1;
#109 lore raw

[tip: timers/core] posix-timers: Clarify posix_timer_fn() comments

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     c78f261e5dcb415b9e35a13876fbf7d5f134c810
Gitweb:        https://git.kernel.org/tip/c78f261e5dcb415b9e35a13876fbf7d5f134c810
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Thu, 01 Jun 2023 21:07:37 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 06 Jun 2023 00:12:55 +02:00

posix-timers: Clarify posix_timer_fn() comments

Make the issues vs. SIG_IGN understandable and remove the 15 years old promise that a proper solution is already on the horizon.

---
 kernel/time/posix-timers.c | 62 +++++++++++++++++++------------------
 1 file changed, 32 insertions(+), 30 deletions(-)
kernel/time/posix-timers.c +32 -30
index f1a7c62..a942020 100644
@@ -326,11 +326,11 @@ int posix_timer_event(struct k_itimer *timr, int si_private)
 }
 
 /*
- * This function gets called when a POSIX.1b interval timer expires.  It
- * is used as a callback from the kernel internal timer.  The
- * run_timer_list code ALWAYS calls with interrupts on.
-
- * This code is for CLOCK_REALTIME* and CLOCK_MONOTONIC* timers.
+ * This function gets called when a POSIX.1b interval timer expires from
+ * the HRTIMER interrupt (soft interrupt on RT kernels).
+ *
+ * Handles CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_BOOTTIME and CLOCK_TAI
+ * based timers.
  */
 static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
 {
@@ -348,9 +348,10 @@ static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
 
 	if (posix_timer_event(timr, si_private)) {
 		/*
-		 * signal was not sent because of sig_ignor
-		 * we will not get a call back to restart it AND
-		 * it should be restarted.
+		 * 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);
@@ -359,34 +360,35 @@ static enum hrtimer_restart posix_timer_fn(struct hrtimer *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 which involves sighand locking
-			 * (sigh !), which we don't want to do late in
-			 * the release cycle.
+			 * change to the signal handling code.
+			 *
+			 * For now let timers with an interval less than a
+			 * jiffie expire every jiffie 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.
 			 *
-			 * For now we just let timers with an interval
-			 * less than a jiffie expire every jiffie to
-			 * avoid softirq starvation in case of SIG_IGN
-			 * and a very small interval, which would put
-			 * the timer right back on the softirq pending
-			 * list. By moving now ahead of time we trick
-			 * hrtimer_forward() to expire the timer
-			 * later, while we still maintain the overrun
-			 * accuracy, but have some inconsistency in
-			 * the timer_gettime() case. This is at least
-			 * better than a starved softirq. A more
-			 * complex fix which solves also another related
-			 * inconsistency is already in the pipeline.
+			 * Moving now ahead of time by one jiffie 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.
 			 */
-#ifdef CONFIG_HIGH_RES_TIMERS
-			{
-				ktime_t kj = NSEC_PER_SEC / HZ;
+			if (IS_ENABLED(CONFIG_HIGHRES_TIMERS)) {
+				ktime_t kj = TICK_NSEC;
 
 				if (timr->it_interval < kj)
 					now = ktime_add(now, kj);
 			}
-#endif
-			timr->it_overrun += hrtimer_forward(timer, now,
-							    timr->it_interval);
+
+			timr->it_overrun += hrtimer_forward(timer, now, timr->it_interval);
 			ret = HRTIMER_RESTART;
 			++timr->it_requeue_pending;
 			timr->it_active = 1;
#110 lore raw

[tip: timers/core] posix-timers: Clarify posix_timer_fn() comments

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     84999b8bdb4969816c7bb7c14c3a55ed42aa4b94
Gitweb:        https://git.kernel.org/tip/84999b8bdb4969816c7bb7c14c3a55ed42aa4b94
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Thu, 01 Jun 2023 21:07:37 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:52 +02:00

posix-timers: Clarify posix_timer_fn() comments

Make the issues vs. SIG_IGN understandable and remove the 15 years old promise that a proper solution is already on the horizon.

---
 kernel/time/posix-timers.c | 62 +++++++++++++++++++------------------
 1 file changed, 32 insertions(+), 30 deletions(-)
kernel/time/posix-timers.c +32 -30
index d8d2169..ae8799e 100644
@@ -326,11 +326,11 @@ int posix_timer_event(struct k_itimer *timr, int si_private)
 }
 
 /*
- * This function gets called when a POSIX.1b interval timer expires.  It
- * is used as a callback from the kernel internal timer.  The
- * run_timer_list code ALWAYS calls with interrupts on.
-
- * This code is for CLOCK_REALTIME* and CLOCK_MONOTONIC* timers.
+ * This function gets called when a POSIX.1b interval timer expires from
+ * the HRTIMER interrupt (soft interrupt on RT kernels).
+ *
+ * Handles CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_BOOTTIME and CLOCK_TAI
+ * based timers.
  */
 static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
 {
@@ -348,9 +348,10 @@ static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
 
 	if (posix_timer_event(timr, si_private)) {
 		/*
-		 * signal was not sent because of sig_ignor
-		 * we will not get a call back to restart it AND
-		 * it should be restarted.
+		 * 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);
@@ -359,34 +360,35 @@ static enum hrtimer_restart posix_timer_fn(struct hrtimer *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 which involves sighand locking
-			 * (sigh !), which we don't want to do late in
-			 * the release cycle.
+			 * change to the signal handling code.
+			 *
+			 * For now let timers with an interval less than a
+			 * jiffie expire every jiffie 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.
 			 *
-			 * For now we just let timers with an interval
-			 * less than a jiffie expire every jiffie to
-			 * avoid softirq starvation in case of SIG_IGN
-			 * and a very small interval, which would put
-			 * the timer right back on the softirq pending
-			 * list. By moving now ahead of time we trick
-			 * hrtimer_forward() to expire the timer
-			 * later, while we still maintain the overrun
-			 * accuracy, but have some inconsistency in
-			 * the timer_gettime() case. This is at least
-			 * better than a starved softirq. A more
-			 * complex fix which solves also another related
-			 * inconsistency is already in the pipeline.
+			 * Moving now ahead of time by one jiffie 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.
 			 */
-#ifdef CONFIG_HIGH_RES_TIMERS
-			{
-				ktime_t kj = NSEC_PER_SEC / HZ;
+			if (IS_ENABLED(CONFIG_HIGHRES_TIMERS)) {
+				ktime_t kj = TICK_NSEC;
 
 				if (timr->it_interval < kj)
 					now = ktime_add(now, kj);
 			}
-#endif
-			timr->it_overrun += hrtimer_forward(timer, now,
-							    timr->it_interval);
+
+			timr->it_overrun += hrtimer_forward(timer, now, timr->it_interval);
 			ret = HRTIMER_RESTART;
 			++timr->it_requeue_pending;
 			timr->it_active = 1;
#111 lore raw

PATCH 19/20 posix-timers: Remove pointless comments

In reply to Thomas Gleixner

Documenting the obvious is just consuming space for no value.

---
 kernel/time/posix-timers.c |   25 -------------------------
 1 file changed, 25 deletions(-)
kernel/time/posix-timers.c +0 -25
@@ -59,19 +59,6 @@ static const struct k_clock clock_realti
 #error "SIGEV_THREAD_ID must not share bit with other SIGEV values!"
 #endif
 
-/*
- * CLOCKs: The POSIX standard calls for a couple of clocks and allows us
- *	    to implement others.  This structure defines the various
- *	    clocks.
- *
- * FUNCTIONS: The CLOCKs structure defines possible functions to
- *	    handle various clock functions.
- *
- *	    The standard POSIX timer management code assumes the
- *	    following: 1.) The k_itimer struct (sched.h) is used for
- *	    the timer.  2.) The list, it_lock, it_clock, it_id and
- *	    it_pid fields are not modified by timer code.
- */
 static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags);
 
 #define lock_timer(tid, flags)						   \
@@ -140,7 +127,6 @@ static inline void unlock_timer(struct k
 	spin_unlock_irqrestore(&timr->it_lock, flags);
 }
 
-/* Get clock_realtime */
 static int posix_get_realtime_timespec(clockid_t which_clock, struct timespec64 *tp)
 {
 	ktime_get_real_ts64(tp);
@@ -152,7 +138,6 @@ static ktime_t posix_get_realtime_ktime(
 	return ktime_get_real();
 }
 
-/* Set clock_realtime */
 static int posix_clock_realtime_set(const clockid_t which_clock,
 				    const struct timespec64 *tp)
 {
@@ -165,9 +150,6 @@ static int posix_clock_realtime_adj(cons
 	return do_adjtimex(t);
 }
 
-/*
- * Get monotonic time for posix timers
- */
 static int posix_get_monotonic_timespec(clockid_t which_clock, struct timespec64 *tp)
 {
 	ktime_get_ts64(tp);
@@ -180,9 +162,6 @@ static ktime_t posix_get_monotonic_ktime
 	return ktime_get();
 }
 
-/*
- * Get monotonic-raw time for posix timers
- */
 static int posix_get_monotonic_raw(clockid_t which_clock, struct timespec64 *tp)
 {
 	ktime_get_raw_ts64(tp);
@@ -190,7 +169,6 @@ static int posix_get_monotonic_raw(clock
 	return 0;
 }
 
-
 static int posix_get_realtime_coarse(clockid_t which_clock, struct timespec64 *tp)
 {
 	ktime_get_coarse_real_ts64(tp);
@@ -241,9 +219,6 @@ static int posix_get_hrtimer_res(clockid
 	return 0;
 }
 
-/*
- * Initialize everything, well, just everything in Posix clocks/timers ;)
- */
 static __init int init_posix_timers(void)
 {
 	posix_timers_cache = kmem_cache_create("posix_timers_cache",
#113 lore raw

[tip: timers/core] posix-timers: Remove pointless comments

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     478a93ac9ed7da5e7cd98e00502b142e22dab4d7
Gitweb:        https://git.kernel.org/tip/478a93ac9ed7da5e7cd98e00502b142e22dab4d7
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:25 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:39 +02:00

posix-timers: Remove pointless comments

Documenting the obvious is just consuming space for no value.

---
 kernel/time/posix-timers.c | 25 -------------------------
 1 file changed, 25 deletions(-)
kernel/time/posix-timers.c +0 -25
index a22c183..fde1ca9 100644
@@ -59,19 +59,6 @@ static const struct k_clock clock_realtime, clock_monotonic;
 #error "SIGEV_THREAD_ID must not share bit with other SIGEV values!"
 #endif
 
-/*
- * CLOCKs: The POSIX standard calls for a couple of clocks and allows us
- *	    to implement others.  This structure defines the various
- *	    clocks.
- *
- * FUNCTIONS: The CLOCKs structure defines possible functions to
- *	    handle various clock functions.
- *
- *	    The standard POSIX timer management code assumes the
- *	    following: 1.) The k_itimer struct (sched.h) is used for
- *	    the timer.  2.) The list, it_lock, it_clock, it_id and
- *	    it_pid fields are not modified by timer code.
- */
 static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags);
 
 #define lock_timer(tid, flags)						   \
@@ -141,7 +128,6 @@ static inline void unlock_timer(struct k_itimer *timr, unsigned long flags)
 	spin_unlock_irqrestore(&timr->it_lock, flags);
 }
 
-/* Get clock_realtime */
 static int posix_get_realtime_timespec(clockid_t which_clock, struct timespec64 *tp)
 {
 	ktime_get_real_ts64(tp);
@@ -153,7 +139,6 @@ static ktime_t posix_get_realtime_ktime(clockid_t which_clock)
 	return ktime_get_real();
 }
 
-/* Set clock_realtime */
 static int posix_clock_realtime_set(const clockid_t which_clock,
 				    const struct timespec64 *tp)
 {
@@ -166,9 +151,6 @@ static int posix_clock_realtime_adj(const clockid_t which_clock,
 	return do_adjtimex(t);
 }
 
-/*
- * Get monotonic time for posix timers
- */
 static int posix_get_monotonic_timespec(clockid_t which_clock, struct timespec64 *tp)
 {
 	ktime_get_ts64(tp);
@@ -181,9 +163,6 @@ static ktime_t posix_get_monotonic_ktime(clockid_t which_clock)
 	return ktime_get();
 }
 
-/*
- * Get monotonic-raw time for posix timers
- */
 static int posix_get_monotonic_raw(clockid_t which_clock, struct timespec64 *tp)
 {
 	ktime_get_raw_ts64(tp);
@@ -191,7 +170,6 @@ static int posix_get_monotonic_raw(clockid_t which_clock, struct timespec64 *tp)
 	return 0;
 }
 
-
 static int posix_get_realtime_coarse(clockid_t which_clock, struct timespec64 *tp)
 {
 	ktime_get_coarse_real_ts64(tp);
@@ -242,9 +220,6 @@ static int posix_get_hrtimer_res(clockid_t which_clock, struct timespec64 *tp)
 	return 0;
 }
 
-/*
- * Initialize everything, well, just everything in Posix clocks/timers ;)
- */
 static __init int init_posix_timers(void)
 {
 	posix_timers_cache = kmem_cache_create("posix_timers_cache",
#114 lore raw

[tip: timers/core] posix-timers: Remove pointless comments

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     298356a3c78198f44ba5df65547a9085334daa38
Gitweb:        https://git.kernel.org/tip/298356a3c78198f44ba5df65547a9085334daa38
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:25 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 06 Jun 2023 00:12:55 +02:00

posix-timers: Remove pointless comments

Documenting the obvious is just consuming space for no value.

---
 kernel/time/posix-timers.c | 25 -------------------------
 1 file changed, 25 deletions(-)
kernel/time/posix-timers.c +0 -25
index a942020..bdd718c 100644
@@ -59,19 +59,6 @@ static const struct k_clock clock_realtime, clock_monotonic;
 #error "SIGEV_THREAD_ID must not share bit with other SIGEV values!"
 #endif
 
-/*
- * CLOCKs: The POSIX standard calls for a couple of clocks and allows us
- *	    to implement others.  This structure defines the various
- *	    clocks.
- *
- * FUNCTIONS: The CLOCKs structure defines possible functions to
- *	    handle various clock functions.
- *
- *	    The standard POSIX timer management code assumes the
- *	    following: 1.) The k_itimer struct (sched.h) is used for
- *	    the timer.  2.) The list, it_lock, it_clock, it_id and
- *	    it_pid fields are not modified by timer code.
- */
 static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags);
 
 #define lock_timer(tid, flags)						   \
@@ -141,7 +128,6 @@ static inline void unlock_timer(struct k_itimer *timr, unsigned long flags)
 	spin_unlock_irqrestore(&timr->it_lock, flags);
 }
 
-/* Get clock_realtime */
 static int posix_get_realtime_timespec(clockid_t which_clock, struct timespec64 *tp)
 {
 	ktime_get_real_ts64(tp);
@@ -153,7 +139,6 @@ static ktime_t posix_get_realtime_ktime(clockid_t which_clock)
 	return ktime_get_real();
 }
 
-/* Set clock_realtime */
 static int posix_clock_realtime_set(const clockid_t which_clock,
 				    const struct timespec64 *tp)
 {
@@ -166,9 +151,6 @@ static int posix_clock_realtime_adj(const clockid_t which_clock,
 	return do_adjtimex(t);
 }
 
-/*
- * Get monotonic time for posix timers
- */
 static int posix_get_monotonic_timespec(clockid_t which_clock, struct timespec64 *tp)
 {
 	ktime_get_ts64(tp);
@@ -181,9 +163,6 @@ static ktime_t posix_get_monotonic_ktime(clockid_t which_clock)
 	return ktime_get();
 }
 
-/*
- * Get monotonic-raw time for posix timers
- */
 static int posix_get_monotonic_raw(clockid_t which_clock, struct timespec64 *tp)
 {
 	ktime_get_raw_ts64(tp);
@@ -191,7 +170,6 @@ static int posix_get_monotonic_raw(clockid_t which_clock, struct timespec64 *tp)
 	return 0;
 }
 
-
 static int posix_get_realtime_coarse(clockid_t which_clock, struct timespec64 *tp)
 {
 	ktime_get_coarse_real_ts64(tp);
@@ -242,9 +220,6 @@ static int posix_get_hrtimer_res(clockid_t which_clock, struct timespec64 *tp)
 	return 0;
 }
 
-/*
- * Initialize everything, well, just everything in Posix clocks/timers ;)
- */
 static __init int init_posix_timers(void)
 {
 	posix_timers_cache = kmem_cache_create("posix_timers_cache",
#115 lore raw

[tip: timers/core] posix-timers: Remove pointless comments

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     200dbd6d14e6a3b22162d78b4f7a253426dba7ee
Gitweb:        https://git.kernel.org/tip/200dbd6d14e6a3b22162d78b4f7a253426dba7ee
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:25 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:52 +02:00

posix-timers: Remove pointless comments

Documenting the obvious is just consuming space for no value.

---
 kernel/time/posix-timers.c | 25 -------------------------
 1 file changed, 25 deletions(-)
kernel/time/posix-timers.c +0 -25
index ae8799e..d357728 100644
@@ -59,19 +59,6 @@ static const struct k_clock clock_realtime, clock_monotonic;
 #error "SIGEV_THREAD_ID must not share bit with other SIGEV values!"
 #endif
 
-/*
- * CLOCKs: The POSIX standard calls for a couple of clocks and allows us
- *	    to implement others.  This structure defines the various
- *	    clocks.
- *
- * FUNCTIONS: The CLOCKs structure defines possible functions to
- *	    handle various clock functions.
- *
- *	    The standard POSIX timer management code assumes the
- *	    following: 1.) The k_itimer struct (sched.h) is used for
- *	    the timer.  2.) The list, it_lock, it_clock, it_id and
- *	    it_pid fields are not modified by timer code.
- */
 static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags);
 
 #define lock_timer(tid, flags)						   \
@@ -141,7 +128,6 @@ static inline void unlock_timer(struct k_itimer *timr, unsigned long flags)
 	spin_unlock_irqrestore(&timr->it_lock, flags);
 }
 
-/* Get clock_realtime */
 static int posix_get_realtime_timespec(clockid_t which_clock, struct timespec64 *tp)
 {
 	ktime_get_real_ts64(tp);
@@ -153,7 +139,6 @@ static ktime_t posix_get_realtime_ktime(clockid_t which_clock)
 	return ktime_get_real();
 }
 
-/* Set clock_realtime */
 static int posix_clock_realtime_set(const clockid_t which_clock,
 				    const struct timespec64 *tp)
 {
@@ -166,9 +151,6 @@ static int posix_clock_realtime_adj(const clockid_t which_clock,
 	return do_adjtimex(t);
 }
 
-/*
- * Get monotonic time for posix timers
- */
 static int posix_get_monotonic_timespec(clockid_t which_clock, struct timespec64 *tp)
 {
 	ktime_get_ts64(tp);
@@ -181,9 +163,6 @@ static ktime_t posix_get_monotonic_ktime(clockid_t which_clock)
 	return ktime_get();
 }
 
-/*
- * Get monotonic-raw time for posix timers
- */
 static int posix_get_monotonic_raw(clockid_t which_clock, struct timespec64 *tp)
 {
 	ktime_get_raw_ts64(tp);
@@ -191,7 +170,6 @@ static int posix_get_monotonic_raw(clockid_t which_clock, struct timespec64 *tp)
 	return 0;
 }
 
-
 static int posix_get_realtime_coarse(clockid_t which_clock, struct timespec64 *tp)
 {
 	ktime_get_coarse_real_ts64(tp);
@@ -242,9 +220,6 @@ static int posix_get_hrtimer_res(clockid_t which_clock, struct timespec64 *tp)
 	return 0;
 }
 
-/*
- * Initialize everything, well, just everything in Posix clocks/timers ;)
- */
 static __init int init_posix_timers(void)
 {
 	posix_timers_cache = kmem_cache_create("posix_timers_cache",
#116 lore raw

PATCH 20/20 posix-timers: Polish coding style in a few places

In reply to Thomas Gleixner

Make it consistent with the TIP tree documentation.

---
 kernel/time/posix-timers.c |   14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
kernel/time/posix-timers.c +7 -7
@@ -308,10 +308,10 @@ int posix_timer_event(struct k_itimer *t
  */
 static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
 {
+	enum hrtimer_restart ret = HRTIMER_NORESTART;
 	struct k_itimer *timr;
 	unsigned long flags;
 	int si_private = 0;
-	enum hrtimer_restart ret = HRTIMER_NORESTART;
 
 	timr = container_of(timer, struct k_itimer, it.real.timer);
 	spin_lock_irqsave(&timr->it_lock, flags);
@@ -395,8 +395,8 @@ static struct pid *good_sigevent(sigeven
 
 static struct k_itimer * alloc_posix_timer(void)
 {
-	struct k_itimer *tmr;
-	tmr = kmem_cache_zalloc(posix_timers_cache, GFP_KERNEL);
+	struct k_itimer *tmr = kmem_cache_zalloc(posix_timers_cache, GFP_KERNEL);
+
 	if (!tmr)
 		return tmr;
 	if (unlikely(!(tmr->sigq = sigqueue_alloc()))) {
@@ -690,8 +690,8 @@ void common_timer_get(struct k_itimer *t
 
 static int do_timer_gettime(timer_t timer_id,  struct itimerspec64 *setting)
 {
-	struct k_itimer *timr;
 	const struct k_clock *kc;
+	struct k_itimer *timr;
 	unsigned long flags;
 	int ret = 0;
 
@@ -762,8 +762,8 @@ SYSCALL_DEFINE2(timer_gettime32, timer_t
 SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id)
 {
 	struct k_itimer *timr;
-	int overrun;
 	unsigned long flags;
+	int overrun;
 
 	timr = lock_timer(timer_id, &flags);
 	if (!timr)
@@ -936,8 +936,7 @@ SYSCALL_DEFINE4(timer_settime, timer_t,
 		const struct __kernel_itimerspec __user *, new_setting,
 		struct __kernel_itimerspec __user *, old_setting)
 {
-	struct itimerspec64 new_spec, old_spec;
-	struct itimerspec64 *rtn = old_setting ? &old_spec : NULL;
+	struct itimerspec64 new_spec, old_spec, *rtn;
 	int error = 0;
 
 	if (!new_setting)
@@ -946,6 +945,7 @@ SYSCALL_DEFINE4(timer_settime, timer_t,
 	if (get_itimerspec64(&new_spec, new_setting))
 		return -EFAULT;
 
+	rtn = old_setting ? &old_spec : NULL;
 	error = do_timer_settime(timer_id, flags, &new_spec, rtn);
 	if (!error && old_setting) {
 		if (put_itimerspec64(&old_spec, old_setting))
#118 lore raw

[tip: timers/core] posix-timers: Polish coding style in a few places

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     1263a2a9d71bac5ffabf9603c36e36cb6edbcdcf
Gitweb:        https://git.kernel.org/tip/1263a2a9d71bac5ffabf9603c36e36cb6edbcdcf
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:27 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 05 Jun 2023 17:03:39 +02:00

posix-timers: Polish coding style in a few places

Make it consistent with the TIP tree documentation.

---
 kernel/time/posix-timers.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
kernel/time/posix-timers.c +7 -7
index fde1ca9..276f231 100644
@@ -309,10 +309,10 @@ int posix_timer_event(struct k_itimer *timr, int si_private)
  */
 static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
 {
+	enum hrtimer_restart ret = HRTIMER_NORESTART;
 	struct k_itimer *timr;
 	unsigned long flags;
 	int si_private = 0;
-	enum hrtimer_restart ret = HRTIMER_NORESTART;
 
 	timr = container_of(timer, struct k_itimer, it.real.timer);
 	spin_lock_irqsave(&timr->it_lock, flags);
@@ -400,8 +400,8 @@ static struct pid *good_sigevent(sigevent_t * event)
 
 static struct k_itimer * alloc_posix_timer(void)
 {
-	struct k_itimer *tmr;
-	tmr = kmem_cache_zalloc(posix_timers_cache, GFP_KERNEL);
+	struct k_itimer *tmr = kmem_cache_zalloc(posix_timers_cache, GFP_KERNEL);
+
 	if (!tmr)
 		return tmr;
 	if (unlikely(!(tmr->sigq = sigqueue_alloc()))) {
@@ -695,8 +695,8 @@ void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting)
 
 static int do_timer_gettime(timer_t timer_id,  struct itimerspec64 *setting)
 {
-	struct k_itimer *timr;
 	const struct k_clock *kc;
+	struct k_itimer *timr;
 	unsigned long flags;
 	int ret = 0;
 
@@ -767,8 +767,8 @@ SYSCALL_DEFINE2(timer_gettime32, timer_t, timer_id,
 SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id)
 {
 	struct k_itimer *timr;
-	int overrun;
 	unsigned long flags;
+	int overrun;
 
 	timr = lock_timer(timer_id, &flags);
 	if (!timr)
@@ -941,8 +941,7 @@ SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags,
 		const struct __kernel_itimerspec __user *, new_setting,
 		struct __kernel_itimerspec __user *, old_setting)
 {
-	struct itimerspec64 new_spec, old_spec;
-	struct itimerspec64 *rtn = old_setting ? &old_spec : NULL;
+	struct itimerspec64 new_spec, old_spec, *rtn;
 	int error = 0;
 
 	if (!new_setting)
@@ -951,6 +950,7 @@ SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags,
 	if (get_itimerspec64(&new_spec, new_setting))
 		return -EFAULT;
 
+	rtn = old_setting ? &old_spec : NULL;
 	error = do_timer_settime(timer_id, flags, &new_spec, rtn);
 	if (!error && old_setting) {
 		if (put_itimerspec64(&old_spec, old_setting))
#119 lore raw

[tip: timers/core] posix-timers: Polish coding style in a few places

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     6fbe3a3b4fa1fe5cf05f91193d81d930c6820eda
Gitweb:        https://git.kernel.org/tip/6fbe3a3b4fa1fe5cf05f91193d81d930c6820eda
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:27 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Tue, 06 Jun 2023 00:12:55 +02:00

posix-timers: Polish coding style in a few places

Make it consistent with the TIP tree documentation.

---
 kernel/time/posix-timers.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
kernel/time/posix-timers.c +7 -7
index bdd718c..8bb0dcf 100644
@@ -309,10 +309,10 @@ int posix_timer_event(struct k_itimer *timr, int si_private)
  */
 static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
 {
+	enum hrtimer_restart ret = HRTIMER_NORESTART;
 	struct k_itimer *timr;
 	unsigned long flags;
 	int si_private = 0;
-	enum hrtimer_restart ret = HRTIMER_NORESTART;
 
 	timr = container_of(timer, struct k_itimer, it.real.timer);
 	spin_lock_irqsave(&timr->it_lock, flags);
@@ -400,8 +400,8 @@ static struct pid *good_sigevent(sigevent_t * event)
 
 static struct k_itimer * alloc_posix_timer(void)
 {
-	struct k_itimer *tmr;
-	tmr = kmem_cache_zalloc(posix_timers_cache, GFP_KERNEL);
+	struct k_itimer *tmr = kmem_cache_zalloc(posix_timers_cache, GFP_KERNEL);
+
 	if (!tmr)
 		return tmr;
 	if (unlikely(!(tmr->sigq = sigqueue_alloc()))) {
@@ -695,8 +695,8 @@ void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting)
 
 static int do_timer_gettime(timer_t timer_id,  struct itimerspec64 *setting)
 {
-	struct k_itimer *timr;
 	const struct k_clock *kc;
+	struct k_itimer *timr;
 	unsigned long flags;
 	int ret = 0;
 
@@ -767,8 +767,8 @@ SYSCALL_DEFINE2(timer_gettime32, timer_t, timer_id,
 SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id)
 {
 	struct k_itimer *timr;
-	int overrun;
 	unsigned long flags;
+	int overrun;
 
 	timr = lock_timer(timer_id, &flags);
 	if (!timr)
@@ -941,8 +941,7 @@ SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags,
 		const struct __kernel_itimerspec __user *, new_setting,
 		struct __kernel_itimerspec __user *, old_setting)
 {
-	struct itimerspec64 new_spec, old_spec;
-	struct itimerspec64 *rtn = old_setting ? &old_spec : NULL;
+	struct itimerspec64 new_spec, old_spec, *rtn;
 	int error = 0;
 
 	if (!new_setting)
@@ -951,6 +950,7 @@ SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags,
 	if (get_itimerspec64(&new_spec, new_setting))
 		return -EFAULT;
 
+	rtn = old_setting ? &old_spec : NULL;
 	error = do_timer_settime(timer_id, flags, &new_spec, rtn);
 	if (!error && old_setting) {
 		if (put_itimerspec64(&old_spec, old_setting))
#120 lore raw

[tip: timers/core] posix-timers: Polish coding style in a few places

In reply to Thomas Gleixner
The following commit has been merged into the timers/core branch of tip:

The following commit has been merged into the timers/core branch of tip:

Commit-ID:     b96ce4931fcd13d73e32c62c2df3fa8f9f467e33
Gitweb:        https://git.kernel.org/tip/b96ce4931fcd13d73e32c62c2df3fa8f9f467e33
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Tue, 25 Apr 2023 20:49:27 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Sun, 18 Jun 2023 22:41:53 +02:00

posix-timers: Polish coding style in a few places

Make it consistent with the TIP tree documentation.

---
 kernel/time/posix-timers.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
kernel/time/posix-timers.c +7 -7
index d357728..e3cddd5 100644
@@ -309,10 +309,10 @@ int posix_timer_event(struct k_itimer *timr, int si_private)
  */
 static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
 {
+	enum hrtimer_restart ret = HRTIMER_NORESTART;
 	struct k_itimer *timr;
 	unsigned long flags;
 	int si_private = 0;
-	enum hrtimer_restart ret = HRTIMER_NORESTART;
 
 	timr = container_of(timer, struct k_itimer, it.real.timer);
 	spin_lock_irqsave(&timr->it_lock, flags);
@@ -400,8 +400,8 @@ static struct pid *good_sigevent(sigevent_t * event)
 
 static struct k_itimer * alloc_posix_timer(void)
 {
-	struct k_itimer *tmr;
-	tmr = kmem_cache_zalloc(posix_timers_cache, GFP_KERNEL);
+	struct k_itimer *tmr = kmem_cache_zalloc(posix_timers_cache, GFP_KERNEL);
+
 	if (!tmr)
 		return tmr;
 	if (unlikely(!(tmr->sigq = sigqueue_alloc()))) {
@@ -695,8 +695,8 @@ void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting)
 
 static int do_timer_gettime(timer_t timer_id,  struct itimerspec64 *setting)
 {
-	struct k_itimer *timr;
 	const struct k_clock *kc;
+	struct k_itimer *timr;
 	unsigned long flags;
 	int ret = 0;
 
@@ -767,8 +767,8 @@ SYSCALL_DEFINE2(timer_gettime32, timer_t, timer_id,
 SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id)
 {
 	struct k_itimer *timr;
-	int overrun;
 	unsigned long flags;
+	int overrun;
 
 	timr = lock_timer(timer_id, &flags);
 	if (!timr)
@@ -941,8 +941,7 @@ SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags,
 		const struct __kernel_itimerspec __user *, new_setting,
 		struct __kernel_itimerspec __user *, old_setting)
 {
-	struct itimerspec64 new_spec, old_spec;
-	struct itimerspec64 *rtn = old_setting ? &old_spec : NULL;
+	struct itimerspec64 new_spec, old_spec, *rtn;
 	int error = 0;
 
 	if (!new_setting)
@@ -951,6 +950,7 @@ SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags,
 	if (get_itimerspec64(&new_spec, new_setting))
 		return -EFAULT;
 
+	rtn = old_setting ? &old_spec : NULL;
 	error = do_timer_settime(timer_id, flags, &new_spec, rtn);
 	if (!error && old_setting) {
 		if (put_itimerspec64(&old_spec, old_setting))
#121 lore raw
In reply to Thomas Gleixner
Quoted 19 lines · Thomas Gleixner

Hi!

A recent syzcaller/KCSAN report about a data race and an actually broken loop termination due to that race made me look deeper into the posix timer code.

Aside of the loop termination issue this unearthed another issue in the exit() path where timer deletion might livelock on RT enabled kernels.

While going through the code with a fine comb, I stumbled over another place which needs READ/WRITE_ONCE() annotations, tons of outdated and/or uncomprehensible comments and a bunch of silly code.

The series has therefore a larger set of cleanups on top of the two fixes.

It is based on

git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers/core

Quoted 7 lines

and also availble from git:

git://git.kernel.org/pub/scm/linux/kernel/git/tglx/devel.git timers/posix

Thanks,

tglx

Thanks for this extremely useful series!