From e978aa7d7d57d04eb5f88a7507c4fb98577def77 Mon Sep 17 00:00:00 2001 From: Deepthi Dharwar Date: Fri, 28 Oct 2011 16:20:09 +0530 Subject: cpuidle: Move dev->last_residency update to driver enter routine; remove dev->last_state Cpuidle governor only suggests the state to enter using the governor->select() interface, but allows the low level driver to override the recommended state. The actual entered state may be different because of software or hardware demotion. Software demotion is done by the back-end cpuidle driver and can be accounted correctly. Current cpuidle code uses last_state field to capture the actual state entered and based on that updates the statistics for the state entered. Ideally the driver enter routine should update the counters, and it should return the state actually entered rather than the time spent there. The generic cpuidle code should simply handle where the counters live in the sysfs namespace, not updating the counters. Reference: https://lkml.org/lkml/2011/3/25/52 Signed-off-by: Deepthi Dharwar Signed-off-by: Trinabh Gupta Tested-by: Jean Pihet Reviewed-by: Kevin Hilman Acked-by: Arjan van de Ven Acked-by: Kevin Hilman Signed-off-by: Len Brown --- drivers/acpi/processor_idle.c | 75 ++++++++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 26 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 431ab11c8c1b..9cd08cecb347 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -741,22 +741,24 @@ static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx) /** * acpi_idle_enter_c1 - enters an ACPI C1 state-type * @dev: the target CPU - * @state: the state data + * @index: index of target state * * This is equivalent to the HALT instruction. */ static int acpi_idle_enter_c1(struct cpuidle_device *dev, - struct cpuidle_state *state) + int index) { ktime_t kt1, kt2; s64 idle_time; struct acpi_processor *pr; + struct cpuidle_state *state = &dev->states[index]; struct acpi_processor_cx *cx = cpuidle_get_statedata(state); pr = __this_cpu_read(processors); + dev->last_residency = 0; if (unlikely(!pr)) - return 0; + return -EINVAL; local_irq_disable(); @@ -764,7 +766,7 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, if (acpi_idle_suspend) { local_irq_enable(); cpu_relax(); - return 0; + return -EINVAL; } lapic_timer_state_broadcast(pr, cx, 1); @@ -773,37 +775,46 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, kt2 = ktime_get_real(); idle_time = ktime_to_us(ktime_sub(kt2, kt1)); + /* Update device last_residency*/ + dev->last_residency = (int)idle_time; + local_irq_enable(); cx->usage++; lapic_timer_state_broadcast(pr, cx, 0); - return idle_time; + return index; } /** * acpi_idle_enter_simple - enters an ACPI state without BM handling * @dev: the target CPU - * @state: the state data + * @index: the index of suggested state */ static int acpi_idle_enter_simple(struct cpuidle_device *dev, - struct cpuidle_state *state) + int index) { struct acpi_processor *pr; + struct cpuidle_state *state = &dev->states[index]; struct acpi_processor_cx *cx = cpuidle_get_statedata(state); ktime_t kt1, kt2; s64 idle_time_ns; s64 idle_time; pr = __this_cpu_read(processors); + dev->last_residency = 0; if (unlikely(!pr)) - return 0; - - if (acpi_idle_suspend) - return(acpi_idle_enter_c1(dev, state)); + return -EINVAL; local_irq_disable(); + if (acpi_idle_suspend) { + local_irq_enable(); + cpu_relax(); + return -EINVAL; + } + + if (cx->entry_method != ACPI_CSTATE_FFH) { current_thread_info()->status &= ~TS_POLLING; /* @@ -815,7 +826,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, if (unlikely(need_resched())) { current_thread_info()->status |= TS_POLLING; local_irq_enable(); - return 0; + return -EINVAL; } } @@ -837,6 +848,9 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, idle_time = idle_time_ns; do_div(idle_time, NSEC_PER_USEC); + /* Update device last_residency*/ + dev->last_residency = (int)idle_time; + /* Tell the scheduler how much we idled: */ sched_clock_idle_wakeup_event(idle_time_ns); @@ -848,7 +862,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, lapic_timer_state_broadcast(pr, cx, 0); cx->time += idle_time; - return idle_time; + return index; } static int c3_cpu_count; @@ -857,14 +871,15 @@ static DEFINE_SPINLOCK(c3_lock); /** * acpi_idle_enter_bm - enters C3 with proper BM handling * @dev: the target CPU - * @state: the state data + * @index: the index of suggested state * * If BM is detected, the deepest non-C3 idle state is entered instead. */ static int acpi_idle_enter_bm(struct cpuidle_device *dev, - struct cpuidle_state *state) + int index) { struct acpi_processor *pr; + struct cpuidle_state *state = &dev->states[index]; struct acpi_processor_cx *cx = cpuidle_get_statedata(state); ktime_t kt1, kt2; s64 idle_time_ns; @@ -872,22 +887,26 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, pr = __this_cpu_read(processors); + dev->last_residency = 0; if (unlikely(!pr)) - return 0; + return -EINVAL; - if (acpi_idle_suspend) - return(acpi_idle_enter_c1(dev, state)); + + if (acpi_idle_suspend) { + cpu_relax(); + return -EINVAL; + } if (!cx->bm_sts_skip && acpi_idle_bm_check()) { - if (dev->safe_state) { - dev->last_state = dev->safe_state; - return dev->safe_state->enter(dev, dev->safe_state); + if (dev->safe_state_index >= 0) { + return dev->states[dev->safe_state_index].enter(dev, + dev->safe_state_index); } else { local_irq_disable(); acpi_safe_halt(); local_irq_enable(); - return 0; + return -EINVAL; } } @@ -904,7 +923,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, if (unlikely(need_resched())) { current_thread_info()->status |= TS_POLLING; local_irq_enable(); - return 0; + return -EINVAL; } } @@ -954,6 +973,9 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, idle_time = idle_time_ns; do_div(idle_time, NSEC_PER_USEC); + /* Update device last_residency*/ + dev->last_residency = (int)idle_time; + /* Tell the scheduler how much we idled: */ sched_clock_idle_wakeup_event(idle_time_ns); @@ -965,7 +987,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, lapic_timer_state_broadcast(pr, cx, 0); cx->time += idle_time; - return idle_time; + return index; } struct cpuidle_driver acpi_idle_driver = { @@ -992,6 +1014,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) } dev->cpu = pr->id; + dev->safe_state_index = -1; for (i = 0; i < CPUIDLE_STATE_MAX; i++) { dev->states[i].name[0] = '\0'; dev->states[i].desc[0] = '\0'; @@ -1027,13 +1050,13 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) state->flags |= CPUIDLE_FLAG_TIME_VALID; state->enter = acpi_idle_enter_c1; - dev->safe_state = state; + dev->safe_state_index = count; break; case ACPI_STATE_C2: state->flags |= CPUIDLE_FLAG_TIME_VALID; state->enter = acpi_idle_enter_simple; - dev->safe_state = state; + dev->safe_state_index = count; break; case ACPI_STATE_C3: -- cgit v1.2.3 From 4202735e8ab6ecfb0381631a0d0b58fefe0bd4e2 Mon Sep 17 00:00:00 2001 From: Deepthi Dharwar Date: Fri, 28 Oct 2011 16:20:33 +0530 Subject: cpuidle: Split cpuidle_state structure and move per-cpu statistics fields This is the first step towards global registration of cpuidle states. The statistics used primarily by the governor are per-cpu and have to be split from rest of the fields inside cpuidle_state, which would be made global i.e. single copy. The driver_data field is also per-cpu and moved. Signed-off-by: Deepthi Dharwar Signed-off-by: Trinabh Gupta Tested-by: Jean Pihet Reviewed-by: Kevin Hilman Acked-by: Arjan van de Ven Acked-by: Kevin Hilman Signed-off-by: Len Brown --- arch/arm/mach-davinci/cpuidle.c | 5 +++-- arch/arm/mach-omap2/cpuidle34xx.c | 13 ++++++----- drivers/acpi/processor_idle.c | 25 ++++++++++----------- drivers/cpuidle/cpuidle.c | 11 +++++----- drivers/cpuidle/sysfs.c | 19 +++++++++++----- drivers/idle/intel_idle.c | 46 +++++++++++++++++++++++++++++---------- include/linux/cpuidle.h | 25 ++++++++++++--------- 7 files changed, 90 insertions(+), 54 deletions(-) (limited to 'drivers/acpi') diff --git a/arch/arm/mach-davinci/cpuidle.c b/arch/arm/mach-davinci/cpuidle.c index ca8582a95ad9..f2d2f34603d9 100644 --- a/arch/arm/mach-davinci/cpuidle.c +++ b/arch/arm/mach-davinci/cpuidle.c @@ -80,7 +80,8 @@ static struct davinci_ops davinci_states[DAVINCI_CPUIDLE_MAX_STATES] = { static int davinci_enter_idle(struct cpuidle_device *dev, int index) { - struct davinci_ops *ops = cpuidle_get_statedata(&dev->states[index]); + struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; + struct davinci_ops *ops = cpuidle_get_statedata(state_usage); struct timeval before, after; int idle_time; @@ -142,7 +143,7 @@ static int __init davinci_cpuidle_probe(struct platform_device *pdev) strcpy(device->states[1].desc, "WFI and DDR Self Refresh"); if (pdata->ddr2_pdown) davinci_states[1].flags |= DAVINCI_CPUIDLE_FLAGS_DDR2_PWDN; - cpuidle_set_statedata(&device->states[1], &davinci_states[1]); + cpuidle_set_statedata(&device->states_usage[1], &davinci_states[1]); device->state_count = DAVINCI_CPUIDLE_MAX_STATES; diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c index 58425c75f1b8..d3fce7b97fcf 100644 --- a/arch/arm/mach-omap2/cpuidle34xx.c +++ b/arch/arm/mach-omap2/cpuidle34xx.c @@ -97,7 +97,7 @@ static int omap3_enter_idle(struct cpuidle_device *dev, int index) { struct omap3_idle_statedata *cx = - cpuidle_get_statedata(&dev->states[index]); + cpuidle_get_statedata(&dev->states_usage[index]); struct timespec ts_preidle, ts_postidle, ts_idle; u32 mpu_state = cx->mpu_state, core_state = cx->core_state; int idle_time; @@ -160,8 +160,9 @@ return_sleep_time: static int next_valid_state(struct cpuidle_device *dev, int index) { + struct cpuidle_state_usage *curr_usage = &dev->states_usage[index]; struct cpuidle_state *curr = &dev->states[index]; - struct omap3_idle_statedata *cx = cpuidle_get_statedata(curr); + struct omap3_idle_statedata *cx = cpuidle_get_statedata(curr_usage); u32 mpu_deepest_state = PWRDM_POWER_RET; u32 core_deepest_state = PWRDM_POWER_RET; int next_index = -1; @@ -202,7 +203,7 @@ static int next_valid_state(struct cpuidle_device *dev, */ idx--; for (; idx >= 0; idx--) { - cx = cpuidle_get_statedata(&dev->states[idx]); + cx = cpuidle_get_statedata(&dev->states_usage[idx]); if ((cx->valid) && (cx->mpu_state >= mpu_deepest_state) && (cx->core_state >= core_deepest_state)) { @@ -231,7 +232,6 @@ static int next_valid_state(struct cpuidle_device *dev, static int omap3_enter_idle_bm(struct cpuidle_device *dev, int index) { - struct cpuidle_state *state = &dev->states[index]; int new_state_idx; u32 core_next_state, per_next_state = 0, per_saved_state = 0, cam_state; struct omap3_idle_statedata *cx; @@ -264,7 +264,7 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev, * Prevent PER off if CORE is not in retention or off as this * would disable PER wakeups completely. */ - cx = cpuidle_get_statedata(state); + cx = cpuidle_get_statedata(&dev->states_usage[index]); core_next_state = cx->core_state; per_next_state = per_saved_state = pwrdm_read_next_pwrst(per_pd); if ((per_next_state == PWRDM_POWER_OFF) && @@ -318,6 +318,7 @@ static inline struct omap3_idle_statedata *_fill_cstate( { struct omap3_idle_statedata *cx = &omap3_idle_data[idx]; struct cpuidle_state *state = &dev->states[idx]; + struct cpuidle_state_usage *state_usage = &dev->states_usage[idx]; state->exit_latency = cpuidle_params_table[idx].exit_latency; state->target_residency = cpuidle_params_table[idx].target_residency; @@ -326,7 +327,7 @@ static inline struct omap3_idle_statedata *_fill_cstate( cx->valid = cpuidle_params_table[idx].valid; sprintf(state->name, "C%d", idx + 1); strncpy(state->desc, descr, CPUIDLE_DESC_LEN); - cpuidle_set_statedata(state, cx); + cpuidle_set_statedata(state_usage, cx); return cx; } diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 9cd08cecb347..b98c75285690 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -745,14 +745,13 @@ static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx) * * This is equivalent to the HALT instruction. */ -static int acpi_idle_enter_c1(struct cpuidle_device *dev, - int index) +static int acpi_idle_enter_c1(struct cpuidle_device *dev, int index) { ktime_t kt1, kt2; s64 idle_time; struct acpi_processor *pr; - struct cpuidle_state *state = &dev->states[index]; - struct acpi_processor_cx *cx = cpuidle_get_statedata(state); + struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; + struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage); pr = __this_cpu_read(processors); dev->last_residency = 0; @@ -790,12 +789,11 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, * @dev: the target CPU * @index: the index of suggested state */ -static int acpi_idle_enter_simple(struct cpuidle_device *dev, - int index) +static int acpi_idle_enter_simple(struct cpuidle_device *dev, int index) { struct acpi_processor *pr; - struct cpuidle_state *state = &dev->states[index]; - struct acpi_processor_cx *cx = cpuidle_get_statedata(state); + struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; + struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage); ktime_t kt1, kt2; s64 idle_time_ns; s64 idle_time; @@ -875,12 +873,11 @@ static DEFINE_SPINLOCK(c3_lock); * * If BM is detected, the deepest non-C3 idle state is entered instead. */ -static int acpi_idle_enter_bm(struct cpuidle_device *dev, - int index) +static int acpi_idle_enter_bm(struct cpuidle_device *dev, int index) { struct acpi_processor *pr; - struct cpuidle_state *state = &dev->states[index]; - struct acpi_processor_cx *cx = cpuidle_get_statedata(state); + struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; + struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage); ktime_t kt1, kt2; s64 idle_time_ns; s64 idle_time; @@ -1004,6 +1001,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) int i, count = CPUIDLE_DRIVER_STATE_START; struct acpi_processor_cx *cx; struct cpuidle_state *state; + struct cpuidle_state_usage *state_usage; struct cpuidle_device *dev = &pr->power.dev; if (!pr->flags.power_setup_done) @@ -1026,6 +1024,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { cx = &pr->power.states[i]; state = &dev->states[count]; + state_usage = &dev->states_usage[count]; if (!cx->valid) continue; @@ -1036,7 +1035,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) continue; #endif - cpuidle_set_statedata(state, cx); + cpuidle_set_statedata(state_usage, cx); snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i); strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN); diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index f66bcf9bfe93..7127e92fa8a1 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -105,9 +105,9 @@ int cpuidle_idle_call(void) /* This can be moved to within driver enter routine * but that results in multiple copies of same code. */ - dev->states[entered_state].time += + dev->states_usage[entered_state].time += (unsigned long long)dev->last_residency; - dev->states[entered_state].usage++; + dev->states_usage[entered_state].usage++; } /* give the governor an opportunity to reflect on the outcome */ @@ -186,8 +186,9 @@ static int poll_idle(struct cpuidle_device *dev, int index) static void poll_idle_init(struct cpuidle_device *dev) { struct cpuidle_state *state = &dev->states[0]; + struct cpuidle_state_usage *state_usage = &dev->states_usage[0]; - cpuidle_set_statedata(state, NULL); + cpuidle_set_statedata(state_usage, NULL); snprintf(state->name, CPUIDLE_NAME_LEN, "POLL"); snprintf(state->desc, CPUIDLE_DESC_LEN, "CPUIDLE CORE POLL IDLE"); @@ -235,8 +236,8 @@ int cpuidle_enable_device(struct cpuidle_device *dev) goto fail_sysfs; for (i = 0; i < dev->state_count; i++) { - dev->states[i].usage = 0; - dev->states[i].time = 0; + dev->states_usage[i].usage = 0; + dev->states_usage[i].time = 0; } dev->last_residency = 0; diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index be7917ec40c9..8a1ace104476 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c @@ -216,7 +216,8 @@ static struct kobj_type ktype_cpuidle = { struct cpuidle_state_attr { struct attribute attr; - ssize_t (*show)(struct cpuidle_state *, char *); + ssize_t (*show)(struct cpuidle_state *, \ + struct cpuidle_state_usage *, char *); ssize_t (*store)(struct cpuidle_state *, const char *, size_t); }; @@ -224,19 +225,22 @@ struct cpuidle_state_attr { static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0444, show, NULL) #define define_show_state_function(_name) \ -static ssize_t show_state_##_name(struct cpuidle_state *state, char *buf) \ +static ssize_t show_state_##_name(struct cpuidle_state *state, \ + struct cpuidle_state_usage *state_usage, char *buf) \ { \ return sprintf(buf, "%u\n", state->_name);\ } #define define_show_state_ull_function(_name) \ -static ssize_t show_state_##_name(struct cpuidle_state *state, char *buf) \ +static ssize_t show_state_##_name(struct cpuidle_state *state, \ + struct cpuidle_state_usage *state_usage, char *buf) \ { \ - return sprintf(buf, "%llu\n", state->_name);\ + return sprintf(buf, "%llu\n", state_usage->_name);\ } #define define_show_state_str_function(_name) \ -static ssize_t show_state_##_name(struct cpuidle_state *state, char *buf) \ +static ssize_t show_state_##_name(struct cpuidle_state *state, \ + struct cpuidle_state_usage *state_usage, char *buf) \ { \ if (state->_name[0] == '\0')\ return sprintf(buf, "\n");\ @@ -269,16 +273,18 @@ static struct attribute *cpuidle_state_default_attrs[] = { #define kobj_to_state_obj(k) container_of(k, struct cpuidle_state_kobj, kobj) #define kobj_to_state(k) (kobj_to_state_obj(k)->state) +#define kobj_to_state_usage(k) (kobj_to_state_obj(k)->state_usage) #define attr_to_stateattr(a) container_of(a, struct cpuidle_state_attr, attr) static ssize_t cpuidle_state_show(struct kobject * kobj, struct attribute * attr ,char * buf) { int ret = -EIO; struct cpuidle_state *state = kobj_to_state(kobj); + struct cpuidle_state_usage *state_usage = kobj_to_state_usage(kobj); struct cpuidle_state_attr * cattr = attr_to_stateattr(attr); if (cattr->show) - ret = cattr->show(state, buf); + ret = cattr->show(state, state_usage, buf); return ret; } @@ -323,6 +329,7 @@ int cpuidle_add_state_sysfs(struct cpuidle_device *device) if (!kobj) goto error_state; kobj->state = &device->states[i]; + kobj->state_usage = &device->states_usage[i]; init_completion(&kobj->kobj_unregister); ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle, &device->kobj, diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index a1c888d2216a..3aa8d4cb6dca 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -109,7 +109,6 @@ static struct cpuidle_state nehalem_cstates[MWAIT_MAX_NUM_CSTATES] = { { /* MWAIT C1 */ .name = "C1-NHM", .desc = "MWAIT 0x00", - .driver_data = (void *) 0x00, .flags = CPUIDLE_FLAG_TIME_VALID, .exit_latency = 3, .target_residency = 6, @@ -117,7 +116,6 @@ static struct cpuidle_state nehalem_cstates[MWAIT_MAX_NUM_CSTATES] = { { /* MWAIT C2 */ .name = "C3-NHM", .desc = "MWAIT 0x10", - .driver_data = (void *) 0x10, .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 20, .target_residency = 80, @@ -125,7 +123,6 @@ static struct cpuidle_state nehalem_cstates[MWAIT_MAX_NUM_CSTATES] = { { /* MWAIT C3 */ .name = "C6-NHM", .desc = "MWAIT 0x20", - .driver_data = (void *) 0x20, .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 200, .target_residency = 800, @@ -137,7 +134,6 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = { { /* MWAIT C1 */ .name = "C1-SNB", .desc = "MWAIT 0x00", - .driver_data = (void *) 0x00, .flags = CPUIDLE_FLAG_TIME_VALID, .exit_latency = 1, .target_residency = 1, @@ -145,7 +141,6 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = { { /* MWAIT C2 */ .name = "C3-SNB", .desc = "MWAIT 0x10", - .driver_data = (void *) 0x10, .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 80, .target_residency = 211, @@ -153,7 +148,6 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = { { /* MWAIT C3 */ .name = "C6-SNB", .desc = "MWAIT 0x20", - .driver_data = (void *) 0x20, .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 104, .target_residency = 345, @@ -161,7 +155,6 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = { { /* MWAIT C4 */ .name = "C7-SNB", .desc = "MWAIT 0x30", - .driver_data = (void *) 0x30, .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 109, .target_residency = 345, @@ -173,7 +166,6 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = { { /* MWAIT C1 */ .name = "C1-ATM", .desc = "MWAIT 0x00", - .driver_data = (void *) 0x00, .flags = CPUIDLE_FLAG_TIME_VALID, .exit_latency = 1, .target_residency = 4, @@ -181,7 +173,6 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = { { /* MWAIT C2 */ .name = "C2-ATM", .desc = "MWAIT 0x10", - .driver_data = (void *) 0x10, .flags = CPUIDLE_FLAG_TIME_VALID, .exit_latency = 20, .target_residency = 80, @@ -190,7 +181,6 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = { { /* MWAIT C4 */ .name = "C4-ATM", .desc = "MWAIT 0x30", - .driver_data = (void *) 0x30, .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 100, .target_residency = 400, @@ -199,13 +189,41 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = { { /* MWAIT C6 */ .name = "C6-ATM", .desc = "MWAIT 0x52", - .driver_data = (void *) 0x52, .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 140, .target_residency = 560, .enter = &intel_idle }, }; +static int get_driver_data(int cstate) +{ + int driver_data; + switch (cstate) { + + case 1: /* MWAIT C1 */ + driver_data = 0x00; + break; + case 2: /* MWAIT C2 */ + driver_data = 0x10; + break; + case 3: /* MWAIT C3 */ + driver_data = 0x20; + break; + case 4: /* MWAIT C4 */ + driver_data = 0x30; + break; + case 5: /* MWAIT C5 */ + driver_data = 0x40; + break; + case 6: /* MWAIT C6 */ + driver_data = 0x52; + break; + default: + driver_data = 0x00; + } + return driver_data; +} + /** * intel_idle * @dev: cpuidle_device @@ -216,7 +234,8 @@ static int intel_idle(struct cpuidle_device *dev, int index) { unsigned long ecx = 1; /* break on interrupt flag */ struct cpuidle_state *state = &dev->states[index]; - unsigned long eax = (unsigned long)cpuidle_get_statedata(state); + struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; + unsigned long eax = (unsigned long)cpuidle_get_statedata(state_usage); unsigned int cstate; ktime_t kt_before, kt_after; s64 usec_delta; @@ -451,6 +470,9 @@ static int intel_idle_cpuidle_devices_init(void) dev->states[dev->state_count] = /* structure copy */ cpuidle_state_table[cstate]; + dev->states_usage[dev->state_count].driver_data = + (void *)get_driver_data(cstate); + dev->state_count += 1; } diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index c6d85cf90eb2..0156540b3f79 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -28,19 +28,22 @@ struct cpuidle_device; * CPUIDLE DEVICE INTERFACE * ****************************/ +struct cpuidle_state_usage { + void *driver_data; + + unsigned long long usage; + unsigned long long time; /* in US */ +}; + struct cpuidle_state { char name[CPUIDLE_NAME_LEN]; char desc[CPUIDLE_DESC_LEN]; - void *driver_data; unsigned int flags; unsigned int exit_latency; /* in US */ unsigned int power_usage; /* in mW */ unsigned int target_residency; /* in US */ - unsigned long long usage; - unsigned long long time; /* in US */ - int (*enter) (struct cpuidle_device *dev, int index); }; @@ -52,26 +55,27 @@ struct cpuidle_state { /** * cpuidle_get_statedata - retrieves private driver state data - * @state: the state + * @st_usage: the state usage statistics */ -static inline void * cpuidle_get_statedata(struct cpuidle_state *state) +static inline void *cpuidle_get_statedata(struct cpuidle_state_usage *st_usage) { - return state->driver_data; + return st_usage->driver_data; } /** * cpuidle_set_statedata - stores private driver state data - * @state: the state + * @st_usage: the state usage statistics * @data: the private data */ static inline void -cpuidle_set_statedata(struct cpuidle_state *state, void *data) +cpuidle_set_statedata(struct cpuidle_state_usage *st_usage, void *data) { - state->driver_data = data; + st_usage->driver_data = data; } struct cpuidle_state_kobj { struct cpuidle_state *state; + struct cpuidle_state_usage *state_usage; struct completion kobj_unregister; struct kobject kobj; }; @@ -85,6 +89,7 @@ struct cpuidle_device { int last_residency; int state_count; struct cpuidle_state states[CPUIDLE_STATE_MAX]; + struct cpuidle_state_usage states_usage[CPUIDLE_STATE_MAX]; struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX]; struct list_head device_list; -- cgit v1.2.3 From 46bcfad7a819bd17ac4e831b04405152d59784ab Mon Sep 17 00:00:00 2001 From: Deepthi Dharwar Date: Fri, 28 Oct 2011 16:20:42 +0530 Subject: 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 Signed-off-by: Trinabh Gupta Tested-by: Jean Pihet Reviewed-by: Kevin Hilman Acked-by: Arjan van de Ven Acked-by: Kevin Hilman Signed-off-by: Len Brown --- arch/arm/mach-at91/cpuidle.c | 31 +++--- arch/arm/mach-davinci/cpuidle.c | 39 +++---- arch/arm/mach-exynos4/cpuidle.c | 23 ++-- arch/arm/mach-kirkwood/cpuidle.c | 30 +++--- arch/arm/mach-omap2/cpuidle34xx.c | 73 ++++++++----- arch/sh/kernel/cpu/shmobile/cpuidle.c | 18 ++-- drivers/acpi/processor_driver.c | 20 +--- drivers/acpi/processor_idle.c | 191 +++++++++++++++++++++++++++++----- drivers/cpuidle/cpuidle.c | 45 +++----- drivers/cpuidle/driver.c | 25 +++++ drivers/cpuidle/governors/ladder.c | 28 +++-- drivers/cpuidle/governors/menu.c | 20 ++-- drivers/cpuidle/sysfs.c | 3 +- drivers/idle/intel_idle.c | 80 ++++++++++---- include/acpi/processor.h | 1 + include/linux/cpuidle.h | 19 ++-- 16 files changed, 439 insertions(+), 207 deletions(-) (limited to 'drivers/acpi') diff --git a/arch/arm/mach-at91/cpuidle.c b/arch/arm/mach-at91/cpuidle.c index 4696a0d61e2e..93178f67420e 100644 --- a/arch/arm/mach-at91/cpuidle.c +++ b/arch/arm/mach-at91/cpuidle.c @@ -33,6 +33,7 @@ static struct cpuidle_driver at91_idle_driver = { /* Actual code that puts the SoC in different idle states */ static int at91_enter_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { struct timeval before, after; @@ -64,27 +65,29 @@ static int at91_enter_idle(struct cpuidle_device *dev, static int at91_init_cpuidle(void) { struct cpuidle_device *device; - - cpuidle_register_driver(&at91_idle_driver); + struct cpuidle_driver *driver = &at91_idle_driver; device = &per_cpu(at91_cpuidle_device, smp_processor_id()); device->state_count = AT91_MAX_STATES; + driver->state_count = AT91_MAX_STATES; /* Wait for interrupt state */ - device->states[0].enter = at91_enter_idle; - device->states[0].exit_latency = 1; - device->states[0].target_residency = 10000; - device->states[0].flags = CPUIDLE_FLAG_TIME_VALID; - strcpy(device->states[0].name, "WFI"); - strcpy(device->states[0].desc, "Wait for interrupt"); + driver->states[0].enter = at91_enter_idle; + driver->states[0].exit_latency = 1; + driver->states[0].target_residency = 10000; + driver->states[0].flags = CPUIDLE_FLAG_TIME_VALID; + strcpy(driver->states[0].name, "WFI"); + strcpy(driver->states[0].desc, "Wait for interrupt"); /* Wait for interrupt and RAM self refresh state */ - device->states[1].enter = at91_enter_idle; - device->states[1].exit_latency = 10; - device->states[1].target_residency = 10000; - device->states[1].flags = CPUIDLE_FLAG_TIME_VALID; - strcpy(device->states[1].name, "RAM_SR"); - strcpy(device->states[1].desc, "WFI and RAM Self Refresh"); + driver->states[1].enter = at91_enter_idle; + driver->states[1].exit_latency = 10; + driver->states[1].target_residency = 10000; + driver->states[1].flags = CPUIDLE_FLAG_TIME_VALID; + strcpy(driver->states[1].name, "RAM_SR"); + strcpy(driver->states[1].desc, "WFI and RAM Self Refresh"); + + cpuidle_register_driver(&at91_idle_driver); if (cpuidle_register_device(device)) { printk(KERN_ERR "at91_init_cpuidle: Failed registering\n"); diff --git a/arch/arm/mach-davinci/cpuidle.c b/arch/arm/mach-davinci/cpuidle.c index f2d2f34603d9..dbeeccd00173 100644 --- a/arch/arm/mach-davinci/cpuidle.c +++ b/arch/arm/mach-davinci/cpuidle.c @@ -78,6 +78,7 @@ static struct davinci_ops davinci_states[DAVINCI_CPUIDLE_MAX_STATES] = { /* Actual code that puts the SoC in different idle states */ static int davinci_enter_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; @@ -109,6 +110,7 @@ static int __init davinci_cpuidle_probe(struct platform_device *pdev) { int ret; struct cpuidle_device *device; + struct cpuidle_driver *driver = &davinci_idle_driver; struct davinci_cpuidle_config *pdata = pdev->dev.platform_data; device = &per_cpu(davinci_cpuidle_device, smp_processor_id()); @@ -120,32 +122,33 @@ static int __init davinci_cpuidle_probe(struct platform_device *pdev) ddr2_reg_base = pdata->ddr2_ctlr_base; - ret = cpuidle_register_driver(&davinci_idle_driver); - if (ret) { - dev_err(&pdev->dev, "failed to register driver\n"); - return ret; - } - /* Wait for interrupt state */ - device->states[0].enter = davinci_enter_idle; - device->states[0].exit_latency = 1; - device->states[0].target_residency = 10000; - device->states[0].flags = CPUIDLE_FLAG_TIME_VALID; - strcpy(device->states[0].name, "WFI"); - strcpy(device->states[0].desc, "Wait for interrupt"); + driver->states[0].enter = davinci_enter_idle; + driver->states[0].exit_latency = 1; + driver->states[0].target_residency = 10000; + driver->states[0].flags = CPUIDLE_FLAG_TIME_VALID; + strcpy(driver->states[0].name, "WFI"); + strcpy(driver->states[0].desc, "Wait for interrupt"); /* Wait for interrupt and DDR self refresh state */ - device->states[1].enter = davinci_enter_idle; - device->states[1].exit_latency = 10; - device->states[1].target_residency = 10000; - device->states[1].flags = CPUIDLE_FLAG_TIME_VALID; - strcpy(device->states[1].name, "DDR SR"); - strcpy(device->states[1].desc, "WFI and DDR Self Refresh"); + driver->states[1].enter = davinci_enter_idle; + driver->states[1].exit_latency = 10; + driver->states[1].target_residency = 10000; + driver->states[1].flags = CPUIDLE_FLAG_TIME_VALID; + strcpy(driver->states[1].name, "DDR SR"); + strcpy(driver->states[1].desc, "WFI and DDR Self Refresh"); if (pdata->ddr2_pdown) davinci_states[1].flags |= DAVINCI_CPUIDLE_FLAGS_DDR2_PWDN; cpuidle_set_statedata(&device->states_usage[1], &davinci_states[1]); device->state_count = DAVINCI_CPUIDLE_MAX_STATES; + driver->state_count = DAVINCI_CPUIDLE_MAX_STATES; + + ret = cpuidle_register_driver(&davinci_idle_driver); + if (ret) { + dev_err(&pdev->dev, "failed to register driver\n"); + return ret; + } ret = cpuidle_register_device(device); if (ret) { diff --git a/arch/arm/mach-exynos4/cpuidle.c b/arch/arm/mach-exynos4/cpuidle.c index ea026e72b977..35f6502144ae 100644 --- a/arch/arm/mach-exynos4/cpuidle.c +++ b/arch/arm/mach-exynos4/cpuidle.c @@ -16,6 +16,7 @@ #include static int exynos4_enter_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index); static struct cpuidle_state exynos4_cpuidle_set[] = { @@ -37,6 +38,7 @@ static struct cpuidle_driver exynos4_idle_driver = { }; static int exynos4_enter_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { struct timeval before, after; @@ -60,22 +62,23 @@ static int __init exynos4_init_cpuidle(void) { int i, max_cpuidle_state, cpu_id; struct cpuidle_device *device; - + struct cpuidle_driver *drv = &exynos4_idle_driver; + + /* Setup cpuidle driver */ + drv->state_count = (sizeof(exynos4_cpuidle_set) / + sizeof(struct cpuidle_state)); + max_cpuidle_state = drv->state_count; + for (i = 0; i < max_cpuidle_state; i++) { + memcpy(&drv->states[i], &exynos4_cpuidle_set[i], + sizeof(struct cpuidle_state)); + } cpuidle_register_driver(&exynos4_idle_driver); for_each_cpu(cpu_id, cpu_online_mask) { device = &per_cpu(exynos4_cpuidle_device, cpu_id); device->cpu = cpu_id; - device->state_count = (sizeof(exynos4_cpuidle_set) / - sizeof(struct cpuidle_state)); - - max_cpuidle_state = device->state_count; - - for (i = 0; i < max_cpuidle_state; i++) { - memcpy(&device->states[i], &exynos4_cpuidle_set[i], - sizeof(struct cpuidle_state)); - } + device->state_count = drv->state_count; if (cpuidle_register_device(device)) { printk(KERN_ERR "CPUidle register device failed\n,"); diff --git a/arch/arm/mach-kirkwood/cpuidle.c b/arch/arm/mach-kirkwood/cpuidle.c index 358dd80b3a07..ffd690dc3d33 100644 --- a/arch/arm/mach-kirkwood/cpuidle.c +++ b/arch/arm/mach-kirkwood/cpuidle.c @@ -32,6 +32,7 @@ static DEFINE_PER_CPU(struct cpuidle_device, kirkwood_cpuidle_device); /* Actual code that puts the SoC in different idle states */ static int kirkwood_enter_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { struct timeval before, after; @@ -68,28 +69,29 @@ static int kirkwood_enter_idle(struct cpuidle_device *dev, static int kirkwood_init_cpuidle(void) { struct cpuidle_device *device; - - cpuidle_register_driver(&kirkwood_idle_driver); + struct cpuidle_driver *driver = &kirkwood_idle_driver; device = &per_cpu(kirkwood_cpuidle_device, smp_processor_id()); device->state_count = KIRKWOOD_MAX_STATES; + driver->state_count = KIRKWOOD_MAX_STATES; /* Wait for interrupt state */ - device->states[0].enter = kirkwood_enter_idle; - device->states[0].exit_latency = 1; - device->states[0].target_residency = 10000; - device->states[0].flags = CPUIDLE_FLAG_TIME_VALID; - strcpy(device->states[0].name, "WFI"); - strcpy(device->states[0].desc, "Wait for interrupt"); + driver->states[0].enter = kirkwood_enter_idle; + driver->states[0].exit_latency = 1; + driver->states[0].target_residency = 10000; + driver->states[0].flags = CPUIDLE_FLAG_TIME_VALID; + strcpy(driver->states[0].name, "WFI"); + strcpy(driver->states[0].desc, "Wait for interrupt"); /* Wait for interrupt and DDR self refresh state */ - device->states[1].enter = kirkwood_enter_idle; - device->states[1].exit_latency = 10; - device->states[1].target_residency = 10000; - device->states[1].flags = CPUIDLE_FLAG_TIME_VALID; - strcpy(device->states[1].name, "DDR SR"); - strcpy(device->states[1].desc, "WFI and DDR Self Refresh"); + driver->states[1].enter = kirkwood_enter_idle; + driver->states[1].exit_latency = 10; + driver->states[1].target_residency = 10000; + driver->states[1].flags = CPUIDLE_FLAG_TIME_VALID; + strcpy(driver->states[1].name, "DDR SR"); + strcpy(driver->states[1].desc, "WFI and DDR Self Refresh"); + cpuidle_register_driver(&kirkwood_idle_driver); if (cpuidle_register_device(device)) { printk(KERN_ERR "kirkwood_init_cpuidle: Failed registering\n"); return -EIO; diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c index d3fce7b97fcf..1fe35c24fba2 100644 --- a/arch/arm/mach-omap2/cpuidle34xx.c +++ b/arch/arm/mach-omap2/cpuidle34xx.c @@ -88,12 +88,14 @@ static int _cpuidle_deny_idle(struct powerdomain *pwrdm, /** * omap3_enter_idle - Programs OMAP3 to enter the specified state * @dev: cpuidle device + * @drv: cpuidle driver * @index: the index of state to be entered * * Called from the CPUidle framework to program the device to the * specified target state selected by the governor. */ static int omap3_enter_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { struct omap3_idle_statedata *cx = @@ -148,6 +150,7 @@ return_sleep_time: /** * next_valid_state - Find next valid C-state * @dev: cpuidle device + * @drv: cpuidle driver * @index: Index of currently selected c-state * * If the state corresponding to index is valid, index is returned back @@ -158,10 +161,11 @@ return_sleep_time: * if it satisfies the enable_off_mode condition. */ static int next_valid_state(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { struct cpuidle_state_usage *curr_usage = &dev->states_usage[index]; - struct cpuidle_state *curr = &dev->states[index]; + struct cpuidle_state *curr = &drv->states[index]; struct omap3_idle_statedata *cx = cpuidle_get_statedata(curr_usage); u32 mpu_deepest_state = PWRDM_POWER_RET; u32 core_deepest_state = PWRDM_POWER_RET; @@ -188,7 +192,7 @@ static int next_valid_state(struct cpuidle_device *dev, /* Reach the current state starting at highest C-state */ for (; idx >= 0; idx--) { - if (&dev->states[idx] == curr) { + if (&drv->states[idx] == curr) { next_index = idx; break; } @@ -224,12 +228,14 @@ static int next_valid_state(struct cpuidle_device *dev, /** * omap3_enter_idle_bm - Checks for any bus activity * @dev: cpuidle device + * @drv: cpuidle driver * @index: array index of target state to be programmed * * This function checks for any pending activity and then programs * the device to the specified or a safer state. */ static int omap3_enter_idle_bm(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { int new_state_idx; @@ -238,7 +244,7 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev, int ret; if (!omap3_can_sleep()) { - new_state_idx = dev->safe_state_index; + new_state_idx = drv->safe_state_index; goto select_state; } @@ -248,7 +254,7 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev, */ cam_state = pwrdm_read_pwrst(cam_pd); if (cam_state == PWRDM_POWER_ON) { - new_state_idx = dev->safe_state_index; + new_state_idx = drv->safe_state_index; goto select_state; } @@ -275,10 +281,10 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev, if (per_next_state != per_saved_state) pwrdm_set_next_pwrst(per_pd, per_next_state); - new_state_idx = next_valid_state(dev, index); + new_state_idx = next_valid_state(dev, drv, index); select_state: - ret = omap3_enter_idle(dev, new_state_idx); + ret = omap3_enter_idle(dev, drv, new_state_idx); /* Restore original PER state if it was modified */ if (per_next_state != per_saved_state) @@ -311,22 +317,30 @@ struct cpuidle_driver omap3_idle_driver = { .owner = THIS_MODULE, }; -/* Helper to fill the C-state common data and register the driver_data */ -static inline struct omap3_idle_statedata *_fill_cstate( - struct cpuidle_device *dev, +/* Helper to fill the C-state common data*/ +static inline void _fill_cstate(struct cpuidle_driver *drv, int idx, const char *descr) { - struct omap3_idle_statedata *cx = &omap3_idle_data[idx]; - struct cpuidle_state *state = &dev->states[idx]; - struct cpuidle_state_usage *state_usage = &dev->states_usage[idx]; + struct cpuidle_state *state = &drv->states[idx]; state->exit_latency = cpuidle_params_table[idx].exit_latency; state->target_residency = cpuidle_params_table[idx].target_residency; state->flags = CPUIDLE_FLAG_TIME_VALID; state->enter = omap3_enter_idle_bm; - cx->valid = cpuidle_params_table[idx].valid; sprintf(state->name, "C%d", idx + 1); strncpy(state->desc, descr, CPUIDLE_DESC_LEN); + +} + +/* Helper to register the driver_data */ +static inline struct omap3_idle_statedata *_fill_cstate_usage( + struct cpuidle_device *dev, + int idx) +{ + struct omap3_idle_statedata *cx = &omap3_idle_data[idx]; + struct cpuidle_state_usage *state_usage = &dev->states_usage[idx]; + + cx->valid = cpuidle_params_table[idx].valid; cpuidle_set_statedata(state_usage, cx); return cx; @@ -341,6 +355,7 @@ static inline struct omap3_idle_statedata *_fill_cstate( int __init omap3_idle_init(void) { struct cpuidle_device *dev; + struct cpuidle_driver *drv = &omap3_idle_driver; struct omap3_idle_statedata *cx; mpu_pd = pwrdm_lookup("mpu_pwrdm"); @@ -348,45 +363,52 @@ int __init omap3_idle_init(void) per_pd = pwrdm_lookup("per_pwrdm"); cam_pd = pwrdm_lookup("cam_pwrdm"); - cpuidle_register_driver(&omap3_idle_driver); + + drv->safe_state_index = -1; dev = &per_cpu(omap3_idle_dev, smp_processor_id()); - dev->safe_state_index = -1; /* C1 . MPU WFI + Core active */ - cx = _fill_cstate(dev, 0, "MPU ON + CORE ON"); - (&dev->states[0])->enter = omap3_enter_idle; - dev->safe_state_index = 0; + _fill_cstate(drv, 0, "MPU ON + CORE ON"); + (&drv->states[0])->enter = omap3_enter_idle; + drv->safe_state_index = 0; + cx = _fill_cstate_usage(dev, 0); cx->valid = 1; /* C1 is always valid */ cx->mpu_state = PWRDM_POWER_ON; cx->core_state = PWRDM_POWER_ON; /* C2 . MPU WFI + Core inactive */ - cx = _fill_cstate(dev, 1, "MPU ON + CORE ON"); + _fill_cstate(drv, 1, "MPU ON + CORE ON"); + cx = _fill_cstate_usage(dev, 1); cx->mpu_state = PWRDM_POWER_ON; cx->core_state = PWRDM_POWER_ON; /* C3 . MPU CSWR + Core inactive */ - cx = _fill_cstate(dev, 2, "MPU RET + CORE ON"); + _fill_cstate(drv, 2, "MPU RET + CORE ON"); + cx = _fill_cstate_usage(dev, 2); cx->mpu_state = PWRDM_POWER_RET; cx->core_state = PWRDM_POWER_ON; /* C4 . MPU OFF + Core inactive */ - cx = _fill_cstate(dev, 3, "MPU OFF + CORE ON"); + _fill_cstate(drv, 3, "MPU OFF + CORE ON"); + cx = _fill_cstate_usage(dev, 3); cx->mpu_state = PWRDM_POWER_OFF; cx->core_state = PWRDM_POWER_ON; /* C5 . MPU RET + Core RET */ - cx = _fill_cstate(dev, 4, "MPU RET + CORE RET"); + _fill_cstate(drv, 4, "MPU RET + CORE RET"); + cx = _fill_cstate_usage(dev, 4); cx->mpu_state = PWRDM_POWER_RET; cx->core_state = PWRDM_POWER_RET; /* C6 . MPU OFF + Core RET */ - cx = _fill_cstate(dev, 5, "MPU OFF + CORE RET"); + _fill_cstate(drv, 5, "MPU OFF + CORE RET"); + cx = _fill_cstate_usage(dev, 5); cx->mpu_state = PWRDM_POWER_OFF; cx->core_state = PWRDM_POWER_RET; /* C7 . MPU OFF + Core OFF */ - cx = _fill_cstate(dev, 6, "MPU OFF + CORE OFF"); + _fill_cstate(drv, 6, "MPU OFF + CORE OFF"); + cx = _fill_cstate_usage(dev, 6); /* * Erratum i583: implementation for ES rev < Es1.2 on 3630. We cannot * enable OFF mode in a stable form for previous revisions. @@ -400,6 +422,9 @@ int __init omap3_idle_init(void) cx->mpu_state = PWRDM_POWER_OFF; cx->core_state = PWRDM_POWER_OFF; + drv->state_count = OMAP3_NUM_STATES; + cpuidle_register_driver(&omap3_idle_driver); + dev->state_count = OMAP3_NUM_STATES; if (cpuidle_register_device(dev)) { printk(KERN_ERR "%s: CPUidle register device failed\n", diff --git a/arch/sh/kernel/cpu/shmobile/cpuidle.c b/arch/sh/kernel/cpu/shmobile/cpuidle.c index 7be50d4c4268..ad1012ad6b42 100644 --- a/arch/sh/kernel/cpu/shmobile/cpuidle.c +++ b/arch/sh/kernel/cpu/shmobile/cpuidle.c @@ -25,6 +25,7 @@ static unsigned long cpuidle_mode[] = { }; static int cpuidle_sleep_enter(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { unsigned long allowed_mode = arch_hwblk_sleep_mode(); @@ -64,19 +65,19 @@ static struct cpuidle_driver cpuidle_driver = { void sh_mobile_setup_cpuidle(void) { struct cpuidle_device *dev = &cpuidle_dev; + struct cpuidle_driver *drv = &cpuidle_driver; struct cpuidle_state *state; int i; - cpuidle_register_driver(&cpuidle_driver); for (i = 0; i < CPUIDLE_STATE_MAX; i++) { - dev->states[i].name[0] = '\0'; - dev->states[i].desc[0] = '\0'; + drv->states[i].name[0] = '\0'; + drv->states[i].desc[0] = '\0'; } i = CPUIDLE_DRIVER_STATE_START; - state = &dev->states[i++]; + state = &drv->states[i++]; snprintf(state->name, CPUIDLE_NAME_LEN, "C1"); strncpy(state->desc, "SuperH Sleep Mode", CPUIDLE_DESC_LEN); state->exit_latency = 1; @@ -86,10 +87,10 @@ void sh_mobile_setup_cpuidle(void) state->flags |= CPUIDLE_FLAG_TIME_VALID; state->enter = cpuidle_sleep_enter; - dev->safe_state_index = i-1; + drv->safe_state_index = i-1; if (sh_mobile_sleep_supported & SUSP_SH_SF) { - state = &dev->states[i++]; + state = &drv->states[i++]; snprintf(state->name, CPUIDLE_NAME_LEN, "C2"); strncpy(state->desc, "SuperH Sleep Mode [SF]", CPUIDLE_DESC_LEN); @@ -102,7 +103,7 @@ void sh_mobile_setup_cpuidle(void) } if (sh_mobile_sleep_supported & SUSP_SH_STANDBY) { - state = &dev->states[i++]; + state = &drv->states[i++]; snprintf(state->name, CPUIDLE_NAME_LEN, "C3"); strncpy(state->desc, "SuperH Mobile Standby Mode [SF]", CPUIDLE_DESC_LEN); @@ -114,7 +115,10 @@ void sh_mobile_setup_cpuidle(void) state->enter = cpuidle_sleep_enter; } + drv->state_count = i; dev->state_count = i; + cpuidle_register_driver(&cpuidle_driver); + cpuidle_register_device(dev); } diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index a4e0f1ba6040..9d7bc9f6b6cc 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -426,7 +426,7 @@ static int acpi_cpu_soft_notify(struct notifier_block *nfb, if (action == CPU_ONLINE && pr) { acpi_processor_ppc_has_changed(pr, 0); - acpi_processor_cst_has_changed(pr); + acpi_processor_hotplug(pr); acpi_processor_reevaluate_tstate(pr, action); acpi_processor_tstate_has_changed(pr); } @@ -503,8 +503,7 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device) acpi_processor_get_throttling_info(pr); acpi_processor_get_limit_info(pr); - - if (cpuidle_get_driver() == &acpi_idle_driver) + if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) acpi_processor_power_init(pr, device); pr->cdev = thermal_cooling_device_register("Processor", device, @@ -800,17 +799,9 @@ static int __init acpi_processor_init(void) memset(&errata, 0, sizeof(errata)); - if (!cpuidle_register_driver(&acpi_idle_driver)) { - printk(KERN_DEBUG "ACPI: %s registered with cpuidle\n", - acpi_idle_driver.name); - } else { - printk(KERN_DEBUG "ACPI: acpi_idle yielding to %s\n", - cpuidle_get_driver()->name); - } - result = acpi_bus_register_driver(&acpi_processor_driver); if (result < 0) - goto out_cpuidle; + return result; acpi_processor_install_hotplug_notify(); @@ -821,11 +812,6 @@ static int __init acpi_processor_init(void) acpi_processor_throttling_init(); return 0; - -out_cpuidle: - cpuidle_unregister_driver(&acpi_idle_driver); - - return result; } static void __exit acpi_processor_exit(void) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index b98c75285690..24fe3afa7119 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -741,11 +741,13 @@ static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx) /** * acpi_idle_enter_c1 - enters an ACPI C1 state-type * @dev: the target CPU + * @drv: cpuidle driver containing cpuidle state info * @index: index of target state * * This is equivalent to the HALT instruction. */ -static int acpi_idle_enter_c1(struct cpuidle_device *dev, int index) +static int acpi_idle_enter_c1(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { ktime_t kt1, kt2; s64 idle_time; @@ -787,9 +789,11 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, int index) /** * acpi_idle_enter_simple - enters an ACPI state without BM handling * @dev: the target CPU + * @drv: cpuidle driver with cpuidle state information * @index: the index of suggested state */ -static int acpi_idle_enter_simple(struct cpuidle_device *dev, int index) +static int acpi_idle_enter_simple(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { struct acpi_processor *pr; struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; @@ -869,11 +873,13 @@ static DEFINE_SPINLOCK(c3_lock); /** * acpi_idle_enter_bm - enters C3 with proper BM handling * @dev: the target CPU + * @drv: cpuidle driver containing state data * @index: the index of suggested state * * If BM is detected, the deepest non-C3 idle state is entered instead. */ -static int acpi_idle_enter_bm(struct cpuidle_device *dev, int index) +static int acpi_idle_enter_bm(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { struct acpi_processor *pr; struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; @@ -896,9 +902,9 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, int index) } if (!cx->bm_sts_skip && acpi_idle_bm_check()) { - if (dev->safe_state_index >= 0) { - return dev->states[dev->safe_state_index].enter(dev, - dev->safe_state_index); + if (drv->safe_state_index >= 0) { + return drv->states[drv->safe_state_index].enter(dev, + drv, drv->safe_state_index); } else { local_irq_disable(); acpi_safe_halt(); @@ -993,14 +999,15 @@ struct cpuidle_driver acpi_idle_driver = { }; /** - * acpi_processor_setup_cpuidle - prepares and configures CPUIDLE + * acpi_processor_setup_cpuidle_cx - prepares and configures CPUIDLE + * device i.e. per-cpu data + * * @pr: the ACPI processor */ -static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) +static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr) { int i, count = CPUIDLE_DRIVER_STATE_START; struct acpi_processor_cx *cx; - struct cpuidle_state *state; struct cpuidle_state_usage *state_usage; struct cpuidle_device *dev = &pr->power.dev; @@ -1012,18 +1019,12 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) } dev->cpu = pr->id; - dev->safe_state_index = -1; - for (i = 0; i < CPUIDLE_STATE_MAX; i++) { - dev->states[i].name[0] = '\0'; - dev->states[i].desc[0] = '\0'; - } if (max_cstate == 0) max_cstate = 1; for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { cx = &pr->power.states[i]; - state = &dev->states[count]; state_usage = &dev->states_usage[count]; if (!cx->valid) @@ -1035,8 +1036,64 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) continue; #endif + cpuidle_set_statedata(state_usage, cx); + count++; + if (count == CPUIDLE_STATE_MAX) + break; + } + + dev->state_count = count; + + if (!count) + return -EINVAL; + + return 0; +} + +/** + * acpi_processor_setup_cpuidle states- prepares and configures cpuidle + * global state data i.e. idle routines + * + * @pr: the ACPI processor + */ +static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) +{ + int i, count = CPUIDLE_DRIVER_STATE_START; + struct acpi_processor_cx *cx; + struct cpuidle_state *state; + struct cpuidle_driver *drv = &acpi_idle_driver; + + if (!pr->flags.power_setup_done) + return -EINVAL; + + if (pr->flags.power == 0) + return -EINVAL; + + drv->safe_state_index = -1; + for (i = 0; i < CPUIDLE_STATE_MAX; i++) { + drv->states[i].name[0] = '\0'; + drv->states[i].desc[0] = '\0'; + } + + if (max_cstate == 0) + max_cstate = 1; + + for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { + cx = &pr->power.states[i]; + + if (!cx->valid) + continue; + +#ifdef CONFIG_HOTPLUG_CPU + if ((cx->type != ACPI_STATE_C1) && (num_online_cpus() > 1) && + !pr->flags.has_cst && + !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) + continue; +#endif + + state = &drv->states[count]; snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i); strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN); state->exit_latency = cx->latency; @@ -1049,13 +1106,13 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) state->flags |= CPUIDLE_FLAG_TIME_VALID; state->enter = acpi_idle_enter_c1; - dev->safe_state_index = count; + drv->safe_state_index = count; break; case ACPI_STATE_C2: state->flags |= CPUIDLE_FLAG_TIME_VALID; state->enter = acpi_idle_enter_simple; - dev->safe_state_index = count; + drv->safe_state_index = count; break; case ACPI_STATE_C3: @@ -1071,7 +1128,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) break; } - dev->state_count = count; + drv->state_count = count; if (!count) return -EINVAL; @@ -1079,7 +1136,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) return 0; } -int acpi_processor_cst_has_changed(struct acpi_processor *pr) +int acpi_processor_hotplug(struct acpi_processor *pr) { int ret = 0; @@ -1100,7 +1157,7 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr) cpuidle_disable_device(&pr->power.dev); acpi_processor_get_power_info(pr); if (pr->flags.power) { - acpi_processor_setup_cpuidle(pr); + acpi_processor_setup_cpuidle_cx(pr); ret = cpuidle_enable_device(&pr->power.dev); } cpuidle_resume_and_unlock(); @@ -1108,10 +1165,72 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr) return ret; } +int acpi_processor_cst_has_changed(struct acpi_processor *pr) +{ + int cpu; + struct acpi_processor *_pr; + + if (disabled_by_idle_boot_param()) + return 0; + + if (!pr) + return -EINVAL; + + if (nocst) + return -ENODEV; + + if (!pr->flags.power_setup_done) + return -ENODEV; + + /* + * FIXME: Design the ACPI notification to make it once per + * system instead of once per-cpu. This condition is a hack + * to make the code that updates C-States be called once. + */ + + if (smp_processor_id() == 0 && + cpuidle_get_driver() == &acpi_idle_driver) { + + cpuidle_pause_and_lock(); + /* Protect against cpu-hotplug */ + get_online_cpus(); + + /* Disable all cpuidle devices */ + for_each_online_cpu(cpu) { + _pr = per_cpu(processors, cpu); + if (!_pr || !_pr->flags.power_setup_done) + continue; + cpuidle_disable_device(&_pr->power.dev); + } + + /* Populate Updated C-state information */ + acpi_processor_setup_cpuidle_states(pr); + + /* Enable all cpuidle devices */ + for_each_online_cpu(cpu) { + _pr = per_cpu(processors, cpu); + if (!_pr || !_pr->flags.power_setup_done) + continue; + acpi_processor_get_power_info(_pr); + if (_pr->flags.power) { + acpi_processor_setup_cpuidle_cx(_pr); + cpuidle_enable_device(&_pr->power.dev); + } + } + put_online_cpus(); + cpuidle_resume_and_unlock(); + } + + return 0; +} + +static int acpi_processor_registered; + int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, struct acpi_device *device) { acpi_status status = 0; + int retval; static int first_run; if (disabled_by_idle_boot_param()) @@ -1148,9 +1267,26 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, * platforms that only support C1. */ if (pr->flags.power) { - acpi_processor_setup_cpuidle(pr); - if (cpuidle_register_device(&pr->power.dev)) - return -EIO; + /* Register acpi_idle_driver if not already registered */ + if (!acpi_processor_registered) { + acpi_processor_setup_cpuidle_states(pr); + retval = cpuidle_register_driver(&acpi_idle_driver); + if (retval) + return retval; + printk(KERN_DEBUG "ACPI: %s registered with cpuidle\n", + acpi_idle_driver.name); + } + /* Register per-cpu cpuidle_device. Cpuidle driver + * must already be registered before registering device + */ + acpi_processor_setup_cpuidle_cx(pr); + retval = cpuidle_register_device(&pr->power.dev); + if (retval) { + if (acpi_processor_registered == 0) + cpuidle_unregister_driver(&acpi_idle_driver); + return retval; + } + acpi_processor_registered++; } return 0; } @@ -1161,8 +1297,13 @@ int acpi_processor_power_exit(struct acpi_processor *pr, if (disabled_by_idle_boot_param()) return 0; - cpuidle_unregister_device(&pr->power.dev); - pr->flags.power_setup_done = 0; + if (pr->flags.power) { + cpuidle_unregister_device(&pr->power.dev); + acpi_processor_registered--; + if (acpi_processor_registered == 0) + cpuidle_unregister_driver(&acpi_idle_driver); + } + pr->flags.power_setup_done = 0; return 0; } diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 7127e92fa8a1..7a57b11eaa8d 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -61,6 +61,7 @@ static int __cpuidle_register_device(struct cpuidle_device *dev); int cpuidle_idle_call(void) { struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); + struct cpuidle_driver *drv = cpuidle_get_driver(); struct cpuidle_state *target_state; int next_state, entered_state; @@ -84,18 +85,18 @@ int cpuidle_idle_call(void) #endif /* ask the governor for the next state */ - next_state = cpuidle_curr_governor->select(dev); + next_state = cpuidle_curr_governor->select(drv, dev); if (need_resched()) { local_irq_enable(); return 0; } - target_state = &dev->states[next_state]; + target_state = &drv->states[next_state]; trace_power_start(POWER_CSTATE, next_state, dev->cpu); trace_cpu_idle(next_state, dev->cpu); - entered_state = target_state->enter(dev, next_state); + entered_state = target_state->enter(dev, drv, next_state); trace_power_end(dev->cpu); trace_cpu_idle(PWR_EVENT_EXIT, dev->cpu); @@ -163,7 +164,8 @@ void cpuidle_resume_and_unlock(void) EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock); #ifdef CONFIG_ARCH_HAS_CPU_RELAX -static int poll_idle(struct cpuidle_device *dev, int index) +static int poll_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { ktime_t t1, t2; s64 diff; @@ -183,12 +185,9 @@ static int poll_idle(struct cpuidle_device *dev, int index) return index; } -static void poll_idle_init(struct cpuidle_device *dev) +static void poll_idle_init(struct cpuidle_driver *drv) { - struct cpuidle_state *state = &dev->states[0]; - struct cpuidle_state_usage *state_usage = &dev->states_usage[0]; - - cpuidle_set_statedata(state_usage, NULL); + struct cpuidle_state *state = &drv->states[0]; snprintf(state->name, CPUIDLE_NAME_LEN, "POLL"); snprintf(state->desc, CPUIDLE_DESC_LEN, "CPUIDLE CORE POLL IDLE"); @@ -199,7 +198,7 @@ static void poll_idle_init(struct cpuidle_device *dev) state->enter = poll_idle; } #else -static void poll_idle_init(struct cpuidle_device *dev) {} +static void poll_idle_init(struct cpuidle_driver *drv) {} #endif /* CONFIG_ARCH_HAS_CPU_RELAX */ /** @@ -226,13 +225,13 @@ int cpuidle_enable_device(struct cpuidle_device *dev) return ret; } - poll_idle_init(dev); + poll_idle_init(cpuidle_get_driver()); if ((ret = cpuidle_add_state_sysfs(dev))) return ret; if (cpuidle_curr_governor->enable && - (ret = cpuidle_curr_governor->enable(dev))) + (ret = cpuidle_curr_governor->enable(cpuidle_get_driver(), dev))) goto fail_sysfs; for (i = 0; i < dev->state_count; i++) { @@ -273,7 +272,7 @@ void cpuidle_disable_device(struct cpuidle_device *dev) dev->enabled = 0; if (cpuidle_curr_governor->disable) - cpuidle_curr_governor->disable(dev); + cpuidle_curr_governor->disable(cpuidle_get_driver(), dev); cpuidle_remove_state_sysfs(dev); enabled_devices--; @@ -301,26 +300,6 @@ static int __cpuidle_register_device(struct cpuidle_device *dev) init_completion(&dev->kobj_unregister); - /* - * cpuidle driver should set the dev->power_specified bit - * before registering the device if the driver provides - * power_usage numbers. - * - * For those devices whose ->power_specified is not set, - * we fill in power_usage with decreasing values as the - * cpuidle code has an implicit assumption that state Cn - * uses less power than C(n-1). - * - * With CONFIG_ARCH_HAS_CPU_RELAX, C0 is already assigned - * an power value of -1. So we use -2, -3, etc, for other - * c-states. - */ - if (!dev->power_specified) { - int i; - for (i = CPUIDLE_DRIVER_STATE_START; i < dev->state_count; i++) - dev->states[i].power_usage = -1 - i; - } - per_cpu(cpuidle_devices, dev->cpu) = dev; list_add(&dev->device_list, &cpuidle_detected_devices); if ((ret = cpuidle_add_sysfs(sys_dev))) { diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 3f7e3cedd133..284d7af5a9c8 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -17,6 +17,30 @@ static struct cpuidle_driver *cpuidle_curr_driver; DEFINE_SPINLOCK(cpuidle_driver_lock); +static void __cpuidle_register_driver(struct cpuidle_driver *drv) +{ + int i; + /* + * cpuidle driver should set the drv->power_specified bit + * before registering if the driver provides + * power_usage numbers. + * + * If power_specified is not set, + * we fill in power_usage with decreasing values as the + * cpuidle code has an implicit assumption that state Cn + * uses less power than C(n-1). + * + * With CONFIG_ARCH_HAS_CPU_RELAX, C0 is already assigned + * an power value of -1. So we use -2, -3, etc, for other + * c-states. + */ + if (!drv->power_specified) { + for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) + drv->states[i].power_usage = -1 - i; + } +} + + /** * cpuidle_register_driver - registers a driver * @drv: the driver @@ -34,6 +58,7 @@ int cpuidle_register_driver(struct cpuidle_driver *drv) spin_unlock(&cpuidle_driver_lock); return -EBUSY; } + __cpuidle_register_driver(drv); cpuidle_curr_driver = drv; spin_unlock(&cpuidle_driver_lock); diff --git a/drivers/cpuidle/governors/ladder.c b/drivers/cpuidle/governors/ladder.c index 6a686a76711f..ef6b9e4727a7 100644 --- a/drivers/cpuidle/governors/ladder.c +++ b/drivers/cpuidle/governors/ladder.c @@ -60,9 +60,11 @@ static inline void ladder_do_selection(struct ladder_device *ldev, /** * ladder_select_state - selects the next state to enter + * @drv: cpuidle driver * @dev: the CPU */ -static int ladder_select_state(struct cpuidle_device *dev) +static int ladder_select_state(struct cpuidle_driver *drv, + struct cpuidle_device *dev) { struct ladder_device *ldev = &__get_cpu_var(ladder_devices); struct ladder_device_state *last_state; @@ -77,15 +79,17 @@ static int ladder_select_state(struct cpuidle_device *dev) last_state = &ldev->states[last_idx]; - if (dev->states[last_idx].flags & CPUIDLE_FLAG_TIME_VALID) - last_residency = cpuidle_get_last_residency(dev) - dev->states[last_idx].exit_latency; + if (drv->states[last_idx].flags & CPUIDLE_FLAG_TIME_VALID) { + last_residency = cpuidle_get_last_residency(dev) - \ + drv->states[last_idx].exit_latency; + } else last_residency = last_state->threshold.promotion_time + 1; /* consider promotion */ - if (last_idx < dev->state_count - 1 && + if (last_idx < drv->state_count - 1 && last_residency > last_state->threshold.promotion_time && - dev->states[last_idx + 1].exit_latency <= latency_req) { + drv->states[last_idx + 1].exit_latency <= latency_req) { last_state->stats.promotion_count++; last_state->stats.demotion_count = 0; if (last_state->stats.promotion_count >= last_state->threshold.promotion_count) { @@ -96,11 +100,11 @@ static int ladder_select_state(struct cpuidle_device *dev) /* consider demotion */ if (last_idx > CPUIDLE_DRIVER_STATE_START && - dev->states[last_idx].exit_latency > latency_req) { + drv->states[last_idx].exit_latency > latency_req) { int i; for (i = last_idx - 1; i > CPUIDLE_DRIVER_STATE_START; i--) { - if (dev->states[i].exit_latency <= latency_req) + if (drv->states[i].exit_latency <= latency_req) break; } ladder_do_selection(ldev, last_idx, i); @@ -123,9 +127,11 @@ static int ladder_select_state(struct cpuidle_device *dev) /** * ladder_enable_device - setup for the governor + * @drv: cpuidle driver * @dev: the CPU */ -static int ladder_enable_device(struct cpuidle_device *dev) +static int ladder_enable_device(struct cpuidle_driver *drv, + struct cpuidle_device *dev) { int i; struct ladder_device *ldev = &per_cpu(ladder_devices, dev->cpu); @@ -134,8 +140,8 @@ static int ladder_enable_device(struct cpuidle_device *dev) ldev->last_state_idx = CPUIDLE_DRIVER_STATE_START; - for (i = 0; i < dev->state_count; i++) { - state = &dev->states[i]; + for (i = 0; i < drv->state_count; i++) { + state = &drv->states[i]; lstate = &ldev->states[i]; lstate->stats.promotion_count = 0; @@ -144,7 +150,7 @@ static int ladder_enable_device(struct cpuidle_device *dev) lstate->threshold.promotion_count = PROMOTION_COUNT; lstate->threshold.demotion_count = DEMOTION_COUNT; - if (i < dev->state_count - 1) + if (i < drv->state_count - 1) lstate->threshold.promotion_time = state->exit_latency; if (i > 0) lstate->threshold.demotion_time = state->exit_latency; diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index af724e823c8e..bcbe88142135 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -182,7 +182,7 @@ static inline int performance_multiplier(void) static DEFINE_PER_CPU(struct menu_device, menu_devices); -static void menu_update(struct cpuidle_device *dev); +static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev); /* This implements DIV_ROUND_CLOSEST but avoids 64 bit division */ static u64 div_round64(u64 dividend, u32 divisor) @@ -228,9 +228,10 @@ static void detect_repeating_patterns(struct menu_device *data) /** * menu_select - selects the next idle state to enter + * @drv: cpuidle driver containing state data * @dev: the CPU */ -static int menu_select(struct cpuidle_device *dev) +static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) { struct menu_device *data = &__get_cpu_var(menu_devices); int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); @@ -240,7 +241,7 @@ static int menu_select(struct cpuidle_device *dev) struct timespec t; if (data->needs_update) { - menu_update(dev); + menu_update(drv, dev); data->needs_update = 0; } @@ -285,8 +286,8 @@ static int menu_select(struct cpuidle_device *dev) * Find the idle state with the lowest power while satisfying * our constraints. */ - for (i = CPUIDLE_DRIVER_STATE_START; i < dev->state_count; i++) { - struct cpuidle_state *s = &dev->states[i]; + for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { + struct cpuidle_state *s = &drv->states[i]; if (s->target_residency > data->predicted_us) continue; @@ -323,14 +324,15 @@ static void menu_reflect(struct cpuidle_device *dev, int index) /** * menu_update - attempts to guess what happened after entry + * @drv: cpuidle driver containing state data * @dev: the CPU */ -static void menu_update(struct cpuidle_device *dev) +static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) { struct menu_device *data = &__get_cpu_var(menu_devices); int last_idx = data->last_state_idx; unsigned int last_idle_us = cpuidle_get_last_residency(dev); - struct cpuidle_state *target = &dev->states[last_idx]; + struct cpuidle_state *target = &drv->states[last_idx]; unsigned int measured_us; u64 new_factor; @@ -384,9 +386,11 @@ static void menu_update(struct cpuidle_device *dev) /** * menu_enable_device - scans a CPU's states and does setup + * @drv: cpuidle driver * @dev: the CPU */ -static int menu_enable_device(struct cpuidle_device *dev) +static int menu_enable_device(struct cpuidle_driver *drv, + struct cpuidle_device *dev) { struct menu_device *data = &per_cpu(menu_devices, dev->cpu); diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index 8a1ace104476..1e756e160dca 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c @@ -322,13 +322,14 @@ int cpuidle_add_state_sysfs(struct cpuidle_device *device) { int i, ret = -ENOMEM; struct cpuidle_state_kobj *kobj; + struct cpuidle_driver *drv = cpuidle_get_driver(); /* state statistics */ for (i = 0; i < device->state_count; i++) { kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL); if (!kobj) goto error_state; - kobj->state = &device->states[i]; + kobj->state = &drv->states[i]; kobj->state_usage = &device->states_usage[i]; init_completion(&kobj->kobj_unregister); 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; static unsigned int lapic_timer_reliable_states = (1 << 1); /* Default to only C1 */ static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; -static int intel_idle(struct cpuidle_device *dev, int index); +static int intel_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index); static struct cpuidle_state *cpuidle_state_table; @@ -227,13 +228,15 @@ static int get_driver_data(int cstate) /** * intel_idle * @dev: cpuidle_device + * @drv: cpuidle driver * @index: index of cpuidle state * */ -static int intel_idle(struct cpuidle_device *dev, int index) +static int intel_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { unsigned long ecx = 1; /* break on interrupt flag */ - struct cpuidle_state *state = &dev->states[index]; + struct cpuidle_state *state = &drv->states[index]; struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; unsigned long eax = (unsigned long)cpuidle_get_statedata(state_usage); unsigned int cstate; @@ -419,6 +422,60 @@ static void intel_idle_cpuidle_devices_uninit(void) free_percpu(intel_idle_cpuidle_devices); return; } +/* + * intel_idle_cpuidle_driver_init() + * allocate, initialize cpuidle_states + */ +static int intel_idle_cpuidle_driver_init(void) +{ + int cstate; + struct cpuidle_driver *drv = &intel_idle_driver; + + drv->state_count = 1; + + for (cstate = 1; cstate < MWAIT_MAX_NUM_CSTATES; ++cstate) { + int num_substates; + + if (cstate > max_cstate) { + printk(PREFIX "max_cstate %d reached\n", + max_cstate); + break; + } + + /* does the state exist in CPUID.MWAIT? */ + num_substates = (mwait_substates >> ((cstate) * 4)) + & MWAIT_SUBSTATE_MASK; + if (num_substates == 0) + continue; + /* is the state not enabled? */ + if (cpuidle_state_table[cstate].enter == NULL) { + /* does the driver not know about the state? */ + if (*cpuidle_state_table[cstate].name == '\0') + pr_debug(PREFIX "unaware of model 0x%x" + " MWAIT %d please" + " contact lenb@kernel.org", + boot_cpu_data.x86_model, cstate); + continue; + } + + if ((cstate > 2) && + !boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) + mark_tsc_unstable("TSC halts in idle" + " states deeper than C2"); + + drv->states[drv->state_count] = /* structure copy */ + cpuidle_state_table[cstate]; + + drv->state_count += 1; + } + + if (auto_demotion_disable_flags) + smp_call_function(auto_demotion_disable, NULL, 1); + + return 0; +} + + /* * intel_idle_cpuidle_devices_init() * allocate, initialize, register cpuidle_devices @@ -453,23 +510,9 @@ static int intel_idle_cpuidle_devices_init(void) continue; /* is the state not enabled? */ if (cpuidle_state_table[cstate].enter == NULL) { - /* does the driver not know about the state? */ - if (*cpuidle_state_table[cstate].name == '\0') - pr_debug(PREFIX "unaware of model 0x%x" - " MWAIT %d please" - " contact lenb@kernel.org", - boot_cpu_data.x86_model, cstate); continue; } - if ((cstate > 2) && - !boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) - mark_tsc_unstable("TSC halts in idle" - " states deeper than C2"); - - dev->states[dev->state_count] = /* structure copy */ - cpuidle_state_table[cstate]; - dev->states_usage[dev->state_count].driver_data = (void *)get_driver_data(cstate); @@ -484,8 +527,6 @@ static int intel_idle_cpuidle_devices_init(void) return -EIO; } } - if (auto_demotion_disable_flags) - smp_call_function(auto_demotion_disable, NULL, 1); return 0; } @@ -503,6 +544,7 @@ static int __init intel_idle_init(void) if (retval) return retval; + intel_idle_cpuidle_driver_init(); retval = cpuidle_register_driver(&intel_idle_driver); if (retval) { printk(KERN_DEBUG PREFIX "intel_idle yielding to %s", diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 67055f180330..610f6fb1bbc2 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -329,6 +329,7 @@ extern void acpi_processor_throttling_init(void); int acpi_processor_power_init(struct acpi_processor *pr, struct acpi_device *device); int acpi_processor_cst_has_changed(struct acpi_processor *pr); +int acpi_processor_hotplug(struct acpi_processor *pr); int acpi_processor_power_exit(struct acpi_processor *pr, struct acpi_device *device); int acpi_processor_suspend(struct acpi_device * device, pm_message_t state); diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 0156540b3f79..c90418822f40 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -22,6 +22,7 @@ #define CPUIDLE_DESC_LEN 32 struct cpuidle_device; +struct cpuidle_driver; /**************************** @@ -45,6 +46,7 @@ struct cpuidle_state { unsigned int target_residency; /* in US */ int (*enter) (struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index); }; @@ -83,12 +85,10 @@ struct cpuidle_state_kobj { struct cpuidle_device { unsigned int registered:1; unsigned int enabled:1; - unsigned int power_specified:1; unsigned int cpu; int last_residency; int state_count; - struct cpuidle_state states[CPUIDLE_STATE_MAX]; struct cpuidle_state_usage states_usage[CPUIDLE_STATE_MAX]; struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX]; @@ -96,7 +96,6 @@ struct cpuidle_device { struct kobject kobj; struct completion kobj_unregister; void *governor_data; - int safe_state_index; }; DECLARE_PER_CPU(struct cpuidle_device *, cpuidle_devices); @@ -120,6 +119,11 @@ static inline int cpuidle_get_last_residency(struct cpuidle_device *dev) struct cpuidle_driver { char name[CPUIDLE_NAME_LEN]; struct module *owner; + + unsigned int power_specified:1; + struct cpuidle_state states[CPUIDLE_STATE_MAX]; + int state_count; + int safe_state_index; }; #ifdef CONFIG_CPU_IDLE @@ -166,10 +170,13 @@ struct cpuidle_governor { struct list_head governor_list; unsigned int rating; - int (*enable) (struct cpuidle_device *dev); - void (*disable) (struct cpuidle_device *dev); + int (*enable) (struct cpuidle_driver *drv, + struct cpuidle_device *dev); + void (*disable) (struct cpuidle_driver *drv, + struct cpuidle_device *dev); - int (*select) (struct cpuidle_device *dev); + int (*select) (struct cpuidle_driver *drv, + struct cpuidle_device *dev); void (*reflect) (struct cpuidle_device *dev, int index); struct module *owner; -- cgit v1.2.3