]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
cpuidle: Add 'above' and 'below' idle state metrics
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 10 Dec 2018 11:30:23 +0000 (12:30 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Wed, 12 Dec 2018 22:22:18 +0000 (23:22 +0100)
Add two new metrics for CPU idle states, "above" and "below", to count
the number of times the given state had been asked for (or entered
from the kernel's perspective), but the observed idle duration turned
out to be too short or too long for it (respectively).

These metrics help to estimate the quality of the CPU idle governor
in use.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Documentation/ABI/testing/sysfs-devices-system-cpu
Documentation/admin-guide/pm/cpuidle.rst
drivers/cpuidle/cpuidle.c
drivers/cpuidle/sysfs.c
include/linux/cpuidle.h

index 73318225a3681b3473abe87d0fe1bb4c3447e7ed..9605dbd4b5b59ecc251913b1b327e6031cfb875f 100644 (file)
@@ -145,6 +145,8 @@ What:               /sys/devices/system/cpu/cpuX/cpuidle/stateN/name
                /sys/devices/system/cpu/cpuX/cpuidle/stateN/power
                /sys/devices/system/cpu/cpuX/cpuidle/stateN/time
                /sys/devices/system/cpu/cpuX/cpuidle/stateN/usage
+               /sys/devices/system/cpu/cpuX/cpuidle/stateN/above
+               /sys/devices/system/cpu/cpuX/cpuidle/stateN/below
 Date:          September 2007
 KernelVersion: v2.6.24
 Contact:       Linux power management list <linux-pm@vger.kernel.org>
@@ -166,6 +168,11 @@ Description:
 
                usage: (RO) Number of times this state was entered (a count).
 
+               above: (RO) Number of times this state was entered, but the
+                      observed CPU idle duration was too short for it (a count).
+
+               below: (RO) Number of times this state was entered, but the
+                      observed CPU idle duration was too long for it (a count).
 
 What:          /sys/devices/system/cpu/cpuX/cpuidle/stateN/desc
 Date:          February 2008
index 9a34484fd6e45304a3d71a65c1894bc8383c9eb7..106379e2619f7a0a396bff400652cf30848225c7 100644 (file)
@@ -398,6 +398,16 @@ deeper the (effective) idle state represented by it.  Each of them contains
 a number of files (attributes) representing the properties of the idle state
 object corresponding to it, as follows:
 
+``above``
+       Total number of times this idle state had been asked for, but the
+       observed idle duration was certainly too short to match its target
+       residency.
+
+``below``
+       Total number of times this idle state had been asked for, but cerainly
+       a deeper idle state would have been a better match for the observed idle
+       duration.
+
 ``desc``
        Description of the idle state.
 
index f7c58043e50f3b1205ee35c7b104a3ea748f2c6d..7f108309e871ea63fbc16b1b896e3ef4cd443216 100644 (file)
@@ -202,7 +202,6 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
        struct cpuidle_state *target_state = &drv->states[index];
        bool broadcast = !!(target_state->flags & CPUIDLE_FLAG_TIMER_STOP);
        ktime_t time_start, time_end;
-       s64 diff;
 
        /*
         * Tell the time framework to switch to a broadcast timer because our
@@ -248,6 +247,9 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
                local_irq_enable();
 
        if (entered_state >= 0) {
+               s64 diff, delay = drv->states[entered_state].exit_latency;
+               int i;
+
                /*
                 * Update cpuidle counters
                 * This can be moved to within driver enter routine,
@@ -260,6 +262,33 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
                dev->last_residency = (int)diff;
                dev->states_usage[entered_state].time += dev->last_residency;
                dev->states_usage[entered_state].usage++;
+
+               if (diff < drv->states[entered_state].target_residency) {
+                       for (i = entered_state - 1; i >= 0; i--) {
+                               if (drv->states[i].disabled ||
+                                   dev->states_usage[i].disable)
+                                       continue;
+
+                               /* Shallower states are enabled, so update. */
+                               dev->states_usage[entered_state].above++;
+                               break;
+                       }
+               } else if (diff > delay) {
+                       for (i = entered_state + 1; i < drv->state_count; i++) {
+                               if (drv->states[i].disabled ||
+                                   dev->states_usage[i].disable)
+                                       continue;
+
+                               /*
+                                * Update if a deeper state would have been a
+                                * better match for the observed idle duration.
+                                */
+                               if (diff - delay >= drv->states[i].target_residency)
+                                       dev->states_usage[entered_state].below++;
+
+                               break;
+                       }
+               }
        } else {
                dev->last_residency = 0;
        }
index e754c7aae7f7bba331459f2500c7339e2321a7ba..eb20adb5de2349c059d328bb24d75696b2e1890e 100644 (file)
@@ -301,6 +301,8 @@ define_show_state_str_function(name)
 define_show_state_str_function(desc)
 define_show_state_ull_function(disable)
 define_store_state_ull_function(disable)
+define_show_state_ull_function(above)
+define_show_state_ull_function(below)
 
 define_one_state_ro(name, show_state_name);
 define_one_state_ro(desc, show_state_desc);
@@ -310,6 +312,8 @@ define_one_state_ro(power, show_state_power_usage);
 define_one_state_ro(usage, show_state_usage);
 define_one_state_ro(time, show_state_time);
 define_one_state_rw(disable, show_state_disable, store_state_disable);
+define_one_state_ro(above, show_state_above);
+define_one_state_ro(below, show_state_below);
 
 static struct attribute *cpuidle_state_default_attrs[] = {
        &attr_name.attr,
@@ -320,6 +324,8 @@ static struct attribute *cpuidle_state_default_attrs[] = {
        &attr_usage.attr,
        &attr_time.attr,
        &attr_disable.attr,
+       &attr_above.attr,
+       &attr_below.attr,
        NULL
 };
 
index faed7a8977e85bd6e34f9803c75d9dfcc05d4803..4dff74f48d4b11024766303a4e0bd17379b26bdb 100644 (file)
@@ -33,6 +33,8 @@ struct cpuidle_state_usage {
        unsigned long long      disable;
        unsigned long long      usage;
        unsigned long long      time; /* in US */
+       unsigned long long      above; /* Number of times it's been too deep */
+       unsigned long long      below; /* Number of times it's been too shallow */
 #ifdef CONFIG_SUSPEND
        unsigned long long      s2idle_usage;
        unsigned long long      s2idle_time; /* in US */