diff options
| author | ShuoX Liu <shuox.liu@intel.com> | 2012-03-28 18:19:11 -0400 |
|---|---|---|
| committer | Len Brown <len.brown@intel.com> | 2012-03-30 01:52:58 -0400 |
| commit | 3a53396b0381ec9d5180fd8fe7a681c8ce95fd9a (patch) | |
| tree | 60a0770554c64250900ccf827ff868b483ae92fe /drivers/cpuidle | |
| parent | 6a6ea0acc9375571a13aa8c4e105a0807e1c16a4 (diff) | |
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>
Diffstat (limited to 'drivers/cpuidle')
| -rw-r--r-- | drivers/cpuidle/cpuidle.c | 1 | ||||
| -rw-r--r-- | drivers/cpuidle/governors/menu.c | 5 | ||||
| -rw-r--r-- | drivers/cpuidle/sysfs.c | 40 |
3 files changed, 45 insertions, 1 deletions
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 4869b5500234..77304b6b8aef 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c | |||
| @@ -245,6 +245,7 @@ static void poll_idle_init(struct cpuidle_driver *drv) | |||
| 245 | state->power_usage = -1; | 245 | state->power_usage = -1; |
| 246 | state->flags = 0; | 246 | state->flags = 0; |
| 247 | state->enter = poll_idle; | 247 | state->enter = poll_idle; |
| 248 | state->disable = 0; | ||
| 248 | } | 249 | } |
| 249 | #else | 250 | #else |
| 250 | static void poll_idle_init(struct cpuidle_driver *drv) {} | 251 | static void poll_idle_init(struct cpuidle_driver *drv) {} |
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index ad0952601ae2..5c17ca112fc2 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c | |||
| @@ -280,7 +280,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) | |||
| 280 | * We want to default to C1 (hlt), not to busy polling | 280 | * We want to default to C1 (hlt), not to busy polling |
| 281 | * unless the timer is happening really really soon. | 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 | data->last_state_idx = CPUIDLE_DRIVER_STATE_START; | 285 | data->last_state_idx = CPUIDLE_DRIVER_STATE_START; |
| 285 | 286 | ||
| 286 | /* | 287 | /* |
| @@ -290,6 +291,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) | |||
| 290 | for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { | 291 | for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { |
| 291 | struct cpuidle_state *s = &drv->states[i]; | 292 | struct cpuidle_state *s = &drv->states[i]; |
| 292 | 293 | ||
| 294 | if (s->disable) | ||
| 295 | continue; | ||
| 293 | if (s->target_residency > data->predicted_us) | 296 | if (s->target_residency > data->predicted_us) |
| 294 | continue; | 297 | continue; |
| 295 | if (s->exit_latency > latency_req) | 298 | if (s->exit_latency > latency_req) |
diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index 3fe41fe4851a..88032b4dc6d2 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include <linux/sysfs.h> | 11 | #include <linux/sysfs.h> |
| 12 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
| 13 | #include <linux/cpu.h> | 13 | #include <linux/cpu.h> |
| 14 | #include <linux/capability.h> | ||
| 14 | 15 | ||
| 15 | #include "cpuidle.h" | 16 | #include "cpuidle.h" |
| 16 | 17 | ||
| @@ -222,6 +223,9 @@ struct cpuidle_state_attr { | |||
| 222 | #define define_one_state_ro(_name, show) \ | 223 | #define define_one_state_ro(_name, show) \ |
| 223 | static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0444, show, NULL) | 224 | static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0444, show, NULL) |
| 224 | 225 | ||
| 226 | #define define_one_state_rw(_name, show, store) \ | ||
| 227 | static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0644, show, store) | ||
| 228 | |||
| 225 | #define define_show_state_function(_name) \ | 229 | #define define_show_state_function(_name) \ |
| 226 | static ssize_t show_state_##_name(struct cpuidle_state *state, \ | 230 | static ssize_t show_state_##_name(struct cpuidle_state *state, \ |
| 227 | struct cpuidle_state_usage *state_usage, char *buf) \ | 231 | struct cpuidle_state_usage *state_usage, char *buf) \ |
| @@ -229,6 +233,24 @@ static ssize_t show_state_##_name(struct cpuidle_state *state, \ | |||
| 229 | return sprintf(buf, "%u\n", state->_name);\ | 233 | return sprintf(buf, "%u\n", state->_name);\ |
| 230 | } | 234 | } |
| 231 | 235 | ||
| 236 | #define define_store_state_function(_name) \ | ||
| 237 | static ssize_t store_state_##_name(struct cpuidle_state *state, \ | ||
| 238 | const char *buf, size_t size) \ | ||
| 239 | { \ | ||
| 240 | long value; \ | ||
| 241 | int err; \ | ||
| 242 | if (!capable(CAP_SYS_ADMIN)) \ | ||
| 243 | return -EPERM; \ | ||
| 244 | err = kstrtol(buf, 0, &value); \ | ||
| 245 | if (err) \ | ||
| 246 | return err; \ | ||
| 247 | if (value) \ | ||
| 248 | state->disable = 1; \ | ||
| 249 | else \ | ||
| 250 | state->disable = 0; \ | ||
| 251 | return size; \ | ||
| 252 | } | ||
| 253 | |||
| 232 | #define define_show_state_ull_function(_name) \ | 254 | #define define_show_state_ull_function(_name) \ |
| 233 | static ssize_t show_state_##_name(struct cpuidle_state *state, \ | 255 | static ssize_t show_state_##_name(struct cpuidle_state *state, \ |
| 234 | struct cpuidle_state_usage *state_usage, char *buf) \ | 256 | struct cpuidle_state_usage *state_usage, char *buf) \ |
| @@ -251,6 +273,8 @@ define_show_state_ull_function(usage) | |||
| 251 | define_show_state_ull_function(time) | 273 | define_show_state_ull_function(time) |
| 252 | define_show_state_str_function(name) | 274 | define_show_state_str_function(name) |
| 253 | define_show_state_str_function(desc) | 275 | define_show_state_str_function(desc) |
| 276 | define_show_state_function(disable) | ||
| 277 | define_store_state_function(disable) | ||
| 254 | 278 | ||
| 255 | define_one_state_ro(name, show_state_name); | 279 | define_one_state_ro(name, show_state_name); |
| 256 | define_one_state_ro(desc, show_state_desc); | 280 | define_one_state_ro(desc, show_state_desc); |
| @@ -258,6 +282,7 @@ define_one_state_ro(latency, show_state_exit_latency); | |||
| 258 | define_one_state_ro(power, show_state_power_usage); | 282 | define_one_state_ro(power, show_state_power_usage); |
| 259 | define_one_state_ro(usage, show_state_usage); | 283 | define_one_state_ro(usage, show_state_usage); |
| 260 | define_one_state_ro(time, show_state_time); | 284 | define_one_state_ro(time, show_state_time); |
| 285 | define_one_state_rw(disable, show_state_disable, store_state_disable); | ||
| 261 | 286 | ||
| 262 | static struct attribute *cpuidle_state_default_attrs[] = { | 287 | static struct attribute *cpuidle_state_default_attrs[] = { |
| 263 | &attr_name.attr, | 288 | &attr_name.attr, |
| @@ -266,6 +291,7 @@ static struct attribute *cpuidle_state_default_attrs[] = { | |||
| 266 | &attr_power.attr, | 291 | &attr_power.attr, |
| 267 | &attr_usage.attr, | 292 | &attr_usage.attr, |
| 268 | &attr_time.attr, | 293 | &attr_time.attr, |
| 294 | &attr_disable.attr, | ||
| 269 | NULL | 295 | NULL |
| 270 | }; | 296 | }; |
| 271 | 297 | ||
| @@ -287,8 +313,22 @@ static ssize_t cpuidle_state_show(struct kobject * kobj, | |||
| 287 | return ret; | 313 | return ret; |
| 288 | } | 314 | } |
| 289 | 315 | ||
| 316 | static ssize_t cpuidle_state_store(struct kobject *kobj, | ||
| 317 | struct attribute *attr, const char *buf, size_t size) | ||
| 318 | { | ||
| 319 | int ret = -EIO; | ||
| 320 | struct cpuidle_state *state = kobj_to_state(kobj); | ||
| 321 | struct cpuidle_state_attr *cattr = attr_to_stateattr(attr); | ||
| 322 | |||
| 323 | if (cattr->store) | ||
| 324 | ret = cattr->store(state, buf, size); | ||
| 325 | |||
| 326 | return ret; | ||
| 327 | } | ||
| 328 | |||
| 290 | static const struct sysfs_ops cpuidle_state_sysfs_ops = { | 329 | static const struct sysfs_ops cpuidle_state_sysfs_ops = { |
| 291 | .show = cpuidle_state_show, | 330 | .show = cpuidle_state_show, |
| 331 | .store = cpuidle_state_store, | ||
| 292 | }; | 332 | }; |
| 293 | 333 | ||
| 294 | static void cpuidle_state_sysfs_release(struct kobject *kobj) | 334 | static void cpuidle_state_sysfs_release(struct kobject *kobj) |
