> Date: Wed, 2 Dec 2015 13:11:51 +0100
> From: Jonathan Matthew <[email protected]>
>
> 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;
> }
>
>