On 16/10/15(Fri) 15:05, Martin Pieuchot wrote:
> 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.

New diff fixing two issues found by jmatthew@

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        22 Oct 2015 13:48:52 -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,96 @@ 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 + new + map->dm_nsegs > (GEM_NTXDESC - 2)) {
+                       m_freem(m);
+                       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 +1740,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     22 Oct 2015 13:46:44 -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        22 Oct 2015 13:46:44 -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      22 Oct 2015 13:46:44 -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