[v9,11/32] timers: Rework idle logic

Message ID 20231201092654.34614-12-anna-maria@linutronix.de
State New
Headers
Series timers: Move from a push remote at enqueue to a pull at expiry model |

Commit Message

Anna-Maria Behnsen Dec. 1, 2023, 9:26 a.m. UTC
  From: Thomas Gleixner <tglx@linutronix.de>

To improve readability of the code, split base->idle calculation and
expires calculation into separate parts. While at it, update the comment
about timer base idle marking.

Thereby the following subtle change happens if the next event is just one
jiffy ahead and the tick was already stopped: Originally base->is_idle
remains true in this situation. Now base->is_idle turns to false. This may
spare an IPI if a timer is enqueued remotely to an idle CPU that is going
to tick on the next jiffy.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Anna-Maria Behnsen <anna-maria@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
---
v9: Re-ordering to not hurt the eyes and update comment
v4: Change condition to force 0 delta and update commit message (Frederic)
---
 kernel/time/timer.c | 31 ++++++++++++++++---------------
 1 file changed, 16 insertions(+), 15 deletions(-)
  

Comments

Frederic Weisbecker Dec. 20, 2023, 2 p.m. UTC | #1
Le Fri, Dec 01, 2023 at 10:26:33AM +0100, Anna-Maria Behnsen a écrit :
> From: Thomas Gleixner <tglx@linutronix.de>
> 
> To improve readability of the code, split base->idle calculation and
> expires calculation into separate parts. While at it, update the comment
> about timer base idle marking.
> 
> Thereby the following subtle change happens if the next event is just one
> jiffy ahead and the tick was already stopped: Originally base->is_idle
> remains true in this situation. Now base->is_idle turns to false. This may
> spare an IPI if a timer is enqueued remotely to an idle CPU that is going
> to tick on the next jiffy.
> 
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> Signed-off-by: Anna-Maria Behnsen <anna-maria@linutronix.de>
> Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
> ---
> v9: Re-ordering to not hurt the eyes and update comment
> v4: Change condition to force 0 delta and update commit message (Frederic)
> ---
>  kernel/time/timer.c | 31 ++++++++++++++++---------------
>  1 file changed, 16 insertions(+), 15 deletions(-)
> 
> diff --git a/kernel/time/timer.c b/kernel/time/timer.c
> index fee42dda8237..0826018d9873 100644
> --- a/kernel/time/timer.c
> +++ b/kernel/time/timer.c
> @@ -1943,22 +1943,23 @@ u64 get_next_timer_interrupt(unsigned long basej, u64 basem)
>  	 */
>  	__forward_timer_base(base, basej);
>  
> -	if (time_before_eq(nextevt, basej)) {
> -		expires = basem;
> -		base->is_idle = false;
> -	} else {
> -		if (base->timers_pending)
> -			expires = basem + (u64)(nextevt - basej) * TICK_NSEC;
> -		/*
> -		 * If we expect to sleep more than a tick, mark the base idle.
> -		 * Also the tick is stopped so any added timer must forward
> -		 * the base clk itself to keep granularity small. This idle
> -		 * logic is only maintained for the BASE_STD base, deferrable
> -		 * timers may still see large granularity skew (by design).
> -		 */
> -		if ((expires - basem) > TICK_NSEC)
> -			base->is_idle = true;
> +	if (base->timers_pending) {
> +		/* If we missed a tick already, force 0 delta */
> +		if (time_before(nextevt, basej))
> +			nextevt = basej;
> +		expires = basem + (u64)(nextevt - basej) * TICK_NSEC;
>  	}
> +
> +	/*
> +	 * Base is idle if the next event is more than a tick away.
> +	 *
> +	 * If the base is marked idle then any timer add operation must forward
> +	 * the base clk itself to keep granularity small. This idle logic is
> +	 * only maintained for the BASE_STD base, deferrable timers may still
> +	 * see large granularity skew (by design).
> +	 */
> +	base->is_idle = time_after(nextevt, basej + 1);
> +

Much better, thanks! :-)
  

Patch

diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index fee42dda8237..0826018d9873 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -1943,22 +1943,23 @@  u64 get_next_timer_interrupt(unsigned long basej, u64 basem)
 	 */
 	__forward_timer_base(base, basej);
 
-	if (time_before_eq(nextevt, basej)) {
-		expires = basem;
-		base->is_idle = false;
-	} else {
-		if (base->timers_pending)
-			expires = basem + (u64)(nextevt - basej) * TICK_NSEC;
-		/*
-		 * If we expect to sleep more than a tick, mark the base idle.
-		 * Also the tick is stopped so any added timer must forward
-		 * the base clk itself to keep granularity small. This idle
-		 * logic is only maintained for the BASE_STD base, deferrable
-		 * timers may still see large granularity skew (by design).
-		 */
-		if ((expires - basem) > TICK_NSEC)
-			base->is_idle = true;
+	if (base->timers_pending) {
+		/* If we missed a tick already, force 0 delta */
+		if (time_before(nextevt, basej))
+			nextevt = basej;
+		expires = basem + (u64)(nextevt - basej) * TICK_NSEC;
 	}
+
+	/*
+	 * Base is idle if the next event is more than a tick away.
+	 *
+	 * If the base is marked idle then any timer add operation must forward
+	 * the base clk itself to keep granularity small. This idle logic is
+	 * only maintained for the BASE_STD base, deferrable timers may still
+	 * see large granularity skew (by design).
+	 */
+	base->is_idle = time_after(nextevt, basej + 1);
+
 	trace_timer_base_idle(base->is_idle, base->cpu);
 	raw_spin_unlock(&base->lock);