Subject: ipv6 TSO
To: None <tech-net@netbsd.org>
From: YAMAMOTO Takashi <yamt@mwd.biglobe.ne.jp>
List: tech-net
Date: 11/17/2006 20:51:37
--NextPart-20061117204353-2440401
Content-Type: Text/Plain; charset=us-ascii
the attached patches implement ipv6 TSO for wm(4).
(partly from Matthias Scheler.)
please review and/or test.
YAMAMOTO Takashi
--NextPart-20061117204353-2440401
Content-Type: Text/Plain; charset=us-ascii
Content-Disposition: attachment; filename="1.diff"
Index: sys/mbuf.h
===================================================================
--- sys/mbuf.h (revision 1830)
+++ sys/mbuf.h (working copy)
@@ -176,6 +176,7 @@ struct pkthdr {
#define M_CSUM_IPv4 0x00000040 /* IPv4 header */
#define M_CSUM_IPv4_BAD 0x00000080 /* IPv4 header checksum bad */
#define M_CSUM_TSOv4 0x00000100 /* TCPv4 segmentation offload */
+#define M_CSUM_TSOv6 0x00000200 /* TCPv6 segmentation offload */
/* Checksum-assist quirks: keep separate from jump-table bits. */
#define M_CSUM_NO_PSEUDOHDR 0x80000000 /* Rx csum_data does not include
@@ -185,7 +186,7 @@ struct pkthdr {
#define M_CSUM_BITS \
"\20\1TCPv4\2UDPv4\3TCP_UDP_BAD\4DATA\5TCPv6\6UDPv6\7IPv4\10IPv4_BAD" \
- "\11TSOv4\38NO_PSEUDOHDR"
+ "\11TSOv4\12TSOv6\38NO_PSEUDOHDR"
/*
* Macros for manipulating csum_data on outgoing packets. These are
Index: net/if.h
===================================================================
--- net/if.h (revision 1897)
+++ net/if.h (working copy)
@@ -364,6 +364,7 @@ struct ifnet { /* and the entries */
#define IFCAP_CSUM_TCPv6_Tx 0x08000 /* can do IPv6/TCP checksums (Tx) */
#define IFCAP_CSUM_UDPv6_Rx 0x10000 /* can do IPv6/UDP checksums (Rx) */
#define IFCAP_CSUM_UDPv6_Tx 0x20000 /* can do IPv6/UDP checksums (Tx) */
+#define IFCAP_TSOv6 0x40000 /* can do TCPv6 segmentation offload */
#define IFCAPBITS \
"\020" \
@@ -377,7 +378,8 @@ struct ifnet { /* and the entries */
"\17TCP6CSUM_Rx" \
"\20TCP6CSUM_Tx" \
"\21UDP6CSUM_Rx" \
- "\22UDP6CSUM_Tx"
+ "\22UDP6CSUM_Tx" \
+ "\23TSO6"
/*
* Output queues (ifp->if_snd) and internetwork datagram level (pup level 1)
Index: netinet/tcp_output.c
===================================================================
--- netinet/tcp_output.c (revision 1850)
+++ netinet/tcp_output.c (working copy)
@@ -200,6 +200,7 @@ __KERNEL_RCSID(0, "$NetBSD: tcp_output.c
#include <netinet/tcpip.h>
#include <netinet/tcp_debug.h>
#include <netinet/in_offload.h>
+#include <netinet6/in6_offload.h>
#ifdef IPSEC
#include <netkey/key.h>
@@ -239,7 +240,8 @@ static
inline
#endif
int
-tcp_segsize(struct tcpcb *tp, int *txsegsizep, int *rxsegsizep)
+tcp_segsize(struct tcpcb *tp, int *txsegsizep, int *rxsegsizep,
+ boolean_t *alwaysfragp)
{
#ifdef INET
struct inpcb *inp = tp->t_inpcb;
@@ -254,6 +256,8 @@ tcp_segsize(struct tcpcb *tp, int *txseg
int hdrlen;
int optlen;
+ *alwaysfragp = FALSE;
+
#ifdef DIAGNOSTIC
if (tp->t_inpcb && tp->t_in6pcb)
panic("tcp_segsize: both t_inpcb and t_in6pcb are set");
@@ -304,6 +308,7 @@ tcp_segsize(struct tcpcb *tp, int *txseg
* attach fragment header.
*/
size = IPV6_MMTU - hdrlen - sizeof(struct ip6_frag);
+ *alwaysfragp = TRUE;
} else
size = rt->rt_rmx.rmx_mtu - hdrlen;
#else
@@ -558,7 +563,9 @@ tcp_output(struct tcpcb *tp)
int maxburst = TCP_MAXBURST;
int af; /* address family on the wire */
int iphdrlen;
+ int has_tso4, has_tso6;
int has_tso, use_tso;
+ boolean_t alwaysfrag;
int sack_rxmit;
int sack_bytes_rxmt;
struct sackhole *p;
@@ -605,7 +612,7 @@ tcp_output(struct tcpcb *tp)
return (EAFNOSUPPORT);
}
- if (tcp_segsize(tp, &txsegsize, &rxsegsize))
+ if (tcp_segsize(tp, &txsegsize, &rxsegsize, &alwaysfrag))
return (EMSGSIZE);
idle = (tp->snd_max == tp->snd_una);
@@ -616,7 +623,9 @@ tcp_output(struct tcpcb *tp)
* - If there is not an IPsec policy that prevents it
* - If the interface can do it
*/
- has_tso = tp->t_inpcb != NULL &&
+ has_tso4 = has_tso6 = FALSE;
+#if defined(INET)
+ has_tso4 = tp->t_inpcb != NULL &&
#if defined(IPSEC) || defined(FAST_IPSEC)
IPSEC_PCB_SKIP_IPSEC(tp->t_inpcb->inp_sp,
IPSEC_DIR_OUTBOUND) &&
@@ -624,6 +633,18 @@ tcp_output(struct tcpcb *tp)
tp->t_inpcb->inp_route.ro_rt != NULL &&
(tp->t_inpcb->inp_route.ro_rt->rt_ifp->if_capenable &
IFCAP_TSOv4) != 0;
+#endif /* defined(INET) */
+#if defined(INET6)
+ has_tso6 = tp->t_in6pcb != NULL &&
+#if defined(IPSEC) || defined(FAST_IPSEC)
+ IPSEC_PCB_SKIP_IPSEC(tp->t_in6pcb->in6p_sp,
+ IPSEC_DIR_OUTBOUND) &&
+#endif
+ tp->t_in6pcb->in6p_route.ro_rt != NULL &&
+ (tp->t_in6pcb->in6p_route.ro_rt->rt_ifp->if_capenable &
+ IFCAP_TSOv6) != 0;
+#endif /* defined(INET6) */
+ has_tso = (has_tso4 || has_tso6) && !alwaysfrag;
/*
* Restart Window computation. From draft-floyd-incr-init-win-03:
@@ -889,6 +910,9 @@ again:
* sure that we send equal size transfers down the
* stack (rather than big-small-big-small-...).
*/
+#if IPV6_MAXPACKET != IP_MAXPACKET
+#error IPV6_MAXPACKET != IP_MAXPACKET
+#endif
len = (min(len, IP_MAXPACKET) / txsegsize) * txsegsize;
if (len <= txsegsize) {
use_tso = 0;
@@ -1405,12 +1429,17 @@ send:
#ifdef INET6
case AF_INET6:
m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
- m->m_pkthdr.csum_flags = M_CSUM_TCPv6;
- if (len + optlen) {
- /* Fixup the pseudo-header checksum. */
- /* XXXJRT: Not IPv6 Jumbogram safe. */
- th->th_sum = in_cksum_addword(th->th_sum,
- htons((u_int16_t) (len + optlen)));
+ if (use_tso) {
+ m->m_pkthdr.segsz = txsegsize;
+ m->m_pkthdr.csum_flags = M_CSUM_TSOv6;
+ } else {
+ m->m_pkthdr.csum_flags = M_CSUM_TCPv6;
+ if (len + optlen) {
+ /* Fixup the pseudo-header checksum. */
+ /* XXXJRT: Not IPv6 Jumbogram safe. */
+ th->th_sum = in_cksum_addword(th->th_sum,
+ htons((u_int16_t) (len + optlen)));
+ }
}
break;
#endif
@@ -1764,3 +1793,124 @@ quit:
return error;
}
#endif /* defined(INET) */
+
+#if defined(INET6)
+/*
+ * tcp6_segment: handle M_CSUM_TSOv6 by software.
+ *
+ * => always consume m.
+ * => call output_func with output_arg for each segments.
+ */
+
+int
+tcp6_segment(struct mbuf *m, int (*output_func)(void *, struct mbuf *),
+ void *output_arg)
+{
+ int mss;
+ int iphlen;
+ int thlen;
+ int hlen;
+ int len;
+ struct ip6_hdr *iph;
+ struct tcphdr *th;
+ uint32_t tcpseq;
+ struct mbuf *hdr = NULL;
+ struct mbuf *t;
+ int error = 0;
+
+ KASSERT((m->m_flags & M_PKTHDR) != 0);
+ KASSERT((m->m_pkthdr.csum_flags & M_CSUM_TSOv6) != 0);
+
+ m->m_pkthdr.csum_flags = 0;
+
+ len = m->m_pkthdr.len;
+ KASSERT(len >= sizeof(*iph) + sizeof(*th));
+
+ if (m->m_len < sizeof(*iph)) {
+ m = m_pullup(m, sizeof(*iph));
+ if (m == NULL) {
+ error = ENOMEM;
+ goto quit;
+ }
+ }
+ iph = mtod(m, struct ip6_hdr *);
+ iphlen = sizeof(*iph);
+ KASSERT((iph->ip6_vfc & IPV6_VERSION_MASK) == IPV6_VERSION);
+ KASSERT(iph->ip6_nxt == IPPROTO_TCP);
+
+ hlen = iphlen + sizeof(*th);
+ if (m->m_len < hlen) {
+ m = m_pullup(m, hlen);
+ if (m == NULL) {
+ error = ENOMEM;
+ goto quit;
+ }
+ }
+ th = (void *)(mtod(m, char *) + iphlen);
+ tcpseq = ntohl(th->th_seq);
+ thlen = th->th_off * 4;
+ hlen = iphlen + thlen;
+
+ mss = m->m_pkthdr.segsz;
+ KASSERT(mss != 0);
+ KASSERT(len > hlen);
+
+ t = m_split(m, hlen, M_NOWAIT);
+ if (t == NULL) {
+ error = ENOMEM;
+ goto quit;
+ }
+ hdr = m;
+ m = t;
+ len -= hlen;
+ KASSERT(len % mss == 0);
+ while (len > 0) {
+ struct mbuf *n;
+
+ n = m_dup(hdr, 0, hlen, M_NOWAIT);
+ if (n == NULL) {
+ error = ENOMEM;
+ goto quit;
+ }
+ KASSERT(n->m_len == hlen); /* XXX */
+
+ t = m_split(m, mss, M_NOWAIT);
+ if (t == NULL) {
+ m_freem(n);
+ error = ENOMEM;
+ goto quit;
+ }
+ m_cat(n, m);
+ m = t;
+
+ KASSERT(n->m_len >= hlen); /* XXX */
+
+ n->m_pkthdr.len = hlen + mss;
+ iph = mtod(n, struct ip6_hdr *);
+ KASSERT((iph->ip6_vfc & IPV6_VERSION_MASK) == IPV6_VERSION);
+ iph->ip6_plen = htons(thlen + mss);
+ th = (void *)(mtod(n, char *) + iphlen);
+ th->th_seq = htonl(tcpseq);
+ th->th_sum = 0;
+ th->th_sum = in6_cksum(n, IPPROTO_TCP, iphlen, thlen + mss);
+
+ error = (*output_func)(output_arg, n);
+ if (error) {
+ goto quit;
+ }
+
+ tcpseq += mss;
+ len -= mss;
+ }
+
+quit:
+ if (hdr != NULL) {
+ m_freem(hdr);
+ }
+ if (m != NULL) {
+ m_freem(m);
+ }
+
+ return error;
+}
+#endif /* defined(INET6) */
Index: netinet6/ip6_output.c
===================================================================
--- netinet6/ip6_output.c (revision 1830)
+++ netinet6/ip6_output.c (working copy)
@@ -91,6 +91,7 @@ __KERNEL_RCSID(0, "$NetBSD: ip6_output.c
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/in_offload.h>
+#include <netinet6/in6_offload.h>
#include <netinet6/ip6_var.h>
#include <netinet6/in6_pcb.h>
#include <netinet6/nd6.h>
@@ -142,6 +143,39 @@ static int ip6_pcbopts __P((struct ip6_p
(((csum_flags) & M_CSUM_UDPv6) != 0 && udp_do_loopback_cksum) || \
(((csum_flags) & M_CSUM_TCPv6) != 0 && tcp_do_loopback_cksum)))
+struct ip6_tso_output_args {
+ struct ifnet *ifp;
+ struct ifnet *origifp;
+ struct sockaddr_in6 *dst;
+ struct rtentry *rt;
+};
+
+static int ip6_tso_output_callback(void *, struct mbuf *);
+static int ip6_tso_output(struct ifnet *, struct ifnet *, struct mbuf *,
+ struct sockaddr_in6 *, struct rtentry *);
+
+static int
+ip6_tso_output_callback(void *vp, struct mbuf *m)
+{
+ struct ip6_tso_output_args *args = vp;
+
+ return nd6_output(args->ifp, args->origifp, m, args->dst, args->rt);
+}
+
+static int
+ip6_tso_output(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m,
+ struct sockaddr_in6 *dst, struct rtentry *rt)
+{
+ struct ip6_tso_output_args args;
+
+ args.ifp = ifp;
+ args.origifp = origifp;
+ args.dst = dst;
+ args.rt = rt;
+
+ return tcp6_segment(m, ip6_tso_output_callback, &args);
+}
+
/*
* IP6 output. The packet in mbuf chain m contains a skeletal IP6
* header (with pri, len, nxt, hlim, src, dst).
@@ -168,6 +202,7 @@ ip6_output(
struct ifnet *ifp, *origifp;
struct mbuf *m = m0;
int hlen, tlen, len, off;
+ boolean_t tso;
struct route_in6 ip6route;
struct rtentry *rt = NULL;
struct sockaddr_in6 *dst, src_sa, dst_sa;
@@ -882,7 +917,7 @@ skip_ipsec2:;
* error, as we cannot handle this conflicting request
*/
tlen = m->m_pkthdr.len;
-
+ tso = (m->m_pkthdr.csum_flags & M_CSUM_TSOv6) != 0;
if (opt && (opt->ip6po_flags & IP6PO_DONTFRAG))
dontfrag = 1;
else
@@ -893,7 +928,7 @@ skip_ipsec2:;
error = EMSGSIZE;
goto bad;
}
- if (dontfrag && tlen > IN6_LINKMTU(ifp)) { /* case 2-b */
+ if (dontfrag && (!tso && tlen > IN6_LINKMTU(ifp))) { /* case 2-b */
/*
* Even if the DONTFRAG option is specified, we cannot send the
* packet when the data length is larger than the MTU of the
@@ -918,7 +953,8 @@ skip_ipsec2:;
/*
* transmit packet without fragmentation
*/
- if (dontfrag || (!alwaysfrag && tlen <= mtu)) { /* case 1-a and 2-a */
+ if (dontfrag || (!alwaysfrag && (tlen <= mtu || tso))) {
+ /* case 1-a and 2-a */
struct in6_ifaddr *ia6;
int sw_csum;
@@ -942,10 +978,20 @@ skip_ipsec2:;
m->m_pkthdr.csum_flags &= ~(M_CSUM_UDPv6|M_CSUM_TCPv6);
}
- error = nd6_output(ifp, origifp, m, dst, rt);
+ if (__predict_true(!tso ||
+ (ifp->if_capenable & IFCAP_TSOv6) != 0)) {
+ error = nd6_output(ifp, origifp, m, dst, rt);
+ } else {
+ error = ip6_tso_output(ifp, origifp, m, dst, rt);
+ }
goto done;
}
+ if (tso) {
+ error = EINVAL; /* XXX */
+ goto bad;
+ }
+
/*
* try to fragment the packet. case 1-b and 3
*/
Index: netinet6/in6_offload.h
===================================================================
--- netinet6/in6_offload.h (revision 0)
+++ netinet6/in6_offload.h (revision 0)
@@ -0,0 +1,38 @@
+/* $NetBSD$ */
+
+/*-
+ * Copyright (c)2005 YAMAMOTO Takashi,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * subroutines to do software-only equivalent of h/w offloading.
+ */
+
+#if !defined(_NETINET6_IN6_OFFLOAD_H_)
+#define _NETINET6_IN6_OFFLOAD_H_
+
+int tcp6_segment(struct mbuf *, int (*)(void *, struct mbuf *), void *);
+
+#endif /* !defined(_NETINET6_IN6_OFFLOAD_H_) */
--NextPart-20061117204353-2440401
Content-Type: Text/Plain; charset=us-ascii
Content-Disposition: attachment; filename="2.diff"
Index: dev/pci/if_wm.c
===================================================================
--- dev/pci/if_wm.c (revision 1900)
+++ dev/pci/if_wm.c (working copy)
@@ -83,6 +83,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.
#include <netinet/in.h> /* XXX for struct ip */
#include <netinet/in_systm.h> /* XXX for struct ip */
#include <netinet/ip.h> /* XXX for struct ip */
+#include <netinet/ip6.h> /* XXX for struct ip6_hdr */
#include <netinet/tcp.h> /* XXX for struct tcphdr */
#include <machine/bus.h>
@@ -286,7 +287,8 @@ struct wm_softc {
struct evcnt sc_ev_txipsum; /* IP checksums comp. out-bound */
struct evcnt sc_ev_txtusum; /* TCP/UDP cksums comp. out-bound */
struct evcnt sc_ev_txtusum6; /* TCP/UDP v6 cksums comp. out-bound */
- struct evcnt sc_ev_txtso; /* TCP seg offload out-bound */
+ struct evcnt sc_ev_txtso; /* TCP seg offload out-bound (IPv4) */
+ struct evcnt sc_ev_txtso6; /* TCP seg offload out-bound (IPv6) */
struct evcnt sc_ev_txtsopain; /* painful header manip. for TSO */
struct evcnt sc_ev_txseg[WM_NTXSEGS]; /* Tx packets w/ N segments */
@@ -1419,8 +1421,13 @@ wm_attach(struct device *parent __unused
* If we're a i82544 or greater (except i82547), we can do
* TCP segmentation offload.
*/
- if (sc->sc_type >= WM_T_82544 && sc->sc_type != WM_T_82547)
+ if (sc->sc_type >= WM_T_82544 && sc->sc_type != WM_T_82547) {
ifp->if_capabilities |= IFCAP_TSOv4;
+ }
+
+ if (sc->sc_type >= WM_T_82571) {
+ ifp->if_capabilities |= IFCAP_TSOv6;
+ }
/*
* Attach the interface.
@@ -1462,6 +1469,8 @@ wm_attach(struct device *parent __unused
evcnt_attach_dynamic(&sc->sc_ev_txtso, EVCNT_TYPE_MISC,
NULL, sc->sc_dev.dv_xname, "txtso");
+ evcnt_attach_dynamic(&sc->sc_ev_txtso6, EVCNT_TYPE_MISC,
+ NULL, sc->sc_dev.dv_xname, "txtso6");
evcnt_attach_dynamic(&sc->sc_ev_txtsopain, EVCNT_TYPE_MISC,
NULL, sc->sc_dev.dv_xname, "txtsopain");
@@ -1587,6 +1596,7 @@ wm_tx_offload(struct wm_softc *sc, struc
struct mbuf *m0 = txs->txs_mbuf;
struct livengood_tcpip_ctxdesc *t;
uint32_t ipcs, tucs, cmd, cmdlen, seg;
+ uint32_t ipcse;
struct ether_header *eh;
int offset, iphl;
uint8_t fields;
@@ -1622,15 +1632,17 @@ wm_tx_offload(struct wm_softc *sc, struc
} else {
iphl = M_CSUM_DATA_IPv6_HL(m0->m_pkthdr.csum_data);
}
+ ipcse = offset + iphl - 1;
cmd = WTX_CMD_DEXT | WTX_DTYP_D;
cmdlen = WTX_CMD_DEXT | WTX_DTYP_C | WTX_CMD_IDE;
seg = 0;
fields = 0;
- if (m0->m_pkthdr.csum_flags & M_CSUM_TSOv4) {
+ if ((m0->m_pkthdr.csum_flags & (M_CSUM_TSOv4 | M_CSUM_TSOv6)) != 0) {
int hlen = offset + iphl;
- WM_EVCNT_INCR(&sc->sc_ev_txtso);
+ boolean_t v4 = (m0->m_pkthdr.csum_flags & M_CSUM_TSOv4) != 0;
+
if (__predict_false(m0->m_len <
(hlen + sizeof(struct tcphdr)))) {
/*
@@ -1638,22 +1650,32 @@ wm_tx_offload(struct wm_softc *sc, struc
* to do this the slow and painful way. Let's just
* hope this doesn't happen very often.
*/
- struct ip ip;
struct tcphdr th;
WM_EVCNT_INCR(&sc->sc_ev_txtsopain);
- m_copydata(m0, offset, sizeof(ip), &ip);
m_copydata(m0, hlen, sizeof(th), &th);
+ if (v4) {
+ struct ip ip;
- ip.ip_len = 0;
-
- m_copyback(m0, offset + offsetof(struct ip, ip_len),
- sizeof(ip.ip_len), &ip.ip_len);
-
- th.th_sum = in_cksum_phdr(ip.ip_src.s_addr,
- ip.ip_dst.s_addr, htons(IPPROTO_TCP));
+ m_copydata(m0, offset, sizeof(ip), &ip);
+ ip.ip_len = 0;
+ m_copyback(m0,
+ offset + offsetof(struct ip, ip_len),
+ sizeof(ip.ip_len), &ip.ip_len);
+ th.th_sum = in_cksum_phdr(ip.ip_src.s_addr,
+ ip.ip_dst.s_addr, htons(IPPROTO_TCP));
+ } else {
+ struct ip6_hdr ip6;
+ m_copydata(m0, offset, sizeof(ip6), &ip6);
+ ip6.ip6_plen = 0;
+ m_copyback(m0,
+ offset + offsetof(struct ip6_hdr, ip6_plen),
+ sizeof(ip6.ip6_plen), &ip6.ip6_plen);
+ th.th_sum = in6_cksum_phdr(&ip6.ip6_src,
+ &ip6.ip6_dst, 0, htonl(IPPROTO_TCP));
+ }
m_copyback(m0, hlen + offsetof(struct tcphdr, th_sum),
sizeof(th.th_sum), &th.th_sum);
@@ -1663,20 +1685,37 @@ wm_tx_offload(struct wm_softc *sc, struc
* TCP/IP headers are in the first mbuf; we can do
* this the easy way.
*/
- struct ip *ip =
- (struct ip *) (mtod(m0, caddr_t) + offset);
- struct tcphdr *th =
- (struct tcphdr *) (mtod(m0, caddr_t) + hlen);
+ struct tcphdr *th;
- ip->ip_len = 0;
- th->th_sum = in_cksum_phdr(ip->ip_src.s_addr,
- ip->ip_dst.s_addr, htons(IPPROTO_TCP));
+ if (v4) {
+ struct ip *ip =
+ (void *)(mtod(m0, caddr_t) + offset);
+ th = (void *)(mtod(m0, caddr_t) + hlen);
+ ip->ip_len = 0;
+ th->th_sum = in_cksum_phdr(ip->ip_src.s_addr,
+ ip->ip_dst.s_addr, htons(IPPROTO_TCP));
+ } else {
+ struct ip6_hdr *ip6 =
+ (void *)(mtod(m0, char *) + offset);
+ th = (void *)(mtod(m0, char *) + hlen);
+
+ ip6->ip6_plen = 0;
+ th->th_sum = in6_cksum_phdr(&ip6->ip6_src,
+ &ip6->ip6_dst, 0, htonl(IPPROTO_TCP));
+ }
hlen += th->th_off << 2;
}
+ if (v4) {
+ WM_EVCNT_INCR(&sc->sc_ev_txtso);
+ cmdlen |= WTX_TCPIP_CMD_IP;
+ } else {
+ WM_EVCNT_INCR(&sc->sc_ev_txtso6);
+ ipcse = 0;
+ }
cmd |= WTX_TCPIP_CMD_TSE;
- cmdlen |= WTX_TCPIP_CMD_TSE | WTX_TCPIP_CMD_IP |
+ cmdlen |= WTX_TCPIP_CMD_TSE |
WTX_TCPIP_CMD_TCP | (m0->m_pkthdr.len - hlen);
seg = WTX_TCPIP_SEG_HDRLEN(hlen) |
WTX_TCPIP_SEG_MSS(m0->m_pkthdr.segsz);
@@ -1690,7 +1729,7 @@ wm_tx_offload(struct wm_softc *sc, struc
ipcs = WTX_TCPIP_IPCSS(offset) |
WTX_TCPIP_IPCSO(offset + offsetof(struct ip, ip_sum)) |
- WTX_TCPIP_IPCSE(offset + iphl - 1);
+ WTX_TCPIP_IPCSE(ipcse);
if (m0->m_pkthdr.csum_flags & (M_CSUM_IPv4|M_CSUM_TSOv4)) {
WM_EVCNT_INCR(&sc->sc_ev_txipsum);
fields |= WTX_IXSM;
@@ -1707,7 +1746,7 @@ wm_tx_offload(struct wm_softc *sc, struc
M_CSUM_DATA_IPv4_OFFSET(m0->m_pkthdr.csum_data)) |
WTX_TCPIP_TUCSE(0) /* rest of packet */;
} else if ((m0->m_pkthdr.csum_flags &
- (M_CSUM_TCPv6|M_CSUM_UDPv6)) != 0) {
+ (M_CSUM_TCPv6|M_CSUM_UDPv6|M_CSUM_TSOv6)) != 0) {
WM_EVCNT_INCR(&sc->sc_ev_txtusum6);
fields |= WTX_TXSM;
tucs = WTX_TCPIP_TUCSS(offset) |
@@ -1904,7 +1943,8 @@ wm_start(struct ifnet *ifp)
txs = &sc->sc_txsoft[sc->sc_txsnext];
dmamap = txs->txs_dmamap;
- use_tso = (m0->m_pkthdr.csum_flags & M_CSUM_TSOv4) != 0;
+ use_tso = (m0->m_pkthdr.csum_flags &
+ (M_CSUM_TSOv4 | M_CSUM_TSOv6)) != 0;
/*
* So says the Linux driver:
@@ -2024,7 +2064,8 @@ wm_start(struct ifnet *ifp)
/* Set up offload parameters for this packet. */
if (m0->m_pkthdr.csum_flags &
- (M_CSUM_IPv4|M_CSUM_TCPv4|M_CSUM_UDPv4|
+ (M_CSUM_TSOv4|M_CSUM_TSOv6|
+ M_CSUM_IPv4|M_CSUM_TCPv4|M_CSUM_UDPv4|
M_CSUM_TCPv6|M_CSUM_UDPv6)) {
if (wm_tx_offload(sc, txs, &cksumcmd,
&cksumfields) != 0) {
--NextPart-20061117204353-2440401
Content-Type: Text/Plain; charset=us-ascii
Content-Disposition: attachment; filename="3.diff"
Index: ifconfig.8
===================================================================
RCS file: /cvsroot/src/sbin/ifconfig/ifconfig.8,v
retrieving revision 1.86
diff -u -p -r1.86 ifconfig.8
--- ifconfig.8 16 Sep 2006 20:15:49 -0000 1.86
+++ ifconfig.8 17 Nov 2006 11:43:18 -0000
@@ -29,7 +29,7 @@
.\"
.\" @(#)ifconfig.8 8.4 (Berkeley) 6/1/94
.\"
-.Dd April 29, 2006
+.Dd June 21, 2006
.Dt IFCONFIG 8
.Os
.Sh NAME
@@ -675,6 +675,12 @@ support it.
.It Cm -tso4
Disable hardware-assisted TCP/IPv4 segmentation on interfaces that
support it.
+.It Cm tso6
+Enable hardware-assisted TCP/IPv6 segmentation on interfaces that
+support it.
+.It Cm -tso6
+Disable hardware-assisted TCP/IPv6 segmentation on interfaces that
+support it.
.El
.Pp
.Nm
Index: ifconfig.c
===================================================================
RCS file: /cvsroot/src/sbin/ifconfig/ifconfig.c,v
retrieving revision 1.178
diff -u -p -r1.178 ifconfig.c
--- ifconfig.c 13 Nov 2006 05:13:39 -0000 1.178
+++ ifconfig.c 17 Nov 2006 11:43:19 -0000
@@ -329,6 +329,8 @@ const struct cmd {
0, setifcaps },
{ "tso4", IFCAP_TSOv4, 0, setifcaps },
{ "-tso4", -IFCAP_TSOv4, 0, setifcaps },
+ { "tso6", IFCAP_TSOv6, 0, setifcaps },
+ { "-tso6", -IFCAP_TSOv6, 0, setifcaps },
{ "agrport", NEXTARG, 0, agraddport } ,
{ "-agrport", NEXTARG, 0, agrremport } ,
{ 0, 0, 0, setifaddr },
--NextPart-20061117204353-2440401--