diff options
| author | 2020-02-26 14:29:52 +0000 | |
|---|---|---|
| committer | 2020-02-26 14:29:52 +0000 | |
| commit | 545c6a2724f86309080e0fba02fe34aeb85cb291 (patch) | |
| tree | 7ca3ce7de7b09630d5599fc5afd7d2698f4eed05 | |
| parent | Remove non-__STDC__ assert macros from <lib/libkern/libkern.h>. (diff) | |
| download | wireguard-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.c | 42 |
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; |
