I'm not sure anyone uses cas(4) but I have one.

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.

ok?

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;
 }

Reply via email to