Skip to content

Instantly share code, notes, and snippets.

@qfen
Forked from bconway/acpi.diff
Created August 21, 2024 15:27
Show Gist options
  • Save qfen/dd2d3519722f10823223d57b9eaecee3 to your computer and use it in GitHub Desktop.
Save qfen/dd2d3519722f10823223d57b9eaecee3 to your computer and use it in GitHub Desktop.
Detect and mitigate uncontrolled ACPI GPE interrupt storms on OpenBSD (7.3, 7.3-current, 7.5-stable)
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 {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment