I'm a bit late to the party, but here's a diff to bring gem(4) to the
group of cool^WIPL_MPSAFE drivers.

sparc and sparc64 are only compile tested, I've been running with this
on my dual G5.

It includes Mark's diff to disable flow control since I'm running with
it and it does help in my lab!

More tests and comments are welcome.

Index: dev/ic/gem.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/gem.c,v
retrieving revision 1.113
diff -u -p -r1.113 gem.c
--- dev/ic/gem.c        11 Sep 2015 13:02:28 -0000      1.113
+++ dev/ic/gem.c        15 Oct 2015 09:37:46 -0000
@@ -96,6 +96,8 @@ void          gem_rx_watchdog(void *);
 void           gem_rxdrain(struct gem_softc *);
 void           gem_fill_rx_ring(struct gem_softc *);
 int            gem_add_rxbuf(struct gem_softc *, int idx);
+int            gem_load_mbuf(struct gem_softc *, struct gem_sxd *,
+                   struct mbuf *);
 void           gem_iff(struct gem_softc *);
 
 /* MII methods & callbacks */
@@ -539,6 +541,10 @@ gem_stop(struct ifnet *ifp, int softonly
                gem_reset_tx(sc);
        }
 
+       intr_barrier(sc->sc_ih);
+
+       KASSERT((ifp->if_flags & IFF_RUNNING) == 0);
+
        /*
         * Release any queued transmit buffers.
         */
@@ -949,6 +955,9 @@ gem_rint(struct gem_softc *sc)
        u_int64_t rxstat;
        int i, len;
 
+       if (if_rxr_inuse(&sc->sc_rx_ring) == 0)
+               return (0);
+
        for (i = sc->sc_rx_cons; if_rxr_inuse(&sc->sc_rx_ring) > 0;
            i = GEM_NEXTRX(i)) {
                rxs = &sc->sc_rxsoft[i];
@@ -1134,8 +1143,11 @@ gem_intr(void *v)
                        printf("%s: MAC tx fault, status %x\n",
                            sc->sc_dev.dv_xname, txstat);
 #endif
-               if (txstat & (GEM_MAC_TX_UNDERRUN | GEM_MAC_TX_PKT_TOO_LONG))
+               if (txstat & (GEM_MAC_TX_UNDERRUN | GEM_MAC_TX_PKT_TOO_LONG)) {
+                       KERNEL_LOCK();
                        gem_init(ifp);
+                       KERNEL_UNLOCK();
+               }
        }
        if (status & GEM_INTR_RX_MAC) {
                int rxstat = bus_space_read_4(t, seb, GEM_MAC_RX_STATUS);
@@ -1617,6 +1629,7 @@ gem_tint(struct gem_softc *sc, u_int32_t
        struct ifnet *ifp = &sc->sc_arpcom.ac_if;
        struct gem_sxd *sd;
        u_int32_t cons, hwcons;
+       u_int32_t used, free = 0;;
 
        hwcons = status >> 19;
        cons = sc->sc_tx_cons;
@@ -1630,76 +1643,95 @@ gem_tint(struct gem_softc *sc, u_int32_t
                        sd->sd_mbuf = NULL;
                        ifp->if_opackets++;
                }
-               sc->sc_tx_cnt--;
+               free++;
                if (++cons == GEM_NTXDESC)
                        cons = 0;
        }
+
        sc->sc_tx_cons = cons;
+       used = atomic_sub_int_nv(&sc->sc_tx_cnt, free);
 
-       if (sc->sc_tx_cnt < GEM_NTXDESC - 2)
-               ifp->if_flags &= ~IFF_OACTIVE;
-       if (sc->sc_tx_cnt == 0)
+       /* All clean, turn off the timer. */
+       if (used == 0)
                ifp->if_timer = 0;
 
-       gem_start(ifp);
+       /*
+        * If we have enough room, clear IFF_OACTIVE to tell the stack
+        * that it iss OK to send packets.
+        */
+       if (ISSET(ifp->if_flags, IFF_OACTIVE) && (used < GEM_NTXDESC - 2)) {
+               KERNEL_LOCK();
+               CLR(ifp->if_flags, IFF_OACTIVE);
+               gem_start(ifp);
+               KERNEL_UNLOCK();
+       }
 
        return (1);
 }
 
