Page MenuHomeFreeBSD

D54410.diff
No OneTemporary

D54410.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>
@@ -3522,10 +3524,26 @@
}
#if defined(__i386__) || defined(__amd64__)
+static void
+set_cpu_idle(void *data)
+{
+ /* XXX sched_do_idle is only implemented in ULE at the moment. */
+#if defined(SCHED_ULE)
+ bool idle = *(bool*) data;
+
+ sched_do_idle(curthread, idle);
+#endif
+}
+
static void
do_idle(struct acpi_softc *sc, enum acpi_sleep_state *slp_state,
register_t rflags)
{
+ struct pcpu *pc;
+ cpuset_t other_cpus;
+ bool idle;
+ struct monitorbuf *mb;
+ size_t intr_count;
intr_suspend();
@@ -3533,7 +3551,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
@@ -3543,8 +3561,65 @@
*/
intr_enable_src(AcpiGbl_FADT.SciInterrupt);
- cpu_idle(0);
+ sc->acpi_s2idle_wake = false;
+
+ /*
+ * Put all CPUs (except for this one, the BSP) in their idle loops. They
+ * will immediately be preempted in sched_do_idle().
+ */
+ other_cpus = all_cpus;
+ CPU_CLR(curcpu, &other_cpus);
+
+ idle = true;
+ smp_rendezvous_cpus(other_cpus, NULL, set_cpu_idle, NULL, &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 APs are still idled, and warn if not. */
+ STAILQ_FOREACH(pc, &cpuhead, pc_allcpu) {
+ if (pc_cpuid == curcpu)
+ 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 BSP "
+ "cpu_idle()!\n", pc->pc_cpuid);
+ }
+ }
+ printf("Suspend-to-idle interrupted %zu times.\n", intr_count);
+
+ /* Unidle all other CPUs. The schedulers will immediately be preempted. */
+ idle = false;
+ smp_rendezvous_cpus(other_cpus, NULL, set_cpu_idle, NULL, &idle);
+
+ /* Resume interrupts. */
intr_resume(false);
intr_restore(rflags);
*slp_state |= ACPI_SS_SLEPT;
@@ -4070,10 +4145,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;
}
@@ -4106,6 +4182,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)))
@@ -4124,6 +4202,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);
@@ -4137,6 +4217,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);
@@ -4150,6 +4232,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,7 @@
int acpi_enabled;
enum power_stype acpi_stype;
int acpi_sleep_disabled;
+ bool acpi_s2idle_wake;
struct sysctl_ctx_list acpi_sysctl_ctx;
struct sysctl_oid *acpi_sysctl_tree;

File Metadata

Mime Type
text/plain
Expires
Mon, Mar 30, 12:46 PM (12 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30587291
Default Alt Text
D54410.diff (5 KB)

Event Timeline