Last active
October 29, 2024 14:30
-
-
Save bconway/4ce1748461eb28f2b7c68ddb05dba571 to your computer and use it in GitHub Desktop.
Detect and mitigate uncontrolled ACPI GPE interrupt storms on OpenBSD (7.3, 7.3-current)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git sys/dev/acpi/acpi.c sys/dev/acpi/acpi.c | |
index 853bad1ab..26a5c1702 100644 | |
--- sys/dev/acpi/acpi.c | |
+++ sys/dev/acpi/acpi.c | |
@@ -52,6 +52,9 @@ | |
#define APMDEV_NORMAL 0 | |
#define APMDEV_CTL 8 | |
+#define GPE_RATE_MIN_CYCLE 5 /* seconds */ | |
+#define GPE_RATE_MAX 1000 /* per second */ | |
+ | |
#include "wd.h" | |
#ifdef ACPI_DEBUG | |
@@ -98,6 +101,8 @@ void acpi_disable_allgpes(struct acpi_softc *); | |
struct gpe_block *acpi_find_gpe(struct acpi_softc *, int); | |
void acpi_enable_onegpe(struct acpi_softc *, int); | |
int acpi_gpe(struct acpi_softc *, int, void *); | |
+void acpi_init_gpe_rate(struct acpi_softc *, int); | |
+int acpi_gpe_rate(struct acpi_softc *, int); | |
void acpi_enable_rungpes(struct acpi_softc *); | |
@@ -2229,6 +2234,7 @@ acpi_enable_onegpe(struct acpi_softc *sc, int gpe) | |
dnprintf(50, "enabling GPE %.2x (current: %sabled) %.2x\n", | |
gpe, (en & mask) ? "en" : "dis", en); | |
acpi_write_pmreg(sc, ACPIREG_GPE_EN, gpe>>3, en | mask); | |
+ acpi_init_gpe_rate(sc, gpe); | |
} | |
/* Clear all GPEs */ | |
@@ -2307,7 +2313,40 @@ acpi_gpe(struct acpi_softc *sc, int gpe, void *arg) | |
if (sc->gpe_table[gpe].flags & GPE_LEVEL) | |
acpi_write_pmreg(sc, ACPIREG_GPE_STS, gpe>>3, mask); | |
en = acpi_read_pmreg(sc, ACPIREG_GPE_EN, gpe>>3); | |
- acpi_write_pmreg(sc, ACPIREG_GPE_EN, gpe>>3, en | mask); | |
+ /* Re-enable if GPE rate passes, otherwise leave disabled */ | |
+ if (!acpi_gpe_rate(sc, gpe)) | |
+ acpi_write_pmreg(sc, ACPIREG_GPE_EN, gpe>>3, en | mask); | |
+ return (0); | |
+} | |
+ | |
+void | |
+acpi_init_gpe_rate(struct acpi_softc *sc, int gpe) | |
+{ | |
+ sc->gpe_table[gpe].rate_start = getuptime(); | |
+ sc->gpe_table[gpe].rate_count = 0; | |
+} | |
+ | |
+int | |
+acpi_gpe_rate(struct acpi_softc *sc, int gpe) | |
+{ | |
+ struct gpe_block *pgpe = &sc->gpe_table[gpe]; | |
+ time_t cycle; | |
+ | |
+ pgpe->rate_count++; | |
+ dnprintf(10, "rate GPE %.2x start %lld elapsed %lld count %zu\n", gpe, | |
+ pgpe->rate_start, getuptime() - pgpe->rate_start, pgpe->rate_count); | |
+ | |
+ cycle = getuptime() - pgpe->rate_start; | |
+ if (cycle >= GPE_RATE_MIN_CYCLE) { | |
+ if (pgpe->rate_count > (GPE_RATE_MAX * cycle)) { | |
+ printf("uncontrolled GPE storm %lld/s, disabling GPE %.2x\n", | |
+ pgpe->rate_count / cycle, gpe); | |
+ return (1); | |
+ } | |
+ | |
+ /* Reset and start a new cycle */ | |
+ acpi_init_gpe_rate(sc, gpe); | |
+ } | |
return (0); | |
} | |
diff --git sys/dev/acpi/acpivar.h sys/dev/acpi/acpivar.h | |
index a9b4a2ae9..4e2f47053 100644 | |
--- sys/dev/acpi/acpivar.h | |
+++ sys/dev/acpi/acpivar.h | |
@@ -185,6 +185,9 @@ struct gpe_block { | |
void *arg; | |
int active; | |
int flags; | |
+ | |
+ time_t rate_start; | |
+ size_t rate_count; | |
}; | |
struct acpi_devlist { |
looks like this is still working on obsd 7.6
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Works on 7.5-stable. On my (similar?) fanless system, idle CPU temp dropped by 6C by about 15 minutes after rebooting into the modified kernel.