aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/macvtap.c
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2010-02-18 00:48:17 -0500
committerDavid S. Miller <davem@davemloft.net>2010-02-18 17:08:38 -0500
commitb9fb9ee07e67fce0b7bfd517a48710465706c30a (patch)
tree10dc1f67a21f2f065f603adf2fc7f86fd009a758 /drivers/net/macvtap.c
parent501c774cb13c3ef8fb7fc5f08fa19473f7d9a0db (diff)
macvtap: add GSO/csum offload support
Added flags field to macvtap_queue to enable/disable processing of virtio_net_hdr via IFF_VNET_HDR. This flag is checked to prepend virtio_net_hdr in the receive path and process/skip virtio_net_hdr in the send path. Original patch by Sridhar, further changes by Arnd. Signed-off-by: Sridhar Samudrala <sri@us.ibm.com> Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/macvtap.c')
-rw-r--r--drivers/net/macvtap.c206
1 files changed, 182 insertions, 24 deletions
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index e354501ab297..55ceae09738e 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -17,6 +17,7 @@
17#include <net/net_namespace.h> 17#include <net/net_namespace.h>
18#include <net/rtnetlink.h> 18#include <net/rtnetlink.h>
19#include <net/sock.h> 19#include <net/sock.h>
20#include <linux/virtio_net.h>
20 21
21/* 22/*
22 * A macvtap queue is the central object of this driver, it connects 23 * A macvtap queue is the central object of this driver, it connects
@@ -37,6 +38,7 @@ struct macvtap_queue {
37 struct socket sock; 38 struct socket sock;
38 struct macvlan_dev *vlan; 39 struct macvlan_dev *vlan;
39 struct file *file; 40 struct file *file;
41 unsigned int flags;
40}; 42};
41 43
42static struct proto macvtap_proto = { 44static struct proto macvtap_proto = {
@@ -276,6 +278,7 @@ static int macvtap_open(struct inode *inode, struct file *file)
276 q->sock.ops = &macvtap_socket_ops; 278 q->sock.ops = &macvtap_socket_ops;
277 sock_init_data(&q->sock, &q->sk); 279 sock_init_data(&q->sock, &q->sk);
278 q->sk.sk_write_space = macvtap_sock_write_space; 280 q->sk.sk_write_space = macvtap_sock_write_space;
281 q->flags = IFF_VNET_HDR | IFF_NO_PI | IFF_TAP;
279 282
280 err = macvtap_set_queue(dev, file, q); 283 err = macvtap_set_queue(dev, file, q);
281 if (err) 284 if (err)
@@ -318,6 +321,111 @@ out:
318 return mask; 321 return mask;
319} 322}
320 323
324static inline struct sk_buff *macvtap_alloc_skb(struct sock *sk, size_t prepad,
325 size_t len, size_t linear,
326 int noblock, int *err)
327{
328 struct sk_buff *skb;
329
330 /* Under a page? Don't bother with paged skb. */
331 if (prepad + len < PAGE_SIZE || !linear)
332 linear = len;
333
334 skb = sock_alloc_send_pskb(sk, prepad + linear, len - linear, noblock,
335 err);
336 if (!skb)
337 return NULL;
338
339 skb_reserve(skb, prepad);
340 skb_put(skb, linear);
341 skb->data_len = len - linear;
342 skb->len += len - linear;
343
344 return skb;
345}
346
347/*
348 * macvtap_skb_from_vnet_hdr and macvtap_skb_to_vnet_hdr should
349 * be shared with the tun/tap driver.
350 */
351static int macvtap_skb_from_vnet_hdr(struct sk_buff *skb,
352 struct virtio_net_hdr *vnet_hdr)
353{
354 unsigned short gso_type = 0;
355 if (vnet_hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
356 switch (vnet_hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
357 case VIRTIO_NET_HDR_GSO_TCPV4:
358 gso_type = SKB_GSO_TCPV4;
359 break;
360 case VIRTIO_NET_HDR_GSO_TCPV6:
361 gso_type = SKB_GSO_TCPV6;
362 break;
363 case VIRTIO_NET_HDR_GSO_UDP:
364 gso_type = SKB_GSO_UDP;
365 break;
366 default:
367 return -EINVAL;
368 }
369
370 if (vnet_hdr->gso_type & VIRTIO_NET_HDR_GSO_ECN)
371 gso_type |= SKB_GSO_TCP_ECN;
372
373 if (vnet_hdr->gso_size == 0)
374 return -EINVAL;
375 }
376
377 if (vnet_hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
378 if (!skb_partial_csum_set(skb, vnet_hdr->csum_start,
379 vnet_hdr->csum_offset))
380 return -EINVAL;
381 }
382
383 if (vnet_hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
384 skb_shinfo(skb)->gso_size = vnet_hdr->gso_size;
385 skb_shinfo(skb)->gso_type = gso_type;
386
387 /* Header must be checked, and gso_segs computed. */
388 skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
389 skb_shinfo(skb)->gso_segs = 0;
390 }
391 return 0;
392}
393
394static int macvtap_skb_to_vnet_hdr(const struct sk_buff *skb,
395 struct virtio_net_hdr *vnet_hdr)
396{
397 memset(vnet_hdr, 0, sizeof(*vnet_hdr));
398
399 if (skb_is_gso(skb)) {
400 struct skb_shared_info *sinfo = skb_shinfo(skb);
401
402 /* This is a hint as to how much should be linear. */
403 vnet_hdr->hdr_len = skb_headlen(skb);
404 vnet_hdr->gso_size = sinfo->gso_size;
405 if (sinfo->gso_type & SKB_GSO_TCPV4)
406 vnet_hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
407 else if (sinfo->gso_type & SKB_GSO_TCPV6)
408 vnet_hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
409 else if (sinfo->gso_type & SKB_GSO_UDP)
410 vnet_hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP;
411 else
412 BUG();
413 if (sinfo->gso_type & SKB_GSO_TCP_ECN)
414 vnet_hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN;
415 } else
416 vnet_hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
417
418 if (skb->ip_summed == CHECKSUM_PARTIAL) {
419 vnet_hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
420 vnet_hdr->csum_start = skb->csum_start -
421 skb_headroom(skb);
422 vnet_hdr->csum_offset = skb->csum_offset;
423 } /* else everything is zero */
424
425 return 0;
426}
427
428
321/* Get packet from user space buffer */ 429/* Get packet from user space buffer */
322static ssize_t macvtap_get_user(struct macvtap_queue *q, 430static ssize_t macvtap_get_user(struct macvtap_queue *q,
323 const struct iovec *iv, size_t count, 431 const struct iovec *iv, size_t count,
@@ -327,22 +435,53 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q,
327 struct macvlan_dev *vlan; 435 struct macvlan_dev *vlan;
328 size_t len = count; 436 size_t len = count;
329 int err; 437 int err;
438 struct virtio_net_hdr vnet_hdr = { 0 };
439 int vnet_hdr_len = 0;
440
441 if (q->flags & IFF_VNET_HDR) {
442 vnet_hdr_len = sizeof(vnet_hdr);
443
444 err = -EINVAL;
445 if ((len -= vnet_hdr_len) < 0)
446 goto err;
447
448 err = memcpy_fromiovecend((void *)&vnet_hdr, iv, 0,
449 vnet_hdr_len);
450 if (err < 0)
451 goto err;
452 if ((vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) &&
453 vnet_hdr.csum_start + vnet_hdr.csum_offset + 2 >
454 vnet_hdr.hdr_len)
455 vnet_hdr.hdr_len = vnet_hdr.csum_start +
456 vnet_hdr.csum_offset + 2;
457 err = -EINVAL;
458 if (vnet_hdr.hdr_len > len)
459 goto err;
460 }
330 461
462 err = -EINVAL;
331 if (unlikely(len < ETH_HLEN)) 463 if (unlikely(len < ETH_HLEN))
332 return -EINVAL; 464 goto err;
333 465
334 skb = sock_alloc_send_skb(&q->sk, NET_IP_ALIGN + len, noblock, &err); 466 skb = macvtap_alloc_skb(&q->sk, NET_IP_ALIGN, len, vnet_hdr.hdr_len,
467 noblock, &err);
335 if (!skb) 468 if (!skb)
336 goto err; 469 goto err;
337 470
338 skb_reserve(skb, NET_IP_ALIGN); 471 err = skb_copy_datagram_from_iovec(skb, 0, iv, vnet_hdr_len, len);
339 skb_put(skb, count);
340
341 err = skb_copy_datagram_from_iovec(skb, 0, iv, 0, len);
342 if (err) 472 if (err)
343 goto err; 473 goto err_kfree;
344 474
345 skb_set_network_header(skb, ETH_HLEN); 475 skb_set_network_header(skb, ETH_HLEN);
476 skb_reset_mac_header(skb);
477 skb->protocol = eth_hdr(skb)->h_proto;
478
479 if (vnet_hdr_len) {
480 err = macvtap_skb_from_vnet_hdr(skb, &vnet_hdr);
481 if (err)
482 goto err_kfree;
483 }
484
346 rcu_read_lock_bh(); 485 rcu_read_lock_bh();
347 vlan = rcu_dereference(q->vlan); 486 vlan = rcu_dereference(q->vlan);
348 if (vlan) 487 if (vlan)
@@ -353,15 +492,16 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q,
353 492
354 return count; 493 return count;
355 494
495err_kfree:
496 kfree_skb(skb);
497
356err: 498err:
357 rcu_read_lock_bh(); 499 rcu_read_lock_bh();
358 vlan = rcu_dereference(q->vlan); 500 vlan = rcu_dereference(q->vlan);
359 if (vlan) 501 if (vlan)
360 macvlan_count_rx(q->vlan, 0, false, false); 502 netdev_get_tx_queue(vlan->dev, 0)->tx_dropped++;
361 rcu_read_unlock_bh(); 503 rcu_read_unlock_bh();
362 504
363 kfree_skb(skb);
364
365 return err; 505 return err;
366} 506}
367 507
@@ -384,10 +524,25 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q,
384{ 524{
385 struct macvlan_dev *vlan; 525 struct macvlan_dev *vlan;
386 int ret; 526 int ret;
527 int vnet_hdr_len = 0;
528
529 if (q->flags & IFF_VNET_HDR) {
530 struct virtio_net_hdr vnet_hdr;
531 vnet_hdr_len = sizeof (vnet_hdr);
532 if ((len -= vnet_hdr_len) < 0)
533 return -EINVAL;
534
535 ret = macvtap_skb_to_vnet_hdr(skb, &vnet_hdr);
536 if (ret)
537 return ret;
538
539 if (memcpy_toiovecend(iv, (void *)&vnet_hdr, 0, vnet_hdr_len))
540 return -EFAULT;
541 }
387 542
388 len = min_t(int, skb->len, len); 543 len = min_t(int, skb->len, len);
389 544
390 ret = skb_copy_datagram_const_iovec(skb, 0, iv, 0, len); 545 ret = skb_copy_datagram_const_iovec(skb, 0, iv, vnet_hdr_len, len);
391 546
392 rcu_read_lock_bh(); 547 rcu_read_lock_bh();
393 vlan = rcu_dereference(q->vlan); 548 vlan = rcu_dereference(q->vlan);
@@ -395,7 +550,7 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q,
395 macvlan_count_rx(vlan, len, ret == 0, 0); 550 macvlan_count_rx(vlan, len, ret == 0, 0);
396 rcu_read_unlock_bh(); 551 rcu_read_unlock_bh();
397 552
398 return ret ? ret : len; 553 return ret ? ret : (len + vnet_hdr_len);
399} 554}
400 555
401static ssize_t macvtap_do_read(struct macvtap_queue *q, struct kiocb *iocb, 556static ssize_t macvtap_do_read(struct macvtap_queue *q, struct kiocb *iocb,
@@ -473,9 +628,14 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd,
473 /* ignore the name, just look at flags */ 628 /* ignore the name, just look at flags */
474 if (get_user(u, &ifr->ifr_flags)) 629 if (get_user(u, &ifr->ifr_flags))
475 return -EFAULT; 630 return -EFAULT;
476 if (u != (IFF_TAP | IFF_NO_PI)) 631
477 return -EINVAL; 632 ret = 0;
478 return 0; 633 if ((u & ~IFF_VNET_HDR) != (IFF_NO_PI | IFF_TAP))
634 ret = -EINVAL;
635 else
636 q->flags = u;
637
638 return ret;
479 639
480 case TUNGETIFF: 640 case TUNGETIFF:
481 rcu_read_lock_bh(); 641 rcu_read_lock_bh();
@@ -489,13 +649,13 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd,
489 649
490 ret = 0; 650 ret = 0;
491 if (copy_to_user(&ifr->ifr_name, q->vlan->dev->name, IFNAMSIZ) || 651 if (copy_to_user(&ifr->ifr_name, q->vlan->dev->name, IFNAMSIZ) ||
492 put_user((TUN_TAP_DEV | TUN_NO_PI), &ifr->ifr_flags)) 652 put_user(q->flags, &ifr->ifr_flags))
493 ret = -EFAULT; 653 ret = -EFAULT;
494 dev_put(vlan->dev); 654 dev_put(vlan->dev);
495 return ret; 655 return ret;
496 656
497 case TUNGETFEATURES: 657 case TUNGETFEATURES:
498 if (put_user((IFF_TAP | IFF_NO_PI), up)) 658 if (put_user(IFF_TAP | IFF_NO_PI | IFF_VNET_HDR, up))
499 return -EFAULT; 659 return -EFAULT;
500 return 0; 660 return 0;
501 661
@@ -509,15 +669,13 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd,
509 case TUNSETOFFLOAD: 669 case TUNSETOFFLOAD:
510 /* let the user check for future flags */ 670 /* let the user check for future flags */
511 if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | 671 if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 |
512 TUN_F_TSO_ECN | TUN_F_UFO)) 672 TUN_F_TSO_ECN | TUN_F_UFO))
513 return -EINVAL;
514
515 /* TODO: add support for these, so far we don't
516 support any offload */
517 if (arg & (TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 |
518 TUN_F_TSO_ECN | TUN_F_UFO))
519 return -EINVAL; 673 return -EINVAL;
520 674
675 /* TODO: only accept frames with the features that
676 got enabled for forwarded frames */
677 if (!(q->flags & IFF_VNET_HDR))
678 return -EINVAL;
521 return 0; 679 return 0;
522 680
523 default: 681 default: