Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 16 Apr 2007 08:48:24 +0900
From:      Pyun YongHyeon <pyunyh@gmail.com>
To:        Peter Grehan <grehan@freebsd.org>
Cc:        freebsd-ppc@freebsd.org
Subject:   Re: CFT: gem(4) checksum offload support
Message-ID:  <20070415234823.GA21416@cdnetworks.co.kr>
In-Reply-To: <46229F3F.40903@freebsd.org>
References:  <20070414060841.GB12777@cdnetworks.co.kr> <46229F3F.40903@freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help

--d6Gm4EdcadzBjdND
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

On Sun, Apr 15, 2007 at 02:55:11PM -0700, Peter Grehan wrote:
 > Hi,
 > 
 >  I get a compile error for this on ppc:
 > 
 > /usr/home/grehan/freebsd/dev_head/src/sys/dev/gem/if_gem.c: In function 
 > `gem_txdma_callback':
 > /usr/home/grehan/freebsd/dev_head/src/sys/dev/gem/if_gem.c:92: warning: 
 > inlining failed in call to 'gem_txcksum': function body not available
 > /usr/home/grehan/freebsd/dev_head/src/sys/dev/gem/if_gem.c:505: warning: 
 > called from here
 > *** Error code 1
 > 
 >  Removing the __inline's fixed it, as I guess would re-ordering.
 > 

Thank you for testing.
I guess you've tested it on RELENG_6.
Here is updated patch.

-- 
Regards,
Pyun YongHyeon

--d6Gm4EdcadzBjdND
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="gem.chksum.patch2"

Index: if_gem.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/gem/if_gem.c,v
retrieving revision 1.40
diff -u -r1.40 if_gem.c
--- if_gem.c	6 Dec 2006 02:04:25 -0000	1.40
+++ if_gem.c	15 Apr 2007 23:45:59 -0000
@@ -65,6 +65,12 @@
 #include <net/if_types.h>
 #include <net/if_vlan_var.h>
 
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+
 #include <machine/bus.h>
 
 #include <dev/mii/mii.h>
@@ -74,6 +80,7 @@
 #include <dev/gem/if_gemvar.h>
 
 #define TRIES	10000
+#define	GEM_CSUM_FEATURES	(CSUM_TCP)
 
 static void	gem_start(struct ifnet *);
 static void	gem_start_locked(struct ifnet *);
@@ -82,6 +89,8 @@
 static void	gem_cddma_callback(void *, bus_dma_segment_t *, int, int);
 static void	gem_txdma_callback(void *, bus_dma_segment_t *, int,
     bus_size_t, int);
+static __inline void gem_txcksum(struct gem_softc *, struct mbuf *, uint64_t *);
+static __inline void gem_rxcksum(struct mbuf *, uint64_t);
 static void	gem_tick(void *);
 static int	gem_watchdog(struct gem_softc *);
 static void	gem_init(void *);
@@ -264,6 +273,7 @@
 	device_printf(sc->sc_dev, "%ukB RX FIFO, %ukB TX FIFO\n",
 	    sc->sc_rxfifosize / 1024, v / 16);
 
+	sc->sc_csum_features = GEM_CSUM_FEATURES;
 	/* Initialize ifnet structure. */
 	ifp->if_softc = sc;
 	if_initname(ifp, device_get_name(sc->sc_dev),
@@ -332,11 +342,12 @@
 #endif
 
 	/*
-	 * Tell the upper layer(s) we support long frames.
+	 * Tell the upper layer(s) we support long frames/checksum offloads.
 	 */
 	ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
-	ifp->if_capabilities |= IFCAP_VLAN_MTU;
-	ifp->if_capenable |= IFCAP_VLAN_MTU;
+	ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_HWCSUM;
+	ifp->if_hwassist |= sc->sc_csum_features;
+	ifp->if_capenable |= IFCAP_VLAN_MTU | IFCAP_HWCSUM;
 
 	return (0);
 
@@ -440,6 +451,112 @@
 	GEM_UNLOCK(sc);
 }
 
+static __inline void
+gem_txcksum(struct gem_softc *sc, struct mbuf *m, uint64_t *cflags)
+{
+	struct ip *ip;
+	uint64_t offset, offset2;
+	char *p;
+
+	offset = sizeof(struct ip) + ETHER_HDR_LEN;
+	for(; m && m->m_len == 0; m = m->m_next)
+		;
+	if (m == NULL || m->m_len < ETHER_HDR_LEN) {
+		device_printf(sc->sc_dev, "%s: m_len < ETHER_HDR_LEN\n",
+		    __func__);
+		/* checksum will be corrupted */
+		goto sendit;
+	}
+	if (m->m_len < ETHER_HDR_LEN + sizeof(uint32_t)) {
+		if (m->m_len != ETHER_HDR_LEN) {
+			device_printf(sc->sc_dev,
+			    "%s: m_len != ETHER_HDR_LEN\n", __func__);
+			/* checksum will be corrupted */
+			goto sendit;
+		}
+		for(m = m->m_next; m && m->m_len == 0; m = m->m_next)
+			;
+		if (m == NULL) {
+			/* checksum will be corrupted */
+			goto sendit;
+		}
+		ip = mtod(m, struct ip *);
+	} else {
+		p = mtod(m, uint8_t *);
+		p += ETHER_HDR_LEN;
+		ip = (struct ip *)p;
+	}
+	offset = (ip->ip_hl << 2) + ETHER_HDR_LEN;
+
+sendit:
+	offset2 = m->m_pkthdr.csum_data;
+	*cflags = offset << GEM_TD_CXSUM_SSHIFT;
+	*cflags |= ((offset + offset2) << GEM_TD_CXSUM_OSHIFT);
+	*cflags |= GEM_TD_CXSUM_ENABLE;
+}
+
+static __inline void
+gem_rxcksum(struct mbuf *m, uint64_t flags)
+{
+	struct ether_header *eh;
+	struct ip *ip;
+	struct udphdr *uh;
+	int32_t hlen, len, pktlen;
+	uint16_t cksum, *opts;
+	uint32_t temp32;
+
+	pktlen = m->m_pkthdr.len;
+	if (pktlen < sizeof(struct ether_header) + sizeof(struct ip))
+		return;
+	eh = mtod(m, struct ether_header *);
+	if (eh->ether_type != htons(ETHERTYPE_IP))
+		return;
+	ip = (struct ip *)(eh + 1);
+	if (ip->ip_v != IPVERSION)
+		return;
+
+	hlen = ip->ip_hl << 2;
+	pktlen -= sizeof(struct ether_header);
+	if (hlen < sizeof(struct ip))
+		return;
+	if (ntohs(ip->ip_len) < hlen)
+		return;
+	if (ntohs(ip->ip_len) != pktlen)
+		return;
+	if (ip->ip_off & htons(IP_MF | IP_OFFMASK))
+		return;	/* can't handle fragmented packet */
+
+	switch (ip->ip_p) {
+	case IPPROTO_TCP:
+		if (pktlen < (hlen + sizeof(struct tcphdr)))
+			return;
+		break;
+	case IPPROTO_UDP:
+		if (pktlen < (hlen + sizeof(struct udphdr)))
+			return;
+		uh = (struct udphdr *)((uint8_t *)ip + hlen);
+		if (uh->uh_sum == 0)
+			return; /* no checksum */
+		break;
+	default:
+		return;
+	}
+
+	cksum = ~(flags & GEM_RD_CHECKSUM);
+	/* checksum fixup for IP options */
+	len = hlen - sizeof(struct ip);
+	if (len > 0) {
+		opts = (uint16_t *)(ip + 1);
+		for (; len > 0; len -= sizeof(uint16_t), opts++) {
+			temp32 = cksum - *opts;
+			temp32 = (temp32 >> 16) + (temp32 & 65535);
+			cksum = temp32 & 65535;
+		}
+	}
+	m->m_pkthdr.csum_flags |= CSUM_DATA_VALID;
+	m->m_pkthdr.csum_data = cksum;
+}
+
 static void
 gem_cddma_callback(xsc, segs, nsegs, error)
 	void *xsc;
@@ -470,7 +587,7 @@
 	struct gem_softc *sc = txd->txd_sc;
 	struct gem_txsoft *txs = txd->txd_txs;
 	bus_size_t len = 0;
-	uint64_t flags = 0;
+	uint64_t cflags, flags;
 	int seg, nexttx;
 
 	if (error != 0)
@@ -488,6 +605,10 @@
 	txs->txs_ndescs = nsegs;
 
 	nexttx = txs->txs_firstdesc;
+
+	flags = cflags = 0;
+	if ((txd->m->m_pkthdr.csum_flags & sc->sc_csum_features) != 0)
+		gem_txcksum(sc, txd->m, &cflags);
 	/*
 	 * Initialize the transmit descriptors.
 	 */
@@ -507,6 +628,7 @@
 		KASSERT(segs[seg].ds_len < GEM_TD_BUFSIZE,
 		    ("gem_txdma_callback: segment size too large!"));
 		flags = segs[seg].ds_len & GEM_TD_BUFSIZE;
+		flags |= cflags;
 		if (len == 0) {
 #ifdef GEM_DEBUG
 			CTR2(KTR_GEM, "txdma_cb: start of packet at seg %d, "
@@ -954,12 +1076,14 @@
 
 	/* Encode Receive Descriptor ring size: four possible values */
 	v = gem_ringsize(GEM_NRXDESC /*XXX*/);
+	/* Rx TCP/UDP checksum offset */
+	v |= ((ETHER_HDR_LEN + sizeof(struct ip)) <<
+	    GEM_RX_CONFIG_CXM_START_SHFT);
 
 	/* Enable DMA */
 	bus_space_write_4(t, h, GEM_RX_CONFIG,
 		v|(GEM_THRSH_1024<<GEM_RX_CONFIG_FIFO_THRS_SHIFT)|
-		(2<<GEM_RX_CONFIG_FBOFF_SHFT)|GEM_RX_CONFIG_RXDMA_EN|
-		(0<<GEM_RX_CONFIG_CXM_START_SHFT));
+		(2<<GEM_RX_CONFIG_FBOFF_SHFT)|GEM_RX_CONFIG_RXDMA_EN);
 	/*
 	 * The following value is for an OFF Threshold of about 3/4 full
 	 * and an ON Threshold of 1/4 full.
@@ -974,7 +1098,7 @@
 
 	/* step 12. RX_MAC Configuration Register */
 	v = bus_space_read_4(t, h, GEM_MAC_RX_CONFIG);
-	v |= GEM_MAC_RX_ENABLE;
+	v |= GEM_MAC_RX_ENABLE | GEM_MAC_RX_STRIP_CRC;
 	bus_space_write_4(t, h, GEM_MAC_RX_CONFIG, v);
 
 	/* step 14. Issue Transmit Pending command */
@@ -1008,6 +1132,7 @@
 	txd.txd_sc = sc;
 	txd.txd_txs = txs;
 	txs->txs_firstdesc = sc->sc_txnext;
+	txd.m = m0;
 	error = bus_dmamap_load_mbuf(sc->sc_tdmatag, txs->txs_dmamap, m0,
 	    gem_txdma_callback, &txd, BUS_DMA_NOWAIT);
 	if (error != 0)
@@ -1454,8 +1579,7 @@
 #endif
 
 		/*
-		 * No errors; receive the packet.  Note the Gem
-		 * includes the CRC with every packet.
+		 * No errors; receive the packet.
 		 */
 		len = GEM_RD_BUFLEN(rxstat);
 
@@ -1473,7 +1597,10 @@
 		m->m_data += 2; /* We're already off by two */
 
 		m->m_pkthdr.rcvif = ifp;
-		m->m_pkthdr.len = m->m_len = len - ETHER_CRC_LEN;
+		m->m_pkthdr.len = m->m_len = len;
+
+		if ((ifp->if_capenable & IFCAP_RXCSUM) != 0)
+			gem_rxcksum(m, rxstat);
 
 		/* Pass it on. */
 		GEM_UNLOCK(sc);
@@ -1876,6 +2003,12 @@
 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
 				gem_stop(ifp, 0);
 		}
+		if ((ifp->if_flags & IFF_LINK0) != 0)
+			sc->sc_csum_features |= CSUM_UDP;
+		else
+			sc->sc_csum_features &= ~CSUM_UDP;
+		if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
+			ifp->if_hwassist = sc->sc_csum_features;
 		sc->sc_ifflags = ifp->if_flags;
 		GEM_UNLOCK(sc);
 		break;
@@ -1889,6 +2022,15 @@
 	case SIOCSIFMEDIA:
 		error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii->mii_media, cmd);
 		break;
+	case SIOCSIFCAP:
+		GEM_LOCK(sc);
+		ifp->if_capenable = ifr->ifr_reqcap;
+		if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
+			ifp->if_hwassist = sc->sc_csum_features;
+		else
+			ifp->if_hwassist = 0;
+		GEM_UNLOCK(sc);
+		break;
 	default:
 		error = ether_ioctl(ifp, cmd, data);
 		break;
Index: if_gemreg.h
===================================================================
RCS file: /home/ncvs/src/sys/dev/gem/if_gemreg.h,v
retrieving revision 1.3
diff -u -r1.3 if_gemreg.h
--- if_gemreg.h	6 Jan 2005 01:42:42 -0000	1.3
+++ if_gemreg.h	15 Apr 2007 23:45:59 -0000
@@ -516,6 +516,10 @@
 #define	GEM_TD_START_OF_PACKET	0x0000000080000000LL
 #define	GEM_TD_INTERRUPT_ME	0x0000000100000000LL	/* Interrupt me now */
 #define	GEM_TD_NO_CRC		0x0000000200000000LL	/* do not insert crc */
+
+#define	GEM_TD_CXSUM_SSHIFT	15
+#define	GEM_TD_CXSUM_OSHIFT	21
+
 /*
  * Only need to set GEM_TD_CXSUM_ENABLE, GEM_TD_CXSUM_STUFF,
  * GEM_TD_CXSUM_START, and GEM_TD_INTERRUPT_ME in 1st descriptor of a group.
Index: if_gemvar.h
===================================================================
RCS file: /home/ncvs/src/sys/dev/gem/if_gemvar.h,v
retrieving revision 1.12
diff -u -r1.12 if_gemvar.h
--- if_gemvar.h	6 Dec 2006 02:04:25 -0000	1.12
+++ if_gemvar.h	15 Apr 2007 23:45:59 -0000
@@ -108,6 +108,7 @@
 struct gem_txdma {
 	struct gem_softc *txd_sc;
 	struct gem_txsoft	*txd_txs;
+	struct mbuf *m;
 };
 
 /*
@@ -189,6 +190,7 @@
 	int		sc_inited;
 	int		sc_debug;
 	int		sc_ifflags;
+	int		sc_csum_features;
 
 	struct mtx	sc_mtx;
 };

--d6Gm4EdcadzBjdND--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20070415234823.GA21416>