Skip to content

Commit ece793f

Browse files
jasowangdavem330
authored andcommitted
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 b92946e (macvtap: zerocopy: validate vectors before building skb). Cc: Michael S. Tsirkin <[email protected]> Signed-off-by: Jason Wang <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 8852917 commit ece793f

File tree

1 file changed

+37
-25
lines changed

1 file changed

+37
-25
lines changed

drivers/net/macvtap.c

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,28 @@ static int macvtap_skb_to_vnet_hdr(const struct sk_buff *skb,
698698
return 0;
699699
}
700700

701+
static 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+
}
701723

702724
/* Get packet from user space buffer */
703725
static 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,
744766
if (unlikely(count > UIO_MAXIOV))
745767
goto err;
746768

747-
if (m && m->msg_control && sock_flag(&q->sk, SOCK_ZEROCOPY))
748-
zerocopy = true;
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;
769+
if (m && m->msg_control && sock_flag(&q->sk, SOCK_ZEROCOPY)) {
770+
copylen = vnet_hdr.hdr_len ? vnet_hdr.hdr_len : GOODCOPY_LEN;
770771
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) {
772778
copylen = len;
773779
linear = vnet_hdr.hdr_len;
774780
}
@@ -780,9 +786,15 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
780786

781787
if (zerocopy)
782788
err = zerocopy_sg_from_iovec(skb, iv, vnet_hdr_len, count);
783-
else
789+
else {
784790
err = skb_copy_datagram_from_iovec(skb, 0, iv, vnet_hdr_len,
785791
len);
792+
if (!err && m && m->msg_control) {
793+
struct ubuf_info *uarg = m->msg_control;
794+
uarg->callback(uarg, false);
795+
}
796+
}
797+
786798
if (err)
787799
goto err_kfree;
788800

0 commit comments

Comments
 (0)