CFT: gem(4) checksum offload support
Pyun YongHyeon
pyunyh at gmail.com
Sun Apr 15 23:48:08 UTC 2007
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
-------------- next part --------------
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;
};
More information about the freebsd-ppc
mailing list