Subject: HW assisted IPv4/TCP/UDP checksumming -- in-bound
To: None <tech-net@netbsd.org>
From: Jason R Thorpe <thorpej@zembu.com>
List: tech-net
Date: 05/18/2001 10:10:56
Folks...

I've done the low-hanging fruit part of HW assisted IPv4/TCP/UDP
checksums -- in-bound processing.  This is quite trivial.  Note
that the mbuf m_pkthdr change will be used for out-bound processing
as well.

Also included are the diffs to make the DP83820 Gigabit Ethernet chip
do in-bound IPv4/TCP/UDP checksum processing.

Comments?

-- 
        -- Jason R. Thorpe <thorpej@zembu.com>

Index: sys/mbuf.h
===================================================================
RCS file: /cvsroot/syssrc/sys/sys/mbuf.h,v
retrieving revision 1.57
diff -c -r1.57 mbuf.h
*** sys/mbuf.h	2001/04/30 01:13:21	1.57
--- sys/mbuf.h	2001/05/18 16:59:01
***************
*** 1,7 ****
  /*	$NetBSD: mbuf.h,v 1.57 2001/04/30 01:13:21 lukem Exp $	*/
  
  /*-
!  * Copyright (c) 1996, 1997, 1999 The NetBSD Foundation, Inc.
   * All rights reserved.
   *
   * This code is derived from software contributed to The NetBSD Foundation
--- 1,7 ----
  /*	$NetBSD: mbuf.h,v 1.57 2001/04/30 01:13:21 lukem Exp $	*/
  
  /*-
!  * Copyright (c) 1996, 1997, 1999, 2001 The NetBSD Foundation, Inc.
   * All rights reserved.
   *
   * This code is derived from software contributed to The NetBSD Foundation
***************
*** 115,123 ****
--- 115,131 ----
  struct	pkthdr {
  	struct	ifnet *rcvif;		/* rcv interface */
  	int	len;			/* total packet length */
+ 	int	csuminfo;		/* checksum information */
  	struct mbuf *aux;		/* extra data buffer; ipsec/others */
  };
  
+ #define	M_CSUM_IPv4		0x0001	/* IPv4 header */
+ #define	M_CSUM_IPv4_BAD		0x0002	/* IPv4 header checksum bad */
+ #define	M_CSUM_TCPv4		0x0004	/* TCP header/payload */
+ #define	M_CSUM_TCPv4_BAD	0x0008	/* TCP header/payload checksum bad */
+ #define	M_CSUM_UDPv4		0x0010	/* UDP header/payload */
+ #define	M_CSUM_UDPv4_BAD	0x0020	/* UDP header/payload checksum bad */
+ 
  /* description of external storage mapped into mbuf, valid if M_EXT set */
  struct m_ext {
  	caddr_t	ext_buf;		/* start of buffer */
***************
*** 246,251 ****
--- 254,260 ----
  		(m)->m_nextpkt = (struct mbuf *)NULL; \
  		(m)->m_data = (m)->m_pktdat; \
  		(m)->m_flags = M_PKTHDR; \
+ 		(m)->m_pkthdr.csuminfo = 0; \
  		(m)->m_pkthdr.aux = (struct mbuf *)NULL; \
  	} else \
  		(m) = m_retryhdr((how), (type)); \
Index: netinet/ip_input.c
===================================================================
RCS file: /cvsroot/syssrc/sys/netinet/ip_input.c,v
retrieving revision 1.133
diff -c -r1.133 ip_input.c
*** netinet/ip_input.c	2001/04/16 17:03:33	1.133
--- netinet/ip_input.c	2001/05/18 16:59:01
***************
*** 433,439 ****
  		}
  	}
  
! 	if (in_cksum(m, hlen) != 0) {
  		ipstat.ips_badsum++;
  		goto bad;
  	}
--- 433,444 ----
  		}
  	}
  
! 	if (m->m_pkthdr.csuminfo & M_CSUM_IPv4) {
! 		if (m->m_pkthdr.csuminfo & M_CSUM_IPv4_BAD) {
! 			ipstat.ips_badsum++;
! 			goto bad;
! 		}
! 	} else if (in_cksum(m, hlen) != 0) {
  		ipstat.ips_badsum++;
  		goto bad;
  	}
Index: netinet/tcp_input.c
===================================================================
RCS file: /cvsroot/syssrc/sys/netinet/tcp_input.c,v
retrieving revision 1.124
diff -c -r1.124 tcp_input.c
*** netinet/tcp_input.c	2001/05/08 10:15:13	1.124
--- netinet/tcp_input.c	2001/05/18 16:59:02
***************
*** 956,968 ****
  		bzero(ipov->ih_x1, sizeof ipov->ih_x1); 
  		ipov->ih_len = htons(tlen + off);
  
