Module Name:    src
Committed By:   ryo
Date:           Wed Aug 24 19:22:37 UTC 2022

Modified Files:
        src/sys/dev/ic: dwc_eqos.c dwc_eqos_var.h

Log Message:
jumboframe support for eqos(4)

Tested up to mtu=9000. Hardware may allow up to mtu=16364
(frame length=16382), but this has not been tested.

- Separate calls to eqos_setup_rxdesc() from eqos_setup_rxbuf().
  if m_getcl() fails, discard the packets received at that time
  and reuse for the next buffer.
- Restore m_adj(m, ETHER_ALIGN) with limited conditions.
  Only if MCLBYTES is greater than 2050, so it is not normally aligned.


To generate a diff of this commit:
cvs rdiff -u -r1.12 -r1.13 src/sys/dev/ic/dwc_eqos.c
cvs rdiff -u -r1.3 -r1.4 src/sys/dev/ic/dwc_eqos_var.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/ic/dwc_eqos.c
diff -u src/sys/dev/ic/dwc_eqos.c:1.12 src/sys/dev/ic/dwc_eqos.c:1.13
--- src/sys/dev/ic/dwc_eqos.c:1.12	Wed Aug 24 19:21:41 2022
+++ src/sys/dev/ic/dwc_eqos.c	Wed Aug 24 19:22:37 2022
@@ -1,4 +1,4 @@
-/* $NetBSD: dwc_eqos.c,v 1.12 2022/08/24 19:21:41 ryo Exp $ */
+/* $NetBSD: dwc_eqos.c,v 1.13 2022/08/24 19:22:37 ryo Exp $ */
 
 /*-
  * Copyright (c) 2022 Jared McNeill <jmcne...@invisible.ca>
@@ -33,7 +33,7 @@
 #include "opt_net_mpsafe.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: dwc_eqos.c,v 1.12 2022/08/24 19:21:41 ryo Exp $");
+__KERNEL_RCSID(0, "$NetBSD: dwc_eqos.c,v 1.13 2022/08/24 19:22:37 ryo Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -59,7 +59,10 @@ __KERNEL_RCSID(0, "$NetBSD: dwc_eqos.c,v
 #include <dev/ic/dwc_eqos_reg.h>
 #include <dev/ic/dwc_eqos_var.h>
 
-CTASSERT(MCLBYTES == 2048);
+#define	EQOS_MAX_MTU		9000	/* up to 16364? but not tested */
+#define	EQOS_TXDMA_SIZE		(EQOS_MAX_MTU + ETHER_HDR_LEN + ETHER_CRC_LEN)
+#define	EQOS_RXDMA_SIZE		2048	/* Fixed value by hardware */
+CTASSERT(MCLBYTES >= EQOS_RXDMA_SIZE);
 
 #ifdef EQOS_DEBUG
 unsigned int eqos_debug;
