diff options
| author | Al Viro <viro@zeniv.linux.org.uk> | 2014-12-10 15:51:28 -0500 |
|---|---|---|
| committer | Al Viro <viro@zeniv.linux.org.uk> | 2015-02-04 01:34:16 -0500 |
| commit | ba7438aed924133df54a60e4cd5499d359bcf2a8 (patch) | |
| tree | e29c9a26421611c374667b022c9904ab527c496f | |
| parent | 98a527aac1eb198dbc4405b800e102563ed8e4dd (diff) | |
vhost: don't bother copying iovecs in handle_rx(), kill memcpy_toiovecend()
Cc: Michael S. Tsirkin <mst@redhat.com>
Cc: kvm@vger.kernel.org
Cc: virtualization@lists.linux-foundation.org
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
| -rw-r--r-- | drivers/vhost/net.c | 82 | ||||
| -rw-r--r-- | include/linux/uio.h | 3 | ||||
| -rw-r--r-- | lib/iovec.c | 26 |
3 files changed, 23 insertions, 88 deletions
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index d86cc9bb9ea4..e022cc40303d 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c | |||
| @@ -84,10 +84,6 @@ struct vhost_net_ubuf_ref { | |||
| 84 | 84 | ||
| 85 | struct vhost_net_virtqueue { | 85 | struct vhost_net_virtqueue { |
| 86 | struct vhost_virtqueue vq; | 86 | struct vhost_virtqueue vq; |
| 87 | /* hdr is used to store the virtio header. | ||
| 88 | * Since each iovec has >= 1 byte length, we never need more than | ||
| 89 | * header length entries to store the header. */ | ||
| 90 | struct iovec hdr[sizeof(struct virtio_net_hdr_mrg_rxbuf)]; | ||
| 91 | size_t vhost_hlen; | 87 | size_t vhost_hlen; |
| 92 | size_t sock_hlen; | 88 | size_t sock_hlen; |
| 93 | /* vhost zerocopy support fields below: */ | 89 | /* vhost zerocopy support fields below: */ |
| @@ -235,44 +231,6 @@ static bool vhost_sock_zcopy(struct socket *sock) | |||
| 235 | sock_flag(sock->sk, SOCK_ZEROCOPY); | 231 | sock_flag(sock->sk, SOCK_ZEROCOPY); |
| 236 | } | 232 | } |
| 237 | 233 | ||
| 238 | /* Pop first len bytes from iovec. Return number of segments used. */ | ||
| 239 | static int move_iovec_hdr(struct iovec *from, struct iovec *to, | ||
| 240 | size_t len, int iov_count) | ||
| 241 | { | ||
| 242 | int seg = 0; | ||
| 243 | size_t size; | ||
| 244 | |||
| 245 | while (len && seg < iov_count) { | ||
| 246 | size = min(from->iov_len, len); | ||
| 247 | to->iov_base = from->iov_base; | ||
| 248 | to->iov_len = size; | ||
| 249 | from->iov_len -= size; | ||
| 250 | from->iov_base += size; | ||
| 251 | len -= size; | ||
| 252 | ++from; | ||
| 253 | ++to; | ||
| 254 | ++seg; | ||
| 255 | } | ||
| 256 | return seg; | ||
| 257 | } | ||
| 258 | /* Copy iovec entries for len bytes from iovec. */ | ||
| 259 | static void copy_iovec_hdr(const struct iovec *from, struct iovec *to, | ||
| 260 | size_t len, int iovcount) | ||
| 261 | { | ||
| 262 | int seg = 0; | ||
| 263 | size_t size; | ||
| 264 | |||
| 265 | while (len && seg < iovcount) { | ||
| 266 | size = min(from->iov_len, len); | ||
| 267 | to->iov_base = from->iov_base; | ||
| 268 | to->iov_len = size; | ||
| 269 | len -= size; | ||
| 270 | ++from; | ||
| 271 | ++to; | ||
| 272 | ++seg; | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | /* In case of DMA done not in order in lower device driver for some reason. | 234 | /* In case of DMA done not in order in lower device driver for some reason. |
| 277 | * upend_idx is used to track end of used idx, done_idx is used to track head | 235 | * upend_idx is used to track end of used idx, done_idx is used to track head |
| 278 | * of used idx. Once lower device DMA done contiguously, we will signal KVM | 236 | * of used idx. Once lower device DMA done contiguously, we will signal KVM |
| @@ -570,9 +528,9 @@ static void handle_rx(struct vhost_net *net) | |||
| 570 | .msg_controllen = 0, | 528 | .msg_controllen = 0, |
| 571 | .msg_flags = MSG_DONTWAIT, | 529 | .msg_flags = MSG_DONTWAIT, |
| 572 | }; | 530 | }; |
| 573 | struct virtio_net_hdr_mrg_rxbuf hdr = { | 531 | struct virtio_net_hdr hdr = { |
| 574 | .hdr.flags = 0, | 532 | .flags = 0, |
| 575 | .hdr.gso_type = VIRTIO_NET_HDR_GSO_NONE | 533 | .gso_type = VIRTIO_NET_HDR_GSO_NONE |
| 576 | }; | 534 | }; |
| 577 | size_t total_len = 0; | 535 | size_t total_len = 0; |
| 578 | int err, mergeable; | 536 | int err, mergeable; |
| @@ -580,6 +538,7 @@ static void handle_rx(struct vhost_net *net) | |||
| 580 | size_t vhost_hlen, sock_hlen; | 538 | size_t vhost_hlen, sock_hlen; |
| 581 | size_t vhost_len, sock_len; | 539 | size_t vhost_len, sock_len; |
| 582 | struct socket *sock; | 540 | struct socket *sock; |
| 541 | struct iov_iter fixup; | ||
| 583 | 542 | ||
| 584 | mutex_lock(&vq->mutex); | 543 | mutex_lock(&vq->mutex); |
| 585 | sock = vq->private_data; | 544 | sock = vq->private_data; |
| @@ -624,14 +583,19 @@ static void handle_rx(struct vhost_net *net) | |||
| 624 | break; | 583 | break; |
| 625 | } | 584 | } |
| 626 | /* We don't need to be notified again. */ | 585 | /* We don't need to be notified again. */ |
| 627 | if (unlikely((vhost_hlen))) | 586 | iov_iter_init(&msg.msg_iter, READ, vq->iov, in, vhost_len); |
| 628 | /* Skip header. TODO: support TSO. */ | 587 | fixup = msg.msg_iter; |
| 629 | move_iovec_hdr(vq->iov, nvq->hdr, vhost_hlen, in); | 588 | if (unlikely((vhost_hlen))) { |
| 630 | else | 589 | /* We will supply the header ourselves |
| 631 | /* Copy the header for use in VIRTIO_NET_F_MRG_RXBUF: | 590 | * TODO: support TSO. |
| 632 | * needed because recvmsg can modify msg_iov. */ | 591 | */ |
| 633 | copy_iovec_hdr(vq->iov, nvq->hdr, sock_hlen, in); | 592 | iov_iter_advance(&msg.msg_iter, vhost_hlen); |
| 634 | iov_iter_init(&msg.msg_iter, READ, vq->iov, in, sock_len); | 593 | } else { |
| 594 | /* It'll come from socket; we'll need to patch | ||
| 595 | * ->num_buffers over if VIRTIO_NET_F_MRG_RXBUF | ||
| 596 | */ | ||
| 597 | iov_iter_advance(&fixup, sizeof(hdr)); | ||
| 598 | } | ||
| 635 | err = sock->ops->recvmsg(NULL, sock, &msg, | 599 | err = sock->ops->recvmsg(NULL, sock, &msg, |
| 636 | sock_len, MSG_DONTWAIT | MSG_TRUNC); | 600 | sock_len, MSG_DONTWAIT | MSG_TRUNC); |
| 637 | /* Userspace might have consumed the packet meanwhile: | 601 | /* Userspace might have consumed the packet meanwhile: |
| @@ -643,18 +607,18 @@ static void handle_rx(struct vhost_net *net) | |||
| 643 | vhost_discard_vq_desc(vq, headcount); | 607 | vhost_discard_vq_desc(vq, headcount); |
| 644 | continue; | 608 | continue; |
| 645 | } | 609 | } |
| 610 | /* Supply virtio_net_hdr if VHOST_NET_F_VIRTIO_NET_HDR */ | ||
| 646 | if (unlikely(vhost_hlen) && | 611 | if (unlikely(vhost_hlen) && |
| 647 | memcpy_toiovecend(nvq->hdr, (unsigned char *)&hdr, 0, | 612 | copy_to_iter(&hdr, sizeof(hdr), &fixup) != sizeof(hdr)) { |
| 648 | vhost_hlen)) { | ||
| 649 | vq_err(vq, "Unable to write vnet_hdr at addr %p\n", | 613 | vq_err(vq, "Unable to write vnet_hdr at addr %p\n", |
| 650 | vq->iov->iov_base); | 614 | vq->iov->iov_base); |
| 651 | break; | 615 | break; |
| 652 | } | 616 | } |
| 653 | /* TODO: Should check and handle checksum. */ | 617 | /* Supply (or replace) ->num_buffers if VIRTIO_NET_F_MRG_RXBUF |
| 618 | * TODO: Should check and handle checksum. | ||
| 619 | */ | ||
| 654 | if (likely(mergeable) && | 620 | if (likely(mergeable) && |
| 655 | memcpy_toiovecend(nvq->hdr, (unsigned char *)&headcount, | 621 | copy_to_iter(&headcount, 2, &fixup) != 2) { |
| 656 | offsetof(typeof(hdr), num_buffers), | ||
| 657 | sizeof hdr.num_buffers)) { | ||
| 658 | vq_err(vq, "Failed num_buffers write"); | 622 | vq_err(vq, "Failed num_buffers write"); |
| 659 | vhost_discard_vq_desc(vq, headcount); | 623 | vhost_discard_vq_desc(vq, headcount); |
| 660 | break; | 624 | break; |
diff --git a/include/linux/uio.h b/include/linux/uio.h index af3439f4ebf2..02bd8a92038a 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h | |||
| @@ -137,7 +137,4 @@ size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum, struct io | |||
| 137 | 137 | ||
| 138 | int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov, | 138 | int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov, |
| 139 | int offset, int len); | 139 | int offset, int len); |
| 140 | int memcpy_toiovecend(const struct iovec *v, unsigned char *kdata, | ||
| 141 | int offset, int len); | ||
| 142 | |||
| 143 | #endif | 140 | #endif |
diff --git a/lib/iovec.c b/lib/iovec.c index 4a90875c64ae..d8f17a9b1ccf 100644 --- a/lib/iovec.c +++ b/lib/iovec.c | |||
| @@ -3,32 +3,6 @@ | |||
| 3 | #include <linux/uio.h> | 3 | #include <linux/uio.h> |
| 4 | 4 | ||
| 5 | /* | 5 | /* |
| 6 | * Copy kernel to iovec. Returns -EFAULT on error. | ||
| 7 | */ | ||
| 8 | |||
| 9 | int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata, | ||
| 10 | int offset, int len) | ||
| 11 | { | ||
| 12 | int copy; | ||
| 13 | for (; len > 0; ++iov) { | ||
| 14 | /* Skip over the finished iovecs */ | ||
| 15 | if (unlikely(offset >= iov->iov_len)) { | ||
| 16 | offset -= iov->iov_len; | ||
| 17 | continue; | ||
| 18 | } | ||
| 19 | copy = min_t(unsigned int, iov->iov_len - offset, len); | ||
| 20 | if (copy_to_user(iov->iov_base + offset, kdata, copy)) | ||
| 21 | return -EFAULT; | ||
| 22 | offset = 0; | ||
| 23 | kdata += copy; | ||
| 24 | len -= copy; | ||
| 25 | } | ||
| 26 | |||
| 27 | return 0; | ||
| 28 | } | ||
| 29 | EXPORT_SYMBOL(memcpy_toiovecend); | ||
| 30 | |||
| 31 | /* | ||
| 32 | * Copy iovec to kernel. Returns -EFAULT on error. | 6 | * Copy iovec to kernel. Returns -EFAULT on error. |
| 33 | */ | 7 | */ |
| 34 | 8 | ||
