ACPI / cpuidle: fix max idle state handling with hotplug CPU support

acpi_processor_hotplug() calls acpi_processor_setup_cpuidle_cx()
without calling acpi_processor_setup_cpuidle_states() first so it
is possible that dev->state_count becomes different from
drv->state_count (in case of SMP system with unsupported C2/C3
states + enabled CPU hotplug and num_online_cpus() becoming > 1).

The driver code assumes that cpuidle core will handle such cases
but currently this is untrue (dev->state_count is used only for
handling cpuidle state sysfs entries and drv->state_count is used
for all other cases) and will not be fixed in the future as
dev->state_count is planned to be removed.

Fix the issue by checking for the max supported idle state in
C2/C3 state's ->enter handler (acpi_idle_enter_simple() for C2/C3
and acpi_idle_enter_bm() for C3 + bm_check flag set) and setting
the C1 state (instead of higher states) when needed.

Also remove no longer needed max idle state checks from
acpi_processor_setup_cpuidle_[states,cx]().

Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Len Brown <lenb@kernel.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
Bartlomiej Zolnierkiewicz 2013-12-20 19:47:26 +01:00 committed by Rafael J. Wysocki
parent 7f74dc0f4f
commit 7ca380f606

View File

@ -785,6 +785,13 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
if (unlikely(!pr)) if (unlikely(!pr))
return -EINVAL; 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))
return acpi_idle_enter_c1(dev, drv, CPUIDLE_DRIVER_STATE_START);
#endif
if (cx->entry_method == ACPI_CSTATE_FFH) { if (cx->entry_method == ACPI_CSTATE_FFH) {
if (current_set_polling_and_test()) if (current_set_polling_and_test())
return -EINVAL; return -EINVAL;
@ -831,6 +838,13 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
if (unlikely(!pr)) if (unlikely(!pr))
return -EINVAL; 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))
return acpi_idle_enter_c1(dev, drv, CPUIDLE_DRIVER_STATE_START);
#endif
if (!cx->bm_sts_skip && acpi_idle_bm_check()) { if (!cx->bm_sts_skip && acpi_idle_bm_check()) {
if (drv->safe_state_index >= 0) { if (drv->safe_state_index >= 0) {
return drv->states[drv->safe_state_index].enter(dev, return drv->states[drv->safe_state_index].enter(dev,
@ -932,12 +946,6 @@ static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr,
if (!cx->valid) if (!cx->valid)
continue; 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
per_cpu(acpi_cstate[count], dev->cpu) = cx; per_cpu(acpi_cstate[count], dev->cpu) = cx;
count++; count++;
@ -987,13 +995,6 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr)
if (!cx->valid) if (!cx->valid)
continue; 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]; state = &drv->states[count];
snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i); snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i);
strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN); strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN);