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

cpuidle: add a sysfs entry to disable specific C state for debug purpose.

Some C states of new CPU might be not good. One reason is BIOS might
configure them incorrectly. To help developers root cause it quickly, the
patch adds a new sysfs entry, so developers could disable specific C state
manually.

In addition, C state might have much impact on performance tuning, as it
takes much time to enter/exit C states, which might delay interrupt
processing. With the new debug option, developers could check if a deep C
state could impact performance and how much impact it could cause.

Also add this option in Documentation/cpuidle/sysfs.txt.

[akpm@linux-foundation.org: check kstrtol return value]
Signed-off-by: ShuoX Liu <shuox.liu@intel.com>
Reviewed-by: Yanmin Zhang <yanmin_zhang@intel.com>
Reviewed-and-Tested-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Len Brown <len.brown@intel.com>

authored by

ShuoX Liu and committed by
Len Brown
3a53396b 6a6ea0ac

+51 -1
+5
Documentation/cpuidle/sysfs.txt
··· 36 36 /sys/devices/system/cpu/cpu0/cpuidle/state0: 37 37 total 0 38 38 -r--r--r-- 1 root root 4096 Feb 8 10:42 desc 39 + -rw-r--r-- 1 root root 4096 Feb 8 10:42 disable 39 40 -r--r--r-- 1 root root 4096 Feb 8 10:42 latency 40 41 -r--r--r-- 1 root root 4096 Feb 8 10:42 name 41 42 -r--r--r-- 1 root root 4096 Feb 8 10:42 power ··· 46 45 /sys/devices/system/cpu/cpu0/cpuidle/state1: 47 46 total 0 48 47 -r--r--r-- 1 root root 4096 Feb 8 10:42 desc 48 + -rw-r--r-- 1 root root 4096 Feb 8 10:42 disable 49 49 -r--r--r-- 1 root root 4096 Feb 8 10:42 latency 50 50 -r--r--r-- 1 root root 4096 Feb 8 10:42 name 51 51 -r--r--r-- 1 root root 4096 Feb 8 10:42 power ··· 56 54 /sys/devices/system/cpu/cpu0/cpuidle/state2: 57 55 total 0 58 56 -r--r--r-- 1 root root 4096 Feb 8 10:42 desc 57 + -rw-r--r-- 1 root root 4096 Feb 8 10:42 disable 59 58 -r--r--r-- 1 root root 4096 Feb 8 10:42 latency 60 59 -r--r--r-- 1 root root 4096 Feb 8 10:42 name 61 60 -r--r--r-- 1 root root 4096 Feb 8 10:42 power ··· 66 63 /sys/devices/system/cpu/cpu0/cpuidle/state3: 67 64 total 0 68 65 -r--r--r-- 1 root root 4096 Feb 8 10:42 desc 66 + -rw-r--r-- 1 root root 4096 Feb 8 10:42 disable 69 67 -r--r--r-- 1 root root 4096 Feb 8 10:42 latency 70 68 -r--r--r-- 1 root root 4096 Feb 8 10:42 name 71 69 -r--r--r-- 1 root root 4096 Feb 8 10:42 power ··· 76 72 77 73 78 74 * desc : Small description about the idle state (string) 75 + * disable : Option to disable this idle state (bool) 79 76 * latency : Latency to exit out of this idle state (in microseconds) 80 77 * name : Name of the idle state (string) 81 78 * power : Power consumed while in this idle state (in milliwatts)
+1
drivers/cpuidle/cpuidle.c
··· 245 245 state->power_usage = -1; 246 246 state->flags = 0; 247 247 state->enter = poll_idle; 248 + state->disable = 0; 248 249 } 249 250 #else 250 251 static void poll_idle_init(struct cpuidle_driver *drv) {}
+4 -1
drivers/cpuidle/governors/menu.c
··· 280 280 * We want to default to C1 (hlt), not to busy polling 281 281 * unless the timer is happening really really soon. 282 282 */ 283 - if (data->expected_us > 5) 283 + if (data->expected_us > 5 && 284 + drv->states[CPUIDLE_DRIVER_STATE_START].disable == 0) 284 285 data->last_state_idx = CPUIDLE_DRIVER_STATE_START; 285 286 286 287 /* ··· 291 290 for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { 292 291 struct cpuidle_state *s = &drv->states[i]; 293 292 293 + if (s->disable) 294 + continue; 294 295 if (s->target_residency > data->predicted_us) 295 296 continue; 296 297 if (s->exit_latency > latency_req)
+40
drivers/cpuidle/sysfs.c
··· 11 11 #include <linux/sysfs.h> 12 12 #include <linux/slab.h> 13 13 #include <linux/cpu.h> 14 + #include <linux/capability.h> 14 15 15 16 #include "cpuidle.h" 16 17 ··· 223 222 #define define_one_state_ro(_name, show) \ 224 223 static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0444, show, NULL) 225 224 225 + #define define_one_state_rw(_name, show, store) \ 226 + static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0644, show, store) 227 + 226 228 #define define_show_state_function(_name) \ 227 229 static ssize_t show_state_##_name(struct cpuidle_state *state, \ 228 230 struct cpuidle_state_usage *state_usage, char *buf) \ 229 231 { \ 230 232 return sprintf(buf, "%u\n", state->_name);\ 233 + } 234 + 235 + #define define_store_state_function(_name) \ 236 + static ssize_t store_state_##_name(struct cpuidle_state *state, \ 237 + const char *buf, size_t size) \ 238 + { \ 239 + long value; \ 240 + int err; \ 241 + if (!capable(CAP_SYS_ADMIN)) \ 242 + return -EPERM; \ 243 + err = kstrtol(buf, 0, &value); \ 244 + if (err) \ 245 + return err; \ 246 + if (value) \ 247 + state->disable = 1; \ 248 + else \ 249 + state->disable = 0; \ 250 + return size; \ 231 251 } 232 252 233 253 #define define_show_state_ull_function(_name) \ ··· 273 251 define_show_state_ull_function(time) 274 252 define_show_state_str_function(name) 275 253 define_show_state_str_function(desc) 254 + define_show_state_function(disable) 255 + define_store_state_function(disable) 276 256 277 257 define_one_state_ro(name, show_state_name); 278 258 define_one_state_ro(desc, show_state_desc); ··· 282 258 define_one_state_ro(power, show_state_power_usage); 283 259 define_one_state_ro(usage, show_state_usage); 284 260 define_one_state_ro(time, show_state_time); 261 + define_one_state_rw(disable, show_state_disable, store_state_disable); 285 262 286 263 static struct attribute *cpuidle_state_default_attrs[] = { 287 264 &attr_name.attr, ··· 291 266 &attr_power.attr, 292 267 &attr_usage.attr, 293 268 &attr_time.attr, 269 + &attr_disable.attr, 294 270 NULL 295 271 }; 296 272 ··· 313 287 return ret; 314 288 } 315 289 290 + static ssize_t cpuidle_state_store(struct kobject *kobj, 291 + struct attribute *attr, const char *buf, size_t size) 292 + { 293 + int ret = -EIO; 294 + struct cpuidle_state *state = kobj_to_state(kobj); 295 + struct cpuidle_state_attr *cattr = attr_to_stateattr(attr); 296 + 297 + if (cattr->store) 298 + ret = cattr->store(state, buf, size); 299 + 300 + return ret; 301 + } 302 + 316 303 static const struct sysfs_ops cpuidle_state_sysfs_ops = { 317 304 .show = cpuidle_state_show, 305 + .store = cpuidle_state_store, 318 306 }; 319 307 320 308 static void cpuidle_state_sysfs_release(struct kobject *kobj)
+1
include/linux/cpuidle.h
··· 46 46 unsigned int exit_latency; /* in US */ 47 47 unsigned int power_usage; /* in mW */ 48 48 unsigned int target_residency; /* in US */ 49 + unsigned int disable; 49 50 50 51 int (*enter) (struct cpuidle_device *dev, 51 52 struct cpuidle_driver *drv,