summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortobhe <tobhe@openbsd.org>2020-02-26 14:29:52 +0000
committertobhe <tobhe@openbsd.org>2020-02-26 14:29:52 +0000
commit545c6a2724f86309080e0fba02fe34aeb85cb291 (patch)
tree7ca3ce7de7b09630d5599fc5afd7d2698f4eed05
parentRemove non-__STDC__ assert macros from <lib/libkern/libkern.h>. (diff)
downloadwireguard-openbsd-545c6a2724f86309080e0fba02fe34aeb85cb291.tar.xz
wireguard-openbsd-545c6a2724f86309080e0fba02fe34aeb85cb291.zip
Rx interrupt handling can lead to a race where the interrupt is handled
before the shared data was updated. As a workaround each "real" interrupt enables the "periodic" Rx interrupt which triggers after 8 ms and detects any dangling Rx activity. If dangling Rx activity was detected the periodic interrupt is restarted, if not it stays disabled until the next "real" Rx interrupt is handled. The periodic interrupt is not supposed to write IWM_CSR_FH_INT_STATUS unless it has detected dangling Rx activity. ok stsp@ patrick@
-rw-r--r--sys/dev/pci/if_iwm.c42
1 files changed, 23 insertions, 19 deletions
diff --git a/sys/dev/pci/if_iwm.c b/sys/dev/pci/if_iwm.c
index 2a276915cfe..51ceb40d6f6 100644
--- a/sys/dev/pci/if_iwm.c
+++ b/sys/dev/pci/if_iwm.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwm.c,v 1.295 2020/02/12 16:02:51 stsp Exp $ */
+/* $OpenBSD: if_iwm.c,v 1.296 2020/02/26 14:29:52 tobhe Exp $ */
/*
* Copyright (c) 2014, 2016 genua gmbh <info@genua.de>
@@ -8615,7 +8615,6 @@ iwm_intr(void *arg)
struct iwm_softc *sc = arg;
int handled = 0;
int rv = 0;
- int isperiodic = 0;
uint32_t r1, r2;
IWM_WRITE(sc, IWM_CSR_INT_MASK, 0);
@@ -8722,27 +8721,32 @@ iwm_intr(void *arg)
wakeup(&sc->sc_fw);
}
- if (r1 & IWM_CSR_INT_BIT_RX_PERIODIC) {
- handled |= IWM_CSR_INT_BIT_RX_PERIODIC;
- IWM_WRITE(sc, IWM_CSR_INT, IWM_CSR_INT_BIT_RX_PERIODIC);
- if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) == 0)
- IWM_WRITE_1(sc,
- IWM_CSR_INT_PERIODIC_REG, IWM_CSR_INT_PERIODIC_DIS);
- isperiodic = 1;
- }
-
- if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) ||
- isperiodic) {
- handled |= (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX);
- IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_RX_MASK);
+ if (r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX |
+ IWM_CSR_INT_BIT_RX_PERIODIC)) {
+ if (r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) {
+ handled |= (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX);
+ IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_RX_MASK);
+ }
+ if (r1 & IWM_CSR_INT_BIT_RX_PERIODIC) {
+ handled |= IWM_CSR_INT_BIT_RX_PERIODIC;
+ IWM_WRITE(sc, IWM_CSR_INT, IWM_CSR_INT_BIT_RX_PERIODIC);
+ }
- iwm_notif_intr(sc);
+ /* Disable periodic interrupt; we use it as just a one-shot. */
+ IWM_WRITE_1(sc, IWM_CSR_INT_PERIODIC_REG, IWM_CSR_INT_PERIODIC_DIS);
- /* enable periodic interrupt, see above */
- if (r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX) &&
- !isperiodic)
+ /*
+ * Enable periodic interrupt in 8 msec only if we received
+ * real RX interrupt (instead of just periodic int), to catch
+ * any dangling Rx interrupt. If it was just the periodic
+ * interrupt, there was no dangling Rx activity, and no need
+ * to extend the periodic interrupt; one-shot is enough.
+ */
+ if (r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX))
IWM_WRITE_1(sc, IWM_CSR_INT_PERIODIC_REG,
IWM_CSR_INT_PERIODIC_ENA);
+
+ iwm_notif_intr(sc);
}
rv = 1;