Module Name: src Committed By: nonaka Date: Sun Jan 8 07:42:00 UTC 2017
Modified Files: src/sys/dev/pci: if_iwm.c if_iwmvar.h Log Message: iwm(4): make interrupt routine running on softint context. see http://mail-index.netbsd.org/tech-kern/2016/12/06/msg021281.html To generate a diff of this commit: cvs rdiff -u -r1.49 -r1.50 src/sys/dev/pci/if_iwm.c cvs rdiff -u -r1.10 -r1.11 src/sys/dev/pci/if_iwmvar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/pci/if_iwm.c diff -u src/sys/dev/pci/if_iwm.c:1.49 src/sys/dev/pci/if_iwm.c:1.50 --- src/sys/dev/pci/if_iwm.c:1.49 Sun Jan 8 06:49:10 2017 +++ src/sys/dev/pci/if_iwm.c Sun Jan 8 07:42:00 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: if_iwm.c,v 1.49 2017/01/08 06:49:10 nonaka Exp $ */ +/* $NetBSD: if_iwm.c,v 1.50 2017/01/08 07:42:00 nonaka Exp $ */ /* OpenBSD: if_iwm.c,v 1.147 2016/11/17 14:12:33 stsp Exp */ #define IEEE80211_NO_HT /* @@ -107,7 +107,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_iwm.c,v 1.49 2017/01/08 06:49:10 nonaka Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_iwm.c,v 1.50 2017/01/08 07:42:00 nonaka Exp $"); #include <sys/param.h> #include <sys/conf.h> @@ -455,7 +455,7 @@ static int iwm_setrates(struct iwm_node static int iwm_media_change(struct ifnet *); static void iwm_newstate_cb(struct work *, void *); static int iwm_newstate(struct ieee80211com *, enum ieee80211_state, int); -static void iwm_endscan_cb(struct work *, void *); +static void iwm_endscan(struct iwm_softc *); static void iwm_fill_sf_command(struct iwm_softc *, struct iwm_sf_cfg_cmd *, struct ieee80211_node *); static int iwm_sf_config(struct iwm_softc *, int); @@ -474,6 +474,7 @@ static void iwm_nic_error(struct iwm_sof static void iwm_nic_umac_error(struct iwm_softc *); #endif static void iwm_notif_intr(struct iwm_softc *); +static void iwm_softintr(void *); static int iwm_intr(void *); static int iwm_preinit(struct iwm_softc *); static void iwm_attach_hook(device_t); @@ -3472,6 +3473,7 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, str uint32_t len; uint32_t rx_pkt_status; int rssi; + int s; bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE, BUS_DMASYNC_POSTREAD); @@ -3519,6 +3521,8 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, str if (le32toh(phy_info->channel) < __arraycount(ic->ic_channels)) c = &ic->ic_channels[le32toh(phy_info->channel)]; + s = splnet(); + ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); if (c) ni->ni_chan = c; @@ -3568,6 +3572,8 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, str } ieee80211_input(ic, m, ni, rssi, device_timestamp); ieee80211_free_node(ni); + + splx(s); } static void @@ -3639,12 +3645,7 @@ iwm_rx_tx_cmd(struct iwm_softc *sc, stru sc->qfullmsk &= ~(1 << ring->qid); if (sc->qfullmsk == 0 && (ifp->if_flags & IFF_OACTIVE)) { ifp->if_flags &= ~IFF_OACTIVE; - /* - * Well, we're in interrupt context, but then again - * I guess net80211 does all sorts of stunts in - * interrupt context, so maybe this is no biggie. - */ - if_schedule_deferred_start(ifp); + if_start_lock(ifp); } } } @@ -5892,9 +5893,8 @@ iwm_newstate(struct ieee80211com *ic, en } static void -iwm_endscan_cb(struct work *work __unused, void *arg) +iwm_endscan(struct iwm_softc *sc) { - struct iwm_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; DPRINTF(("scan ended\n")); @@ -6311,6 +6311,7 @@ iwm_start(struct ifnet *ifp) IF_DEQUEUE(&ic->ic_mgtq, m); if (m) { ni = M_GETCTX(m, struct ieee80211_node *); + M_CLEARCTX(m); ac = WME_AC_BE; goto sendit; } @@ -6319,8 +6320,9 @@ iwm_start(struct ifnet *ifp) } IFQ_DEQUEUE(&ifp->if_snd, m); - if (!m) + if (m == NULL) break; + if (m->m_len < sizeof (*eh) && (m = m_pullup(m, sizeof (*eh))) == NULL) { ifp->if_oerrors++; @@ -6334,6 +6336,7 @@ iwm_start(struct ifnet *ifp) ifp->if_oerrors++; continue; } + /* classify mbuf so we can find which tx ring to use */ if (ieee80211_classify(ic, m, ni) != 0) { m_freem(m); @@ -6943,21 +6946,21 @@ iwm_notif_intr(struct iwm_softc *sc) case IWM_SCAN_ITERATION_COMPLETE: { struct iwm_lmac_scan_complete_notif *notif; SYNC_RESP_STRUCT(notif, pkt); - workqueue_enqueue(sc->sc_eswq, &sc->sc_eswk, NULL); + iwm_endscan(sc); break; } case IWM_SCAN_COMPLETE_UMAC: { struct iwm_umac_scan_complete *notif; SYNC_RESP_STRUCT(notif, pkt); - workqueue_enqueue(sc->sc_eswq, &sc->sc_eswk, NULL); + iwm_endscan(sc); break; } case IWM_SCAN_ITERATION_COMPLETE_UMAC: { struct iwm_umac_scan_iter_complete_notif *notif; SYNC_RESP_STRUCT(notif, pkt); - workqueue_enqueue(sc->sc_eswq, &sc->sc_eswk, NULL); + iwm_endscan(sc); break; } @@ -7017,64 +7020,17 @@ iwm_notif_intr(struct iwm_softc *sc) IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, hw & ~7); } -static int -iwm_intr(void *arg) +static void +iwm_softintr(void *arg) { struct iwm_softc *sc = arg; struct ifnet *ifp = IC2IFP(&sc->sc_ic); - int handled = 0; - int r1, r2, rv = 0; + uint32_t r1; int isperiodic = 0; - IWM_WRITE(sc, IWM_CSR_INT_MASK, 0); - - if (sc->sc_flags & IWM_FLAG_USE_ICT) { - uint32_t *ict = sc->ict_dma.vaddr; - int tmp; - - bus_dmamap_sync(sc->sc_dmat, sc->ict_dma.map, - 0, sc->ict_dma.size, BUS_DMASYNC_POSTREAD); - tmp = htole32(ict[sc->ict_cur]); - if (!tmp) - goto out_ena; - - /* - * ok, there was something. keep plowing until we have all. - */ - r1 = r2 = 0; - while (tmp) { - r1 |= tmp; - ict[sc->ict_cur] = 0; /* Acknowledge. */ - bus_dmamap_sync(sc->sc_dmat, sc->ict_dma.map, - &ict[sc->ict_cur] - ict, sizeof(*ict), - BUS_DMASYNC_PREWRITE); - sc->ict_cur = (sc->ict_cur + 1) % IWM_ICT_COUNT; - tmp = htole32(ict[sc->ict_cur]); - } - - /* this is where the fun begins. don't ask */ - if (r1 == 0xffffffff) - r1 = 0; - - /* i am not expected to understand this */ - if (r1 & 0xc0000) - r1 |= 0x8000; - r1 = (0xff & r1) | ((0xff00 & r1) << 16); - } else { - r1 = IWM_READ(sc, IWM_CSR_INT); - if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) - goto out; - r2 = IWM_READ(sc, IWM_CSR_FH_INT_STATUS); - } - if (r1 == 0 && r2 == 0) { - goto out_ena; - } - - IWM_WRITE(sc, IWM_CSR_INT, r1 | ~sc->sc_intmask); - - /* ignored */ - handled |= (r1 & (IWM_CSR_INT_BIT_ALIVE /*| IWM_CSR_INT_BIT_SCD*/)); + r1 = atomic_swap_32(&sc->sc_soft_flags, 0); + restart: if (r1 & IWM_CSR_INT_BIT_SW_ERR) { #ifdef IWM_DEBUG int i; @@ -7095,33 +7051,28 @@ iwm_intr(void *arg) #endif aprint_error_dev(sc->sc_dev, "fatal firmware error\n"); + fatal: ifp->if_flags &= ~IFF_UP; iwm_stop(ifp, 1); - rv = 1; - goto out; + /* Don't restore interrupt mask */ + return; } if (r1 & IWM_CSR_INT_BIT_HW_ERR) { - handled |= IWM_CSR_INT_BIT_HW_ERR; aprint_error_dev(sc->sc_dev, "hardware error, stopping device\n"); - ifp->if_flags &= ~IFF_UP; - iwm_stop(ifp, 1); - rv = 1; - goto out; + goto fatal; } /* firmware chunk loaded */ if (r1 & IWM_CSR_INT_BIT_FH_TX) { IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_TX_MASK); - handled |= IWM_CSR_INT_BIT_FH_TX; sc->sc_fw_chunk_done = 1; wakeup(&sc->sc_fw); } if (r1 & IWM_CSR_INT_BIT_RF_KILL) { - handled |= IWM_CSR_INT_BIT_RF_KILL; if (iwm_check_rfkill(sc) && (ifp->if_flags & IFF_UP)) { ifp->if_flags &= ~IFF_UP; iwm_stop(ifp, 1); @@ -7129,7 +7080,6 @@ iwm_intr(void *arg) } 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, @@ -7139,7 +7089,6 @@ iwm_intr(void *arg) 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); iwm_notif_intr(sc); @@ -7151,12 +7100,73 @@ iwm_intr(void *arg) IWM_CSR_INT_PERIODIC_ENA); } - rv = 1; + r1 = atomic_swap_32(&sc->sc_soft_flags, 0); + if (r1 != 0) + goto restart; + + iwm_restore_interrupts(sc); +} + +static int +iwm_intr(void *arg) +{ + struct iwm_softc *sc = arg; + int r1, r2; + + IWM_WRITE(sc, IWM_CSR_INT_MASK, 0); + + if (sc->sc_flags & IWM_FLAG_USE_ICT) { + uint32_t *ict = sc->ict_dma.vaddr; + int tmp; + + bus_dmamap_sync(sc->sc_dmat, sc->ict_dma.map, + 0, sc->ict_dma.size, BUS_DMASYNC_POSTREAD); + tmp = htole32(ict[sc->ict_cur]); + if (!tmp) + goto out_ena; + + /* + * ok, there was something. keep plowing until we have all. + */ + r1 = r2 = 0; + while (tmp) { + r1 |= tmp; + ict[sc->ict_cur] = 0; /* Acknowledge. */ + bus_dmamap_sync(sc->sc_dmat, sc->ict_dma.map, + &ict[sc->ict_cur] - ict, sizeof(*ict), + BUS_DMASYNC_PREWRITE); + sc->ict_cur = (sc->ict_cur + 1) % IWM_ICT_COUNT; + tmp = htole32(ict[sc->ict_cur]); + } + + /* this is where the fun begins. don't ask */ + if (r1 == 0xffffffff) + r1 = 0; + + /* i am not expected to understand this */ + if (r1 & 0xc0000) + r1 |= 0x8000; + r1 = (0xff & r1) | ((0xff00 & r1) << 16); + } else { + r1 = IWM_READ(sc, IWM_CSR_INT); + if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) + goto out; + r2 = IWM_READ(sc, IWM_CSR_FH_INT_STATUS); + } + if (r1 == 0 && r2 == 0) { + goto out_ena; + } + + IWM_WRITE(sc, IWM_CSR_INT, r1 | ~sc->sc_intmask); + + atomic_or_32(&sc->sc_soft_flags, r1); + softint_schedule(sc->sc_soft_ih); + return 1; out_ena: iwm_restore_interrupts(sc); out: - return rv; + return 0; } /* @@ -7273,14 +7283,13 @@ iwm_attach(device_t parent, device_t sel pci_aprint_devinfo(pa, NULL); - if (workqueue_create(&sc->sc_eswq, "iwmes", - iwm_endscan_cb, sc, PRI_NONE, IPL_NET, 0)) - panic("%s: could not create workqueue: scan", - device_xname(self)); if (workqueue_create(&sc->sc_nswq, "iwmns", iwm_newstate_cb, sc, PRI_NONE, IPL_NET, 0)) panic("%s: could not create workqueue: newstate", device_xname(self)); + sc->sc_soft_ih = softint_establish(SOFTINT_NET, iwm_softintr, sc); + if (sc->sc_soft_ih == NULL) + panic("%s: could not establish softint", device_xname(self)); /* * Get the offset of the PCI Express Capability Structure in PCI Index: src/sys/dev/pci/if_iwmvar.h diff -u src/sys/dev/pci/if_iwmvar.h:1.10 src/sys/dev/pci/if_iwmvar.h:1.11 --- src/sys/dev/pci/if_iwmvar.h:1.10 Sun Dec 18 02:18:29 2016 +++ src/sys/dev/pci/if_iwmvar.h Sun Jan 8 07:42:00 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: if_iwmvar.h,v 1.10 2016/12/18 02:18:29 nonaka Exp $ */ +/* $NetBSD: if_iwmvar.h,v 1.11 2017/01/08 07:42:00 nonaka Exp $ */ /* OpenBSD: if_iwmvar.h,v 1.24 2016/09/21 13:53:18 stsp Exp */ /* @@ -372,6 +372,8 @@ struct iwm_softc { pcitag_t sc_pcitag; pcireg_t sc_pciid; const void *sc_ih; + void *sc_soft_ih; + uint32_t sc_soft_flags; /* TX scheduler rings. */ struct iwm_dma_info sched_dma; @@ -455,8 +457,7 @@ struct iwm_softc { uint8_t sc_cmd_resp[IWM_CMD_RESP_MAX]; int sc_wantresp; - struct workqueue *sc_nswq, *sc_eswq; - struct work sc_eswk; + struct workqueue *sc_nswq; struct iwm_rx_phy_info sc_last_phy_info; int sc_ampdu_ref; @@ -471,7 +472,7 @@ struct iwm_softc { struct sysctllog *sc_clog; - struct bpf_if * sc_drvbpf; + struct bpf_if *sc_drvbpf; union { struct iwm_rx_radiotap_header th;