===== include/linux/tcp.h 1.34 vs edited ===== --- 1.34/include/linux/tcp.h 2005-01-17 14:09:33 -08:00 +++ edited/include/linux/tcp.h 2005-01-31 16:03:32 -08:00 @@ -262,6 +262,7 @@ __u32 pmtu_cookie; /* Last pmtu seen by socket */ __u32 mss_cache; /* Cached effective mss, not including SACKS */ __u16 mss_cache_std; /* Like mss_cache, but without TSO */ + __u16 tso_goal; /* TSO packet count goal, 1 w/non-TSO paths */ __u16 mss_clamp; /* Maximal mss, negotiated at connection setup */ __u16 ext_header_len; /* Network protocol overhead (IP/IPv6 options) */ __u16 ext2_header_len;/* Options depending on route */ ===== net/ipv4/tcp_output.c 1.77 vs edited ===== --- 1.77/net/ipv4/tcp_output.c 2005-01-18 12:23:36 -08:00 +++ edited/net/ipv4/tcp_output.c 2005-02-01 14:32:46 -08:00 @@ -707,15 +707,103 @@ if (factor > limit) factor = limit; - tp->mss_cache = mss_now * factor; + /* If this ever triggers, change tp->tso_goal to + * a larger type and update this bug check. + */ + BUG_ON(factor > 65535); - mss_now = tp->mss_cache; - } + tp->tso_goal = factor; + } else + tp->tso_goal = 1; if (tp->eff_sacks) mss_now -= (TCPOLEN_SACK_BASE_ALIGNED + (tp->eff_sacks * TCPOLEN_SACK_PERBLOCK)); return mss_now; +} + +static inline int tcp_skb_data_all_paged(struct sk_buff *skb) +{ + return skb_headlen(skb) == 0; +} + +/* If possible, append paged data of SRC_SKB onto the + * tail of DST_SKB. + */ +static int skb_append_pages(struct sk_buff *dst_skb, struct sk_buff *src_skb) +{ + int i; + + if (!tcp_skb_data_all_paged(src_skb)) + return -EINVAL; + + for (i = 0; i < skb_shinfo(src_skb)->nr_frags; i++) { + skb_frag_t *src_frag = &skb_shinfo(src_skb)->frags[i]; + skb_frag_t *dst_frag; + int dst_frag_idx; + + dst_frag_idx = skb_shinfo(dst_skb)->nr_frags; + + if (skb_can_coalesce(dst_skb, dst_frag_idx, + src_frag->page, src_frag->page_offset)) { + dst_frag = &skb_shinfo(dst_skb)->frags[dst_frag_idx-1]; + dst_frag->size += src_frag->size; + } else { + if (dst_frag_idx >= MAX_SKB_FRAGS) + return -EMSGSIZE; + + dst_frag = &skb_shinfo(dst_skb)->frags[dst_frag_idx]; + skb_shinfo(dst_skb)->nr_frags = dst_frag_idx + 1; + + dst_frag->page = src_frag->page; + get_page(src_frag->page); + + dst_frag->page_offset = src_frag->page_offset; + dst_frag->size = src_frag->size; + } + dst_skb->data_len += src_frag->size; + } + + return 0; +} + +static struct sk_buff *tcp_tso_build(struct sk_buff *head, int mss, int num) +{ + struct sk_buff *skb; + struct sock *sk; + int err; + + sk = head->sk; + skb = alloc_skb(sk->sk_prot->max_header, GFP_ATOMIC); + err = -ENOMEM; + if (!skb) + goto fail; + + err = 0; + skb_shinfo(skb)->tso_size = mss; + skb_shinfo(skb)->tso_segs = num; + while (num--) { + err = skb_append_pages(skb, head); + if (err) + goto fail; + + head = head->next; + } + return skb; + +fail: + if (skb) { + int i; + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + put_page(frag->page); + } + + kfree_skb(skb); + } + return NULL; } /* This routine writes packets to the network. It advances the