aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorJason Wang <jasowang@redhat.com>2013-07-17 22:55:16 -0400
committerDavid S. Miller <davem@davemloft.net>2013-07-18 16:04:25 -0400
commitece793fcfc417b3925844be88a6a6dc82ae8f7c6 (patch)
tree82348926544697293154d9870ce166c06fb6919f /drivers/net
parent885291761dba2bfe04df4c0f7bb75e4c920ab82e (diff)
macvtap: do not zerocopy if iov needs more pages than MAX_SKB_FRAGS
We try to linearize part of the skb when the number of iov is greater than MAX_SKB_FRAGS. This is not enough since each single vector may occupy more than one pages, so zerocopy_sg_fromiovec() may still fail and may break the guest network. Solve this problem by calculate the pages needed for iov before trying to do zerocopy and switch to use copy instead of zerocopy if it needs more than MAX_SKB_FRAGS. This is done through introducing a new helper to count the pages for iov, and call uarg->callback() manually when switching from zerocopy to copy to notify vhost. We can do further optimization on top. This bug were introduced from b92946e2919134ebe2a4083e4302236295ea2a73 (macvtap: zerocopy: validate vectors before building skb). Cc: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Jason Wang <jasowang@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/macvtap.c62
1 files changed, 37 insertions, 25 deletions
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index a7c5654a2e5c..a98fb0ed6aef 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -698,6 +698,28 @@ static int macvtap_skb_to_vnet_hdr(const struct sk_buff *skb,
698 return 0; 698 return 0;
699} 699}
700 700
701static unsigned long iov_pages(const struct iovec *iv, int offset,
702 unsigned long nr_segs)
703{
704 unsigned long seg, base;
705 int pages = 0, len, size;
706
707 while (nr_segs && (offset >= iv->iov_len)) {
708 offset -= iv->iov_len;
709 ++iv;
710 --nr_segs;
711 }
712
713 for (seg = 0; seg < nr_segs; seg++) {
714 base = (unsigned long)iv[seg].iov_base + offset;
715 len = iv[seg].iov_len - offset;
716 size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT;
717 pages += size;
718 offset = 0;
719 }
720
721 return pages;
722}
701 723
702/* Get packet from user space buffer */ 724/* Get packet from user space buffer */
703static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, 725static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
@@ -744,31 +766,15 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
744 if (unlikely(count > UIO_MAXIOV)) 766 if (unlikely(count > UIO_MAXIOV))
745 goto err; 767 goto err;
746 768
747 if (m && m->msg_control && sock_flag(&q->sk, SOCK_ZEROCOPY)) 769 if (m && m->msg_control && sock_flag(&q->sk, SOCK_ZEROCOPY)) {
748 zerocopy = true; 770 copylen = vnet_hdr.hdr_len ? vnet_hdr.hdr_len : GOODCOPY_LEN;
749
750 if (zerocopy) {
751 /* Userspace may produce vectors with count greater than
752 * MAX_SKB_FRAGS, so we need to linearize parts of the skb
753 * to let the rest of data to be fit in the frags.
754 */
755 if (count > MAX_SKB_FRAGS) {
756 copylen = iov_length(iv, count - MAX_SKB_FRAGS);
757 if (copylen < vnet_hdr_len)
758 copylen = 0;
759 else
760 copylen -= vnet_hdr_len;
761 }
762 /* There are 256 bytes to be copied in skb, so there is enough
763 * room for skb expand head in case it is used.
764 * The rest buffer is mapped from userspace.
765 */
766 if (copylen < vnet_hdr.hdr_len)
767 copylen = vnet_hdr.hdr_len;
768 if (!copylen)
769 copylen = GOODCOPY_LEN;
770 linear = copylen; 771 linear = copylen;
771 } else { 772 if (iov_pages(iv, vnet_hdr_len + copylen, count)
773 <= MAX_SKB_FRAGS)
774 zerocopy = true;
775 }
776
777 if (!zerocopy) {
772 copylen = len; 778 copylen = len;
773 linear = vnet_hdr.hdr_len; 779 linear = vnet_hdr.hdr_len;
774 } 780 }
@@ -780,9 +786,15 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
780 786
781 if (zerocopy) 787 if (zerocopy)
782 err = zerocopy_sg_from_iovec(skb, iv, vnet_hdr_len, count); 788 err = zerocopy_sg_from_iovec(skb, iv, vnet_hdr_len, count);
783 else 789 else {
784 err = skb_copy_datagram_from_iovec(skb, 0, iv, vnet_hdr_len, 790 err = skb_copy_datagram_from_iovec(skb, 0, iv, vnet_hdr_len,
785 len); 791 len);
792 if (!err && m && m->msg_control) {
793 struct ubuf_info *uarg = m->msg_control;
794 uarg->callback(uarg, false);
795 }
796 }
797
786 if (err) 798 if (err)
787 goto err_kfree; 799 goto err_kfree;
788 800