[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

sk(4) checksum offloading



Here's a patch for hardware TCP/IP checksum offloading for SysKonnect GigE
cards (only receive, for now). Please test and report to me.

Index: share/man/man4/sk.4
===================================================================
RCS file: /cvs/src/share/man/man4/sk.4,v
retrieving revision 1.13
diff -u -r1.13 sk.4
--- share/man/man4/sk.4	5 Oct 2001 14:45:53 -0000	1.13
+++ share/man/man4/sk.4	9 Jun 2002 22:25:21 -0000
@@ -91,6 +91,8 @@
 Using jumbo frames can greatly improve performance for certain tasks,
 such as file transfers and data streaming.
 .Pp
+Hardware TCP/IP checksum offloading for IPv4 is supported.
+.Pp
 The following media types and options (as given to
 .Xr ifconfig 8 )
 are supported:
Index: sys/dev/pci/if_sk.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_sk.c,v
retrieving revision 1.25
diff -u -r1.25 if_sk.c
--- sys/dev/pci/if_sk.c	8 Jun 2002 23:32:16 -0000	1.25
+++ sys/dev/pci/if_sk.c	9 Jun 2002 22:25:21 -0000
@@ -163,6 +163,8 @@
 void sk_setmulti(struct sk_if_softc *);
 void sk_tick(void *);

+int sk_rxcsum(struct mbuf *, u_int16_t, u_int16_t);
+
 #define SK_SETBIT(sc, reg, x)		\
 	CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | x)

@@ -547,6 +549,9 @@
 			rd->sk_rx_ring[i].sk_next =
 			    vtophys(&rd->sk_rx_ring[i + 1]);
 		}
+		rd->sk_rx_ring[i].sk_csum1_start = ETHER_HDR_LEN;
+		rd->sk_rx_ring[i].sk_csum2_start = ETHER_HDR_LEN +
+		    sizeof(struct ip);
 	}

 	sc_if->sk_cdata.sk_rx_prod = 0;
@@ -1342,8 +1347,10 @@
 	struct mbuf		*m;
 	struct ifnet		*ifp;
 	struct sk_chain		*cur_rx;
+	struct ether_header	*eh;
 	int			total_len = 0;
 	int			i;
+	u_int16_t		csum1, csum2;
 	u_int32_t		rxstat;

 	ifp = &sc_if->arpcom.ac_if;
@@ -1357,6 +1364,8 @@
 		m = cur_rx->sk_mbuf;
 		cur_rx->sk_mbuf = NULL;
 		total_len = SK_RXBYTES(sc_if->sk_rdata->sk_rx_ring[i].sk_ctl);
+		csum1 = sc_if->sk_rdata->sk_rx_ring[i].sk_csum1;
+		csum2 = sc_if->sk_rdata->sk_rx_ring[i].sk_csum2;
 		SK_INC(i, SK_RX_RING_CNT);

 		if (rxstat & XM_RXSTAT_ERRFRAME) {
@@ -1397,8 +1406,13 @@
 		if (ifp->if_bpf)
 			bpf_mtap(ifp->if_bpf, m);
 #endif
+		eh = mtod(m, struct ether_header *);
+		m_adj(m, ETHER_HDR_LEN);
+
+		m->m_pkthdr.csum = sk_rxcsum(m, csum1, csum2);
+
 		/* pass it on. */
-		ether_input_mbuf(ifp, m);
+		ether_input(ifp, eh, m);
 	}

 	sc_if->sk_cdata.sk_rx_prod = i;
@@ -1487,6 +1501,81 @@
 	mii_tick(mii);
 	mii_pollstat(mii);
 	timeout_del(&sc_if->sk_tick_ch);
+}
+
+int
+sk_rxcsum(m, csum1, csum2)
+	struct mbuf *m;
+	u_int16_t csum1, csum2;
+{
+	struct ip *ip = mtod(m, struct ip *);
+	u_int16_t fragoff = ntohs(ip->ip_off), iph_csum, ipo_csum, ipd_csum;
+	u_int32_t csum, lenproto;
+	int hlen = ip->ip_hl << 2, sumflags = 0;
+
+	if (ip->ip_v != IPVERSION)
+		return (0);
+
+	if (hlen < sizeof(struct ip))
+		return (0);
+
+	if (hlen > ntohs(ip->ip_len))
+		return (0);
+
+	/*
+	 * csum1 = checksum of the data following the Ethernet header.
+	 * csum2 = checksum of the data following the IP header (w/o options).
+	 *
+	 * We compute the IP header checksum by subtracting csum2 from csum1,
+	 * then adding the checksum of the IP options.
+	 */
+	iph_csum = in_cksum_addword(csum1, (~csum2 & 0xffff));
+	if (hlen > sizeof(struct ip)) {
+		ipo_csum = in4_cksum(m, 0, sizeof(struct ip), hlen -
+		    sizeof(struct ip));
+		iph_csum = in_cksum_addword(iph_csum, ipo_csum);
+		ipd_csum = in_cksum_addword(csum2, (~ipo_csum & 0xffff));
+	} else
+		ipd_csum = csum2;
+
+	/*
+	 * Check for bad IP checksum.
+	 */
+	if (iph_csum != 0xffff) {
+		sumflags |= M_IPV4_CSUM_IN_BAD;
+		return (sumflags);
+	} else
+		sumflags |= M_IPV4_CSUM_IN_OK;
+
+	/*
+	 * An IP fragment. Return now.
+	 */
+	if ((fragoff & IP_MF) != 0 || ((fragoff & IP_OFFMASK) << 3) != 0)
+		return (sumflags);
+
+	/* XXX - check for no UDP checksum? */
+
+	switch (ip->ip_p) {
+	case IPPROTO_TCP:
+	case IPPROTO_UDP:
+		lenproto = ntohs(ip->ip_len) - hlen + ip->ip_p;
+
+		/* Calculate the pseudo header checksum. */
+		csum = in_cksum_addword(in_cksum_phdr(ip->ip_src.s_addr,
+		    ip->ip_dst.s_addr, htonl(lenproto)) + ipd_csum, 0);
+		if (csum != 0xffff) {
+			sumflags |= (ip->ip_p == IPPROTO_TCP) ?
+			    M_TCP_CSUM_IN_BAD : M_UDP_CSUM_IN_BAD;
+		} else {
+			sumflags |= (ip->ip_p == IPPROTO_TCP) ?
+			    M_TCP_CSUM_IN_OK : M_UDP_CSUM_IN_OK;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return (sumflags);
 }

 void
Index: sys/dev/pci/if_skreg.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_skreg.h,v
retrieving revision 1.6
diff -u -r1.6 if_skreg.h
--- sys/dev/pci/if_skreg.h	23 Jun 2001 22:03:12 -0000	1.6
+++ sys/dev/pci/if_skreg.h	9 Jun 2002 22:25:22 -0000
@@ -1068,7 +1068,7 @@
 #define SK_RXCTL_OWN		0x80000000

 #define SK_RXSTAT	\
-	(SK_OPCODE_DEFAULT|SK_RXCTL_EOF_INTR|SK_RXCTL_LASTFRAG| \
+	(SK_OPCODE_CSUM|SK_RXCTL_EOF_INTR|SK_RXCTL_LASTFRAG| \
 	 SK_RXCTL_FIRSTFRAG|SK_RXCTL_OWN)

 struct sk_tx_desc {

---
Aaron Campbell (aaron_(_at_)_monkey_(_dot_)_org || aaron_(_at_)_openbsd_(_dot_)_org)
http://www.monkey.org/~aaron



Visit your host, monkey.org