+int
+gem_load_mbuf(struct gem_softc *sc, struct gem_sxd *sd, struct mbuf *m)
+{
+       int error;
+
+       error = bus_dmamap_load_mbuf(sc->sc_dmatag, sd->sd_map, m,
+           BUS_DMA_NOWAIT);
+       switch (error) {
+       case 0:
+               break;
+
+       case EFBIG: /* mbuf chain is too fragmented */
+               if (m_defrag(m, M_DONTWAIT) == 0 &&
+                   bus_dmamap_load_mbuf(sc->sc_dmatag, sd->sd_map, m,
+                   BUS_DMA_NOWAIT) == 0)
+                       break;
+               /* FALLTHROUGH */
+       default:
+               return (1);
+       }
+
+       sd->sd_mbuf = m;
+       return (0);
+}
+
 void
 gem_start(struct ifnet *ifp)
 {
        struct gem_softc *sc = ifp->if_softc;
+       struct gem_sxd *sd;
        struct mbuf *m;
        u_int64_t flags;
        bus_dmamap_t map;
-       u_int32_t cur, frag, i;
-       int error;
+       u_int32_t cons, prod;
+       unsigned int used, new;
 
        if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
                return;
 
-       while (sc->sc_txd[sc->sc_tx_prod].sd_mbuf == NULL) {
-               IFQ_POLL(&ifp->if_snd, m);
+       cons = prod = sc->sc_tx_prod;
+       used = sc->sc_tx_cnt;
+       new = 0;
+
+       for (;;) {
+               IFQ_DEQUEUE(&ifp->if_snd, m);
                if (m == NULL)
                        break;
 
-               /*
-                * Encapsulate this packet and start it going...
-                * or fail...
-                */
+               sd = &sc->sc_txd[prod];
+               map = sd->sd_map;
 
-               cur = frag = sc->sc_tx_prod;
-               map = sc->sc_txd[cur].sd_map;
-
-               error = bus_dmamap_load_mbuf(sc->sc_dmatag, map, m,
-                   BUS_DMA_NOWAIT);
-               if (error != 0 && error != EFBIG)
-                       goto drop;
-               if (error != 0) {
-                       /* Too many fragments, linearize. */
-                       if (m_defrag(m, M_DONTWAIT))
-                               goto drop;
-                       error = bus_dmamap_load_mbuf(sc->sc_dmatag, map, m,
-                           BUS_DMA_NOWAIT);
-                       if (error != 0)
-                               goto drop;
-               }
-
-               if ((sc->sc_tx_cnt + map->dm_nsegs) > (GEM_NTXDESC - 2)) {
-                       bus_dmamap_unload(sc->sc_dmatag, map);
-                       ifp->if_flags |= IFF_OACTIVE;
+               if (used + map->dm_nsegs > (GEM_NTXDESC - 2)) {
+                       SET(ifp->if_flags, IFF_OACTIVE);
                        break;
                }
 
-               /* We are now committed to transmitting the packet. */
-               IFQ_DEQUEUE(&ifp->if_snd, m);
+               if (gem_load_mbuf(sc, sd, m)) {
+                       m_freem(m);
+                       ifp->if_oerrors++;
+                       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
@@ -1707,39 +1739,53 @@ gem_start(struct ifnet *ifp)
                bus_dmamap_sync(sc->sc_dmatag, map, 0, map->dm_mapsize,
                    BUS_DMASYNC_PREWRITE);
 
+               new += map->dm_nsegs;
+
+               prod += map->dm_nsegs;
+               if (prod >= GEM_NTXDESC)
+                       prod -= GEM_NTXDESC;
+       }
+
+       if (cons == prod)
+               return;
+
+       atomic_add_int(&sc->sc_tx_cnt, new);
+
+       while (cons != prod) {
+               uint32_t i, last;
+
+               sd = &sc->sc_txd[cons];
+               map = sd->sd_map;
+
                for (i = 0; i < map->dm_nsegs; i++) {
-                       GEM_DMA_WRITE(sc, &sc->sc_txdescs[frag].gd_addr,
+                       GEM_DMA_WRITE(sc, &sc->sc_txdescs[cons].gd_addr,
                            map->dm_segs[i].ds_addr);
                        flags = map->dm_segs[i].ds_len & GEM_TD_BUFSIZE;
                        if (i == 0)
                                flags |= GEM_TD_START_OF_PACKET;
                        if (i == (map->dm_nsegs - 1))
                                flags |= GEM_TD_END_OF_PACKET;
-                       GEM_DMA_WRITE(sc, &sc->sc_txdescs[frag].gd_flags,
+                       GEM_DMA_WRITE(sc, &sc->sc_txdescs[cons].gd_flags,
                            flags);
                        bus_dmamap_sync(sc->sc_dmatag, sc->sc_cddmamap,
-                           GEM_CDTXOFF(frag), sizeof(struct gem_desc),
+                           GEM_CDTXOFF(cons), sizeof(struct gem_desc),
                            BUS_DMASYNC_PREWRITE);
-                       cur = frag;
-                       if (++frag == GEM_NTXDESC)
-                               frag = 0;
-               }
-
-               sc->sc_tx_cnt += map->dm_nsegs;
-               sc->sc_txd[sc->sc_tx_prod].sd_map = sc->sc_txd[cur].sd_map;
-               sc->sc_txd[cur].sd_map = map;
-               sc->sc_txd[cur].sd_mbuf = m;
 
-               bus_space_write_4(sc->sc_bustag, sc->sc_h1, GEM_TX_KICK, frag);
-               sc->sc_tx_prod = frag;
+                       last = cons;
+                       if (++cons == GEM_NTXDESC)
+                               cons = 0;
+               }
 
-               ifp->if_timer = 5;
+               sd->sd_map = sc->sc_txd[last].sd_map;
+               sc->sc_txd[last].sd_map = map;
        }
 
-       return;
+       /* Transmit. */
+       bus_space_write_4(sc->sc_bustag, sc->sc_h1, GEM_TX_KICK, prod);
+
+       /* Commit. */
+       sc->sc_tx_prod = prod;
 
- drop:
-       IFQ_DEQUEUE(&ifp->if_snd, m);
-       m_freem(m);
-       ifp->if_oerrors++;
+       /* Set timeout in case hardware has problems transmitting. */
+       ifp->if_timer = 5;
 }
Index: dev/ic/gemvar.h
===================================================================
RCS file: /cvs/src/sys/dev/ic/gemvar.h,v
retrieving revision 1.30
diff -u -p -r1.30 gemvar.h
--- dev/ic/gemvar.h     10 Sep 2015 12:49:55 -0000      1.30
+++ dev/ic/gemvar.h     1 Oct 2015 11:04:04 -0000
@@ -124,6 +124,7 @@ struct gem_softc {
        struct mii_data sc_mii;         /* MII media control */
 #define sc_media       sc_mii.mii_media/* shorthand */
        struct timeout  sc_tick_ch;     /* tick callout */
+       void            *sc_ih;         /* interrupt handler */
 
        /* The following bus handles are to be provided by the bus front-end */
        bus_space_tag_t sc_bustag;      /* bus tag */
Index: dev/pci/if_gem_pci.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_gem_pci.c,v
retrieving revision 1.37
diff -u -p -r1.37 if_gem_pci.c
--- dev/pci/if_gem_pci.c        14 Mar 2015 03:38:48 -0000      1.37
+++ dev/pci/if_gem_pci.c        1 Oct 2015 11:07:03 -0000
@@ -72,7 +72,6 @@ struct gem_pci_softc {
        bus_space_tag_t         gsc_memt;
        bus_space_handle_t      gsc_memh;
        bus_size_t              gsc_memsize;
-       void                    *gsc_ih;
        pci_chipset_tag_t       gsc_pc;
 };
 
@@ -276,9 +275,9 @@ gem_attach_pci(struct device *parent, st
                return;
        }
        intrstr = pci_intr_string(pa->pa_pc, ih);
-       gsc->gsc_ih = pci_intr_establish(pa->pa_pc,
-           ih, IPL_NET, gem_intr, sc, self->dv_xname);
-       if (gsc->gsc_ih == NULL) {
+       sc->sc_ih = pci_intr_establish(pa->pa_pc,
+           ih, IPL_NET | IPL_MPSAFE, gem_intr, sc, self->dv_xname);
+       if (sc->sc_ih == NULL) {
                printf(": couldn't establish interrupt");
                if (intrstr != NULL)
                        printf(" at %s", intrstr);
@@ -305,7 +304,7 @@ gem_detach_pci(struct device *self, int 
        timeout_del(&sc->sc_rx_watchdog);
 
        gem_unconfig(sc);
-       pci_intr_disestablish(gsc->gsc_pc, gsc->gsc_ih);
+       pci_intr_disestablish(gsc->gsc_pc, sc->sc_ih);
        bus_space_unmap(gsc->gsc_memt, gsc->gsc_memh, gsc->gsc_memsize);
 
        return (0);
Index: dev/sbus/if_gem_sbus.c
===================================================================
RCS file: /cvs/src/sys/dev/sbus/if_gem_sbus.c,v
retrieving revision 1.8
diff -u -p -r1.8 if_gem_sbus.c
--- dev/sbus/if_gem_sbus.c      11 Aug 2014 12:45:45 -0000      1.8
+++ dev/sbus/if_gem_sbus.c      1 Oct 2015 11:06:57 -0000
@@ -141,8 +141,8 @@ gemattach_sbus(struct device *parent, st
            GEM_SBUS_CFG_BSIZE128|GEM_SBUS_CFG_PARITY|GEM_SBUS_CFG_BMODE64);
 
        /* Establish interrupt handler */
-       bus_intr_establish(sa->sa_bustag, sa->sa_pri, IPL_NET, 0, gem_intr,
-           sc, self->dv_xname);
+       sc->sc_ih = bus_intr_establish(sa->sa_bustag, sa->sa_pri, IPL_NET, 0,
+           gem_intr, sc, self->dv_xname);
 
        gem_config(sc);
 }

Reply via email to