diff --git a/sys/dev/acpica/acpi_cpu.c b/sys/dev/acpica/acpi_cpu.c index 0447337..b55d043 100644 --- a/sys/dev/acpica/acpi_cpu.c +++ b/sys/dev/acpica/acpi_cpu.c @@ -95,6 +95,7 @@ struct acpi_cpu_softc { struct sysctl_oid *cpu_sysctl_tree; int cpu_cx_lowest; int cpu_cx_lowest_lim; + int cpu_disable_idle; /* Disable entry to idle function */ char cpu_cx_supported[64]; }; @@ -153,9 +154,6 @@ static uint32_t cpu_smi_cmd; /* Value to write to SMI_CMD. */ static uint8_t cpu_cst_cnt; /* Indicate we are _CST aware. */ static int cpu_quirks; /* Indicate any hardware bugs. */ -/* Runtime state. */ -static int cpu_disable_idle; /* Disable entry to idle function */ - /* Values for sysctl. */ static struct sysctl_ctx_list cpu_sysctl_ctx; static struct sysctl_oid *cpu_sysctl_tree; @@ -447,6 +445,39 @@ acpi_cpu_postattach(void *unused __unused) SYSINIT(acpi_cpu, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, acpi_cpu_postattach, NULL); +static void +disable_idle(struct acpi_cpu_softc *sc) +{ + cpuset_t cpuset; + + CPU_SETOF(sc->cpu_pcpu->pc_cpuid, &cpuset); + sc->cpu_disable_idle = TRUE; + + /* + * Ensure that the CPU is not in idle state or in acpi_cpu_idle(). + * Note that this code depends on the fact that the rendezvous IPI + * can not penetrate context where interrupts are disabled and acpi_cpu_idle + * is called and executed in such a context with interrupts being re-enabled + * right before return. + */ + smp_rendezvous_cpus(cpuset, smp_no_rendevous_barrier, NULL, + smp_no_rendevous_barrier, NULL); +} + +static void +enable_idle(struct acpi_cpu_softc *sc) +{ + + sc->cpu_disable_idle = FALSE; +} + +static int +is_idle_disabled(struct acpi_cpu_softc *sc) +{ + + return (sc->cpu_disable_idle); +} + /* * Disable any entry to the idle function during suspend and re-enable it * during resume. @@ -459,7 +490,7 @@ acpi_cpu_suspend(device_t dev) error = bus_generic_suspend(dev); if (error) return (error); - cpu_disable_idle = TRUE; + disable_idle(device_get_softc(dev)); return (0); } @@ -467,7 +498,7 @@ static int acpi_cpu_resume(device_t dev) { - cpu_disable_idle = FALSE; + enable_idle(device_get_softc(dev)); return (bus_generic_resume(dev)); } @@ -601,12 +632,14 @@ acpi_cpu_shutdown(device_t dev) bus_generic_shutdown(dev); /* - * Disable any entry to the idle function. There is a small race where - * an idle thread have passed this check but not gone to sleep. This - * is ok since device_shutdown() does not free the softc, otherwise - * we'd have to be sure all threads were evicted before returning. + * Disable any entry to the idle function. + */ + disable_idle(device_get_softc(dev)); + + /* + * CPU devices are not truely detached and remain referenced, + * so their resources are not freed. */ - cpu_disable_idle = TRUE; return_VALUE (0); } @@ -923,7 +956,10 @@ acpi_cpu_startup(void *arg) /* Take over idling from cpu_idle_default(). */ cpu_cx_lowest_lim = 0; - cpu_disable_idle = FALSE; + for (i = 0; i < cpu_ndevices; i++) { + sc = device_get_softc(cpu_devices[i]); + enable_idle(sc); + } cpu_idle_hook = acpi_cpu_idle; } @@ -1043,12 +1079,6 @@ acpi_cpu_idle() uint32_t start_time, end_time; int bm_active, cx_next_idx, i; - /* If disabled, return immediately. */ - if (cpu_disable_idle) { - ACPI_ENABLE_IRQS(); - return; - } - /* * Look up our CPU id to get our softc. If it's NULL, we'll use C1 * since there is no ACPI processor object for this CPU. This occurs @@ -1060,6 +1090,12 @@ acpi_cpu_idle() return; } + /* If disabled, take the safe path. */ + if (is_idle_disabled(sc)) { + acpi_cpu_c1(); + return; + } + /* Find the lowest state that has small enough latency. */ cx_next_idx = 0; if (cpu_disable_deep_sleep) @@ -1172,14 +1208,16 @@ acpi_cpu_notify(ACPI_HANDLE h, UINT32 notify, void *context) if (notify != ACPI_NOTIFY_CX_STATES) return; + disable_idle(sc); + ACPI_SERIAL_BEGIN(cpu); /* Update the list of Cx states. */ acpi_cpu_cx_cst(sc); acpi_cpu_cx_list(sc); - - ACPI_SERIAL_BEGIN(cpu); acpi_cpu_set_cx_lowest(sc); + ACPI_SERIAL_END(cpu); + enable_idle(sc); acpi_UserNotify("PROCESSOR", sc->cpu_handle, notify); }