diff options
author | Pravin B Shelar <pshelar@nicira.com> | 2013-02-11 04:27:41 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-02-13 13:30:10 -0500 |
commit | c9af6db4c11ccc6c3e7f19bbc15d54023956f97c (patch) | |
tree | c596e747d8940b848931ac31701e245a6c0efaf6 /net/ipv4 | |
parent | b8fa4100350432504df438014e2e5e9c1bbb6325 (diff) |
net: Fix possible wrong checksum generation.
Patch cef401de7be8c4e (net: fix possible wrong checksum
generation) fixed wrong checksum calculation but it broke TSO by
defining new GSO type but not a netdev feature for that type.
net_gso_ok() would not allow hardware checksum/segmentation
offload of such packets without the feature.
Following patch fixes TSO and wrong checksum. This patch uses
same logic that Eric Dumazet used. Patch introduces new flag
SKBTX_SHARED_FRAG if at least one frag can be modified by
the user. but SKBTX_SHARED_FRAG flag is kept in skb shared
info tx_flags rather than gso_type.
tx_flags is better compared to gso_type since we can have skb with
shared frag without gso packet. It does not link SHARED_FRAG to
GSO, So there is no need to define netdev feature for this.
Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/af_inet.c | 1 | ||||
-rw-r--r-- | net/ipv4/ip_output.c | 1 | ||||
-rw-r--r-- | net/ipv4/tcp.c | 4 | ||||
-rw-r--r-- | net/ipv4/tcp_input.c | 4 | ||||
-rw-r--r-- | net/ipv4/tcp_output.c | 4 |
5 files changed, 6 insertions, 8 deletions
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 1aec92bf8018..e6e5d8506336 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c | |||
@@ -1287,7 +1287,6 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, | |||
1287 | SKB_GSO_UDP | | 1287 | SKB_GSO_UDP | |
1288 | SKB_GSO_DODGY | | 1288 | SKB_GSO_DODGY | |
1289 | SKB_GSO_TCP_ECN | | 1289 | SKB_GSO_TCP_ECN | |
1290 | SKB_GSO_SHARED_FRAG | | ||
1291 | 0))) | 1290 | 0))) |
1292 | goto out; | 1291 | goto out; |
1293 | 1292 | ||
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 3e98ed2bff55..5e12dca7b3dd 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c | |||
@@ -598,6 +598,7 @@ slow_path: | |||
598 | /* for offloaded checksums cleanup checksum before fragmentation */ | 598 | /* for offloaded checksums cleanup checksum before fragmentation */ |
599 | if ((skb->ip_summed == CHECKSUM_PARTIAL) && skb_checksum_help(skb)) | 599 | if ((skb->ip_summed == CHECKSUM_PARTIAL) && skb_checksum_help(skb)) |
600 | goto fail; | 600 | goto fail; |
601 | iph = ip_hdr(skb); | ||
601 | 602 | ||
602 | left = skb->len - hlen; /* Space per frame */ | 603 | left = skb->len - hlen; /* Space per frame */ |
603 | ptr = hlen; /* Where to start from */ | 604 | ptr = hlen; /* Where to start from */ |
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 801b07b796f0..1f0bedb8622f 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c | |||
@@ -897,8 +897,7 @@ new_segment: | |||
897 | get_page(page); | 897 | get_page(page); |
898 | skb_fill_page_desc(skb, i, page, offset, copy); | 898 | skb_fill_page_desc(skb, i, page, offset, copy); |
899 | } | 899 | } |
900 | 900 | skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; | |
901 | skb_shinfo(skb)->gso_type |= SKB_GSO_SHARED_FRAG; | ||
902 | 901 | ||
903 | skb->len += copy; | 902 | skb->len += copy; |
904 | skb->data_len += copy; | 903 | skb->data_len += copy; |
@@ -3044,7 +3043,6 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, | |||
3044 | SKB_GSO_DODGY | | 3043 | SKB_GSO_DODGY | |
3045 | SKB_GSO_TCP_ECN | | 3044 | SKB_GSO_TCP_ECN | |
3046 | SKB_GSO_TCPV6 | | 3045 | SKB_GSO_TCPV6 | |
3047 | SKB_GSO_SHARED_FRAG | | ||
3048 | 0) || | 3046 | 0) || |
3049 | !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) | 3047 | !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) |
3050 | goto out; | 3048 | goto out; |
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index d9bfaea34322..a759e19496d2 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c | |||
@@ -1239,13 +1239,13 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *skb, | |||
1239 | */ | 1239 | */ |
1240 | if (!skb_shinfo(prev)->gso_size) { | 1240 | if (!skb_shinfo(prev)->gso_size) { |
1241 | skb_shinfo(prev)->gso_size = mss; | 1241 | skb_shinfo(prev)->gso_size = mss; |
1242 | skb_shinfo(prev)->gso_type |= sk->sk_gso_type; | 1242 | skb_shinfo(prev)->gso_type = sk->sk_gso_type; |
1243 | } | 1243 | } |
1244 | 1244 | ||
1245 | /* CHECKME: To clear or not to clear? Mimics normal skb currently */ | 1245 | /* CHECKME: To clear or not to clear? Mimics normal skb currently */ |
1246 | if (skb_shinfo(skb)->gso_segs <= 1) { | 1246 | if (skb_shinfo(skb)->gso_segs <= 1) { |
1247 | skb_shinfo(skb)->gso_size = 0; | 1247 | skb_shinfo(skb)->gso_size = 0; |
1248 | skb_shinfo(skb)->gso_type &= SKB_GSO_SHARED_FRAG; | 1248 | skb_shinfo(skb)->gso_type = 0; |
1249 | } | 1249 | } |
1250 | 1250 | ||
1251 | /* Difference in this won't matter, both ACKed by the same cumul. ACK */ | 1251 | /* Difference in this won't matter, both ACKed by the same cumul. ACK */ |
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 564bf89d9fd3..6182d90e97b0 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c | |||
@@ -1133,7 +1133,6 @@ static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb) | |||
1133 | static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, | 1133 | static 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; | ||
1137 | if (skb->len <= mss_now || !sk_can_gso(sk) || | 1136 | if (skb->len <= mss_now || !sk_can_gso(sk) || |
1138 | skb->ip_summed == CHECKSUM_NONE) { | 1137 | skb->ip_summed == CHECKSUM_NONE) { |
1139 | /* Avoid the costly divide in the normal | 1138 | /* Avoid the costly divide in the normal |
@@ -1141,10 +1140,11 @@ static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, | |||
1141 | */ | 1140 | */ |
1142 | skb_shinfo(skb)->gso_segs = 1; | 1141 | skb_shinfo(skb)->gso_segs = 1; |
1143 | skb_shinfo(skb)->gso_size = 0; | 1142 | 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 | ||