Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+55 -1
+7
Documentation/ABI/testing/sysfs-devices-system-cpu
··· 145 145 /sys/devices/system/cpu/cpuX/cpuidle/stateN/power 146 146 /sys/devices/system/cpu/cpuX/cpuidle/stateN/time 147 147 /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 148 150 Date: September 2007 149 151 KernelVersion: v2.6.24 150 152 Contact: Linux power management list <linux-pm@vger.kernel.org> ··· 168 166 169 167 usage: (RO) Number of times this state was entered (a count). 170 168 169 + above: (RO) Number of times this state was entered, but the 170 + observed CPU idle duration was too short for it (a count). 171 + 172 + below: (RO) Number of times this state was entered, but the 173 + observed CPU idle duration was too long for it (a count). 171 174 172 175 What: /sys/devices/system/cpu/cpuX/cpuidle/stateN/desc 173 176 Date: February 2008
+10
Documentation/admin-guide/pm/cpuidle.rst
··· 398 398 a number of files (attributes) representing the properties of the idle state 399 399 object corresponding to it, as follows: 400 400 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 + 401 411 ``desc`` 402 412 Description of the idle state. 403 413
+30 -1
drivers/cpuidle/cpuidle.c
··· 202 202 struct cpuidle_state *target_state = &drv->states[index]; 203 203 bool broadcast = !!(target_state->flags & CPUIDLE_FLAG_TIMER_STOP); 204 204 ktime_t time_start, time_end; 205 - s64 diff; 206 205 207 206 /* 208 207 * Tell the time framework to switch to a broadcast timer because our ··· 247 248 local_irq_enable(); 248 249 249 250 if (entered_state >= 0) { 251 + s64 diff, delay = drv->states[entered_state].exit_latency; 252 + int i; 253 + 250 254 /* 251 255 * Update cpuidle counters 252 256 * This can be moved to within driver enter routine, ··· 262 260 dev->last_residency = (int)diff; 263 261 dev->states_usage[entered_state].time += dev->last_residency; 264 262 dev->states_usage[entered_state].usage++; 263 + 264 + if (diff < drv->states[entered_state].target_residency) { 265 + for (i = entered_state - 1; i >= 0; i--) { 266 + if (drv->states[i].disabled || 267 + dev->states_usage[i].disable) 268 + continue; 269 + 270 + /* Shallower states are enabled, so update. */ 271 + dev->states_usage[entered_state].above++; 272 + break; 273 + } 274 + } else if (diff > delay) { 275 + for (i = entered_state + 1; i < drv->state_count; i++) { 276 + if (drv->states[i].disabled || 277 + dev->states_usage[i].disable) 278 + continue; 279 + 280 + /* 281 + * Update if a deeper state would have been a 282 + * better match for the observed idle duration. 283 + */ 284 + if (diff - delay >= drv->states[i].target_residency) 285 + dev->states_usage[entered_state].below++; 286 + 287 + break; 288 + } 289 + } 265 290 } else { 266 291 dev->last_residency = 0; 267 292 }
+6
drivers/cpuidle/sysfs.c
··· 301 301 define_show_state_str_function(desc) 302 302 define_show_state_ull_function(disable) 303 303 define_store_state_ull_function(disable) 304 + define_show_state_ull_function(above) 305 + define_show_state_ull_function(below) 304 306 305 307 define_one_state_ro(name, show_state_name); 306 308 define_one_state_ro(desc, show_state_desc); ··· 312 310 define_one_state_ro(usage, show_state_usage); 313 311 define_one_state_ro(time, show_state_time); 314 312 define_one_state_rw(disable, show_state_disable, store_state_disable); 313 + define_one_state_ro(above, show_state_above); 314 + define_one_state_ro(below, show_state_below); 315 315 316 316 static struct attribute *cpuidle_state_default_attrs[] = { 317 317 &attr_name.attr, ··· 324 320 &attr_usage.attr, 325 321 &attr_time.attr, 326 322 &attr_disable.attr, 323 + &attr_above.attr, 324 + &attr_below.attr, 327 325 NULL 328 326 }; 329 327
+2
include/linux/cpuidle.h
··· 33 33 unsigned long long disable; 34 34 unsigned long long usage; 35 35 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 */ 36 38 #ifdef CONFIG_SUSPEND 37 39 unsigned long long s2idle_usage; 38 40 unsigned long long s2idle_time; /* in US */