! 		if (in_cksum(m, len) != 0) {
  			tcpstat.tcps_rcvbadsum++;
  			goto drop;
  		}
  	    }
  #else
! 		if (in4_cksum(m, IPPROTO_TCP, toff, tlen + off) != 0) {
  			tcpstat.tcps_rcvbadsum++; 
  			goto drop;
  		}
--- 956,978 ----
  		bzero(ipov->ih_x1, sizeof ipov->ih_x1); 
  		ipov->ih_len = htons(tlen + off);
  
! 		if (m->m_pkthdr.csuminfo & M_CSUM_TCPv4) {
! 			if (m->m_pkthdr.csuminfo & M_CSUM_TCPv4_BAD) {
! 				tcpstat.tcps_rcvbadsum++;
! 				goto drop;
! 			}
! 		} else if (in_cksum(m, len) != 0) {
  			tcpstat.tcps_rcvbadsum++;
  			goto drop;
  		}
  	    }
  #else
! 		if (m->m_pkthdr.csuminfo & M_CSUM_TCPv4) {
! 			if (m->m_pkthdr.csuminfo & M_CSUM_TCPv4_BAD) {
! 				tcpstat.tcps_rcvbadsum++;
! 				goto drop;
! 			}
! 		} else if (in4_cksum(m, IPPROTO_TCP, toff, tlen + off) != 0) {
  			tcpstat.tcps_rcvbadsum++; 
  			goto drop;
  		}
