891 lines · 21.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/rcupdate.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;module_param(experimental_zcopytx, int, 0444);MODULE_PARM_DESC(experimental_zcopytx, "Enable Experimental Zero Copy TX"); /* 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 enum {	VHOST_NET_VQ_RX = 0,	VHOST_NET_VQ_TX = 1,	VHOST_NET_VQ_MAX = 2,}; enum vhost_net_poll_state {	VHOST_NET_POLL_DISABLED = 0,	VHOST_NET_POLL_STARTED = 1,	VHOST_NET_POLL_STOPPED = 2,}; struct vhost_net {	struct vhost_dev dev;	struct vhost_virtqueue vqs[VHOST_NET_VQ_MAX];	struct vhost_poll poll[VHOST_NET_VQ_MAX];	/* Tells us whether we are polling a socket for TX.	 * We only do this when socket buffer fills up.	 * Protected by tx vq lock. */	enum vhost_net_poll_state tx_poll_state;}; 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;	}} /* Caller must have TX VQ lock */static void tx_poll_stop(struct vhost_net *net){	if (likely(net->tx_poll_state != VHOST_NET_POLL_STARTED))		return;	vhost_poll_stop(net->poll + VHOST_NET_VQ_TX);	net->tx_poll_state = VHOST_NET_POLL_STOPPED;} /* Caller must have TX VQ lock */static void tx_poll_start(struct vhost_net *net, struct socket *sock){	if (unlikely(net->tx_poll_state != VHOST_NET_POLL_STOPPED))		return;	vhost_poll_start(net->poll + VHOST_NET_VQ_TX, sock->file);	net->tx_poll_state = VHOST_NET_POLL_STARTED;} /* 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_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_TX];	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, wmem;	size_t hdr_size;	struct socket *sock;	struct vhost_ubuf_ref *uninitialized_var(ubufs);	bool zcopy; 	/* TODO: check that we are running from vhost_worker? */	sock = rcu_dereference_check(vq->private_data, 1);	if (!sock)		return; 	wmem = atomic_read(&sock->sk->sk_wmem_alloc);	if (wmem >= sock->sk->sk_sndbuf) {		mutex_lock(&vq->mutex);		tx_poll_start(net, sock);		mutex_unlock(&vq->mutex);		return;	} 	mutex_lock(&vq->mutex);	vhost_disable_notify(&net->dev, vq); 	if (wmem < sock->sk->sk_sndbuf / 2)		tx_poll_stop(net);	hdr_size = vq->vhost_hlen;	zcopy = vq->ubufs; 	for (;;) {		/* Release DMAs done buffers first */		if (zcopy)			vhost_zerocopy_signal_used(vq); 		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) {			int num_pends; 			wmem = atomic_read(&sock->sk->sk_wmem_alloc);			if (wmem >= sock->sk->sk_sndbuf * 3 / 4) {				tx_poll_start(net, sock);				set_bit(SOCK_ASYNC_NOSPACE, &sock->flags);				break;			}			/* If more outstanding DMAs, queue the work.			 * Handle upend_idx wrap around			 */			num_pends = likely(vq->upend_idx >= vq->done_idx) ?				    (vq->upend_idx - vq->done_idx) :				    (vq->upend_idx + UIO_MAXIOV - vq->done_idx);			if (unlikely(num_pends > VHOST_MAX_PEND)) {				tx_poll_start(net, sock);				set_bit(SOCK_ASYNC_NOSPACE, &sock->flags);				break;			}			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, vq->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(vq->hdr, s), hdr_size);			break;		}		/* use msg_control to pass vhost zerocopy ubuf info to skb */		if (zcopy) {			vq->heads[vq->upend_idx].id = head;			if (len < VHOST_GOODCOPY_LEN) {				/* copy don't need to wait for DMA done */				vq->heads[vq->upend_idx].len =							VHOST_DMA_DONE_LEN;				msg.msg_control = NULL;				msg.msg_controllen = 0;				ubufs = NULL;			} else {				struct ubuf_info *ubuf = &vq->ubuf_info[head]; 				vq->heads[vq->upend_idx].len = len;				ubuf->callback = vhost_zerocopy_callback;				ubuf->ctx = vq->ubufs;				ubuf->desc = vq->upend_idx;				msg.msg_control = ubuf;				msg.msg_controllen = sizeof(ubuf);				ubufs = vq->ubufs;				kref_get(&ubufs->kref);			}			vq->upend_idx = (vq->upend_idx + 1) % UIO_MAXIOV;		}		/* TODO: Check specific error and bomb out unless ENOBUFS? */		err = sock->ops->sendmsg(NULL, sock, &msg, len);		if (unlikely(err < 0)) {			if (zcopy) {				if (ubufs)					vhost_ubuf_put(ubufs);				vq->upend_idx = ((unsigned)vq->upend_idx - 1) %					UIO_MAXIOV;			}			vhost_discard_vq_desc(vq, 1);			if (err == -EAGAIN || err == -ENOBUFS)				tx_poll_start(net, sock);			break;		}		if (err != len)			pr_debug("Truncated TX packet: "				 " len %d != %zd\n", err, len);		if (!zcopy)			vhost_add_used_and_signal(&net->dev, vq, head, 0);		else			vhost_zerocopy_signal_used(vq);		total_len += len;		if (unlikely(total_len >= VHOST_NET_WEIGHT)) {			vhost_poll_queue(&vq->poll);			break;		}	} 	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) {		if (unlikely(seg >= UIO_MAXIOV)) {			r = -ENOBUFS;			goto err;		}		d = vhost_get_vq_desc(vq->dev, vq, vq->iov + seg,				      ARRAY_SIZE(vq->iov) - seg, &out,				      &in, log, log_num);		if (d == vq->num) {			r = 0;			goto err;		}		if (unlikely(out || in <= 0)) {			vq_err(vq, "unexpected descriptor format for RX: "				"out %d, in %d\n", out, in);			r = -EINVAL;			goto err;		}		if (unlikely(log)) {			nlogs += *log_num;			log += *log_num;		}		heads[headcount].id = d;		heads[headcount].len = iov_length(vq->iov + seg, in);		datalen -= heads[headcount].len;		++headcount;		seg += in;	}	heads[headcount - 1].len += datalen;	*iovcount = seg;	if (unlikely(log))		*log_num = nlogs;	return headcount;err:	vhost_discard_vq_desc(vq, headcount);	return r;} /* Expects to be always run from workqueue - which acts as * read-size critical section for our kind of RCU. */static void handle_rx(struct vhost_net *net){	struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_RX];	unsigned uninitialized_var(in), log;	struct vhost_log *vq_log;	struct msghdr msg = {		.msg_name = NULL,		.msg_namelen = 0,		.msg_control = NULL, /* FIXME: get and handle RX aux data. */		.msg_controllen = 0,		.msg_iov = vq->iov,		.msg_flags = MSG_DONTWAIT,	};	struct virtio_net_hdr_mrg_rxbuf hdr = {		.hdr.flags = 0,		.hdr.gso_type = VIRTIO_NET_HDR_GSO_NONE	};	size_t total_len = 0;	int err, headcount, mergeable;	size_t vhost_hlen, sock_hlen;	size_t vhost_len, sock_len;	/* TODO: check that we are running from vhost_worker? */	struct socket *sock = rcu_dereference_check(vq->private_data, 1); 	if (!sock)		return; 	mutex_lock(&vq->mutex);	vhost_disable_notify(&net->dev, vq);	vhost_hlen = vq->vhost_hlen;	sock_hlen = vq->sock_hlen; 	vq_log = unlikely(vhost_has_feature(&net->dev, VHOST_F_LOG_ALL)) ?		vq->log : NULL;	mergeable = vhost_has_feature(&net->dev, VIRTIO_NET_F_MRG_RXBUF); 	while ((sock_len = peek_head_len(sock->sk))) {		sock_len += sock_hlen;		vhost_len = sock_len + vhost_hlen;		headcount = get_rx_bufs(vq, vq->heads, vhost_len,					&in, vq_log, &log,					likely(mergeable) ? UIO_MAXIOV : 1);		/* On error, stop handling until the next kick. */		if (unlikely(headcount < 0))			break;		/* OK, now we need to know about added descriptors. */		if (!headcount) {			if (unlikely(vhost_enable_notify(&net->dev, vq))) {				/* They have slipped one in as we were				 * doing that: check again. */				vhost_disable_notify(&net->dev, vq);				continue;			}			/* Nothing new?  Wait for eventfd to tell us			 * they refilled. */			break;		}		/* We don't need to be notified again. */		if (unlikely((vhost_hlen)))			/* Skip header. TODO: support TSO. */			move_iovec_hdr(vq->iov, vq->hdr, vhost_hlen, in);		else			/* Copy the header for use in VIRTIO_NET_F_MRG_RXBUF:			 * needed because recvmsg can modify msg_iov. */			copy_iovec_hdr(vq->iov, vq->hdr, sock_hlen, in);		msg.msg_iovlen = in;		err = sock->ops->recvmsg(NULL, sock, &msg,					 sock_len, MSG_DONTWAIT | MSG_TRUNC);		/* Userspace might have consumed the packet meanwhile:		 * it's not supposed to do this usually, but might be hard		 * to prevent. Discard data we got (if any) and keep going. */		if (unlikely(err != sock_len)) {			pr_debug("Discarded rx packet: "				 " len %d, expected %zd\n", err, sock_len);			vhost_discard_vq_desc(vq, headcount);			continue;		}		if (unlikely(vhost_hlen) &&		    memcpy_toiovecend(vq->hdr, (unsigned char *)&hdr, 0,				      vhost_hlen)) {			vq_err(vq, "Unable to write vnet_hdr at addr %p\n",			       vq->iov->iov_base);			break;		}		/* TODO: Should check and handle checksum. */		if (likely(mergeable) &&		    memcpy_toiovecend(vq->hdr, (unsigned char *)&headcount,				      offsetof(typeof(hdr), num_buffers),				      sizeof hdr.num_buffers)) {			vq_err(vq, "Failed num_buffers write");			vhost_discard_vq_desc(vq, headcount);			break;		}		vhost_add_used_and_signal_n(&net->dev, vq, vq->heads,					    headcount);		if (unlikely(vq_log))			vhost_log_write(vq, vq_log, log, vhost_len);		total_len += vhost_len;		if (unlikely(total_len >= VHOST_NET_WEIGHT)) {			vhost_poll_queue(&vq->poll);			break;		}	} 	mutex_unlock(&vq->mutex);} static void handle_tx_kick(struct vhost_work *work){	struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,						  poll.work);	struct vhost_net *net = container_of(vq->dev, struct vhost_net, dev); 	handle_tx(net);} static void handle_rx_kick(struct vhost_work *work){	struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,						  poll.work);	struct vhost_net *net = container_of(vq->dev, struct vhost_net, dev); 	handle_rx(net);} static void handle_tx_net(struct vhost_work *work){	struct vhost_net *net = container_of(work, struct vhost_net,					     poll[VHOST_NET_VQ_TX].work);	handle_tx(net);} static void handle_rx_net(struct vhost_work *work){	struct vhost_net *net = container_of(work, struct vhost_net,					     poll[VHOST_NET_VQ_RX].work);	handle_rx(net);

Lines 1–500 of 891.