1143 lines · 27.7 KB · C
RawDownload
/* Copyright (C) 2009 Red Hat, Inc. * Author: Michael S. Tsirkin <mst@redhat.com> * * This work is licensed under the terms of the GNU GPL, version 2. * * virtio-net server in host kernel. */ #include <linux/compat.h>#include <linux/eventfd.h>#include <linux/vhost.h>#include <linux/virtio_net.h>#include <linux/miscdevice.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/mutex.h>#include <linux/workqueue.h>#include <linux/file.h>#include <linux/slab.h> #include <linux/net.h>#include <linux/if_packet.h>#include <linux/if_arp.h>#include <linux/if_tun.h>#include <linux/if_macvlan.h>#include <linux/if_vlan.h> #include <net/sock.h> #include "vhost.h" static int experimental_zcopytx = 1;module_param(experimental_zcopytx, int, 0444);MODULE_PARM_DESC(experimental_zcopytx, "Enable Zero Copy TX;"		                       " 1 -Enable; 0 - Disable"); /* Max number of bytes transferred before requeueing the job. * Using this limit prevents one virtqueue from starving others. */#define VHOST_NET_WEIGHT 0x80000 /* MAX number of TX used buffers for outstanding zerocopy */#define VHOST_MAX_PEND 128#define VHOST_GOODCOPY_LEN 256 /* * For transmit, used buffer len is unused; we override it to track buffer * status internally; used for zerocopy tx only. *//* Lower device DMA failed */#define VHOST_DMA_FAILED_LEN	3/* Lower device DMA done */#define VHOST_DMA_DONE_LEN	2/* Lower device DMA in progress */#define VHOST_DMA_IN_PROGRESS	1/* Buffer unused */#define VHOST_DMA_CLEAR_LEN	0 #define VHOST_DMA_IS_DONE(len) ((len) >= VHOST_DMA_DONE_LEN) enum {	VHOST_NET_FEATURES = VHOST_FEATURES |			 (1ULL << VHOST_NET_F_VIRTIO_NET_HDR) |			 (1ULL << VIRTIO_NET_F_MRG_RXBUF),}; enum {	VHOST_NET_VQ_RX = 0,	VHOST_NET_VQ_TX = 1,	VHOST_NET_VQ_MAX = 2,}; struct vhost_net_ubuf_ref {	struct kref kref;	wait_queue_head_t wait;	struct vhost_virtqueue *vq;}; struct vhost_net_virtqueue {	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 sock_hlen;	/* vhost zerocopy support fields below: */	/* last used idx for outstanding DMA zerocopy buffers */	int upend_idx;	/* first used idx for DMA done zerocopy buffers */	int done_idx;	/* an array of userspace buffers info */	struct ubuf_info *ubuf_info;	/* Reference counting for outstanding ubufs.	 * Protected by vq mutex. Writers must also take device mutex. */	struct vhost_net_ubuf_ref *ubufs;}; struct vhost_net {	struct vhost_dev dev;	struct vhost_net_virtqueue vqs[VHOST_NET_VQ_MAX];	struct vhost_poll poll[VHOST_NET_VQ_MAX];	/* Number of TX recently submitted.	 * Protected by tx vq lock. */	unsigned tx_packets;	/* Number of times zerocopy TX recently failed.	 * Protected by tx vq lock. */	unsigned tx_zcopy_err;	/* Flush in progress. Protected by tx vq lock. */	bool tx_flush;}; static unsigned vhost_net_zcopy_mask __read_mostly; static void vhost_net_enable_zcopy(int vq){	vhost_net_zcopy_mask |= 0x1 << vq;} static void vhost_net_zerocopy_done_signal(struct kref *kref){	struct vhost_net_ubuf_ref *ubufs; 	ubufs = container_of(kref, struct vhost_net_ubuf_ref, kref);	wake_up(&ubufs->wait);} static struct vhost_net_ubuf_ref *vhost_net_ubuf_alloc(struct vhost_virtqueue *vq, bool zcopy){	struct vhost_net_ubuf_ref *ubufs;	/* No zero copy backend? Nothing to count. */	if (!zcopy)		return NULL;	ubufs = kmalloc(sizeof(*ubufs), GFP_KERNEL);	if (!ubufs)		return ERR_PTR(-ENOMEM);	kref_init(&ubufs->kref);	init_waitqueue_head(&ubufs->wait);	ubufs->vq = vq;	return ubufs;} static void vhost_net_ubuf_put(struct vhost_net_ubuf_ref *ubufs){	kref_put(&ubufs->kref, vhost_net_zerocopy_done_signal);} static void vhost_net_ubuf_put_and_wait(struct vhost_net_ubuf_ref *ubufs){	kref_put(&ubufs->kref, vhost_net_zerocopy_done_signal);	wait_event(ubufs->wait, !atomic_read(&ubufs->kref.refcount));} static void vhost_net_ubuf_put_wait_and_free(struct vhost_net_ubuf_ref *ubufs){	vhost_net_ubuf_put_and_wait(ubufs);	kfree(ubufs);} static void vhost_net_clear_ubuf_info(struct vhost_net *n){	int i; 	for (i = 0; i < VHOST_NET_VQ_MAX; ++i) {		kfree(n->vqs[i].ubuf_info);		n->vqs[i].ubuf_info = NULL;	}} static int vhost_net_set_ubuf_info(struct vhost_net *n){	bool zcopy;	int i; 	for (i = 0; i < VHOST_NET_VQ_MAX; ++i) {		zcopy = vhost_net_zcopy_mask & (0x1 << i);		if (!zcopy)			continue;		n->vqs[i].ubuf_info = kmalloc(sizeof(*n->vqs[i].ubuf_info) *					      UIO_MAXIOV, GFP_KERNEL);		if  (!n->vqs[i].ubuf_info)			goto err;	}	return 0; err:	vhost_net_clear_ubuf_info(n);	return -ENOMEM;} static void vhost_net_vq_reset(struct vhost_net *n){	int i; 	vhost_net_clear_ubuf_info(n); 	for (i = 0; i < VHOST_NET_VQ_MAX; i++) {		n->vqs[i].done_idx = 0;		n->vqs[i].upend_idx = 0;		n->vqs[i].ubufs = NULL;		n->vqs[i].vhost_hlen = 0;		n->vqs[i].sock_hlen = 0;	} } static void vhost_net_tx_packet(struct vhost_net *net){	++net->tx_packets;	if (net->tx_packets < 1024)		return;	net->tx_packets = 0;	net->tx_zcopy_err = 0;} static void vhost_net_tx_err(struct vhost_net *net){	++net->tx_zcopy_err;} static bool vhost_net_tx_select_zcopy(struct vhost_net *net){	/* TX flush waits for outstanding DMAs to be done.	 * Don't start new DMAs.	 */	return !net->tx_flush &&		net->tx_packets / 64 >= net->tx_zcopy_err;} static bool vhost_sock_zcopy(struct socket *sock){	return unlikely(experimental_zcopytx) &&		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. * 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 * guest used idx. */static void vhost_zerocopy_signal_used(struct vhost_net *net,				       struct vhost_virtqueue *vq){	struct vhost_net_virtqueue *nvq =		container_of(vq, struct vhost_net_virtqueue, vq);	int i, add;	int j = 0; 	for (i = nvq->done_idx; i != nvq->upend_idx; i = (i + 1) % UIO_MAXIOV) {		if (vq->heads[i].len == VHOST_DMA_FAILED_LEN)			vhost_net_tx_err(net);		if (VHOST_DMA_IS_DONE(vq->heads[i].len)) {			vq->heads[i].len = VHOST_DMA_CLEAR_LEN;			++j;		} else			break;	}	while (j) {		add = min(UIO_MAXIOV - nvq->done_idx, j);		vhost_add_used_and_signal_n(vq->dev, vq,					    &vq->heads[nvq->done_idx], add);		nvq->done_idx = (nvq->done_idx + add) % UIO_MAXIOV;		j -= add;	}} static void vhost_zerocopy_callback(struct ubuf_info *ubuf, bool success){	struct vhost_net_ubuf_ref *ubufs = ubuf->ctx;	struct vhost_virtqueue *vq = ubufs->vq;	int cnt = atomic_read(&ubufs->kref.refcount); 	/* set len to mark this desc buffers done DMA */	vq->heads[ubuf->desc].len = success ?		VHOST_DMA_DONE_LEN : VHOST_DMA_FAILED_LEN;	vhost_net_ubuf_put(ubufs); 	/*	 * Trigger polling thread if guest stopped submitting new buffers:	 * in this case, the refcount after decrement will eventually reach 1	 * so here it is 2.	 * We also trigger polling periodically after each 16 packets	 * (the value 16 here is more or less arbitrary, it's tuned to trigger	 * less than 10% of times).	 */	if (cnt <= 2 || !(cnt % 16))		vhost_poll_queue(&vq->poll);} /* Expects to be always run from workqueue - which acts as * read-size critical section for our kind of RCU. */static void handle_tx(struct vhost_net *net){	struct vhost_net_virtqueue *nvq = &net->vqs[VHOST_NET_VQ_TX];	struct vhost_virtqueue *vq = &nvq->vq;	unsigned out, in, s;	int head;	struct msghdr msg = {		.msg_name = NULL,		.msg_namelen = 0,		.msg_control = NULL,		.msg_controllen = 0,		.msg_iov = vq->iov,		.msg_flags = MSG_DONTWAIT,	};	size_t len, total_len = 0;	int err;	size_t hdr_size;	struct socket *sock;	struct vhost_net_ubuf_ref *uninitialized_var(ubufs);	bool zcopy, zcopy_used; 	mutex_lock(&vq->mutex);	sock = vq->private_data;	if (!sock)		goto out; 	vhost_disable_notify(&net->dev, vq); 	hdr_size = nvq->vhost_hlen;	zcopy = nvq->ubufs; 	for (;;) {		/* Release DMAs done buffers first */		if (zcopy)			vhost_zerocopy_signal_used(net, vq); 		/* If more outstanding DMAs, queue the work.		 * Handle upend_idx wrap around		 */		if (unlikely((nvq->upend_idx + vq->num - VHOST_MAX_PEND)			      % UIO_MAXIOV == nvq->done_idx))			break; 		head = vhost_get_vq_desc(&net->dev, vq, vq->iov,					 ARRAY_SIZE(vq->iov),					 &out, &in,					 NULL, NULL);		/* On error, stop handling until the next kick. */		if (unlikely(head < 0))			break;		/* Nothing new?  Wait for eventfd to tell us they refilled. */		if (head == vq->num) {			if (unlikely(vhost_enable_notify(&net->dev, vq))) {				vhost_disable_notify(&net->dev, vq);				continue;			}			break;		}		if (in) {			vq_err(vq, "Unexpected descriptor format for TX: "			       "out %d, int %d\n", out, in);			break;		}		/* Skip header. TODO: support TSO. */		s = move_iovec_hdr(vq->iov, nvq->hdr, hdr_size, out);		msg.msg_iovlen = out;		len = iov_length(vq->iov, out);		/* Sanity check */		if (!len) {			vq_err(vq, "Unexpected header len for TX: "			       "%zd expected %zd\n",			       iov_length(nvq->hdr, s), hdr_size);			break;		} 		zcopy_used = zcopy && len >= VHOST_GOODCOPY_LEN				   && (nvq->upend_idx + 1) % UIO_MAXIOV !=				      nvq->done_idx				   && vhost_net_tx_select_zcopy(net); 		/* use msg_control to pass vhost zerocopy ubuf info to skb */		if (zcopy_used) {			struct ubuf_info *ubuf;			ubuf = nvq->ubuf_info + nvq->upend_idx; 			vq->heads[nvq->upend_idx].id = head;			vq->heads[nvq->upend_idx].len = VHOST_DMA_IN_PROGRESS;			ubuf->callback = vhost_zerocopy_callback;			ubuf->ctx = nvq->ubufs;			ubuf->desc = nvq->upend_idx;			msg.msg_control = ubuf;			msg.msg_controllen = sizeof(ubuf);			ubufs = nvq->ubufs;			kref_get(&ubufs->kref);			nvq->upend_idx = (nvq->upend_idx + 1) % UIO_MAXIOV;		} else {			msg.msg_control = NULL;			ubufs = NULL;		}		/* TODO: Check specific error and bomb out unless ENOBUFS? */		err = sock->ops->sendmsg(NULL, sock, &msg, len);		if (unlikely(err < 0)) {			if (zcopy_used) {				vhost_net_ubuf_put(ubufs);				nvq->upend_idx = ((unsigned)nvq->upend_idx - 1)					% UIO_MAXIOV;			}			vhost_discard_vq_desc(vq, 1);			break;		}		if (err != len)			pr_debug("Truncated TX packet: "				 " len %d != %zd\n", err, len);		if (!zcopy_used)			vhost_add_used_and_signal(&net->dev, vq, head, 0);		else			vhost_zerocopy_signal_used(net, vq);		total_len += len;		vhost_net_tx_packet(net);		if (unlikely(total_len >= VHOST_NET_WEIGHT)) {			vhost_poll_queue(&vq->poll);			break;		}	}out:	mutex_unlock(&vq->mutex);} static int peek_head_len(struct sock *sk){	struct sk_buff *head;	int len = 0;	unsigned long flags; 	spin_lock_irqsave(&sk->sk_receive_queue.lock, flags);	head = skb_peek(&sk->sk_receive_queue);	if (likely(head)) {		len = head->len;		if (vlan_tx_tag_present(head))			len += VLAN_HLEN;	} 	spin_unlock_irqrestore(&sk->sk_receive_queue.lock, flags);	return len;} /* This is a multi-buffer version of vhost_get_desc, that works if *	vq has read descriptors only. * @vq		- the relevant virtqueue * @datalen	- data length we'll be reading * @iovcount	- returned count of io vectors we fill * @log		- vhost log * @log_num	- log offset * @quota       - headcount quota, 1 for big buffer *	returns number of buffer heads allocated, negative on error */static int get_rx_bufs(struct vhost_virtqueue *vq,		       struct vring_used_elem *heads,		       int datalen,		       unsigned *iovcount,		       struct vhost_log *log,		       unsigned *log_num,		       unsigned int quota){	unsigned int out, in;	int seg = 0;	int headcount = 0;	unsigned d;	int r, nlogs = 0; 	while (datalen > 0 && headcount < quota) {

Lines 1–500 of 1143.