1401 lines · 33.9 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/sched/clock.h>#include <linux/sched/signal.h>#include <linux/vmalloc.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_tap.h>#include <linux/if_vlan.h>#include <linux/skb_array.h>#include <linux/skbuff.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	((__force __virtio32)3)/* Lower device DMA done */#define VHOST_DMA_DONE_LEN	((__force __virtio32)2)/* Lower device DMA in progress */#define VHOST_DMA_IN_PROGRESS	((__force __virtio32)1)/* Buffer unused */#define VHOST_DMA_CLEAR_LEN	((__force __virtio32)0) #define VHOST_DMA_IS_DONE(len) ((__force u32)(len) >= (__force u32)VHOST_DMA_DONE_LEN) enum {	VHOST_NET_FEATURES = VHOST_FEATURES |			 (1ULL << VHOST_NET_F_VIRTIO_NET_HDR) |			 (1ULL << VIRTIO_NET_F_MRG_RXBUF) |			 (1ULL << VIRTIO_F_IOMMU_PLATFORM)}; enum {	VHOST_NET_VQ_RX = 0,	VHOST_NET_VQ_TX = 1,	VHOST_NET_VQ_MAX = 2,}; struct vhost_net_ubuf_ref {	/* refcount follows semantics similar to kref:	 *  0: object is released	 *  1: no outstanding ubufs	 * >1: outstanding ubufs	 */	atomic_t refcount;	wait_queue_head_t wait;	struct vhost_virtqueue *vq;}; #define VHOST_RX_BATCH 64struct vhost_net_buf {	struct sk_buff **queue;	int tail;	int head;}; struct vhost_net_virtqueue {	struct vhost_virtqueue vq;	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 skb_array *rx_array;	struct vhost_net_buf rxq;}; 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_buf_get_ptr(struct vhost_net_buf *rxq){	if (rxq->tail != rxq->head)		return rxq->queue[rxq->head];	else		return NULL;} static int vhost_net_buf_get_size(struct vhost_net_buf *rxq){	return rxq->tail - rxq->head;} static int vhost_net_buf_is_empty(struct vhost_net_buf *rxq){	return rxq->tail == rxq->head;} static void *vhost_net_buf_consume(struct vhost_net_buf *rxq){	void *ret = vhost_net_buf_get_ptr(rxq);	++rxq->head;	return ret;} static int vhost_net_buf_produce(struct vhost_net_virtqueue *nvq){	struct vhost_net_buf *rxq = &nvq->rxq; 	rxq->head = 0;	rxq->tail = skb_array_consume_batched(nvq->rx_array, rxq->queue,					      VHOST_RX_BATCH);	return rxq->tail;} static void vhost_net_buf_unproduce(struct vhost_net_virtqueue *nvq){	struct vhost_net_buf *rxq = &nvq->rxq; 	if (nvq->rx_array && !vhost_net_buf_is_empty(rxq)) {		skb_array_unconsume(nvq->rx_array, rxq->queue + rxq->head,				    vhost_net_buf_get_size(rxq));		rxq->head = rxq->tail = 0;	}} static int vhost_net_buf_peek(struct vhost_net_virtqueue *nvq){	struct vhost_net_buf *rxq = &nvq->rxq; 	if (!vhost_net_buf_is_empty(rxq))		goto out; 	if (!vhost_net_buf_produce(nvq))		return 0; out:	return __skb_array_len_with_tag(vhost_net_buf_get_ptr(rxq));} static void vhost_net_buf_init(struct vhost_net_buf *rxq){	rxq->head = rxq->tail = 0;} static void vhost_net_enable_zcopy(int vq){	vhost_net_zcopy_mask |= 0x1 << vq;} 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);	atomic_set(&ubufs->refcount, 1);	init_waitqueue_head(&ubufs->wait);	ubufs->vq = vq;	return ubufs;} static int vhost_net_ubuf_put(struct vhost_net_ubuf_ref *ubufs){	int r = atomic_sub_return(1, &ubufs->refcount);	if (unlikely(!r))		wake_up(&ubufs->wait);	return r;} static void vhost_net_ubuf_put_and_wait(struct vhost_net_ubuf_ref *ubufs){	vhost_net_ubuf_put(ubufs);	wait_event(ubufs->wait, !atomic_read(&ubufs->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;		vhost_net_buf_init(&n->vqs[i].rxq);	} } 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);} /* 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; 	rcu_read_lock_bh(); 	/* set len to mark this desc buffers done DMA */	vq->heads[ubuf->desc].len = success ?		VHOST_DMA_DONE_LEN : VHOST_DMA_FAILED_LEN;	cnt = 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.	 * 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 <= 1 || !(cnt % 16))		vhost_poll_queue(&vq->poll); 	rcu_read_unlock_bh();} static inline unsigned long busy_clock(void){	return local_clock() >> 10;} static bool vhost_can_busy_poll(struct vhost_dev *dev,				unsigned long endtime){	return likely(!need_resched()) &&	       likely(!time_after(busy_clock(), endtime)) &&	       likely(!signal_pending(current)) &&	       !vhost_has_work(dev);} static void vhost_net_disable_vq(struct vhost_net *n,				 struct vhost_virtqueue *vq){	struct vhost_net_virtqueue *nvq =		container_of(vq, struct vhost_net_virtqueue, vq);	struct vhost_poll *poll = n->poll + (nvq - n->vqs);	if (!vq->private_data)		return;	vhost_poll_stop(poll);} static int vhost_net_enable_vq(struct vhost_net *n,				struct vhost_virtqueue *vq){	struct vhost_net_virtqueue *nvq =		container_of(vq, struct vhost_net_virtqueue, vq);	struct vhost_poll *poll = n->poll + (nvq - n->vqs);	struct socket *sock; 	sock = vq->private_data;	if (!sock)		return 0; 	return vhost_poll_start(poll, sock->file);} static int vhost_net_tx_get_vq_desc(struct vhost_net *net,				    struct vhost_virtqueue *vq,				    struct iovec iov[], unsigned int iov_size,				    unsigned int *out_num, unsigned int *in_num){	unsigned long uninitialized_var(endtime);	int r = vhost_get_vq_desc(vq, vq->iov, ARRAY_SIZE(vq->iov),				  out_num, in_num, NULL, NULL); 	if (r == vq->num && vq->busyloop_timeout) {		preempt_disable();		endtime = busy_clock() + vq->busyloop_timeout;		while (vhost_can_busy_poll(vq->dev, endtime) &&		       vhost_vq_avail_empty(vq->dev, vq))			cpu_relax();		preempt_enable();		r = vhost_get_vq_desc(vq, vq->iov, ARRAY_SIZE(vq->iov),				      out_num, in_num, NULL, NULL);	} 	return r;} static bool vhost_exceeds_maxpend(struct vhost_net *net){	struct vhost_net_virtqueue *nvq = &net->vqs[VHOST_NET_VQ_TX];	struct vhost_virtqueue *vq = &nvq->vq; 	return (nvq->upend_idx + vq->num - VHOST_MAX_PEND) % UIO_MAXIOV		== nvq->done_idx;} /* 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;	int head;	struct msghdr msg = {		.msg_name = NULL,		.msg_namelen = 0,		.msg_control = NULL,		.msg_controllen = 0,		.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; 	if (!vq_iotlb_prefetch(vq))		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(vhost_exceeds_maxpend(net)))			break; 		head = vhost_net_tx_get_vq_desc(net, vq, vq->iov,						ARRAY_SIZE(vq->iov),						&out, &in);		/* 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;			}

Lines 1–500 of 1401.