[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1465353004-15044-2-git-send-email-riel@redhat.com>
Date: Tue, 7 Jun 2016 22:30:00 -0400
From: riel@...hat.com
To: linux-kernel@...r.kernel.org
Cc: kernellwp@...il.com, mingo@...nel.org, peterz@...radead.org,
tglx@...utronix.de, fweisbec@...hat.com
Subject: [PATCH 1/5] sched,time: count actually elapsed irq & softirq time
From: Rik van Riel <riel@...hat.com>
Currently, if there was any irq or softirq time during 'ticks'
jiffies, the entire period will be accounted as irq or softirq
time.
This is inaccurate if only a subset of 'ticks' jiffies was
actually spent handling irqs, and could conceivably mis-count
all of the ticks during a period as irq time, when there was
some irq and some softirq time.
Fix this by changing irqtime_account_hi_update and
irqtime_account_si_update to round elapsed irq and softirq
time to jiffies, and return the number of jiffies spent in
each mode, similar to how steal time is handled.
Additionally, have irqtime_account_process_tick take into
account how much time was spent in each of steal, irq,
and softirq time.
The latter could help improve the accuracy of timekeeping
when returning from idle on a NO_HZ_IDLE CPU.
Properly accounting how much time was spent in hardirq and
softirq time will also allow the NO_HZ_FULL code to re-use
these same functions for hardirq and softirq accounting.
Signed-off-by: Rik van Riel <riel@...hat.com>
---
kernel/sched/cputime.c | 54 +++++++++++++++++++++++++++++++-------------------
1 file changed, 34 insertions(+), 20 deletions(-)
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
index f51c98c740b5..4bd6d1b774ab 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -79,34 +79,36 @@ void irqtime_account_irq(struct task_struct *curr)
}
EXPORT_SYMBOL_GPL(irqtime_account_irq);
-static int irqtime_account_hi_update(void)
+static unsigned long irqtime_account_hi_update(void)
{
u64 *cpustat = kcpustat_this_cpu->cpustat;
+ unsigned long irq_jiffies;
unsigned long flags;
- u64 latest_ns;
- int ret = 0;
+ u64 irq;
local_irq_save(flags);
- latest_ns = this_cpu_read(cpu_hardirq_time);
- if (nsecs_to_cputime64(latest_ns) > cpustat[CPUTIME_IRQ])
- ret = 1;
+ irq = this_cpu_read(cpu_hardirq_time) - cpustat[CPUTIME_IRQ];
+ irq_jiffies = cputime_to_jiffies(irq);
+ if (irq_jiffies)
+ cpustat[CPUTIME_IRQ] += jiffies_to_cputime(irq_jiffies);
local_irq_restore(flags);
- return ret;
+ return irq_jiffies;
}
-static int irqtime_account_si_update(void)
+static unsigned long irqtime_account_si_update(void)
{
u64 *cpustat = kcpustat_this_cpu->cpustat;
+ unsigned long si_jiffies;
unsigned long flags;
- u64 latest_ns;
- int ret = 0;
+ u64 softirq;
local_irq_save(flags);
- latest_ns = this_cpu_read(cpu_softirq_time);
- if (nsecs_to_cputime64(latest_ns) > cpustat[CPUTIME_SOFTIRQ])
- ret = 1;
+ delta = this_cpu_read(cpu_softirq_time) - cpustat[CPUTIME_SOFTIRQ];
+ si_jiffies = cputime_to_jiffies(delta);
+ if (si_jiffies)
+ cpustat[CPUSTIME_SOFTIRQ] += jiffies_to_cputime(si_jiffies);
local_irq_restore(flags);
- return ret;
+ return si_jiffies;
}
#else /* CONFIG_IRQ_TIME_ACCOUNTING */
@@ -345,18 +347,30 @@ static void irqtime_account_process_tick(struct task_struct *p, int user_tick,
cputime_t scaled = cputime_to_scaled(cputime_one_jiffy);
u64 cputime = (__force u64) cputime_one_jiffy;
u64 *cpustat = kcpustat_this_cpu->cpustat;
+ unsigned long other;
- if (steal_account_process_tick())
+ /*
+ * Subtract steal, irq & softirq time (if any) from ticks.
+ * These are counted in nanoseconds and not aligned with jiffies.
+ * Multiple of these could "break a jiffy" simultaneously due to
+ * rounding; be careful to not account too much of this time at once.
+ */
+ other = steal_account_process_tick();
+ if (other >= ticks)
+ return;
+
+ other += irqtime_account_hi_update();
+ if (other >= ticks)
+ return;
+
+ other += irqtime_account_si_update();
+ if (other >= ticks)
return;
cputime *= ticks;
scaled *= ticks;
- if (irqtime_account_hi_update()) {
- cpustat[CPUTIME_IRQ] += cputime;
- } else if (irqtime_account_si_update()) {
- cpustat[CPUTIME_SOFTIRQ] += cputime;
- } else if (this_cpu_ksoftirqd() == p) {
+ if (this_cpu_ksoftirqd() == p) {
/*
* ksoftirqd time do not get accounted in cpu_softirq_time.
* So, we have to handle it separately here.
--
2.5.5
Powered by blists - more mailing lists