Skip to content

Commit 04dab58

Browse files
committed
cpuidle: Add 'above' and 'below' idle state metrics
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>
1 parent 9456823 commit 04dab58

File tree

5 files changed

+55
-1
lines changed

5 files changed

+55
-1
lines changed

Documentation/ABI/testing/sysfs-devices-system-cpu

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ What: /sys/devices/system/cpu/cpuX/cpuidle/stateN/name
145145
/sys/devices/system/cpu/cpuX/cpuidle/stateN/power
146146
/sys/devices/system/cpu/cpuX/cpuidle/stateN/time
147147
/sys/devices/system/cpu/cpuX/cpuidle/stateN/usage
148+
/sys/devices/system/cpu/cpuX/cpuidle/stateN/above
149+
/sys/devices/system/cpu/cpuX/cpuidle/stateN/below
148150
Date: September 2007
149151
KernelVersion: v2.6.24
150152
Contact: Linux power management list <linux-pm@vger.kernel.org>
@@ -166,6 +168,11 @@ Description:
166168

167169
usage: (RO) Number of times this state was entered (a count).
168170

171+
above: (RO) Number of times this state was entered, but the
172+
observed CPU idle duration was too short for it (a count).
173+
174+
below: (RO) Number of times this state was entered, but the
175+
observed CPU idle duration was too long for it (a count).
169176

170177
What: /sys/devices/system/cpu/cpuX/cpuidle/stateN/desc
171178
Date: February 2008

Documentation/admin-guide/pm/cpuidle.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,16 @@ deeper the (effective) idle state represented by it. Each of them contains
398398
a number of files (attributes) representing the properties of the idle state
399399
object corresponding to it, as follows:
400400

401+
``above``
402+
Total number of times this idle state had been asked for, but the
403+
observed idle duration was certainly too short to match its target
404+
residency.
405+
406+
``below``
407+
Total number of times this idle state had been asked for, but cerainly
408+
a deeper idle state would have been a better match for the observed idle
409+
duration.
410+
401411
``desc``
402412
Description of the idle state.
403413

drivers/cpuidle/cpuidle.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,6 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
202202
struct cpuidle_state *target_state = &drv->states[index];
203203
bool broadcast = !!(target_state->flags & CPUIDLE_FLAG_TIMER_STOP);
204204
ktime_t time_start, time_end;
205-
s64 diff;
206205

207206
/*
208207
* 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,
248247
local_irq_enable();
249248

250249
if (entered_state >= 0) {
250+
s64 diff, delay = drv->states[entered_state].exit_latency;
251+
int i;
252+
251253
/*
252254
* Update cpuidle counters
253255
* 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,
260262
dev->last_residency = (int)diff;
261263
dev->states_usage[entered_state].time += dev->last_residency;
262264
dev->states_usage[entered_state].usage++;
265+
266+
if (diff < drv->states[entered_state].target_residency) {
267+
for (i = entered_state - 1; i >= 0; i--) {
268+
if (drv->states[i].disabled ||
269+
dev->states_usage[i].disable)
270+
continue;
271+
272+
/* Shallower states are enabled, so update. */
273+
dev->states_usage[entered_state].above++;
274+
break;
275+
}
276+
} else if (diff > delay) {
277+
for (i = entered_state + 1; i < drv->state_count; i++) {
278+
if (drv->states[i].disabled ||
279+
dev->states_usage[i].disable)
280+
continue;
281+
282+
/*
283+
* Update if a deeper state would have been a
284+
* better match for the observed idle duration.
285+
*/
286+
if (diff - delay >= drv->states[i].target_residency)
287+
dev->states_usage[entered_state].below++;
288+
289+
break;
290+
}
291+
}
263292
} else {
264293
dev->last_residency = 0;
265294
}

drivers/cpuidle/sysfs.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@ define_show_state_str_function(name)
301301
define_show_state_str_function(desc)
302302
define_show_state_ull_function(disable)
303303
define_store_state_ull_function(disable)
304+
define_show_state_ull_function(above)
305+
define_show_state_ull_function(below)
304306

305307
define_one_state_ro(name, show_state_name);
306308
define_one_state_ro(desc, show_state_desc);
@@ -310,6 +312,8 @@ define_one_state_ro(power, show_state_power_usage);
310312
define_one_state_ro(usage, show_state_usage);
311313
define_one_state_ro(time, show_state_time);
312314
define_one_state_rw(disable, show_state_disable, store_state_disable);
315+
define_one_state_ro(above, show_state_above);
316+
define_one_state_ro(below, show_state_below);
313317

314318
static struct attribute *cpuidle_state_default_attrs[] = {
315319
&attr_name.attr,
@@ -320,6 +324,8 @@ static struct attribute *cpuidle_state_default_attrs[] = {
320324
&attr_usage.attr,
321325
&attr_time.attr,
322326
&attr_disable.attr,
327+
&attr_above.attr,
328+
&attr_below.attr,
323329
NULL
324330
};
325331

include/linux/cpuidle.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ struct cpuidle_state_usage {
3333
unsigned long long disable;
3434
unsigned long long usage;
3535
unsigned long long time; /* in US */
36+
unsigned long long above; /* Number of times it's been too deep */
37+
unsigned long long below; /* Number of times it's been too shallow */
3638
#ifdef CONFIG_SUSPEND
3739
unsigned long long s2idle_usage;
3840
unsigned long long s2idle_time; /* in US */

0 commit comments

Comments
 (0)