Skip to content

Commit

Permalink
cpuidle: Add 'above' and 'below' idle state metrics
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Rafael J. Wysocki committed Dec 12, 2018
1 parent 9456823 commit 04dab58
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 1 deletion.
7 changes: 7 additions & 0 deletions Documentation/ABI/testing/sysfs-devices-system-cpu
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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
Expand Down
10 changes: 10 additions & 0 deletions Documentation/admin-guide/pm/cpuidle.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
31 changes: 30 additions & 1 deletion drivers/cpuidle/cpuidle.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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;
}
Expand Down
6 changes: 6 additions & 0 deletions drivers/cpuidle/sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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,
Expand All @@ -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
};

Expand Down
2 changes: 2 additions & 0 deletions include/linux/cpuidle.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down

0 comments on commit 04dab58

Please sign in to comment.