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