summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netinet/tcp_output.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/netinet/tcp_output.c')
-rw-r--r--freebsd/sys/netinet/tcp_output.c106
1 files changed, 95 insertions, 11 deletions
diff --git a/freebsd/sys/netinet/tcp_output.c b/freebsd/sys/netinet/tcp_output.c
index 6215c4e2..550af64f 100644
--- a/freebsd/sys/netinet/tcp_output.c
+++ b/freebsd/sys/netinet/tcp_output.c
@@ -767,28 +767,112 @@ send:
flags &= ~TH_FIN;
if (tso) {
+ u_int if_hw_tsomax;
+ u_int if_hw_tsomaxsegcount;
+ u_int if_hw_tsomaxsegsize;
+ struct mbuf *mb;
+ u_int moff;
+ int max_len;
+
+ /* extract TSO information */
+ if_hw_tsomax = tp->t_tsomax;
+ if_hw_tsomaxsegcount = tp->t_tsomaxsegcount;
+ if_hw_tsomaxsegsize = tp->t_tsomaxsegsize;
+
+ /*
+ * Limit a TSO burst to prevent it from
+ * overflowing or exceeding the maximum length
+ * allowed by the network interface:
+ */
KASSERT(ipoptlen == 0,
("%s: TSO can't do IP options", __func__));
/*
- * Limit a burst to t_tsomax minus IP,
- * TCP and options length to keep ip->ip_len
- * from overflowing or exceeding the maximum
- * length allowed by the network interface.
+ * Check if we should limit by maximum payload
+ * length:
*/
- if (len > tp->t_tsomax - hdrlen) {
- len = tp->t_tsomax - hdrlen;
- sendalot = 1;
+ if (if_hw_tsomax != 0) {
+ /* compute maximum TSO length */
+ max_len = (if_hw_tsomax - hdrlen);
+ if (max_len <= 0) {
+ len = 0;
+ } else if (len > max_len) {
+ sendalot = 1;
+ len = max_len;
+ }
+ }
+
+ /*
+ * Check if we should limit by maximum segment
+ * size and count:
+ */
+ if (if_hw_tsomaxsegcount != 0 &&
+ if_hw_tsomaxsegsize != 0) {
+ max_len = 0;
+ mb = sbsndmbuf(&so->so_snd, off, &moff);
+
+ while (mb != NULL && max_len < len) {
+ u_int mlen;
+ u_int frags;
+
+ /*
+ * Get length of mbuf fragment
+ * and how many hardware frags,
+ * rounded up, it would use:
+ */
+ mlen = (mb->m_len - moff);
+ frags = howmany(mlen,
+ if_hw_tsomaxsegsize);
+
+ /* Handle special case: Zero Length Mbuf */
+ if (frags == 0)
+ frags = 1;
+
+ /*
+ * Check if the fragment limit
+ * will be reached or exceeded:
+ */
+ if (frags >= if_hw_tsomaxsegcount) {
+ max_len += min(mlen,
+ if_hw_tsomaxsegcount *
+ if_hw_tsomaxsegsize);
+ break;
+ }
+ max_len += mlen;
+ if_hw_tsomaxsegcount -= frags;
+ moff = 0;
+ mb = mb->m_next;
+ }
+ if (max_len <= 0) {
+ len = 0;
+ } else if (len > max_len) {
+ sendalot = 1;
+ len = max_len;
+ }
}
/*
* Prevent the last segment from being
- * fractional unless the send sockbuf can
- * be emptied.
+ * fractional unless the send sockbuf can be
+ * emptied:
+ */
+ max_len = (tp->t_maxopd - optlen);
+ if ((off + len) < so->so_snd.sb_cc) {
+ moff = len % max_len;
+ if (moff != 0) {
+ len -= moff;
+ sendalot = 1;
+ }
+ }
+
+ /*
+ * In case there are too many small fragments
+ * don't use TSO:
*/
- if (sendalot && off + len < so->so_snd.sb_cc) {
- len -= len % (tp->t_maxopd - optlen);
+ if (len <= max_len) {
+ len = max_len;
sendalot = 1;
+ tso = 0;
}
/*