> Date: Wed, 2 Dec 2015 13:11:51 +0100 > From: Jonathan Matthew <jonat...@d14n.org> > > I'm not sure anyone uses cas(4) but I have one.
I have a couple, but I don't use them a lot. > I actually started working on this to fix the tx path (it got stuck > in OACTIVE, didn't use m_defrag) but once I did that, making it > mpsafe was pretty easy so I did that too. > > Tested with: > cas0 at pci13 dev 2 function 0 "NS Saturn" rev 0x30: ivec 0x7ce, address > 00:03:ba:da:0d:f8 > > on a sun v245, where it's faster than the onboard bge. Wow. That's somewhat unexpected. > ok? I think we can treat cas(4) the same way as myx(4). Commit it and we'll fix any remaining bugs afterwards, > Index: if_cas.c > =================================================================== > RCS file: /cvs/src/sys/dev/pci/if_cas.c,v > retrieving revision 1.46 > diff -u -p -u -p -r1.46 if_cas.c > --- if_cas.c 25 Nov 2015 03:09:59 -0000 1.46 > +++ if_cas.c 2 Dec 2015 11:53:29 -0000 > @@ -56,6 +56,7 @@ > #include <sys/errno.h> > #include <sys/device.h> > #include <sys/endian.h> > +#include <sys/atomic.h> > > #include <net/if.h> > #include <net/if_media.h> > @@ -120,7 +121,7 @@ int cas_disable_tx(struct cas_softc *); > void cas_rxdrain(struct cas_softc *); > int cas_add_rxbuf(struct cas_softc *, int idx); > void cas_iff(struct cas_softc *); > -int cas_encap(struct cas_softc *, struct mbuf *, u_int32_t *); > +int cas_encap(struct cas_softc *, struct mbuf *, int *); > > /* MII methods & callbacks */ > int cas_mii_readreg(struct device *, int, int); > @@ -346,7 +347,7 @@ cas_attach(struct device *parent, struct > } > intrstr = pci_intr_string(pa->pa_pc, ih); > sc->sc_ih = pci_intr_establish(pa->pa_pc, > - ih, IPL_NET, cas_intr, sc, self->dv_xname); > + ih, IPL_NET | IPL_MPSAFE, cas_intr, sc, self->dv_xname); > if (sc->sc_ih == NULL) { > printf(": couldn't establish interrupt"); > if (intrstr != NULL) > @@ -728,6 +729,9 @@ cas_stop(struct ifnet *ifp, int disable) > cas_reset_rx(sc); > cas_reset_tx(sc); > > + intr_barrier(sc->sc_ih); > + KASSERT((ifp->if_flags & IFF_RUNNING) == 0); > + > /* > * Release any queued transmit buffers. > */ > @@ -1347,8 +1351,11 @@ cas_intr(void *v) > printf("%s: MAC tx fault, status %x\n", > sc->sc_dev.dv_xname, txstat); > #endif > - if (txstat & (CAS_MAC_TX_UNDERRUN | CAS_MAC_TX_PKT_TOO_LONG)) > + if (txstat & (CAS_MAC_TX_UNDERRUN | CAS_MAC_TX_PKT_TOO_LONG)) { > + KERNEL_LOCK(); > cas_init(ifp); > + KERNEL_UNLOCK(); > + } > } > if (status & CAS_INTR_RX_MAC) { > int rxstat = bus_space_read_4(t, seb, CAS_MAC_RX_STATUS); > @@ -1362,8 +1369,10 @@ cas_intr(void *v) > * due to a silicon bug so handle them silently. > */ > if (rxstat & CAS_MAC_RX_OVERFLOW) { > + KERNEL_LOCK(); > ifp->if_ierrors++; > cas_init(ifp); > + KERNEL_UNLOCK(); > } > #ifdef CAS_DEBUG > else if (rxstat & ~(CAS_MAC_RX_DONE | CAS_MAC_RX_FRAME_CNT)) > @@ -1762,28 +1771,33 @@ cas_iff(struct cas_softc *sc) > } > > int > -cas_encap(struct cas_softc *sc, struct mbuf *mhead, u_int32_t *bixp) > +cas_encap(struct cas_softc *sc, struct mbuf *m, int *used) > { > u_int64_t flags; > - u_int32_t cur, frag, i; > + u_int32_t first, cur, frag, i; > bus_dmamap_t map; > > - cur = frag = *bixp; > + cur = frag = (sc->sc_tx_prod + *used) % CAS_NTXDESC; > map = sc->sc_txd[cur].sd_map; > > - if (bus_dmamap_load_mbuf(sc->sc_dmatag, map, mhead, > + switch (bus_dmamap_load_mbuf(sc->sc_dmatag, map, m, > BUS_DMA_NOWAIT) != 0) { > - return (ENOBUFS); > - } > - > - if ((sc->sc_tx_cnt + map->dm_nsegs) > (CAS_NTXDESC - 2)) { > - bus_dmamap_unload(sc->sc_dmatag, map); > + case 0: > + break; > + case EFBIG: > + if (m_defrag(m, M_DONTWAIT) == 0 && > + bus_dmamap_load_mbuf(sc->sc_dmatag, map, m, > + BUS_DMA_NOWAIT) == 0) > + break; > + /* FALLTHROUGH */ > + default: > return (ENOBUFS); > } > > bus_dmamap_sync(sc->sc_dmatag, map, 0, map->dm_mapsize, > BUS_DMASYNC_PREWRITE); > > + first = cur; > for (i = 0; i < map->dm_nsegs; i++) { > sc->sc_txdescs[frag].cd_addr = > CAS_DMA_WRITE(map->dm_segs[i].ds_addr); > @@ -1799,14 +1813,13 @@ cas_encap(struct cas_softc *sc, struct m > frag = 0; > } > > - sc->sc_tx_cnt += map->dm_nsegs; > - sc->sc_txd[*bixp].sd_map = sc->sc_txd[cur].sd_map; > + sc->sc_txd[first].sd_map = sc->sc_txd[cur].sd_map; > sc->sc_txd[cur].sd_map = map; > - sc->sc_txd[cur].sd_mbuf = mhead; > + sc->sc_txd[cur].sd_mbuf = m; > > bus_space_write_4(sc->sc_memt, sc->sc_memh, CAS_TX_KICK, frag); > > - *bixp = frag; > + *used += map->dm_nsegs; > > /* sync descriptors */ > > @@ -1822,9 +1835,11 @@ cas_tint(struct cas_softc *sc, u_int32_t > struct ifnet *ifp = &sc->sc_arpcom.ac_if; > struct cas_sxd *sd; > u_int32_t cons, comp; > + int freed, used; > > comp = bus_space_read_4(sc->sc_memt, sc->sc_memh, CAS_TX_COMPLETION); > cons = sc->sc_tx_cons; > + freed = 0; > while (cons != comp) { > sd = &sc->sc_txd[cons]; > if (sd->sd_mbuf != NULL) { > @@ -1835,18 +1850,23 @@ cas_tint(struct cas_softc *sc, u_int32_t > sd->sd_mbuf = NULL; > ifp->if_opackets++; > } > - sc->sc_tx_cnt--; > + freed++; > if (++cons == CAS_NTXDESC) > cons = 0; > } > sc->sc_tx_cons = cons; > > - if (sc->sc_tx_cnt < CAS_NTXDESC - 2) > + used = atomic_sub_int_nv(&sc->sc_tx_cnt, freed); > + if (used < CAS_NTXDESC - 2) > ifq_clr_oactive(&ifp->if_snd); > - if (sc->sc_tx_cnt == 0) > + if (used == 0) > ifp->if_timer = 0; > > - cas_start(ifp); > + if (!IFQ_IS_EMPTY(&ifp->if_snd)) { > + KERNEL_LOCK(); > + cas_start(ifp); > + KERNEL_UNLOCK(); > + } > > return (1); > } > @@ -1855,40 +1875,37 @@ void > cas_start(struct ifnet *ifp) > { > struct cas_softc *sc = ifp->if_softc; > - struct mbuf *m; > - u_int32_t bix; > + struct mbuf *m = NULL; > + int used; > > if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd)) > return; > > - bix = sc->sc_tx_prod; > - while (sc->sc_txd[bix].sd_mbuf == NULL) { > - m = ifq_deq_begin(&ifp->if_snd); > + used = 0; > + while (1) { > + if ((sc->sc_tx_cnt + used + CAS_NTXSEGS) >= (CAS_NTXDESC - 2)) { > + ifq_set_oactive(&ifp->if_snd); > + break; > + } > + > + IFQ_DEQUEUE(&ifp->if_snd, m); > if (m == NULL) > break; > > + if (cas_encap(sc, m, &used)) { > + m_freem(m); > + continue; > + } > + > #if NBPFILTER > 0 > - /* > - * If BPF is listening on this interface, let it see the > - * packet before we commit it to the wire. > - */ > if (ifp->if_bpf) > bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); > #endif > + } > > - /* > - * Encapsulate this packet and start it going... > - * or fail... > - */ > - if (cas_encap(sc, m, &bix)) { > - ifq_deq_rollback(&ifp->if_snd, m); > - ifq_set_oactive(&ifp->if_snd); > - break; > - } > - > - ifq_deq_commit(&ifp->if_snd, m); > + if (used != 0) { > ifp->if_timer = 5; > + sc->sc_tx_prod = (sc->sc_tx_prod + used) % CAS_NTXDESC; > + atomic_add_int(&sc->sc_tx_cnt, used); > } > - > - sc->sc_tx_prod = bix; > } > >