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 /drivers/net/tun.c | |
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 'drivers/net/tun.c')
-rw-r--r-- | drivers/net/tun.c | 13 |
1 files changed, 5 insertions, 8 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index b1038c0e2240..b6f45c5d84d5 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c | |||
@@ -1019,7 +1019,6 @@ static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from, | |||
1019 | skb->data_len += len; | 1019 | skb->data_len += len; |
1020 | skb->len += len; | 1020 | skb->len += len; |
1021 | skb->truesize += truesize; | 1021 | skb->truesize += truesize; |
1022 | skb_shinfo(skb)->gso_type |= SKB_GSO_SHARED_FRAG; | ||
1023 | atomic_add(truesize, &skb->sk->sk_wmem_alloc); | 1022 | atomic_add(truesize, &skb->sk->sk_wmem_alloc); |
1024 | while (len) { | 1023 | while (len) { |
1025 | int off = base & ~PAGE_MASK; | 1024 | int off = base & ~PAGE_MASK; |
@@ -1165,18 +1164,16 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, | |||
1165 | } | 1164 | } |
1166 | 1165 | ||
1167 | if (gso.gso_type != VIRTIO_NET_HDR_GSO_NONE) { | 1166 | if (gso.gso_type != VIRTIO_NET_HDR_GSO_NONE) { |
1168 | unsigned short gso_type = 0; | ||
1169 | |||
1170 | pr_debug("GSO!\n"); | 1167 | pr_debug("GSO!\n"); |
1171 | switch (gso.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { | 1168 | switch (gso.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { |
1172 | case VIRTIO_NET_HDR_GSO_TCPV4: | 1169 | case VIRTIO_NET_HDR_GSO_TCPV4: |
1173 | gso_type = SKB_GSO_TCPV4; | 1170 | skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; |
1174 | break; | 1171 | break; |
1175 | case VIRTIO_NET_HDR_GSO_TCPV6: | 1172 | case VIRTIO_NET_HDR_GSO_TCPV6: |
1176 | gso_type = SKB_GSO_TCPV6; | 1173 | skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; |
1177 | break; | 1174 | break; |
1178 | case VIRTIO_NET_HDR_GSO_UDP: | 1175 | case VIRTIO_NET_HDR_GSO_UDP: |
1179 | gso_type = SKB_GSO_UDP; | 1176 | skb_shinfo(skb)->gso_type = SKB_GSO_UDP; |
1180 | break; | 1177 | break; |
1181 | default: | 1178 | default: |
1182 | tun->dev->stats.rx_frame_errors++; | 1179 | tun->dev->stats.rx_frame_errors++; |
@@ -1185,10 +1182,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, | |||
1185 | } | 1182 | } |
1186 | 1183 | ||
1187 | if (gso.gso_type & VIRTIO_NET_HDR_GSO_ECN) | 1184 | if (gso.gso_type & VIRTIO_NET_HDR_GSO_ECN) |
1188 | gso_type |= SKB_GSO_TCP_ECN; | 1185 | skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; |
1189 | 1186 | ||
1190 | skb_shinfo(skb)->gso_size = gso.gso_size; | 1187 | skb_shinfo(skb)->gso_size = gso.gso_size; |
1191 | skb_shinfo(skb)->gso_type |= gso_type; | ||
1192 | if (skb_shinfo(skb)->gso_size == 0) { | 1188 | if (skb_shinfo(skb)->gso_size == 0) { |
1193 | tun->dev->stats.rx_frame_errors++; | 1189 | tun->dev->stats.rx_frame_errors++; |
1194 | kfree_skb(skb); | 1190 | kfree_skb(skb); |
@@ -1204,6 +1200,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, | |||
1204 | if (zerocopy) { | 1200 | if (zerocopy) { |
1205 | skb_shinfo(skb)->destructor_arg = msg_control; | 1201 | skb_shinfo(skb)->destructor_arg = msg_control; |
1206 | skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY; | 1202 | skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY; |
1203 | skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; | ||
1207 | } | 1204 | } |
1208 | 1205 | ||
1209 | skb_reset_network_header(skb); | 1206 | skb_reset_network_header(skb); |