aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/tcp_output.c
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2013-01-25 15:34:37 -0500
committerDavid S. Miller <davem@davemloft.net>2013-01-28 00:27:15 -0500
commitcef401de7be8c4e155c6746bfccf721a4fa5fab9 (patch)
treeaf082329bf0613ed9de5b9575e3f94f3f03b77ec /net/ipv4/tcp_output.c
parent61550022b9586972082904b80de26a464c558437 (diff)
net: fix possible wrong checksum generation
Pravin Shelar mentioned that GSO could potentially generate wrong TX checksum if skb has fragments that are overwritten by the user between the checksum computation and transmit. He suggested to linearize skbs but this extra copy can be avoided for normal tcp skbs cooked by tcp_sendmsg(). This patch introduces a new SKB_GSO_SHARED_FRAG flag, set in skb_shinfo(skb)->gso_type if at least one frag can be modified by the user. Typical sources of such possible overwrites are {vm}splice(), sendfile(), and macvtap/tun/virtio_net drivers. Tested: $ netperf -H 7.7.8.84 MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 7.7.8.84 () port 0 AF_INET Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/sec 87380 16384 16384 10.00 3959.52 $ netperf -H 7.7.8.84 -t TCP_SENDFILE TCP SENDFILE TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 7.7.8.84 () port 0 AF_INET Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/sec 87380 16384 16384 10.00 3216.80 Performance of the SENDFILE is impacted by the extra allocation and copy, and because we use order-0 pages, while the TCP_STREAM uses bigger pages. Reported-by: Pravin Shelar <pshelar@nicira.com> Signed-off-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp_output.c')
-rw-r--r--net/ipv4/tcp_output.c4
1 files changed, 2 insertions, 2 deletions
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 667a6adfccf8..367e2ec01da1 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -1133,6 +1133,7 @@ static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb)
1133static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, 1133static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb,
1134 unsigned int mss_now) 1134 unsigned int mss_now)
1135{ 1135{
1136 skb_shinfo(skb)->gso_type &= SKB_GSO_SHARED_FRAG;
1136 if (skb->len <= mss_now || !sk_can_gso(sk) || 1137 if (skb->len <= mss_now || !sk_can_gso(sk) ||
1137 skb->ip_summed == CHECKSUM_NONE) { 1138 skb->ip_summed == CHECKSUM_NONE) {
1138 /* Avoid the costly divide in the normal 1139 /* Avoid the costly divide in the normal
@@ -1140,11 +1141,10 @@ static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb,
1140 */ 1141 */
1141 skb_shinfo(skb)->gso_segs = 1; 1142 skb_shinfo(skb)->gso_segs = 1;
1142 skb_shinfo(skb)->gso_size = 0; 1143 skb_shinfo(skb)->gso_size = 0;
1143 skb_shinfo(skb)->gso_type = 0;
1144 } else { 1144 } else {
1145 skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss_now); 1145 skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss_now);
1146 skb_shinfo(skb)->gso_size = mss_now; 1146 skb_shinfo(skb)->gso_size = mss_now;
1147 skb_shinfo(skb)->gso_type = sk->sk_gso_type; 1147 skb_shinfo(skb)->gso_type |= sk->sk_gso_type;
1148 } 1148 }
1149} 1149}
1150 1150