1291 lines · 28.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only#include <linux/etherdevice.h>#include <linux/if_tap.h>#include <linux/if_vlan.h>#include <linux/interrupt.h>#include <linux/nsproxy.h>#include <linux/compat.h>#include <linux/if_tun.h>#include <linux/module.h>#include <linux/skbuff.h>#include <linux/cache.h>#include <linux/sched/signal.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/wait.h>#include <linux/cdev.h>#include <linux/idr.h>#include <linux/fs.h>#include <linux/uio.h> #include <net/gso.h>#include <net/net_namespace.h>#include <net/rtnetlink.h>#include <net/sock.h>#include <net/xdp.h>#include <linux/virtio_net.h>#include <linux/skb_array.h> #include "tun_vnet.h" #define TAP_IFFEATURES (IFF_VNET_HDR | IFF_MULTI_QUEUE) static struct proto tap_proto = {	.name = "tap",	.owner = THIS_MODULE,	.obj_size = sizeof(struct tap_queue),}; #define TAP_NUM_DEVS (1U << MINORBITS) static LIST_HEAD(major_list); struct major_info {	struct rcu_head rcu;	dev_t major;	struct idr minor_idr;	spinlock_t minor_lock;	const char *device_name;	struct list_head next;}; #define GOODCOPY_LEN 128 static const struct proto_ops tap_socket_ops; #define RX_OFFLOADS (NETIF_F_GRO | NETIF_F_LRO)#define TAP_FEATURES (NETIF_F_GSO | NETIF_F_SG | NETIF_F_FRAGLIST) static struct tap_dev *tap_dev_get_rcu(const struct net_device *dev){	return rcu_dereference(dev->rx_handler_data);} /* * RCU usage: * The tap_queue and the macvlan_dev are loosely coupled, the * pointers from one to the other can only be read while rcu_read_lock * or rtnl is held. * * Both the file and the macvlan_dev hold a reference on the tap_queue * through sock_hold(&q->sk). When the macvlan_dev goes away first, * q->vlan becomes inaccessible. When the files gets closed, * tap_get_queue() fails. * * There may still be references to the struct sock inside of the * queue from outbound SKBs, but these never reference back to the * file or the dev. The data structure is freed through __sk_free * when both our references and any pending SKBs are gone. */ static int tap_enable_queue(struct tap_dev *tap, struct file *file,			    struct tap_queue *q){	int err = -EINVAL; 	ASSERT_RTNL(); 	if (q->enabled)		goto out; 	err = 0;	rcu_assign_pointer(tap->taps[tap->numvtaps], q);	q->queue_index = tap->numvtaps;	q->enabled = true; 	tap->numvtaps++;out:	return err;} /* Requires RTNL */static int tap_set_queue(struct tap_dev *tap, struct file *file,			 struct tap_queue *q){	if (tap->numqueues == MAX_TAP_QUEUES)		return -EBUSY; 	rcu_assign_pointer(q->tap, tap);	rcu_assign_pointer(tap->taps[tap->numvtaps], q);	sock_hold(&q->sk); 	q->file = file;	q->queue_index = tap->numvtaps;	q->enabled = true;	file->private_data = q;	list_add_tail(&q->next, &tap->queue_list); 	tap->numvtaps++;	tap->numqueues++; 	return 0;} static int tap_disable_queue(struct tap_queue *q){	struct tap_dev *tap;	struct tap_queue *nq; 	ASSERT_RTNL();	if (!q->enabled)		return -EINVAL; 	tap = rtnl_dereference(q->tap); 	if (tap) {		int index = q->queue_index;		BUG_ON(index >= tap->numvtaps);		nq = rtnl_dereference(tap->taps[tap->numvtaps - 1]);		nq->queue_index = index; 		rcu_assign_pointer(tap->taps[index], nq);		RCU_INIT_POINTER(tap->taps[tap->numvtaps - 1], NULL);		q->enabled = false; 		tap->numvtaps--;	} 	return 0;} /* * The file owning the queue got closed, give up both * the reference that the files holds as well as the * one from the macvlan_dev if that still exists. * * Using the spinlock makes sure that we don't get * to the queue again after destroying it. */static void tap_put_queue(struct tap_queue *q){	struct tap_dev *tap; 	rtnl_lock();	tap = rtnl_dereference(q->tap); 	if (tap) {		if (q->enabled)			BUG_ON(tap_disable_queue(q)); 		tap->numqueues--;		RCU_INIT_POINTER(q->tap, NULL);		sock_put(&q->sk);		list_del_init(&q->next);	} 	rtnl_unlock(); 	synchronize_rcu();	sock_put(&q->sk);} /* * Select a queue based on the rxq of the device on which this packet * arrived. If the incoming device is not mq, calculate a flow hash * to select a queue. If all fails, find the first available queue. * Cache vlan->numvtaps since it can become zero during the execution * of this function. */static struct tap_queue *tap_get_queue(struct tap_dev *tap,				       struct sk_buff *skb){	struct tap_queue *queue = NULL;	/* Access to taps array is protected by rcu, but access to numvtaps	 * isn't. Below we use it to lookup a queue, but treat it as a hint	 * and validate that the result isn't NULL - in case we are	 * racing against queue removal.	 */	int numvtaps = READ_ONCE(tap->numvtaps);	__u32 rxq; 	if (!numvtaps)		goto out; 	if (numvtaps == 1)		goto single; 	/* Check if we can use flow to select a queue */	rxq = skb_get_hash(skb);	if (rxq) {		queue = rcu_dereference(tap->taps[rxq % numvtaps]);		goto out;	} 	if (likely(skb_rx_queue_recorded(skb))) {		rxq = skb_get_rx_queue(skb); 		while (unlikely(rxq >= numvtaps))			rxq -= numvtaps; 		queue = rcu_dereference(tap->taps[rxq]);		goto out;	} single:	queue = rcu_dereference(tap->taps[0]);out:	return queue;} /* * The net_device is going away, give up the reference * that it holds on all queues and safely set the pointer * from the queues to NULL. */void tap_del_queues(struct tap_dev *tap){	struct tap_queue *q, *tmp; 	ASSERT_RTNL();	list_for_each_entry_safe(q, tmp, &tap->queue_list, next) {		list_del_init(&q->next);		RCU_INIT_POINTER(q->tap, NULL);		if (q->enabled)			tap->numvtaps--;		tap->numqueues--;		sock_put(&q->sk);	}	BUG_ON(tap->numvtaps);	BUG_ON(tap->numqueues);	/* guarantee that any future tap_set_queue will fail */	tap->numvtaps = MAX_TAP_QUEUES;}EXPORT_SYMBOL_GPL(tap_del_queues); rx_handler_result_t tap_handle_frame(struct sk_buff **pskb){	struct sk_buff *skb = *pskb;	struct net_device *dev = skb->dev;	struct tap_dev *tap;	struct tap_queue *q;	netdev_features_t features = TAP_FEATURES;	enum skb_drop_reason drop_reason; 	tap = tap_dev_get_rcu(dev);	if (!tap)		return RX_HANDLER_PASS; 	q = tap_get_queue(tap, skb);	if (!q)		return RX_HANDLER_PASS; 	skb_push(skb, ETH_HLEN); 	/* Apply the forward feature mask so that we perform segmentation	 * according to users wishes.  This only works if VNET_HDR is	 * enabled.	 */	if (q->flags & IFF_VNET_HDR)		features |= tap->tap_features;	if (netif_needs_gso(skb, features)) {		struct sk_buff *segs = __skb_gso_segment(skb, features, false);		struct sk_buff *next; 		if (IS_ERR(segs)) {			drop_reason = SKB_DROP_REASON_SKB_GSO_SEG;			goto drop;		} 		if (!segs) {			if (ptr_ring_produce(&q->ring, skb)) {				drop_reason = SKB_DROP_REASON_FULL_RING;				goto drop;			}			goto wake_up;		} 		consume_skb(skb);		skb_list_walk_safe(segs, skb, next) {			skb_mark_not_on_list(skb);			if (ptr_ring_produce(&q->ring, skb)) {				drop_reason = SKB_DROP_REASON_FULL_RING;				kfree_skb_reason(skb, drop_reason);				kfree_skb_list_reason(next, drop_reason);				break;			}		}	} else {		/* If we receive a partial checksum and the tap side		 * doesn't support checksum offload, compute the checksum.		 * Note: it doesn't matter which checksum feature to		 *	  check, we either support them all or none.		 */		if (skb->ip_summed == CHECKSUM_PARTIAL &&		    !(features & NETIF_F_CSUM_MASK) &&		    skb_checksum_help(skb)) {			drop_reason = SKB_DROP_REASON_SKB_CSUM;			goto drop;		}		if (ptr_ring_produce(&q->ring, skb)) {			drop_reason = SKB_DROP_REASON_FULL_RING;			goto drop;		}	} wake_up:	wake_up_interruptible_poll(sk_sleep(&q->sk), EPOLLIN | EPOLLRDNORM | EPOLLRDBAND);	return RX_HANDLER_CONSUMED; drop:	/* Count errors/drops only here, thus don't care about args. */	if (tap->count_rx_dropped)		tap->count_rx_dropped(tap);	kfree_skb_reason(skb, drop_reason);	return RX_HANDLER_CONSUMED;}EXPORT_SYMBOL_GPL(tap_handle_frame); static struct major_info *tap_get_major(int major){	struct major_info *tap_major; 	list_for_each_entry_rcu(tap_major, &major_list, next) {		if (tap_major->major == major)			return tap_major;	} 	return NULL;} int tap_get_minor(dev_t major, struct tap_dev *tap){	int retval = -ENOMEM;	struct major_info *tap_major; 	rcu_read_lock();	tap_major = tap_get_major(MAJOR(major));	if (!tap_major) {		retval = -EINVAL;		goto unlock;	} 	spin_lock(&tap_major->minor_lock);	retval = idr_alloc(&tap_major->minor_idr, tap, 1, TAP_NUM_DEVS, GFP_ATOMIC);	if (retval >= 0) {		tap->minor = retval;	} else if (retval == -ENOSPC) {		netdev_err(tap->dev, "Too many tap devices\n");		retval = -EINVAL;	}	spin_unlock(&tap_major->minor_lock); unlock:	rcu_read_unlock();	return retval < 0 ? retval : 0;}EXPORT_SYMBOL_GPL(tap_get_minor); void tap_free_minor(dev_t major, struct tap_dev *tap){	struct major_info *tap_major; 	rcu_read_lock();	tap_major = tap_get_major(MAJOR(major));	if (!tap_major) {		goto unlock;	} 	spin_lock(&tap_major->minor_lock);	if (tap->minor) {		idr_remove(&tap_major->minor_idr, tap->minor);		tap->minor = 0;	}	spin_unlock(&tap_major->minor_lock); unlock:	rcu_read_unlock();}EXPORT_SYMBOL_GPL(tap_free_minor); static struct tap_dev *dev_get_by_tap_file(int major, int minor){	struct net_device *dev = NULL;	struct tap_dev *tap;	struct major_info *tap_major; 	rcu_read_lock();	tap_major = tap_get_major(major);	if (!tap_major) {		tap = NULL;		goto unlock;	} 	spin_lock(&tap_major->minor_lock);	tap = idr_find(&tap_major->minor_idr, minor);	if (tap) {		dev = tap->dev;		dev_hold(dev);	}	spin_unlock(&tap_major->minor_lock); unlock:	rcu_read_unlock();	return tap;} static void tap_sock_write_space(struct sock *sk){	wait_queue_head_t *wqueue; 	if (!sock_writeable(sk) ||	    !test_and_clear_bit(SOCKWQ_ASYNC_NOSPACE, &sk->sk_socket->flags))		return; 	wqueue = sk_sleep(sk);	if (wqueue && waitqueue_active(wqueue))		wake_up_interruptible_poll(wqueue, EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND);} static void tap_sock_destruct(struct sock *sk){	struct tap_queue *q = container_of(sk, struct tap_queue, sk); 	ptr_ring_cleanup(&q->ring, __skb_array_destroy_skb);} static int tap_open(struct inode *inode, struct file *file){	struct net *net = current->nsproxy->net_ns;	struct tap_dev *tap;	struct tap_queue *q;	int err = -ENODEV; 	rtnl_lock();	tap = dev_get_by_tap_file(imajor(inode), iminor(inode));	if (!tap)		goto err; 	err = -ENOMEM;	q = (struct tap_queue *)sk_alloc(net, AF_UNSPEC, GFP_KERNEL,					     &tap_proto, 0);	if (!q)		goto err;	if (ptr_ring_init(&q->ring, tap->dev->tx_queue_len, GFP_KERNEL)) {		sk_free(&q->sk);		goto err;	} 	init_waitqueue_head(&q->sock.wq.wait);	q->sock.type = SOCK_RAW;	q->sock.state = SS_CONNECTED;	q->sock.file = file;	q->sock.ops = &tap_socket_ops;	sock_init_data_uid(&q->sock, &q->sk, current_fsuid());	q->sk.sk_write_space = tap_sock_write_space;	q->sk.sk_destruct = tap_sock_destruct;	q->flags = IFF_VNET_HDR | IFF_NO_PI | IFF_TAP;	q->vnet_hdr_sz = sizeof(struct virtio_net_hdr); 	/*	 * so far only KVM virtio_net uses tap, enable zero copy between	 * guest kernel and host kernel when lower device supports zerocopy	 *	 * The macvlan supports zerocopy iff the lower device supports zero	 * copy so we don't have to look at the lower device directly.	 */	if ((tap->dev->features & NETIF_F_HIGHDMA) && (tap->dev->features & NETIF_F_SG))		sock_set_flag(&q->sk, SOCK_ZEROCOPY); 	err = tap_set_queue(tap, file, q);	if (err) {		/* tap_sock_destruct() will take care of freeing ptr_ring */		goto err_put;	} 	/* tap groks IOCB_NOWAIT just fine, mark it as such */	file->f_mode |= FMODE_NOWAIT; 	dev_put(tap->dev); 	rtnl_unlock();

Lines 1–500 of 1291.