From b00783fd90388d221dabd4df2affeea7da2363cd Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 2 Feb 2015 23:55:08 +0100 Subject: ACPI / cpuidle: Drop unnecessary calls from acpi_idle_do_entry() Since the cpuidle core calls stop_critical_timings() and start_critical_timings() around the execution of ->enter callbacks, acpi_idle_do_entry() doesn't need to do that (and really shouldn't). Also using "inline" on it is pointless and the kerneldoc entry does not reflect the actual situation any more. Fix all of the above. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_idle.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 87b704e41877..f36517164553 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -681,15 +681,13 @@ static int acpi_idle_bm_check(void) } /** - * acpi_idle_do_entry - a helper function that does C2 and C3 type entry + * acpi_idle_do_entry - enter idle state using the appropriate method * @cx: cstate data * * Caller disables interrupt before call and enables interrupt after return. */ -static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx) +static void acpi_idle_do_entry(struct acpi_processor_cx *cx) { - /* Don't trace irqs off for idle */ - stop_critical_timings(); if (cx->entry_method == ACPI_CSTATE_FFH) { /* Call into architectural FFH based C-state */ acpi_processor_ffh_cstate_enter(cx); @@ -703,7 +701,6 @@ static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx) gets asserted in time to freeze execution properly. */ inl(acpi_gbl_FADT.xpm_timer_block.address); } - start_critical_timings(); } /** -- cgit v1.2.3-70-g09d2 From 67f592c8f681061d69c621b97a868e679c8a77be Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 2 Feb 2015 23:55:17 +0100 Subject: ACPI / cpuidle: Drop unnecessary calls from ->enter callback routines acpi_idle_enter_simple() and acpi_idle_enter_bm() don't need to call sched_clock_idle_sleep/wakeup_event(), because that's taken care of by the core already. Namely, sched_clock_idle_sleep_event() is called by tick_nohz_start_idle() called by __tick_nohz_idle_enter() which in turn is called by tick_nohz_idle_enter() and that is called by cpu_idle_loop(). Analogously for sched_clock_idle_wakeup_event(). For this reason, drop those calls from the ACPI cpuidle driver's ->enter callback routines. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_idle.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index f36517164553..ceeff3d473f1 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -791,12 +791,8 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, if (cx->type == ACPI_STATE_C3) ACPI_FLUSH_CPU_CACHE(); - /* Tell the scheduler that we are going deep-idle: */ - sched_clock_idle_sleep_event(); acpi_idle_do_entry(cx); - sched_clock_idle_wakeup_event(0); - lapic_timer_state_broadcast(pr, cx, 0); return index; } @@ -842,8 +838,6 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, acpi_unlazy_tlb(smp_processor_id()); - /* Tell the scheduler that we are going deep-idle: */ - sched_clock_idle_sleep_event(); /* * Must be done before busmaster disable as we might need to * access HPET ! @@ -881,8 +875,6 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, raw_spin_unlock(&c3_lock); } - sched_clock_idle_wakeup_event(0); - lapic_timer_state_broadcast(pr, cx, 0); return index; } -- cgit v1.2.3-70-g09d2 From adcb2623f149abd4d4783ecada543a01e25d1c04 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 2 Feb 2015 23:55:42 +0100 Subject: ACPI / cpuidle: Clean up fallback to C1 checks acpi_idle_enter_simple() and acpi_idle_enter_bm() both check if C2/C3 type entry is supported on MP in the same way, so move those checks to a separate function and call it from both places (and it doesn't need to check if the state type is not C1, because the functions in question won't be called otherwise). While at it, use IS_ENABLED() for the CONFIG_HOTPLUG_CPU check. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_idle.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index ceeff3d473f1..94c85ff2d123 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -758,6 +758,13 @@ static int acpi_idle_play_dead(struct cpuidle_device *dev, int index) return 0; } +static bool acpi_idle_fallback_to_c1(struct acpi_processor *pr) +{ + return IS_ENABLED(CONFIG_HOTPLUG_CPU) && num_online_cpus() > 1 && + !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED) && + !pr->flags.has_cst; +} + /** * acpi_idle_enter_simple - enters an ACPI state without BM handling * @dev: the target CPU @@ -775,12 +782,8 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, if (unlikely(!pr)) return -EINVAL; -#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)) + if (acpi_idle_fallback_to_c1(pr)) return acpi_idle_enter_c1(dev, drv, CPUIDLE_DRIVER_STATE_START); -#endif /* * Must be done before busmaster disable as we might need to @@ -819,12 +822,8 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, if (unlikely(!pr)) return -EINVAL; -#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)) + if (acpi_idle_fallback_to_c1(pr)) return acpi_idle_enter_c1(dev, drv, CPUIDLE_DRIVER_STATE_START); -#endif if (!cx->bm_sts_skip && acpi_idle_bm_check()) { if (drv->safe_state_index >= 0) { -- cgit v1.2.3-70-g09d2 From de6cc4ec6ea05de1933dc3b832013960af49c852 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 2 Feb 2015 23:55:47 +0100 Subject: ACPI / cpuidle: Drop irrelevant comment from acpi_idle_enter_simple() The comment about bus master disable in acpi_idle_enter_simple() is irrelevant, because the function doesn't disable bus mastering, so drop it. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_idle.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 94c85ff2d123..1798bbeff96b 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -785,10 +785,6 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, if (acpi_idle_fallback_to_c1(pr)) return acpi_idle_enter_c1(dev, drv, CPUIDLE_DRIVER_STATE_START); - /* - * Must be done before busmaster disable as we might need to - * access HPET ! - */ lapic_timer_state_broadcast(pr, cx, 1); if (cx->type == ACPI_STATE_C3) -- cgit v1.2.3-70-g09d2 From 5f97d6628640842229824c9f4fd433621c5a7b72 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 2 Feb 2015 23:55:55 +0100 Subject: ACPI / cpuidle: Clean up white space in a switch statement White space in the switch statement in acpi_processor_setup_cpuidle_states() does not adhere to the kernel coding style and that makes the code difficult to read. Clean that up. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_idle.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 1798bbeff96b..c05d5ec9882a 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -968,20 +968,20 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) state->flags = 0; switch (cx->type) { - case ACPI_STATE_C1: + case ACPI_STATE_C1: state->enter = acpi_idle_enter_c1; state->enter_dead = acpi_idle_play_dead; drv->safe_state_index = count; break; - case ACPI_STATE_C2: + case ACPI_STATE_C2: state->enter = acpi_idle_enter_simple; state->enter_dead = acpi_idle_play_dead; drv->safe_state_index = count; break; - case ACPI_STATE_C3: + case ACPI_STATE_C3: state->enter = pr->flags.bm_check ? acpi_idle_enter_bm : acpi_idle_enter_simple; -- cgit v1.2.3-70-g09d2 From 2a7383529109252a7f9d81639784e5fddb4f3df4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 2 Feb 2015 23:56:03 +0100 Subject: ACPI / cpuidle: Drop flags.bm_check tests from acpi_idle_enter_bm() Since acpi_idle_enter_bm() is only used if flags.bm_check is set for the given acpi_processor object, it doesn't make sense to check that flag in there. For this reason, drop flags.bm_check tests (and some code depending on them) from acpi_idle_enter_bm(). Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_idle.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index c05d5ec9882a..eaa32586f89c 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -842,28 +842,25 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, /* * disable bus master * bm_check implies we need ARB_DIS - * !bm_check implies we need cache flush * bm_control implies whether we can do ARB_DIS * * That leaves a case where bm_check is set and bm_control is * not set. In that case we cannot do much, we enter C3 * without doing anything. */ - if (pr->flags.bm_check && pr->flags.bm_control) { + if (pr->flags.bm_control) { raw_spin_lock(&c3_lock); c3_cpu_count++; /* Disable bus master arbitration when all CPUs are in C3 */ if (c3_cpu_count == num_online_cpus()) acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 1); raw_spin_unlock(&c3_lock); - } else if (!pr->flags.bm_check) { - ACPI_FLUSH_CPU_CACHE(); } acpi_idle_do_entry(cx); /* Re-enable bus master arbitration */ - if (pr->flags.bm_check && pr->flags.bm_control) { + if (pr->flags.bm_control) { raw_spin_lock(&c3_lock); acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 0); c3_cpu_count--; -- cgit v1.2.3-70-g09d2 From d2cecb3d66e39765f3dec8adfb88c322e709b06d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 3 Feb 2015 21:54:48 +0100 Subject: ACPI / cpuidle: Merge acpi_idle_enter_c1() and acpi_idle_enter_simple() acpi_idle_enter_c1() and acpi_idle_enter_simple() are close enough to each other that they can be merged into one function which reduces duplication of code quite a bit. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_idle.c | 43 ++++++------------------------------------- 1 file changed, 6 insertions(+), 37 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index eaa32586f89c..58113a6fa1d3 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -703,34 +703,6 @@ static 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, - struct cpuidle_driver *drv, int index) -{ - struct acpi_processor *pr; - struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); - - pr = __this_cpu_read(processors); - - if (unlikely(!pr)) - return -EINVAL; - - lapic_timer_state_broadcast(pr, cx, 1); - acpi_idle_do_entry(cx); - - lapic_timer_state_broadcast(pr, cx, 0); - - return index; -} - - /** * acpi_idle_play_dead - enters an ACPI state for long-term idle (i.e. off-lining) * @dev: the target CPU @@ -766,7 +738,7 @@ static bool acpi_idle_fallback_to_c1(struct acpi_processor *pr) } /** - * acpi_idle_enter_simple - enters an ACPI state without BM handling + * acpi_idle_enter_simple - enters a CPU idle state without BM handling * @dev: the target CPU * @drv: cpuidle driver with cpuidle state information * @index: the index of suggested state @@ -782,8 +754,10 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, if (unlikely(!pr)) return -EINVAL; - if (acpi_idle_fallback_to_c1(pr)) - return acpi_idle_enter_c1(dev, drv, CPUIDLE_DRIVER_STATE_START); + if (cx->type != ACPI_STATE_C1 && acpi_idle_fallback_to_c1(pr)) { + index = CPUIDLE_DRIVER_STATE_START; + cx = per_cpu(acpi_cstate[index], dev->cpu); + } lapic_timer_state_broadcast(pr, cx, 1); @@ -819,7 +793,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, return -EINVAL; if (acpi_idle_fallback_to_c1(pr)) - return acpi_idle_enter_c1(dev, drv, CPUIDLE_DRIVER_STATE_START); + return acpi_idle_enter_simple(dev, drv, CPUIDLE_DRIVER_STATE_START); if (!cx->bm_sts_skip && acpi_idle_bm_check()) { if (drv->safe_state_index >= 0) { @@ -967,11 +941,6 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) switch (cx->type) { case ACPI_STATE_C1: - state->enter = acpi_idle_enter_c1; - state->enter_dead = acpi_idle_play_dead; - drv->safe_state_index = count; - break; - case ACPI_STATE_C2: state->enter = acpi_idle_enter_simple; state->enter_dead = acpi_idle_play_dead; -- cgit v1.2.3-70-g09d2 From 6491bc0c616969f7ae1fcb30a8823c333e2944c7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 3 Feb 2015 21:55:11 +0100 Subject: ACPI / cpuidle: Common callback routine for entering states Introduce a common ->enter callback routine for the ACPI cpuidle driver, acpi_idle_enter(), which helps to reduce code complexity, size and duplication and prevents theoretically possible failues that an incorrect routine may be run to enter the given idle state due to a firmware bug (eg. when _CST returns a different set of states for each processor). Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_idle.c | 118 ++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 73 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 58113a6fa1d3..c256bd7fbd78 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -737,74 +737,17 @@ static bool acpi_idle_fallback_to_c1(struct acpi_processor *pr) !pr->flags.has_cst; } -/** - * acpi_idle_enter_simple - enters a CPU idle 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, - struct cpuidle_driver *drv, int index) -{ - struct acpi_processor *pr; - struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); - - pr = __this_cpu_read(processors); - - if (unlikely(!pr)) - return -EINVAL; - - if (cx->type != ACPI_STATE_C1 && acpi_idle_fallback_to_c1(pr)) { - index = CPUIDLE_DRIVER_STATE_START; - cx = per_cpu(acpi_cstate[index], dev->cpu); - } - - lapic_timer_state_broadcast(pr, cx, 1); - - if (cx->type == ACPI_STATE_C3) - ACPI_FLUSH_CPU_CACHE(); - - acpi_idle_do_entry(cx); - - lapic_timer_state_broadcast(pr, cx, 0); - return index; -} - static int c3_cpu_count; static DEFINE_RAW_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. + * @pr: Target processor + * @cx: Target state context */ -static int acpi_idle_enter_bm(struct cpuidle_device *dev, - struct cpuidle_driver *drv, int index) +static void acpi_idle_enter_bm(struct acpi_processor *pr, + struct acpi_processor_cx *cx) { - struct acpi_processor *pr; - struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); - - pr = __this_cpu_read(processors); - - if (unlikely(!pr)) - return -EINVAL; - - if (acpi_idle_fallback_to_c1(pr)) - return acpi_idle_enter_simple(dev, drv, CPUIDLE_DRIVER_STATE_START); - - if (!cx->bm_sts_skip && acpi_idle_bm_check()) { - if (drv->safe_state_index >= 0) { - return drv->states[drv->safe_state_index].enter(dev, - drv, drv->safe_state_index); - } else { - acpi_safe_halt(); - return -EBUSY; - } - } - acpi_unlazy_tlb(smp_processor_id()); /* @@ -842,6 +785,45 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, } lapic_timer_state_broadcast(pr, cx, 0); +} + +static int acpi_idle_enter(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); + struct acpi_processor *pr; + + pr = __this_cpu_read(processors); + if (unlikely(!pr)) + return -EINVAL; + + if (cx->type != ACPI_STATE_C1) { + if (acpi_idle_fallback_to_c1(pr)) { + index = CPUIDLE_DRIVER_STATE_START; + cx = per_cpu(acpi_cstate[index], dev->cpu); + } else if (cx->type == ACPI_STATE_C3 && pr->flags.bm_check) { + if (cx->bm_sts_skip || !acpi_idle_bm_check()) { + acpi_idle_enter_bm(pr, cx); + return index; + } else if (drv->safe_state_index >= 0) { + index = drv->safe_state_index; + cx = per_cpu(acpi_cstate[index], dev->cpu); + } else { + acpi_safe_halt(); + return -EBUSY; + } + } + } + + lapic_timer_state_broadcast(pr, cx, 1); + + if (cx->type == ACPI_STATE_C3) + ACPI_FLUSH_CPU_CACHE(); + + acpi_idle_do_entry(cx); + + lapic_timer_state_broadcast(pr, cx, 0); + return index; } @@ -936,22 +918,12 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN); state->exit_latency = cx->latency; state->target_residency = cx->latency * latency_factor; + state->enter = acpi_idle_enter; state->flags = 0; - switch (cx->type) { - - case ACPI_STATE_C1: - case ACPI_STATE_C2: - state->enter = acpi_idle_enter_simple; + if (cx->type == ACPI_STATE_C1 || cx->type == ACPI_STATE_C2) { state->enter_dead = acpi_idle_play_dead; drv->safe_state_index = count; - break; - - case ACPI_STATE_C3: - state->enter = pr->flags.bm_check ? - acpi_idle_enter_bm : - acpi_idle_enter_simple; - break; } count++; -- cgit v1.2.3-70-g09d2