]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
sched/cputime: Handle idle irqtime gracefully
authorFrederic Weisbecker <frederic@kernel.org>
Fri, 8 May 2026 13:16:46 +0000 (15:16 +0200)
committerThomas Gleixner <tglx@kernel.org>
Tue, 2 Jun 2026 19:27:26 +0000 (21:27 +0200)
The dyntick-idle cputime accounting always assumes that interrupt time
accounting is enabled and consequently stops elapsing the idle time during
dyntick-idle interrupts.

This doesn't mix up well with disabled interrupt time accounting because
then idle interrupts become a cputime blind-spot. Also this feature is
disabled on most configurations and the overhead of pausing dyntick-idle
accounting while in idle interrupts could then be avoided.

Fix the situation with conditionally pausing dyntick-idle accounting during
idle interrupts only iff either native vtime (which does interrupt time
accounting) or generic interrupt time accounting are enabled.

Also make sure that the accumulated interrupt time is not accidentally
substracted from later accounting.

Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com>
Link: https://patch.msgid.link/20260508131647.43868-15-frederic@kernel.org
kernel/sched/cputime.c

index 335d2c1277633d747206bafccbfd8692cb6980dc..94be22aa5cb693ace109cf05c0deb254b5a16bd9 100644 (file)
@@ -47,7 +47,8 @@ static void irqtime_account_delta(struct irqtime *irqtime, u64 delta,
        u64_stats_update_begin(&irqtime->sync);
        cpustat[idx] += delta;
        irqtime->total += delta;
-       irqtime->tick_delta += delta;
+       if (!kcpustat_idle_dyntick())
+               irqtime->tick_delta += delta;
        u64_stats_update_end(&irqtime->sync);
 }
 
@@ -444,6 +445,10 @@ static void kcpustat_idle_stop(struct kernel_cpustat *kc, u64 now)
 
 static void kcpustat_idle_start(struct kernel_cpustat *kc, u64 now)
 {
+       /* Irqtime accounting might have been enabled in the middle of the IRQ */
+       if (kc->idle_elapse)
+               return;
+
        write_seqcount_begin(&kc->idle_sleeptime_seq);
        kc->idle_entrytime = now;
        kc->idle_elapse = true;
@@ -478,7 +483,8 @@ void kcpustat_irq_enter(u64 now)
 {
        struct kernel_cpustat *kc = kcpustat_this_cpu;
 
-       if (!vtime_generic_enabled_this_cpu())
+       if (!vtime_generic_enabled_this_cpu() &&
+           (irqtime_enabled() || vtime_accounting_enabled_this_cpu()))
                kcpustat_idle_stop(kc, now);
 }
 
@@ -486,7 +492,15 @@ void kcpustat_irq_exit(u64 now)
 {
        struct kernel_cpustat *kc = kcpustat_this_cpu;
 
-       if (!vtime_generic_enabled_this_cpu())
+       /*
+        * Generic vtime already does its own idle accounting.
+        * But irqtime accounting or arch vtime which also accounts IRQs
+        * need to pause nohz accounting. Resume nohz accounting as long
+        * as the irqtime config is enabled to handle case where irqtime
+        * accounting got runtime disabled in the middle of an IRQ.
+        */
+       if (!vtime_generic_enabled_this_cpu() &&
+           (IS_ENABLED(CONFIG_IRQ_TIME_ACCOUNTING) || vtime_accounting_enabled_this_cpu()))
                kcpustat_idle_start(kc, now);
 }