Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F150116870
D54410.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
5 KB
Referenced Files
None
Subscribers
None
D54410.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D54410: acpi: Implement s2idle loop
Attached
Detach File
Event Timeline
Log In to Comment