diff options
author | Sainath Grandhi <sainath.grandhi@intel.com> | 2017-02-11 01:03:49 +0100 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-02-12 02:59:41 +0100 |
commit | 6fe3faf86757eb7f078ff06b23b206f17dc4fb36 (patch) | |
tree | 4602f5f435ad34f406ec980694daae4faf5d6119 /drivers/net/tap.c | |
parent | tap: Tap character device creation/destroy API (diff) | |
download | linux-6fe3faf86757eb7f078ff06b23b206f17dc4fb36.tar.xz linux-6fe3faf86757eb7f078ff06b23b206f17dc4fb36.zip |
tap: Abstract type of virtual interface from tap implementation
macvlan object is re-structured to hold tap related elements in a separate
entity, tap_dev. Upon NETDEV_REGISTER device_event, tap_dev is registered with
idr and fetched again on tap_open. Few of the tap functions are modified to
accepted tap_dev as argument. tap_dev object includes callbacks to be used by
underlying virtual interface to take care of tx and rx accounting.
Signed-off-by: Sainath Grandhi <sainath.grandhi@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/tap.c')
-rw-r--r-- | drivers/net/tap.c | 264 |
1 files changed, 118 insertions, 146 deletions
diff --git a/drivers/net/tap.c b/drivers/net/tap.c index 04ba9782c2f3..7d3e8b18f5e6 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -1,5 +1,5 @@ #include <linux/etherdevice.h> -#include <linux/if_macvlan.h> +#include <linux/if_tap.h> #include <linux/if_vlan.h> #include <linux/interrupt.h> #include <linux/nsproxy.h> @@ -23,30 +23,6 @@ #include <linux/virtio_net.h> #include <linux/skb_array.h> -/* - * A tap 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. - * - * tap_proto is used to allocate queues through the sock allocation - * mechanism. - * - */ -struct tap_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; - struct skb_array skb_array; -}; - #define TAP_IFFEATURES (IFF_VNET_HDR | IFF_MULTI_QUEUE) #define TAP_VNET_LE 0x80000000 @@ -137,7 +113,7 @@ 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 macvlan_dev *tap_get_vlan_rcu(const struct net_device *dev) +static struct tap_dev *tap_dev_get_rcu(const struct net_device *dev) { return rcu_dereference(dev->rx_handler_data); } @@ -159,10 +135,9 @@ static struct macvlan_dev *tap_get_vlan_rcu(const struct net_device *dev) * when both our references and any pending SKBs are gone. */ -static int tap_enable_queue(struct net_device *dev, struct file *file, +static int tap_enable_queue(struct tap_dev *tap, struct file *file, struct tap_queue *q) { - struct macvlan_dev *vlan = netdev_priv(dev); int err = -EINVAL; ASSERT_RTNL(); @@ -171,62 +146,60 @@ static int tap_enable_queue(struct net_device *dev, struct file *file, goto out; err = 0; - rcu_assign_pointer(vlan->taps[vlan->numvtaps], q); - q->queue_index = vlan->numvtaps; + rcu_assign_pointer(tap->taps[tap->numvtaps], q); + q->queue_index = tap->numvtaps; q->enabled = true; - vlan->numvtaps++; + tap->numvtaps++; out: return err; } /* Requires RTNL */ -static int tap_set_queue(struct net_device *dev, struct file *file, +static int tap_set_queue(struct tap_dev *tap, struct file *file, struct tap_queue *q) { - struct macvlan_dev *vlan = netdev_priv(dev); - - if (vlan->numqueues == MAX_TAP_QUEUES) + if (tap->numqueues == MAX_TAP_QUEUES) return -EBUSY; - rcu_assign_pointer(q->vlan, vlan); - rcu_assign_pointer(vlan->taps[vlan->numvtaps], q); + rcu_assign_pointer(q->tap, tap); + rcu_assign_pointer(tap->taps[tap->numvtaps], q); sock_hold(&q->sk); q->file = file; - q->queue_index = vlan->numvtaps; + q->queue_index = tap->numvtaps; q->enabled = true; file->private_data = q; - list_add_tail(&q->next, &vlan->queue_list); + list_add_tail(&q->next, &tap->queue_list); - vlan->numvtaps++; - vlan->numqueues++; + tap->numvtaps++; + tap->numqueues++; return 0; } static int tap_disable_queue(struct tap_queue *q) { - struct macvlan_dev *vlan; + struct tap_dev *tap; struct tap_queue *nq; ASSERT_RTNL(); if (!q->enabled) return -EINVAL; - vlan = rtnl_dereference(q->vlan); + tap = rtnl_dereference(q->tap); - if (vlan) { + if (tap) { int index = q->queue_index; - BUG_ON(index >= vlan->numvtaps); - nq = rtnl_dereference(vlan->taps[vlan->numvtaps - 1]); + BUG_ON(index >= tap->numvtaps); + nq = rtnl_dereference(tap->taps[tap->numvtaps - 1]); nq->queue_index = index; - rcu_assign_pointer(vlan->taps[index], nq); - RCU_INIT_POINTER(vlan->taps[vlan->numvtaps - 1], NULL); + rcu_assign_pointer(tap->taps[index], nq); + RCU_INIT_POINTER(tap->taps[tap->numvtaps - 1], NULL); q->enabled = false; - vlan->numvtaps--; + tap->numvtaps--; } return 0; @@ -242,17 +215,17 @@ static int tap_disable_queue(struct tap_queue *q) */ static void tap_put_queue(struct tap_queue *q) { - struct macvlan_dev *vlan; + struct tap_dev *tap; rtnl_lock(); - vlan = rtnl_dereference(q->vlan); + tap = rtnl_dereference(q->tap); - if (vlan) { + if (tap) { if (q->enabled) BUG_ON(tap_disable_queue(q)); - vlan->numqueues--; - RCU_INIT_POINTER(q->vlan, NULL); + tap->numqueues--; + RCU_INIT_POINTER(q->tap, NULL); sock_put(&q->sk); list_del_init(&q->next); } @@ -270,17 +243,16 @@ static void tap_put_queue(struct tap_queue *q) * Cache vlan->numvtaps since it can become zero during the execution * of this function. */ -static struct tap_queue *tap_get_queue(struct net_device *dev, +static struct tap_queue *tap_get_queue(struct tap_dev *tap, struct sk_buff *skb) { - struct macvlan_dev *vlan = netdev_priv(dev); - struct tap_queue *tap = NULL; + 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 = ACCESS_ONCE(vlan->numvtaps); + int numvtaps = ACCESS_ONCE(tap->numvtaps); __u32 rxq; if (!numvtaps) @@ -292,7 +264,7 @@ static struct tap_queue *tap_get_queue(struct net_device *dev, /* Check if we can use flow to select a queue */ rxq = skb_get_hash(skb); if (rxq) { - tap = rcu_dereference(vlan->taps[rxq % numvtaps]); + queue = rcu_dereference(tap->taps[rxq % numvtaps]); goto out; } @@ -302,14 +274,14 @@ static struct tap_queue *tap_get_queue(struct net_device *dev, while (unlikely(rxq >= numvtaps)) rxq -= numvtaps; - tap = rcu_dereference(vlan->taps[rxq]); + queue = rcu_dereference(tap->taps[rxq]); goto out; } single: - tap = rcu_dereference(vlan->taps[0]); + queue = rcu_dereference(tap->taps[0]); out: - return tap; + return queue; } /* @@ -317,39 +289,38 @@ out: * that it holds on all queues and safely set the pointer * from the queues to NULL. */ -void tap_del_queues(struct net_device *dev) +void tap_del_queues(struct tap_dev *tap) { - struct macvlan_dev *vlan = netdev_priv(dev); struct tap_queue *q, *tmp; ASSERT_RTNL(); - list_for_each_entry_safe(q, tmp, &vlan->queue_list, next) { + list_for_each_entry_safe(q, tmp, &tap->queue_list, next) { list_del_init(&q->next); - RCU_INIT_POINTER(q->vlan, NULL); + RCU_INIT_POINTER(q->tap, NULL); if (q->enabled) - vlan->numvtaps--; - vlan->numqueues--; + tap->numvtaps--; + tap->numqueues--; sock_put(&q->sk); } - BUG_ON(vlan->numvtaps); - BUG_ON(vlan->numqueues); + BUG_ON(tap->numvtaps); + BUG_ON(tap->numqueues); /* guarantee that any future tap_set_queue will fail */ - vlan->numvtaps = MAX_TAP_QUEUES; + tap->numvtaps = MAX_TAP_QUEUES; } rx_handler_result_t tap_handle_frame(struct sk_buff **pskb) { struct sk_buff *skb = *pskb; struct net_device *dev = skb->dev; - struct macvlan_dev *vlan; + struct tap_dev *tap; struct tap_queue *q; netdev_features_t features = TAP_FEATURES; - vlan = tap_get_vlan_rcu(dev); - if (!vlan) + tap = tap_dev_get_rcu(dev); + if (!tap) return RX_HANDLER_PASS; - q = tap_get_queue(dev, skb); + q = tap_get_queue(tap, skb); if (!q) return RX_HANDLER_PASS; @@ -363,7 +334,7 @@ rx_handler_result_t tap_handle_frame(struct sk_buff **pskb) * enabled. */ if (q->flags & IFF_VNET_HDR) - features |= vlan->tap_features; + features |= tap->tap_features; if (netif_needs_gso(skb, features)) { struct sk_buff *segs = __skb_gso_segment(skb, features, false); @@ -408,50 +379,51 @@ wake_up: drop: /* Count errors/drops only here, thus don't care about args. */ - macvlan_count_rx(vlan, 0, 0, 0); + if (tap->count_rx_dropped) + tap->count_rx_dropped(tap); kfree_skb(skb); return RX_HANDLER_CONSUMED; } -int tap_get_minor(struct macvlan_dev *vlan) +int tap_get_minor(struct tap_dev *tap) { int retval = -ENOMEM; mutex_lock(&macvtap_major.minor_lock); - retval = idr_alloc(&macvtap_major.minor_idr, vlan, 1, TAP_NUM_DEVS, GFP_KERNEL); + retval = idr_alloc(&macvtap_major.minor_idr, tap, 1, TAP_NUM_DEVS, GFP_KERNEL); if (retval >= 0) { - vlan->minor = retval; + tap->minor = retval; } else if (retval == -ENOSPC) { - netdev_err(vlan->dev, "Too many tap devices\n"); + netdev_err(tap->dev, "Too many tap devices\n"); retval = -EINVAL; } mutex_unlock(&macvtap_major.minor_lock); return retval < 0 ? retval : 0; } -void tap_free_minor(struct macvlan_dev *vlan) +void tap_free_minor(struct tap_dev *tap) { mutex_lock(&macvtap_major.minor_lock); - if (vlan->minor) { - idr_remove(&macvtap_major.minor_idr, vlan->minor); - vlan->minor = 0; + if (tap->minor) { + idr_remove(&macvtap_major.minor_idr, tap->minor); + tap->minor = 0; } mutex_unlock(&macvtap_major.minor_lock); } -static struct net_device *dev_get_by_tap_minor(int minor) +static struct tap_dev *dev_get_by_tap_minor(int minor) { struct net_device *dev = NULL; - struct macvlan_dev *vlan; + struct tap_dev *tap; mutex_lock(&macvtap_major.minor_lock); - vlan = idr_find(&macvtap_major.minor_idr, minor); - if (vlan) { - dev = vlan->dev; + tap = idr_find(&macvtap_major.minor_idr, minor); + if (tap) { + dev = tap->dev; dev_hold(dev); } mutex_unlock(&macvtap_major.minor_lock); - return dev; + return tap; } static void tap_sock_write_space(struct sock *sk) @@ -477,13 +449,13 @@ static void tap_sock_destruct(struct sock *sk) static int tap_open(struct inode *inode, struct file *file) { struct net *net = current->nsproxy->net_ns; - struct net_device *dev; + struct tap_dev *tap; struct tap_queue *q; int err = -ENODEV; rtnl_lock(); - dev = dev_get_by_tap_minor(iminor(inode)); - if (!dev) + tap = dev_get_by_tap_minor(iminor(inode)); + if (!tap) goto err; err = -ENOMEM; @@ -511,18 +483,18 @@ static int tap_open(struct inode *inode, struct file *file) * 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)) + if ((tap->dev->features & NETIF_F_HIGHDMA) && (tap->dev->features & NETIF_F_SG)) sock_set_flag(&q->sk, SOCK_ZEROCOPY); err = -ENOMEM; - if (skb_array_init(&q->skb_array, dev->tx_queue_len, GFP_KERNEL)) + if (skb_array_init(&q->skb_array, tap->dev->tx_queue_len, GFP_KERNEL)) goto err_array; - err = tap_set_queue(dev, file, q); + err = tap_set_queue(tap, file, q); if (err) goto err_queue; - dev_put(dev); + dev_put(tap->dev); rtnl_unlock(); return err; @@ -532,8 +504,8 @@ err_queue: err_array: sock_put(&q->sk); err: - if (dev) - dev_put(dev); + if (tap) + dev_put(tap->dev); rtnl_unlock(); return err; @@ -601,7 +573,7 @@ static ssize_t tap_get_user(struct tap_queue *q, struct msghdr *m, { int good_linear = SKB_MAX_HEAD(TAP_RESERVE); struct sk_buff *skb; - struct macvlan_dev *vlan; + struct tap_dev *tap; unsigned long total_len = iov_iter_count(from); unsigned long len = total_len; int err; @@ -698,7 +670,7 @@ static ssize_t tap_get_user(struct tap_queue *q, struct msghdr *m, skb_set_network_header(skb, depth); rcu_read_lock(); - vlan = rcu_dereference(q->vlan); + tap = rcu_dereference(q->tap); /* copy skb_ubuf_info for callback when skb has no error */ if (zerocopy) { skb_shinfo(skb)->destructor_arg = m->msg_control; @@ -709,8 +681,8 @@ static ssize_t tap_get_user(struct tap_queue *q, struct msghdr *m, uarg->callback(uarg, false); } - if (vlan) { - skb->dev = vlan->dev; + if (tap) { + skb->dev = tap->dev; dev_queue_xmit(skb); } else { kfree_skb(skb); @@ -724,9 +696,9 @@ err_kfree: err: rcu_read_lock(); - vlan = rcu_dereference(q->vlan); - if (vlan) - this_cpu_inc(vlan->pcpu_stats->tx_dropped); + tap = rcu_dereference(q->tap); + if (tap && tap->count_tx_dropped) + tap->count_tx_dropped(tap); rcu_read_unlock(); return err; @@ -853,55 +825,55 @@ static ssize_t tap_read_iter(struct kiocb *iocb, struct iov_iter *to) return ret; } -static struct macvlan_dev *tap_get_vlan(struct tap_queue *q) +static struct tap_dev *tap_get_tap_dev(struct tap_queue *q) { - struct macvlan_dev *vlan; + struct tap_dev *tap; ASSERT_RTNL(); - vlan = rtnl_dereference(q->vlan); - if (vlan) - dev_hold(vlan->dev); + tap = rtnl_dereference(q->tap); + if (tap) + dev_hold(tap->dev); - return vlan; + return tap; } -static void tap_put_vlan(struct macvlan_dev *vlan) +static void tap_put_tap_dev(struct tap_dev *tap) { - dev_put(vlan->dev); + dev_put(tap->dev); } static int tap_ioctl_set_queue(struct file *file, unsigned int flags) { struct tap_queue *q = file->private_data; - struct macvlan_dev *vlan; + struct tap_dev *tap; int ret; - vlan = tap_get_vlan(q); - if (!vlan) + tap = tap_get_tap_dev(q); + if (!tap) return -EINVAL; if (flags & IFF_ATTACH_QUEUE) - ret = tap_enable_queue(vlan->dev, file, q); + ret = tap_enable_queue(tap, file, q); else if (flags & IFF_DETACH_QUEUE) ret = tap_disable_queue(q); else ret = -EINVAL; - tap_put_vlan(vlan); + tap_put_tap_dev(tap); return ret; } static int set_offload(struct tap_queue *q, unsigned long arg) { - struct macvlan_dev *vlan; + struct tap_dev *tap; netdev_features_t features; netdev_features_t feature_mask = 0; - vlan = rtnl_dereference(q->vlan); - if (!vlan) + tap = rtnl_dereference(q->tap); + if (!tap) return -ENOLINK; - features = vlan->dev->features; + features = tap->dev->features; if (arg & TUN_F_CSUM) { feature_mask = NETIF_F_HW_CSUM; @@ -935,9 +907,9 @@ static int set_offload(struct tap_queue *q, unsigned long arg) /* tap_features are the same as features on tun/tap and * reflect user expectations. */ - vlan->tap_features = feature_mask; - vlan->set_features = features; - netdev_update_features(vlan->dev); + tap->tap_features = feature_mask; + if (tap->update_features) + tap->update_features(tap, features); return 0; } @@ -949,7 +921,7 @@ static long tap_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct tap_queue *q = file->private_data; - struct macvlan_dev *vlan; + struct tap_dev *tap; void __user *argp = (void __user *)arg; struct ifreq __user *ifr = argp; unsigned int __user *up = argp; @@ -975,18 +947,18 @@ static long tap_ioctl(struct file *file, unsigned int cmd, case TUNGETIFF: rtnl_lock(); - vlan = tap_get_vlan(q); - if (!vlan) { + tap = tap_get_tap_dev(q); + if (!tap) { rtnl_unlock(); return -ENOLINK; } ret = 0; u = q->flags; - if (copy_to_user(&ifr->ifr_name, vlan->dev->name, IFNAMSIZ) || + if (copy_to_user(&ifr->ifr_name, tap->dev->name, IFNAMSIZ) || put_user(u, &ifr->ifr_flags)) ret = -EFAULT; - tap_put_vlan(vlan); + tap_put_tap_dev(tap); rtnl_unlock(); return ret; @@ -1059,18 +1031,18 @@ static long tap_ioctl(struct file *file, unsigned int cmd, case SIOCGIFHWADDR: rtnl_lock(); - vlan = tap_get_vlan(q); - if (!vlan) { + tap = tap_get_tap_dev(q); + if (!tap) { rtnl_unlock(); return -ENOLINK; } ret = 0; - u = vlan->dev->type; - if (copy_to_user(&ifr->ifr_name, vlan->dev->name, IFNAMSIZ) || - copy_to_user(&ifr->ifr_hwaddr.sa_data, vlan->dev->dev_addr, ETH_ALEN) || + u = tap->dev->type; + if (copy_to_user(&ifr->ifr_name, tap->dev->name, IFNAMSIZ) || + copy_to_user(&ifr->ifr_hwaddr.sa_data, tap->dev->dev_addr, ETH_ALEN) || put_user(u, &ifr->ifr_hwaddr.sa_family)) ret = -EFAULT; - tap_put_vlan(vlan); + tap_put_tap_dev(tap); rtnl_unlock(); return ret; @@ -1078,13 +1050,13 @@ static long tap_ioctl(struct file *file, unsigned int cmd, if (copy_from_user(&sa, &ifr->ifr_hwaddr, sizeof(sa))) return -EFAULT; rtnl_lock(); - vlan = tap_get_vlan(q); - if (!vlan) { + tap = tap_get_tap_dev(q); + if (!tap) { rtnl_unlock(); return -ENOLINK; } - ret = dev_set_mac_address(vlan->dev, &sa); - tap_put_vlan(vlan); + ret = dev_set_mac_address(tap->dev, &sa); + tap_put_tap_dev(tap); rtnl_unlock(); return ret; @@ -1167,19 +1139,19 @@ struct socket *tap_get_socket(struct file *file) } EXPORT_SYMBOL_GPL(tap_get_socket); -int tap_queue_resize(struct macvlan_dev *vlan) +int tap_queue_resize(struct tap_dev *tap) { - struct net_device *dev = vlan->dev; + struct net_device *dev = tap->dev; struct tap_queue *q; struct skb_array **arrays; - int n = vlan->numqueues; + int n = tap->numqueues; int ret, i = 0; arrays = kmalloc(sizeof *arrays * n, GFP_KERNEL); if (!arrays) return -ENOMEM; - list_for_each_entry(q, &vlan->queue_list, next) + list_for_each_entry(q, &tap->queue_list, next) arrays[i++] = &q->skb_array; ret = skb_array_resize_multiple(arrays, n, |