sched/cpuacct: Fix user/system in shown cpuacct.usage*
authorAndrey Ryabinin <arbn@yandex-team.com>
Mon, 15 Nov 2021 16:46:06 +0000 (19:46 +0300)
committerPeter Zijlstra <peterz@infradead.org>
Tue, 23 Nov 2021 08:55:22 +0000 (09:55 +0100)
cpuacct has 2 different ways of accounting and showing user
and system times.

The first one uses cpuacct_account_field() to account times
and cpuacct.stat file to expose them. And this one seems to work ok.

The second one is uses cpuacct_charge() function for accounting and
set of cpuacct.usage* files to show times. Despite some attempts to
fix it in the past it still doesn't work. Sometimes while running KVM
guest the cpuacct_charge() accounts most of the guest time as
system time. This doesn't match with user&system times shown in
cpuacct.stat or proc/<pid>/stat.

Demonstration:
 # git clone https://github.com/aryabinin/kvmsample
 # make
 # mkdir /sys/fs/cgroup/cpuacct/test
 # echo $$ > /sys/fs/cgroup/cpuacct/test/tasks
 # ./kvmsample &
 # for i in {1..5}; do cat /sys/fs/cgroup/cpuacct/test/cpuacct.usage_sys; sleep 1; done
 1976535645
 2979839428
 3979832704
 4983603153
 5983604157

Use cpustats accounted in cpuacct_account_field() as the source
of user/sys times for cpuacct.usage* files. Make cpuacct_charge()
to account only summary execution time.

Fixes: d740037fac70 ("sched/cpuacct: Split usage accounting into user_usage and sys_usage")
Signed-off-by: Andrey Ryabinin <arbn@yandex-team.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Daniel Jordan <daniel.m.jordan@oracle.com>
Acked-by: Tejun Heo <tj@kernel.org>
Cc: <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/20211115164607.23784-3-arbn@yandex-team.com
kernel/sched/cpuacct.c

index f347cf9..9de7dd5 100644 (file)
@@ -21,15 +21,11 @@ static const char * const cpuacct_stat_desc[] = {
        [CPUACCT_STAT_SYSTEM] = "system",
 };
 
-struct cpuacct_usage {
-       u64     usages[CPUACCT_STAT_NSTATS];
-};
-
 /* track CPU usage of a group of tasks and its child groups */
 struct cpuacct {
        struct cgroup_subsys_state      css;
        /* cpuusage holds pointer to a u64-type object on every CPU */
-       struct cpuacct_usage __percpu   *cpuusage;
+       u64 __percpu    *cpuusage;
        struct kernel_cpustat __percpu  *cpustat;
 };
 
@@ -49,7 +45,7 @@ static inline struct cpuacct *parent_ca(struct cpuacct *ca)
        return css_ca(ca->css.parent);
 }
 
