1915 lines · 47.3 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* Copyright (C) 2009 Red Hat, Inc. * Author: Michael S. Tsirkin <mst@redhat.com> * * 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 <net/xdp.h> #include "vhost.h" static int experimental_zcopytx = 0;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 packets transferred before requeueing the job. * Using this limit prevents one virtqueue from starving others with small * pkts. */#define VHOST_NET_PKT_WEIGHT 256 /* 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) static const int vhost_net_bits[] = {	VHOST_FEATURES,	VHOST_NET_F_VIRTIO_NET_HDR,	VIRTIO_NET_F_MRG_RXBUF,	VIRTIO_F_ACCESS_PLATFORM,	VIRTIO_F_RING_RESET,	VIRTIO_F_IN_ORDER,	VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO,	VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO}; enum {	VHOST_NET_BACKEND_FEATURES = (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2)}; 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;	struct rcu_head rcu;}; #define VHOST_NET_BATCH 64struct vhost_net_buf {	void **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;	/* For TX, first used idx for DMA done zerocopy buffers	 * For RX, number of batched heads	 */	int done_idx;	/* Number of XDP frames batched */	int batched_xdp;	/* an array of userspace buffers info */	struct ubuf_info_msgzc *ubuf_info;	/* Reference counting for outstanding ubufs.	 * Protected by vq mutex. Writers must also take device mutex. */	struct vhost_net_ubuf_ref *ubufs;	struct ptr_ring *rx_ring;	struct vhost_net_buf rxq;	/* Batched XDP buffs */	struct xdp_buff *xdp;}; 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;	/* Private page frag cache */	struct page_frag_cache pf_cache;}; 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 sock *sk,				 struct vhost_net_virtqueue *nvq){	struct file *file = sk->sk_socket->file;	struct vhost_net_buf *rxq = &nvq->rxq; 	rxq->head = 0;	spin_lock(&nvq->rx_ring->consumer_lock);	rxq->tail = __ptr_ring_consume_batched(nvq->rx_ring, rxq->queue,					       VHOST_NET_BATCH); 	if (rxq->tail)		tun_wake_queue(file, rxq->tail); 	spin_unlock(&nvq->rx_ring->consumer_lock);	return rxq->tail;} static void vhost_net_buf_unproduce(struct vhost_net_virtqueue *nvq){	struct vhost_net_buf *rxq = &nvq->rxq; 	if (nvq->rx_ring && !vhost_net_buf_is_empty(rxq)) {		ptr_ring_unconsume(nvq->rx_ring, rxq->queue + rxq->head,				   vhost_net_buf_get_size(rxq),				   tun_ptr_free);		rxq->head = rxq->tail = 0;	}} static int vhost_net_buf_peek_len(void *ptr){	if (tun_is_xdp_frame(ptr)) {		struct xdp_frame *xdpf = tun_ptr_to_xdp(ptr); 		return xdpf->len;	} 	return __skb_array_len_with_tag(ptr);} static int vhost_net_buf_peek(struct sock *sk,			      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(sk, nvq))		return 0; out:	return vhost_net_buf_peek_len(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_obj(*ubufs);	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; 	rcu_read_lock();	r = atomic_sub_return(1, &ubufs->refcount);	if (unlikely(!r))		wake_up(&ubufs->wait);	rcu_read_unlock();	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_rcu(ubufs, rcu);} 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_objs(*n->vqs[i].ubuf_info, UIO_MAXIOV);		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);} static bool vhost_sock_xdp(struct socket *sock){	return sock_flag(sock->sk, SOCK_XDP);} /* 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],					    NULL, add);		nvq->done_idx = (nvq->done_idx + add) % UIO_MAXIOV;		j -= add;	}} static void vhost_zerocopy_complete(struct sk_buff *skb,				    struct ubuf_info *ubuf_base, bool success){	struct ubuf_info_msgzc *ubuf;	struct vhost_net_ubuf_ref *ubufs;	struct vhost_virtqueue *vq;	int cnt; 	/* Only the final cloned skb reference completes the vhost descriptor. */	if (!refcount_dec_and_test(&ubuf_base->refcnt))		return; 	ubuf = uarg_to_msgzc(ubuf_base);	ubufs = ubuf->ctx;	vq = ubufs->vq; 	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 const struct ubuf_info_ops vhost_ubuf_ops = {	.complete = vhost_zerocopy_complete,}; static inline unsigned long busy_clock(void){	return local_clock() >> 10;} static bool vhost_can_busy_poll(unsigned long endtime){	return likely(!need_resched() && !time_after(busy_clock(), endtime) &&		      !signal_pending(current));} 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 (!vhost_vq_get_backend(vq))		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 = vhost_vq_get_backend(vq);	if (!sock)		return 0; 	return vhost_poll_start(poll, sock->file);} static void vhost_net_signal_used(struct vhost_net_virtqueue *nvq,				  unsigned int count){	struct vhost_virtqueue *vq = &nvq->vq;	struct vhost_dev *dev = vq->dev; 	if (!nvq->done_idx)		return; 	vhost_add_used_and_signal_n(dev, vq, vq->heads,				    vq->nheads, count);	nvq->done_idx = 0;} static void vhost_tx_batch(struct vhost_net *net,			   struct vhost_net_virtqueue *nvq,			   struct socket *sock,			   struct msghdr *msghdr){	struct vhost_virtqueue *vq = &nvq->vq;	bool in_order = vhost_has_feature(vq, VIRTIO_F_IN_ORDER);	struct tun_msg_ctl ctl = {		.type = TUN_MSG_PTR,		.num = nvq->batched_xdp,		.ptr = nvq->xdp,	};

Lines 1–500 of 1915.