@@ -365,6 +368,10 @@ eqos_setup_rxbuf(struct eqos_softc *sc, 
 {
 	int error;
 
+#if MCLBYTES >= (EQOS_RXDMA_SIZE + ETHER_ALIGN)
+	m_adj(m, ETHER_ALIGN);
+#endif
+
 	error = bus_dmamap_load_mbuf(sc->sc_dmat,
 	    sc->sc_rx.buf_map[index].map, m, BUS_DMA_READ | BUS_DMA_NOWAIT);
 	if (error != 0)
@@ -375,8 +382,6 @@ eqos_setup_rxbuf(struct eqos_softc *sc, 
 	    BUS_DMASYNC_PREREAD);
 
 	sc->sc_rx.buf_map[index].mbuf = m;
-	eqos_setup_rxdesc(sc, index,
-	    sc->sc_rx.buf_map[index].map->dm_segs[0].ds_addr);
 
 	return 0;
 }
@@ -524,6 +529,12 @@ eqos_init_rings(struct eqos_softc *sc, i
 {
 	sc->sc_tx.cur = sc->sc_tx.next = sc->sc_tx.queued = 0;
 
+	sc->sc_rx_discarding = false;
+	if (sc->sc_rx_receiving_m != NULL)
+		m_freem(sc->sc_rx_receiving_m);
+	sc->sc_rx_receiving_m = NULL;
+	sc->sc_rx_receiving_m_last = NULL;
+
 	WR4(sc, GMAC_DMA_CHAN0_TX_BASE_ADDR_HI,
 	    (uint32_t)(sc->sc_tx.desc_ring_paddr >> 32));
 	WR4(sc, GMAC_DMA_CHAN0_TX_BASE_ADDR,
@@ -737,9 +748,15 @@ static void
 eqos_rxintr(struct eqos_softc *sc, int qid)
 {
 	struct ifnet *ifp = &sc->sc_ec.ec_if;
-	int error, index, len, pkts = 0;
-	struct mbuf *m, *m0;
+	int error, index, pkts = 0;
+	struct mbuf *m, *m0, *new_m, *mprev;
 	uint32_t tdes3;
+	bool discarding;
+
+	/* restore jumboframe context */
+	discarding = sc->sc_rx_discarding;
+	m0 = sc->sc_rx_receiving_m;
+	mprev = sc->sc_rx_receiving_m_last;
 
 	for (index = sc->sc_rx.cur; ; index = RX_NEXT(index)) {
 		eqos_dma_sync(sc, sc->sc_rx.desc_map,
@@ -751,33 +768,100 @@ eqos_rxintr(struct eqos_softc *sc, int q
 			break;
 		}
 
+		/* now discarding untill the last packet */
+		if (discarding)
+			goto rx_next;
+
+		if ((tdes3 & EQOS_TDES3_RX_CTXT) != 0)
+			goto rx_next;	/* ignore receive context descriptor */
+
+		/* error packet? */
+		if ((tdes3 & (EQOS_TDES3_RX_CE | EQOS_TDES3_RX_RWT |
+		    EQOS_TDES3_RX_OE | EQOS_TDES3_RX_RE |
+		    EQOS_TDES3_RX_DE)) != 0) {
+#ifdef EQOS_DEBUG
+			char buf[128];
+			snprintb(buf, sizeof(buf),
+			    "\177\020"
+			    "b\x1e" "CTXT\0"	/* 30 */
+			    "b\x18" "CE\0"	/* 24 */
+			    "b\x17" "GP\0"	/* 23 */
+			    "b\x16" "WDT\0"	/* 22 */
+			    "b\x15" "OE\0"	/* 21 */
+			    "b\x14" "RE\0"	/* 20 */
+			    "b\x13" "DE\0"	/* 19 */
+			    "b\x0f" "ES\0"	/* 15 */
+			    "\0", tdes3);
+			DPRINTF(EDEB_NOTE, "rxdesc[%d].tdes3=%s\n", index, buf);
+#endif
+			if_statinc(ifp, if_ierrors);
+			if (m0 != NULL) {
+				m_freem(m0);
+				m0 = mprev = NULL;
+			}
+			discarding = true;
+			goto rx_next;
+		}
+
 		bus_dmamap_sync(sc->sc_dmat, sc->sc_rx.buf_map[index].map,
 		    0, sc->sc_rx.buf_map[index].map->dm_mapsize,
 		    BUS_DMASYNC_POSTREAD);
-		bus_dmamap_unload(sc->sc_dmat,
-		    sc->sc_rx.buf_map[index].map);
+		m = sc->sc_rx.buf_map[index].mbuf;
+		new_m = eqos_alloc_mbufcl(sc);
+		if (new_m == NULL) {
+			/*
+			 * cannot allocate new mbuf. discard this received
+			 * packet, and reuse the mbuf for next.
+			 */
+			if_statinc(ifp, if_ierrors);
+			if (m0 != NULL) {
+				/* also discard the halfway jumbo packet */
+				m_freem(m0);
+				m0 = mprev = NULL;
+			}
+			discarding = true;
+			goto rx_next;
+		}
+		error = eqos_setup_rxbuf(sc, index, new_m);
+		if (error)
+			panic("%s: %s: unable to load RX mbuf. error=%d",
+			    device_xname(sc->sc_dev), __func__, error);
 
-		len = tdes3 & EQOS_TDES3_RX_LENGTH_MASK;
-		if (len != 0) {
-			m = sc->sc_rx.buf_map[index].mbuf;
-			m_set_rcvif(m, ifp);
-			m->m_flags |= M_HASFCS;
-			m->m_pkthdr.len = len;
-			m->m_len = len;
-			m->m_nextpkt = NULL;
+		if (m0 == NULL) {
+			m0 = m;
+		} else {
+			if (m->m_flags & M_PKTHDR)
+				m_remove_pkthdr(m);
+			mprev->m_next = m;
+		}
+		mprev = m;
+
+		if ((tdes3 & EQOS_TDES3_RX_LD) == 0) {
+			/* to be continued in the next segment */
+			m->m_len = EQOS_RXDMA_SIZE;
+		} else {
+			/* last segment */
+			uint32_t totallen = tdes3 & EQOS_TDES3_RX_LENGTH_MASK;
+			uint32_t mlen = totallen % EQOS_RXDMA_SIZE;
+			if (mlen == 0)
+				mlen = EQOS_RXDMA_SIZE;
+			m->m_len = mlen;
+			m0->m_pkthdr.len = totallen;
+			m_set_rcvif(m0, ifp);
+			m0->m_flags |= M_HASFCS;
+			m0->m_nextpkt = NULL;
+			if_percpuq_enqueue(ifp->if_percpuq, m0);
+			m0 = mprev = NULL;
 
-			if_percpuq_enqueue(ifp->if_percpuq, m);
 			++pkts;
 		}
 
-		if ((m0 = eqos_alloc_mbufcl(sc)) != NULL) {
-			error = eqos_setup_rxbuf(sc, index, m0);
-			if (error != 0) {
-				/* XXX hole in RX ring */
-			}
-		} else {
-			if_statinc(ifp, if_ierrors);
-		}
+ rx_next:
+		if (discarding && (tdes3 & EQOS_TDES3_RX_LD) != 0)
+			discarding = false;
+
+		eqos_setup_rxdesc(sc, index,
+		    sc->sc_rx.buf_map[index].map->dm_segs[0].ds_addr);
 		eqos_dma_sync(sc, sc->sc_rx.desc_map,
 		    index, index + 1, RX_DESC_COUNT,
 		    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
@@ -786,6 +870,10 @@ eqos_rxintr(struct eqos_softc *sc, int q
 		    (uint32_t)sc->sc_rx.desc_ring_paddr +
 		    DESC_OFF(sc->sc_rx.cur));
 	}
+	/* save jumboframe context */
+	sc->sc_rx_discarding = discarding;
+	sc->sc_rx_receiving_m = m0;
+	sc->sc_rx_receiving_m_last = mprev;
 
 	sc->sc_rx.cur = index;
 
@@ -1053,6 +1141,7 @@ static int
 eqos_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 {
 	struct eqos_softc *sc = ifp->if_softc;
+	struct ifreq *ifr = (struct ifreq *)data;
 	int error, s;
 
 #ifndef EQOS_MPSAFE
@@ -1060,6 +1149,14 @@ eqos_ioctl(struct ifnet *ifp, u_long cmd
 #endif
 
 	switch (cmd) {
+	case SIOCSIFMTU:
+		if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > EQOS_MAX_MTU) {
+			error = EINVAL;
+		} else {
+			ifp->if_mtu = ifr->ifr_mtu;
+			error = 0;	/* no need ENETRESET */
+		}
+		break;
 	default:
 #ifdef EQOS_MPSAFE
 		s = splnet();
@@ -1196,7 +1293,7 @@ eqos_setup_dma(struct eqos_softc *sc, in
 
 	sc->sc_tx.queued = TX_DESC_COUNT;
 	for (i = 0; i < TX_DESC_COUNT; i++) {
-		error = bus_dmamap_create(sc->sc_dmat, MCLBYTES,
+		error = bus_dmamap_create(sc->sc_dmat, EQOS_TXDMA_SIZE,
 		    TX_MAX_SEGS, MCLBYTES, 0, BUS_DMA_WAITOK,
 		    &sc->sc_tx.buf_map[i].map);
 		if (error != 0) {
@@ -1250,6 +1347,8 @@ eqos_setup_dma(struct eqos_softc *sc, in
 			device_printf(sc->sc_dev, "cannot create RX buffer\n");
 			return error;
 		}
+		eqos_setup_rxdesc(sc, i,
+		    sc->sc_rx.buf_map[i].map->dm_segs[0].ds_addr);
 	}
 	bus_dmamap_sync(sc->sc_dmat, sc->sc_rx.desc_map,
 	    0, sc->sc_rx.desc_map->dm_mapsize,
@@ -1369,8 +1468,9 @@ eqos_attach(struct eqos_softc *sc)
 	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
 	IFQ_SET_READY(&ifp->if_snd);
 
-	/* 802.1Q VLAN-sized frames are supported */
+	/* 802.1Q VLAN-sized frames, and jumbo frame are supported */
 	sc->sc_ec.ec_capabilities |= ETHERCAP_VLAN_MTU;
+	sc->sc_ec.ec_capabilities |= ETHERCAP_JUMBO_MTU;
 
 	/* Attach MII driver */
 	sc->sc_ec.ec_mii = mii;

Index: src/sys/dev/ic/dwc_eqos_var.h
diff -u src/sys/dev/ic/dwc_eqos_var.h:1.3 src/sys/dev/ic/dwc_eqos_var.h:1.4
--- src/sys/dev/ic/dwc_eqos_var.h:1.3	Sun Jan  9 00:36:28 2022
+++ src/sys/dev/ic/dwc_eqos_var.h	Wed Aug 24 19:22:37 2022
@@ -1,4 +1,4 @@
-/* $NetBSD: dwc_eqos_var.h,v 1.3 2022/01/09 00:36:28 mrg Exp $ */
+/* $NetBSD: dwc_eqos_var.h,v 1.4 2022/08/24 19:22:37 ryo Exp $ */
 
 /*-
  * Copyright (c) 2022 Jared McNeill <jmcne...@invisible.ca>
@@ -71,6 +71,11 @@ struct eqos_softc {
 	struct eqos_ring	sc_tx;
 	struct eqos_ring	sc_rx;
 
+	/* receiving context for jumbo frame */
+	bool			sc_rx_discarding;
+	struct mbuf		*sc_rx_receiving_m;
+	struct mbuf		*sc_rx_receiving_m_last;
+
 	krndsource_t		sc_rndsource;
 
 	/* Indents indicate groups within evcnt. */

Reply via email to