1244 lines · 29.0 KB · C
RawDownload
#include <linux/etherdevice.h>#include <linux/if_macvlan.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.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/wait.h>#include <linux/cdev.h>#include <linux/idr.h>#include <linux/fs.h> #include <net/net_namespace.h>#include <net/rtnetlink.h>#include <net/sock.h>#include <linux/virtio_net.h> /* * A macvtap queue is the central object of this driver, it connects * an open character device to a macvlan interface. There can be * multiple queues on one interface, which map back to queues * implemented in hardware on the underlying device. * * macvtap_proto is used to allocate queues through the sock allocation * mechanism. * */struct macvtap_queue {	struct sock sk;	struct socket sock;	struct socket_wq wq;	int vnet_hdr_sz;	struct macvlan_dev __rcu *vlan;	struct file *file;	unsigned int flags;	u16 queue_index;	bool enabled;	struct list_head next;}; static struct proto macvtap_proto = {	.name = "macvtap",	.owner = THIS_MODULE,	.obj_size = sizeof (struct macvtap_queue),}; /* * Variables for dealing with macvtaps device numbers. */static dev_t macvtap_major;#define MACVTAP_NUM_DEVS (1U << MINORBITS)static DEFINE_MUTEX(minor_lock);static DEFINE_IDR(minor_idr); #define GOODCOPY_LEN 128static struct class *macvtap_class;static struct cdev macvtap_cdev; static const struct proto_ops macvtap_socket_ops; #define TUN_OFFLOADS (NETIF_F_HW_CSUM | NETIF_F_TSO_ECN | NETIF_F_TSO | \		      NETIF_F_TSO6 | NETIF_F_UFO)#define RX_OFFLOADS (NETIF_F_GRO | NETIF_F_LRO)#define TAP_FEATURES (NETIF_F_GSO | NETIF_F_SG) /* * RCU usage: * The macvtap_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 macvtap_queue * through sock_hold(&q->sk). When the macvlan_dev goes away first, * q->vlan becomes inaccessible. When the files gets closed, * macvtap_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 macvtap_enable_queue(struct net_device *dev, struct file *file,				struct macvtap_queue *q){	struct macvlan_dev *vlan = netdev_priv(dev);	int err = -EINVAL; 	ASSERT_RTNL(); 	if (q->enabled)		goto out; 	err = 0;	rcu_assign_pointer(vlan->taps[vlan->numvtaps], q);	q->queue_index = vlan->numvtaps;	q->enabled = true; 	vlan->numvtaps++;out:	return err;} static int macvtap_set_queue(struct net_device *dev, struct file *file,			     struct macvtap_queue *q){	struct macvlan_dev *vlan = netdev_priv(dev);	int err = -EBUSY; 	rtnl_lock();	if (vlan->numqueues == MAX_MACVTAP_QUEUES)		goto out; 	err = 0;	rcu_assign_pointer(q->vlan, vlan);	rcu_assign_pointer(vlan->taps[vlan->numvtaps], q);	sock_hold(&q->sk); 	q->file = file;	q->queue_index = vlan->numvtaps;	q->enabled = true;	file->private_data = q;	list_add_tail(&q->next, &vlan->queue_list); 	vlan->numvtaps++;	vlan->numqueues++; out:	rtnl_unlock();	return err;} static int macvtap_disable_queue(struct macvtap_queue *q){	struct macvlan_dev *vlan;	struct macvtap_queue *nq; 	ASSERT_RTNL();	if (!q->enabled)		return -EINVAL; 	vlan = rtnl_dereference(q->vlan); 	if (vlan) {		int index = q->queue_index;		BUG_ON(index >= vlan->numvtaps);		nq = rtnl_dereference(vlan->taps[vlan->numvtaps - 1]);		nq->queue_index = index; 		rcu_assign_pointer(vlan->taps[index], nq);		RCU_INIT_POINTER(vlan->taps[vlan->numvtaps - 1], NULL);		q->enabled = false; 		vlan->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 macvtap_put_queue(struct macvtap_queue *q){	struct macvlan_dev *vlan; 	rtnl_lock();	vlan = rtnl_dereference(q->vlan); 	if (vlan) {		if (q->enabled)			BUG_ON(macvtap_disable_queue(q)); 		vlan->numqueues--;		RCU_INIT_POINTER(q->vlan, 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 macvtap_queue *macvtap_get_queue(struct net_device *dev,					       struct sk_buff *skb){	struct macvlan_dev *vlan = netdev_priv(dev);	struct macvtap_queue *tap = 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 = ACCESS_ONCE(vlan->numvtaps);	__u32 rxq; 	if (!numvtaps)		goto out; 	/* Check if we can use flow to select a queue */	rxq = skb_get_rxhash(skb);	if (rxq) {		tap = rcu_dereference(vlan->taps[rxq % numvtaps]);		goto out;	} 	if (likely(skb_rx_queue_recorded(skb))) {		rxq = skb_get_rx_queue(skb); 		while (unlikely(rxq >= numvtaps))			rxq -= numvtaps; 		tap = rcu_dereference(vlan->taps[rxq]);		goto out;	} 	tap = rcu_dereference(vlan->taps[0]);out:	return tap;} /* * 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. */static void macvtap_del_queues(struct net_device *dev){	struct macvlan_dev *vlan = netdev_priv(dev);	struct macvtap_queue *q, *tmp, *qlist[MAX_MACVTAP_QUEUES];	int i, j = 0; 	ASSERT_RTNL();	list_for_each_entry_safe(q, tmp, &vlan->queue_list, next) {		list_del_init(&q->next);		qlist[j++] = q;		RCU_INIT_POINTER(q->vlan, NULL);		if (q->enabled)			vlan->numvtaps--;		vlan->numqueues--;	}	for (i = 0; i < vlan->numvtaps; i++)		RCU_INIT_POINTER(vlan->taps[i], NULL);	BUG_ON(vlan->numvtaps);	BUG_ON(vlan->numqueues);	/* guarantee that any future macvtap_set_queue will fail */	vlan->numvtaps = MAX_MACVTAP_QUEUES; 	for (--j; j >= 0; j--)		sock_put(&qlist[j]->sk);} /* * Forward happens for data that gets sent from one macvlan * endpoint to another one in bridge mode. We just take * the skb and put it into the receive queue. */static int macvtap_forward(struct net_device *dev, struct sk_buff *skb){	struct macvlan_dev *vlan = netdev_priv(dev);	struct macvtap_queue *q = macvtap_get_queue(dev, skb);	netdev_features_t features = TAP_FEATURES; 	if (!q)		goto drop; 	if (skb_queue_len(&q->sk.sk_receive_queue) >= dev->tx_queue_len)		goto drop; 	skb->dev = dev;	/* 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 |= vlan->tap_features;	if (netif_needs_gso(skb, features)) {		struct sk_buff *segs = __skb_gso_segment(skb, features, false); 		if (IS_ERR(segs))			goto drop; 		if (!segs) {			skb_queue_tail(&q->sk.sk_receive_queue, skb);			goto wake_up;		} 		kfree_skb(skb);		while (segs) {			struct sk_buff *nskb = segs->next; 			segs->next = NULL;			skb_queue_tail(&q->sk.sk_receive_queue, segs);			segs = nskb;		}	} else {		skb_queue_tail(&q->sk.sk_receive_queue, skb);	} wake_up:	wake_up_interruptible_poll(sk_sleep(&q->sk), POLLIN | POLLRDNORM | POLLRDBAND);	return NET_RX_SUCCESS; drop:	kfree_skb(skb);	return NET_RX_DROP;} /* * Receive is for data from the external interface (lowerdev), * in case of macvtap, we can treat that the same way as * forward, which macvlan cannot. */static int macvtap_receive(struct sk_buff *skb){	skb_push(skb, ETH_HLEN);	return macvtap_forward(skb->dev, skb);} static int macvtap_get_minor(struct macvlan_dev *vlan){	int retval = -ENOMEM; 	mutex_lock(&minor_lock);	retval = idr_alloc(&minor_idr, vlan, 1, MACVTAP_NUM_DEVS, GFP_KERNEL);	if (retval >= 0) {		vlan->minor = retval;	} else if (retval == -ENOSPC) {		printk(KERN_ERR "too many macvtap devices\n");		retval = -EINVAL;	}	mutex_unlock(&minor_lock);	return retval < 0 ? retval : 0;} static void macvtap_free_minor(struct macvlan_dev *vlan){	mutex_lock(&minor_lock);	if (vlan->minor) {		idr_remove(&minor_idr, vlan->minor);		vlan->minor = 0;	}	mutex_unlock(&minor_lock);} static struct net_device *dev_get_by_macvtap_minor(int minor){	struct net_device *dev = NULL;	struct macvlan_dev *vlan; 	mutex_lock(&minor_lock);	vlan = idr_find(&minor_idr, minor);	if (vlan) {		dev = vlan->dev;		dev_hold(dev);	}	mutex_unlock(&minor_lock);	return dev;} static int macvtap_newlink(struct net *src_net,			   struct net_device *dev,			   struct nlattr *tb[],			   struct nlattr *data[]){	struct macvlan_dev *vlan = netdev_priv(dev);	INIT_LIST_HEAD(&vlan->queue_list); 	/* Since macvlan supports all offloads by default, make	 * tap support all offloads also.	 */	vlan->tap_features = TUN_OFFLOADS; 	/* Don't put anything that may fail after macvlan_common_newlink	 * because we can't undo what it does.	 */	return macvlan_common_newlink(src_net, dev, tb, data,				      macvtap_receive, macvtap_forward);} static void macvtap_dellink(struct net_device *dev,			    struct list_head *head){	macvtap_del_queues(dev);	macvlan_dellink(dev, head);} static void macvtap_setup(struct net_device *dev){	macvlan_common_setup(dev);	dev->tx_queue_len = TUN_READQ_SIZE;} static struct rtnl_link_ops macvtap_link_ops __read_mostly = {	.kind		= "macvtap",	.setup		= macvtap_setup,	.newlink	= macvtap_newlink,	.dellink	= macvtap_dellink,};  static void macvtap_sock_write_space(struct sock *sk){	wait_queue_head_t *wqueue; 	if (!sock_writeable(sk) ||	    !test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags))		return; 	wqueue = sk_sleep(sk);	if (wqueue && waitqueue_active(wqueue))		wake_up_interruptible_poll(wqueue, POLLOUT | POLLWRNORM | POLLWRBAND);} static void macvtap_sock_destruct(struct sock *sk){	skb_queue_purge(&sk->sk_receive_queue);} static int macvtap_open(struct inode *inode, struct file *file){	struct net *net = current->nsproxy->net_ns;	struct net_device *dev = dev_get_by_macvtap_minor(iminor(inode));	struct macvtap_queue *q;	int err; 	err = -ENODEV;	if (!dev)		goto out; 	err = -ENOMEM;	q = (struct macvtap_queue *)sk_alloc(net, AF_UNSPEC, GFP_KERNEL,					     &macvtap_proto);	if (!q)		goto out; 	RCU_INIT_POINTER(q->sock.wq, &q->wq);	init_waitqueue_head(&q->wq.wait);	q->sock.type = SOCK_RAW;	q->sock.state = SS_CONNECTED;	q->sock.file = file;	q->sock.ops = &macvtap_socket_ops;	sock_init_data(&q->sock, &q->sk);	q->sk.sk_write_space = macvtap_sock_write_space;	q->sk.sk_destruct = macvtap_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 macvtap, 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 ((dev->features & NETIF_F_HIGHDMA) && (dev->features & NETIF_F_SG))		sock_set_flag(&q->sk, SOCK_ZEROCOPY); 	err = macvtap_set_queue(dev, file, q);	if (err)		sock_put(&q->sk); out:	if (dev)		dev_put(dev); 	return err;} static int macvtap_release(struct inode *inode, struct file *file){	struct macvtap_queue *q = file->private_data;	macvtap_put_queue(q);	return 0;} static unsigned int macvtap_poll(struct file *file, poll_table * wait){	struct macvtap_queue *q = file->private_data;

Lines 1–500 of 1244.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t