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>
This commit is contained in:
		
					parent
					
						
							
								98a527aac1
							
						
					
				
			
			
				commit
				
					
						ba7438aed9
					
				
			
		
					 3 changed files with 23 additions and 88 deletions
				
			
		| 
						 | 
					@ -84,10 +84,6 @@ struct vhost_net_ubuf_ref {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct vhost_net_virtqueue {
 | 
					struct vhost_net_virtqueue {
 | 
				
			||||||
	struct vhost_virtqueue vq;
 | 
						struct vhost_virtqueue vq;
 | 
				
			||||||
	/* hdr is used to store the virtio header.
 | 
					 | 
				
			||||||
	 * Since each iovec has >= 1 byte length, we never need more than
 | 
					 | 
				
			||||||
	 * header length entries to store the header. */
 | 
					 | 
				
			||||||
	struct iovec hdr[sizeof(struct virtio_net_hdr_mrg_rxbuf)];
 | 
					 | 
				
			||||||
	size_t vhost_hlen;
 | 
						size_t vhost_hlen;
 | 
				
			||||||
	size_t sock_hlen;
 | 
						size_t sock_hlen;
 | 
				
			||||||
	/* vhost zerocopy support fields below: */
 | 
						/* vhost zerocopy support fields below: */
 | 
				
			||||||
| 
						 | 
					@ -235,44 +231,6 @@ static bool vhost_sock_zcopy(struct socket *sock)
 | 
				
			||||||
		sock_flag(sock->sk, SOCK_ZEROCOPY);
 | 
							sock_flag(sock->sk, SOCK_ZEROCOPY);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Pop first len bytes from iovec. Return number of segments used. */
 | 
					 | 
				
			||||||
static int move_iovec_hdr(struct iovec *from, struct iovec *to,
 | 
					 | 
				
			||||||
			  size_t len, int iov_count)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int seg = 0;
 | 
					 | 
				
			||||||
	size_t size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	while (len && seg < iov_count) {
 | 
					 | 
				
			||||||
		size = min(from->iov_len, len);
 | 
					 | 
				
			||||||
		to->iov_base = from->iov_base;
 | 
					 | 
				
			||||||
		to->iov_len = size;
 | 
					 | 
				
			||||||
		from->iov_len -= size;
 | 
					 | 
				
			||||||
		from->iov_base += size;
 | 
					 | 
				
			||||||
		len -= size;
 | 
					 | 
				
			||||||
		++from;
 | 
					 | 
				
			||||||
		++to;
 | 
					 | 
				
			||||||
		++seg;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return seg;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
/* Copy iovec entries for len bytes from iovec. */
 | 
					 | 
				
			||||||
static void copy_iovec_hdr(const struct iovec *from, struct iovec *to,
 | 
					 | 
				
			||||||
			   size_t len, int iovcount)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int seg = 0;
 | 
					 | 
				
			||||||
	size_t size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	while (len && seg < iovcount) {
 | 
					 | 
				
			||||||
		size = min(from->iov_len, len);
 | 
					 | 
				
			||||||
		to->iov_base = from->iov_base;
 | 
					 | 
				
			||||||
		to->iov_len = size;
 | 
					 | 
				
			||||||
		len -= size;
 | 
					 | 
				
			||||||
		++from;
 | 
					 | 
				
			||||||
		++to;
 | 
					 | 
				
			||||||
		++seg;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* In case of DMA done not in order in lower device driver for some reason.
 | 
					/* In case of DMA done not in order in lower device driver for some reason.
 | 
				
			||||||
 * upend_idx is used to track end of used idx, done_idx is used to track head
 | 
					 * upend_idx is used to track end of used idx, done_idx is used to track head
 | 
				
			||||||
 * of used idx. Once lower device DMA done contiguously, we will signal KVM
 | 
					 * 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)
 | 
				
			||||||
		.msg_controllen = 0,
 | 
							.msg_controllen = 0,
 | 
				
			||||||
		.msg_flags = MSG_DONTWAIT,
 | 
							.msg_flags = MSG_DONTWAIT,
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	struct virtio_net_hdr_mrg_rxbuf hdr = {
 | 
						struct virtio_net_hdr hdr = {
 | 
				
			||||||
		.hdr.flags = 0,
 | 
							.flags = 0,
 | 
				
			||||||
		.hdr.gso_type = VIRTIO_NET_HDR_GSO_NONE
 | 
							.gso_type = VIRTIO_NET_HDR_GSO_NONE
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	size_t total_len = 0;
 | 
						size_t total_len = 0;
 | 
				
			||||||
	int err, mergeable;
 | 
						int err, mergeable;
 | 
				
			||||||
| 
						 | 
					@ -580,6 +538,7 @@ static void handle_rx(struct vhost_net *net)
 | 
				
			||||||
	size_t vhost_hlen, sock_hlen;
 | 
						size_t vhost_hlen, sock_hlen;
 | 
				
			||||||
	size_t vhost_len, sock_len;
 | 
						size_t vhost_len, sock_len;
 | 
				
			||||||
	struct socket *sock;
 | 
						struct socket *sock;
 | 
				
			||||||
 | 
						struct iov_iter fixup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_lock(&vq->mutex);
 | 
						mutex_lock(&vq->mutex);
 | 
				
			||||||
	sock = vq->private_data;
 | 
						sock = vq->private_data;
 | 
				
			||||||
| 
						 | 
					@ -624,14 +583,19 @@ static void handle_rx(struct vhost_net *net)
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		/* We don't need to be notified again. */
 | 
							/* We don't need to be notified again. */
 | 
				
			||||||
		if (unlikely((vhost_hlen)))
 | 
							iov_iter_init(&msg.msg_iter, READ, vq->iov, in, vhost_len);
 | 
				
			||||||
			/* Skip header. TODO: support TSO. */
 | 
							fixup = msg.msg_iter;
 | 
				
			||||||
			move_iovec_hdr(vq->iov, nvq->hdr, vhost_hlen, in);
 | 
							if (unlikely((vhost_hlen))) {
 | 
				
			||||||
		else
 | 
								/* We will supply the header ourselves
 | 
				
			||||||
			/* Copy the header for use in VIRTIO_NET_F_MRG_RXBUF:
 | 
								 * TODO: support TSO.
 | 
				
			||||||
			 * needed because recvmsg can modify msg_iov. */
 | 
								 */
 | 
				
			||||||
			copy_iovec_hdr(vq->iov, nvq->hdr, sock_hlen, in);
 | 
								iov_iter_advance(&msg.msg_iter, vhost_hlen);
 | 
				
			||||||
		iov_iter_init(&msg.msg_iter, READ, vq->iov, in, sock_len);
 | 
							} else {
 | 
				
			||||||
 | 
								/* It'll come from socket; we'll need to patch
 | 
				
			||||||
 | 
								 * ->num_buffers over if VIRTIO_NET_F_MRG_RXBUF
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								iov_iter_advance(&fixup, sizeof(hdr));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		err = sock->ops->recvmsg(NULL, sock, &msg,
 | 
							err = sock->ops->recvmsg(NULL, sock, &msg,
 | 
				
			||||||
					 sock_len, MSG_DONTWAIT | MSG_TRUNC);
 | 
										 sock_len, MSG_DONTWAIT | MSG_TRUNC);
 | 
				
			||||||
		/* Userspace might have consumed the packet meanwhile:
 | 
							/* Userspace might have consumed the packet meanwhile:
 | 
				
			||||||
| 
						 | 
					@ -643,18 +607,18 @@ static void handle_rx(struct vhost_net *net)
 | 
				
			||||||
			vhost_discard_vq_desc(vq, headcount);
 | 
								vhost_discard_vq_desc(vq, headcount);
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							/* Supply virtio_net_hdr if VHOST_NET_F_VIRTIO_NET_HDR */
 | 
				
			||||||
		if (unlikely(vhost_hlen) &&
 | 
							if (unlikely(vhost_hlen) &&
 | 
				
			||||||
		    memcpy_toiovecend(nvq->hdr, (unsigned char *)&hdr, 0,
 | 
							    copy_to_iter(&hdr, sizeof(hdr), &fixup) != sizeof(hdr)) {
 | 
				
			||||||
				      vhost_hlen)) {
 | 
					 | 
				
			||||||
			vq_err(vq, "Unable to write vnet_hdr at addr %p\n",
 | 
								vq_err(vq, "Unable to write vnet_hdr at addr %p\n",
 | 
				
			||||||
			       vq->iov->iov_base);
 | 
								       vq->iov->iov_base);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		/* TODO: Should check and handle checksum. */
 | 
							/* Supply (or replace) ->num_buffers if VIRTIO_NET_F_MRG_RXBUF
 | 
				
			||||||
 | 
							 * TODO: Should check and handle checksum.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
		if (likely(mergeable) &&
 | 
							if (likely(mergeable) &&
 | 
				
			||||||
		    memcpy_toiovecend(nvq->hdr, (unsigned char *)&headcount,
 | 
							    copy_to_iter(&headcount, 2, &fixup) != 2) {
 | 
				
			||||||
				      offsetof(typeof(hdr), num_buffers),
 | 
					 | 
				
			||||||
				      sizeof hdr.num_buffers)) {
 | 
					 | 
				
			||||||
			vq_err(vq, "Failed num_buffers write");
 | 
								vq_err(vq, "Failed num_buffers write");
 | 
				
			||||||
			vhost_discard_vq_desc(vq, headcount);
 | 
								vhost_discard_vq_desc(vq, headcount);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -137,7 +137,4 @@ size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum, struct io
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov,
 | 
					int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov,
 | 
				
			||||||
			int offset, int len);
 | 
								int offset, int len);
 | 
				
			||||||
int memcpy_toiovecend(const struct iovec *v, unsigned char *kdata,
 | 
					 | 
				
			||||||
		      int offset, int len);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										26
									
								
								lib/iovec.c
									
										
									
									
									
								
							
							
						
						
									
										26
									
								
								lib/iovec.c
									
										
									
									
									
								
							| 
						 | 
					@ -2,32 +2,6 @@
 | 
				
			||||||
#include <linux/export.h>
 | 
					#include <linux/export.h>
 | 
				
			||||||
#include <linux/uio.h>
 | 
					#include <linux/uio.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 *	Copy kernel to iovec. Returns -EFAULT on error.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata,
 | 
					 | 
				
			||||||
		      int offset, int len)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int copy;
 | 
					 | 
				
			||||||
	for (; len > 0; ++iov) {
 | 
					 | 
				
			||||||
		/* Skip over the finished iovecs */
 | 
					 | 
				
			||||||
		if (unlikely(offset >= iov->iov_len)) {
 | 
					 | 
				
			||||||
			offset -= iov->iov_len;
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		copy = min_t(unsigned int, iov->iov_len - offset, len);
 | 
					 | 
				
			||||||
		if (copy_to_user(iov->iov_base + offset, kdata, copy))
 | 
					 | 
				
			||||||
			return -EFAULT;
 | 
					 | 
				
			||||||
		offset = 0;
 | 
					 | 
				
			||||||
		kdata += copy;
 | 
					 | 
				
			||||||
		len -= copy;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
EXPORT_SYMBOL(memcpy_toiovecend);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 *	Copy iovec to kernel. Returns -EFAULT on error.
 | 
					 *	Copy iovec to kernel. Returns -EFAULT on error.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue