Skip to content

Instantly share code, notes, and snippets.

@bconway
Last active April 14, 2025 20:09
Show Gist options
  • Save bconway/4ce1748461eb28f2b7c68ddb05dba571 to your computer and use it in GitHub Desktop.
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)
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 {
@bconway
Copy link
Author

bconway commented Feb 24, 2023

An uncontrolled GPE storm will be logged as such:

Feb 21 02:06:03 acpitest2 /bsd: uncontrolled GPE storm 7106/s, disabling GPE 6f

@joemiller
Copy link

@bconway I tried this out on 7.2-stable branch and it is working well. thank you

@bconway
Copy link
Author

bconway commented Mar 30, 2023

Verified on OpenBSD 7.3.

@withs
Copy link

withs commented Jan 10, 2024

Applied and tested on 7.4 -stable, it work great thanks.

@bconway
Copy link
Author

bconway commented Jan 10, 2024

Thanks! I'm no longer using this diff, as the one system I had afflicted was a cured with an (unofficial) BIOS update, but I will come back to it in the future if the occasion arises.

@dmffen
Copy link

dmffen commented Aug 21, 2024

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.

@withs
Copy link

withs commented Oct 29, 2024

looks like this is still working on obsd 7.6

@vincele
Copy link

vincele commented Apr 14, 2025

This is confirmed still working, and useful on 7.7-snapshot.

bios0: vendor Techvision, LLC. version "5.19" date 11/08/2022
bios0: Techvision TVI7309X
efi0 at bios0: UEFI 2.7
efi0: American Megatrends rev 0x50013
acpi0 at bios0: ACPI 6.2
[...]
cpu0: Intel(R) Celeron(R) N5100 @ 1.10GHz, 2793.96 MHz, 06-9c-00, patch 24000026

The acpi0 interrupt rate was ~7kps before patching (with a bit of fuzz).
Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment