It is implemented mainly according to NetBSD's TSO implementation.
Following stuffs are only in DragonFly
- Add comment about devices' expected behaviour upon PUSH and FIN flags
Obtained-from: Microsoft's LSO online document
- Don't use TSO, if there are SACK or DSACK blocks to report
- Don't use TSO, if congestion window needs validation
- Don't use TSO, if URG flag is to be set
- Take IP and TCP header sizes into consideration when calculate the
large TCP segment size
- Pseudo checksum for the large TCP segment is calculated using only
source address, destination address and IPPROTO_TCP according to
Microsoft's LSO online document. This fashion of pseudo checksum
calculation seems to be adopted by several NIC chips.
Several driver helper functions are added:
- tcp_tso_pullup(), which extracts IPv4 and TCP header's location and
length. And make sure that IPv4 and TCP headers are in contiguous
memory.
- ether_tso_pullup(), in addition to what tcp_tso_pullup() does, it
also extracts ethernet header's length and make sure that ethernet,
IPv4 and TCP headers are in contiguous memory.
Sysctl node net.inet.tcp.tso could be used to globally disable TSO.
TSO is by default on.
tso/-tso are added to ifconfig(8), which could be used to enable or
disable TSO on the specific interface.
If the driver supports user-configurable checksum offloading,
disable receive (or transmit) checksum offloading on the interface.
These settings may not always be independent of each other.
+.It Cm tso
+If the driver supports TCP segmentation offloading,
+enable TCP segmentation offloading on the interface.
+.It Fl tso
+If the driver supports TCP segmentation offloading,
+disable TCP segmentation offloading on the interface.
.It Cm vlanmtu , vlanhwtag
If the driver offers user-configurable VLAN support, enable
reception of extended frames or tag processing in hardware,
"\20MULTICAST\21POLLING\22PPROMISC\23MONITOR\24STATICARP\25NPOLLING"
#define IFCAPBITS \
-"\020\1RXCSUM\2TXCSUM\3NETCONS\4VLAN_MTU\5VLAN_HWTAGGING\6JUMBO_MTU\7RSS"
+"\020\1RXCSUM\2TXCSUM\3NETCONS\4VLAN_MTU\5VLAN_HWTAGGING\6JUMBO_MTU\7RSS" \
+"\10VLAN_HWCSUM\11TSO"
/*
* Print the status of the interface. If an address family was
DEF_CMD("-netcons", -IFCAP_NETCONS, setifcap),
DEF_CMD("rss", IFCAP_RSS, setifcap),
DEF_CMD("-rss", -IFCAP_RSS, setifcap),
+ DEF_CMD("tso", IFCAP_TSO, setifcap),
+ DEF_CMD("-tso", -IFCAP_TSO, setifcap),
DEF_CMD("normal", -IFF_LINK0, setifflags),
DEF_CMD("compress", IFF_LINK0, setifflags),
DEF_CMD("noicmp", IFF_LINK1, setifflags),
#define IFCAP_JUMBO_MTU 0x00020 /* 9000 byte MTU support */
#define IFCAP_RSS 0x00040 /* Receive Side Scaling for IPv4 */
#define IFCAP_VLAN_HWCSUM 0x00080 /* can do IFCAP_HWCSUM on VLANs */
+#define IFCAP_TSO 0x00100 /* can offload TCP segementation */
#define IFCAP_HWCSUM (IFCAP_RXCSUM | IFCAP_TXCSUM)
#include <net/bpf.h>
#include <net/ethernet.h>
#include <net/vlan/if_vlan_ether.h>
+#include <net/vlan/if_vlan_var.h>
#include <net/netmsg2.h>
#if defined(INET) || defined(INET6)
#include <netinet/in.h>
#include <netinet/ip_var.h>
+#include <netinet/tcp_var.h>
#include <netinet/if_ether.h>
#include <netinet/ip_flow.h>
#include <net/ipfw/ip_fw.h>
lwkt_sendmsg(cpu_portfn(m->m_pkthdr.hash), &pmsg->base.lmsg);
}
+boolean_t
+ether_tso_pullup(struct mbuf **mp, int *hoff0, struct ip **ip, int *iphlen,
+ struct tcphdr **th, int *thoff)
+{
+ struct mbuf *m = *mp;
+ struct ether_header *eh;
+ uint16_t type;
+ int hoff;
+
+ KASSERT(M_WRITABLE(m), ("not writable"));
+
+ hoff = ETHER_HDR_LEN;
+ if (m->m_len < hoff) {
+ m = m_pullup(m, hoff);
+ if (m == NULL)
+ goto failed;
+ }
+ eh = mtod(m, struct ether_header *);
+ type = eh->ether_type;
+
+ if (type == htons(ETHERTYPE_VLAN)) {
+ struct ether_vlan_header *evh;
+
+ hoff += EVL_ENCAPLEN;
+ if (m->m_len < hoff) {
+ m = m_pullup(m, hoff);
+ if (m == NULL)
+ goto failed;
+ }
+ evh = mtod(m, struct ether_vlan_header *);
+ type = evh->evl_proto;
+ }
+ KASSERT(type == htons(ETHERTYPE_IP), ("not IP %d", ntohs(type)));
+
+ *mp = m;
+ *hoff0 = hoff;
+ return tcp_tso_pullup(mp, hoff, ip, iphlen, th, thoff);
+
+failed:
+ if (m != NULL)
+ m_freem(m);
+ *mp = NULL;
+ return FALSE;
+}
+
MODULE_VERSION(ether, 1);
extern struct ifnet loif[];
extern int if_index;
+struct ip;
+struct tcphdr;
+
void ether_ifattach(struct ifnet *, uint8_t *, struct lwkt_serialize *);
void ether_ifattach_bpf(struct ifnet *, uint8_t *, u_int, u_int,
struct lwkt_serialize *);
void ether_input_pkt(struct ifnet *, struct mbuf *, const struct pktinfo *);
int ether_output_frame(struct ifnet *, struct mbuf *);
int ether_ioctl(struct ifnet *, u_long, caddr_t);
+boolean_t ether_tso_pullup(struct mbuf **, int *, struct ip **, int *,
+ struct tcphdr **, int *);
struct ifnet *ether_bridge_interface(struct ifnet *ifp);
uint32_t ether_crc32_le(const uint8_t *, size_t);
uint32_t ether_crc32_be(const uint8_t *, size_t);
}
}
- m->m_pkthdr.csum_flags |= CSUM_IP;
- sw_csum = m->m_pkthdr.csum_flags & ~ifp->if_hwassist;
- if (sw_csum & CSUM_DELAY_DATA) {
- in_delayed_cksum(m);
- sw_csum &= ~CSUM_DELAY_DATA;
+ if ((m->m_pkthdr.csum_flags & CSUM_TSO) == 0) {
+ m->m_pkthdr.csum_flags |= CSUM_IP;
+ sw_csum = m->m_pkthdr.csum_flags & ~ifp->if_hwassist;
+ if (sw_csum & CSUM_DELAY_DATA) {
+ in_delayed_cksum(m);
+ sw_csum &= ~CSUM_DELAY_DATA;
+ }
+ m->m_pkthdr.csum_flags &= ifp->if_hwassist;
+ } else {
+ sw_csum = 0;
}
- m->m_pkthdr.csum_flags &= ifp->if_hwassist;
/*
* If small enough for interface, or the interface will take
- * care of the fragmentation for us, can just send directly.
+ * care of the fragmentation or segmentation for us, can just
+ * send directly.
*/
- if (ip->ip_len <= ifp->if_mtu || ((ifp->if_hwassist & CSUM_FRAGMENT) &&
- !(ip->ip_off & IP_DF))) {
+ if (ip->ip_len <= ifp->if_mtu ||
+ ((ifp->if_hwassist & CSUM_FRAGMENT) && !(ip->ip_off & IP_DF)) ||
+ (m->m_pkthdr.csum_flags & CSUM_TSO)) {
ip->ip_len = htons(ip->ip_len);
ip->ip_off = htons(ip->ip_off);
ip->ip_sum = 0;
#include <sys/thread.h>
#include <sys/globaldata.h>
+#include <net/if_var.h>
#include <net/route.h>
#include <netinet/in.h>
SYSCTL_INT(_net_inet_tcp, OID_AUTO, idle_restart, CTLFLAG_RW,
&tcp_idle_restart, 0, "Reset congestion window after idle period");
+static int tcp_do_tso = 1;
+SYSCTL_INT(_net_inet_tcp, OID_AUTO, tso, CTLFLAG_RW,
+ &tcp_do_tso, 0, "Enable TCP Segmentation Offload (TSO)");
+
static void tcp_idle_cwnd_validate(struct tcpcb *);
+static int tcp_tso_getsize(struct tcpcb *tp, u_int *segsz, u_int *hlen);
+
/*
* Tcp output routine: figure out what should be sent and send it.
*/
struct tcphdr *th;
u_char opt[TCP_MAXOLEN];
unsigned int ipoptlen, optlen, hdrlen;
- int idle, idle_cwv = 0;
+ int idle;
boolean_t sendalot;
struct ip6_hdr *ip6;
#ifdef INET6
#else
const boolean_t isipv6 = FALSE;
#endif
+ boolean_t can_tso = FALSE, use_tso;
+ boolean_t report_sack, idle_cwv = FALSE;
+ u_int segsz, tso_hlen;
KKASSERT(so->so_port == &curthread->td_msgport);
*/
if (tp->snd_max == tp->snd_una &&
(ticks - tp->snd_last) >= tp->t_rxtcur && tcp_idle_restart)
- idle_cwv = 1;
+ idle_cwv = TRUE;
/*
* Calculate whether the transmit stream was previously idle
!IN_FASTRECOVERY(tp))
nsacked = tcp_sack_bytes_below(&tp->scb, tp->snd_nxt);
+ /*
+ * Find out whether TSO could be used or not
+ *
+ * For TSO capable devices, the following assumptions apply to
+ * the processing of TCP flags:
+ * - If FIN is set on the large TCP segment, the device must set
+ * FIN on the last segment that it creates from the large TCP
+ * segment.
+ * - If PUSH is set on the large TCP segment, the device must set
+ * PUSH on the last segment that it creates from the large TCP
+ * segment.
+ */
+#if !defined(IPSEC) && !defined(FAST_IPSEC)
+ if (tcp_do_tso
+#ifdef TCP_SIGNATURE
+ && (tp->t_flags & TF_SIGNATURE) == 0
+#endif
+ ) {
+ if (!isipv6) {
+ struct rtentry *rt = inp->inp_route.ro_rt;
+
+ if (rt != NULL && (rt->rt_flags & RTF_UP) &&
+ (rt->rt_ifp->if_hwassist & CSUM_TSO))
+ can_tso = TRUE;
+ }
+ }
+#endif /* !IPSEC && !FAST_IPSEC */
+
again:
m = NULL;
ip = NULL;
th = NULL;
ip6 = NULL;
+ if ((tp->t_flags & (TF_SACK_PERMITTED | TF_NOOPT)) ==
+ TF_SACK_PERMITTED &&
+ (!TAILQ_EMPTY(&tp->t_segq) ||
+ tp->reportblk.rblk_start != tp->reportblk.rblk_end))
+ report_sack = TRUE;
+ else
+ report_sack = FALSE;
+
/* Make use of SACK information when slow-starting after a RTO. */
if (TCP_DO_SACK(tp) && tp->snd_nxt != tp->snd_max &&
!IN_FASTRECOVERY(tp)) {
}
/*
- * Truncate to the maximum segment length and ensure that FIN is
- * removed if the length no longer contains the last data byte.
+ * Don't use TSO, if:
+ * - Congestion window needs validation
+ * - There are SACK blocks to report
+ * - RST or SYN flags is set
+ * - URG will be set
+ *
+ * XXX
+ * Checking for SYN|RST looks overkill, just to be safe than sorry
+ */
+ use_tso = can_tso;
+ if (report_sack || idle_cwv || (flags & (TH_RST | TH_SYN)))
+ use_tso = FALSE;
+ if (use_tso) {
+ tcp_seq ugr_nxt = tp->snd_nxt;
+
+ if ((flags & TH_FIN) && (tp->t_flags & TF_SENTFIN) &&
+ tp->snd_nxt == tp->snd_max)
+ --ugr_nxt;
+
+ if (SEQ_GT(tp->snd_up, ugr_nxt))
+ use_tso = FALSE;
+ }
+
+ if (use_tso) {
+ /*
+ * Find out segment size and header length for TSO
+ */
+ error = tcp_tso_getsize(tp, &segsz, &tso_hlen);
+ if (error)
+ use_tso = FALSE;
+ }
+ if (!use_tso) {
+ segsz = tp->t_maxseg;
+ tso_hlen = 0; /* not used */
+ }
+
+ /*
+ * Truncate to the maximum segment length if not TSO, and ensure that
+ * FIN is removed if the length no longer contains the last data byte.
*/
- if (len > tp->t_maxseg) {
- len = tp->t_maxseg;
+ if (len > segsz) {
+ if (!use_tso) {
+ len = segsz;
+ } else {
+ /*
+ * Truncate TSO transfers to (IP_MAXPACKET - iphlen -
+ * thoff), and make sure that we send equal size
+ * transfers down the stack (rather than big-small-
+ * big-small-...).
+ */
+ len = (min(len, (IP_MAXPACKET - tso_hlen)) / segsz) *
+ segsz;
+ if (len <= segsz)
+ use_tso = FALSE;
+ }
sendalot = TRUE;
+ } else {
+ use_tso = FALSE;
}
if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.ssb_cc))
flags &= ~TH_FIN;
* - we need to retransmit
*/
if (len) {
- if (len == tp->t_maxseg)
+ if (len >= segsz)
goto send;
/*
* NOTE! on localhost connections an 'ack' from the remote
*/
if (avoid_pure_win_update == 0 ||
(tp->t_flags & TF_RXRESIZED)) {
- if (adv >= (long) (2 * tp->t_maxseg)) {
+ if (adv >= (long) (2 * segsz)) {
goto send;
}
}
* If this is a SACK connection and we have a block to report,
* fill in the SACK blocks in the TCP options.
*/
- if ((tp->t_flags & (TF_SACK_PERMITTED | TF_NOOPT)) ==
- TF_SACK_PERMITTED &&
- (!TAILQ_EMPTY(&tp->t_segq) ||
- tp->reportblk.rblk_start != tp->reportblk.rblk_end))
+ if (report_sack)
tcp_sack_fill_report(tp, opt, &optlen);
#ifdef TCP_SIGNATURE
ipoptlen += ipsec_hdrsiz_tcp(tp);
#endif
- /*
- * Adjust data length if insertion of options will bump the packet
- * length beyond the t_maxopd length. Clear FIN to prevent premature
- * closure since there is still more data to send after this (now
- * truncated) packet.
- *
- * If just the options do not fit we are in a no-win situation and
- * we treat it as an unreachable host.
- */
- if (len + optlen + ipoptlen > tp->t_maxopd) {
- if (tp->t_maxopd <= optlen + ipoptlen) {
- static time_t last_optlen_report;
+ if (use_tso) {
+ KASSERT(len > segsz,
+ ("invalid TSO len %ld, segsz %u", len, segsz));
+ } else {
+ KASSERT(len <= segsz,
+ ("invalid len %ld, segsz %u", len, segsz));
- if (last_optlen_report != time_second) {
- last_optlen_report = time_second;
- kprintf("tcpcb %p: MSS (%d) too small to hold options!\n", tp, tp->t_maxopd);
+ /*
+ * Adjust data length if insertion of options will bump
+ * the packet length beyond the t_maxopd length. Clear
+ * FIN to prevent premature closure since there is still
+ * more data to send after this (now truncated) packet.
+ *
+ * If just the options do not fit we are in a no-win
+ * situation and we treat it as an unreachable host.
+ */
+ if (len + optlen + ipoptlen > tp->t_maxopd) {
+ if (tp->t_maxopd <= optlen + ipoptlen) {
+ static time_t last_optlen_report;
+
+ if (last_optlen_report != time_second) {
+ last_optlen_report = time_second;
+ kprintf("tcpcb %p: MSS (%d) too "
+ "small to hold options!\n",
+ tp, tp->t_maxopd);
+ }
+ error = EHOSTUNREACH;
+ goto out;
+ } else {
+ flags &= ~TH_FIN;
+ len = tp->t_maxopd - optlen - ipoptlen;
+ sendalot = TRUE;
}
- error = EHOSTUNREACH;
- goto out;
- } else {
- flags &= ~TH_FIN;
- len = tp->t_maxopd - optlen - ipoptlen;
- sendalot = TRUE;
}
}
tcpstat.tcps_sndbyte += len;
}
if (idle_cwv) {
- idle_cwv = 0;
+ idle_cwv = FALSE;
tcp_idle_cwnd_validate(tp);
}
/* Update last send time after CWV */
if (isipv6) {
ip6 = mtod(m, struct ip6_hdr *);
th = (struct tcphdr *)(ip6 + 1);
- tcp_fillheaders(tp, ip6, th);
+ tcp_fillheaders(tp, ip6, th, use_tso);
} else {
ip = mtod(m, struct ip *);
ipov = (struct ipovly *)ip;
th = (struct tcphdr *)(ip + 1);
/* this picks up the pseudo header (w/o the length) */
- tcp_fillheaders(tp, ip, th);
+ tcp_fillheaders(tp, ip, th, use_tso);
}
after_th:
/*
* window is less then one segment.
*/
if (recvwin < (long)(so->so_rcv.ssb_hiwat / 4) &&
- recvwin < (long)tp->t_maxseg)
+ recvwin < (long)segsz)
recvwin = 0;
if (recvwin < (tcp_seq_diff_t)(tp->rcv_adv - tp->rcv_nxt))
recvwin = (tcp_seq_diff_t)(tp->rcv_adv - tp->rcv_nxt);
th->th_win = htons((u_short) (recvwin>>tp->rcv_scale));
if (SEQ_GT(tp->snd_up, tp->snd_nxt)) {
+ KASSERT(!use_tso, ("URG with TSO"));
if (th != NULL) {
th->th_urp = htons((u_short)(tp->snd_up - tp->snd_nxt));
th->th_flags |= TH_URG;
sizeof(struct ip6_hdr),
sizeof(struct tcphdr) + optlen + len);
} else {
- m->m_pkthdr.csum_flags = CSUM_TCP;
m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
- if (len + optlen) {
- th->th_sum = in_addword(th->th_sum,
- htons((u_short)(optlen + len)));
+ if (use_tso) {
+ m->m_pkthdr.csum_flags = CSUM_TSO;
+ m->m_pkthdr.segsz = segsz;
+ } else {
+ m->m_pkthdr.csum_flags = CSUM_TCP;
+ if (len + optlen) {
+ th->th_sum = in_addword(th->th_sum,
+ htons((u_short)(optlen + len)));
+ }
}
/*
/* Restart ABC counting during congestion avoidance */
tp->snd_wacked = 0;
}
+
+static int
+tcp_tso_getsize(struct tcpcb *tp, u_int *segsz, u_int *hlen0)
+{
+ struct inpcb * const inp = tp->t_inpcb;
+#ifdef INET6
+ const boolean_t isipv6 = (inp->inp_vflag & INP_IPV6) != 0;
+#else
+ const boolean_t isipv6 = FALSE;
+#endif
+ unsigned int ipoptlen, optlen;
+ u_int hlen;
+
+ hlen = sizeof(struct ip) + sizeof(struct tcphdr);
+
+ if (isipv6) {
+ ipoptlen = ip6_optlen(inp);
+ } else {
+ if (inp->inp_options) {
+ ipoptlen = inp->inp_options->m_len -
+ offsetof(struct ipoption, ipopt_list);
+ } else {
+ ipoptlen = 0;
+ }
+ }
+#ifdef IPSEC
+ ipoptlen += ipsec_hdrsiz_tcp(tp);
+#endif
+ hlen += ipoptlen;
+
+ optlen = 0;
+ if ((tp->t_flags & (TF_REQ_TSTMP | TF_NOOPT)) == TF_REQ_TSTMP &&
+ (tp->t_flags & TF_RCVD_TSTMP))
+ optlen += TCPOLEN_TSTAMP_APPA;
+ hlen += optlen;
+
+ if (tp->t_maxopd <= optlen + ipoptlen)
+ return EHOSTUNREACH;
+
+ *segsz = tp->t_maxopd - optlen - ipoptlen;
+ *hlen0 = hlen;
+ return 0;
+}
* of the tcpcb each time to conserve mbufs.
*/
void
-tcp_fillheaders(struct tcpcb *tp, void *ip_ptr, void *tcp_ptr)
+tcp_fillheaders(struct tcpcb *tp, void *ip_ptr, void *tcp_ptr, boolean_t tso)
{
struct inpcb *inp = tp->t_inpcb;
struct tcphdr *tcp_hdr = (struct tcphdr *)tcp_ptr;
#endif
{
struct ip *ip = (struct ip *) ip_ptr;
+ u_int plen;
ip->ip_vhl = IP_VHL_BORING;
ip->ip_tos = 0;
ip->ip_p = IPPROTO_TCP;
ip->ip_src = inp->inp_laddr;
ip->ip_dst = inp->inp_faddr;
+
+ if (tso)
+ plen = htons(IPPROTO_TCP);
+ else
+ plen = htons(sizeof(struct tcphdr) + IPPROTO_TCP);
tcp_hdr->th_sum = in_pseudo(ip->ip_src.s_addr,
- ip->ip_dst.s_addr,
- htons(sizeof(struct tcphdr) + IPPROTO_TCP));
+ ip->ip_dst.s_addr, plen);
}
tcp_hdr->th_sport = inp->inp_lport;
if ((tmp = mpipe_alloc_nowait(&tcptemp_mpipe)) == NULL)
return (NULL);
- tcp_fillheaders(tp, &tmp->tt_ipgen, &tmp->tt_t);
+ tcp_fillheaders(tp, &tmp->tt_ipgen, &tmp->tt_t, FALSE);
return (tmp);
}
th = (struct tcphdr *)(ip6 + 1);
m->m_pkthdr.len = m->m_len =
sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
- tcp_fillheaders(tp, ip6, th);
+ tcp_fillheaders(tp, ip6, th, FALSE);
hdrsiz = ipsec6_hdrsiz(m, IPSEC_DIR_OUTBOUND, inp);
} else
#endif
ip = mtod(m, struct ip *);
th = (struct tcphdr *)(ip + 1);
m->m_pkthdr.len = m->m_len = sizeof(struct tcpiphdr);
- tcp_fillheaders(tp, ip, th);
+ tcp_fillheaders(tp, ip, th, FALSE);
hdrsiz = ipsec4_hdrsiz(m, IPSEC_DIR_OUTBOUND, inp);
}
return (0);
}
#endif /* TCP_SIGNATURE */
+
+boolean_t
+tcp_tso_pullup(struct mbuf **mp, int hoff, struct ip **ip0, int *iphlen0,
+ struct tcphdr **th0, int *thoff0)
+{
+ struct mbuf *m = *mp;
+ struct ip *ip;
+ int len, iphlen;
+ struct tcphdr *th;
+ int thoff;
+
+ len = hoff + sizeof(struct ip);
+
+ /* The fixed IP header must reside completely in the first mbuf. */
+ if (m->m_len < len) {
+ m = m_pullup(m, len);
+ if (m == NULL)
+ goto fail;
+ }
+
+ ip = mtodoff(m, struct ip *, hoff);
+ iphlen = IP_VHL_HL(ip->ip_vhl) << 2;
+
+ /* The full IP header must reside completely in the one mbuf. */
+ if (m->m_len < hoff + iphlen) {
+ m = m_pullup(m, hoff + iphlen);
+ if (m == NULL)
+ goto fail;
+ ip = mtodoff(m, struct ip *, hoff);
+ }
+
+ KASSERT(ip->ip_p == IPPROTO_TCP, ("not tcp %d", ip->ip_p));
+
+ if (m->m_len < hoff + iphlen + sizeof(struct tcphdr)) {
+ m = m_pullup(m, hoff + iphlen + sizeof(struct tcphdr));
+ if (m == NULL)
+ goto fail;
+ ip = mtodoff(m, struct ip *, hoff);
+ }
+
+ th = (struct tcphdr *)((caddr_t)ip + iphlen);
+ thoff = th->th_off << 2;
+
+ if (m->m_len < hoff + iphlen + thoff) {
+ m = m_pullup(m, hoff + iphlen + thoff);
+ if (m == NULL)
+ goto fail;
+ ip = mtodoff(m, struct ip *, hoff);
+ th = (struct tcphdr *)((caddr_t)ip + iphlen);
+ }
+
+ *mp = m;
+ *ip0 = ip;
+ *iphlen0 = iphlen;
+ *th0 = th;
+ *thoff0 = thoff;
+ return TRUE;
+
+fail:
+ if (m != NULL)
+ m_freem(m);
+ *mp = NULL;
+ return FALSE;
+}
extern int tcp_delack_enabled;
extern int path_mtu_discovery;
+struct ip;
union netmsg;
int tcp_addrcpu(in_addr_t faddr, in_port_t fport,
void tcp_setpersist (struct tcpcb *);
struct tcptemp *tcp_maketemplate (struct tcpcb *);
void tcp_freetemplate (struct tcptemp *);
-void tcp_fillheaders (struct tcpcb *, void *, void *);
+void tcp_fillheaders (struct tcpcb *, void *, void *, boolean_t);
struct lwkt_port *
tcp_soport(struct socket *, struct sockaddr *, struct mbuf **);
struct lwkt_port *
void tcp_xmit_bandwidth_limit(struct tcpcb *tp, tcp_seq ack_seq);
u_long tcp_initial_window(struct tcpcb *tp);
void tcp_timer_keep_activity(struct tcpcb *tp, int thflags);
+boolean_t
+ tcp_tso_pullup(struct mbuf **mp, int hoff, struct ip **ip, int *iphlen,
+ struct tcphdr **th, int *thoff);
void syncache_init(void);
void syncache_unreach(struct in_conninfo *, struct tcphdr *);
int syncache_expand(struct in_conninfo *, struct tcphdr *,
uint16_t hash; /* packet hash */
uint16_t wlan_seqno; /* IEEE 802.11 seq no. */
+ uint16_t segsz; /* TSO segment size */
};
/*
* NB: This flag is only used
* by IP defragmenter.
*/
+#define CSUM_TSO 0x2000 /* will do TCP segmentation */
#define CSUM_DELAY_DATA (CSUM_TCP | CSUM_UDP)
#define CSUM_DELAY_IP (CSUM_IP) /* XXX add ipv6 here too? */