diff options
-rw-r--r-- | drivers/net/macvtap.c | 68 |
1 files changed, 43 insertions, 25 deletions
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 880cc090dc44..af90ab5e5768 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c | |||
@@ -45,6 +45,18 @@ struct macvtap_queue { | |||
45 | struct list_head next; | 45 | struct list_head next; |
46 | }; | 46 | }; |
47 | 47 | ||
48 | #define MACVTAP_FEATURES (IFF_VNET_HDR | IFF_VNET_LE | IFF_MULTI_QUEUE) | ||
49 | |||
50 | static inline u16 macvtap16_to_cpu(struct macvtap_queue *q, __virtio16 val) | ||
51 | { | ||
52 | return __virtio16_to_cpu(q->flags & IFF_VNET_LE, val); | ||
53 | } | ||
54 | |||
55 | static inline __virtio16 cpu_to_macvtap16(struct macvtap_queue *q, u16 val) | ||
56 | { | ||
57 | return __cpu_to_virtio16(q->flags & IFF_VNET_LE, val); | ||
58 | } | ||
59 | |||
48 | static struct proto macvtap_proto = { | 60 | static struct proto macvtap_proto = { |
49 | .name = "macvtap", | 61 | .name = "macvtap", |
50 | .owner = THIS_MODULE, | 62 | .owner = THIS_MODULE, |
@@ -557,7 +569,8 @@ static inline struct sk_buff *macvtap_alloc_skb(struct sock *sk, size_t prepad, | |||
557 | * macvtap_skb_from_vnet_hdr and macvtap_skb_to_vnet_hdr should | 569 | * macvtap_skb_from_vnet_hdr and macvtap_skb_to_vnet_hdr should |
558 | * be shared with the tun/tap driver. | 570 | * be shared with the tun/tap driver. |
559 | */ | 571 | */ |
560 | static int macvtap_skb_from_vnet_hdr(struct sk_buff *skb, | 572 | static int macvtap_skb_from_vnet_hdr(struct macvtap_queue *q, |
573 | struct sk_buff *skb, | ||
561 | struct virtio_net_hdr *vnet_hdr) | 574 | struct virtio_net_hdr *vnet_hdr) |
562 | { | 575 | { |
563 | unsigned short gso_type = 0; | 576 | unsigned short gso_type = 0; |
@@ -588,13 +601,13 @@ static int macvtap_skb_from_vnet_hdr(struct sk_buff *skb, | |||
588 | } | 601 | } |
589 | 602 | ||
590 | if (vnet_hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { | 603 | if (vnet_hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { |
591 | if (!skb_partial_csum_set(skb, vnet_hdr->csum_start, | 604 | if (!skb_partial_csum_set(skb, macvtap16_to_cpu(q, vnet_hdr->csum_start), |
592 | vnet_hdr->csum_offset)) | 605 | macvtap16_to_cpu(q, vnet_hdr->csum_offset))) |
593 | return -EINVAL; | 606 | return -EINVAL; |
594 | } | 607 | } |
595 | 608 | ||
596 | if (vnet_hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { | 609 | if (vnet_hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { |
597 | skb_shinfo(skb)->gso_size = vnet_hdr->gso_size; | 610 | skb_shinfo(skb)->gso_size = macvtap16_to_cpu(q, vnet_hdr->gso_size); |
598 | skb_shinfo(skb)->gso_type = gso_type; | 611 | skb_shinfo(skb)->gso_type = gso_type; |
599 | 612 | ||
600 | /* Header must be checked, and gso_segs computed. */ | 613 | /* Header must be checked, and gso_segs computed. */ |
@@ -604,8 +617,9 @@ static int macvtap_skb_from_vnet_hdr(struct sk_buff *skb, | |||
604 | return 0; | 617 | return 0; |
605 | } | 618 | } |
606 | 619 | ||
607 | static void macvtap_skb_to_vnet_hdr(const struct sk_buff *skb, | 620 | static void macvtap_skb_to_vnet_hdr(struct macvtap_queue *q, |
608 | struct virtio_net_hdr *vnet_hdr) | 621 | const struct sk_buff *skb, |
622 | struct virtio_net_hdr *vnet_hdr) | ||
609 | { | 623 | { |
610 | memset(vnet_hdr, 0, sizeof(*vnet_hdr)); | 624 | memset(vnet_hdr, 0, sizeof(*vnet_hdr)); |
611 | 625 | ||
@@ -613,8 +627,8 @@ static void macvtap_skb_to_vnet_hdr(const struct sk_buff *skb, | |||
613 | struct skb_shared_info *sinfo = skb_shinfo(skb); | 627 | struct skb_shared_info *sinfo = skb_shinfo(skb); |
614 | 628 | ||
615 | /* This is a hint as to how much should be linear. */ | 629 | /* This is a hint as to how much should be linear. */ |
616 | vnet_hdr->hdr_len = skb_headlen(skb); | 630 | vnet_hdr->hdr_len = cpu_to_macvtap16(q, skb_headlen(skb)); |
617 | vnet_hdr->gso_size = sinfo->gso_size; | 631 | vnet_hdr->gso_size = cpu_to_macvtap16(q, sinfo->gso_size); |
618 | if (sinfo->gso_type & SKB_GSO_TCPV4) | 632 | if (sinfo->gso_type & SKB_GSO_TCPV4) |
619 | vnet_hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; | 633 | vnet_hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; |
620 | else if (sinfo->gso_type & SKB_GSO_TCPV6) | 634 | else if (sinfo->gso_type & SKB_GSO_TCPV6) |
@@ -628,10 +642,13 @@ static void macvtap_skb_to_vnet_hdr(const struct sk_buff *skb, | |||
628 | 642 | ||
629 | if (skb->ip_summed == CHECKSUM_PARTIAL) { | 643 | if (skb->ip_summed == CHECKSUM_PARTIAL) { |
630 | vnet_hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; | 644 | vnet_hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; |
631 | vnet_hdr->csum_start = skb_checksum_start_offset(skb); | ||
632 | if (vlan_tx_tag_present(skb)) | 645 | if (vlan_tx_tag_present(skb)) |
633 | vnet_hdr->csum_start += VLAN_HLEN; | 646 | vnet_hdr->csum_start = cpu_to_macvtap16(q, |
634 | vnet_hdr->csum_offset = skb->csum_offset; | 647 | skb_checksum_start_offset(skb) + VLAN_HLEN); |
648 | else | ||
649 | vnet_hdr->csum_start = cpu_to_macvtap16(q, | ||
650 | skb_checksum_start_offset(skb)); | ||
651 | vnet_hdr->csum_offset = cpu_to_macvtap16(q, skb->csum_offset); | ||
635 | } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) { | 652 | } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) { |
636 | vnet_hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID; | 653 | vnet_hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID; |
637 | } /* else everything is zero */ | 654 | } /* else everything is zero */ |
@@ -666,12 +683,14 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, | |||
666 | if (err < 0) | 683 | if (err < 0) |
667 | goto err; | 684 | goto err; |
668 | if ((vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && | 685 | if ((vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && |
669 | vnet_hdr.csum_start + vnet_hdr.csum_offset + 2 > | 686 | macvtap16_to_cpu(q, vnet_hdr.csum_start) + |
670 | vnet_hdr.hdr_len) | 687 | macvtap16_to_cpu(q, vnet_hdr.csum_offset) + 2 > |
671 | vnet_hdr.hdr_len = vnet_hdr.csum_start + | 688 | macvtap16_to_cpu(q, vnet_hdr.hdr_len)) |
672 | vnet_hdr.csum_offset + 2; | 689 | vnet_hdr.hdr_len = cpu_to_macvtap16(q, |
690 | macvtap16_to_cpu(q, vnet_hdr.csum_start) + | ||
691 | macvtap16_to_cpu(q, vnet_hdr.csum_offset) + 2); | ||
673 | err = -EINVAL; | 692 | err = -EINVAL; |
674 | if (vnet_hdr.hdr_len > len) | 693 | if (macvtap16_to_cpu(q, vnet_hdr.hdr_len) > len) |
675 | goto err; | 694 | goto err; |
676 | } | 695 | } |
677 | 696 | ||
@@ -684,7 +703,8 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, | |||
684 | goto err; | 703 | goto err; |
685 | 704 | ||
686 | if (m && m->msg_control && sock_flag(&q->sk, SOCK_ZEROCOPY)) { | 705 | if (m && m->msg_control && sock_flag(&q->sk, SOCK_ZEROCOPY)) { |
687 | copylen = vnet_hdr.hdr_len ? vnet_hdr.hdr_len : GOODCOPY_LEN; | 706 | copylen = vnet_hdr.hdr_len ? |
707 | macvtap16_to_cpu(q, vnet_hdr.hdr_len) : GOODCOPY_LEN; | ||
688 | if (copylen > good_linear) | 708 | if (copylen > good_linear) |
689 | copylen = good_linear; | 709 | copylen = good_linear; |
690 | linear = copylen; | 710 | linear = copylen; |
@@ -695,10 +715,10 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, | |||
695 | 715 | ||
696 | if (!zerocopy) { | 716 | if (!zerocopy) { |
697 | copylen = len; | 717 | copylen = len; |
698 | if (vnet_hdr.hdr_len > good_linear) | 718 | if (macvtap16_to_cpu(q, vnet_hdr.hdr_len) > good_linear) |
699 | linear = good_linear; | 719 | linear = good_linear; |
700 | else | 720 | else |
701 | linear = vnet_hdr.hdr_len; | 721 | linear = macvtap16_to_cpu(q, vnet_hdr.hdr_len); |
702 | } | 722 | } |
703 | 723 | ||
704 | skb = macvtap_alloc_skb(&q->sk, NET_IP_ALIGN, copylen, | 724 | skb = macvtap_alloc_skb(&q->sk, NET_IP_ALIGN, copylen, |
@@ -725,7 +745,7 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, | |||
725 | skb->protocol = eth_hdr(skb)->h_proto; | 745 | skb->protocol = eth_hdr(skb)->h_proto; |
726 | 746 | ||
727 | if (vnet_hdr_len) { | 747 | if (vnet_hdr_len) { |
728 | err = macvtap_skb_from_vnet_hdr(skb, &vnet_hdr); | 748 | err = macvtap_skb_from_vnet_hdr(q, skb, &vnet_hdr); |
729 | if (err) | 749 | if (err) |
730 | goto err_kfree; | 750 | goto err_kfree; |
731 | } | 751 | } |
@@ -791,7 +811,7 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q, | |||
791 | if ((len -= vnet_hdr_len) < 0) | 811 | if ((len -= vnet_hdr_len) < 0) |
792 | return -EINVAL; | 812 | return -EINVAL; |
793 | 813 | ||
794 | macvtap_skb_to_vnet_hdr(skb, &vnet_hdr); | 814 | macvtap_skb_to_vnet_hdr(q, skb, &vnet_hdr); |
795 | 815 | ||
796 | if (memcpy_toiovecend(iv, (void *)&vnet_hdr, 0, sizeof(vnet_hdr))) | 816 | if (memcpy_toiovecend(iv, (void *)&vnet_hdr, 0, sizeof(vnet_hdr))) |
797 | return -EFAULT; | 817 | return -EFAULT; |
@@ -1003,8 +1023,7 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, | |||
1003 | return -EFAULT; | 1023 | return -EFAULT; |
1004 | 1024 | ||
1005 | ret = 0; | 1025 | ret = 0; |
1006 | if ((u & ~(IFF_VNET_HDR | IFF_MULTI_QUEUE)) != | 1026 | if ((u & ~MACVTAP_FEATURES) != (IFF_NO_PI | IFF_TAP)) |
1007 | (IFF_NO_PI | IFF_TAP)) | ||
1008 | ret = -EINVAL; | 1027 | ret = -EINVAL; |
1009 | else | 1028 | else |
1010 | q->flags = u; | 1029 | q->flags = u; |
@@ -1036,8 +1055,7 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, | |||
1036 | return ret; | 1055 | return ret; |
1037 | 1056 | ||
1038 | case TUNGETFEATURES: | 1057 | case TUNGETFEATURES: |
1039 | if (put_user(IFF_TAP | IFF_NO_PI | IFF_VNET_HDR | | 1058 | if (put_user(IFF_TAP | IFF_NO_PI | MACVTAP_FEATURES, up)) |
1040 | IFF_MULTI_QUEUE, up)) | ||
1041 | return -EFAULT; | 1059 | return -EFAULT; |
1042 | return 0; | 1060 | return 0; |
1043 | 1061 | ||