acpi: do not run resume/backout code when entering S0/S5 states

This change adds (possibly redundant) early check for invalid
state input parameter (including S0). Handling of S5 request
is reduced to simply calling shutdown_nice(). As a result
control flow of acpi_EnterSleepState is somewhat simplified
and resume/backout half of the function is not executed
for S5 (soft poweroff) request and invalid state requests.

Note: it seems that shutdown_nice may act as nop when initproc
is already initialized (to grab pid of 1), but init process is in
"pre-natal" state.

Tested by:	Fabian Keil <fk@fabiankeil.de>
Reviewed by:	njl, jkim
Approved by:	rpaulo
This commit is contained in:
Andriy Gapon 2009-04-28 11:56:54 +00:00
parent 7981aa2431
commit 33c70d417e

View File

@ -2482,6 +2482,18 @@ acpi_EnterSleepState(struct acpi_softc *sc, int state)
ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, state);
if (state < ACPI_STATE_S1 || state > ACPI_STATE_S5)
return_ACPI_STATUS (AE_BAD_PARAMETER);
if (state == ACPI_STATE_S5) {
/*
* Shut down cleanly and power off. This will call us back through the
* shutdown handlers.
*/
shutdown_nice(RB_POWEROFF);
return_ACPI_STATUS (AE_OK);
}
/* Re-entry once we're suspending is not allowed. */
status = acpi_sleep_disable(sc);
if (ACPI_FAILURE(status)) {
@ -2502,92 +2514,74 @@ acpi_EnterSleepState(struct acpi_softc *sc, int state)
mtx_lock(&Giant);
slp_state = ACPI_SS_NONE;
switch (state) {
case ACPI_STATE_S1:
case ACPI_STATE_S2:
case ACPI_STATE_S3:
case ACPI_STATE_S4:
status = AcpiGetSleepTypeData(state, &TypeA, &TypeB);
if (status == AE_NOT_FOUND) {
device_printf(sc->acpi_dev,
"Sleep state S%d not supported by BIOS\n", state);
break;
} else if (ACPI_FAILURE(status)) {
device_printf(sc->acpi_dev, "AcpiGetSleepTypeData failed - %s\n",
AcpiFormatException(status));
break;
}
sc->acpi_sstate = state;
/* Enable any GPEs as appropriate and requested by the user. */
acpi_wake_prep_walk(state);
slp_state = ACPI_SS_GPE_SET;
/*
* Inform all devices that we are going to sleep. If at least one
* device fails, DEVICE_SUSPEND() automatically resumes the tree.
*
* XXX Note that a better two-pass approach with a 'veto' pass
* followed by a "real thing" pass would be better, but the current
* bus interface does not provide for this.
*/
if (DEVICE_SUSPEND(root_bus) != 0) {
device_printf(sc->acpi_dev, "device_suspend failed\n");
break;
}
slp_state = ACPI_SS_DEV_SUSPEND;
/* If testing device suspend only, back out of everything here. */
if (acpi_susp_bounce)
break;
status = AcpiEnterSleepStatePrep(state);
if (ACPI_FAILURE(status)) {
device_printf(sc->acpi_dev, "AcpiEnterSleepStatePrep failed - %s\n",
AcpiFormatException(status));
break;
}
slp_state = ACPI_SS_SLP_PREP;
if (sc->acpi_sleep_delay > 0)
DELAY(sc->acpi_sleep_delay * 1000000);
if (state != ACPI_STATE_S1) {
acpi_sleep_machdep(sc, state);
/* Re-enable ACPI hardware on wakeup from sleep state 4. */
if (state == ACPI_STATE_S4)
AcpiEnable();
} else {
ACPI_DISABLE_IRQS();
status = AcpiEnterSleepState(state);
if (ACPI_FAILURE(status)) {
device_printf(sc->acpi_dev, "AcpiEnterSleepState failed - %s\n",
AcpiFormatException(status));
break;
}
}
slp_state = ACPI_SS_SLEPT;
break;
case ACPI_STATE_S5:
/*
* Shut down cleanly and power off. This will call us back through the
* shutdown handlers.
*/
shutdown_nice(RB_POWEROFF);
status = AE_OK;
break;
case ACPI_STATE_S0:
default:
status = AE_BAD_PARAMETER;
break;
status = AcpiGetSleepTypeData(state, &TypeA, &TypeB);
if (status == AE_NOT_FOUND) {
device_printf(sc->acpi_dev,
"Sleep state S%d not supported by BIOS\n", state);
goto backout;
} else if (ACPI_FAILURE(status)) {
device_printf(sc->acpi_dev, "AcpiGetSleepTypeData failed - %s\n",
AcpiFormatException(status));
goto backout;
}
sc->acpi_sstate = state;
/* Enable any GPEs as appropriate and requested by the user. */
acpi_wake_prep_walk(state);
slp_state = ACPI_SS_GPE_SET;
/*
* Inform all devices that we are going to sleep. If at least one
* device fails, DEVICE_SUSPEND() automatically resumes the tree.
*
* XXX Note that a better two-pass approach with a 'veto' pass
* followed by a "real thing" pass would be better, but the current
* bus interface does not provide for this.
*/
if (DEVICE_SUSPEND(root_bus) != 0) {
device_printf(sc->acpi_dev, "device_suspend failed\n");
goto backout;
}
slp_state = ACPI_SS_DEV_SUSPEND;
/* If testing device suspend only, back out of everything here. */
if (acpi_susp_bounce)
goto backout;
status = AcpiEnterSleepStatePrep(state);
if (ACPI_FAILURE(status)) {
device_printf(sc->acpi_dev, "AcpiEnterSleepStatePrep failed - %s\n",
AcpiFormatException(status));
goto backout;
}
slp_state = ACPI_SS_SLP_PREP;
if (sc->acpi_sleep_delay > 0)
DELAY(sc->acpi_sleep_delay * 1000000);
if (state != ACPI_STATE_S1) {
acpi_sleep_machdep(sc, state);
/* Re-enable ACPI hardware on wakeup from sleep state 4. */
if (state == ACPI_STATE_S4)
AcpiEnable();
} else {
ACPI_DISABLE_IRQS();
status = AcpiEnterSleepState(state);
if (ACPI_FAILURE(status)) {
device_printf(sc->acpi_dev, "AcpiEnterSleepState failed - %s\n",
AcpiFormatException(status));
goto backout;
}
}
slp_state = ACPI_SS_SLEPT;
/*
* Back out state according to how far along we got in the suspend
* process. This handles both the error and success cases.
*/
backout:
sc->acpi_next_sstate = 0;
if (slp_state >= ACPI_SS_GPE_SET) {
acpi_wake_prep_walk(state);
@ -2609,8 +2603,7 @@ acpi_EnterSleepState(struct acpi_softc *sc, int state)
#endif
/* Allow another sleep request after a while. */
if (state != ACPI_STATE_S5)
timeout(acpi_sleep_enable, sc, hz * ACPI_MINIMUM_AWAKETIME);
timeout(acpi_sleep_enable, sc, hz * ACPI_MINIMUM_AWAKETIME);
/* Run /etc/rc.resume after we are back. */
if (devctl_process_running())