aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/virtio_net.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 /drivers/net/virtio_net.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 'drivers/net/virtio_net.c')
-rw-r--r--drivers/net/virtio_net.c12
1 files changed, 8 insertions, 4 deletions
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 701408a1ded6..58914c8ea68f 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -220,6 +220,7 @@ static void set_skb_frag(struct sk_buff *skb, struct page *page,
220 skb->len += size; 220 skb->len += size;
221 skb->truesize += PAGE_SIZE; 221 skb->truesize += PAGE_SIZE;
222 skb_shinfo(skb)->nr_frags++; 222 skb_shinfo(skb)->nr_frags++;
223 skb_shinfo(skb)->gso_type |= SKB_GSO_SHARED_FRAG;
223 *len -= size; 224 *len -= size;
224} 225}
225 226
@@ -379,16 +380,18 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len)
379 ntohs(skb->protocol), skb->len, skb->pkt_type); 380 ntohs(skb->protocol), skb->len, skb->pkt_type);
380 381
381 if (hdr->hdr.gso_type != VIRTIO_NET_HDR_GSO_NONE) { 382 if (hdr->hdr.gso_type != VIRTIO_NET_HDR_GSO_NONE) {
383 unsigned short gso_type = 0;
384
382 pr_debug("GSO!\n"); 385 pr_debug("GSO!\n");
383 switch (hdr->hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { 386 switch (hdr->hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
384 case VIRTIO_NET_HDR_GSO_TCPV4: 387 case VIRTIO_NET_HDR_GSO_TCPV4:
385 skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; 388 gso_type = SKB_GSO_TCPV4;
386 break; 389 break;
387 case VIRTIO_NET_HDR_GSO_UDP: 390 case VIRTIO_NET_HDR_GSO_UDP:
388 skb_shinfo(skb)->gso_type = SKB_GSO_UDP; 391 gso_type = SKB_GSO_UDP;
389 break; 392 break;
390 case VIRTIO_NET_HDR_GSO_TCPV6: 393 case VIRTIO_NET_HDR_GSO_TCPV6:
391 skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; 394 gso_type = SKB_GSO_TCPV6;
392 break; 395 break;
393 default: 396 default:
394 net_warn_ratelimited("%s: bad gso type %u.\n", 397 net_warn_ratelimited("%s: bad gso type %u.\n",
@@ -397,7 +400,7 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len)
397 } 400 }
398 401
399 if (hdr->hdr.gso_type & VIRTIO_NET_HDR_GSO_ECN) 402 if (hdr->hdr.gso_type & VIRTIO_NET_HDR_GSO_ECN)
400 skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; 403 gso_type |= SKB_GSO_TCP_ECN;
401 404
402 skb_shinfo(skb)->gso_size = hdr->hdr.gso_size; 405 skb_shinfo(skb)->gso_size = hdr->hdr.gso_size;
403 if (skb_shinfo(skb)->gso_size == 0) { 406 if (skb_shinfo(skb)->gso_size == 0) {
@@ -405,6 +408,7 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len)
405 goto frame_err; 408 goto frame_err;
406 } 409 }
407 410
411 skb_shinfo(skb)->gso_type |= gso_type;
408 /* Header must be checked, and gso_segs computed. */ 412 /* Header must be checked, and gso_segs computed. */
409 skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY; 413 skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
410 skb_shinfo(skb)->gso_segs = 0; 414 skb_shinfo(skb)->gso_segs = 0;