Index: netinet/udp_usrreq.c
===================================================================
RCS file: /cvsroot/syssrc/sys/netinet/udp_usrreq.c,v
retrieving revision 1.76
diff -c -r1.76 udp_usrreq.c
*** netinet/udp_usrreq.c	2001/05/08 10:15:14	1.76
--- netinet/udp_usrreq.c	2001/05/18 16:59:02
***************
*** 254,260 ****
  	 * Checksum extended UDP header and data.
  	 */
  	if (uh->uh_sum) {
! 		if (in4_cksum(m, IPPROTO_UDP, iphlen, len) != 0) {
  			udpstat.udps_badsum++;
  			m_freem(m);
  			return;
--- 254,266 ----
  	 * Checksum extended UDP header and data.
  	 */
  	if (uh->uh_sum) {
! 		if (m->m_pkthdr.csuminfo & M_CSUM_UDPv4) {
! 			if (m->m_pkthdr.csuminfo & M_CSUM_UDPv4_BAD) {
! 				udpstat.udps_badsum++;
! 				m_freem(m);
! 				return;
! 			}
! 		} else if (in4_cksum(m, IPPROTO_UDP, iphlen, len) != 0) {
  			udpstat.udps_badsum++;
  			m_freem(m);
  			return;
***************
*** 945,957 ****
  	 * Checksum extended UDP header and data.
  	 */
  	if (uh->uh_sum) {
! 		bzero(((struct ipovly *)ip)->ih_x1,
! 		    sizeof ((struct ipovly *)ip)->ih_x1);
! 		((struct ipovly *)ip)->ih_len = uh->uh_ulen;
! 		if (in_cksum(m, len + sizeof (struct ip)) != 0) {
! 			udpstat.udps_badsum++;
! 			m_freem(m);
! 			return;
  		}
  	}
  
--- 951,971 ----
  	 * Checksum extended UDP header and data.
  	 */
  	if (uh->uh_sum) {
! 		if (m->m_pkthdr.csuminfo & M_CSUM_UDPv4) {
! 			if (m->m_pkthdr.csuminfo & M_CSUM_UDPv4_BAD) {
! 				udpstat.udps_badsum++;
! 				m_freem(m);
! 				return;
! 			}
! 		} else {
! 			bzero(((struct ipovly *)ip)->ih_x1,
! 			    sizeof ((struct ipovly *)ip)->ih_x1);
! 			((struct ipovly *)ip)->ih_len = uh->uh_ulen;
! 			if (in_cksum(m, len + sizeof (struct ip)) != 0) {
! 				udpstat.udps_badsum++;
! 				m_freem(m);
! 				return;
! 			}
  		}
  	}
  
Index: dev/pci/if_sip.c
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/pci/if_sip.c,v
retrieving revision 1.30
diff -c -r1.30 if_sip.c
*** dev/pci/if_sip.c	2001/05/18 04:38:30	1.30
--- dev/pci/if_sip.c	2001/05/18 16:59:03
***************
*** 1,3 ****
--- 1,5 ----
+ #define	SIP_EVENT_COUNTERS
+ #define	SIP_HWCSUM
  /*	$NetBSD: if_sip.c,v 1.30 2001/05/18 04:38:30 thorpej Exp $	*/
  
  /*-
***************
*** 250,255 ****
--- 252,260 ----
  	struct evcnt sc_ev_txdstall;	/* Tx stalled due to no txd */
  	struct evcnt sc_ev_txintr;	/* Tx interrupts */
  	struct evcnt sc_ev_rxintr;	/* Rx interrupts */
+ 	struct evcnt sc_ev_rxipsum;	/* IP checksums checked in-bound */
+ 	struct evcnt sc_ev_rxtcpsum;	/* TCP checksums checked in-bound */
+ 	struct evcnt sc_ev_rxudpsum;	/* UDP checksums checked in-boudn */
  #endif /* SIP_EVENT_COUNTERS */
  
  	u_int32_t sc_txcfg;		/* prototype TXCFG register */
***************
*** 812,817 ****
--- 817,828 ----
  	    NULL, sc->sc_dev.dv_xname, "txintr");
  	evcnt_attach_dynamic(&sc->sc_ev_rxintr, EVCNT_TYPE_INTR,
  	    NULL, sc->sc_dev.dv_xname, "rxintr");
+ 	evcnt_attach_dynamic(&sc->sc_ev_rxipsum, EVCNT_TYPE_MISC,
+ 	    NULL, sc->sc_dev.dv_xname, "rxipsum");
+ 	evcnt_attach_dynamic(&sc->sc_ev_rxtcpsum, EVCNT_TYPE_MISC,
+ 	    NULL, sc->sc_dev.dv_xname, "rxtcpsum");
+ 	evcnt_attach_dynamic(&sc->sc_ev_rxudpsum, EVCNT_TYPE_MISC,
+ 	    NULL, sc->sc_dev.dv_xname, "rxudpsum");
  #endif /* SIP_EVENT_COUNTERS */
  
  	/*
***************
*** 1547,1552 ****
--- 1558,1589 ----
  			*mtod(vtag, int *) = ntohs(extsts & EXTSTS_VTCI);
  			vtag->m_len = sizeof(int);
  		}
+ 
+ #ifdef SIP_HWCSUM
+ 		/*
+ 		 * Set the incoming checksum information for the
+ 		 * packet.
+ 		 */
+ 		if (extsts & EXTSTS_IPPKT) {
+ 			SIP_EVCNT_INCR(&sc->sc_ev_rxipsum);
+ 			m->m_pkthdr.csuminfo |= M_CSUM_IPv4;
+ 			if (extsts & EXTSTS_Rx_IPERR)
+ 				m->m_pkthdr.csuminfo |= M_CSUM_IPv4_BAD;
+ 			if (extsts & EXTSTS_TCPPKT) {
+ 				SIP_EVCNT_INCR(&sc->sc_ev_rxtcpsum);
+ 				m->m_pkthdr.csuminfo |= M_CSUM_TCPv4;
+ 				if (extsts & EXTSTS_Rx_TCPERR)
+ 					m->m_pkthdr.csuminfo |=
+ 					    M_CSUM_TCPv4_BAD;
+ 			} else if (extsts & EXTSTS_UDPPKT) {
+ 				SIP_EVCNT_INCR(&sc->sc_ev_rxtcpsum);
+ 				m->m_pkthdr.csuminfo |= M_CSUM_UDPv4;
+ 				if (extsts & EXTSTS_Rx_UDPERR)
+ 					m->m_pkthdr.csuminfo |=
+ 					    M_CSUM_UDPv4_BAD;
+ 			}
+ 		}
+ #endif /* SIP_HWCSUM */
  #endif /* DP83820 */
  
  		/* Pass it on. */
***************
*** 1783,1798 ****
--- 1820,1847 ----
  #ifdef DP83820
  	/*
  	 * Initialize the VLAN/IP receive control register.
+ 	 * We enable checksum computation on all incoming
+ 	 * packets, and do not reject packets w/ bad checksums.
  	 */
+ #ifdef SIP_HWCSUM
+ 	reg = VRCR_IPEN;
+ #else
  	reg = 0;
+ #endif
  	if (sc->sc_ethercom.ec_nvlans != 0)
  		reg |= VRCR_VTDEN|VRCR_VTREN;
  	bus_space_write_4(st, sh, SIP_VRCR, reg);
  
  	/*
  	 * Initialize the VLAN/IP transmit control register.
+ 	 * We enable outgoing checksum computation on a
+ 	 * per-packet basis.
  	 */
+ #ifdef SIP_HWCSUM
+ 	reg = VTCR_PPCHK;
+ #else
  	reg = 0;
+ #endif
  	if (sc->sc_ethercom.ec_nvlans != 0)
  		reg |= VTCR_VPPTI;
  	bus_space_write_4(st, sh, SIP_VTCR, reg);