diff options
author | Deepthi Dharwar <deepthi@linux.vnet.ibm.com> | 2011-10-28 06:50:42 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2011-11-06 21:13:58 -0500 |
commit | 46bcfad7a819bd17ac4e831b04405152d59784ab (patch) | |
tree | 20041e788154d103edff2699f88d4a30320e3ee2 /drivers/idle | |
parent | 4202735e8ab6ecfb0381631a0d0b58fefe0bd4e2 (diff) |
cpuidle: Single/Global registration of idle states
This patch makes the cpuidle_states structure global (single copy)
instead of per-cpu. The statistics needed on per-cpu basis
by the governor are kept per-cpu. This simplifies the cpuidle
subsystem as state registration is done by single cpu only.
Having single copy of cpuidle_states saves memory. Rare case
of asymmetric C-states can be handled within the cpuidle driver
and architectures such as POWER do not have asymmetric C-states.
Having single/global registration of all the idle states,
dynamic C-state transitions on x86 are handled by
the boot cpu. Here, the boot cpu would disable all the devices,
re-populate the states and later enable all the devices,
irrespective of the cpu that would receive the notification first.
Reference:
https://lkml.org/lkml/2011/4/25/83
Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Signed-off-by: Trinabh Gupta <g.trinabh@gmail.com>
Tested-by: Jean Pihet <j-pihet@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
Acked-by: Arjan van de Ven <arjan@linux.intel.com>
Acked-by: Kevin Hilman <khilman@ti.com>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/idle')
-rw-r--r-- | drivers/idle/intel_idle.c | 80 |
1 files changed, 61 insertions, 19 deletions
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 3aa8d4cb6dca..5be9d599ff6b 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c | |||
@@ -81,7 +81,8 @@ static unsigned int mwait_substates; | |||
81 | static unsigned int lapic_timer_reliable_states = (1 << 1); /* Default to only C1 */ | 81 | static unsigned int lapic_timer_reliable_states = (1 << 1); /* Default to only C1 */ |
82 | 82 | ||
83 | static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; | 83 | static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; |
84 | static int intel_idle(struct cpuidle_device *dev, int index); | 84 | static int intel_idle(struct cpuidle_device *dev, |
85 | struct cpuidle_driver *drv, int index); | ||
85 | 86 | ||
86 | static struct cpuidle_state *cpuidle_state_table; | 87 | static struct cpuidle_state *cpuidle_state_table; |
87 | 88 | ||
@@ -227,13 +228,15 @@ static int get_driver_data(int cstate) | |||
227 | /** | 228 | /** |
228 | * intel_idle | 229 | * intel_idle |
229 | * @dev: cpuidle_device | 230 | * @dev: cpuidle_device |
231 | * @drv: cpuidle driver | ||
230 | * @index: index of cpuidle state | 232 | * @index: index of cpuidle state |
231 | * | 233 | * |
232 | */ | 234 | */ |
233 | static int intel_idle(struct cpuidle_device *dev, int index) | 235 | static int intel_idle(struct cpuidle_device *dev, |
236 | struct cpuidle_driver *drv, int index) | ||
234 | { | 237 | { |
235 | unsigned long ecx = 1; /* break on interrupt flag */ | 238 | unsigned long ecx = 1; /* break on interrupt flag */ |
236 | struct cpuidle_state *state = &dev->states[index]; | 239 | struct cpuidle_state *state = &drv->states[index]; |
237 | struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; | 240 | struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; |
238 | unsigned long eax = (unsigned long)cpuidle_get_statedata(state_usage); | 241 | unsigned long eax = (unsigned long)cpuidle_get_statedata(state_usage); |
239 | unsigned int cstate; | 242 | unsigned int cstate; |
@@ -420,6 +423,60 @@ static void intel_idle_cpuidle_devices_uninit(void) | |||
420 | return; | 423 | return; |
421 | } | 424 | } |
422 | /* | 425 | /* |
426 | * intel_idle_cpuidle_driver_init() | ||
427 | * allocate, initialize cpuidle_states | ||
428 | */ | ||
429 | static int intel_idle_cpuidle_driver_init(void) | ||
430 | { | ||
431 | int cstate; | ||
432 | struct cpuidle_driver *drv = &intel_idle_driver; | ||
433 | |||
434 | drv->state_count = 1; | ||
435 | |||
436 | for (cstate = 1; cstate < MWAIT_MAX_NUM_CSTATES; ++cstate) { | ||
437 | int num_substates; | ||
438 | |||
439 | if (cstate > max_cstate) { | ||
440 | printk(PREFIX "max_cstate %d reached\n", | ||
441 | max_cstate); | ||
442 | break; | ||
443 | } | ||
444 | |||
445 | /* does the state exist in CPUID.MWAIT? */ | ||
446 | num_substates = (mwait_substates >> ((cstate) * 4)) | ||
447 | & MWAIT_SUBSTATE_MASK; | ||
448 | if (num_substates == 0) | ||
449 | continue; | ||
450 | /* is the state not enabled? */ | ||
451 | if (cpuidle_state_table[cstate].enter == NULL) { | ||
452 | /* does the driver not know about the state? */ | ||
453 | if (*cpuidle_state_table[cstate].name == '\0') | ||
454 | pr_debug(PREFIX "unaware of model 0x%x" | ||
455 | " MWAIT %d please" | ||
456 | " contact lenb@kernel.org", | ||
457 | boot_cpu_data.x86_model, cstate); | ||
458 | continue; | ||
459 | } | ||
460 | |||
461 | if ((cstate > 2) && | ||
462 | !boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) | ||
463 | mark_tsc_unstable("TSC halts in idle" | ||
464 | " states deeper than C2"); | ||
465 | |||
466 | drv->states[drv->state_count] = /* structure copy */ | ||
467 | cpuidle_state_table[cstate]; | ||
468 | |||
469 | drv->state_count += 1; | ||
470 | } | ||
471 | |||
472 | if (auto_demotion_disable_flags) | ||
473 | smp_call_function(auto_demotion_disable, NULL, 1); | ||
474 | |||
475 | return 0; | ||
476 | } | ||
477 | |||
478 | |||
479 | /* | ||
423 | * intel_idle_cpuidle_devices_init() | 480 | * intel_idle_cpuidle_devices_init() |
424 | * allocate, initialize, register cpuidle_devices | 481 | * allocate, initialize, register cpuidle_devices |
425 | */ | 482 | */ |
@@ -453,23 +510,9 @@ static int intel_idle_cpuidle_devices_init(void) | |||
453 | continue; | 510 | continue; |
454 | /* is the state not enabled? */ | 511 | /* is the state not enabled? */ |
455 | if (cpuidle_state_table[cstate].enter == NULL) { | 512 | if (cpuidle_state_table[cstate].enter == NULL) { |
456 | /* does the driver not know about the state? */ | ||
457 | if (*cpuidle_state_table[cstate].name == '\0') | ||
458 | pr_debug(PREFIX "unaware of model 0x%x" | ||
459 | " MWAIT %d please" | ||
460 | " contact lenb@kernel.org", | ||
461 | boot_cpu_data.x86_model, cstate); | ||
462 | continue; | 513 | continue; |
463 | } | 514 | } |
464 | 515 | ||
465 | if ((cstate > 2) && | ||
466 | !boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) | ||
467 | mark_tsc_unstable("TSC halts in idle" | ||
468 | " states deeper than C2"); | ||
469 | |||
470 | dev->states[dev->state_count] = /* structure copy */ | ||
471 | cpuidle_state_table[cstate]; | ||
472 | |||
473 | dev->states_usage[dev->state_count].driver_data = | 516 | dev->states_usage[dev->state_count].driver_data = |
474 | (void *)get_driver_data(cstate); | 517 | (void *)get_driver_data(cstate); |
475 | 518 | ||
@@ -484,8 +527,6 @@ static int intel_idle_cpuidle_devices_init(void) | |||
484 | return -EIO; | 527 | return -EIO; |
485 | } | 528 | } |
486 | } | 529 | } |
487 | if (auto_demotion_disable_flags) | ||
488 | smp_call_function(auto_demotion_disable, NULL, 1); | ||
489 | 530 | ||
490 | return 0; | 531 | return 0; |
491 | } | 532 | } |
@@ -503,6 +544,7 @@ static int __init intel_idle_init(void) | |||
503 | if (retval) | 544 | if (retval) |
504 | return retval; | 545 | return retval; |
505 | 546 | ||
547 | intel_idle_cpuidle_driver_init(); | ||
506 | retval = cpuidle_register_driver(&intel_idle_driver); | 548 | retval = cpuidle_register_driver(&intel_idle_driver); |
507 | if (retval) { | 549 | if (retval) { |
508 | printk(KERN_DEBUG PREFIX "intel_idle yielding to %s", | 550 | printk(KERN_DEBUG PREFIX "intel_idle yielding to %s", |