Page MenuHomeFreeBSD

D54410.id168743.diff
No OneTemporary

D54410.id168743.diff

diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c
--- a/sys/dev/acpica/acpi.c
+++ b/sys/dev/acpica/acpi.c
@@ -53,6 +53,7 @@
#include <sys/sbuf.h>
#include <sys/sched.h>
#include <sys/smp.h>
+#include <sys/taskqueue.h>
#include <sys/timetc.h>
#include <sys/uuid.h>
@@ -61,6 +62,7 @@
#include <machine/intr_machdep.h>
#include <machine/pci_cfgreg.h>
#include <x86/cputypes.h>
+#include <x86/x86_smp.h>
#include <x86/x86_var.h>
#endif
#include <machine/resource.h>
@@ -3565,6 +3567,10 @@
do_idle(struct acpi_softc *sc, enum acpi_sleep_state *slp_state,
register_t rflags)
{
+ struct pcpu *pc;
+ cpuset_t other_cpus;
+ struct monitorbuf *mb;
+ size_t intr_count;
intr_suspend();
@@ -3572,7 +3578,7 @@
* The CPU will exit idle when interrupted, so we want to minimize the
* number of interrupts it can receive while idle. We do this by only
* allowing SCI (system control interrupt) interrupts, which are used by
- * the ACPI firmware to send wake GPEs to the OS.
+ * the ACPI firmware to send wake GPEs to OSPM.
*
* XXX We might still receive other spurious non-wake GPEs from noisy
* devices that can't be disabled, so this will need to end up being a
@@ -3582,8 +3588,72 @@
*/
intr_enable_src(AcpiGbl_FADT.SciInterrupt);
- cpu_idle(0);
+ sc->acpi_s2idle_wake = false;
+ sc->acpi_s2idle_looping = true;
+ /*
+ * Put all CPUs except for this one in their idle loops by sending them an
+ * idle IPI.
+ */
+ other_cpus = all_cpus;
+ CPU_CLR(curcpu, &other_cpus);
+ ipi_selected(other_cpus, IPI_IDLE);
+
+ /*
+ * Suspend-to-idle loop.
+ *
+ * This is needed, because we might still receive other spurious non-wake
+ * GPEs (and, thus, SCI interrupts) from devices that can't be disabled.
+ * When breaking out of idle, we check the reason for the wakeup and
+ * immediately idle the CPU again if it was not a proper wake event.
+ */
+ for (intr_count = 0; !sc->acpi_s2idle_wake; intr_count++) {
+ /*
+ * Actually idle the main CPU. The only thing that will break us out of
+ * this at this point is an SCI interrupt.
+ */
+ cpu_idle(0);
+
+ /*
+ * When we get an SCI from the platform, we don't actually handle the
+ * GPE in the interrupt handler. Instead, it gets added to the
+ * acpi_taskq taskqueue. Since we are interested in what this GPE is
+ * (since it might we a wake event), we must wait until it has been
+ * drained so we know if we should break out of the s2idle loop or if
+ * we should reenter idle.
+ *
+ * XXX This is buggy because we're breaking out to the scheduler, and
+ * other kernel threads might be scheduled and prevent us from getting
+ * scheduled again in a timely manner (or, theoretically, at all).
+ */
+ extern struct taskqueue *acpi_taskq;
+ taskqueue_quiesce(acpi_taskq);
+
+ /*
+ * Check that all CPUs are still idled, and warn if not.
+ */
+ STAILQ_FOREACH(pc, &cpuhead, pc_allcpu) {
+ if (!CPU_ISSET(pc->pc_cpuid, &other_cpus))
+ continue;
+ mb = &pc->pc_monitorbuf;
+ if (atomic_load_int(&mb->idle_state) != 0x2 /* TODO STATE_SLEEPING */)
+ printf("CPU%d was not sleeping after breaking out of CPU0 "
+ "cpu_idle()!\n", pc->pc_cpuid);
+ }
+ }
+ printf("Suspend-to-idle interrupted %zu times.\n", intr_count);
+
+ /*
+ * Kick all other CPUs by sending them a preempt IPI. This will resume any
+ * kernel threads that had to wait while we were idling.
+ */
+ ipi_selected(other_cpus, IPI_UNIDLE);
+
+ sc->acpi_s2idle_looping = false;
+
+ /*
+ * Resume interrupts.
+ */
intr_resume(false);
intr_restore(rflags);
*slp_state |= ACPI_SS_SLEPT;
@@ -4131,10 +4201,11 @@
static void
acpi_system_eventhandler_wakeup(void *arg, enum power_stype stype)
{
+ struct acpi_softc *sc = (struct acpi_softc *)arg;
ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, stype);
- /* Currently, nothing to do for wakeup. */
+ sc->acpi_s2idle_wake = true;
return_VOID;
}
@@ -4167,6 +4238,8 @@
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ sc->acpi_s2idle_wake = true;
+
#if defined(__amd64__) || defined(__i386__)
if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER,
acpi_invoke_sleep_eventhandler, &sc->acpi_power_button_stype)))
@@ -4185,6 +4258,8 @@
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ sc->acpi_s2idle_wake = true;
+
if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER,
acpi_invoke_wake_eventhandler, &sc->acpi_power_button_stype)))
return_VALUE (ACPI_INTERRUPT_NOT_HANDLED);
@@ -4198,6 +4273,8 @@
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ sc->acpi_s2idle_wake = true;
+
if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER,
acpi_invoke_sleep_eventhandler, &sc->acpi_sleep_button_stype)))
return_VALUE (ACPI_INTERRUPT_NOT_HANDLED);
@@ -4211,6 +4288,8 @@
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ sc->acpi_s2idle_wake = true;
+
if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER,
acpi_invoke_wake_eventhandler, &sc->acpi_sleep_button_stype)))
return_VALUE (ACPI_INTERRUPT_NOT_HANDLED);
diff --git a/sys/dev/acpica/acpivar.h b/sys/dev/acpica/acpivar.h
--- a/sys/dev/acpica/acpivar.h
+++ b/sys/dev/acpica/acpivar.h
@@ -56,6 +56,8 @@
int acpi_enabled;
enum power_stype acpi_stype;
int acpi_sleep_disabled;
+ bool acpi_s2idle_looping;
+ bool acpi_s2idle_wake;
struct sysctl_ctx_list acpi_sysctl_ctx;
struct sysctl_oid *acpi_sysctl_tree;

File Metadata

Mime Type
text/plain
Expires
Tue, Jun 16, 4:18 AM (1 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33981831
Default Alt Text
D54410.id168743.diff (5 KB)

Event Timeline