-static DEFINE_PER_CPU(struct cpuacct_usage, root_cpuacct_cpuusage);
+static DEFINE_PER_CPU(u64, root_cpuacct_cpuusage);
 static struct cpuacct root_cpuacct = {
        .cpustat        = &kernel_cpustat,
        .cpuusage       = &root_cpuacct_cpuusage,
@@ -68,7 +64,7 @@ cpuacct_css_alloc(struct cgroup_subsys_state *parent_css)
        if (!ca)
                goto out;
 
-       ca->cpuusage = alloc_percpu(struct cpuacct_usage);
+       ca->cpuusage = alloc_percpu(u64);
        if (!ca->cpuusage)
                goto out_free_ca;
 
@@ -99,7 +95,8 @@ static void cpuacct_css_free(struct cgroup_subsys_state *css)
 static u64 cpuacct_cpuusage_read(struct cpuacct *ca, int cpu,
                                 enum cpuacct_stat_index index)
 {
-       struct cpuacct_usage *cpuusage = per_cpu_ptr(ca->cpuusage, cpu);
+       u64 *cpuusage = per_cpu_ptr(ca->cpuusage, cpu);
+       u64 *cpustat = per_cpu_ptr(ca->cpustat, cpu)->cpustat;
        u64 data;
 
        /*
@@ -116,14 +113,17 @@ static u64 cpuacct_cpuusage_read(struct cpuacct *ca, int cpu,
        raw_spin_rq_lock_irq(cpu_rq(cpu));
 #endif
 
-       if (index == CPUACCT_STAT_NSTATS) {
-               int i = 0;
-
-               data = 0;
-               for (i = 0; i < CPUACCT_STAT_NSTATS; i++)
-                       data += cpuusage->usages[i];
-       } else {
-               data = cpuusage->usages[index];
+       switch (index) {
+       case CPUACCT_STAT_USER:
+               data = cpustat[CPUTIME_USER] + cpustat[CPUTIME_NICE];
+               break;
+       case CPUACCT_STAT_SYSTEM:
+               data = cpustat[CPUTIME_SYSTEM] + cpustat[CPUTIME_IRQ] +
+                       cpustat[CPUTIME_SOFTIRQ];
+               break;
+       case CPUACCT_STAT_NSTATS:
+               data = *cpuusage;
+               break;
        }
 
 #ifndef CONFIG_64BIT
@@ -133,10 +133,14 @@ static u64 cpuacct_cpuusage_read(struct cpuacct *ca, int cpu,
        return data;
 }
 
-static void cpuacct_cpuusage_write(struct cpuacct *ca, int cpu, u64 val)
+static void cpuacct_cpuusage_write(struct cpuacct *ca, int cpu)
 {
-       struct cpuacct_usage *cpuusage = per_cpu_ptr(ca->cpuusage, cpu);
-       int i;
+       u64 *cpuusage = per_cpu_ptr(ca->cpuusage, cpu);
+       u64 *cpustat = per_cpu_ptr(ca->cpustat, cpu)->cpustat;
+
+       /* Don't allow to reset global kernel_cpustat */
+       if (ca == &root_cpuacct)
+               return;
 
 #ifndef CONFIG_64BIT
        /*
@@ -144,9 +148,10 @@ static void cpuacct_cpuusage_write(struct cpuacct *ca, int cpu, u64 val)
         */
        raw_spin_rq_lock_irq(cpu_rq(cpu));
 #endif
-
-       for (i = 0; i < CPUACCT_STAT_NSTATS; i++)
-               cpuusage->usages[i] = val;
+       *cpuusage = 0;
+       cpustat[CPUTIME_USER] = cpustat[CPUTIME_NICE] = 0;
+       cpustat[CPUTIME_SYSTEM] = cpustat[CPUTIME_IRQ] = 0;
+       cpustat[CPUTIME_SOFTIRQ] = 0;
 
 #ifndef CONFIG_64BIT
        raw_spin_rq_unlock_irq(cpu_rq(cpu));
@@ -197,7 +202,7 @@ static int cpuusage_write(struct cgroup_subsys_state *css, struct cftype *cft,
                return -EINVAL;
 
        for_each_possible_cpu(cpu)
-               cpuacct_cpuusage_write(ca, cpu, 0);
+               cpuacct_cpuusage_write(ca, cpu);
 
        return 0;
 }
@@ -244,25 +249,10 @@ static int cpuacct_all_seq_show(struct seq_file *m, void *V)
        seq_puts(m, "\n");
 
        for_each_possible_cpu(cpu) {
-               struct cpuacct_usage *cpuusage = per_cpu_ptr(ca->cpuusage, cpu);
-
                seq_printf(m, "%d", cpu);
-
-               for (index = 0; index < CPUACCT_STAT_NSTATS; index++) {
-#ifndef CONFIG_64BIT
-                       /*
-                        * Take rq->lock to make 64-bit read safe on 32-bit
-                        * platforms.
-                        */
-                       raw_spin_rq_lock_irq(cpu_rq(cpu));
-#endif
-
-                       seq_printf(m, " %llu", cpuusage->usages[index]);
-
-#ifndef CONFIG_64BIT
-                       raw_spin_rq_unlock_irq(cpu_rq(cpu));
-#endif
-               }
+               for (index = 0; index < CPUACCT_STAT_NSTATS; index++)
+                       seq_printf(m, " %llu",
+                                  cpuacct_cpuusage_read(ca, cpu, index));
                seq_puts(m, "\n");
        }
        return 0;
@@ -340,16 +330,11 @@ static struct cftype files[] = {
 void cpuacct_charge(struct task_struct *tsk, u64 cputime)
 {
        struct cpuacct *ca;
-       int index = CPUACCT_STAT_SYSTEM;
-       struct pt_regs *regs = get_irq_regs() ? : task_pt_regs(tsk);
-
-       if (regs && user_mode(regs))
-               index = CPUACCT_STAT_USER;
 
        rcu_read_lock();
 
        for (ca = task_ca(tsk); ca; ca = parent_ca(ca))
-               __this_cpu_add(ca->cpuusage->usages[index], cputime);
+               __this_cpu_add(*ca->cpuusage, cputime);
 
        rcu_read_unlock();
 }