=== sys/dev/oce/oce_if.c ================================================================== --- sys/dev/oce/oce_if.c (revision 270996) +++ sys/dev/oce/oce_if.c (local) @@ -1731,7 +1731,9 @@ sc->ifp->if_baudrate = IF_Gbps(10); #if __FreeBSD_version >= 1000000 - sc->ifp->if_hw_tsomax = OCE_MAX_TSO_SIZE; + sc->ifp->if_hw_tsomax.frag_count = 29; /* 29 elements */ + sc->ifp->if_hw_tsomax.frag_size_log2 = 12; /* 4K */ + sc->ifp->if_hw_tsomax.hdr_size_log2 = 5; /* ETH+VLAN < 2**5 */ #endif ether_ifattach(sc->ifp, sc->macaddr.mac_addr); === sys/dev/oce/oce_if.h ================================================================== --- sys/dev/oce/oce_if.h (revision 270996) +++ sys/dev/oce/oce_if.h (local) @@ -152,7 +152,6 @@ #define OCE_MAX_TX_ELEMENTS 29 #define OCE_MAX_TX_DESC 1024 #define OCE_MAX_TX_SIZE 65535 -#define OCE_MAX_TSO_SIZE (65535 - ETHER_HDR_LEN) #define OCE_MAX_RX_SIZE 4096 #define OCE_MAX_RQ_POSTS 255 #define OCE_DEFAULT_PROMISCUOUS 0 === sys/dev/vmware/vmxnet3/if_vmx.c ================================================================== --- sys/dev/vmware/vmxnet3/if_vmx.c (revision 270996) +++ sys/dev/vmware/vmxnet3/if_vmx.c (local) @@ -1722,7 +1722,9 @@ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = vmxnet3_init; ifp->if_ioctl = vmxnet3_ioctl; - ifp->if_hw_tsomax = VMXNET3_TSO_MAXSIZE; + ifp->if_hw_tsomax.frag_count = VMXNET3_TX_MAXSEGS; + ifp->if_hw_tsomax.frag_size_log2 = VMXNET3_TX_MAXSEGSHIFT; + ifp->if_hw_tsomax.hdr_size_log2 = 5; /* ETH+VLAN < 2**5 */ #ifdef VMXNET3_LEGACY_TX ifp->if_start = vmxnet3_start; === sys/dev/vmware/vmxnet3/if_vmxvar.h ================================================================== --- sys/dev/vmware/vmxnet3/if_vmxvar.h (revision 270996) +++ sys/dev/vmware/vmxnet3/if_vmxvar.h (local) @@ -277,14 +277,13 @@ */ #define VMXNET3_TX_MAXSEGS 32 #define VMXNET3_TX_MAXSIZE (VMXNET3_TX_MAXSEGS * MCLBYTES) -#define VMXNET3_TSO_MAXSIZE \ - (VMXNET3_TX_MAXSIZE - sizeof(struct ether_vlan_header)) /* * Maximum support Tx segments size. The length field in the * Tx descriptor is 14 bits. */ -#define VMXNET3_TX_MAXSEGSIZE (1 << 14) +#define VMXNET3_TX_MAXSEGSHIFT 14 +#define VMXNET3_TX_MAXSEGSIZE (1 << VMXNET3_TX_MAXSEGSHIFT) /* * The maximum number of Rx segments we accept. When LRO is enabled, === sys/dev/xen/netfront/netfront.c ================================================================== --- sys/dev/xen/netfront/netfront.c (revision 270996) +++ sys/dev/xen/netfront/netfront.c (local) @@ -134,7 +134,6 @@ * to mirror the Linux MAX_SKB_FRAGS constant. */ #define MAX_TX_REQ_FRAGS (65536 / PAGE_SIZE + 2) -#define NF_TSO_MAXBURST ((IP_MAXPACKET / PAGE_SIZE) * MCLBYTES) #define RX_COPY_THRESHOLD 256 @@ -2102,7 +2101,9 @@ ifp->if_hwassist = XN_CSUM_FEATURES; ifp->if_capabilities = IFCAP_HWCSUM; - ifp->if_hw_tsomax = NF_TSO_MAXBURST; + ifp->if_hw_tsomax.frag_count = MAX_TX_REQ_FRAGS; + ifp->if_hw_tsomax.frag_size_log2 = PAGE_SHIFT; + ifp->if_hw_tsomax.hdr_size_log2 = 5; /* ETH+VLAN < 2**5 */ ether_ifattach(ifp, np->mac); callout_init(&np->xn_stat_ch, CALLOUT_MPSAFE); === sys/net/if.c ================================================================== --- sys/net/if.c (revision 270996) +++ sys/net/if.c (local) @@ -445,6 +445,7 @@ ifp->if_index = idx; ifp->if_type = type; ifp->if_alloctype = type; + ifp->if_hw_tsomax = IF_TSO_LIMIT_DEFAULT(); if (if_com_alloc[type] != NULL) { ifp->if_l2com = if_com_alloc[type](type, ifp); if (ifp->if_l2com == NULL) { @@ -657,16 +658,6 @@ TAILQ_INSERT_HEAD(&ifp->if_addrhead, ifa, ifa_link); /* Reliably crash if used uninitialized. */ ifp->if_broadcastaddr = NULL; - -#if defined(INET) || defined(INET6) - /* Initialize to max value. */ - if (ifp->if_hw_tsomax == 0) - ifp->if_hw_tsomax = min(IP_MAXPACKET, 32 * MCLBYTES - - (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN)); - KASSERT(ifp->if_hw_tsomax <= IP_MAXPACKET && - ifp->if_hw_tsomax >= IP_MAXPACKET / 8, - ("%s: tsomax outside of range", __func__)); -#endif } #ifdef VIMAGE else { === sys/net/if_lagg.c ================================================================== --- sys/net/if_lagg.c (revision 270996) +++ sys/net/if_lagg.c (local) @@ -445,11 +445,7 @@ struct lagg_port *lp; int cap = ~0, ena = ~0; u_long hwa = ~0UL; -#if defined(INET) || defined(INET6) - u_int hw_tsomax = IP_MAXPACKET; /* Initialize to the maximum value. */ -#else - u_int hw_tsomax = ~0; /* if_hw_tsomax is only for INET/INET6, but.. */ -#endif + struct if_tso_limit hw_tsomax = IF_TSO_LIMIT_DEFAULT(); LAGG_WLOCK_ASSERT(sc); @@ -458,10 +454,9 @@ cap &= lp->lp_ifp->if_capabilities; ena &= lp->lp_ifp->if_capenable; hwa &= lp->lp_ifp->if_hwassist; - /* Set to the minimum value of the lagg ports. */ - if (lp->lp_ifp->if_hw_tsomax < hw_tsomax && - lp->lp_ifp->if_hw_tsomax > 0) - hw_tsomax = lp->lp_ifp->if_hw_tsomax; + /* Set to the common value of the lagg ports. */ + hw_tsomax = IF_TSO_LIMIT_COMMON(&hw_tsomax, + &lp->lp_ifp->if_hw_tsomax); } cap = (cap == ~0 ? 0 : cap); ena = (ena == ~0 ? 0 : ena); @@ -470,7 +465,7 @@ if (sc->sc_ifp->if_capabilities != cap || sc->sc_ifp->if_capenable != ena || sc->sc_ifp->if_hwassist != hwa || - sc->sc_ifp->if_hw_tsomax != hw_tsomax) { + IF_TSO_LIMIT_CMP(&sc->sc_ifp->if_hw_tsomax, !=, &hw_tsomax)) { sc->sc_ifp->if_capabilities = cap; sc->sc_ifp->if_capenable = ena; sc->sc_ifp->if_hwassist = hwa; === sys/net/if_var.h ================================================================== --- sys/net/if_var.h (revision 270996) +++ sys/net/if_var.h (local) @@ -120,6 +120,36 @@ typedef uint64_t (*if_get_counter_t)(if_t, ifnet_counter); /* + * Structure defining hardware TSO limits. + */ +struct if_tso_limit { + u_int raw_value[0]; /* access all fields as one */ + u_char frag_count; /* maximum number of fragments: 1..255 */ + u_char frag_size_log2; /* maximum fragment size: 2 ** (12..16) */ + u_char hdr_size_log2; /* maximum header size: 2 ** (2..8) */ + u_char reserved; /* zero */ +}; + +#define IF_TSO_LIMIT_DEFAULT() ({ \ +struct if_tso_limit tso_temp = { \ + .frag_count = 128, \ + .frag_size_log2 = 16, \ + .hdr_size_log2 = 2, \ + .reserved = 0, \ +}; tso_temp; }) + +#define IF_TSO_LIMIT_COMMON(a,b) ({ \ +struct if_tso_limit tso_temp = { \ + .frag_count = min((a)->frag_count, (b)->frag_count), \ + .frag_size_log2 = min((a)->frag_size_log2, (b)->frag_size_log2),\ + .hdr_size_log2 = max((a)->hdr_size_log2, (b)->hdr_size_log2), \ + .reserved = 0, \ +}; tso_temp; }) + +#define IF_TSO_LIMIT_CMP(a,op,b) \ + ((a)->raw_value[0] op (b)->raw_value[0]) + +/* * Structure defining a network interface. * * Size ILP32: 592 (approx) @@ -222,10 +252,8 @@ if_get_counter_t if_get_counter; /* get counter values */ /* Stuff that's only temporary and doesn't belong here. */ - u_int if_hw_tsomax; /* tso burst length limit, the minimum - * is (IP_MAXPACKET / 8). - * XXXAO: Have to find a better place - * for it eventually. */ + struct if_tso_limit if_hw_tsomax; + /* * Old, racy and expensive statistics, should not be used in * new drivers. === sys/net/if_vlan.c ================================================================== --- sys/net/if_vlan.c (revision 270996) +++ sys/net/if_vlan.c (local) @@ -1511,8 +1511,8 @@ * propagate the hardware-assisted flag. TSO on VLANs * does not necessarily require hardware VLAN tagging. */ - if (p->if_hw_tsomax > 0) - ifp->if_hw_tsomax = p->if_hw_tsomax; + ifp->if_hw_tsomax = IF_TSO_LIMIT_COMMON(&ifp->if_hw_tsomax, + &p->if_hw_tsomax); if (p->if_capabilities & IFCAP_VLAN_HWTSO) ifp->if_capabilities |= p->if_capabilities & IFCAP_TSO; if (p->if_capenable & IFCAP_VLAN_HWTSO) { === sys/netinet/tcp_output.c ================================================================== --- sys/netinet/tcp_output.c (revision 270996) +++ sys/netinet/tcp_output.c (local) @@ -767,9 +767,70 @@ flags &= ~TH_FIN; if (tso) { + struct if_tso_limit if_hw_tsomax; + struct mbuf *mb; + u_int rem_frags; + u_int moff; + int max_len; + + /* copy TSO limit information */ + if_hw_tsomax.raw_value[0] = tp->t_tsomax; + + /* compute maximum TSO length */ + max_len = (((u_int)if_hw_tsomax.frag_count) << + if_hw_tsomax.frag_size_log2) - hdrlen - + (1 << if_hw_tsomax.hdr_size_log2); + + /* clamp maximum length value */ + if (max_len > IP_MAXPACKET) + max_len = IP_MAXPACKET; + else if (max_len < 0) + max_len = 0; + + /* get smallest length */ + if (len > (u_int)max_len) { + sendalot = 1; + len = (u_int)max_len; + } + + /* get remaining fragments */ + rem_frags = if_hw_tsomax.frag_count; + KASSERT(ipoptlen == 0, ("%s: TSO can't do IP options", __func__)); + max_len = 0; + mb = sbsndptr(&so->so_snd, off, len, &moff); + + /* now make sure the number of fragments fit too */ + while (mb != NULL && (u_int)max_len < len) { + u_int cur_length; + u_int cur_frags; + + /* + * Get length of mbuf fragment and how + * many hardware frags it would use: + */ + cur_length = (mb->m_len - moff); + cur_frags = (cur_length + + (1 << if_hw_tsomax.frag_size_log2) - 1) + >> if_hw_tsomax.frag_size_log2; + + /* Handle special case: Zero Length Mbuf */ + if (cur_frags == 0) + cur_frags = 1; + + /* Check if fragment limit will be exceeded */ + if (cur_frags >= rem_frags) { + max_len += min(cur_length, rem_frags << if_hw_tsomax.frag_size_log2); + break; + } + max_len += cur_length; + rem_frags -= cur_frags; + moff = 0; + mb = mb->m_next; + } + /* * Limit a burst to t_tsomax minus IP, * TCP and options length to keep ip->ip_len @@ -776,8 +837,8 @@ * from overflowing or exceeding the maximum * length allowed by the network interface. */ - if (len > tp->t_tsomax - hdrlen) { - len = tp->t_tsomax - hdrlen; + if (len > (u_int)max_len) { + len = (u_int)max_len; sendalot = 1; } === sys/netinet/tcp_subr.c ================================================================== --- sys/netinet/tcp_subr.c (revision 270996) +++ sys/netinet/tcp_subr.c (local) @@ -1818,7 +1818,7 @@ if (ifp->if_capenable & IFCAP_TSO4 && ifp->if_hwassist & CSUM_TSO) { cap->ifcap |= CSUM_TSO; - cap->tsomax = ifp->if_hw_tsomax; + cap->tsomax = ifp->if_hw_tsomax.raw_value[0]; } } RTFREE(sro.ro_rt); @@ -1857,7 +1857,7 @@ if (ifp->if_capenable & IFCAP_TSO6 && ifp->if_hwassist & CSUM_TSO) { cap->ifcap |= CSUM_TSO; - cap->tsomax = ifp->if_hw_tsomax; + cap->tsomax = ifp->if_hw_tsomax.raw_value[0]; } } RTFREE(sro6.ro_rt);