2019-05-27 14:55:06 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* TUN - Universal TUN/TAP device driver.
|
|
|
|
* Copyright (C) 1999-2002 Maxim Krasnyansky <maxk@qualcomm.com>
|
|
|
|
*
|
|
|
|
* $Id: tun.c,v 1.15 2002/03/01 02:44:24 maxk Exp $
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Changes:
|
|
|
|
*
|
2005-09-02 08:40:05 +08:00
|
|
|
* Mike Kershaw <dragorn@kismetwireless.net> 2005/08/14
|
|
|
|
* Add TUNSETLINK ioctl to set the link encapsulation
|
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* Mark Smith <markzzzsmith@yahoo.com.au>
|
2012-07-13 03:33:09 +08:00
|
|
|
* Use eth_random_addr() for tap MAC address.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Harald Roelle <harald.roelle@ifi.lmu.de> 2004/04/20
|
|
|
|
* Fixes in packet dropping, queue length setting and queue wakeup.
|
|
|
|
* Increased default tx queue length.
|
|
|
|
* Added ethtool API.
|
|
|
|
* Minor cleanups
|
|
|
|
*
|
|
|
|
* Daniel Podlejski <underley@underley.eu.org>
|
|
|
|
* Modifications for 2.3.99-pre5 kernel.
|
|
|
|
*/
|
|
|
|
|
2011-03-02 15:18:10 +08:00
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#define DRV_NAME "tun"
|
|
|
|
#define DRV_VERSION "1.6"
|
|
|
|
#define DRV_DESCRIPTION "Universal TUN/TAP device driver"
|
|
|
|
#define DRV_COPYRIGHT "(C) 1999-2004 Max Krasnyansky <maxk@qualcomm.com>"
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/kernel.h>
|
2017-02-03 02:15:33 +08:00
|
|
|
#include <linux/sched/signal.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/major.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/poll.h>
|
|
|
|
#include <linux/fcntl.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
#include <linux/miscdevice.h>
|
|
|
|
#include <linux/ethtool.h>
|
|
|
|
#include <linux/rtnetlink.h>
|
2009-11-07 14:52:32 +08:00
|
|
|
#include <linux/compat.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/if.h>
|
|
|
|
#include <linux/if_arp.h>
|
|
|
|
#include <linux/if_ether.h>
|
|
|
|
#include <linux/if_tun.h>
|
2013-07-25 13:00:33 +08:00
|
|
|
#include <linux/if_vlan.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/crc32.h>
|
2008-04-16 15:41:16 +08:00
|
|
|
#include <linux/nsproxy.h>
|
2008-07-03 18:48:02 +08:00
|
|
|
#include <linux/virtio_net.h>
|
2010-02-14 09:01:10 +08:00
|
|
|
#include <linux/rcupdate.h>
|
2007-09-18 02:56:21 +08:00
|
|
|
#include <net/net_namespace.h>
|
2008-04-16 15:40:46 +08:00
|
|
|
#include <net/netns/generic.h>
|
2009-01-22 08:02:16 +08:00
|
|
|
#include <net/rtnetlink.h>
|
2009-02-06 13:25:32 +08:00
|
|
|
#include <net/sock.h>
|
xdp: change ndo_xdp_xmit API to support bulking
This patch change the API for ndo_xdp_xmit to support bulking
xdp_frames.
When kernel is compiled with CONFIG_RETPOLINE, XDP sees a huge slowdown.
Most of the slowdown is caused by DMA API indirect function calls, but
also the net_device->ndo_xdp_xmit() call.
Benchmarked patch with CONFIG_RETPOLINE, using xdp_redirect_map with
single flow/core test (CPU E5-1650 v4 @ 3.60GHz), showed
performance improved:
for driver ixgbe: 6,042,682 pps -> 6,853,768 pps = +811,086 pps
for driver i40e : 6,187,169 pps -> 6,724,519 pps = +537,350 pps
With frames avail as a bulk inside the driver ndo_xdp_xmit call,
further optimizations are possible, like bulk DMA-mapping for TX.
Testing without CONFIG_RETPOLINE show the same performance for
physical NIC drivers.
The virtual NIC driver tun sees a huge performance boost, as it can
avoid doing per frame producer locking, but instead amortize the
locking cost over the bulk.
V2: Fix compile errors reported by kbuild test robot <lkp@intel.com>
V4: Isolated ndo, driver changes and callers.
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2018-05-24 22:46:12 +08:00
|
|
|
#include <net/xdp.h>
|
2020-06-30 09:06:22 +08:00
|
|
|
#include <net/ip_tunnels.h>
|
2014-01-29 15:43:31 +08:00
|
|
|
#include <linux/seq_file.h>
|
2014-11-07 21:22:23 +08:00
|
|
|
#include <linux/uio.h>
|
2016-06-30 14:45:36 +08:00
|
|
|
#include <linux/skb_array.h>
|
2017-08-11 19:41:18 +08:00
|
|
|
#include <linux/bpf.h>
|
|
|
|
#include <linux/bpf_trace.h>
|
2017-09-23 04:49:15 +08:00
|
|
|
#include <linux/mutex.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2016-12-25 03:46:01 +08:00
|
|
|
#include <linux/uaccess.h>
|
2018-02-14 21:40:14 +08:00
|
|
|
#include <linux/proc_fs.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2018-06-03 05:49:53 +08:00
|
|
|
static void tun_default_link_ksettings(struct net_device *dev,
|
|
|
|
struct ethtool_link_ksettings *cmd);
|
|
|
|
|
2017-09-04 11:36:08 +08:00
|
|
|
#define TUN_RX_PAD (NET_IP_ALIGN + NET_SKB_PAD)
|
2017-08-11 19:41:16 +08:00
|
|
|
|
2014-11-19 20:44:40 +08:00
|
|
|
/* TUN device flags */
|
|
|
|
|
|
|
|
/* IFF_ATTACH_QUEUE is never stored in device flags,
|
|
|
|
* overload it to mean fasync when stored there.
|
|
|
|
*/
|
|
|
|
#define TUN_FASYNC IFF_ATTACH_QUEUE
|
2014-12-16 21:05:06 +08:00
|
|
|
/* High bits in flags field are unused. */
|
|
|
|
#define TUN_VNET_LE 0x80000000
|
2015-04-24 20:50:36 +08:00
|
|
|
#define TUN_VNET_BE 0x40000000
|
2014-11-19 20:44:40 +08:00
|
|
|
|
|
|
|
#define TUN_FEATURES (IFF_NO_PI | IFF_ONE_QUEUE | IFF_VNET_HDR | \
|
2017-09-23 04:49:15 +08:00
|
|
|
IFF_MULTI_QUEUE | IFF_NAPI | IFF_NAPI_FRAGS)
|
|
|
|
|
2012-07-20 17:23:23 +08:00
|
|
|
#define GOODCOPY_LEN 128
|
|
|
|
|
2008-07-15 13:18:19 +08:00
|
|
|
#define FLT_EXACT_COUNT 8
|
|
|
|
struct tap_filter {
|
|
|
|
unsigned int count; /* Number of addrs. Zero means disabled */
|
|
|
|
u32 mask[2]; /* Mask of the hashed addrs */
|
|
|
|
unsigned char addr[FLT_EXACT_COUNT][ETH_ALEN];
|
|
|
|
};
|
|
|
|
|
2015-01-12 14:11:29 +08:00
|
|
|
/* MAX_TAP_QUEUES 256 is chosen to allow rx/tx queues to be equal
|
|
|
|
* to max number of VCPUs in guest. */
|
|
|
|
#define MAX_TAP_QUEUES 256
|
2013-01-23 11:59:13 +08:00
|
|
|
#define MAX_TAP_FLOWS 4096
|
2012-11-01 03:46:00 +08:00
|
|
|
|
2012-11-01 03:46:02 +08:00
|
|
|
#define TUN_FLOW_EXPIRE (3 * HZ)
|
|
|
|
|
2012-11-01 03:45:57 +08:00
|
|
|
/* A tun_file connects an open character device to a tuntap netdevice. It
|
2013-12-06 12:42:58 +08:00
|
|
|
* also contains all socket related structures (except sock_fprog and tap_filter)
|
2012-11-01 03:45:57 +08:00
|
|
|
* to serve as one transmit queue for tuntap device. The sock_fprog and
|
|
|
|
* tap_filter were kept in tun_struct since they were used for filtering for the
|
2012-11-26 06:07:40 +08:00
|
|
|
* netdevice not for a specific queue (at least I didn't see the requirement for
|
2012-11-01 03:45:57 +08:00
|
|
|
* this).
|
2012-11-01 03:45:58 +08:00
|
|
|
*
|
|
|
|
* RCU usage:
|
2012-11-26 06:07:40 +08:00
|
|
|
* The tun_file and tun_struct are loosely coupled, the pointer from one to the
|
2012-11-01 03:45:58 +08:00
|
|
|
* other can only be read while rcu_read_lock or rtnl_lock is held.
|
2012-11-01 03:45:57 +08:00
|
|
|
*/
|
2009-01-20 19:00:40 +08:00
|
|
|
struct tun_file {
|
2012-11-01 03:45:57 +08:00
|
|
|
struct sock sk;
|
|
|
|
struct socket socket;
|
2012-11-01 03:45:58 +08:00
|
|
|
struct tun_struct __rcu *tun;
|
2012-11-01 03:45:57 +08:00
|
|
|
struct fasync_struct *fasync;
|
|
|
|
/* only used for fasnyc */
|
|
|
|
unsigned int flags;
|
tun: Add ability to create tun device with given index
Tun devices cannot be created with ifidex user wants, but it's
required by checkpoint-restore project.
Long time ago such ability was implemented for rtnl_ops-based
interface for creating links (9c7dafbf net: Allow to create links
with given ifindex), but the only API for creating and managing
tuntap devices is ioctl-based and is evolving with adding new ones
(cde8b15f tuntap: add ioctl to attach or detach a file form tuntap
device).
Following that trend, here's how a new ioctl that sets the ifindex
for device, that _will_ be created by TUNSETIFF ioctl looks like.
So those who want a tuntap device with the ifindex N, should open
the tun device, call ioctl(fd, TUNSETIFINDEX, &N), then call TUNSETIFF.
If the index N is busy, then the register_netdev will find this out
and the ioctl would be failed with -EBUSY.
If setifindex is not called, then it will be generated as before.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-08-21 18:31:38 +08:00
|
|
|
union {
|
|
|
|
u16 queue_index;
|
|
|
|
unsigned int ifindex;
|
|
|
|
};
|
2017-09-23 04:49:14 +08:00
|
|
|
struct napi_struct napi;
|
2017-10-19 03:12:09 +08:00
|
|
|
bool napi_enabled;
|
2018-09-29 05:51:49 +08:00
|
|
|
bool napi_frags_enabled;
|
2017-09-23 04:49:15 +08:00
|
|
|
struct mutex napi_mutex; /* Protects access to the above napi */
|
2012-12-14 07:53:30 +08:00
|
|
|
struct list_head next;
|
|
|
|
struct tun_struct *detached;
|
2018-01-04 11:14:27 +08:00
|
|
|
struct ptr_ring tx_ring;
|
2018-01-03 18:25:59 +08:00
|
|
|
struct xdp_rxq_info xdp_rxq;
|
2009-01-20 19:00:40 +08:00
|
|
|
};
|
|
|
|
|
2018-11-15 17:43:10 +08:00
|
|
|
struct tun_page {
|
|
|
|
struct page *page;
|
|
|
|
int count;
|
|
|
|
};
|
|
|
|
|
2012-11-01 03:46:02 +08:00
|
|
|
struct tun_flow_entry {
|
|
|
|
struct hlist_node hash_link;
|
|
|
|
struct rcu_head rcu;
|
|
|
|
struct tun_struct *tun;
|
|
|
|
|
|
|
|
u32 rxhash;
|
2013-12-22 18:54:32 +08:00
|
|
|
u32 rps_rxhash;
|
2012-11-01 03:46:02 +08:00
|
|
|
int queue_index;
|
2018-12-06 16:08:17 +08:00
|
|
|
unsigned long updated ____cacheline_aligned_in_smp;
|
2012-11-01 03:46:02 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
#define TUN_NUM_FLOW_ENTRIES 1024
|
2018-08-03 15:50:02 +08:00
|
|
|
#define TUN_MASK_FLOW_ENTRIES (TUN_NUM_FLOW_ENTRIES - 1)
|
2012-11-01 03:46:02 +08:00
|
|
|
|
2018-01-16 16:31:01 +08:00
|
|
|
struct tun_prog {
|
2017-12-04 17:31:23 +08:00
|
|
|
struct rcu_head rcu;
|
|
|
|
struct bpf_prog *prog;
|
|
|
|
};
|
|
|
|
|
2012-11-01 03:45:57 +08:00
|
|
|
/* Since the socket were moved to tun_file, to preserve the behavior of persist
|
2012-11-26 06:07:40 +08:00
|
|
|
* device, socket filter, sndbuf and vnet header size were restore when the
|
2012-11-01 03:45:57 +08:00
|
|
|
* file were attached to a persist device.
|
|
|
|
*/
|
2008-04-13 09:48:58 +08:00
|
|
|
struct tun_struct {
|
2012-11-01 03:46:00 +08:00
|
|
|
struct tun_file __rcu *tfiles[MAX_TAP_QUEUES];
|
|
|
|
unsigned int numqueues;
|
2008-07-15 13:18:19 +08:00
|
|
|
unsigned int flags;
|
2012-02-08 08:48:55 +08:00
|
|
|
kuid_t owner;
|
|
|
|
kgid_t group;
|
2008-04-13 09:48:58 +08:00
|
|
|
|
|
|
|
struct net_device *dev;
|
2011-11-15 23:29:55 +08:00
|
|
|
netdev_features_t set_features;
|
2011-04-19 14:13:10 +08:00
|
|
|
#define TUN_USER_FEATURES (NETIF_F_HW_CSUM|NETIF_F_TSO_ECN|NETIF_F_TSO| \
|
2017-07-03 21:35:32 +08:00
|
|
|
NETIF_F_TSO6)
|
2010-03-17 23:45:01 +08:00
|
|
|
|
2016-02-26 17:45:40 +08:00
|
|
|
int align;
|
2010-03-17 23:45:01 +08:00
|
|
|
int vnet_hdr_sz;
|
2012-11-01 03:45:57 +08:00
|
|
|
int sndbuf;
|
|
|
|
struct tap_filter txflt;
|
|
|
|
struct sock_fprog fprog;
|
|
|
|
/* protected by rtnl lock */
|
|
|
|
bool filter_attached;
|
2020-03-05 00:24:14 +08:00
|
|
|
u32 msg_enable;
|
2012-11-01 03:46:02 +08:00
|
|
|
spinlock_t lock;
|
|
|
|
struct hlist_head flows[TUN_NUM_FLOW_ENTRIES];
|
|
|
|
struct timer_list flow_gc_timer;
|
|
|
|
unsigned long ageing_time;
|
2012-12-14 07:53:30 +08:00
|
|
|
unsigned int numdisabled;
|
|
|
|
struct list_head disabled;
|
2013-01-14 15:12:19 +08:00
|
|
|
void *security;
|
2013-01-23 11:59:13 +08:00
|
|
|
u32 flow_count;
|
2017-01-18 15:02:03 +08:00
|
|
|
u32 rx_batched;
|
2020-11-08 04:50:56 +08:00
|
|
|
atomic_long_t rx_frame_errors;
|
2017-08-11 19:41:18 +08:00
|
|
|
struct bpf_prog __rcu *xdp_prog;
|
2018-01-16 16:31:01 +08:00
|
|
|
struct tun_prog __rcu *steering_prog;
|
2018-01-16 16:31:02 +08:00
|
|
|
struct tun_prog __rcu *filter_prog;
|
2018-06-03 05:49:53 +08:00
|
|
|
struct ethtool_link_ksettings link_ksettings;
|
2008-04-13 09:48:58 +08:00
|
|
|
};
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2018-01-16 16:31:02 +08:00
|
|
|
struct veth {
|
|
|
|
__be16 h_vlan_proto;
|
|
|
|
__be16 h_vlan_TCI;
|
2008-04-13 09:48:58 +08:00
|
|
|
};
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-09-23 04:49:14 +08:00
|
|
|
static int tun_napi_receive(struct napi_struct *napi, int budget)
|
|
|
|
{
|
|
|
|
struct tun_file *tfile = container_of(napi, struct tun_file, napi);
|
|
|
|
struct sk_buff_head *queue = &tfile->sk.sk_write_queue;
|
|
|
|
struct sk_buff_head process_queue;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
int received = 0;
|
|
|
|
|
|
|
|
__skb_queue_head_init(&process_queue);
|
|
|
|
|
|
|
|
spin_lock(&queue->lock);
|
|
|
|
skb_queue_splice_tail_init(queue, &process_queue);
|
|
|
|
spin_unlock(&queue->lock);
|
|
|
|
|
|
|
|
while (received < budget && (skb = __skb_dequeue(&process_queue))) {
|
|
|
|
napi_gro_receive(napi, skb);
|
|
|
|
++received;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!skb_queue_empty(&process_queue)) {
|
|
|
|
spin_lock(&queue->lock);
|
|
|
|
skb_queue_splice(&process_queue, queue);
|
|
|
|
spin_unlock(&queue->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
return received;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tun_napi_poll(struct napi_struct *napi, int budget)
|
|
|
|
{
|
|
|
|
unsigned int received;
|
|
|
|
|
|
|
|
received = tun_napi_receive(napi, budget);
|
|
|
|
|
|
|
|
if (received < budget)
|
|
|
|
napi_complete_done(napi, received);
|
|
|
|
|
|
|
|
return received;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_napi_init(struct tun_struct *tun, struct tun_file *tfile,
|
2018-09-29 05:51:49 +08:00
|
|
|
bool napi_en, bool napi_frags)
|
2017-09-23 04:49:14 +08:00
|
|
|
{
|
2017-10-19 03:12:09 +08:00
|
|
|
tfile->napi_enabled = napi_en;
|
2018-09-29 05:51:49 +08:00
|
|
|
tfile->napi_frags_enabled = napi_en && napi_frags;
|
2017-09-23 04:49:14 +08:00
|
|
|
if (napi_en) {
|
2019-11-15 01:52:09 +08:00
|
|
|
netif_tx_napi_add(tun->dev, &tfile->napi, tun_napi_poll,
|
|
|
|
NAPI_POLL_WEIGHT);
|
2017-09-23 04:49:14 +08:00
|
|
|
napi_enable(&tfile->napi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-29 05:51:47 +08:00
|
|
|
static void tun_napi_disable(struct tun_file *tfile)
|
2017-09-23 04:49:14 +08:00
|
|
|
{
|
2017-10-19 03:12:09 +08:00
|
|
|
if (tfile->napi_enabled)
|
2017-09-23 04:49:14 +08:00
|
|
|
napi_disable(&tfile->napi);
|
|
|
|
}
|
|
|
|
|
2018-09-29 05:51:47 +08:00
|
|
|
static void tun_napi_del(struct tun_file *tfile)
|
2017-09-23 04:49:14 +08:00
|
|
|
{
|
2017-10-19 03:12:09 +08:00
|
|
|
if (tfile->napi_enabled)
|
2017-09-23 04:49:14 +08:00
|
|
|
netif_napi_del(&tfile->napi);
|
|
|
|
}
|
|
|
|
|
2018-09-29 05:51:49 +08:00
|
|
|
static bool tun_napi_frags_enabled(const struct tun_file *tfile)
|
2017-09-23 04:49:15 +08:00
|
|
|
{
|
2018-09-29 05:51:49 +08:00
|
|
|
return tfile->napi_frags_enabled;
|
2017-09-23 04:49:15 +08:00
|
|
|
}
|
|
|
|
|
2015-04-24 20:50:36 +08:00
|
|
|
#ifdef CONFIG_TUN_VNET_CROSS_LE
|
|
|
|
static inline bool tun_legacy_is_little_endian(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
return tun->flags & TUN_VNET_BE ? false :
|
|
|
|
virtio_legacy_is_little_endian();
|
|
|
|
}
|
|
|
|
|
|
|
|
static long tun_get_vnet_be(struct tun_struct *tun, int __user *argp)
|
|
|
|
{
|
|
|
|
int be = !!(tun->flags & TUN_VNET_BE);
|
|
|
|
|
|
|
|
if (put_user(be, argp))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long tun_set_vnet_be(struct tun_struct *tun, int __user *argp)
|
|
|
|
{
|
|
|
|
int be;
|
|
|
|
|
|
|
|
if (get_user(be, argp))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (be)
|
|
|
|
tun->flags |= TUN_VNET_BE;
|
|
|
|
else
|
|
|
|
tun->flags &= ~TUN_VNET_BE;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static inline bool tun_legacy_is_little_endian(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
return virtio_legacy_is_little_endian();
|
|
|
|
}
|
|
|
|
|
|
|
|
static long tun_get_vnet_be(struct tun_struct *tun, int __user *argp)
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long tun_set_vnet_be(struct tun_struct *tun, int __user *argp)
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_TUN_VNET_CROSS_LE */
|
|
|
|
|
2015-04-24 20:24:38 +08:00
|
|
|
static inline bool tun_is_little_endian(struct tun_struct *tun)
|
|
|
|
{
|
2015-04-24 20:26:24 +08:00
|
|
|
return tun->flags & TUN_VNET_LE ||
|
2015-04-24 20:50:36 +08:00
|
|
|
tun_legacy_is_little_endian(tun);
|
2015-04-24 20:24:38 +08:00
|
|
|
}
|
|
|
|
|
2014-10-24 03:59:31 +08:00
|
|
|
static inline u16 tun16_to_cpu(struct tun_struct *tun, __virtio16 val)
|
|
|
|
{
|
2015-04-24 20:24:38 +08:00
|
|
|
return __virtio16_to_cpu(tun_is_little_endian(tun), val);
|
2014-10-24 03:59:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline __virtio16 cpu_to_tun16(struct tun_struct *tun, u16 val)
|
|
|
|
{
|
2015-04-24 20:24:38 +08:00
|
|
|
return __cpu_to_virtio16(tun_is_little_endian(tun), val);
|
2014-10-24 03:59:31 +08:00
|
|
|
}
|
|
|
|
|
2012-11-01 03:46:02 +08:00
|
|
|
static inline u32 tun_hashfn(u32 rxhash)
|
|
|
|
{
|
2018-08-03 15:50:02 +08:00
|
|
|
return rxhash & TUN_MASK_FLOW_ENTRIES;
|
2012-11-01 03:46:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct tun_flow_entry *tun_flow_find(struct hlist_head *head, u32 rxhash)
|
|
|
|
{
|
|
|
|
struct tun_flow_entry *e;
|
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
hlist_for_each_entry_rcu(e, head, hash_link) {
|
2012-11-01 03:46:02 +08:00
|
|
|
if (e->rxhash == rxhash)
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct tun_flow_entry *tun_flow_create(struct tun_struct *tun,
|
|
|
|
struct hlist_head *head,
|
|
|
|
u32 rxhash, u16 queue_index)
|
|
|
|
{
|
2012-12-21 15:17:21 +08:00
|
|
|
struct tun_flow_entry *e = kmalloc(sizeof(*e), GFP_ATOMIC);
|
|
|
|
|
2012-11-01 03:46:02 +08:00
|
|
|
if (e) {
|
2020-03-05 00:24:14 +08:00
|
|
|
netif_info(tun, tx_queued, tun->dev,
|
|
|
|
"create flow: hash %u index %u\n",
|
|
|
|
rxhash, queue_index);
|
2012-11-01 03:46:02 +08:00
|
|
|
e->updated = jiffies;
|
|
|
|
e->rxhash = rxhash;
|
2013-12-22 18:54:32 +08:00
|
|
|
e->rps_rxhash = 0;
|
2012-11-01 03:46:02 +08:00
|
|
|
e->queue_index = queue_index;
|
|
|
|
e->tun = tun;
|
|
|
|
hlist_add_head_rcu(&e->hash_link, head);
|
2013-01-23 11:59:13 +08:00
|
|
|
++tun->flow_count;
|
2012-11-01 03:46:02 +08:00
|
|
|
}
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_flow_delete(struct tun_struct *tun, struct tun_flow_entry *e)
|
|
|
|
{
|
2020-03-05 00:24:14 +08:00
|
|
|
netif_info(tun, tx_queued, tun->dev, "delete flow: hash %u index %u\n",
|
|
|
|
e->rxhash, e->queue_index);
|
2012-11-01 03:46:02 +08:00
|
|
|
hlist_del_rcu(&e->hash_link);
|
2012-12-21 15:17:21 +08:00
|
|
|
kfree_rcu(e, rcu);
|
2013-01-23 11:59:13 +08:00
|
|
|
--tun->flow_count;
|
2012-11-01 03:46:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_flow_flush(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
spin_lock_bh(&tun->lock);
|
|
|
|
for (i = 0; i < TUN_NUM_FLOW_ENTRIES; i++) {
|
|
|
|
struct tun_flow_entry *e;
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
struct hlist_node *n;
|
2012-11-01 03:46:02 +08:00
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
hlist_for_each_entry_safe(e, n, &tun->flows[i], hash_link)
|
2012-11-01 03:46:02 +08:00
|
|
|
tun_flow_delete(tun, e);
|
|
|
|
}
|
|
|
|
spin_unlock_bh(&tun->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_flow_delete_by_queue(struct tun_struct *tun, u16 queue_index)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
spin_lock_bh(&tun->lock);
|
|
|
|
for (i = 0; i < TUN_NUM_FLOW_ENTRIES; i++) {
|
|
|
|
struct tun_flow_entry *e;
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
struct hlist_node *n;
|
2012-11-01 03:46:02 +08:00
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
hlist_for_each_entry_safe(e, n, &tun->flows[i], hash_link) {
|
2012-11-01 03:46:02 +08:00
|
|
|
if (e->queue_index == queue_index)
|
|
|
|
tun_flow_delete(tun, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_bh(&tun->lock);
|
|
|
|
}
|
|
|
|
|
treewide: setup_timer() -> timer_setup()
This converts all remaining cases of the old setup_timer() API into using
timer_setup(), where the callback argument is the structure already
holding the struct timer_list. These should have no behavioral changes,
since they just change which pointer is passed into the callback with
the same available pointers after conversion. It handles the following
examples, in addition to some other variations.
Casting from unsigned long:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
setup_timer(&ptr->my_timer, my_callback, ptr);
and forced object casts:
void my_callback(struct something *ptr)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
become:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
Direct function assignments:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
ptr->my_timer.function = my_callback;
have a temporary cast added, along with converting the args:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
And finally, callbacks without a data assignment:
void my_callback(unsigned long data)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, 0);
have their argument renamed to verify they're unused during conversion:
void my_callback(struct timer_list *unused)
{
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
The conversion is done with the following Coccinelle script:
spatch --very-quiet --all-includes --include-headers \
-I ./arch/x86/include -I ./arch/x86/include/generated \
-I ./include -I ./arch/x86/include/uapi \
-I ./arch/x86/include/generated/uapi -I ./include/uapi \
-I ./include/generated/uapi --include ./include/linux/kconfig.h \
--dir . \
--cocci-file ~/src/data/timer_setup.cocci
@fix_address_of@
expression e;
@@
setup_timer(
-&(e)
+&e
, ...)
// Update any raw setup_timer() usages that have a NULL callback, but
// would otherwise match change_timer_function_usage, since the latter
// will update all function assignments done in the face of a NULL
// function initialization in setup_timer().
@change_timer_function_usage_NULL@
expression _E;
identifier _timer;
type _cast_data;
@@
(
-setup_timer(&_E->_timer, NULL, _E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E->_timer, NULL, (_cast_data)_E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, &_E);
+timer_setup(&_E._timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, (_cast_data)&_E);
+timer_setup(&_E._timer, NULL, 0);
)
@change_timer_function_usage@
expression _E;
identifier _timer;
struct timer_list _stl;
identifier _callback;
type _cast_func, _cast_data;
@@
(
-setup_timer(&_E->_timer, _callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
_E->_timer@_stl.function = _callback;
|
_E->_timer@_stl.function = &_callback;
|
_E->_timer@_stl.function = (_cast_func)_callback;
|
_E->_timer@_stl.function = (_cast_func)&_callback;
|
_E._timer@_stl.function = _callback;
|
_E._timer@_stl.function = &_callback;
|
_E._timer@_stl.function = (_cast_func)_callback;
|
_E._timer@_stl.function = (_cast_func)&_callback;
)
// callback(unsigned long arg)
@change_callback_handle_cast
depends on change_timer_function_usage@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
identifier _handle;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
(
... when != _origarg
_handletype *_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
)
}
// callback(unsigned long arg) without existing variable
@change_callback_handle_cast_no_arg
depends on change_timer_function_usage &&
!change_callback_handle_cast@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
+ _handletype *_origarg = from_timer(_origarg, t, _timer);
+
... when != _origarg
- (_handletype *)_origarg
+ _origarg
... when != _origarg
}
// Avoid already converted callbacks.
@match_callback_converted
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier t;
@@
void _callback(struct timer_list *t)
{ ... }
// callback(struct something *handle)
@change_callback_handle_arg
depends on change_timer_function_usage &&
!match_callback_converted &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
@@
void _callback(
-_handletype *_handle
+struct timer_list *t
)
{
+ _handletype *_handle = from_timer(_handle, t, _timer);
...
}
// If change_callback_handle_arg ran on an empty function, remove
// the added handler.
@unchange_callback_handle_arg
depends on change_timer_function_usage &&
change_callback_handle_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
identifier t;
@@
void _callback(struct timer_list *t)
{
- _handletype *_handle = from_timer(_handle, t, _timer);
}
// We only want to refactor the setup_timer() data argument if we've found
// the matching callback. This undoes changes in change_timer_function_usage.
@unchange_timer_function_usage
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg &&
!change_callback_handle_arg@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type change_timer_function_usage._cast_data;
@@
(
-timer_setup(&_E->_timer, _callback, 0);
+setup_timer(&_E->_timer, _callback, (_cast_data)_E);
|
-timer_setup(&_E._timer, _callback, 0);
+setup_timer(&_E._timer, _callback, (_cast_data)&_E);
)
// If we fixed a callback from a .function assignment, fix the
// assignment cast now.
@change_timer_function_assignment
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_func;
typedef TIMER_FUNC_TYPE;
@@
(
_E->_timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-&_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
)
// Sometimes timer functions are called directly. Replace matched args.
@change_timer_function_calls
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression _E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_data;
@@
_callback(
(
-(_cast_data)_E
+&_E->_timer
|
-(_cast_data)&_E
+&_E._timer
|
-_E
+&_E->_timer
)
)
// If a timer has been configured without a data argument, it can be
// converted without regard to the callback argument, since it is unused.
@match_timer_function_unused_data@
expression _E;
identifier _timer;
identifier _callback;
@@
(
-setup_timer(&_E->_timer, _callback, 0);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0L);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0UL);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0L);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0UL);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0L);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0UL);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0L);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0UL);
+timer_setup(_timer, _callback, 0);
)
@change_callback_unused_data
depends on match_timer_function_unused_data@
identifier match_timer_function_unused_data._callback;
type _origtype;
identifier _origarg;
@@
void _callback(
-_origtype _origarg
+struct timer_list *unused
)
{
... when != _origarg
}
Signed-off-by: Kees Cook <keescook@chromium.org>
2017-10-17 05:43:17 +08:00
|
|
|
static void tun_flow_cleanup(struct timer_list *t)
|
2012-11-01 03:46:02 +08:00
|
|
|
{
|
treewide: setup_timer() -> timer_setup()
This converts all remaining cases of the old setup_timer() API into using
timer_setup(), where the callback argument is the structure already
holding the struct timer_list. These should have no behavioral changes,
since they just change which pointer is passed into the callback with
the same available pointers after conversion. It handles the following
examples, in addition to some other variations.
Casting from unsigned long:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
setup_timer(&ptr->my_timer, my_callback, ptr);
and forced object casts:
void my_callback(struct something *ptr)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
become:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
Direct function assignments:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
ptr->my_timer.function = my_callback;
have a temporary cast added, along with converting the args:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
And finally, callbacks without a data assignment:
void my_callback(unsigned long data)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, 0);
have their argument renamed to verify they're unused during conversion:
void my_callback(struct timer_list *unused)
{
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
The conversion is done with the following Coccinelle script:
spatch --very-quiet --all-includes --include-headers \
-I ./arch/x86/include -I ./arch/x86/include/generated \
-I ./include -I ./arch/x86/include/uapi \
-I ./arch/x86/include/generated/uapi -I ./include/uapi \
-I ./include/generated/uapi --include ./include/linux/kconfig.h \
--dir . \
--cocci-file ~/src/data/timer_setup.cocci
@fix_address_of@
expression e;
@@
setup_timer(
-&(e)
+&e
, ...)
// Update any raw setup_timer() usages that have a NULL callback, but
// would otherwise match change_timer_function_usage, since the latter
// will update all function assignments done in the face of a NULL
// function initialization in setup_timer().
@change_timer_function_usage_NULL@
expression _E;
identifier _timer;
type _cast_data;
@@
(
-setup_timer(&_E->_timer, NULL, _E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E->_timer, NULL, (_cast_data)_E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, &_E);
+timer_setup(&_E._timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, (_cast_data)&_E);
+timer_setup(&_E._timer, NULL, 0);
)
@change_timer_function_usage@
expression _E;
identifier _timer;
struct timer_list _stl;
identifier _callback;
type _cast_func, _cast_data;
@@
(
-setup_timer(&_E->_timer, _callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
_E->_timer@_stl.function = _callback;
|
_E->_timer@_stl.function = &_callback;
|
_E->_timer@_stl.function = (_cast_func)_callback;
|
_E->_timer@_stl.function = (_cast_func)&_callback;
|
_E._timer@_stl.function = _callback;
|
_E._timer@_stl.function = &_callback;
|
_E._timer@_stl.function = (_cast_func)_callback;
|
_E._timer@_stl.function = (_cast_func)&_callback;
)
// callback(unsigned long arg)
@change_callback_handle_cast
depends on change_timer_function_usage@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
identifier _handle;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
(
... when != _origarg
_handletype *_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
)
}
// callback(unsigned long arg) without existing variable
@change_callback_handle_cast_no_arg
depends on change_timer_function_usage &&
!change_callback_handle_cast@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
+ _handletype *_origarg = from_timer(_origarg, t, _timer);
+
... when != _origarg
- (_handletype *)_origarg
+ _origarg
... when != _origarg
}
// Avoid already converted callbacks.
@match_callback_converted
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier t;
@@
void _callback(struct timer_list *t)
{ ... }
// callback(struct something *handle)
@change_callback_handle_arg
depends on change_timer_function_usage &&
!match_callback_converted &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
@@
void _callback(
-_handletype *_handle
+struct timer_list *t
)
{
+ _handletype *_handle = from_timer(_handle, t, _timer);
...
}
// If change_callback_handle_arg ran on an empty function, remove
// the added handler.
@unchange_callback_handle_arg
depends on change_timer_function_usage &&
change_callback_handle_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
identifier t;
@@
void _callback(struct timer_list *t)
{
- _handletype *_handle = from_timer(_handle, t, _timer);
}
// We only want to refactor the setup_timer() data argument if we've found
// the matching callback. This undoes changes in change_timer_function_usage.
@unchange_timer_function_usage
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg &&
!change_callback_handle_arg@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type change_timer_function_usage._cast_data;
@@
(
-timer_setup(&_E->_timer, _callback, 0);
+setup_timer(&_E->_timer, _callback, (_cast_data)_E);
|
-timer_setup(&_E._timer, _callback, 0);
+setup_timer(&_E._timer, _callback, (_cast_data)&_E);
)
// If we fixed a callback from a .function assignment, fix the
// assignment cast now.
@change_timer_function_assignment
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_func;
typedef TIMER_FUNC_TYPE;
@@
(
_E->_timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-&_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
)
// Sometimes timer functions are called directly. Replace matched args.
@change_timer_function_calls
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression _E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_data;
@@
_callback(
(
-(_cast_data)_E
+&_E->_timer
|
-(_cast_data)&_E
+&_E._timer
|
-_E
+&_E->_timer
)
)
// If a timer has been configured without a data argument, it can be
// converted without regard to the callback argument, since it is unused.
@match_timer_function_unused_data@
expression _E;
identifier _timer;
identifier _callback;
@@
(
-setup_timer(&_E->_timer, _callback, 0);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0L);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0UL);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0L);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0UL);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0L);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0UL);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0L);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0UL);
+timer_setup(_timer, _callback, 0);
)
@change_callback_unused_data
depends on match_timer_function_unused_data@
identifier match_timer_function_unused_data._callback;
type _origtype;
identifier _origarg;
@@
void _callback(
-_origtype _origarg
+struct timer_list *unused
)
{
... when != _origarg
}
Signed-off-by: Kees Cook <keescook@chromium.org>
2017-10-17 05:43:17 +08:00
|
|
|
struct tun_struct *tun = from_timer(tun, t, flow_gc_timer);
|
2012-11-01 03:46:02 +08:00
|
|
|
unsigned long delay = tun->ageing_time;
|
|
|
|
unsigned long next_timer = jiffies + delay;
|
|
|
|
unsigned long count = 0;
|
|
|
|
int i;
|
|
|
|
|
2017-10-21 02:29:55 +08:00
|
|
|
spin_lock(&tun->lock);
|
2012-11-01 03:46:02 +08:00
|
|
|
for (i = 0; i < TUN_NUM_FLOW_ENTRIES; i++) {
|
|
|
|
struct tun_flow_entry *e;
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
struct hlist_node *n;
|
2012-11-01 03:46:02 +08:00
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
hlist_for_each_entry_safe(e, n, &tun->flows[i], hash_link) {
|
2012-11-01 03:46:02 +08:00
|
|
|
unsigned long this_timer;
|
2017-10-21 02:29:56 +08:00
|
|
|
|
2012-11-01 03:46:02 +08:00
|
|
|
this_timer = e->updated + delay;
|
2017-10-21 02:29:56 +08:00
|
|
|
if (time_before_eq(this_timer, jiffies)) {
|
2012-11-01 03:46:02 +08:00
|
|
|
tun_flow_delete(tun, e);
|
2017-10-21 02:29:56 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
count++;
|
|
|
|
if (time_before(this_timer, next_timer))
|
2012-11-01 03:46:02 +08:00
|
|
|
next_timer = this_timer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count)
|
|
|
|
mod_timer(&tun->flow_gc_timer, round_jiffies_up(next_timer));
|
2017-10-21 02:29:55 +08:00
|
|
|
spin_unlock(&tun->lock);
|
2012-11-01 03:46:02 +08:00
|
|
|
}
|
|
|
|
|
2012-12-13 03:22:57 +08:00
|
|
|
static void tun_flow_update(struct tun_struct *tun, u32 rxhash,
|
2013-01-28 09:05:19 +08:00
|
|
|
struct tun_file *tfile)
|
2012-11-01 03:46:02 +08:00
|
|
|
{
|
|
|
|
struct hlist_head *head;
|
|
|
|
struct tun_flow_entry *e;
|
|
|
|
unsigned long delay = tun->ageing_time;
|
2013-01-28 09:05:19 +08:00
|
|
|
u16 queue_index = tfile->queue_index;
|
2012-11-01 03:46:02 +08:00
|
|
|
|
2018-12-06 16:28:11 +08:00
|
|
|
head = &tun->flows[tun_hashfn(rxhash)];
|
2012-11-01 03:46:02 +08:00
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
|
|
|
|
e = tun_flow_find(head, rxhash);
|
|
|
|
if (likely(e)) {
|
|
|
|
/* TODO: keep queueing to old queue until it's empty? */
|
2019-10-10 00:20:02 +08:00
|
|
|
if (READ_ONCE(e->queue_index) != queue_index)
|
|
|
|
WRITE_ONCE(e->queue_index, queue_index);
|
2018-12-06 16:08:17 +08:00
|
|
|
if (e->updated != jiffies)
|
|
|
|
e->updated = jiffies;
|
2013-12-22 18:54:32 +08:00
|
|
|
sock_rps_record_flow_hash(e->rps_rxhash);
|
2012-11-01 03:46:02 +08:00
|
|
|
} else {
|
|
|
|
spin_lock_bh(&tun->lock);
|
2013-01-23 11:59:13 +08:00
|
|
|
if (!tun_flow_find(head, rxhash) &&
|
|
|
|
tun->flow_count < MAX_TAP_FLOWS)
|
2012-11-01 03:46:02 +08:00
|
|
|
tun_flow_create(tun, head, rxhash, queue_index);
|
|
|
|
|
|
|
|
if (!timer_pending(&tun->flow_gc_timer))
|
|
|
|
mod_timer(&tun->flow_gc_timer,
|
|
|
|
round_jiffies_up(jiffies + delay));
|
|
|
|
spin_unlock_bh(&tun->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
|
|
|
|
2020-03-05 00:23:59 +08:00
|
|
|
/* Save the hash received in the stack receive path and update the
|
2013-12-22 18:54:32 +08:00
|
|
|
* flow_hash table accordingly.
|
|
|
|
*/
|
|
|
|
static inline void tun_flow_save_rps_rxhash(struct tun_flow_entry *e, u32 hash)
|
|
|
|
{
|
net: rfs: add hash collision detection
Receive Flow Steering is a nice solution but suffers from
hash collisions when a mix of connected and unconnected traffic
is received on the host, when flow hash table is populated.
Also, clearing flow in inet_release() makes RFS not very good
for short lived flows, as many packets can follow close().
(FIN , ACK packets, ...)
This patch extends the information stored into global hash table
to not only include cpu number, but upper part of the hash value.
I use a 32bit value, and dynamically split it in two parts.
For host with less than 64 possible cpus, this gives 6 bits for the
cpu number, and 26 (32-6) bits for the upper part of the hash.
Since hash bucket selection use low order bits of the hash, we have
a full hash match, if /proc/sys/net/core/rps_sock_flow_entries is big
enough.
If the hash found in flow table does not match, we fallback to RPS (if
it is enabled for the rxqueue).
This means that a packet for an non connected flow can avoid the
IPI through a unrelated/victim CPU.
This also means we no longer have to clear the table at socket
close time, and this helps short lived flows performance.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Acked-by: Tom Herbert <therbert@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-02-07 04:59:01 +08:00
|
|
|
if (unlikely(e->rps_rxhash != hash))
|
2013-12-22 18:54:32 +08:00
|
|
|
e->rps_rxhash = hash;
|
|
|
|
}
|
|
|
|
|
2018-10-09 10:32:04 +08:00
|
|
|
/* We try to identify a flow through its rxhash. The reason that
|
2013-12-06 12:42:58 +08:00
|
|
|
* we do not check rxq no. is because some cards(e.g 82599), chooses
|
2012-11-01 03:46:00 +08:00
|
|
|
* the rxq based on the txq where the last packet of the flow comes. As
|
|
|
|
* the userspace application move between processors, we may get a
|
2018-10-09 10:32:04 +08:00
|
|
|
* different rxq no. here.
|
2012-11-01 03:46:00 +08:00
|
|
|
*/
|
2017-12-04 17:31:23 +08:00
|
|
|
static u16 tun_automq_select_queue(struct tun_struct *tun, struct sk_buff *skb)
|
2012-11-01 03:46:00 +08:00
|
|
|
{
|
2012-11-01 03:46:02 +08:00
|
|
|
struct tun_flow_entry *e;
|
2012-11-01 03:46:00 +08:00
|
|
|
u32 txq = 0;
|
|
|
|
u32 numqueues = 0;
|
|
|
|
|
locking/atomics: COCCINELLE/treewide: Convert trivial ACCESS_ONCE() patterns to READ_ONCE()/WRITE_ONCE()
Please do not apply this to mainline directly, instead please re-run the
coccinelle script shown below and apply its output.
For several reasons, it is desirable to use {READ,WRITE}_ONCE() in
preference to ACCESS_ONCE(), and new code is expected to use one of the
former. So far, there's been no reason to change most existing uses of
ACCESS_ONCE(), as these aren't harmful, and changing them results in
churn.
However, for some features, the read/write distinction is critical to
correct operation. To distinguish these cases, separate read/write
accessors must be used. This patch migrates (most) remaining
ACCESS_ONCE() instances to {READ,WRITE}_ONCE(), using the following
coccinelle script:
----
// Convert trivial ACCESS_ONCE() uses to equivalent READ_ONCE() and
// WRITE_ONCE()
// $ make coccicheck COCCI=/home/mark/once.cocci SPFLAGS="--include-headers" MODE=patch
virtual patch
@ depends on patch @
expression E1, E2;
@@
- ACCESS_ONCE(E1) = E2
+ WRITE_ONCE(E1, E2)
@ depends on patch @
expression E;
@@
- ACCESS_ONCE(E)
+ READ_ONCE(E)
----
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: davem@davemloft.net
Cc: linux-arch@vger.kernel.org
Cc: mpe@ellerman.id.au
Cc: shuah@kernel.org
Cc: snitzer@redhat.com
Cc: thor.thayer@linux.intel.com
Cc: tj@kernel.org
Cc: viro@zeniv.linux.org.uk
Cc: will.deacon@arm.com
Link: http://lkml.kernel.org/r/1508792849-3115-19-git-send-email-paulmck@linux.vnet.ibm.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-10-24 05:07:29 +08:00
|
|
|
numqueues = READ_ONCE(tun->numqueues);
|
2012-11-01 03:46:00 +08:00
|
|
|
|
2017-06-06 14:09:49 +08:00
|
|
|
txq = __skb_get_hash_symmetric(skb);
|
2018-10-09 10:32:04 +08:00
|
|
|
e = tun_flow_find(&tun->flows[tun_hashfn(txq)], txq);
|
|
|
|
if (e) {
|
|
|
|
tun_flow_save_rps_rxhash(e, txq);
|
|
|
|
txq = e->queue_index;
|
|
|
|
} else {
|
|
|
|
/* use multiply and shift instead of expensive divide */
|
|
|
|
txq = ((u64)txq * numqueues) >> 32;
|
2012-11-01 03:46:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return txq;
|
|
|
|
}
|
|
|
|
|
2017-12-04 17:31:23 +08:00
|
|
|
static u16 tun_ebpf_select_queue(struct tun_struct *tun, struct sk_buff *skb)
|
|
|
|
{
|
2018-01-16 16:31:01 +08:00
|
|
|
struct tun_prog *prog;
|
2019-05-09 11:20:17 +08:00
|
|
|
u32 numqueues;
|
2017-12-04 17:31:23 +08:00
|
|
|
u16 ret = 0;
|
|
|
|
|
2019-05-09 11:20:17 +08:00
|
|
|
numqueues = READ_ONCE(tun->numqueues);
|
|
|
|
if (!numqueues)
|
|
|
|
return 0;
|
|
|
|
|
2017-12-04 17:31:23 +08:00
|
|
|
prog = rcu_dereference(tun->steering_prog);
|
|
|
|
if (prog)
|
|
|
|
ret = bpf_prog_run_clear_cb(prog->prog, skb);
|
|
|
|
|
2019-05-09 11:20:17 +08:00
|
|
|
return ret % numqueues;
|
2017-12-04 17:31:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static u16 tun_select_queue(struct net_device *dev, struct sk_buff *skb,
|
2019-03-20 18:02:06 +08:00
|
|
|
struct net_device *sb_dev)
|
2017-12-04 17:31:23 +08:00
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
u16 ret;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
if (rcu_dereference(tun->steering_prog))
|
|
|
|
ret = tun_ebpf_select_queue(tun, skb);
|
|
|
|
else
|
|
|
|
ret = tun_automq_select_queue(tun, skb);
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-11-01 03:46:01 +08:00
|
|
|
static inline bool tun_not_capable(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
const struct cred *cred = current_cred();
|
2012-11-19 05:34:11 +08:00
|
|
|
struct net *net = dev_net(tun->dev);
|
2012-11-01 03:46:01 +08:00
|
|
|
|
|
|
|
return ((uid_valid(tun->owner) && !uid_eq(cred->euid, tun->owner)) ||
|
|
|
|
(gid_valid(tun->group) && !in_egroup_p(tun->group))) &&
|
2012-11-19 05:34:11 +08:00
|
|
|
!ns_capable(net->user_ns, CAP_NET_ADMIN);
|
2012-11-01 03:46:01 +08:00
|
|
|
}
|
|
|
|
|
2012-11-01 03:46:00 +08:00
|
|
|
static void tun_set_real_num_queues(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
netif_set_real_num_tx_queues(tun->dev, tun->numqueues);
|
|
|
|
netif_set_real_num_rx_queues(tun->dev, tun->numqueues);
|
|
|
|
}
|
|
|
|
|
2012-12-14 07:53:30 +08:00
|
|
|
static void tun_disable_queue(struct tun_struct *tun, struct tun_file *tfile)
|
|
|
|
{
|
|
|
|
tfile->detached = tun;
|
|
|
|
list_add_tail(&tfile->next, &tun->disabled);
|
|
|
|
++tun->numdisabled;
|
|
|
|
}
|
|
|
|
|
2012-12-18 11:00:27 +08:00
|
|
|
static struct tun_struct *tun_enable_queue(struct tun_file *tfile)
|
2012-12-14 07:53:30 +08:00
|
|
|
{
|
|
|
|
struct tun_struct *tun = tfile->detached;
|
|
|
|
|
|
|
|
tfile->detached = NULL;
|
|
|
|
list_del_init(&tfile->next);
|
|
|
|
--tun->numdisabled;
|
|
|
|
return tun;
|
|
|
|
}
|
|
|
|
|
2018-03-09 14:50:34 +08:00
|
|
|
void tun_ptr_free(void *ptr)
|
2018-01-04 11:14:28 +08:00
|
|
|
{
|
|
|
|
if (!ptr)
|
|
|
|
return;
|
2018-04-17 22:45:47 +08:00
|
|
|
if (tun_is_xdp_frame(ptr)) {
|
|
|
|
struct xdp_frame *xdpf = tun_ptr_to_xdp(ptr);
|
2018-01-04 11:14:28 +08:00
|
|
|
|
xdp: transition into using xdp_frame for return API
Changing API xdp_return_frame() to take struct xdp_frame as argument,
seems like a natural choice. But there are some subtle performance
details here that needs extra care, which is a deliberate choice.
When de-referencing xdp_frame on a remote CPU during DMA-TX
completion, result in the cache-line is change to "Shared"
state. Later when the page is reused for RX, then this xdp_frame
cache-line is written, which change the state to "Modified".
This situation already happens (naturally) for, virtio_net, tun and
cpumap as the xdp_frame pointer is the queued object. In tun and
cpumap, the ptr_ring is used for efficiently transferring cache-lines
(with pointers) between CPUs. Thus, the only option is to
de-referencing xdp_frame.
It is only the ixgbe driver that had an optimization, in which it can
avoid doing the de-reference of xdp_frame. The driver already have
TX-ring queue, which (in case of remote DMA-TX completion) have to be
transferred between CPUs anyhow. In this data area, we stored a
struct xdp_mem_info and a data pointer, which allowed us to avoid
de-referencing xdp_frame.
To compensate for this, a prefetchw is used for telling the cache
coherency protocol about our access pattern. My benchmarks show that
this prefetchw is enough to compensate the ixgbe driver.
V7: Adjust for commit d9314c474d4f ("i40e: add support for XDP_REDIRECT")
V8: Adjust for commit bd658dda4237 ("net/mlx5e: Separate dma base address
and offset in dma_sync call")
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2018-04-17 22:46:32 +08:00
|
|
|
xdp_return_frame(xdpf);
|
2018-01-04 11:14:28 +08:00
|
|
|
} else {
|
|
|
|
__skb_array_destroy_skb(ptr);
|
|
|
|
}
|
|
|
|
}
|
2018-03-09 14:50:34 +08:00
|
|
|
EXPORT_SYMBOL_GPL(tun_ptr_free);
|
2018-01-04 11:14:28 +08:00
|
|
|
|
2013-09-05 17:53:59 +08:00
|
|
|
static void tun_queue_purge(struct tun_file *tfile)
|
|
|
|
{
|
2018-01-04 11:14:28 +08:00
|
|
|
void *ptr;
|
2016-06-30 14:45:36 +08:00
|
|
|
|
2018-01-04 11:14:28 +08:00
|
|
|
while ((ptr = ptr_ring_consume(&tfile->tx_ring)) != NULL)
|
|
|
|
tun_ptr_free(ptr);
|
2016-06-30 14:45:36 +08:00
|
|
|
|
2017-01-18 15:02:03 +08:00
|
|
|
skb_queue_purge(&tfile->sk.sk_write_queue);
|
2013-09-05 17:53:59 +08:00
|
|
|
skb_queue_purge(&tfile->sk.sk_error_queue);
|
|
|
|
}
|
|
|
|
|
2012-11-01 03:46:00 +08:00
|
|
|
static void __tun_detach(struct tun_file *tfile, bool clean)
|
|
|
|
{
|
|
|
|
struct tun_file *ntfile;
|
|
|
|
struct tun_struct *tun;
|
|
|
|
|
2013-01-12 00:59:32 +08:00
|
|
|
tun = rtnl_dereference(tfile->tun);
|
|
|
|
|
2017-09-23 04:49:14 +08:00
|
|
|
if (tun && clean) {
|
2018-09-29 05:51:47 +08:00
|
|
|
tun_napi_disable(tfile);
|
|
|
|
tun_napi_del(tfile);
|
2017-09-23 04:49:14 +08:00
|
|
|
}
|
|
|
|
|
2013-01-28 09:05:19 +08:00
|
|
|
if (tun && !tfile->detached) {
|
2012-11-01 03:46:00 +08:00
|
|
|
u16 index = tfile->queue_index;
|
|
|
|
BUG_ON(index >= tun->numqueues);
|
|
|
|
|
|
|
|
rcu_assign_pointer(tun->tfiles[index],
|
|
|
|
tun->tfiles[tun->numqueues - 1]);
|
2013-01-12 00:59:32 +08:00
|
|
|
ntfile = rtnl_dereference(tun->tfiles[index]);
|
2012-11-01 03:46:00 +08:00
|
|
|
ntfile->queue_index = index;
|
2019-05-09 11:20:18 +08:00
|
|
|
rcu_assign_pointer(tun->tfiles[tun->numqueues - 1],
|
|
|
|
NULL);
|
2012-11-01 03:46:00 +08:00
|
|
|
|
|
|
|
--tun->numqueues;
|
2013-01-28 09:05:19 +08:00
|
|
|
if (clean) {
|
2014-03-24 02:32:32 +08:00
|
|
|
RCU_INIT_POINTER(tfile->tun, NULL);
|
2012-12-14 07:53:30 +08:00
|
|
|
sock_put(&tfile->sk);
|
2013-01-28 09:05:19 +08:00
|
|
|
} else
|
2012-12-14 07:53:30 +08:00
|
|
|
tun_disable_queue(tun, tfile);
|
2012-11-01 03:46:00 +08:00
|
|
|
|
|
|
|
synchronize_net();
|
2012-11-01 03:46:02 +08:00
|
|
|
tun_flow_delete_by_queue(tun, tun->numqueues + 1);
|
2012-11-01 03:46:00 +08:00
|
|
|
/* Drop read queue */
|
2013-09-05 17:53:59 +08:00
|
|
|
tun_queue_purge(tfile);
|
2012-11-01 03:46:00 +08:00
|
|
|
tun_set_real_num_queues(tun);
|
2013-01-12 00:59:34 +08:00
|
|
|
} else if (tfile->detached && clean) {
|
2012-12-14 07:53:30 +08:00
|
|
|
tun = tun_enable_queue(tfile);
|
2013-01-12 00:59:34 +08:00
|
|
|
sock_put(&tfile->sk);
|
|
|
|
}
|
2012-11-01 03:46:00 +08:00
|
|
|
|
|
|
|
if (clean) {
|
2013-01-28 08:38:02 +08:00
|
|
|
if (tun && tun->numqueues == 0 && tun->numdisabled == 0) {
|
|
|
|
netif_carrier_off(tun->dev);
|
|
|
|
|
2014-11-19 21:17:31 +08:00
|
|
|
if (!(tun->flags & IFF_PERSIST) &&
|
2013-01-28 08:38:02 +08:00
|
|
|
tun->dev->reg_state == NETREG_REGISTERED)
|
2012-12-14 07:53:30 +08:00
|
|
|
unregister_netdevice(tun->dev);
|
2013-01-28 08:38:02 +08:00
|
|
|
}
|
2018-05-11 10:49:25 +08:00
|
|
|
if (tun)
|
|
|
|
xdp_rxq_info_unreg(&tfile->xdp_rxq);
|
2018-05-16 20:39:33 +08:00
|
|
|
ptr_ring_cleanup(&tfile->tx_ring, tun_ptr_free);
|
2015-05-09 10:07:08 +08:00
|
|
|
sock_put(&tfile->sk);
|
2012-11-01 03:46:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_detach(struct tun_file *tfile, bool clean)
|
|
|
|
{
|
2018-04-10 22:28:56 +08:00
|
|
|
struct tun_struct *tun;
|
|
|
|
struct net_device *dev;
|
|
|
|
|
2012-11-01 03:46:00 +08:00
|
|
|
rtnl_lock();
|
2018-04-10 22:28:56 +08:00
|
|
|
tun = rtnl_dereference(tfile->tun);
|
|
|
|
dev = tun ? tun->dev : NULL;
|
2012-11-01 03:46:00 +08:00
|
|
|
__tun_detach(tfile, clean);
|
2018-04-10 22:28:56 +08:00
|
|
|
if (dev)
|
|
|
|
netdev_state_change(dev);
|
2012-11-01 03:46:00 +08:00
|
|
|
rtnl_unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_detach_all(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
2012-12-14 07:53:30 +08:00
|
|
|
struct tun_file *tfile, *tmp;
|
2012-11-01 03:46:00 +08:00
|
|
|
int i, n = tun->numqueues;
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
2013-01-12 00:59:32 +08:00
|
|
|
tfile = rtnl_dereference(tun->tfiles[i]);
|
2012-11-01 03:46:00 +08:00
|
|
|
BUG_ON(!tfile);
|
2018-09-29 05:51:47 +08:00
|
|
|
tun_napi_disable(tfile);
|
2016-05-19 13:36:51 +08:00
|
|
|
tfile->socket.sk->sk_shutdown = RCV_SHUTDOWN;
|
2014-05-17 06:11:48 +08:00
|
|
|
tfile->socket.sk->sk_data_ready(tfile->socket.sk);
|
2014-03-24 02:32:32 +08:00
|
|
|
RCU_INIT_POINTER(tfile->tun, NULL);
|
2012-11-01 03:46:00 +08:00
|
|
|
--tun->numqueues;
|
|
|
|
}
|
2013-01-28 09:05:19 +08:00
|
|
|
list_for_each_entry(tfile, &tun->disabled, next) {
|
2016-05-19 13:36:51 +08:00
|
|
|
tfile->socket.sk->sk_shutdown = RCV_SHUTDOWN;
|
2014-05-17 06:11:48 +08:00
|
|
|
tfile->socket.sk->sk_data_ready(tfile->socket.sk);
|
2014-03-24 02:32:32 +08:00
|
|
|
RCU_INIT_POINTER(tfile->tun, NULL);
|
2013-01-28 09:05:19 +08:00
|
|
|
}
|
2012-11-01 03:46:00 +08:00
|
|
|
BUG_ON(tun->numqueues != 0);
|
|
|
|
|
|
|
|
synchronize_net();
|
|
|
|
for (i = 0; i < n; i++) {
|
2013-01-12 00:59:32 +08:00
|
|
|
tfile = rtnl_dereference(tun->tfiles[i]);
|
2018-09-29 05:51:47 +08:00
|
|
|
tun_napi_del(tfile);
|
2012-11-01 03:46:00 +08:00
|
|
|
/* Drop read queue */
|
2013-09-05 17:53:59 +08:00
|
|
|
tun_queue_purge(tfile);
|
2018-05-11 10:49:25 +08:00
|
|
|
xdp_rxq_info_unreg(&tfile->xdp_rxq);
|
2012-11-01 03:46:00 +08:00
|
|
|
sock_put(&tfile->sk);
|
|
|
|
}
|
2012-12-14 07:53:30 +08:00
|
|
|
list_for_each_entry_safe(tfile, tmp, &tun->disabled, next) {
|
|
|
|
tun_enable_queue(tfile);
|
2013-09-05 17:53:59 +08:00
|
|
|
tun_queue_purge(tfile);
|
2018-05-11 10:49:25 +08:00
|
|
|
xdp_rxq_info_unreg(&tfile->xdp_rxq);
|
2012-12-14 07:53:30 +08:00
|
|
|
sock_put(&tfile->sk);
|
|
|
|
}
|
|
|
|
BUG_ON(tun->numdisabled != 0);
|
2013-01-12 00:59:34 +08:00
|
|
|
|
2014-11-19 21:17:31 +08:00
|
|
|
if (tun->flags & IFF_PERSIST)
|
2013-01-12 00:59:34 +08:00
|
|
|
module_put(THIS_MODULE);
|
2012-11-01 03:46:00 +08:00
|
|
|
}
|
|
|
|
|
2017-09-23 04:49:14 +08:00
|
|
|
static int tun_attach(struct tun_struct *tun, struct file *file,
|
2019-09-10 18:56:57 +08:00
|
|
|
bool skip_filter, bool napi, bool napi_frags,
|
|
|
|
bool publish_tun)
|
2009-01-20 18:57:48 +08:00
|
|
|
{
|
2009-01-20 19:00:40 +08:00
|
|
|
struct tun_file *tfile = file->private_data;
|
2016-06-30 14:45:36 +08:00
|
|
|
struct net_device *dev = tun->dev;
|
2009-01-20 19:02:28 +08:00
|
|
|
int err;
|
2009-01-20 18:57:48 +08:00
|
|
|
|
2013-01-14 15:12:19 +08:00
|
|
|
err = security_tun_dev_attach(tfile->socket.sk, tun->security);
|
|
|
|
if (err < 0)
|
|
|
|
goto out;
|
|
|
|
|
2009-01-20 19:02:28 +08:00
|
|
|
err = -EINVAL;
|
2013-01-28 09:05:19 +08:00
|
|
|
if (rtnl_dereference(tfile->tun) && !tfile->detached)
|
2009-01-20 19:02:28 +08:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = -EBUSY;
|
2014-11-19 21:17:31 +08:00
|
|
|
if (!(tun->flags & IFF_MULTI_QUEUE) && tun->numqueues == 1)
|
2012-11-01 03:46:00 +08:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = -E2BIG;
|
2012-12-14 07:53:30 +08:00
|
|
|
if (!tfile->detached &&
|
|
|
|
tun->numqueues + tun->numdisabled == MAX_TAP_QUEUES)
|
2009-01-20 19:02:28 +08:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = 0;
|
2012-11-01 03:45:57 +08:00
|
|
|
|
2013-12-06 12:42:58 +08:00
|
|
|
/* Re-attach the filter to persist device */
|
tun: Allow to skip filter on attach
There's a small problem with sk-filters on tun devices. Consider
an application doing this sequence of steps:
fd = open("/dev/net/tun");
ioctl(fd, TUNSETIFF, { .ifr_name = "tun0" });
ioctl(fd, TUNATTACHFILTER, &my_filter);
ioctl(fd, TUNSETPERSIST, 1);
close(fd);
At that point the tun0 will remain in the system and will keep in
mind that there should be a socket filter at address '&my_filter'.
If after that we do
fd = open("/dev/net/tun");
ioctl(fd, TUNSETIFF, { .ifr_name = "tun0" });
we most likely receive the -EFAULT error, since tun_attach() would
try to connect the filter back. But (!) if we provide a filter at
address &my_filter, then tun0 will be created and the "new" filter
would be attached, but application may not know about that.
This may create certain problems to anyone using tun-s, but it's
critical problem for c/r -- if we meet a persistent tun device
with a filter in mind, we will not be able to attach to it to dump
its state (flags, owner, address, vnethdr size, etc.).
The proposal is to allow to attach to tun device (with TUNSETIFF)
w/o attaching the filter to the tun-file's socket. After this
attach app may e.g clean the device by dropping the filter, it
doesn't want to have one, or (in case of c/r) get information
about the device with tun ioctls.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-08-21 18:32:21 +08:00
|
|
|
if (!skip_filter && (tun->filter_attached == true)) {
|
2016-04-05 23:10:16 +08:00
|
|
|
lock_sock(tfile->socket.sk);
|
|
|
|
err = sk_attach_filter(&tun->fprog, tfile->socket.sk);
|
|
|
|
release_sock(tfile->socket.sk);
|
2012-11-01 03:45:57 +08:00
|
|
|
if (!err)
|
|
|
|
goto out;
|
|
|
|
}
|
2016-06-30 14:45:36 +08:00
|
|
|
|
|
|
|
if (!tfile->detached &&
|
2018-05-11 10:49:25 +08:00
|
|
|
ptr_ring_resize(&tfile->tx_ring, dev->tx_queue_len,
|
|
|
|
GFP_KERNEL, tun_ptr_free)) {
|
2016-06-30 14:45:36 +08:00
|
|
|
err = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2012-11-01 03:46:00 +08:00
|
|
|
tfile->queue_index = tun->numqueues;
|
2016-05-19 13:36:51 +08:00
|
|
|
tfile->socket.sk->sk_shutdown &= ~RCV_SHUTDOWN;
|
2018-01-03 18:25:59 +08:00
|
|
|
|
|
|
|
if (tfile->detached) {
|
|
|
|
/* Re-attach detached tfile, updating XDP queue_index */
|
|
|
|
WARN_ON(!xdp_rxq_info_is_reg(&tfile->xdp_rxq));
|
|
|
|
|
|
|
|
if (tfile->xdp_rxq.queue_index != tfile->queue_index)
|
|
|
|
tfile->xdp_rxq.queue_index = tfile->queue_index;
|
|
|
|
} else {
|
|
|
|
/* Setup XDP RX-queue info, for new tfile getting attached */
|
|
|
|
err = xdp_rxq_info_reg(&tfile->xdp_rxq,
|
2020-12-01 02:52:01 +08:00
|
|
|
tun->dev, tfile->queue_index, 0);
|
2018-01-03 18:25:59 +08:00
|
|
|
if (err < 0)
|
|
|
|
goto out;
|
xdp: rhashtable with allocator ID to pointer mapping
Use the IDA infrastructure for getting a cyclic increasing ID number,
that is used for keeping track of each registered allocator per
RX-queue xdp_rxq_info. Instead of using the IDR infrastructure, which
uses a radix tree, use a dynamic rhashtable, for creating ID to
pointer lookup table, because this is faster.
The problem that is being solved here is that, the xdp_rxq_info
pointer (stored in xdp_buff) cannot be used directly, as the
guaranteed lifetime is too short. The info is needed on a
(potentially) remote CPU during DMA-TX completion time . In an
xdp_frame the xdp_mem_info is stored, when it got converted from an
xdp_buff, which is sufficient for the simple page refcnt based recycle
schemes.
For more advanced allocators there is a need to store a pointer to the
registered allocator. Thus, there is a need to guard the lifetime or
validity of the allocator pointer, which is done through this
rhashtable ID map to pointer. The removal and validity of of the
allocator and helper struct xdp_mem_allocator is guarded by RCU. The
allocator will be created by the driver, and registered with
xdp_rxq_info_reg_mem_model().
It is up-to debate who is responsible for freeing the allocator
pointer or invoking the allocator destructor function. In any case,
this must happen via RCU freeing.
Use the IDA infrastructure for getting a cyclic increasing ID number,
that is used for keeping track of each registered allocator per
RX-queue xdp_rxq_info.
V4: Per req of Jason Wang
- Use xdp_rxq_info_reg_mem_model() in all drivers implementing
XDP_REDIRECT, even-though it's not strictly necessary when
allocator==NULL for type MEM_TYPE_PAGE_SHARED (given it's zero).
V6: Per req of Alex Duyck
- Introduce rhashtable_lookup() call in later patch
V8: Address sparse should be static warnings (from kbuild test robot)
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2018-04-17 22:46:12 +08:00
|
|
|
err = xdp_rxq_info_reg_mem_model(&tfile->xdp_rxq,
|
|
|
|
MEM_TYPE_PAGE_SHARED, NULL);
|
|
|
|
if (err < 0) {
|
|
|
|
xdp_rxq_info_unreg(&tfile->xdp_rxq);
|
|
|
|
goto out;
|
|
|
|
}
|
2018-01-03 18:25:59 +08:00
|
|
|
err = 0;
|
|
|
|
}
|
|
|
|
|
2017-09-23 04:49:14 +08:00
|
|
|
if (tfile->detached) {
|
2012-12-14 07:53:30 +08:00
|
|
|
tun_enable_queue(tfile);
|
2017-09-23 04:49:14 +08:00
|
|
|
} else {
|
2012-12-14 07:53:30 +08:00
|
|
|
sock_hold(&tfile->sk);
|
2018-09-29 05:51:49 +08:00
|
|
|
tun_napi_init(tun, tfile, napi, napi_frags);
|
2017-09-23 04:49:14 +08:00
|
|
|
}
|
2012-12-14 07:53:30 +08:00
|
|
|
|
2018-09-12 11:16:59 +08:00
|
|
|
if (rtnl_dereference(tun->xdp_prog))
|
|
|
|
sock_set_flag(&tfile->sk, SOCK_XDP);
|
|
|
|
|
2012-11-01 03:46:00 +08:00
|
|
|
/* device is allowed to go away first, so no need to hold extra
|
|
|
|
* refcnt.
|
|
|
|
*/
|
|
|
|
|
2019-01-08 05:38:38 +08:00
|
|
|
/* Publish tfile->tun and tun->tfiles only after we've fully
|
|
|
|
* initialized tfile; otherwise we risk using half-initialized
|
|
|
|
* object.
|
|
|
|
*/
|
2019-09-10 18:56:57 +08:00
|
|
|
if (publish_tun)
|
|
|
|
rcu_assign_pointer(tfile->tun, tun);
|
2019-01-08 05:38:38 +08:00
|
|
|
rcu_assign_pointer(tun->tfiles[tun->numqueues], tfile);
|
|
|
|
tun->numqueues++;
|
2019-01-30 11:50:13 +08:00
|
|
|
tun_set_real_num_queues(tun);
|
2012-11-01 03:46:00 +08:00
|
|
|
out:
|
|
|
|
return err;
|
2009-01-20 19:00:40 +08:00
|
|
|
}
|
|
|
|
|
2017-09-23 22:36:52 +08:00
|
|
|
static struct tun_struct *tun_get(struct tun_file *tfile)
|
2009-01-20 19:00:40 +08:00
|
|
|
{
|
2012-11-01 03:45:58 +08:00
|
|
|
struct tun_struct *tun;
|
2009-01-20 19:07:17 +08:00
|
|
|
|
2012-11-01 03:45:58 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
tun = rcu_dereference(tfile->tun);
|
|
|
|
if (tun)
|
|
|
|
dev_hold(tun->dev);
|
|
|
|
rcu_read_unlock();
|
2009-01-20 19:07:17 +08:00
|
|
|
|
|
|
|
return tun;
|
2009-01-20 19:00:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_put(struct tun_struct *tun)
|
|
|
|
{
|
2012-11-01 03:45:58 +08:00
|
|
|
dev_put(tun->dev);
|
2009-01-20 19:00:40 +08:00
|
|
|
}
|
|
|
|
|
2011-03-02 15:18:10 +08:00
|
|
|
/* TAP filtering */
|
2008-07-15 13:18:19 +08:00
|
|
|
static void addr_hash_set(u32 *mask, const u8 *addr)
|
|
|
|
{
|
|
|
|
int n = ether_crc(ETH_ALEN, addr) >> 26;
|
|
|
|
mask[n >> 5] |= (1 << (n & 31));
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int addr_hash_test(const u32 *mask, const u8 *addr)
|
|
|
|
{
|
|
|
|
int n = ether_crc(ETH_ALEN, addr) >> 26;
|
|
|
|
return mask[n >> 5] & (1 << (n & 31));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int update_filter(struct tap_filter *filter, void __user *arg)
|
|
|
|
{
|
|
|
|
struct { u8 u[ETH_ALEN]; } *addr;
|
|
|
|
struct tun_filter uf;
|
|
|
|
int err, alen, n, nexact;
|
|
|
|
|
|
|
|
if (copy_from_user(&uf, arg, sizeof(uf)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (!uf.count) {
|
|
|
|
/* Disabled */
|
|
|
|
filter->count = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
alen = ETH_ALEN * uf.count;
|
2016-08-20 14:54:15 +08:00
|
|
|
addr = memdup_user(arg + sizeof(uf), alen);
|
|
|
|
if (IS_ERR(addr))
|
|
|
|
return PTR_ERR(addr);
|
2008-07-15 13:18:19 +08:00
|
|
|
|
|
|
|
/* The filter is updated without holding any locks. Which is
|
|
|
|
* perfectly safe. We disable it first and in the worst
|
|
|
|
* case we'll accept a few undesired packets. */
|
|
|
|
filter->count = 0;
|
|
|
|
wmb();
|
|
|
|
|
|
|
|
/* Use first set of addresses as an exact filter */
|
|
|
|
for (n = 0; n < uf.count && n < FLT_EXACT_COUNT; n++)
|
|
|
|
memcpy(filter->addr[n], addr[n].u, ETH_ALEN);
|
|
|
|
|
|
|
|
nexact = n;
|
|
|
|
|
2009-02-09 09:49:17 +08:00
|
|
|
/* Remaining multicast addresses are hashed,
|
|
|
|
* unicast will leave the filter disabled. */
|
2008-07-15 13:18:19 +08:00
|
|
|
memset(filter->mask, 0, sizeof(filter->mask));
|
2009-02-09 09:49:17 +08:00
|
|
|
for (; n < uf.count; n++) {
|
|
|
|
if (!is_multicast_ether_addr(addr[n].u)) {
|
|
|
|
err = 0; /* no filter */
|
2016-08-20 15:00:34 +08:00
|
|
|
goto free_addr;
|
2009-02-09 09:49:17 +08:00
|
|
|
}
|
2008-07-15 13:18:19 +08:00
|
|
|
addr_hash_set(filter->mask, addr[n].u);
|
2009-02-09 09:49:17 +08:00
|
|
|
}
|
2008-07-15 13:18:19 +08:00
|
|
|
|
|
|
|
/* For ALLMULTI just set the mask to all ones.
|
|
|
|
* This overrides the mask populated above. */
|
|
|
|
if ((uf.flags & TUN_FLT_ALLMULTI))
|
|
|
|
memset(filter->mask, ~0, sizeof(filter->mask));
|
|
|
|
|
|
|
|
/* Now enable the filter */
|
|
|
|
wmb();
|
|
|
|
filter->count = nexact;
|
|
|
|
|
|
|
|
/* Return the number of exact filters */
|
|
|
|
err = nexact;
|
2016-08-20 15:00:34 +08:00
|
|
|
free_addr:
|
2008-07-15 13:18:19 +08:00
|
|
|
kfree(addr);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns: 0 - drop, !=0 - accept */
|
|
|
|
static int run_filter(struct tap_filter *filter, const struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
/* Cannot use eth_hdr(skb) here because skb_mac_hdr() is incorrect
|
|
|
|
* at this point. */
|
|
|
|
struct ethhdr *eh = (struct ethhdr *) skb->data;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Exact match */
|
|
|
|
for (i = 0; i < filter->count; i++)
|
drivers/net: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-10 01:17:46 +08:00
|
|
|
if (ether_addr_equal(eh->h_dest, filter->addr[i]))
|
2008-07-15 13:18:19 +08:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Inexact match (multicast only) */
|
|
|
|
if (is_multicast_ether_addr(eh->h_dest))
|
|
|
|
return addr_hash_test(filter->mask, eh->h_dest);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Checks whether the packet is accepted or not.
|
|
|
|
* Returns: 0 - drop, !=0 - accept
|
|
|
|
*/
|
|
|
|
static int check_filter(struct tap_filter *filter, const struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
if (!filter->count)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return run_filter(filter, skb);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Network device part of the driver */
|
|
|
|
|
2006-09-14 02:30:00 +08:00
|
|
|
static const struct ethtool_ops tun_ethtool_ops;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-01-20 19:07:17 +08:00
|
|
|
/* Net device detach from fd. */
|
|
|
|
static void tun_net_uninit(struct net_device *dev)
|
|
|
|
{
|
2012-11-01 03:46:00 +08:00
|
|
|
tun_detach_all(dev);
|
2009-01-20 19:07:17 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Net device open. */
|
|
|
|
static int tun_net_open(struct net_device *dev)
|
|
|
|
{
|
2012-11-01 03:46:00 +08:00
|
|
|
netif_tx_start_all_queues(dev);
|
2017-03-13 07:00:26 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Net device close. */
|
|
|
|
static int tun_net_close(struct net_device *dev)
|
|
|
|
{
|
2012-11-01 03:46:00 +08:00
|
|
|
netif_tx_stop_all_queues(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Net device start xmit */
|
2017-12-04 17:31:23 +08:00
|
|
|
static void tun_automq_xmit(struct tun_struct *tun, struct sk_buff *skb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2016-04-26 11:13:42 +08:00
|
|
|
#ifdef CONFIG_RPS
|
2019-03-22 23:56:38 +08:00
|
|
|
if (tun->numqueues == 1 && static_branch_unlikely(&rps_needed)) {
|
2013-12-22 18:54:32 +08:00
|
|
|
/* Select queue was not called for the skbuff, so we extract the
|
|
|
|
* RPS hash and save it into the flow_table here.
|
|
|
|
*/
|
2018-10-09 10:32:04 +08:00
|
|
|
struct tun_flow_entry *e;
|
2013-12-22 18:54:32 +08:00
|
|
|
__u32 rxhash;
|
|
|
|
|
2017-06-06 14:09:49 +08:00
|
|
|
rxhash = __skb_get_hash_symmetric(skb);
|
2018-10-09 10:32:04 +08:00
|
|
|
e = tun_flow_find(&tun->flows[tun_hashfn(rxhash)], rxhash);
|
|
|
|
if (e)
|
|
|
|
tun_flow_save_rps_rxhash(e, rxhash);
|
2013-12-22 18:54:32 +08:00
|
|
|
}
|
2016-04-26 11:13:42 +08:00
|
|
|
#endif
|
2017-12-04 17:31:23 +08:00
|
|
|
}
|
|
|
|
|
2018-01-16 16:31:02 +08:00
|
|
|
static unsigned int run_ebpf_filter(struct tun_struct *tun,
|
|
|
|
struct sk_buff *skb,
|
|
|
|
int len)
|
|
|
|
{
|
|
|
|
struct tun_prog *prog = rcu_dereference(tun->filter_prog);
|
|
|
|
|
|
|
|
if (prog)
|
|
|
|
len = bpf_prog_run_clear_cb(prog->prog, skb);
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2017-12-04 17:31:23 +08:00
|
|
|
/* Net device start xmit */
|
|
|
|
static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
int txq = skb->queue_mapping;
|
|
|
|
struct tun_file *tfile;
|
2018-01-16 16:31:02 +08:00
|
|
|
int len = skb->len;
|
2017-12-04 17:31:23 +08:00
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
tfile = rcu_dereference(tun->tfiles[txq]);
|
|
|
|
|
|
|
|
/* Drop packet if interface is not attached */
|
2019-05-09 11:20:18 +08:00
|
|
|
if (!tfile)
|
2017-12-04 17:31:23 +08:00
|
|
|
goto drop;
|
|
|
|
|
|
|
|
if (!rcu_dereference(tun->steering_prog))
|
|
|
|
tun_automq_xmit(tun, skb);
|
2013-12-22 18:54:32 +08:00
|
|
|
|
2020-03-05 00:24:14 +08:00
|
|
|
netif_info(tun, tx_queued, tun->dev, "%s %d\n", __func__, skb->len);
|
2012-11-01 03:45:58 +08:00
|
|
|
|
2008-07-15 13:18:19 +08:00
|
|
|
/* Drop if the filter does not like it.
|
|
|
|
* This is a noop if the filter is disabled.
|
|
|
|
* Filter can be enabled only for the TAP devices. */
|
|
|
|
if (!check_filter(&tun->txflt, skb))
|
|
|
|
goto drop;
|
|
|
|
|
2012-11-01 03:45:57 +08:00
|
|
|
if (tfile->socket.sk->sk_filter &&
|
|
|
|
sk_filter(tfile->socket.sk, skb))
|
2010-02-14 09:01:10 +08:00
|
|
|
goto drop;
|
|
|
|
|
2018-01-16 16:31:02 +08:00
|
|
|
len = run_ebpf_filter(tun, skb, len);
|
2018-04-18 04:46:38 +08:00
|
|
|
if (len == 0 || pskb_trim(skb, len))
|
2018-01-16 16:31:02 +08:00
|
|
|
goto drop;
|
|
|
|
|
sock: enable MSG_ZEROCOPY
Prepare the datapath for refcounted ubuf_info. Clone ubuf_info with
skb_zerocopy_clone() wherever needed due to skb split, merge, resize
or clone.
Split skb_orphan_frags into two variants. The split, merge, .. paths
support reference counted zerocopy buffers, so do not do a deep copy.
Add skb_orphan_frags_rx for paths that may loop packets to receive
sockets. That is not allowed, as it may cause unbounded latency.
Deep copy all zerocopy copy buffers, ref-counted or not, in this path.
The exact locations to modify were chosen by exhaustively searching
through all code that might modify skb_frag references and/or the
the SKBTX_DEV_ZEROCOPY tx_flags bit.
The changes err on the safe side, in two ways.
(1) legacy ubuf_info paths virtio and tap are not modified. They keep
a 1:1 ubuf_info to sk_buff relationship. Calls to skb_orphan_frags
still call skb_copy_ubufs and thus copy frags in this case.
(2) not all copies deep in the stack are addressed yet. skb_shift,
skb_split and skb_try_coalesce can be refined to avoid copying.
These are not in the hot path and this patch is hairy enough as
is, so that is left for future refinement.
Signed-off-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-08-04 04:29:41 +08:00
|
|
|
if (unlikely(skb_orphan_frags_rx(skb, GFP_ATOMIC)))
|
2013-09-05 17:54:00 +08:00
|
|
|
goto drop;
|
|
|
|
|
2016-08-24 06:22:33 +08:00
|
|
|
skb_tx_timestamp(skb);
|
2013-07-20 01:40:10 +08:00
|
|
|
|
tun: orphan an skb on tx
The following situation was observed in the field:
tap1 sends packets, tap2 does not consume them, as a result
tap1 can not be closed. This happens because
tun/tap devices can hang on to skbs undefinitely.
As noted by Herbert, possible solutions include a timeout followed by a
copy/change of ownership of the skb, or always copying/changing
ownership if we're going into a hostile device.
This patch implements the second approach.
Note: one issue still remaining is that since skbs
keep reference to tun socket and tun socket has a
reference to tun device, we won't flush backlog,
instead simply waiting for all skbs to get transmitted.
At least this is not user-triggerable, and
this was not reported in practice, my assumption is
other devices besides tap complete an skb
within finite time after it has been queued.
A possible solution for the second issue
would not to have socket reference the device,
instead, implement dev->destructor for tun, and
wait for all skbs to complete there, but this
needs some thought, probably too risky for 2.6.34.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Tested-by: Yan Vugenfirer <yvugenfi@redhat.com>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2010-04-13 12:59:44 +08:00
|
|
|
/* Orphan the skb - required as we might hang on to it
|
2013-09-05 17:54:00 +08:00
|
|
|
* for indefinite time.
|
|
|
|
*/
|
tun: orphan an skb on tx
The following situation was observed in the field:
tap1 sends packets, tap2 does not consume them, as a result
tap1 can not be closed. This happens because
tun/tap devices can hang on to skbs undefinitely.
As noted by Herbert, possible solutions include a timeout followed by a
copy/change of ownership of the skb, or always copying/changing
ownership if we're going into a hostile device.
This patch implements the second approach.
Note: one issue still remaining is that since skbs
keep reference to tun socket and tun socket has a
reference to tun device, we won't flush backlog,
instead simply waiting for all skbs to get transmitted.
At least this is not user-triggerable, and
this was not reported in practice, my assumption is
other devices besides tap complete an skb
within finite time after it has been queued.
A possible solution for the second issue
would not to have socket reference the device,
instead, implement dev->destructor for tun, and
wait for all skbs to complete there, but this
needs some thought, probably too risky for 2.6.34.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Tested-by: Yan Vugenfirer <yvugenfi@redhat.com>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2010-04-13 12:59:44 +08:00
|
|
|
skb_orphan(skb);
|
|
|
|
|
2019-09-30 02:54:03 +08:00
|
|
|
nf_reset_ct(skb);
|
2013-03-06 19:02:37 +08:00
|
|
|
|
2018-01-04 11:14:27 +08:00
|
|
|
if (ptr_ring_produce(&tfile->tx_ring, skb))
|
2016-06-30 14:45:36 +08:00
|
|
|
goto drop;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Notify and wake up reader process */
|
2012-11-01 03:45:57 +08:00
|
|
|
if (tfile->flags & TUN_FASYNC)
|
|
|
|
kill_fasync(&tfile->fasync, SIGIO, POLL_IN);
|
2014-05-17 06:11:48 +08:00
|
|
|
tfile->socket.sk->sk_data_ready(tfile->socket.sk);
|
2012-11-01 03:45:58 +08:00
|
|
|
|
|
|
|
rcu_read_unlock();
|
2009-06-23 14:03:08 +08:00
|
|
|
return NETDEV_TX_OK;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
drop:
|
2020-11-08 04:50:56 +08:00
|
|
|
atomic_long_inc(&dev->tx_dropped);
|
2012-11-01 17:16:32 +08:00
|
|
|
skb_tx_error(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree_skb(skb);
|
2012-11-01 03:45:58 +08:00
|
|
|
rcu_read_unlock();
|
2014-11-18 13:20:41 +08:00
|
|
|
return NET_XMIT_DROP;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-07-15 13:18:19 +08:00
|
|
|
static void tun_net_mclist(struct net_device *dev)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-15 13:18:19 +08:00
|
|
|
/*
|
|
|
|
* This callback is supposed to deal with mc filter in
|
|
|
|
* _rx_ path and has nothing to do with the _tx_ path.
|
|
|
|
* In rx path we always accept everything userspace gives us.
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-11-15 23:29:55 +08:00
|
|
|
static netdev_features_t tun_net_fix_features(struct net_device *dev,
|
|
|
|
netdev_features_t features)
|
2011-04-19 14:13:10 +08:00
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
|
|
|
return (features & tun->set_features) | (features & ~TUN_USER_FEATURES);
|
|
|
|
}
|
2016-02-26 17:45:40 +08:00
|
|
|
|
|
|
|
static void tun_set_headroom(struct net_device *dev, int new_hr)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
|
|
|
if (new_hr < NET_SKB_PAD)
|
|
|
|
new_hr = NET_SKB_PAD;
|
|
|
|
|
|
|
|
tun->align = new_hr;
|
|
|
|
}
|
|
|
|
|
2017-01-07 11:12:52 +08:00
|
|
|
static void
|
2016-04-13 16:52:20 +08:00
|
|
|
tun_net_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
2020-11-08 04:50:56 +08:00
|
|
|
dev_get_tstats64(dev, stats);
|
2016-04-13 16:52:20 +08:00
|
|
|
|
2020-11-08 04:50:56 +08:00
|
|
|
stats->rx_frame_errors +=
|
|
|
|
(unsigned long)atomic_long_read(&tun->rx_frame_errors);
|
2016-04-13 16:52:20 +08:00
|
|
|
}
|
|
|
|
|
2017-08-11 19:41:18 +08:00
|
|
|
static int tun_xdp_set(struct net_device *dev, struct bpf_prog *prog,
|
|
|
|
struct netlink_ext_ack *extack)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
2018-09-12 11:16:59 +08:00
|
|
|
struct tun_file *tfile;
|
2017-08-11 19:41:18 +08:00
|
|
|
struct bpf_prog *old_prog;
|
2018-09-12 11:16:59 +08:00
|
|
|
int i;
|
2017-08-11 19:41:18 +08:00
|
|
|
|
|
|
|
old_prog = rtnl_dereference(tun->xdp_prog);
|
|
|
|
rcu_assign_pointer(tun->xdp_prog, prog);
|
|
|
|
if (old_prog)
|
|
|
|
bpf_prog_put(old_prog);
|
|
|
|
|
2018-09-12 11:16:59 +08:00
|
|
|
for (i = 0; i < tun->numqueues; i++) {
|
|
|
|
tfile = rtnl_dereference(tun->tfiles[i]);
|
|
|
|
if (prog)
|
|
|
|
sock_set_flag(&tfile->sk, SOCK_XDP);
|
|
|
|
else
|
|
|
|
sock_reset_flag(&tfile->sk, SOCK_XDP);
|
|
|
|
}
|
|
|
|
list_for_each_entry(tfile, &tun->disabled, next) {
|
|
|
|
if (prog)
|
|
|
|
sock_set_flag(&tfile->sk, SOCK_XDP);
|
|
|
|
else
|
|
|
|
sock_reset_flag(&tfile->sk, SOCK_XDP);
|
|
|
|
}
|
|
|
|
|
2017-08-11 19:41:18 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-11-04 04:56:16 +08:00
|
|
|
static int tun_xdp(struct net_device *dev, struct netdev_bpf *xdp)
|
2017-08-11 19:41:18 +08:00
|
|
|
{
|
|
|
|
switch (xdp->command) {
|
|
|
|
case XDP_SETUP_PROG:
|
|
|
|
return tun_xdp_set(dev, xdp->prog, xdp->extack);
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-29 02:12:56 +08:00
|
|
|
static int tun_net_change_carrier(struct net_device *dev, bool new_carrier)
|
|
|
|
{
|
|
|
|
if (new_carrier) {
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
|
|
|
if (!tun->numqueues)
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
netif_carrier_on(dev);
|
|
|
|
} else {
|
|
|
|
netif_carrier_off(dev);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-11-20 14:10:37 +08:00
|
|
|
static const struct net_device_ops tun_netdev_ops = {
|
2009-01-20 19:07:17 +08:00
|
|
|
.ndo_uninit = tun_net_uninit,
|
2008-11-20 14:10:37 +08:00
|
|
|
.ndo_open = tun_net_open,
|
|
|
|
.ndo_stop = tun_net_close,
|
2008-11-21 12:14:53 +08:00
|
|
|
.ndo_start_xmit = tun_net_xmit,
|
2011-04-19 14:13:10 +08:00
|
|
|
.ndo_fix_features = tun_net_fix_features,
|
2012-11-01 03:46:00 +08:00
|
|
|
.ndo_select_queue = tun_select_queue,
|
2016-02-26 17:45:40 +08:00
|
|
|
.ndo_set_rx_headroom = tun_set_headroom,
|
2016-04-13 16:52:20 +08:00
|
|
|
.ndo_get_stats64 = tun_net_get_stats64,
|
2018-11-29 02:12:56 +08:00
|
|
|
.ndo_change_carrier = tun_net_change_carrier,
|
2008-11-20 14:10:37 +08:00
|
|
|
};
|
|
|
|
|
2018-05-31 17:00:03 +08:00
|
|
|
static void __tun_xdp_flush_tfile(struct tun_file *tfile)
|
|
|
|
{
|
|
|
|
/* Notify and wake up reader process */
|
|
|
|
if (tfile->flags & TUN_FASYNC)
|
|
|
|
kill_fasync(&tfile->fasync, SIGIO, POLL_IN);
|
|
|
|
tfile->socket.sk->sk_data_ready(tfile->socket.sk);
|
|
|
|
}
|
|
|
|
|
2018-05-31 16:59:47 +08:00
|
|
|
static int tun_xdp_xmit(struct net_device *dev, int n,
|
|
|
|
struct xdp_frame **frames, u32 flags)
|
2018-01-04 11:14:28 +08:00
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
struct tun_file *tfile;
|
|
|
|
u32 numqueues;
|
xdp: change ndo_xdp_xmit API to support bulking
This patch change the API for ndo_xdp_xmit to support bulking
xdp_frames.
When kernel is compiled with CONFIG_RETPOLINE, XDP sees a huge slowdown.
Most of the slowdown is caused by DMA API indirect function calls, but
also the net_device->ndo_xdp_xmit() call.
Benchmarked patch with CONFIG_RETPOLINE, using xdp_redirect_map with
single flow/core test (CPU E5-1650 v4 @ 3.60GHz), showed
performance improved:
for driver ixgbe: 6,042,682 pps -> 6,853,768 pps = +811,086 pps
for driver i40e : 6,187,169 pps -> 6,724,519 pps = +537,350 pps
With frames avail as a bulk inside the driver ndo_xdp_xmit call,
further optimizations are possible, like bulk DMA-mapping for TX.
Testing without CONFIG_RETPOLINE show the same performance for
physical NIC drivers.
The virtual NIC driver tun sees a huge performance boost, as it can
avoid doing per frame producer locking, but instead amortize the
locking cost over the bulk.
V2: Fix compile errors reported by kbuild test robot <lkp@intel.com>
V4: Isolated ndo, driver changes and callers.
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2018-05-24 22:46:12 +08:00
|
|
|
int drops = 0;
|
|
|
|
int cnt = n;
|
|
|
|
int i;
|
2018-01-04 11:14:28 +08:00
|
|
|
|
2018-05-31 17:00:03 +08:00
|
|
|
if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
|
2018-05-31 16:59:47 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2018-01-04 11:14:28 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
|
2019-05-09 11:20:18 +08:00
|
|
|
resample:
|
2018-01-04 11:14:28 +08:00
|
|
|
numqueues = READ_ONCE(tun->numqueues);
|
|
|
|
if (!numqueues) {
|
xdp: change ndo_xdp_xmit API to support bulking
This patch change the API for ndo_xdp_xmit to support bulking
xdp_frames.
When kernel is compiled with CONFIG_RETPOLINE, XDP sees a huge slowdown.
Most of the slowdown is caused by DMA API indirect function calls, but
also the net_device->ndo_xdp_xmit() call.
Benchmarked patch with CONFIG_RETPOLINE, using xdp_redirect_map with
single flow/core test (CPU E5-1650 v4 @ 3.60GHz), showed
performance improved:
for driver ixgbe: 6,042,682 pps -> 6,853,768 pps = +811,086 pps
for driver i40e : 6,187,169 pps -> 6,724,519 pps = +537,350 pps
With frames avail as a bulk inside the driver ndo_xdp_xmit call,
further optimizations are possible, like bulk DMA-mapping for TX.
Testing without CONFIG_RETPOLINE show the same performance for
physical NIC drivers.
The virtual NIC driver tun sees a huge performance boost, as it can
avoid doing per frame producer locking, but instead amortize the
locking cost over the bulk.
V2: Fix compile errors reported by kbuild test robot <lkp@intel.com>
V4: Isolated ndo, driver changes and callers.
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2018-05-24 22:46:12 +08:00
|
|
|
rcu_read_unlock();
|
|
|
|
return -ENXIO; /* Caller will free/return all frames */
|
2018-01-04 11:14:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
tfile = rcu_dereference(tun->tfiles[smp_processor_id() %
|
|
|
|
numqueues]);
|
2019-05-09 11:20:18 +08:00
|
|
|
if (unlikely(!tfile))
|
|
|
|
goto resample;
|
xdp: change ndo_xdp_xmit API to support bulking
This patch change the API for ndo_xdp_xmit to support bulking
xdp_frames.
When kernel is compiled with CONFIG_RETPOLINE, XDP sees a huge slowdown.
Most of the slowdown is caused by DMA API indirect function calls, but
also the net_device->ndo_xdp_xmit() call.
Benchmarked patch with CONFIG_RETPOLINE, using xdp_redirect_map with
single flow/core test (CPU E5-1650 v4 @ 3.60GHz), showed
performance improved:
for driver ixgbe: 6,042,682 pps -> 6,853,768 pps = +811,086 pps
for driver i40e : 6,187,169 pps -> 6,724,519 pps = +537,350 pps
With frames avail as a bulk inside the driver ndo_xdp_xmit call,
further optimizations are possible, like bulk DMA-mapping for TX.
Testing without CONFIG_RETPOLINE show the same performance for
physical NIC drivers.
The virtual NIC driver tun sees a huge performance boost, as it can
avoid doing per frame producer locking, but instead amortize the
locking cost over the bulk.
V2: Fix compile errors reported by kbuild test robot <lkp@intel.com>
V4: Isolated ndo, driver changes and callers.
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2018-05-24 22:46:12 +08:00
|
|
|
|
|
|
|
spin_lock(&tfile->tx_ring.producer_lock);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
struct xdp_frame *xdp = frames[i];
|
|
|
|
/* Encode the XDP flag into lowest bit for consumer to differ
|
|
|
|
* XDP buffer from sk_buff.
|
|
|
|
*/
|
|
|
|
void *frame = tun_xdp_to_ptr(xdp);
|
|
|
|
|
|
|
|
if (__ptr_ring_produce(&tfile->tx_ring, frame)) {
|
2020-11-08 04:50:56 +08:00
|
|
|
atomic_long_inc(&dev->tx_dropped);
|
xdp: change ndo_xdp_xmit API to support bulking
This patch change the API for ndo_xdp_xmit to support bulking
xdp_frames.
When kernel is compiled with CONFIG_RETPOLINE, XDP sees a huge slowdown.
Most of the slowdown is caused by DMA API indirect function calls, but
also the net_device->ndo_xdp_xmit() call.
Benchmarked patch with CONFIG_RETPOLINE, using xdp_redirect_map with
single flow/core test (CPU E5-1650 v4 @ 3.60GHz), showed
performance improved:
for driver ixgbe: 6,042,682 pps -> 6,853,768 pps = +811,086 pps
for driver i40e : 6,187,169 pps -> 6,724,519 pps = +537,350 pps
With frames avail as a bulk inside the driver ndo_xdp_xmit call,
further optimizations are possible, like bulk DMA-mapping for TX.
Testing without CONFIG_RETPOLINE show the same performance for
physical NIC drivers.
The virtual NIC driver tun sees a huge performance boost, as it can
avoid doing per frame producer locking, but instead amortize the
locking cost over the bulk.
V2: Fix compile errors reported by kbuild test robot <lkp@intel.com>
V4: Isolated ndo, driver changes and callers.
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2018-05-24 22:46:12 +08:00
|
|
|
xdp_return_frame_rx_napi(xdp);
|
|
|
|
drops++;
|
|
|
|
}
|
2018-01-04 11:14:28 +08:00
|
|
|
}
|
xdp: change ndo_xdp_xmit API to support bulking
This patch change the API for ndo_xdp_xmit to support bulking
xdp_frames.
When kernel is compiled with CONFIG_RETPOLINE, XDP sees a huge slowdown.
Most of the slowdown is caused by DMA API indirect function calls, but
also the net_device->ndo_xdp_xmit() call.
Benchmarked patch with CONFIG_RETPOLINE, using xdp_redirect_map with
single flow/core test (CPU E5-1650 v4 @ 3.60GHz), showed
performance improved:
for driver ixgbe: 6,042,682 pps -> 6,853,768 pps = +811,086 pps
for driver i40e : 6,187,169 pps -> 6,724,519 pps = +537,350 pps
With frames avail as a bulk inside the driver ndo_xdp_xmit call,
further optimizations are possible, like bulk DMA-mapping for TX.
Testing without CONFIG_RETPOLINE show the same performance for
physical NIC drivers.
The virtual NIC driver tun sees a huge performance boost, as it can
avoid doing per frame producer locking, but instead amortize the
locking cost over the bulk.
V2: Fix compile errors reported by kbuild test robot <lkp@intel.com>
V4: Isolated ndo, driver changes and callers.
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2018-05-24 22:46:12 +08:00
|
|
|
spin_unlock(&tfile->tx_ring.producer_lock);
|
2018-01-04 11:14:28 +08:00
|
|
|
|
2018-05-31 17:00:03 +08:00
|
|
|
if (flags & XDP_XMIT_FLUSH)
|
|
|
|
__tun_xdp_flush_tfile(tfile);
|
|
|
|
|
2018-01-04 11:14:28 +08:00
|
|
|
rcu_read_unlock();
|
xdp: change ndo_xdp_xmit API to support bulking
This patch change the API for ndo_xdp_xmit to support bulking
xdp_frames.
When kernel is compiled with CONFIG_RETPOLINE, XDP sees a huge slowdown.
Most of the slowdown is caused by DMA API indirect function calls, but
also the net_device->ndo_xdp_xmit() call.
Benchmarked patch with CONFIG_RETPOLINE, using xdp_redirect_map with
single flow/core test (CPU E5-1650 v4 @ 3.60GHz), showed
performance improved:
for driver ixgbe: 6,042,682 pps -> 6,853,768 pps = +811,086 pps
for driver i40e : 6,187,169 pps -> 6,724,519 pps = +537,350 pps
With frames avail as a bulk inside the driver ndo_xdp_xmit call,
further optimizations are possible, like bulk DMA-mapping for TX.
Testing without CONFIG_RETPOLINE show the same performance for
physical NIC drivers.
The virtual NIC driver tun sees a huge performance boost, as it can
avoid doing per frame producer locking, but instead amortize the
locking cost over the bulk.
V2: Fix compile errors reported by kbuild test robot <lkp@intel.com>
V4: Isolated ndo, driver changes and callers.
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2018-05-24 22:46:12 +08:00
|
|
|
return cnt - drops;
|
2018-01-04 11:14:28 +08:00
|
|
|
}
|
|
|
|
|
2018-04-17 22:46:37 +08:00
|
|
|
static int tun_xdp_tx(struct net_device *dev, struct xdp_buff *xdp)
|
|
|
|
{
|
2020-05-29 04:47:29 +08:00
|
|
|
struct xdp_frame *frame = xdp_convert_buff_to_frame(xdp);
|
2018-04-17 22:46:37 +08:00
|
|
|
|
|
|
|
if (unlikely(!frame))
|
|
|
|
return -EOVERFLOW;
|
|
|
|
|
2018-06-05 19:55:45 +08:00
|
|
|
return tun_xdp_xmit(dev, 1, &frame, XDP_XMIT_FLUSH);
|
2018-01-04 11:14:28 +08:00
|
|
|
}
|
|
|
|
|
2008-11-20 14:10:37 +08:00
|
|
|
static const struct net_device_ops tap_netdev_ops = {
|
2009-01-20 19:07:17 +08:00
|
|
|
.ndo_uninit = tun_net_uninit,
|
2008-11-20 14:10:37 +08:00
|
|
|
.ndo_open = tun_net_open,
|
|
|
|
.ndo_stop = tun_net_close,
|
2008-11-21 12:14:53 +08:00
|
|
|
.ndo_start_xmit = tun_net_xmit,
|
2011-04-19 14:13:10 +08:00
|
|
|
.ndo_fix_features = tun_net_fix_features,
|
2011-08-16 14:29:01 +08:00
|
|
|
.ndo_set_rx_mode = tun_net_mclist,
|
2008-11-20 14:10:37 +08:00
|
|
|
.ndo_set_mac_address = eth_mac_addr,
|
|
|
|
.ndo_validate_addr = eth_validate_addr,
|
2012-11-01 03:46:00 +08:00
|
|
|
.ndo_select_queue = tun_select_queue,
|
2015-07-31 14:03:27 +08:00
|
|
|
.ndo_features_check = passthru_features_check,
|
2016-02-26 17:45:40 +08:00
|
|
|
.ndo_set_rx_headroom = tun_set_headroom,
|
2020-11-08 04:50:56 +08:00
|
|
|
.ndo_get_stats64 = dev_get_tstats64,
|
2017-11-04 04:56:16 +08:00
|
|
|
.ndo_bpf = tun_xdp,
|
2018-01-04 11:14:28 +08:00
|
|
|
.ndo_xdp_xmit = tun_xdp_xmit,
|
2018-11-29 02:12:56 +08:00
|
|
|
.ndo_change_carrier = tun_net_change_carrier,
|
2008-11-20 14:10:37 +08:00
|
|
|
};
|
|
|
|
|
2013-06-11 21:01:08 +08:00
|
|
|
static void tun_flow_init(struct tun_struct *tun)
|
2012-11-01 03:46:02 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < TUN_NUM_FLOW_ENTRIES; i++)
|
|
|
|
INIT_HLIST_HEAD(&tun->flows[i]);
|
|
|
|
|
|
|
|
tun->ageing_time = TUN_FLOW_EXPIRE;
|
treewide: setup_timer() -> timer_setup()
This converts all remaining cases of the old setup_timer() API into using
timer_setup(), where the callback argument is the structure already
holding the struct timer_list. These should have no behavioral changes,
since they just change which pointer is passed into the callback with
the same available pointers after conversion. It handles the following
examples, in addition to some other variations.
Casting from unsigned long:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
setup_timer(&ptr->my_timer, my_callback, ptr);
and forced object casts:
void my_callback(struct something *ptr)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
become:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
Direct function assignments:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
ptr->my_timer.function = my_callback;
have a temporary cast added, along with converting the args:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
And finally, callbacks without a data assignment:
void my_callback(unsigned long data)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, 0);
have their argument renamed to verify they're unused during conversion:
void my_callback(struct timer_list *unused)
{
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
The conversion is done with the following Coccinelle script:
spatch --very-quiet --all-includes --include-headers \
-I ./arch/x86/include -I ./arch/x86/include/generated \
-I ./include -I ./arch/x86/include/uapi \
-I ./arch/x86/include/generated/uapi -I ./include/uapi \
-I ./include/generated/uapi --include ./include/linux/kconfig.h \
--dir . \
--cocci-file ~/src/data/timer_setup.cocci
@fix_address_of@
expression e;
@@
setup_timer(
-&(e)
+&e
, ...)
// Update any raw setup_timer() usages that have a NULL callback, but
// would otherwise match change_timer_function_usage, since the latter
// will update all function assignments done in the face of a NULL
// function initialization in setup_timer().
@change_timer_function_usage_NULL@
expression _E;
identifier _timer;
type _cast_data;
@@
(
-setup_timer(&_E->_timer, NULL, _E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E->_timer, NULL, (_cast_data)_E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, &_E);
+timer_setup(&_E._timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, (_cast_data)&_E);
+timer_setup(&_E._timer, NULL, 0);
)
@change_timer_function_usage@
expression _E;
identifier _timer;
struct timer_list _stl;
identifier _callback;
type _cast_func, _cast_data;
@@
(
-setup_timer(&_E->_timer, _callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
_E->_timer@_stl.function = _callback;
|
_E->_timer@_stl.function = &_callback;
|
_E->_timer@_stl.function = (_cast_func)_callback;
|
_E->_timer@_stl.function = (_cast_func)&_callback;
|
_E._timer@_stl.function = _callback;
|
_E._timer@_stl.function = &_callback;
|
_E._timer@_stl.function = (_cast_func)_callback;
|
_E._timer@_stl.function = (_cast_func)&_callback;
)
// callback(unsigned long arg)
@change_callback_handle_cast
depends on change_timer_function_usage@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
identifier _handle;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
(
... when != _origarg
_handletype *_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
)
}
// callback(unsigned long arg) without existing variable
@change_callback_handle_cast_no_arg
depends on change_timer_function_usage &&
!change_callback_handle_cast@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
+ _handletype *_origarg = from_timer(_origarg, t, _timer);
+
... when != _origarg
- (_handletype *)_origarg
+ _origarg
... when != _origarg
}
// Avoid already converted callbacks.
@match_callback_converted
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier t;
@@
void _callback(struct timer_list *t)
{ ... }
// callback(struct something *handle)
@change_callback_handle_arg
depends on change_timer_function_usage &&
!match_callback_converted &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
@@
void _callback(
-_handletype *_handle
+struct timer_list *t
)
{
+ _handletype *_handle = from_timer(_handle, t, _timer);
...
}
// If change_callback_handle_arg ran on an empty function, remove
// the added handler.
@unchange_callback_handle_arg
depends on change_timer_function_usage &&
change_callback_handle_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
identifier t;
@@
void _callback(struct timer_list *t)
{
- _handletype *_handle = from_timer(_handle, t, _timer);
}
// We only want to refactor the setup_timer() data argument if we've found
// the matching callback. This undoes changes in change_timer_function_usage.
@unchange_timer_function_usage
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg &&
!change_callback_handle_arg@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type change_timer_function_usage._cast_data;
@@
(
-timer_setup(&_E->_timer, _callback, 0);
+setup_timer(&_E->_timer, _callback, (_cast_data)_E);
|
-timer_setup(&_E._timer, _callback, 0);
+setup_timer(&_E._timer, _callback, (_cast_data)&_E);
)
// If we fixed a callback from a .function assignment, fix the
// assignment cast now.
@change_timer_function_assignment
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_func;
typedef TIMER_FUNC_TYPE;
@@
(
_E->_timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-&_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
)
// Sometimes timer functions are called directly. Replace matched args.
@change_timer_function_calls
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression _E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_data;
@@
_callback(
(
-(_cast_data)_E
+&_E->_timer
|
-(_cast_data)&_E
+&_E._timer
|
-_E
+&_E->_timer
)
)
// If a timer has been configured without a data argument, it can be
// converted without regard to the callback argument, since it is unused.
@match_timer_function_unused_data@
expression _E;
identifier _timer;
identifier _callback;
@@
(
-setup_timer(&_E->_timer, _callback, 0);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0L);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0UL);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0L);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0UL);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0L);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0UL);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0L);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0UL);
+timer_setup(_timer, _callback, 0);
)
@change_callback_unused_data
depends on match_timer_function_unused_data@
identifier match_timer_function_unused_data._callback;
type _origtype;
identifier _origarg;
@@
void _callback(
-_origtype _origarg
+struct timer_list *unused
)
{
... when != _origarg
}
Signed-off-by: Kees Cook <keescook@chromium.org>
2017-10-17 05:43:17 +08:00
|
|
|
timer_setup(&tun->flow_gc_timer, tun_flow_cleanup, 0);
|
|
|
|
mod_timer(&tun->flow_gc_timer,
|
|
|
|
round_jiffies_up(jiffies + tun->ageing_time));
|
2012-11-01 03:46:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_flow_uninit(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
del_timer_sync(&tun->flow_gc_timer);
|
|
|
|
tun_flow_flush(tun);
|
|
|
|
}
|
|
|
|
|
net: use core MTU range checking in core net infra
geneve:
- Merge __geneve_change_mtu back into geneve_change_mtu, set max_mtu
- This one isn't quite as straight-forward as others, could use some
closer inspection and testing
macvlan:
- set min/max_mtu
tun:
- set min/max_mtu, remove tun_net_change_mtu
vxlan:
- Merge __vxlan_change_mtu back into vxlan_change_mtu
- Set max_mtu to IP_MAX_MTU and retain dynamic MTU range checks in
change_mtu function
- This one is also not as straight-forward and could use closer inspection
and testing from vxlan folks
bridge:
- set max_mtu of IP_MAX_MTU and retain dynamic MTU range checks in
change_mtu function
openvswitch:
- set min/max_mtu, remove internal_dev_change_mtu
- note: max_mtu wasn't checked previously, it's been set to 65535, which
is the largest possible size supported
sch_teql:
- set min/max_mtu (note: max_mtu previously unchecked, used max of 65535)
macsec:
- min_mtu = 0, max_mtu = 65535
macvlan:
- min_mtu = 0, max_mtu = 65535
ntb_netdev:
- min_mtu = 0, max_mtu = 65535
veth:
- min_mtu = 68, max_mtu = 65535
8021q:
- min_mtu = 0, max_mtu = 65535
CC: netdev@vger.kernel.org
CC: Nicolas Dichtel <nicolas.dichtel@6wind.com>
CC: Hannes Frederic Sowa <hannes@stressinduktion.org>
CC: Tom Herbert <tom@herbertland.com>
CC: Daniel Borkmann <daniel@iogearbox.net>
CC: Alexander Duyck <alexander.h.duyck@intel.com>
CC: Paolo Abeni <pabeni@redhat.com>
CC: Jiri Benc <jbenc@redhat.com>
CC: WANG Cong <xiyou.wangcong@gmail.com>
CC: Roopa Prabhu <roopa@cumulusnetworks.com>
CC: Pravin B Shelar <pshelar@ovn.org>
CC: Sabrina Dubroca <sd@queasysnail.net>
CC: Patrick McHardy <kaber@trash.net>
CC: Stephen Hemminger <stephen@networkplumber.org>
CC: Pravin Shelar <pshelar@nicira.com>
CC: Maxim Krasnyansky <maxk@qti.qualcomm.com>
Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-10-21 01:55:20 +08:00
|
|
|
#define MIN_MTU 68
|
|
|
|
#define MAX_MTU 65535
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Initialize net device. */
|
|
|
|
static void tun_net_init(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
2006-09-14 01:24:59 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
switch (tun->flags & TUN_TYPE_MASK) {
|
2014-11-19 21:17:31 +08:00
|
|
|
case IFF_TUN:
|
2008-11-20 14:10:37 +08:00
|
|
|
dev->netdev_ops = &tun_netdev_ops;
|
2020-06-30 09:06:22 +08:00
|
|
|
dev->header_ops = &ip_tunnel_header_ops;
|
2008-11-20 14:10:37 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Point-to-Point TUN Device */
|
|
|
|
dev->hard_header_len = 0;
|
|
|
|
dev->addr_len = 0;
|
|
|
|
dev->mtu = 1500;
|
|
|
|
|
|
|
|
/* Zero header length */
|
2006-09-14 01:24:59 +08:00
|
|
|
dev->type = ARPHRD_NONE;
|
2005-04-17 06:20:36 +08:00
|
|
|
dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
|
|
|
|
break;
|
|
|
|
|
2014-11-19 21:17:31 +08:00
|
|
|
case IFF_TAP:
|
2008-12-30 10:23:28 +08:00
|
|
|
dev->netdev_ops = &tap_netdev_ops;
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Ethernet TAP Device */
|
|
|
|
ether_setup(dev);
|
2011-07-26 14:05:38 +08:00
|
|
|
dev->priv_flags &= ~IFF_TX_SKB_SHARING;
|
2012-12-10 23:16:00 +08:00
|
|
|
dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
|
2007-04-26 16:00:55 +08:00
|
|
|
|
2012-02-15 14:45:39 +08:00
|
|
|
eth_hw_addr_random(dev);
|
2007-04-26 16:00:55 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
net: use core MTU range checking in core net infra
geneve:
- Merge __geneve_change_mtu back into geneve_change_mtu, set max_mtu
- This one isn't quite as straight-forward as others, could use some
closer inspection and testing
macvlan:
- set min/max_mtu
tun:
- set min/max_mtu, remove tun_net_change_mtu
vxlan:
- Merge __vxlan_change_mtu back into vxlan_change_mtu
- Set max_mtu to IP_MAX_MTU and retain dynamic MTU range checks in
change_mtu function
- This one is also not as straight-forward and could use closer inspection
and testing from vxlan folks
bridge:
- set max_mtu of IP_MAX_MTU and retain dynamic MTU range checks in
change_mtu function
openvswitch:
- set min/max_mtu, remove internal_dev_change_mtu
- note: max_mtu wasn't checked previously, it's been set to 65535, which
is the largest possible size supported
sch_teql:
- set min/max_mtu (note: max_mtu previously unchecked, used max of 65535)
macsec:
- min_mtu = 0, max_mtu = 65535
macvlan:
- min_mtu = 0, max_mtu = 65535
ntb_netdev:
- min_mtu = 0, max_mtu = 65535
veth:
- min_mtu = 68, max_mtu = 65535
8021q:
- min_mtu = 0, max_mtu = 65535
CC: netdev@vger.kernel.org
CC: Nicolas Dichtel <nicolas.dichtel@6wind.com>
CC: Hannes Frederic Sowa <hannes@stressinduktion.org>
CC: Tom Herbert <tom@herbertland.com>
CC: Daniel Borkmann <daniel@iogearbox.net>
CC: Alexander Duyck <alexander.h.duyck@intel.com>
CC: Paolo Abeni <pabeni@redhat.com>
CC: Jiri Benc <jbenc@redhat.com>
CC: WANG Cong <xiyou.wangcong@gmail.com>
CC: Roopa Prabhu <roopa@cumulusnetworks.com>
CC: Pravin B Shelar <pshelar@ovn.org>
CC: Sabrina Dubroca <sd@queasysnail.net>
CC: Patrick McHardy <kaber@trash.net>
CC: Stephen Hemminger <stephen@networkplumber.org>
CC: Pravin Shelar <pshelar@nicira.com>
CC: Maxim Krasnyansky <maxk@qti.qualcomm.com>
Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-10-21 01:55:20 +08:00
|
|
|
|
|
|
|
dev->min_mtu = MIN_MTU;
|
|
|
|
dev->max_mtu = MAX_MTU - dev->hard_header_len;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2018-05-22 14:21:04 +08:00
|
|
|
static bool tun_sock_writeable(struct tun_struct *tun, struct tun_file *tfile)
|
|
|
|
{
|
|
|
|
struct sock *sk = tfile->socket.sk;
|
|
|
|
|
|
|
|
return (tun->dev->flags & IFF_UP) && sock_writeable(sk);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Character device part */
|
|
|
|
|
|
|
|
/* Poll */
|
2017-07-03 18:39:46 +08:00
|
|
|
static __poll_t tun_chr_poll(struct file *file, poll_table *wait)
|
2006-09-14 01:24:59 +08:00
|
|
|
{
|
2009-01-20 19:03:21 +08:00
|
|
|
struct tun_file *tfile = file->private_data;
|
2017-09-23 22:36:52 +08:00
|
|
|
struct tun_struct *tun = tun_get(tfile);
|
2009-07-06 03:48:35 +08:00
|
|
|
struct sock *sk;
|
2017-07-03 18:39:46 +08:00
|
|
|
__poll_t mask = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!tun)
|
2018-02-12 06:34:03 +08:00
|
|
|
return EPOLLERR;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-11-01 03:45:57 +08:00
|
|
|
sk = tfile->socket.sk;
|
2009-07-06 03:48:35 +08:00
|
|
|
|
2014-05-17 06:11:48 +08:00
|
|
|
poll_wait(file, sk_sleep(sk), wait);
|
2006-09-14 01:24:59 +08:00
|
|
|
|
2018-01-04 11:14:27 +08:00
|
|
|
if (!ptr_ring_empty(&tfile->tx_ring))
|
2018-02-12 06:34:03 +08:00
|
|
|
mask |= EPOLLIN | EPOLLRDNORM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2018-05-22 14:21:04 +08:00
|
|
|
/* Make sure SOCKWQ_ASYNC_NOSPACE is set if not writable to
|
|
|
|
* guarantee EPOLLOUT to be raised by either here or
|
|
|
|
* tun_sock_write_space(). Then process could get notification
|
|
|
|
* after it writes to a down device and meets -EIO.
|
|
|
|
*/
|
|
|
|
if (tun_sock_writeable(tun, tfile) ||
|
|
|
|
(!test_and_set_bit(SOCKWQ_ASYNC_NOSPACE, &sk->sk_socket->flags) &&
|
|
|
|
tun_sock_writeable(tun, tfile)))
|
2018-02-12 06:34:03 +08:00
|
|
|
mask |= EPOLLOUT | EPOLLWRNORM;
|
2009-02-06 13:25:32 +08:00
|
|
|
|
2009-01-20 19:07:17 +08:00
|
|
|
if (tun->dev->reg_state != NETREG_REGISTERED)
|
2018-02-12 06:34:03 +08:00
|
|
|
mask = EPOLLERR;
|
2009-01-20 19:07:17 +08:00
|
|
|
|
2009-01-20 19:00:40 +08:00
|
|
|
tun_put(tun);
|
2005-04-17 06:20:36 +08:00
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
2017-09-23 04:49:15 +08:00
|
|
|
static struct sk_buff *tun_napi_alloc_frags(struct tun_file *tfile,
|
|
|
|
size_t len,
|
|
|
|
const struct iov_iter *it)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
size_t linear;
|
|
|
|
int err;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (it->nr_segs > MAX_SKB_FRAGS + 1)
|
2020-12-25 10:52:16 +08:00
|
|
|
return ERR_PTR(-EMSGSIZE);
|
2017-09-23 04:49:15 +08:00
|
|
|
|
|
|
|
local_bh_disable();
|
|
|
|
skb = napi_get_frags(&tfile->napi);
|
|
|
|
local_bh_enable();
|
|
|
|
if (!skb)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
linear = iov_iter_single_seg_count(it);
|
|
|
|
err = __skb_grow(skb, linear);
|
|
|
|
if (err)
|
|
|
|
goto free;
|
|
|
|
|
|
|
|
skb->len = len;
|
|
|
|
skb->data_len = len - linear;
|
|
|
|
skb->truesize += skb->data_len;
|
|
|
|
|
|
|
|
for (i = 1; i < it->nr_segs; i++) {
|
|
|
|
size_t fragsz = it->iov[i].iov_len;
|
2018-11-18 23:37:33 +08:00
|
|
|
struct page *page;
|
|
|
|
void *frag;
|
2017-09-23 04:49:15 +08:00
|
|
|
|
|
|
|
if (fragsz == 0 || fragsz > PAGE_SIZE) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto free;
|
|
|
|
}
|
2018-11-18 23:37:33 +08:00
|
|
|
frag = netdev_alloc_frag(fragsz);
|
|
|
|
if (!frag) {
|
2017-09-23 04:49:15 +08:00
|
|
|
err = -ENOMEM;
|
|
|
|
goto free;
|
|
|
|
}
|
2018-11-18 23:37:33 +08:00
|
|
|
page = virt_to_head_page(frag);
|
|
|
|
skb_fill_page_desc(skb, i - 1, page,
|
|
|
|
frag - page_address(page), fragsz);
|
2017-09-23 04:49:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return skb;
|
|
|
|
free:
|
|
|
|
/* frees skb and all frags allocated with napi_alloc_frag() */
|
|
|
|
napi_free_frags(&tfile->napi);
|
|
|
|
return ERR_PTR(err);
|
|
|
|
}
|
|
|
|
|
2008-08-16 06:15:10 +08:00
|
|
|
/* prepad is the amount to reserve at front. len is length after that.
|
|
|
|
* linear is a hint as to how much to copy (usually headers). */
|
2012-11-01 03:45:57 +08:00
|
|
|
static struct sk_buff *tun_alloc_skb(struct tun_file *tfile,
|
2011-06-08 22:33:08 +08:00
|
|
|
size_t prepad, size_t len,
|
|
|
|
size_t linear, int noblock)
|
2008-08-16 06:15:10 +08:00
|
|
|
{
|
2012-11-01 03:45:57 +08:00
|
|
|
struct sock *sk = tfile->socket.sk;
|
2008-08-16 06:15:10 +08:00
|
|
|
struct sk_buff *skb;
|
2009-02-06 13:25:32 +08:00
|
|
|
int err;
|
2008-08-16 06:15:10 +08:00
|
|
|
|
|
|
|
/* Under a page? Don't bother with paged skb. */
|
2009-04-14 17:09:43 +08:00
|
|
|
if (prepad + len < PAGE_SIZE || !linear)
|
2009-02-06 13:25:32 +08:00
|
|
|
linear = len;
|
2008-08-16 06:15:10 +08:00
|
|
|
|
2009-02-06 13:25:32 +08:00
|
|
|
skb = sock_alloc_send_pskb(sk, prepad + linear, len - linear, noblock,
|
2013-08-09 05:38:47 +08:00
|
|
|
&err, 0);
|
2008-08-16 06:15:10 +08:00
|
|
|
if (!skb)
|
2009-02-06 13:25:32 +08:00
|
|
|
return ERR_PTR(err);
|
2008-08-16 06:15:10 +08:00
|
|
|
|
|
|
|
skb_reserve(skb, prepad);
|
|
|
|
skb_put(skb, linear);
|
2009-02-06 13:25:32 +08:00
|
|
|
skb->data_len = len - linear;
|
|
|
|
skb->len += len - linear;
|
2008-08-16 06:15:10 +08:00
|
|
|
|
|
|
|
return skb;
|
|
|
|
}
|
|
|
|
|
2017-01-18 15:02:03 +08:00
|
|
|
static void tun_rx_batched(struct tun_struct *tun, struct tun_file *tfile,
|
|
|
|
struct sk_buff *skb, int more)
|
|
|
|
{
|
|
|
|
struct sk_buff_head *queue = &tfile->sk.sk_write_queue;
|
|
|
|
struct sk_buff_head process_queue;
|
|
|
|
u32 rx_batched = tun->rx_batched;
|
|
|
|
bool rcv = false;
|
|
|
|
|
|
|
|
if (!rx_batched || (!more && skb_queue_empty(queue))) {
|
|
|
|
local_bh_disable();
|
2018-11-18 15:46:00 +08:00
|
|
|
skb_record_rx_queue(skb, tfile->queue_index);
|
2017-01-18 15:02:03 +08:00
|
|
|
netif_receive_skb(skb);
|
|
|
|
local_bh_enable();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock(&queue->lock);
|
|
|
|
if (!more || skb_queue_len(queue) == rx_batched) {
|
|
|
|
__skb_queue_head_init(&process_queue);
|
|
|
|
skb_queue_splice_tail_init(queue, &process_queue);
|
|
|
|
rcv = true;
|
|
|
|
} else {
|
|
|
|
__skb_queue_tail(queue, skb);
|
|
|
|
}
|
|
|
|
spin_unlock(&queue->lock);
|
|
|
|
|
|
|
|
if (rcv) {
|
|
|
|
struct sk_buff *nskb;
|
|
|
|
|
|
|
|
local_bh_disable();
|
2018-11-18 15:46:00 +08:00
|
|
|
while ((nskb = __skb_dequeue(&process_queue))) {
|
|
|
|
skb_record_rx_queue(nskb, tfile->queue_index);
|
2017-01-18 15:02:03 +08:00
|
|
|
netif_receive_skb(nskb);
|
2018-11-18 15:46:00 +08:00
|
|
|
}
|
|
|
|
skb_record_rx_queue(skb, tfile->queue_index);
|
2017-01-18 15:02:03 +08:00
|
|
|
netif_receive_skb(skb);
|
|
|
|
local_bh_enable();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-11 19:41:16 +08:00
|
|
|
static bool tun_can_build_skb(struct tun_struct *tun, struct tun_file *tfile,
|
|
|
|
int len, int noblock, bool zerocopy)
|
|
|
|
{
|
|
|
|
if ((tun->flags & TUN_TYPE_MASK) != IFF_TAP)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (tfile->socket.sk->sk_sndbuf != INT_MAX)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!noblock)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (zerocopy)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (SKB_DATA_ALIGN(len + TUN_RX_PAD) +
|
|
|
|
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) > PAGE_SIZE)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-07-23 22:23:01 +08:00
|
|
|
static struct sk_buff *__tun_build_skb(struct tun_file *tfile,
|
|
|
|
struct page_frag *alloc_frag, char *buf,
|
2018-09-12 11:17:04 +08:00
|
|
|
int buflen, int len, int pad)
|
2018-09-12 11:17:03 +08:00
|
|
|
{
|
|
|
|
struct sk_buff *skb = build_skb(buf, buflen);
|
|
|
|
|
|
|
|
if (!skb)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
2018-09-12 11:17:04 +08:00
|
|
|
skb_reserve(skb, pad);
|
2018-09-12 11:17:03 +08:00
|
|
|
skb_put(skb, len);
|
2019-07-23 22:23:01 +08:00
|
|
|
skb_set_owner_w(skb, tfile->socket.sk);
|
2018-09-12 11:17:03 +08:00
|
|
|
|
|
|
|
get_page(alloc_frag->page);
|
|
|
|
alloc_frag->offset += buflen;
|
|
|
|
|
|
|
|
return skb;
|
|
|
|
}
|
|
|
|
|
2018-09-12 11:17:04 +08:00
|
|
|
static int tun_xdp_act(struct tun_struct *tun, struct bpf_prog *xdp_prog,
|
|
|
|
struct xdp_buff *xdp, u32 act)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
switch (act) {
|
|
|
|
case XDP_REDIRECT:
|
|
|
|
err = xdp_do_redirect(tun->dev, xdp, xdp_prog);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
break;
|
|
|
|
case XDP_TX:
|
|
|
|
err = tun_xdp_tx(tun->dev, xdp);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
break;
|
|
|
|
case XDP_PASS:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
bpf_warn_invalid_xdp_action(act);
|
2020-08-24 06:36:59 +08:00
|
|
|
fallthrough;
|
2018-09-12 11:17:04 +08:00
|
|
|
case XDP_ABORTED:
|
|
|
|
trace_xdp_exception(tun->dev, xdp_prog, act);
|
2020-08-24 06:36:59 +08:00
|
|
|
fallthrough;
|
2018-09-12 11:17:04 +08:00
|
|
|
case XDP_DROP:
|
2020-11-08 04:50:56 +08:00
|
|
|
atomic_long_inc(&tun->dev->rx_dropped);
|
2018-09-12 11:17:04 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return act;
|
|
|
|
}
|
|
|
|
|
2017-08-11 19:41:18 +08:00
|
|
|
static struct sk_buff *tun_build_skb(struct tun_struct *tun,
|
|
|
|
struct tun_file *tfile,
|
2017-08-11 19:41:16 +08:00
|
|
|
struct iov_iter *from,
|
2017-08-11 19:41:18 +08:00
|
|
|
struct virtio_net_hdr *hdr,
|
2017-09-04 11:36:09 +08:00
|
|
|
int len, int *skb_xdp)
|
2017-08-11 19:41:16 +08:00
|
|
|
{
|
2017-08-16 22:14:33 +08:00
|
|
|
struct page_frag *alloc_frag = ¤t->task_frag;
|
2017-08-11 19:41:18 +08:00
|
|
|
struct bpf_prog *xdp_prog;
|
2017-09-04 11:36:08 +08:00
|
|
|
int buflen = SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
|
2017-08-11 19:41:16 +08:00
|
|
|
char *buf;
|
|
|
|
size_t copied;
|
2018-09-12 11:17:04 +08:00
|
|
|
int pad = TUN_RX_PAD;
|
|
|
|
int err = 0;
|
2017-09-04 11:36:08 +08:00
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
xdp_prog = rcu_dereference(tun->xdp_prog);
|
|
|
|
if (xdp_prog)
|
2018-09-12 11:17:00 +08:00
|
|
|
pad += XDP_PACKET_HEADROOM;
|
2017-09-04 11:36:08 +08:00
|
|
|
buflen += SKB_DATA_ALIGN(len + pad);
|
|
|
|
rcu_read_unlock();
|
2017-08-11 19:41:16 +08:00
|
|
|
|
2017-10-27 11:05:44 +08:00
|
|
|
alloc_frag->offset = ALIGN((u64)alloc_frag->offset, SMP_CACHE_BYTES);
|
2017-08-11 19:41:16 +08:00
|
|
|
if (unlikely(!skb_page_frag_refill(buflen, alloc_frag, GFP_KERNEL)))
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
buf = (char *)page_address(alloc_frag->page) + alloc_frag->offset;
|
|
|
|
copied = copy_page_from_iter(alloc_frag->page,
|
2017-09-04 11:36:08 +08:00
|
|
|
alloc_frag->offset + pad,
|
2017-08-11 19:41:16 +08:00
|
|
|
len, from);
|
|
|
|
if (copied != len)
|
|
|
|
return ERR_PTR(-EFAULT);
|
|
|
|
|
2017-09-04 11:36:08 +08:00
|
|
|
/* There's a small window that XDP may be set after the check
|
|
|
|
* of xdp_prog above, this should be rare and for simplicity
|
|
|
|
* we do XDP on skb in case the headroom is not enough.
|
|
|
|
*/
|
2018-09-12 11:17:03 +08:00
|
|
|
if (hdr->gso_type || !xdp_prog) {
|
2017-09-04 11:36:09 +08:00
|
|
|
*skb_xdp = 1;
|
2019-07-23 22:23:01 +08:00
|
|
|
return __tun_build_skb(tfile, alloc_frag, buf, buflen, len,
|
|
|
|
pad);
|
2018-09-12 11:17:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
*skb_xdp = 0;
|
2017-08-11 19:41:18 +08:00
|
|
|
|
2018-05-28 18:37:49 +08:00
|
|
|
local_bh_disable();
|
2017-08-11 19:41:18 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
xdp_prog = rcu_dereference(tun->xdp_prog);
|
2018-09-12 11:17:04 +08:00
|
|
|
if (xdp_prog) {
|
2017-08-11 19:41:18 +08:00
|
|
|
struct xdp_buff xdp;
|
|
|
|
u32 act;
|
|
|
|
|
|
|
|
xdp.data_hard_start = buf;
|
2017-09-04 11:36:08 +08:00
|
|
|
xdp.data = buf + pad;
|
bpf: add meta pointer for direct access
This work enables generic transfer of metadata from XDP into skb. The
basic idea is that we can make use of the fact that the resulting skb
must be linear and already comes with a larger headroom for supporting
bpf_xdp_adjust_head(), which mangles xdp->data. Here, we base our work
on a similar principle and introduce a small helper bpf_xdp_adjust_meta()
for adjusting a new pointer called xdp->data_meta. Thus, the packet has
a flexible and programmable room for meta data, followed by the actual
packet data. struct xdp_buff is therefore laid out that we first point
to data_hard_start, then data_meta directly prepended to data followed
by data_end marking the end of packet. bpf_xdp_adjust_head() takes into
account whether we have meta data already prepended and if so, memmove()s
this along with the given offset provided there's enough room.
xdp->data_meta is optional and programs are not required to use it. The
rationale is that when we process the packet in XDP (e.g. as DoS filter),
we can push further meta data along with it for the XDP_PASS case, and
give the guarantee that a clsact ingress BPF program on the same device
can pick this up for further post-processing. Since we work with skb
there, we can also set skb->mark, skb->priority or other skb meta data
out of BPF, thus having this scratch space generic and programmable
allows for more flexibility than defining a direct 1:1 transfer of
potentially new XDP members into skb (it's also more efficient as we
don't need to initialize/handle each of such new members). The facility
also works together with GRO aggregation. The scratch space at the head
of the packet can be multiple of 4 byte up to 32 byte large. Drivers not
yet supporting xdp->data_meta can simply be set up with xdp->data_meta
as xdp->data + 1 as bpf_xdp_adjust_meta() will detect this and bail out,
such that the subsequent match against xdp->data for later access is
guaranteed to fail.
The verifier treats xdp->data_meta/xdp->data the same way as we treat
xdp->data/xdp->data_end pointer comparisons. The requirement for doing
the compare against xdp->data is that it hasn't been modified from it's
original address we got from ctx access. It may have a range marking
already from prior successful xdp->data/xdp->data_end pointer comparisons
though.
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: John Fastabend <john.fastabend@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-09-25 08:25:51 +08:00
|
|
|
xdp_set_data_meta_invalid(&xdp);
|
2017-08-11 19:41:18 +08:00
|
|
|
xdp.data_end = xdp.data + len;
|
2018-01-03 18:25:59 +08:00
|
|
|
xdp.rxq = &tfile->xdp_rxq;
|
2020-05-14 18:50:34 +08:00
|
|
|
xdp.frame_sz = buflen;
|
2017-08-11 19:41:18 +08:00
|
|
|
|
2018-09-12 11:17:04 +08:00
|
|
|
act = bpf_prog_run_xdp(xdp_prog, &xdp);
|
|
|
|
if (act == XDP_REDIRECT || act == XDP_TX) {
|
2018-03-14 11:23:40 +08:00
|
|
|
get_page(alloc_frag->page);
|
|
|
|
alloc_frag->offset += buflen;
|
2017-08-11 19:41:18 +08:00
|
|
|
}
|
2018-09-12 11:17:04 +08:00
|
|
|
err = tun_xdp_act(tun, xdp_prog, &xdp, act);
|
2020-04-03 23:13:21 +08:00
|
|
|
if (err < 0) {
|
|
|
|
if (act == XDP_REDIRECT || act == XDP_TX)
|
|
|
|
put_page(alloc_frag->page);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2018-09-12 11:17:05 +08:00
|
|
|
if (err == XDP_REDIRECT)
|
xdp: Use bulking for non-map XDP_REDIRECT and consolidate code paths
Since the bulk queue used by XDP_REDIRECT now lives in struct net_device,
we can re-use the bulking for the non-map version of the bpf_redirect()
helper. This is a simple matter of having xdp_do_redirect_slow() queue the
frame on the bulk queue instead of sending it out with __bpf_tx_xdp().
Unfortunately we can't make the bpf_redirect() helper return an error if
the ifindex doesn't exit (as bpf_redirect_map() does), because we don't
have a reference to the network namespace of the ingress device at the time
the helper is called. So we have to leave it as-is and keep the device
lookup in xdp_do_redirect_slow().
Since this leaves less reason to have the non-map redirect code in a
separate function, so we get rid of the xdp_do_redirect_slow() function
entirely. This does lose us the tracepoint disambiguation, but fortunately
the xdp_redirect and xdp_redirect_map tracepoints use the same tracepoint
entry structures. This means both can contain a map index, so we can just
amend the tracepoint definitions so we always emit the xdp_redirect(_err)
tracepoints, but with the map ID only populated if a map is present. This
means we retire the xdp_redirect_map(_err) tracepoints entirely, but keep
the definitions around in case someone is still listening for them.
With this change, the performance of the xdp_redirect sample program goes
from 5Mpps to 8.4Mpps (a 68% increase).
Since the flush functions are no longer map-specific, rename the flush()
functions to drop _map from their names. One of the renamed functions is
the xdp_do_flush_map() callback used in all the xdp-enabled drivers. To
keep from having to update all drivers, use a #define to keep the old name
working, and only update the virtual drivers in this patch.
Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: John Fastabend <john.fastabend@gmail.com>
Link: https://lore.kernel.org/bpf/157918768505.1458396.17518057312953572912.stgit@toke.dk
2020-01-16 23:14:45 +08:00
|
|
|
xdp_do_flush();
|
2018-09-12 11:17:04 +08:00
|
|
|
if (err != XDP_PASS)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
pad = xdp.data - xdp.data_hard_start;
|
|
|
|
len = xdp.data_end - xdp.data;
|
2017-08-11 19:41:18 +08:00
|
|
|
}
|
2018-09-12 11:17:01 +08:00
|
|
|
rcu_read_unlock();
|
|
|
|
local_bh_enable();
|
2017-08-11 19:41:18 +08:00
|
|
|
|
2019-07-23 22:23:01 +08:00
|
|
|
return __tun_build_skb(tfile, alloc_frag, buf, buflen, len, pad);
|
2017-08-11 19:41:18 +08:00
|
|
|
|
2018-09-12 11:17:02 +08:00
|
|
|
out:
|
2017-08-11 19:41:18 +08:00
|
|
|
rcu_read_unlock();
|
2018-05-28 18:37:49 +08:00
|
|
|
local_bh_enable();
|
2017-08-11 19:41:18 +08:00
|
|
|
return NULL;
|
2017-08-11 19:41:16 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Get packet from user space buffer */
|
2012-11-01 03:45:57 +08:00
|
|
|
static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
|
2014-06-20 03:36:49 +08:00
|
|
|
void *msg_control, struct iov_iter *from,
|
2017-01-18 15:02:03 +08:00
|
|
|
int noblock, bool more)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2009-02-01 16:45:17 +08:00
|
|
|
struct tun_pi pi = { 0, cpu_to_be16(ETH_P_IP) };
|
2005-04-17 06:20:36 +08:00
|
|
|
struct sk_buff *skb;
|
2014-06-20 03:36:49 +08:00
|
|
|
size_t total_len = iov_iter_count(from);
|
2016-02-26 17:45:40 +08:00
|
|
|
size_t len = total_len, align = tun->align, linear;
|
2008-07-03 18:48:02 +08:00
|
|
|
struct virtio_net_hdr gso = { 0 };
|
2013-11-13 14:00:39 +08:00
|
|
|
int good_linear;
|
2012-07-20 17:23:23 +08:00
|
|
|
int copylen;
|
|
|
|
bool zerocopy = false;
|
|
|
|
int err;
|
2017-12-04 17:31:23 +08:00
|
|
|
u32 rxhash = 0;
|
2017-09-04 11:36:09 +08:00
|
|
|
int skb_xdp = 1;
|
2018-09-29 05:51:49 +08:00
|
|
|
bool frags = tun_napi_frags_enabled(tfile);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-11-19 21:17:31 +08:00
|
|
|
if (!(tun->flags & IFF_NO_PI)) {
|
2013-08-15 20:52:57 +08:00
|
|
|
if (len < sizeof(pi))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
2013-08-15 20:52:57 +08:00
|
|
|
len -= sizeof(pi);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2016-11-02 10:09:04 +08:00
|
|
|
if (!copy_from_iter_full(&pi, sizeof(pi), from))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
2014-11-19 21:17:31 +08:00
|
|
|
if (tun->flags & IFF_VNET_HDR) {
|
2017-02-04 07:20:48 +08:00
|
|
|
int vnet_hdr_sz = READ_ONCE(tun->vnet_hdr_sz);
|
|
|
|
|
|
|
|
if (len < vnet_hdr_sz)
|
2008-07-03 18:48:02 +08:00
|
|
|
return -EINVAL;
|
2017-02-04 07:20:48 +08:00
|
|
|
len -= vnet_hdr_sz;
|
2008-07-03 18:48:02 +08:00
|
|
|
|
2016-11-02 10:09:04 +08:00
|
|
|
if (!copy_from_iter_full(&gso, sizeof(gso), from))
|
2008-07-03 18:48:02 +08:00
|
|
|
return -EFAULT;
|
|
|
|
|
2009-06-08 15:20:01 +08:00
|
|
|
if ((gso.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) &&
|
2014-10-24 03:59:31 +08:00
|
|
|
tun16_to_cpu(tun, gso.csum_start) + tun16_to_cpu(tun, gso.csum_offset) + 2 > tun16_to_cpu(tun, gso.hdr_len))
|
|
|
|
gso.hdr_len = cpu_to_tun16(tun, tun16_to_cpu(tun, gso.csum_start) + tun16_to_cpu(tun, gso.csum_offset) + 2);
|
2009-06-08 15:20:01 +08:00
|
|
|
|
2014-10-24 03:59:31 +08:00
|
|
|
if (tun16_to_cpu(tun, gso.hdr_len) > len)
|
2008-07-03 18:48:02 +08:00
|
|
|
return -EINVAL;
|
2017-02-04 07:20:48 +08:00
|
|
|
iov_iter_advance(from, vnet_hdr_sz - sizeof(gso));
|
2008-07-03 18:48:02 +08:00
|
|
|
}
|
|
|
|
|
2014-11-19 21:17:31 +08:00
|
|
|
if ((tun->flags & TUN_TYPE_MASK) == IFF_TAP) {
|
2011-06-08 22:33:07 +08:00
|
|
|
align += NET_IP_ALIGN;
|
2009-04-14 17:09:43 +08:00
|
|
|
if (unlikely(len < ETH_HLEN ||
|
2014-10-24 03:59:31 +08:00
|
|
|
(gso.hdr_len && tun16_to_cpu(tun, gso.hdr_len) < ETH_HLEN)))
|
2008-04-13 09:49:30 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2006-09-14 01:24:59 +08:00
|
|
|
|
2013-11-13 14:00:39 +08:00
|
|
|
good_linear = SKB_MAX_HEAD(align);
|
|
|
|
|
2013-07-18 10:55:15 +08:00
|
|
|
if (msg_control) {
|
2014-06-20 03:36:49 +08:00
|
|
|
struct iov_iter i = *from;
|
|
|
|
|
2013-07-18 10:55:15 +08:00
|
|
|
/* There are 256 bytes to be copied in skb, so there is
|
|
|
|
* enough room for skb expand head in case it is used.
|
2012-07-20 17:23:23 +08:00
|
|
|
* The rest of the buffer is mapped from userspace.
|
|
|
|
*/
|
2014-10-24 03:59:31 +08:00
|
|
|
copylen = gso.hdr_len ? tun16_to_cpu(tun, gso.hdr_len) : GOODCOPY_LEN;
|
2013-11-13 14:00:39 +08:00
|
|
|
if (copylen > good_linear)
|
|
|
|
copylen = good_linear;
|
2013-07-10 13:43:27 +08:00
|
|
|
linear = copylen;
|
2014-06-20 03:36:49 +08:00
|
|
|
iov_iter_advance(&i, copylen);
|
|
|
|
if (iov_iter_npages(&i, INT_MAX) <= MAX_SKB_FRAGS)
|
2013-07-18 10:55:15 +08:00
|
|
|
zerocopy = true;
|
|
|
|
}
|
|
|
|
|
2017-09-23 04:49:15 +08:00
|
|
|
if (!frags && tun_can_build_skb(tun, tfile, len, noblock, zerocopy)) {
|
2017-09-04 11:36:09 +08:00
|
|
|
/* For the packet that is not easy to be processed
|
|
|
|
* (e.g gso or jumbo packet), we will do it at after
|
|
|
|
* skb was created with generic XDP routine.
|
|
|
|
*/
|
|
|
|
skb = tun_build_skb(tun, tfile, from, &gso, len, &skb_xdp);
|
2017-08-11 19:41:16 +08:00
|
|
|
if (IS_ERR(skb)) {
|
2020-11-08 04:50:56 +08:00
|
|
|
atomic_long_inc(&tun->dev->rx_dropped);
|
2017-08-11 19:41:16 +08:00
|
|
|
return PTR_ERR(skb);
|
|
|
|
}
|
2017-08-11 19:41:18 +08:00
|
|
|
if (!skb)
|
|
|
|
return total_len;
|
2017-08-11 19:41:16 +08:00
|
|
|
} else {
|
|
|
|
if (!zerocopy) {
|
|
|
|
copylen = len;
|
|
|
|
if (tun16_to_cpu(tun, gso.hdr_len) > good_linear)
|
|
|
|
linear = good_linear;
|
|
|
|
else
|
|
|
|
linear = tun16_to_cpu(tun, gso.hdr_len);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-09-23 04:49:15 +08:00
|
|
|
if (frags) {
|
|
|
|
mutex_lock(&tfile->napi_mutex);
|
|
|
|
skb = tun_napi_alloc_frags(tfile, copylen, from);
|
|
|
|
/* tun_napi_alloc_frags() enforces a layout for the skb.
|
|
|
|
* If zerocopy is enabled, then this layout will be
|
|
|
|
* overwritten by zerocopy_sg_from_iter().
|
|
|
|
*/
|
|
|
|
zerocopy = false;
|
|
|
|
} else {
|
|
|
|
skb = tun_alloc_skb(tfile, align, copylen, linear,
|
|
|
|
noblock);
|
|
|
|
}
|
|
|
|
|
2017-08-11 19:41:16 +08:00
|
|
|
if (IS_ERR(skb)) {
|
|
|
|
if (PTR_ERR(skb) != -EAGAIN)
|
2020-11-08 04:50:56 +08:00
|
|
|
atomic_long_inc(&tun->dev->rx_dropped);
|
2017-09-23 04:49:15 +08:00
|
|
|
if (frags)
|
|
|
|
mutex_unlock(&tfile->napi_mutex);
|
2017-08-11 19:41:16 +08:00
|
|
|
return PTR_ERR(skb);
|
|
|
|
}
|
2012-07-20 17:23:23 +08:00
|
|
|
|
2017-08-11 19:41:16 +08:00
|
|
|
if (zerocopy)
|
|
|
|
err = zerocopy_sg_from_iter(skb, from);
|
|
|
|
else
|
|
|
|
err = skb_copy_datagram_from_iter(skb, 0, from, len);
|
|
|
|
|
|
|
|
if (err) {
|
2019-03-15 11:19:47 +08:00
|
|
|
err = -EFAULT;
|
|
|
|
drop:
|
2020-11-08 04:50:56 +08:00
|
|
|
atomic_long_inc(&tun->dev->rx_dropped);
|
2017-08-11 19:41:16 +08:00
|
|
|
kfree_skb(skb);
|
2017-09-23 04:49:15 +08:00
|
|
|
if (frags) {
|
|
|
|
tfile->napi.skb = NULL;
|
|
|
|
mutex_unlock(&tfile->napi_mutex);
|
|
|
|
}
|
|
|
|
|
2019-03-15 11:19:47 +08:00
|
|
|
return err;
|
2017-08-11 19:41:16 +08:00
|
|
|
}
|
2006-03-12 10:49:13 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2016-11-19 07:40:38 +08:00
|
|
|
if (virtio_net_hdr_to_skb(skb, &gso, tun_is_little_endian(tun))) {
|
2020-11-08 04:50:56 +08:00
|
|
|
atomic_long_inc(&tun->rx_frame_errors);
|
2016-06-14 06:00:04 +08:00
|
|
|
kfree_skb(skb);
|
2017-09-23 04:49:15 +08:00
|
|
|
if (frags) {
|
|
|
|
tfile->napi.skb = NULL;
|
|
|
|
mutex_unlock(&tfile->napi_mutex);
|
|
|
|
}
|
|
|
|
|
2016-06-14 06:00:04 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
switch (tun->flags & TUN_TYPE_MASK) {
|
2014-11-19 21:17:31 +08:00
|
|
|
case IFF_TUN:
|
|
|
|
if (tun->flags & IFF_NO_PI) {
|
tun: bail out from tun_get_user() if the skb is empty
KMSAN (https://github.com/google/kmsan) reported accessing uninitialized
skb->data[0] in the case the skb is empty (i.e. skb->len is 0):
================================================
BUG: KMSAN: use of uninitialized memory in tun_get_user+0x19ba/0x3770
CPU: 0 PID: 3051 Comm: probe Not tainted 4.13.0+ #3140
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
Call Trace:
...
__msan_warning_32+0x66/0xb0 mm/kmsan/kmsan_instr.c:477
tun_get_user+0x19ba/0x3770 drivers/net/tun.c:1301
tun_chr_write_iter+0x19f/0x300 drivers/net/tun.c:1365
call_write_iter ./include/linux/fs.h:1743
new_sync_write fs/read_write.c:457
__vfs_write+0x6c3/0x7f0 fs/read_write.c:470
vfs_write+0x3e4/0x770 fs/read_write.c:518
SYSC_write+0x12f/0x2b0 fs/read_write.c:565
SyS_write+0x55/0x80 fs/read_write.c:557
do_syscall_64+0x242/0x330 arch/x86/entry/common.c:284
entry_SYSCALL64_slow_path+0x25/0x25 arch/x86/entry/entry_64.S:245
...
origin:
...
kmsan_poison_shadow+0x6e/0xc0 mm/kmsan/kmsan.c:211
slab_alloc_node mm/slub.c:2732
__kmalloc_node_track_caller+0x351/0x370 mm/slub.c:4351
__kmalloc_reserve net/core/skbuff.c:138
__alloc_skb+0x26a/0x810 net/core/skbuff.c:231
alloc_skb ./include/linux/skbuff.h:903
alloc_skb_with_frags+0x1d7/0xc80 net/core/skbuff.c:4756
sock_alloc_send_pskb+0xabf/0xfe0 net/core/sock.c:2037
tun_alloc_skb drivers/net/tun.c:1144
tun_get_user+0x9a8/0x3770 drivers/net/tun.c:1274
tun_chr_write_iter+0x19f/0x300 drivers/net/tun.c:1365
call_write_iter ./include/linux/fs.h:1743
new_sync_write fs/read_write.c:457
__vfs_write+0x6c3/0x7f0 fs/read_write.c:470
vfs_write+0x3e4/0x770 fs/read_write.c:518
SYSC_write+0x12f/0x2b0 fs/read_write.c:565
SyS_write+0x55/0x80 fs/read_write.c:557
do_syscall_64+0x242/0x330 arch/x86/entry/common.c:284
return_from_SYSCALL_64+0x0/0x6a arch/x86/entry/entry_64.S:245
================================================
Make sure tun_get_user() doesn't touch skb->data[0] unless there is
actual data.
C reproducer below:
==========================
// autogenerated by syzkaller (http://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <fcntl.h>
#include <linux/if_tun.h>
#include <netinet/ip.h>
#include <net/if.h>
#include <string.h>
#include <sys/ioctl.h>
int main()
{
int sock = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
int tun_fd = open("/dev/net/tun", O_RDWR);
struct ifreq req;
memset(&req, 0, sizeof(struct ifreq));
strcpy((char*)&req.ifr_name, "gre0");
req.ifr_flags = IFF_UP | IFF_MULTICAST;
ioctl(tun_fd, TUNSETIFF, &req);
ioctl(sock, SIOCSIFFLAGS, "gre0");
write(tun_fd, "hi", 0);
return 0;
}
==========================
Signed-off-by: Alexander Potapenko <glider@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-09-28 17:32:37 +08:00
|
|
|
u8 ip_version = skb->len ? (skb->data[0] >> 4) : 0;
|
|
|
|
|
|
|
|
switch (ip_version) {
|
|
|
|
case 4:
|
2008-06-18 12:10:33 +08:00
|
|
|
pi.proto = htons(ETH_P_IP);
|
|
|
|
break;
|
tun: bail out from tun_get_user() if the skb is empty
KMSAN (https://github.com/google/kmsan) reported accessing uninitialized
skb->data[0] in the case the skb is empty (i.e. skb->len is 0):
================================================
BUG: KMSAN: use of uninitialized memory in tun_get_user+0x19ba/0x3770
CPU: 0 PID: 3051 Comm: probe Not tainted 4.13.0+ #3140
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
Call Trace:
...
__msan_warning_32+0x66/0xb0 mm/kmsan/kmsan_instr.c:477
tun_get_user+0x19ba/0x3770 drivers/net/tun.c:1301
tun_chr_write_iter+0x19f/0x300 drivers/net/tun.c:1365
call_write_iter ./include/linux/fs.h:1743
new_sync_write fs/read_write.c:457
__vfs_write+0x6c3/0x7f0 fs/read_write.c:470
vfs_write+0x3e4/0x770 fs/read_write.c:518
SYSC_write+0x12f/0x2b0 fs/read_write.c:565
SyS_write+0x55/0x80 fs/read_write.c:557
do_syscall_64+0x242/0x330 arch/x86/entry/common.c:284
entry_SYSCALL64_slow_path+0x25/0x25 arch/x86/entry/entry_64.S:245
...
origin:
...
kmsan_poison_shadow+0x6e/0xc0 mm/kmsan/kmsan.c:211
slab_alloc_node mm/slub.c:2732
__kmalloc_node_track_caller+0x351/0x370 mm/slub.c:4351
__kmalloc_reserve net/core/skbuff.c:138
__alloc_skb+0x26a/0x810 net/core/skbuff.c:231
alloc_skb ./include/linux/skbuff.h:903
alloc_skb_with_frags+0x1d7/0xc80 net/core/skbuff.c:4756
sock_alloc_send_pskb+0xabf/0xfe0 net/core/sock.c:2037
tun_alloc_skb drivers/net/tun.c:1144
tun_get_user+0x9a8/0x3770 drivers/net/tun.c:1274
tun_chr_write_iter+0x19f/0x300 drivers/net/tun.c:1365
call_write_iter ./include/linux/fs.h:1743
new_sync_write fs/read_write.c:457
__vfs_write+0x6c3/0x7f0 fs/read_write.c:470
vfs_write+0x3e4/0x770 fs/read_write.c:518
SYSC_write+0x12f/0x2b0 fs/read_write.c:565
SyS_write+0x55/0x80 fs/read_write.c:557
do_syscall_64+0x242/0x330 arch/x86/entry/common.c:284
return_from_SYSCALL_64+0x0/0x6a arch/x86/entry/entry_64.S:245
================================================
Make sure tun_get_user() doesn't touch skb->data[0] unless there is
actual data.
C reproducer below:
==========================
// autogenerated by syzkaller (http://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <fcntl.h>
#include <linux/if_tun.h>
#include <netinet/ip.h>
#include <net/if.h>
#include <string.h>
#include <sys/ioctl.h>
int main()
{
int sock = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
int tun_fd = open("/dev/net/tun", O_RDWR);
struct ifreq req;
memset(&req, 0, sizeof(struct ifreq));
strcpy((char*)&req.ifr_name, "gre0");
req.ifr_flags = IFF_UP | IFF_MULTICAST;
ioctl(tun_fd, TUNSETIFF, &req);
ioctl(sock, SIOCSIFFLAGS, "gre0");
write(tun_fd, "hi", 0);
return 0;
}
==========================
Signed-off-by: Alexander Potapenko <glider@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-09-28 17:32:37 +08:00
|
|
|
case 6:
|
2008-06-18 12:10:33 +08:00
|
|
|
pi.proto = htons(ETH_P_IPV6);
|
|
|
|
break;
|
|
|
|
default:
|
2020-11-08 04:50:56 +08:00
|
|
|
atomic_long_inc(&tun->dev->rx_dropped);
|
2008-06-18 12:10:33 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-20 06:30:44 +08:00
|
|
|
skb_reset_mac_header(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
skb->protocol = pi.proto;
|
2007-04-26 08:40:23 +08:00
|
|
|
skb->dev = tun->dev;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2014-11-19 21:17:31 +08:00
|
|
|
case IFF_TAP:
|
2020-05-31 03:41:31 +08:00
|
|
|
if (frags && !pskb_may_pull(skb, ETH_HLEN)) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
skb->protocol = eth_type_trans(skb, tun->dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2011-06-03 19:51:20 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-07-20 17:23:23 +08:00
|
|
|
/* copy skb_ubuf_info for callback when skb has no error */
|
|
|
|
if (zerocopy) {
|
2021-01-07 06:18:40 +08:00
|
|
|
skb_zcopy_init(skb, msg_control);
|
2016-11-30 13:17:51 +08:00
|
|
|
} else if (msg_control) {
|
|
|
|
struct ubuf_info *uarg = msg_control;
|
2021-01-07 06:18:34 +08:00
|
|
|
uarg->callback(NULL, uarg, false);
|
2012-07-20 17:23:23 +08:00
|
|
|
}
|
|
|
|
|
2015-02-04 05:36:16 +08:00
|
|
|
skb_reset_network_header(skb);
|
net: Don't set transport offset to invalid value
If the socket was created with socket(AF_PACKET, SOCK_RAW, 0),
skb->protocol will be unset, __skb_flow_dissect() will fail, and
skb_probe_transport_header() will fall back to the offset_hint, making
the resulting skb_transport_offset incorrect.
If, however, there is no transport header in the packet,
transport_header shouldn't be set to an arbitrary value.
Fix it by leaving the transport offset unset if it couldn't be found, to
be explicit rather than to fill it with some wrong value. It changes the
behavior, but if some code relied on the old behavior, it would be
broken anyway, as the old one is incorrect.
Signed-off-by: Maxim Mikityanskiy <maximmi@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-02-21 20:39:57 +08:00
|
|
|
skb_probe_transport_header(skb);
|
2020-04-11 00:20:59 +08:00
|
|
|
skb_record_rx_queue(skb, tfile->queue_index);
|
2013-03-26 04:19:56 +08:00
|
|
|
|
2017-09-04 11:36:09 +08:00
|
|
|
if (skb_xdp) {
|
2017-08-11 19:41:18 +08:00
|
|
|
struct bpf_prog *xdp_prog;
|
|
|
|
int ret;
|
|
|
|
|
2018-05-28 18:37:49 +08:00
|
|
|
local_bh_disable();
|
2017-08-11 19:41:18 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
xdp_prog = rcu_dereference(tun->xdp_prog);
|
|
|
|
if (xdp_prog) {
|
|
|
|
ret = do_xdp_generic(xdp_prog, skb);
|
|
|
|
if (ret != XDP_PASS) {
|
|
|
|
rcu_read_unlock();
|
2018-05-28 18:37:49 +08:00
|
|
|
local_bh_enable();
|
2020-01-23 01:07:35 +08:00
|
|
|
if (frags) {
|
|
|
|
tfile->napi.skb = NULL;
|
|
|
|
mutex_unlock(&tfile->napi_mutex);
|
|
|
|
}
|
2017-08-11 19:41:18 +08:00
|
|
|
return total_len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
2018-05-28 18:37:49 +08:00
|
|
|
local_bh_enable();
|
2017-08-11 19:41:18 +08:00
|
|
|
}
|
|
|
|
|
2018-04-20 19:18:16 +08:00
|
|
|
/* Compute the costly rx hash only if needed for flow updates.
|
|
|
|
* We may get a very small possibility of OOO during switching, not
|
|
|
|
* worth to optimize.
|
|
|
|
*/
|
|
|
|
if (!rcu_access_pointer(tun->steering_prog) && tun->numqueues > 1 &&
|
|
|
|
!tfile->detached)
|
2017-12-04 17:31:23 +08:00
|
|
|
rxhash = __skb_get_hash_symmetric(skb);
|
2017-09-23 04:49:14 +08:00
|
|
|
|
2019-03-15 11:19:47 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
if (unlikely(!(tun->dev->flags & IFF_UP))) {
|
|
|
|
err = -EIO;
|
2019-03-17 04:09:53 +08:00
|
|
|
rcu_read_unlock();
|
2019-03-15 11:19:47 +08:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2017-09-23 04:49:15 +08:00
|
|
|
if (frags) {
|
2020-05-31 03:41:31 +08:00
|
|
|
u32 headlen;
|
|
|
|
|
2017-09-23 04:49:15 +08:00
|
|
|
/* Exercise flow dissector code path. */
|
2020-05-31 03:41:31 +08:00
|
|
|
skb_push(skb, ETH_HLEN);
|
|
|
|
headlen = eth_get_headlen(tun->dev, skb->data,
|
|
|
|
skb_headlen(skb));
|
2017-09-23 04:49:15 +08:00
|
|
|
|
2017-10-18 01:07:44 +08:00
|
|
|
if (unlikely(headlen > skb_headlen(skb))) {
|
2020-11-08 04:50:56 +08:00
|
|
|
atomic_long_inc(&tun->dev->rx_dropped);
|
2017-09-23 04:49:15 +08:00
|
|
|
napi_free_frags(&tfile->napi);
|
2019-03-15 11:19:47 +08:00
|
|
|
rcu_read_unlock();
|
2017-09-23 04:49:15 +08:00
|
|
|
mutex_unlock(&tfile->napi_mutex);
|
|
|
|
WARN_ON(1);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
local_bh_disable();
|
|
|
|
napi_gro_frags(&tfile->napi);
|
|
|
|
local_bh_enable();
|
|
|
|
mutex_unlock(&tfile->napi_mutex);
|
2017-10-19 03:12:09 +08:00
|
|
|
} else if (tfile->napi_enabled) {
|
2017-09-23 04:49:14 +08:00
|
|
|
struct sk_buff_head *queue = &tfile->sk.sk_write_queue;
|
|
|
|
int queue_len;
|
|
|
|
|
|
|
|
spin_lock_bh(&queue->lock);
|
|
|
|
__skb_queue_tail(queue, skb);
|
|
|
|
queue_len = skb_queue_len(queue);
|
|
|
|
spin_unlock(&queue->lock);
|
|
|
|
|
|
|
|
if (!more || queue_len > NAPI_POLL_WEIGHT)
|
|
|
|
napi_schedule(&tfile->napi);
|
|
|
|
|
|
|
|
local_bh_enable();
|
|
|
|
} else if (!IS_ENABLED(CONFIG_4KSTACKS)) {
|
|
|
|
tun_rx_batched(tun, tfile, skb, more);
|
|
|
|
} else {
|
|
|
|
netif_rx_ni(skb);
|
|
|
|
}
|
2019-03-15 11:19:47 +08:00
|
|
|
rcu_read_unlock();
|
2006-09-14 01:24:59 +08:00
|
|
|
|
2020-11-08 04:50:56 +08:00
|
|
|
preempt_disable();
|
|
|
|
dev_sw_netstats_rx_add(tun->dev, len);
|
|
|
|
preempt_enable();
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-12-04 17:31:23 +08:00
|
|
|
if (rxhash)
|
|
|
|
tun_flow_update(tun, rxhash, tfile);
|
|
|
|
|
2012-07-20 17:23:23 +08:00
|
|
|
return total_len;
|
2006-09-14 01:24:59 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-06-20 03:36:49 +08:00
|
|
|
static ssize_t tun_chr_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2009-02-06 13:25:32 +08:00
|
|
|
struct file *file = iocb->ki_filp;
|
2012-11-01 03:45:57 +08:00
|
|
|
struct tun_file *tfile = file->private_data;
|
2017-09-23 22:36:52 +08:00
|
|
|
struct tun_struct *tun = tun_get(tfile);
|
2009-01-20 19:00:40 +08:00
|
|
|
ssize_t result;
|
2020-11-20 22:59:54 +08:00
|
|
|
int noblock = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!tun)
|
|
|
|
return -EBADFD;
|
|
|
|
|
2020-11-20 22:59:54 +08:00
|
|
|
if ((file->f_flags & O_NONBLOCK) || (iocb->ki_flags & IOCB_NOWAIT))
|
|
|
|
noblock = 1;
|
|
|
|
|
|
|
|
result = tun_get_user(tun, tfile, NULL, from, noblock, false);
|
2009-01-20 19:00:40 +08:00
|
|
|
|
|
|
|
tun_put(tun);
|
|
|
|
return result;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2018-01-04 11:14:28 +08:00
|
|
|
static ssize_t tun_put_user_xdp(struct tun_struct *tun,
|
|
|
|
struct tun_file *tfile,
|
2018-04-17 22:45:47 +08:00
|
|
|
struct xdp_frame *xdp_frame,
|
2018-01-04 11:14:28 +08:00
|
|
|
struct iov_iter *iter)
|
|
|
|
{
|
|
|
|
int vnet_hdr_sz = 0;
|
2018-04-17 22:45:47 +08:00
|
|
|
size_t size = xdp_frame->len;
|
2018-01-04 11:14:28 +08:00
|
|
|
size_t ret;
|
|
|
|
|
|
|
|
if (tun->flags & IFF_VNET_HDR) {
|
|
|
|
struct virtio_net_hdr gso = { 0 };
|
|
|
|
|
|
|
|
vnet_hdr_sz = READ_ONCE(tun->vnet_hdr_sz);
|
|
|
|
if (unlikely(iov_iter_count(iter) < vnet_hdr_sz))
|
|
|
|
return -EINVAL;
|
|
|
|
if (unlikely(copy_to_iter(&gso, sizeof(gso), iter) !=
|
|
|
|
sizeof(gso)))
|
|
|
|
return -EFAULT;
|
|
|
|
iov_iter_advance(iter, vnet_hdr_sz - sizeof(gso));
|
|
|
|
}
|
|
|
|
|
2018-04-17 22:45:47 +08:00
|
|
|
ret = copy_to_iter(xdp_frame->data, size, iter) + vnet_hdr_sz;
|
2018-01-04 11:14:28 +08:00
|
|
|
|
2020-11-08 04:50:56 +08:00
|
|
|
preempt_disable();
|
|
|
|
dev_sw_netstats_tx_add(tun->dev, 1, ret);
|
|
|
|
preempt_enable();
|
2018-01-04 11:14:28 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Put packet to the user space buffer */
|
2011-06-08 22:33:08 +08:00
|
|
|
static ssize_t tun_put_user(struct tun_struct *tun,
|
2012-11-01 03:45:57 +08:00
|
|
|
struct tun_file *tfile,
|
2011-06-08 22:33:08 +08:00
|
|
|
struct sk_buff *skb,
|
2014-11-07 21:22:23 +08:00
|
|
|
struct iov_iter *iter)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct tun_pi pi = { 0, skb->protocol };
|
2014-11-07 21:22:23 +08:00
|
|
|
ssize_t total;
|
2014-11-13 16:54:14 +08:00
|
|
|
int vlan_offset = 0;
|
2014-11-03 04:30:13 +08:00
|
|
|
int vlan_hlen = 0;
|
2014-11-03 04:30:14 +08:00
|
|
|
int vnet_hdr_sz = 0;
|
2014-11-03 04:30:13 +08:00
|
|
|
|
2015-01-14 00:13:44 +08:00
|
|
|
if (skb_vlan_tag_present(skb))
|
2014-11-03 04:30:13 +08:00
|
|
|
vlan_hlen = VLAN_HLEN;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-11-19 21:17:31 +08:00
|
|
|
if (tun->flags & IFF_VNET_HDR)
|
2017-02-04 07:20:48 +08:00
|
|
|
vnet_hdr_sz = READ_ONCE(tun->vnet_hdr_sz);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-11-07 21:22:23 +08:00
|
|
|
total = skb->len + vlan_hlen + vnet_hdr_sz;
|
|
|
|
|
2014-11-19 21:17:31 +08:00
|
|
|
if (!(tun->flags & IFF_NO_PI)) {
|
2014-11-07 21:22:23 +08:00
|
|
|
if (iov_iter_count(iter) < sizeof(pi))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2014-11-07 21:22:23 +08:00
|
|
|
total += sizeof(pi);
|
|
|
|
if (iov_iter_count(iter) < total) {
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Packet will be striped */
|
|
|
|
pi.flags |= TUN_PKT_STRIP;
|
|
|
|
}
|
2006-09-14 01:24:59 +08:00
|
|
|
|
2014-11-07 21:22:23 +08:00
|
|
|
if (copy_to_iter(&pi, sizeof(pi), iter) != sizeof(pi))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EFAULT;
|
2006-09-14 01:24:59 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-11-03 04:30:14 +08:00
|
|
|
if (vnet_hdr_sz) {
|
2016-11-19 07:40:40 +08:00
|
|
|
struct virtio_net_hdr gso;
|
2016-06-08 21:09:20 +08:00
|
|
|
|
2014-11-07 21:22:23 +08:00
|
|
|
if (iov_iter_count(iter) < vnet_hdr_sz)
|
2008-07-03 18:48:02 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2016-11-19 07:40:38 +08:00
|
|
|
if (virtio_net_hdr_from_skb(skb, &gso,
|
2018-06-06 23:23:01 +08:00
|
|
|
tun_is_little_endian(tun), true,
|
|
|
|
vlan_hlen)) {
|
2008-07-03 18:48:02 +08:00
|
|
|
struct skb_shared_info *sinfo = skb_shinfo(skb);
|
2016-06-08 21:09:20 +08:00
|
|
|
pr_err("unexpected GSO type: "
|
|
|
|
"0x%x, gso_size %d, hdr_len %d\n",
|
|
|
|
sinfo->gso_type, tun16_to_cpu(tun, gso.gso_size),
|
|
|
|
tun16_to_cpu(tun, gso.hdr_len));
|
|
|
|
print_hex_dump(KERN_ERR, "tun: ",
|
|
|
|
DUMP_PREFIX_NONE,
|
|
|
|
16, 1, skb->head,
|
|
|
|
min((int)tun16_to_cpu(tun, gso.hdr_len), 64), true);
|
|
|
|
WARN_ON_ONCE(1);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2008-07-03 18:48:02 +08:00
|
|
|
|
2014-11-07 21:22:23 +08:00
|
|
|
if (copy_to_iter(&gso, sizeof(gso), iter) != sizeof(gso))
|
2008-07-03 18:48:02 +08:00
|
|
|
return -EFAULT;
|
2014-11-13 16:54:14 +08:00
|
|
|
|
|
|
|
iov_iter_advance(iter, vnet_hdr_sz - sizeof(gso));
|
2008-07-03 18:48:02 +08:00
|
|
|
}
|
|
|
|
|
2014-11-03 04:30:13 +08:00
|
|
|
if (vlan_hlen) {
|
2014-11-07 21:22:23 +08:00
|
|
|
int ret;
|
2018-01-16 16:31:02 +08:00
|
|
|
struct veth veth;
|
2013-07-25 13:00:33 +08:00
|
|
|
|
|
|
|
veth.h_vlan_proto = skb->vlan_proto;
|
2015-01-14 00:13:44 +08:00
|
|
|
veth.h_vlan_TCI = htons(skb_vlan_tag_get(skb));
|
2013-07-25 13:00:33 +08:00
|
|
|
|
|
|
|
vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto);
|
|
|
|
|
2014-11-07 21:22:23 +08:00
|
|
|
ret = skb_copy_datagram_iter(skb, 0, iter, vlan_offset);
|
|
|
|
if (ret || !iov_iter_count(iter))
|
2013-07-25 13:00:33 +08:00
|
|
|
goto done;
|
|
|
|
|
2014-11-07 21:22:23 +08:00
|
|
|
ret = copy_to_iter(&veth, sizeof(veth), iter);
|
|
|
|
if (ret != sizeof(veth) || !iov_iter_count(iter))
|
2013-07-25 13:00:33 +08:00
|
|
|
goto done;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-11-07 21:22:23 +08:00
|
|
|
skb_copy_datagram_iter(skb, vlan_offset, iter, skb->len - vlan_offset);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-07-25 13:00:33 +08:00
|
|
|
done:
|
2016-04-13 16:52:20 +08:00
|
|
|
/* caller is in process context, */
|
2020-11-08 04:50:56 +08:00
|
|
|
preempt_disable();
|
|
|
|
dev_sw_netstats_tx_add(tun->dev, 1, skb->len + vlan_hlen);
|
|
|
|
preempt_enable();
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
2018-01-04 11:14:28 +08:00
|
|
|
static void *tun_ring_recv(struct tun_file *tfile, int noblock, int *err)
|
2016-06-30 14:45:36 +08:00
|
|
|
{
|
|
|
|
DECLARE_WAITQUEUE(wait, current);
|
2018-01-04 11:14:28 +08:00
|
|
|
void *ptr = NULL;
|
2016-07-04 13:53:38 +08:00
|
|
|
int error = 0;
|
2016-06-30 14:45:36 +08:00
|
|
|
|
2018-01-04 11:14:28 +08:00
|
|
|
ptr = ptr_ring_consume(&tfile->tx_ring);
|
|
|
|
if (ptr)
|
2016-06-30 14:45:36 +08:00
|
|
|
goto out;
|
|
|
|
if (noblock) {
|
2016-07-04 13:53:38 +08:00
|
|
|
error = -EAGAIN;
|
2016-06-30 14:45:36 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2019-07-06 03:14:16 +08:00
|
|
|
add_wait_queue(&tfile->socket.wq.wait, &wait);
|
2016-06-30 14:45:36 +08:00
|
|
|
|
|
|
|
while (1) {
|
2019-02-23 19:53:13 +08:00
|
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
2018-01-04 11:14:28 +08:00
|
|
|
ptr = ptr_ring_consume(&tfile->tx_ring);
|
|
|
|
if (ptr)
|
2016-06-30 14:45:36 +08:00
|
|
|
break;
|
|
|
|
if (signal_pending(current)) {
|
2016-07-04 13:53:38 +08:00
|
|
|
error = -ERESTARTSYS;
|
2016-06-30 14:45:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (tfile->socket.sk->sk_shutdown & RCV_SHUTDOWN) {
|
2016-07-04 13:53:38 +08:00
|
|
|
error = -EFAULT;
|
2016-06-30 14:45:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
schedule();
|
|
|
|
}
|
|
|
|
|
2019-02-26 04:13:13 +08:00
|
|
|
__set_current_state(TASK_RUNNING);
|
2019-07-06 03:14:16 +08:00
|
|
|
remove_wait_queue(&tfile->socket.wq.wait, &wait);
|
2016-06-30 14:45:36 +08:00
|
|
|
|
|
|
|
out:
|
2016-07-04 13:53:38 +08:00
|
|
|
*err = error;
|
2018-01-04 11:14:28 +08:00
|
|
|
return ptr;
|
2016-06-30 14:45:36 +08:00
|
|
|
}
|
|
|
|
|
2012-11-01 03:45:57 +08:00
|
|
|
static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile,
|
2014-11-08 02:52:07 +08:00
|
|
|
struct iov_iter *to,
|
2018-01-04 11:14:28 +08:00
|
|
|
int noblock, void *ptr)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2014-11-08 02:52:07 +08:00
|
|
|
ssize_t ret;
|
2016-06-30 14:45:36 +08:00
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-12-01 18:10:37 +08:00
|
|
|
if (!iov_iter_count(to)) {
|
2018-01-04 11:14:28 +08:00
|
|
|
tun_ptr_free(ptr);
|
2014-11-08 02:52:07 +08:00
|
|
|
return 0;
|
2017-12-01 18:10:37 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2018-01-04 11:14:28 +08:00
|
|
|
if (!ptr) {
|
2017-05-17 12:14:43 +08:00
|
|
|
/* Read frames from ring */
|
2018-01-04 11:14:28 +08:00
|
|
|
ptr = tun_ring_recv(tfile, noblock, &err);
|
|
|
|
if (!ptr)
|
2017-05-17 12:14:43 +08:00
|
|
|
return err;
|
|
|
|
}
|
2014-11-07 21:22:23 +08:00
|
|
|
|
2018-04-17 22:45:47 +08:00
|
|
|
if (tun_is_xdp_frame(ptr)) {
|
|
|
|
struct xdp_frame *xdpf = tun_ptr_to_xdp(ptr);
|
2018-01-04 11:14:28 +08:00
|
|
|
|
2018-04-17 22:45:47 +08:00
|
|
|
ret = tun_put_user_xdp(tun, tfile, xdpf, to);
|
xdp: transition into using xdp_frame for return API
Changing API xdp_return_frame() to take struct xdp_frame as argument,
seems like a natural choice. But there are some subtle performance
details here that needs extra care, which is a deliberate choice.
When de-referencing xdp_frame on a remote CPU during DMA-TX
completion, result in the cache-line is change to "Shared"
state. Later when the page is reused for RX, then this xdp_frame
cache-line is written, which change the state to "Modified".
This situation already happens (naturally) for, virtio_net, tun and
cpumap as the xdp_frame pointer is the queued object. In tun and
cpumap, the ptr_ring is used for efficiently transferring cache-lines
(with pointers) between CPUs. Thus, the only option is to
de-referencing xdp_frame.
It is only the ixgbe driver that had an optimization, in which it can
avoid doing the de-reference of xdp_frame. The driver already have
TX-ring queue, which (in case of remote DMA-TX completion) have to be
transferred between CPUs anyhow. In this data area, we stored a
struct xdp_mem_info and a data pointer, which allowed us to avoid
de-referencing xdp_frame.
To compensate for this, a prefetchw is used for telling the cache
coherency protocol about our access pattern. My benchmarks show that
this prefetchw is enough to compensate the ixgbe driver.
V7: Adjust for commit d9314c474d4f ("i40e: add support for XDP_REDIRECT")
V8: Adjust for commit bd658dda4237 ("net/mlx5e: Separate dma base address
and offset in dma_sync call")
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2018-04-17 22:46:32 +08:00
|
|
|
xdp_return_frame(xdpf);
|
2018-01-04 11:14:28 +08:00
|
|
|
} else {
|
|
|
|
struct sk_buff *skb = ptr;
|
|
|
|
|
|
|
|
ret = tun_put_user(tun, tfile, skb, to);
|
|
|
|
if (unlikely(ret < 0))
|
|
|
|
kfree_skb(skb);
|
|
|
|
else
|
|
|
|
consume_skb(skb);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-01-14 14:17:09 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-11-08 02:52:07 +08:00
|
|
|
static ssize_t tun_chr_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
2010-01-14 14:17:09 +08:00
|
|
|
{
|
|
|
|
struct file *file = iocb->ki_filp;
|
|
|
|
struct tun_file *tfile = file->private_data;
|
2017-09-23 22:36:52 +08:00
|
|
|
struct tun_struct *tun = tun_get(tfile);
|
2014-11-08 02:52:07 +08:00
|
|
|
ssize_t len = iov_iter_count(to), ret;
|
2020-11-20 22:59:54 +08:00
|
|
|
int noblock = 0;
|
2010-01-14 14:17:09 +08:00
|
|
|
|
|
|
|
if (!tun)
|
|
|
|
return -EBADFD;
|
2020-11-20 22:59:54 +08:00
|
|
|
|
|
|
|
if ((file->f_flags & O_NONBLOCK) || (iocb->ki_flags & IOCB_NOWAIT))
|
|
|
|
noblock = 1;
|
|
|
|
|
|
|
|
ret = tun_do_read(tun, tfile, to, noblock, NULL);
|
2013-12-11 11:05:45 +08:00
|
|
|
ret = min_t(ssize_t, ret, len);
|
2013-12-06 14:16:51 +08:00
|
|
|
if (ret > 0)
|
|
|
|
iocb->ki_pos = ret;
|
2009-01-20 19:00:40 +08:00
|
|
|
tun_put(tun);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-01-16 16:31:01 +08:00
|
|
|
static void tun_prog_free(struct rcu_head *rcu)
|
2017-12-04 17:31:23 +08:00
|
|
|
{
|
2018-01-16 16:31:01 +08:00
|
|
|
struct tun_prog *prog = container_of(rcu, struct tun_prog, rcu);
|
2017-12-04 17:31:23 +08:00
|
|
|
|
|
|
|
bpf_prog_destroy(prog->prog);
|
|
|
|
kfree(prog);
|
|
|
|
}
|
|
|
|
|
2018-01-22 10:55:38 +08:00
|
|
|
static int __tun_set_ebpf(struct tun_struct *tun,
|
|
|
|
struct tun_prog __rcu **prog_p,
|
2018-01-16 16:31:01 +08:00
|
|
|
struct bpf_prog *prog)
|
2017-12-04 17:31:23 +08:00
|
|
|
{
|
2018-01-16 16:31:01 +08:00
|
|
|
struct tun_prog *old, *new = NULL;
|
2017-12-04 17:31:23 +08:00
|
|
|
|
|
|
|
if (prog) {
|
|
|
|
new = kmalloc(sizeof(*new), GFP_KERNEL);
|
|
|
|
if (!new)
|
|
|
|
return -ENOMEM;
|
|
|
|
new->prog = prog;
|
|
|
|
}
|
|
|
|
|
2017-12-08 12:02:30 +08:00
|
|
|
spin_lock_bh(&tun->lock);
|
2018-01-16 16:31:01 +08:00
|
|
|
old = rcu_dereference_protected(*prog_p,
|
2017-12-08 12:02:30 +08:00
|
|
|
lockdep_is_held(&tun->lock));
|
2018-01-16 16:31:01 +08:00
|
|
|
rcu_assign_pointer(*prog_p, new);
|
2017-12-08 12:02:30 +08:00
|
|
|
spin_unlock_bh(&tun->lock);
|
2017-12-04 17:31:23 +08:00
|
|
|
|
|
|
|
if (old)
|
2018-01-16 16:31:01 +08:00
|
|
|
call_rcu(&old->rcu, tun_prog_free);
|
2017-12-04 17:31:23 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-01 03:46:02 +08:00
|
|
|
static void tun_free_netdev(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
2012-12-14 07:53:30 +08:00
|
|
|
BUG_ON(!(list_empty(&tun->disabled)));
|
2019-10-08 03:21:05 +08:00
|
|
|
|
2020-11-08 04:50:56 +08:00
|
|
|
free_percpu(dev->tstats);
|
|
|
|
/* We clear tstats so that tun_set_iff() can tell if
|
2019-10-08 03:21:05 +08:00
|
|
|
* tun_free_netdev() has been called from register_netdevice().
|
|
|
|
*/
|
2020-11-08 04:50:56 +08:00
|
|
|
dev->tstats = NULL;
|
2019-10-08 03:21:05 +08:00
|
|
|
|
2012-11-01 03:46:02 +08:00
|
|
|
tun_flow_uninit(tun);
|
2013-01-14 15:12:19 +08:00
|
|
|
security_tun_dev_free_security(tun->security);
|
2018-01-16 16:31:01 +08:00
|
|
|
__tun_set_ebpf(tun, &tun->steering_prog, NULL);
|
2018-01-16 16:31:02 +08:00
|
|
|
__tun_set_ebpf(tun, &tun->filter_prog, NULL);
|
2012-11-01 03:46:02 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static void tun_setup(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
2012-02-08 08:48:55 +08:00
|
|
|
tun->owner = INVALID_UID;
|
|
|
|
tun->group = INVALID_GID;
|
2018-06-03 05:49:53 +08:00
|
|
|
tun_default_link_ksettings(dev, &tun->link_ksettings);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
dev->ethtool_ops = &tun_ethtool_ops;
|
net: Fix inconsistent teardown and release of private netdev state.
Network devices can allocate reasources and private memory using
netdev_ops->ndo_init(). However, the release of these resources
can occur in one of two different places.
Either netdev_ops->ndo_uninit() or netdev->destructor().
The decision of which operation frees the resources depends upon
whether it is necessary for all netdev refs to be released before it
is safe to perform the freeing.
netdev_ops->ndo_uninit() presumably can occur right after the
NETDEV_UNREGISTER notifier completes and the unicast and multicast
address lists are flushed.
netdev->destructor(), on the other hand, does not run until the
netdev references all go away.
Further complicating the situation is that netdev->destructor()
almost universally does also a free_netdev().
This creates a problem for the logic in register_netdevice().
Because all callers of register_netdevice() manage the freeing
of the netdev, and invoke free_netdev(dev) if register_netdevice()
fails.
If netdev_ops->ndo_init() succeeds, but something else fails inside
of register_netdevice(), it does call ndo_ops->ndo_uninit(). But
it is not able to invoke netdev->destructor().
This is because netdev->destructor() will do a free_netdev() and
then the caller of register_netdevice() will do the same.
However, this means that the resources that would normally be released
by netdev->destructor() will not be.
Over the years drivers have added local hacks to deal with this, by
invoking their destructor parts by hand when register_netdevice()
fails.
Many drivers do not try to deal with this, and instead we have leaks.
Let's close this hole by formalizing the distinction between what
private things need to be freed up by netdev->destructor() and whether
the driver needs unregister_netdevice() to perform the free_netdev().
netdev->priv_destructor() performs all actions to free up the private
resources that used to be freed by netdev->destructor(), except for
free_netdev().
netdev->needs_free_netdev is a boolean that indicates whether
free_netdev() should be done at the end of unregister_netdevice().
Now, register_netdevice() can sanely release all resources after
ndo_ops->ndo_init() succeeds, by invoking both ndo_ops->ndo_uninit()
and netdev->priv_destructor().
And at the end of unregister_netdevice(), we invoke
netdev->priv_destructor() and optionally call free_netdev().
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-05-09 00:52:56 +08:00
|
|
|
dev->needs_free_netdev = true;
|
|
|
|
dev->priv_destructor = tun_free_netdev;
|
2016-04-08 13:26:48 +08:00
|
|
|
/* We prefer our own queue length */
|
|
|
|
dev->tx_queue_len = TUN_READQ_SIZE;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-01-22 08:02:16 +08:00
|
|
|
/* Trivial set of netlink ops to allow deleting tun or tap
|
|
|
|
* device with netlink.
|
|
|
|
*/
|
2017-06-26 05:56:01 +08:00
|
|
|
static int tun_validate(struct nlattr *tb[], struct nlattr *data[],
|
|
|
|
struct netlink_ext_ack *extack)
|
2009-01-22 08:02:16 +08:00
|
|
|
{
|
2018-11-29 21:45:39 +08:00
|
|
|
NL_SET_ERR_MSG(extack,
|
|
|
|
"tun/tap creation via rtnetlink is not supported.");
|
|
|
|
return -EOPNOTSUPP;
|
2009-01-22 08:02:16 +08:00
|
|
|
}
|
|
|
|
|
2018-02-16 18:03:07 +08:00
|
|
|
static size_t tun_get_size(const struct net_device *dev)
|
|
|
|
{
|
|
|
|
BUILD_BUG_ON(sizeof(u32) != sizeof(uid_t));
|
|
|
|
BUILD_BUG_ON(sizeof(u32) != sizeof(gid_t));
|
|
|
|
|
|
|
|
return nla_total_size(sizeof(uid_t)) + /* OWNER */
|
|
|
|
nla_total_size(sizeof(gid_t)) + /* GROUP */
|
|
|
|
nla_total_size(sizeof(u8)) + /* TYPE */
|
|
|
|
nla_total_size(sizeof(u8)) + /* PI */
|
|
|
|
nla_total_size(sizeof(u8)) + /* VNET_HDR */
|
|
|
|
nla_total_size(sizeof(u8)) + /* PERSIST */
|
|
|
|
nla_total_size(sizeof(u8)) + /* MULTI_QUEUE */
|
|
|
|
nla_total_size(sizeof(u32)) + /* NUM_QUEUES */
|
|
|
|
nla_total_size(sizeof(u32)) + /* NUM_DISABLED_QUEUES */
|
|
|
|
0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tun_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
|
|
|
if (nla_put_u8(skb, IFLA_TUN_TYPE, tun->flags & TUN_TYPE_MASK))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (uid_valid(tun->owner) &&
|
|
|
|
nla_put_u32(skb, IFLA_TUN_OWNER,
|
|
|
|
from_kuid_munged(current_user_ns(), tun->owner)))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (gid_valid(tun->group) &&
|
|
|
|
nla_put_u32(skb, IFLA_TUN_GROUP,
|
|
|
|
from_kgid_munged(current_user_ns(), tun->group)))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (nla_put_u8(skb, IFLA_TUN_PI, !(tun->flags & IFF_NO_PI)))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (nla_put_u8(skb, IFLA_TUN_VNET_HDR, !!(tun->flags & IFF_VNET_HDR)))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (nla_put_u8(skb, IFLA_TUN_PERSIST, !!(tun->flags & IFF_PERSIST)))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (nla_put_u8(skb, IFLA_TUN_MULTI_QUEUE,
|
|
|
|
!!(tun->flags & IFF_MULTI_QUEUE)))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (tun->flags & IFF_MULTI_QUEUE) {
|
|
|
|
if (nla_put_u32(skb, IFLA_TUN_NUM_QUEUES, tun->numqueues))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (nla_put_u32(skb, IFLA_TUN_NUM_DISABLED_QUEUES,
|
|
|
|
tun->numdisabled))
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
return -EMSGSIZE;
|
|
|
|
}
|
|
|
|
|
2009-01-22 08:02:16 +08:00
|
|
|
static struct rtnl_link_ops tun_link_ops __read_mostly = {
|
|
|
|
.kind = DRV_NAME,
|
|
|
|
.priv_size = sizeof(struct tun_struct),
|
|
|
|
.setup = tun_setup,
|
|
|
|
.validate = tun_validate,
|
2018-02-16 18:03:07 +08:00
|
|
|
.get_size = tun_get_size,
|
|
|
|
.fill_info = tun_fill_info,
|
2009-01-22 08:02:16 +08:00
|
|
|
};
|
|
|
|
|
2009-02-06 13:25:32 +08:00
|
|
|
static void tun_sock_write_space(struct sock *sk)
|
|
|
|
{
|
2012-11-01 03:45:57 +08:00
|
|
|
struct tun_file *tfile;
|
2010-04-29 19:01:49 +08:00
|
|
|
wait_queue_head_t *wqueue;
|
2009-02-06 13:25:32 +08:00
|
|
|
|
|
|
|
if (!sock_writeable(sk))
|
|
|
|
return;
|
|
|
|
|
2015-11-30 12:03:10 +08:00
|
|
|
if (!test_and_clear_bit(SOCKWQ_ASYNC_NOSPACE, &sk->sk_socket->flags))
|
2009-02-06 13:25:32 +08:00
|
|
|
return;
|
|
|
|
|
2010-04-29 19:01:49 +08:00
|
|
|
wqueue = sk_sleep(sk);
|
|
|
|
if (wqueue && waitqueue_active(wqueue))
|
2018-02-12 06:34:03 +08:00
|
|
|
wake_up_interruptible_sync_poll(wqueue, EPOLLOUT |
|
|
|
|
EPOLLWRNORM | EPOLLWRBAND);
|
2009-06-04 12:45:55 +08:00
|
|
|
|
2012-11-01 03:45:57 +08:00
|
|
|
tfile = container_of(sk, struct tun_file, sk);
|
|
|
|
kill_fasync(&tfile->fasync, SIGIO, POLL_OUT);
|
2009-02-06 13:25:32 +08:00
|
|
|
}
|
|
|
|
|
2018-11-15 17:43:10 +08:00
|
|
|
static void tun_put_page(struct tun_page *tpage)
|
|
|
|
{
|
|
|
|
if (tpage->page)
|
|
|
|
__page_frag_cache_drain(tpage->page, tpage->count);
|
|
|
|
}
|
|
|
|
|
2018-09-12 11:17:07 +08:00
|
|
|
static int tun_xdp_one(struct tun_struct *tun,
|
|
|
|
struct tun_file *tfile,
|
2018-11-15 17:43:10 +08:00
|
|
|
struct xdp_buff *xdp, int *flush,
|
|
|
|
struct tun_page *tpage)
|
2018-09-12 11:17:07 +08:00
|
|
|
{
|
2018-12-03 17:09:24 +08:00
|
|
|
unsigned int datasize = xdp->data_end - xdp->data;
|
2018-09-12 11:17:07 +08:00
|
|
|
struct tun_xdp_hdr *hdr = xdp->data_hard_start;
|
|
|
|
struct virtio_net_hdr *gso = &hdr->gso;
|
|
|
|
struct bpf_prog *xdp_prog;
|
|
|
|
struct sk_buff *skb = NULL;
|
|
|
|
u32 rxhash = 0, act;
|
|
|
|
int buflen = hdr->buflen;
|
|
|
|
int err = 0;
|
|
|
|
bool skb_xdp = false;
|
2018-11-15 17:43:10 +08:00
|
|
|
struct page *page;
|
2018-09-12 11:17:07 +08:00
|
|
|
|
|
|
|
xdp_prog = rcu_dereference(tun->xdp_prog);
|
|
|
|
if (xdp_prog) {
|
|
|
|
if (gso->gso_type) {
|
|
|
|
skb_xdp = true;
|
|
|
|
goto build;
|
|
|
|
}
|
|
|
|
xdp_set_data_meta_invalid(xdp);
|
|
|
|
xdp->rxq = &tfile->xdp_rxq;
|
2020-05-14 18:50:34 +08:00
|
|
|
xdp->frame_sz = buflen;
|
2018-09-12 11:17:07 +08:00
|
|
|
|
|
|
|
act = bpf_prog_run_xdp(xdp_prog, xdp);
|
|
|
|
err = tun_xdp_act(tun, xdp_prog, xdp, act);
|
|
|
|
if (err < 0) {
|
|
|
|
put_page(virt_to_head_page(xdp->data));
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (err) {
|
|
|
|
case XDP_REDIRECT:
|
|
|
|
*flush = true;
|
2020-08-24 06:36:59 +08:00
|
|
|
fallthrough;
|
2018-09-12 11:17:07 +08:00
|
|
|
case XDP_TX:
|
|
|
|
return 0;
|
|
|
|
case XDP_PASS:
|
|
|
|
break;
|
|
|
|
default:
|
2018-11-15 17:43:10 +08:00
|
|
|
page = virt_to_head_page(xdp->data);
|
|
|
|
if (tpage->page == page) {
|
|
|
|
++tpage->count;
|
|
|
|
} else {
|
|
|
|
tun_put_page(tpage);
|
|
|
|
tpage->page = page;
|
|
|
|
tpage->count = 1;
|
|
|
|
}
|
2018-09-12 11:17:07 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
build:
|
|
|
|
skb = build_skb(xdp->data_hard_start, buflen);
|
|
|
|
if (!skb) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
skb_reserve(skb, xdp->data - xdp->data_hard_start);
|
|
|
|
skb_put(skb, xdp->data_end - xdp->data);
|
|
|
|
|
|
|
|
if (virtio_net_hdr_to_skb(skb, gso, tun_is_little_endian(tun))) {
|
2020-11-08 04:50:56 +08:00
|
|
|
atomic_long_inc(&tun->rx_frame_errors);
|
2018-09-12 11:17:07 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
skb->protocol = eth_type_trans(skb, tun->dev);
|
|
|
|
skb_reset_network_header(skb);
|
net: Don't set transport offset to invalid value
If the socket was created with socket(AF_PACKET, SOCK_RAW, 0),
skb->protocol will be unset, __skb_flow_dissect() will fail, and
skb_probe_transport_header() will fall back to the offset_hint, making
the resulting skb_transport_offset incorrect.
If, however, there is no transport header in the packet,
transport_header shouldn't be set to an arbitrary value.
Fix it by leaving the transport offset unset if it couldn't be found, to
be explicit rather than to fill it with some wrong value. It changes the
behavior, but if some code relied on the old behavior, it would be
broken anyway, as the old one is incorrect.
Signed-off-by: Maxim Mikityanskiy <maximmi@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-02-21 20:39:57 +08:00
|
|
|
skb_probe_transport_header(skb);
|
2020-04-11 00:20:59 +08:00
|
|
|
skb_record_rx_queue(skb, tfile->queue_index);
|
2018-09-12 11:17:07 +08:00
|
|
|
|
|
|
|
if (skb_xdp) {
|
|
|
|
err = do_xdp_generic(xdp_prog, skb);
|
|
|
|
if (err != XDP_PASS)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2018-11-07 17:34:36 +08:00
|
|
|
if (!rcu_dereference(tun->steering_prog) && tun->numqueues > 1 &&
|
|
|
|
!tfile->detached)
|
2018-09-12 11:17:07 +08:00
|
|
|
rxhash = __skb_get_hash_symmetric(skb);
|
|
|
|
|
|
|
|
netif_receive_skb(skb);
|
|
|
|
|
2020-11-08 04:50:56 +08:00
|
|
|
/* No need to disable preemption here since this function is
|
2018-12-11 10:43:07 +08:00
|
|
|
* always called with bh disabled
|
|
|
|
*/
|
2020-11-08 04:50:56 +08:00
|
|
|
dev_sw_netstats_rx_add(tun->dev, datasize);
|
2018-09-12 11:17:07 +08:00
|
|
|
|
|
|
|
if (rxhash)
|
|
|
|
tun_flow_update(tun, rxhash, tfile);
|
|
|
|
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2015-03-02 15:37:48 +08:00
|
|
|
static int tun_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len)
|
2010-01-14 14:17:09 +08:00
|
|
|
{
|
2018-09-12 11:17:07 +08:00
|
|
|
int ret, i;
|
2012-11-01 03:45:57 +08:00
|
|
|
struct tun_file *tfile = container_of(sock, struct tun_file, socket);
|
2017-09-23 22:36:52 +08:00
|
|
|
struct tun_struct *tun = tun_get(tfile);
|
2018-09-12 11:17:06 +08:00
|
|
|
struct tun_msg_ctl *ctl = m->msg_control;
|
2018-09-12 11:17:07 +08:00
|
|
|
struct xdp_buff *xdp;
|
2012-11-01 03:45:57 +08:00
|
|
|
|
|
|
|
if (!tun)
|
|
|
|
return -EBADFD;
|
2014-06-20 03:36:49 +08:00
|
|
|
|
2018-09-12 11:17:07 +08:00
|
|
|
if (ctl && (ctl->type == TUN_MSG_PTR)) {
|
2018-11-18 08:53:46 +08:00
|
|
|
struct tun_page tpage;
|
2018-09-12 11:17:07 +08:00
|
|
|
int n = ctl->num;
|
|
|
|
int flush = 0;
|
|
|
|
|
2018-11-18 08:53:46 +08:00
|
|
|
memset(&tpage, 0, sizeof(tpage));
|
|
|
|
|
2018-09-12 11:17:07 +08:00
|
|
|
local_bh_disable();
|
|
|
|
rcu_read_lock();
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
xdp = &((struct xdp_buff *)ctl->ptr)[i];
|
2018-11-15 17:43:10 +08:00
|
|
|
tun_xdp_one(tun, tfile, xdp, &flush, &tpage);
|
2018-09-12 11:17:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (flush)
|
xdp: Use bulking for non-map XDP_REDIRECT and consolidate code paths
Since the bulk queue used by XDP_REDIRECT now lives in struct net_device,
we can re-use the bulking for the non-map version of the bpf_redirect()
helper. This is a simple matter of having xdp_do_redirect_slow() queue the
frame on the bulk queue instead of sending it out with __bpf_tx_xdp().
Unfortunately we can't make the bpf_redirect() helper return an error if
the ifindex doesn't exit (as bpf_redirect_map() does), because we don't
have a reference to the network namespace of the ingress device at the time
the helper is called. So we have to leave it as-is and keep the device
lookup in xdp_do_redirect_slow().
Since this leaves less reason to have the non-map redirect code in a
separate function, so we get rid of the xdp_do_redirect_slow() function
entirely. This does lose us the tracepoint disambiguation, but fortunately
the xdp_redirect and xdp_redirect_map tracepoints use the same tracepoint
entry structures. This means both can contain a map index, so we can just
amend the tracepoint definitions so we always emit the xdp_redirect(_err)
tracepoints, but with the map ID only populated if a map is present. This
means we retire the xdp_redirect_map(_err) tracepoints entirely, but keep
the definitions around in case someone is still listening for them.
With this change, the performance of the xdp_redirect sample program goes
from 5Mpps to 8.4Mpps (a 68% increase).
Since the flush functions are no longer map-specific, rename the flush()
functions to drop _map from their names. One of the renamed functions is
the xdp_do_flush_map() callback used in all the xdp-enabled drivers. To
keep from having to update all drivers, use a #define to keep the old name
working, and only update the virtual drivers in this patch.
Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: John Fastabend <john.fastabend@gmail.com>
Link: https://lore.kernel.org/bpf/157918768505.1458396.17518057312953572912.stgit@toke.dk
2020-01-16 23:14:45 +08:00
|
|
|
xdp_do_flush();
|
2018-09-12 11:17:07 +08:00
|
|
|
|
|
|
|
rcu_read_unlock();
|
|
|
|
local_bh_enable();
|
|
|
|
|
2018-11-15 17:43:10 +08:00
|
|
|
tun_put_page(&tpage);
|
|
|
|
|
2018-09-12 11:17:07 +08:00
|
|
|
ret = total_len;
|
|
|
|
goto out;
|
|
|
|
}
|
2018-09-12 11:17:06 +08:00
|
|
|
|
|
|
|
ret = tun_get_user(tun, tfile, ctl ? ctl->ptr : NULL, &m->msg_iter,
|
2017-01-18 15:02:03 +08:00
|
|
|
m->msg_flags & MSG_DONTWAIT,
|
|
|
|
m->msg_flags & MSG_MORE);
|
2018-09-12 11:17:07 +08:00
|
|
|
out:
|
2012-11-01 03:45:57 +08:00
|
|
|
tun_put(tun);
|
|
|
|
return ret;
|
2010-01-14 14:17:09 +08:00
|
|
|
}
|
|
|
|
|
2015-03-02 15:37:48 +08:00
|
|
|
static int tun_recvmsg(struct socket *sock, struct msghdr *m, size_t total_len,
|
2010-01-14 14:17:09 +08:00
|
|
|
int flags)
|
|
|
|
{
|
2012-11-01 03:45:57 +08:00
|
|
|
struct tun_file *tfile = container_of(sock, struct tun_file, socket);
|
2017-09-23 22:36:52 +08:00
|
|
|
struct tun_struct *tun = tun_get(tfile);
|
2018-01-04 11:14:28 +08:00
|
|
|
void *ptr = m->msg_control;
|
2010-01-14 14:17:09 +08:00
|
|
|
int ret;
|
2012-11-01 03:45:57 +08:00
|
|
|
|
2017-12-01 18:10:37 +08:00
|
|
|
if (!tun) {
|
|
|
|
ret = -EBADFD;
|
2018-01-04 11:14:28 +08:00
|
|
|
goto out_free;
|
2017-12-01 18:10:37 +08:00
|
|
|
}
|
2012-11-01 03:45:57 +08:00
|
|
|
|
2013-07-20 01:40:10 +08:00
|
|
|
if (flags & ~(MSG_DONTWAIT|MSG_TRUNC|MSG_ERRQUEUE)) {
|
2013-04-25 05:59:23 +08:00
|
|
|
ret = -EINVAL;
|
2017-12-01 18:10:37 +08:00
|
|
|
goto out_put_tun;
|
2013-04-25 05:59:23 +08:00
|
|
|
}
|
2013-07-20 01:40:10 +08:00
|
|
|
if (flags & MSG_ERRQUEUE) {
|
|
|
|
ret = sock_recv_errqueue(sock->sk, m, total_len,
|
|
|
|
SOL_PACKET, TUN_TX_TIMESTAMP);
|
|
|
|
goto out;
|
|
|
|
}
|
2018-01-04 11:14:28 +08:00
|
|
|
ret = tun_do_read(tun, tfile, &m->msg_iter, flags & MSG_DONTWAIT, ptr);
|
2014-12-26 15:05:03 +08:00
|
|
|
if (ret > (ssize_t)total_len) {
|
2013-12-11 11:05:45 +08:00
|
|
|
m->msg_flags |= MSG_TRUNC;
|
|
|
|
ret = flags & MSG_TRUNC ? ret : total_len;
|
|
|
|
}
|
2013-04-25 05:59:23 +08:00
|
|
|
out:
|
2012-11-01 03:45:57 +08:00
|
|
|
tun_put(tun);
|
2010-01-14 14:17:09 +08:00
|
|
|
return ret;
|
2017-12-01 18:10:37 +08:00
|
|
|
|
|
|
|
out_put_tun:
|
|
|
|
tun_put(tun);
|
2018-01-04 11:14:28 +08:00
|
|
|
out_free:
|
|
|
|
tun_ptr_free(ptr);
|
2017-12-01 18:10:37 +08:00
|
|
|
return ret;
|
2010-01-14 14:17:09 +08:00
|
|
|
}
|
|
|
|
|
2018-01-04 11:14:28 +08:00
|
|
|
static int tun_ptr_peek_len(void *ptr)
|
|
|
|
{
|
|
|
|
if (likely(ptr)) {
|
2018-04-17 22:45:47 +08:00
|
|
|
if (tun_is_xdp_frame(ptr)) {
|
|
|
|
struct xdp_frame *xdpf = tun_ptr_to_xdp(ptr);
|
2018-01-04 11:14:28 +08:00
|
|
|
|
2018-04-17 22:45:47 +08:00
|
|
|
return xdpf->len;
|
2018-01-04 11:14:28 +08:00
|
|
|
}
|
|
|
|
return __skb_array_len_with_tag(ptr);
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-30 14:45:36 +08:00
|
|
|
static int tun_peek_len(struct socket *sock)
|
|
|
|
{
|
|
|
|
struct tun_file *tfile = container_of(sock, struct tun_file, socket);
|
|
|
|
struct tun_struct *tun;
|
|
|
|
int ret = 0;
|
|
|
|
|
2017-09-23 22:36:52 +08:00
|
|
|
tun = tun_get(tfile);
|
2016-06-30 14:45:36 +08:00
|
|
|
if (!tun)
|
|
|
|
return 0;
|
|
|
|
|
2018-01-04 11:14:28 +08:00
|
|
|
ret = PTR_RING_PEEK_CALL(&tfile->tx_ring, tun_ptr_peek_len);
|
2016-06-30 14:45:36 +08:00
|
|
|
tun_put(tun);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-01-14 14:17:09 +08:00
|
|
|
/* Ops structure to mimic raw sockets with tun */
|
|
|
|
static const struct proto_ops tun_socket_ops = {
|
2016-06-30 14:45:36 +08:00
|
|
|
.peek_len = tun_peek_len,
|
2010-01-14 14:17:09 +08:00
|
|
|
.sendmsg = tun_sendmsg,
|
|
|
|
.recvmsg = tun_recvmsg,
|
|
|
|
};
|
|
|
|
|
2009-02-06 13:25:32 +08:00
|
|
|
static struct proto tun_proto = {
|
|
|
|
.name = "tun",
|
|
|
|
.owner = THIS_MODULE,
|
2012-11-01 03:45:57 +08:00
|
|
|
.obj_size = sizeof(struct tun_file),
|
2009-02-06 13:25:32 +08:00
|
|
|
};
|
2009-01-22 08:02:16 +08:00
|
|
|
|
2009-05-10 13:54:21 +08:00
|
|
|
static int tun_flags(struct tun_struct *tun)
|
|
|
|
{
|
2014-11-19 20:44:40 +08:00
|
|
|
return tun->flags & (TUN_FEATURES | IFF_PERSIST | IFF_TUN | IFF_TAP);
|
2009-05-10 13:54:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t tun_show_flags(struct device *dev, struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(to_net_dev(dev));
|
|
|
|
return sprintf(buf, "0x%x\n", tun_flags(tun));
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t tun_show_owner(struct device *dev, struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(to_net_dev(dev));
|
2012-02-08 08:48:55 +08:00
|
|
|
return uid_valid(tun->owner)?
|
|
|
|
sprintf(buf, "%u\n",
|
|
|
|
from_kuid_munged(current_user_ns(), tun->owner)):
|
|
|
|
sprintf(buf, "-1\n");
|
2009-05-10 13:54:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t tun_show_group(struct device *dev, struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(to_net_dev(dev));
|
2012-02-08 08:48:55 +08:00
|
|
|
return gid_valid(tun->group) ?
|
|
|
|
sprintf(buf, "%u\n",
|
|
|
|
from_kgid_munged(current_user_ns(), tun->group)):
|
|
|
|
sprintf(buf, "-1\n");
|
2009-05-10 13:54:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static DEVICE_ATTR(tun_flags, 0444, tun_show_flags, NULL);
|
|
|
|
static DEVICE_ATTR(owner, 0444, tun_show_owner, NULL);
|
|
|
|
static DEVICE_ATTR(group, 0444, tun_show_group, NULL);
|
|
|
|
|
2015-02-04 21:37:34 +08:00
|
|
|
static struct attribute *tun_dev_attrs[] = {
|
|
|
|
&dev_attr_tun_flags.attr,
|
|
|
|
&dev_attr_owner.attr,
|
|
|
|
&dev_attr_group.attr,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct attribute_group tun_attr_group = {
|
|
|
|
.attrs = tun_dev_attrs
|
|
|
|
};
|
|
|
|
|
2008-04-16 15:41:16 +08:00
|
|
|
static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct tun_struct *tun;
|
2012-11-01 03:45:57 +08:00
|
|
|
struct tun_file *tfile = file->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct net_device *dev;
|
|
|
|
int err;
|
|
|
|
|
2013-01-12 00:59:33 +08:00
|
|
|
if (tfile->detached)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2017-09-23 04:49:15 +08:00
|
|
|
if ((ifr->ifr_flags & IFF_NAPI_FRAGS)) {
|
|
|
|
if (!capable(CAP_NET_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
if (!(ifr->ifr_flags & IFF_NAPI) ||
|
|
|
|
(ifr->ifr_flags & TUN_TYPE_MASK) != IFF_TAP)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2009-01-20 18:56:20 +08:00
|
|
|
dev = __dev_get_by_name(net, ifr->ifr_name);
|
|
|
|
if (dev) {
|
2009-04-27 18:23:54 +08:00
|
|
|
if (ifr->ifr_flags & IFF_TUN_EXCL)
|
|
|
|
return -EBUSY;
|
2009-01-20 18:56:20 +08:00
|
|
|
if ((ifr->ifr_flags & IFF_TUN) && dev->netdev_ops == &tun_netdev_ops)
|
|
|
|
tun = netdev_priv(dev);
|
|
|
|
else if ((ifr->ifr_flags & IFF_TAP) && dev->netdev_ops == &tap_netdev_ops)
|
|
|
|
tun = netdev_priv(dev);
|
|
|
|
else
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-05-29 02:32:11 +08:00
|
|
|
if (!!(ifr->ifr_flags & IFF_MULTI_QUEUE) !=
|
2014-11-19 21:17:31 +08:00
|
|
|
!!(tun->flags & IFF_MULTI_QUEUE))
|
2013-05-29 02:32:11 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2012-11-01 03:46:01 +08:00
|
|
|
if (tun_not_capable(tun))
|
2009-08-29 06:12:43 +08:00
|
|
|
return -EPERM;
|
2013-01-14 15:12:19 +08:00
|
|
|
err = security_tun_dev_open(tun->security);
|
2009-08-29 06:12:43 +08:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
2017-09-23 04:49:14 +08:00
|
|
|
err = tun_attach(tun, file, ifr->ifr_flags & IFF_NOFILTER,
|
2018-09-29 05:51:49 +08:00
|
|
|
ifr->ifr_flags & IFF_NAPI,
|
2019-09-10 18:56:57 +08:00
|
|
|
ifr->ifr_flags & IFF_NAPI_FRAGS, true);
|
2009-01-20 18:57:48 +08:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2012-12-14 07:53:30 +08:00
|
|
|
|
2014-11-19 21:17:31 +08:00
|
|
|
if (tun->flags & IFF_MULTI_QUEUE &&
|
2013-04-23 04:40:39 +08:00
|
|
|
(tun->numqueues + tun->numdisabled > 1)) {
|
|
|
|
/* One or more queue has already been attached, no need
|
|
|
|
* to initialize the device again.
|
|
|
|
*/
|
2018-04-10 22:28:56 +08:00
|
|
|
netdev_state_change(dev);
|
2013-04-23 04:40:39 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2018-04-10 22:28:55 +08:00
|
|
|
|
|
|
|
tun->flags = (tun->flags & ~TUN_FEATURES) |
|
|
|
|
(ifr->ifr_flags & TUN_FEATURES);
|
2018-04-10 22:28:56 +08:00
|
|
|
|
|
|
|
netdev_state_change(dev);
|
|
|
|
} else {
|
2005-04-17 06:20:36 +08:00
|
|
|
char *name;
|
|
|
|
unsigned long flags = 0;
|
2013-01-23 11:59:12 +08:00
|
|
|
int queues = ifr->ifr_flags & IFF_MULTI_QUEUE ?
|
|
|
|
MAX_TAP_QUEUES : 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-11-19 05:34:11 +08:00
|
|
|
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
|
2006-06-23 07:07:52 +08:00
|
|
|
return -EPERM;
|
2009-08-29 06:12:43 +08:00
|
|
|
err = security_tun_dev_create();
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2006-06-23 07:07:52 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Set dev type */
|
|
|
|
if (ifr->ifr_flags & IFF_TUN) {
|
|
|
|
/* TUN device */
|
2014-11-19 21:17:31 +08:00
|
|
|
flags |= IFF_TUN;
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "tun%d";
|
|
|
|
} else if (ifr->ifr_flags & IFF_TAP) {
|
|
|
|
/* TAP device */
|
2014-11-19 21:17:31 +08:00
|
|
|
flags |= IFF_TAP;
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "tap%d";
|
2006-09-14 01:24:59 +08:00
|
|
|
} else
|
2009-09-17 05:36:13 +08:00
|
|
|
return -EINVAL;
|
2006-09-14 01:24:59 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (*ifr->ifr_name)
|
|
|
|
name = ifr->ifr_name;
|
|
|
|
|
2012-11-01 03:46:00 +08:00
|
|
|
dev = alloc_netdev_mqs(sizeof(struct tun_struct), name,
|
net: set name_assign_type in alloc_netdev()
Extend alloc_netdev{,_mq{,s}}() to take name_assign_type as argument, and convert
all users to pass NET_NAME_UNKNOWN.
Coccinelle patch:
@@
expression sizeof_priv, name, setup, txqs, rxqs, count;
@@
(
-alloc_netdev_mqs(sizeof_priv, name, setup, txqs, rxqs)
+alloc_netdev_mqs(sizeof_priv, name, NET_NAME_UNKNOWN, setup, txqs, rxqs)
|
-alloc_netdev_mq(sizeof_priv, name, setup, count)
+alloc_netdev_mq(sizeof_priv, name, NET_NAME_UNKNOWN, setup, count)
|
-alloc_netdev(sizeof_priv, name, setup)
+alloc_netdev(sizeof_priv, name, NET_NAME_UNKNOWN, setup)
)
v9: move comments here from the wrong commit
Signed-off-by: Tom Gundersen <teg@jklm.no>
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2014-07-14 22:37:24 +08:00
|
|
|
NET_NAME_UNKNOWN, tun_setup, queues,
|
|
|
|
queues);
|
2013-01-23 11:59:12 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!dev)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2008-04-16 15:41:53 +08:00
|
|
|
dev_net_set(dev, net);
|
2009-01-22 08:02:16 +08:00
|
|
|
dev->rtnl_link_ops = &tun_link_ops;
|
tun: Add ability to create tun device with given index
Tun devices cannot be created with ifidex user wants, but it's
required by checkpoint-restore project.
Long time ago such ability was implemented for rtnl_ops-based
interface for creating links (9c7dafbf net: Allow to create links
with given ifindex), but the only API for creating and managing
tuntap devices is ioctl-based and is evolving with adding new ones
(cde8b15f tuntap: add ioctl to attach or detach a file form tuntap
device).
Following that trend, here's how a new ioctl that sets the ifindex
for device, that _will_ be created by TUNSETIFF ioctl looks like.
So those who want a tuntap device with the ifindex N, should open
the tun device, call ioctl(fd, TUNSETIFINDEX, &N), then call TUNSETIFF.
If the index N is busy, then the register_netdev will find this out
and the ioctl would be failed with -EBUSY.
If setifindex is not called, then it will be generated as before.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-08-21 18:31:38 +08:00
|
|
|
dev->ifindex = tfile->ifindex;
|
2015-02-04 21:37:34 +08:00
|
|
|
dev->sysfs_groups[0] = &tun_attr_group;
|
2008-11-20 14:10:37 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
tun = netdev_priv(dev);
|
|
|
|
tun->dev = dev;
|
|
|
|
tun->flags = flags;
|
2008-07-15 13:18:19 +08:00
|
|
|
tun->txflt.count = 0;
|
2010-03-17 23:45:01 +08:00
|
|
|
tun->vnet_hdr_sz = sizeof(struct virtio_net_hdr);
|
2009-02-06 13:25:32 +08:00
|
|
|
|
2016-02-26 17:45:40 +08:00
|
|
|
tun->align = NET_SKB_PAD;
|
2012-11-01 03:45:57 +08:00
|
|
|
tun->filter_attached = false;
|
|
|
|
tun->sndbuf = tfile->socket.sk->sk_sndbuf;
|
2017-01-18 15:02:03 +08:00
|
|
|
tun->rx_batched = 0;
|
2017-12-04 17:31:23 +08:00
|
|
|
RCU_INIT_POINTER(tun->steering_prog, NULL);
|
2009-02-06 13:25:32 +08:00
|
|
|
|
2020-11-08 04:50:56 +08:00
|
|
|
dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
|
|
|
|
if (!dev->tstats) {
|
2016-04-13 16:52:20 +08:00
|
|
|
err = -ENOMEM;
|
|
|
|
goto err_free_dev;
|
|
|
|
}
|
|
|
|
|
2012-11-01 03:46:02 +08:00
|
|
|
spin_lock_init(&tun->lock);
|
|
|
|
|
2013-01-14 15:12:19 +08:00
|
|
|
err = security_tun_dev_alloc_security(&tun->security);
|
|
|
|
if (err < 0)
|
2016-04-13 16:52:20 +08:00
|
|
|
goto err_free_stat;
|
2009-08-29 06:12:43 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
tun_net_init(dev);
|
2013-06-11 21:01:08 +08:00
|
|
|
tun_flow_init(tun);
|
2012-11-01 03:46:02 +08:00
|
|
|
|
2011-04-19 14:13:10 +08:00
|
|
|
dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST |
|
2013-07-25 13:00:33 +08:00
|
|
|
TUN_USER_FEATURES | NETIF_F_HW_VLAN_CTAG_TX |
|
|
|
|
NETIF_F_HW_VLAN_STAG_TX;
|
2016-04-15 00:39:39 +08:00
|
|
|
dev->features = dev->hw_features | NETIF_F_LLTX;
|
2014-02-18 20:20:09 +08:00
|
|
|
dev->vlan_features = dev->features &
|
|
|
|
~(NETIF_F_HW_VLAN_CTAG_TX |
|
|
|
|
NETIF_F_HW_VLAN_STAG_TX);
|
2011-04-19 14:13:10 +08:00
|
|
|
|
2018-04-10 22:28:55 +08:00
|
|
|
tun->flags = (tun->flags & ~TUN_FEATURES) |
|
|
|
|
(ifr->ifr_flags & TUN_FEATURES);
|
|
|
|
|
2012-12-14 07:53:30 +08:00
|
|
|
INIT_LIST_HEAD(&tun->disabled);
|
2018-09-29 05:51:49 +08:00
|
|
|
err = tun_attach(tun, file, false, ifr->ifr_flags & IFF_NAPI,
|
2019-09-10 18:56:57 +08:00
|
|
|
ifr->ifr_flags & IFF_NAPI_FRAGS, false);
|
2012-12-03 01:19:45 +08:00
|
|
|
if (err < 0)
|
2013-09-11 18:09:48 +08:00
|
|
|
goto err_free_flow;
|
2012-12-03 01:19:45 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
err = register_netdevice(tun->dev);
|
|
|
|
if (err < 0)
|
2013-09-11 18:09:48 +08:00
|
|
|
goto err_detach;
|
2019-09-10 18:56:57 +08:00
|
|
|
/* free_netdev() won't check refcnt, to aovid race
|
|
|
|
* with dev_put() we need publish tun after registration.
|
|
|
|
*/
|
|
|
|
rcu_assign_pointer(tfile->tun, tun);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-01-28 08:38:02 +08:00
|
|
|
netif_carrier_on(tun->dev);
|
|
|
|
|
2008-07-11 07:59:11 +08:00
|
|
|
/* Make sure persistent devices do not get stuck in
|
|
|
|
* xoff state.
|
|
|
|
*/
|
|
|
|
if (netif_running(tun->dev))
|
2012-11-01 03:46:00 +08:00
|
|
|
netif_tx_wake_all_queues(tun->dev);
|
2008-07-11 07:59:11 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
strcpy(ifr->ifr_name, tun->dev->name);
|
|
|
|
return 0;
|
|
|
|
|
2013-09-11 18:09:48 +08:00
|
|
|
err_detach:
|
|
|
|
tun_detach_all(dev);
|
2019-10-08 03:21:05 +08:00
|
|
|
/* We are here because register_netdevice() has failed.
|
|
|
|
* If register_netdevice() already called tun_free_netdev()
|
2020-11-08 04:50:56 +08:00
|
|
|
* while dealing with the error, dev->stats has been cleared.
|
2019-10-08 03:21:05 +08:00
|
|
|
*/
|
2020-11-08 04:50:56 +08:00
|
|
|
if (!dev->tstats)
|
2019-10-08 03:21:05 +08:00
|
|
|
goto err_free_dev;
|
2017-08-19 04:39:56 +08:00
|
|
|
|
2013-09-11 18:09:48 +08:00
|
|
|
err_free_flow:
|
|
|
|
tun_flow_uninit(tun);
|
|
|
|
security_tun_dev_free_security(tun->security);
|
2016-04-13 16:52:20 +08:00
|
|
|
err_free_stat:
|
2020-11-08 04:50:56 +08:00
|
|
|
free_percpu(dev->tstats);
|
2013-09-11 18:09:48 +08:00
|
|
|
err_free_dev:
|
2005-04-17 06:20:36 +08:00
|
|
|
free_netdev(dev);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2019-03-20 17:16:53 +08:00
|
|
|
static void tun_get_iff(struct tun_struct *tun, struct ifreq *ifr)
|
2008-08-16 06:09:56 +08:00
|
|
|
{
|
|
|
|
strcpy(ifr->ifr_name, tun->dev->name);
|
|
|
|
|
2009-05-10 13:54:21 +08:00
|
|
|
ifr->ifr_flags = tun_flags(tun);
|
2008-08-16 06:09:56 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2008-07-03 18:46:16 +08:00
|
|
|
/* This is like a cut-down ethtool ops, except done via tun fd so no
|
|
|
|
* privs required. */
|
2011-04-19 14:13:10 +08:00
|
|
|
static int set_offload(struct tun_struct *tun, unsigned long arg)
|
2008-07-03 18:46:16 +08:00
|
|
|
{
|
2011-11-15 23:29:55 +08:00
|
|
|
netdev_features_t features = 0;
|
2008-07-03 18:46:16 +08:00
|
|
|
|
|
|
|
if (arg & TUN_F_CSUM) {
|
2011-04-19 14:13:10 +08:00
|
|
|
features |= NETIF_F_HW_CSUM;
|
2008-07-03 18:46:16 +08:00
|
|
|
arg &= ~TUN_F_CSUM;
|
|
|
|
|
|
|
|
if (arg & (TUN_F_TSO4|TUN_F_TSO6)) {
|
|
|
|
if (arg & TUN_F_TSO_ECN) {
|
|
|
|
features |= NETIF_F_TSO_ECN;
|
|
|
|
arg &= ~TUN_F_TSO_ECN;
|
|
|
|
}
|
|
|
|
if (arg & TUN_F_TSO4)
|
|
|
|
features |= NETIF_F_TSO;
|
|
|
|
if (arg & TUN_F_TSO6)
|
|
|
|
features |= NETIF_F_TSO6;
|
|
|
|
arg &= ~(TUN_F_TSO4|TUN_F_TSO6);
|
|
|
|
}
|
net: accept UFO datagrams from tuntap and packet
Tuntap and similar devices can inject GSO packets. Accept type
VIRTIO_NET_HDR_GSO_UDP, even though not generating UFO natively.
Processes are expected to use feature negotiation such as TUNSETOFFLOAD
to detect supported offload types and refrain from injecting other
packets. This process breaks down with live migration: guest kernels
do not renegotiate flags, so destination hosts need to expose all
features that the source host does.
Partially revert the UFO removal from 182e0b6b5846~1..d9d30adf5677.
This patch introduces nearly(*) no new code to simplify verification.
It brings back verbatim tuntap UFO negotiation, VIRTIO_NET_HDR_GSO_UDP
insertion and software UFO segmentation.
It does not reinstate protocol stack support, hardware offload
(NETIF_F_UFO), SKB_GSO_UDP tunneling in SKB_GSO_SOFTWARE or reception
of VIRTIO_NET_HDR_GSO_UDP packets in tuntap.
To support SKB_GSO_UDP reappearing in the stack, also reinstate
logic in act_csum and openvswitch. Achieve equivalence with v4.13 HEAD
by squashing in commit 939912216fa8 ("net: skb_needs_check() removes
CHECKSUM_UNNECESSARY check for tx.") and reverting commit 8d63bee643f1
("net: avoid skb_warn_bad_offload false positives on UFO").
(*) To avoid having to bring back skb_shinfo(skb)->ip6_frag_id,
ipv6_proxy_select_ident is changed to return a __be32 and this is
assigned directly to the frag_hdr. Also, SKB_GSO_UDP is inserted
at the end of the enum to minimize code churn.
Tested
Booted a v4.13 guest kernel with QEMU. On a host kernel before this
patch `ethtool -k eth0` shows UFO disabled. After the patch, it is
enabled, same as on a v4.13 host kernel.
A UFO packet sent from the guest appears on the tap device:
host:
nc -l -p -u 8000 &
tcpdump -n -i tap0
guest:
dd if=/dev/zero of=payload.txt bs=1 count=2000
nc -u 192.16.1.1 8000 < payload.txt
Direct tap to tap transmission of VIRTIO_NET_HDR_GSO_UDP succeeds,
packets arriving fragmented:
./with_tap_pair.sh ./tap_send_ufo tap0 tap1
(from https://github.com/wdebruij/kerneltools/tree/master/tests)
Changes
v1 -> v2
- simplified set_offload change (review comment)
- documented test procedure
Link: http://lkml.kernel.org/r/<CAF=yD-LuUeDuL9YWPJD9ykOZ0QCjNeznPDr6whqZ9NGMNF12Mw@mail.gmail.com>
Fixes: fb652fdfe837 ("macvlan/macvtap: Remove NETIF_F_UFO advertisement.")
Reported-by: Michal Kubecek <mkubecek@suse.cz>
Signed-off-by: Willem de Bruijn <willemb@google.com>
Acked-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-11-21 23:22:25 +08:00
|
|
|
|
|
|
|
arg &= ~TUN_F_UFO;
|
2008-07-03 18:46:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* This gives the user a way to test for new features in future by
|
|
|
|
* trying to set them. */
|
|
|
|
if (arg)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2011-04-19 14:13:10 +08:00
|
|
|
tun->set_features = features;
|
2017-03-17 03:44:10 +08:00
|
|
|
tun->dev->wanted_features &= ~TUN_USER_FEATURES;
|
|
|
|
tun->dev->wanted_features |= features;
|
2011-04-19 14:13:10 +08:00
|
|
|
netdev_update_features(tun->dev);
|
2008-07-03 18:46:16 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-01 03:46:00 +08:00
|
|
|
static void tun_detach_filter(struct tun_struct *tun, int n)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct tun_file *tfile;
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
2013-01-12 00:59:32 +08:00
|
|
|
tfile = rtnl_dereference(tun->tfiles[i]);
|
2016-04-05 23:10:16 +08:00
|
|
|
lock_sock(tfile->socket.sk);
|
|
|
|
sk_detach_filter(tfile->socket.sk);
|
|
|
|
release_sock(tfile->socket.sk);
|
2012-11-01 03:46:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
tun->filter_attached = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tun_attach_filter(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
int i, ret = 0;
|
|
|
|
struct tun_file *tfile;
|
|
|
|
|
|
|
|
for (i = 0; i < tun->numqueues; i++) {
|
2013-01-12 00:59:32 +08:00
|
|
|
tfile = rtnl_dereference(tun->tfiles[i]);
|
2016-04-05 23:10:16 +08:00
|
|
|
lock_sock(tfile->socket.sk);
|
|
|
|
ret = sk_attach_filter(&tun->fprog, tfile->socket.sk);
|
|
|
|
release_sock(tfile->socket.sk);
|
2012-11-01 03:46:00 +08:00
|
|
|
if (ret) {
|
|
|
|
tun_detach_filter(tun, i);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tun->filter_attached = true;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_set_sndbuf(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
struct tun_file *tfile;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < tun->numqueues; i++) {
|
2013-01-12 00:59:32 +08:00
|
|
|
tfile = rtnl_dereference(tun->tfiles[i]);
|
2012-11-01 03:46:00 +08:00
|
|
|
tfile->socket.sk->sk_sndbuf = tun->sndbuf;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-01 03:46:01 +08:00
|
|
|
static int tun_set_queue(struct file *file, struct ifreq *ifr)
|
|
|
|
{
|
|
|
|
struct tun_file *tfile = file->private_data;
|
|
|
|
struct tun_struct *tun;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
|
|
if (ifr->ifr_flags & IFF_ATTACH_QUEUE) {
|
2012-12-14 07:53:30 +08:00
|
|
|
tun = tfile->detached;
|
2013-01-14 15:12:19 +08:00
|
|
|
if (!tun) {
|
2012-11-01 03:46:01 +08:00
|
|
|
ret = -EINVAL;
|
2013-01-14 15:12:19 +08:00
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
ret = security_tun_dev_attach_queue(tun->security);
|
|
|
|
if (ret < 0)
|
|
|
|
goto unlock;
|
2018-09-29 05:51:49 +08:00
|
|
|
ret = tun_attach(tun, file, false, tun->flags & IFF_NAPI,
|
2019-09-10 18:56:57 +08:00
|
|
|
tun->flags & IFF_NAPI_FRAGS, true);
|
2012-12-14 07:53:30 +08:00
|
|
|
} else if (ifr->ifr_flags & IFF_DETACH_QUEUE) {
|
2013-01-12 00:59:32 +08:00
|
|
|
tun = rtnl_dereference(tfile->tun);
|
2014-11-19 21:17:31 +08:00
|
|
|
if (!tun || !(tun->flags & IFF_MULTI_QUEUE) || tfile->detached)
|
2012-12-14 07:53:30 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
else
|
|
|
|
__tun_detach(tfile, false);
|
|
|
|
} else
|
2012-11-01 03:46:01 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
|
2018-04-10 22:28:56 +08:00
|
|
|
if (ret >= 0)
|
|
|
|
netdev_state_change(tun->dev);
|
|
|
|
|
2013-01-14 15:12:19 +08:00
|
|
|
unlock:
|
2012-11-01 03:46:01 +08:00
|
|
|
rtnl_unlock();
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-07-31 12:17:20 +08:00
|
|
|
static int tun_set_ebpf(struct tun_struct *tun, struct tun_prog __rcu **prog_p,
|
2018-01-16 16:31:01 +08:00
|
|
|
void __user *data)
|
2017-12-04 17:31:23 +08:00
|
|
|
{
|
|
|
|
struct bpf_prog *prog;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
if (copy_from_user(&fd, data, sizeof(fd)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (fd == -1) {
|
|
|
|
prog = NULL;
|
|
|
|
} else {
|
|
|
|
prog = bpf_prog_get_type(fd, BPF_PROG_TYPE_SOCKET_FILTER);
|
|
|
|
if (IS_ERR(prog))
|
|
|
|
return PTR_ERR(prog);
|
|
|
|
}
|
|
|
|
|
2018-01-16 16:31:01 +08:00
|
|
|
return __tun_set_ebpf(tun, prog_p, prog);
|
2017-12-04 17:31:23 +08:00
|
|
|
}
|
|
|
|
|
2009-11-07 14:52:32 +08:00
|
|
|
static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
|
|
|
|
unsigned long arg, int ifreq_len)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2009-01-20 19:01:48 +08:00
|
|
|
struct tun_file *tfile = file->private_data;
|
2018-05-09 00:21:34 +08:00
|
|
|
struct net *net = sock_net(&tfile->sk);
|
2009-01-20 19:00:40 +08:00
|
|
|
struct tun_struct *tun;
|
2005-04-17 06:20:36 +08:00
|
|
|
void __user* argp = (void __user*)arg;
|
2018-11-29 02:12:56 +08:00
|
|
|
unsigned int ifindex, carrier;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct ifreq ifr;
|
2012-02-08 08:48:55 +08:00
|
|
|
kuid_t owner;
|
|
|
|
kgid_t group;
|
2009-02-06 13:25:32 +08:00
|
|
|
int sndbuf;
|
2010-03-17 23:45:01 +08:00
|
|
|
int vnet_hdr_sz;
|
2014-12-16 21:05:06 +08:00
|
|
|
int le;
|
2008-07-15 13:18:19 +08:00
|
|
|
int ret;
|
2018-04-10 22:28:56 +08:00
|
|
|
bool do_notify = false;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2018-02-14 21:40:14 +08:00
|
|
|
if (cmd == TUNSETIFF || cmd == TUNSETQUEUE ||
|
|
|
|
(_IOC_TYPE(cmd) == SOCK_IOC_TYPE && cmd != SIOCGSKNS)) {
|
2009-11-07 14:52:32 +08:00
|
|
|
if (copy_from_user(&ifr, argp, ifreq_len))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EFAULT;
|
2012-07-31 05:52:48 +08:00
|
|
|
} else {
|
2012-07-30 03:45:14 +08:00
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
2012-07-31 05:52:48 +08:00
|
|
|
}
|
2009-01-20 19:00:40 +08:00
|
|
|
if (cmd == TUNGETFEATURES) {
|
|
|
|
/* Currently this just means: "what IFF flags are valid?".
|
|
|
|
* This is needed because we never checked for invalid flags on
|
2014-11-19 20:44:40 +08:00
|
|
|
* TUNSETIFF.
|
|
|
|
*/
|
|
|
|
return put_user(IFF_TUN | IFF_TAP | TUN_FEATURES,
|
2009-01-20 19:00:40 +08:00
|
|
|
(unsigned int __user*)argp);
|
2018-05-09 00:21:34 +08:00
|
|
|
} else if (cmd == TUNSETQUEUE) {
|
2012-11-01 03:46:01 +08:00
|
|
|
return tun_set_queue(file, &ifr);
|
2018-05-09 00:21:34 +08:00
|
|
|
} else if (cmd == SIOCGSKNS) {
|
|
|
|
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
return open_related_ns(&net->ns, get_net_ns);
|
|
|
|
}
|
2009-01-20 19:00:40 +08:00
|
|
|
|
2012-11-01 03:46:00 +08:00
|
|
|
ret = 0;
|
2009-08-06 22:22:44 +08:00
|
|
|
rtnl_lock();
|
|
|
|
|
2017-09-23 22:36:52 +08:00
|
|
|
tun = tun_get(tfile);
|
2016-10-25 22:26:09 +08:00
|
|
|
if (cmd == TUNSETIFF) {
|
|
|
|
ret = -EEXIST;
|
|
|
|
if (tun)
|
|
|
|
goto unlock;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
ifr.ifr_name[IFNAMSIZ-1] = '\0';
|
|
|
|
|
2018-02-14 21:40:14 +08:00
|
|
|
ret = tun_set_iff(net, file, &ifr);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-08-06 22:22:44 +08:00
|
|
|
if (ret)
|
|
|
|
goto unlock;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-11-07 14:52:32 +08:00
|
|
|
if (copy_to_user(argp, &ifr, ifreq_len))
|
2009-08-06 22:22:44 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
goto unlock;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
tun: Add ability to create tun device with given index
Tun devices cannot be created with ifidex user wants, but it's
required by checkpoint-restore project.
Long time ago such ability was implemented for rtnl_ops-based
interface for creating links (9c7dafbf net: Allow to create links
with given ifindex), but the only API for creating and managing
tuntap devices is ioctl-based and is evolving with adding new ones
(cde8b15f tuntap: add ioctl to attach or detach a file form tuntap
device).
Following that trend, here's how a new ioctl that sets the ifindex
for device, that _will_ be created by TUNSETIFF ioctl looks like.
So those who want a tuntap device with the ifindex N, should open
the tun device, call ioctl(fd, TUNSETIFINDEX, &N), then call TUNSETIFF.
If the index N is busy, then the register_netdev will find this out
and the ioctl would be failed with -EBUSY.
If setifindex is not called, then it will be generated as before.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-08-21 18:31:38 +08:00
|
|
|
if (cmd == TUNSETIFINDEX) {
|
|
|
|
ret = -EPERM;
|
|
|
|
if (tun)
|
|
|
|
goto unlock;
|
|
|
|
|
|
|
|
ret = -EFAULT;
|
|
|
|
if (copy_from_user(&ifindex, argp, sizeof(ifindex)))
|
|
|
|
goto unlock;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
tfile->ifindex = ifindex;
|
|
|
|
goto unlock;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-08-06 22:22:44 +08:00
|
|
|
ret = -EBADFD;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!tun)
|
2009-08-06 22:22:44 +08:00
|
|
|
goto unlock;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2020-03-05 00:24:14 +08:00
|
|
|
netif_info(tun, drv, tun->dev, "tun_chr_ioctl cmd %u\n", cmd);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-03-20 17:16:42 +08:00
|
|
|
net = dev_net(tun->dev);
|
2009-01-20 19:00:40 +08:00
|
|
|
ret = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
switch (cmd) {
|
2008-08-16 06:09:56 +08:00
|
|
|
case TUNGETIFF:
|
2019-03-20 17:16:53 +08:00
|
|
|
tun_get_iff(tun, &ifr);
|
2008-08-16 06:09:56 +08:00
|
|
|
|
2013-08-21 18:32:00 +08:00
|
|
|
if (tfile->detached)
|
|
|
|
ifr.ifr_flags |= IFF_DETACH_QUEUE;
|
tun: Allow to skip filter on attach
There's a small problem with sk-filters on tun devices. Consider
an application doing this sequence of steps:
fd = open("/dev/net/tun");
ioctl(fd, TUNSETIFF, { .ifr_name = "tun0" });
ioctl(fd, TUNATTACHFILTER, &my_filter);
ioctl(fd, TUNSETPERSIST, 1);
close(fd);
At that point the tun0 will remain in the system and will keep in
mind that there should be a socket filter at address '&my_filter'.
If after that we do
fd = open("/dev/net/tun");
ioctl(fd, TUNSETIFF, { .ifr_name = "tun0" });
we most likely receive the -EFAULT error, since tun_attach() would
try to connect the filter back. But (!) if we provide a filter at
address &my_filter, then tun0 will be created and the "new" filter
would be attached, but application may not know about that.
This may create certain problems to anyone using tun-s, but it's
critical problem for c/r -- if we meet a persistent tun device
with a filter in mind, we will not be able to attach to it to dump
its state (flags, owner, address, vnethdr size, etc.).
The proposal is to allow to attach to tun device (with TUNSETIFF)
w/o attaching the filter to the tun-file's socket. After this
attach app may e.g clean the device by dropping the filter, it
doesn't want to have one, or (in case of c/r) get information
about the device with tun ioctls.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-08-21 18:32:21 +08:00
|
|
|
if (!tfile->socket.sk->sk_filter)
|
|
|
|
ifr.ifr_flags |= IFF_NOFILTER;
|
2013-08-21 18:32:00 +08:00
|
|
|
|
2009-11-07 14:52:32 +08:00
|
|
|
if (copy_to_user(argp, &ifr, ifreq_len))
|
2009-01-20 19:00:40 +08:00
|
|
|
ret = -EFAULT;
|
2008-08-16 06:09:56 +08:00
|
|
|
break;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
case TUNSETNOCSUM:
|
|
|
|
/* Disable/Enable checksum */
|
|
|
|
|
2011-04-19 14:13:10 +08:00
|
|
|
/* [unimplemented] */
|
2020-03-05 00:24:14 +08:00
|
|
|
netif_info(tun, drv, tun->dev, "ignored: set checksum %s\n",
|
|
|
|
arg ? "disabled" : "enabled");
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case TUNSETPERSIST:
|
2012-11-01 03:45:57 +08:00
|
|
|
/* Disable/Enable persist mode. Keep an extra reference to the
|
|
|
|
* module to prevent the module being unprobed.
|
|
|
|
*/
|
2014-11-19 21:17:31 +08:00
|
|
|
if (arg && !(tun->flags & IFF_PERSIST)) {
|
|
|
|
tun->flags |= IFF_PERSIST;
|
2012-11-01 03:45:57 +08:00
|
|
|
__module_get(THIS_MODULE);
|
2018-04-10 22:28:56 +08:00
|
|
|
do_notify = true;
|
2013-01-12 00:59:34 +08:00
|
|
|
}
|
2014-11-19 21:17:31 +08:00
|
|
|
if (!arg && (tun->flags & IFF_PERSIST)) {
|
|
|
|
tun->flags &= ~IFF_PERSIST;
|
2012-11-01 03:45:57 +08:00
|
|
|
module_put(THIS_MODULE);
|
2018-04-10 22:28:56 +08:00
|
|
|
do_notify = true;
|
2012-11-01 03:45:57 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2020-03-05 00:24:14 +08:00
|
|
|
netif_info(tun, drv, tun->dev, "persist %s\n",
|
|
|
|
arg ? "enabled" : "disabled");
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case TUNSETOWNER:
|
|
|
|
/* Set owner of the device */
|
2012-02-08 08:48:55 +08:00
|
|
|
owner = make_kuid(current_user_ns(), arg);
|
|
|
|
if (!uid_valid(owner)) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
tun->owner = owner;
|
2018-04-10 22:28:56 +08:00
|
|
|
do_notify = true;
|
2020-03-05 00:24:14 +08:00
|
|
|
netif_info(tun, drv, tun->dev, "owner set to %u\n",
|
|
|
|
from_kuid(&init_user_ns, tun->owner));
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
2007-07-03 13:50:25 +08:00
|
|
|
case TUNSETGROUP:
|
|
|
|
/* Set group of the device */
|
2012-02-08 08:48:55 +08:00
|
|
|
group = make_kgid(current_user_ns(), arg);
|
|
|
|
if (!gid_valid(group)) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
tun->group = group;
|
2018-04-10 22:28:56 +08:00
|
|
|
do_notify = true;
|
2020-03-05 00:24:14 +08:00
|
|
|
netif_info(tun, drv, tun->dev, "group set to %u\n",
|
|
|
|
from_kgid(&init_user_ns, tun->group));
|
2007-07-03 13:50:25 +08:00
|
|
|
break;
|
|
|
|
|
2005-09-02 08:40:05 +08:00
|
|
|
case TUNSETLINK:
|
|
|
|
/* Only allow setting the type when the interface is down */
|
|
|
|
if (tun->dev->flags & IFF_UP) {
|
2020-03-05 00:24:14 +08:00
|
|
|
netif_info(tun, drv, tun->dev,
|
|
|
|
"Linktype set failed because interface is up\n");
|
2008-04-24 10:37:58 +08:00
|
|
|
ret = -EBUSY;
|
2005-09-02 08:40:05 +08:00
|
|
|
} else {
|
2020-11-18 14:39:19 +08:00
|
|
|
ret = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE,
|
|
|
|
tun->dev);
|
|
|
|
ret = notifier_to_errno(ret);
|
|
|
|
if (ret) {
|
|
|
|
netif_info(tun, drv, tun->dev,
|
|
|
|
"Refused to change device type\n");
|
|
|
|
break;
|
|
|
|
}
|
2005-09-02 08:40:05 +08:00
|
|
|
tun->dev->type = (int) arg;
|
2020-03-05 00:24:14 +08:00
|
|
|
netif_info(tun, drv, tun->dev, "linktype set to %d\n",
|
|
|
|
tun->dev->type);
|
2020-11-18 14:39:19 +08:00
|
|
|
call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE,
|
|
|
|
tun->dev);
|
2005-09-02 08:40:05 +08:00
|
|
|
}
|
2009-01-20 19:00:40 +08:00
|
|
|
break;
|
2005-09-02 08:40:05 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
case TUNSETDEBUG:
|
2020-03-05 00:24:14 +08:00
|
|
|
tun->msg_enable = (u32)arg;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2020-03-05 00:24:14 +08:00
|
|
|
|
2008-07-03 18:46:16 +08:00
|
|
|
case TUNSETOFFLOAD:
|
2011-04-19 14:13:10 +08:00
|
|
|
ret = set_offload(tun, arg);
|
2009-01-20 19:00:40 +08:00
|
|
|
break;
|
2008-07-03 18:46:16 +08:00
|
|
|
|
2008-07-15 13:18:19 +08:00
|
|
|
case TUNSETTXFILTER:
|
|
|
|
/* Can be set only for TAPs */
|
2009-01-20 19:00:40 +08:00
|
|
|
ret = -EINVAL;
|
2014-11-19 21:17:31 +08:00
|
|
|
if ((tun->flags & TUN_TYPE_MASK) != IFF_TAP)
|
2009-01-20 19:00:40 +08:00
|
|
|
break;
|
2008-07-17 03:45:34 +08:00
|
|
|
ret = update_filter(&tun->txflt, (void __user *)arg);
|
2009-01-20 19:00:40 +08:00
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
case SIOCGIFHWADDR:
|
tree-wide: fix comment/printk typos
"gadget", "through", "command", "maintain", "maintain", "controller", "address",
"between", "initiali[zs]e", "instead", "function", "select", "already",
"equal", "access", "management", "hierarchy", "registration", "interest",
"relative", "memory", "offset", "already",
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-11-02 03:38:34 +08:00
|
|
|
/* Get hw address */
|
2008-07-15 13:18:19 +08:00
|
|
|
memcpy(ifr.ifr_hwaddr.sa_data, tun->dev->dev_addr, ETH_ALEN);
|
|
|
|
ifr.ifr_hwaddr.sa_family = tun->dev->type;
|
2009-11-07 14:52:32 +08:00
|
|
|
if (copy_to_user(argp, &ifr, ifreq_len))
|
2009-01-20 19:00:40 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
case SIOCSIFHWADDR:
|
2008-07-15 13:18:19 +08:00
|
|
|
/* Set hw address */
|
2018-12-13 19:54:30 +08:00
|
|
|
ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr, NULL);
|
2009-01-20 19:00:40 +08:00
|
|
|
break;
|
2009-02-06 13:25:32 +08:00
|
|
|
|
|
|
|
case TUNGETSNDBUF:
|
2012-11-01 03:45:57 +08:00
|
|
|
sndbuf = tfile->socket.sk->sk_sndbuf;
|
2009-02-06 13:25:32 +08:00
|
|
|
if (copy_to_user(argp, &sndbuf, sizeof(sndbuf)))
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TUNSETSNDBUF:
|
|
|
|
if (copy_from_user(&sndbuf, argp, sizeof(sndbuf))) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
2017-10-31 06:50:11 +08:00
|
|
|
if (sndbuf <= 0) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
2009-02-06 13:25:32 +08:00
|
|
|
|
2012-11-01 03:46:00 +08:00
|
|
|
tun->sndbuf = sndbuf;
|
|
|
|
tun_set_sndbuf(tun);
|
2009-02-06 13:25:32 +08:00
|
|
|
break;
|
|
|
|
|
2010-03-17 23:45:01 +08:00
|
|
|
case TUNGETVNETHDRSZ:
|
|
|
|
vnet_hdr_sz = tun->vnet_hdr_sz;
|
|
|
|
if (copy_to_user(argp, &vnet_hdr_sz, sizeof(vnet_hdr_sz)))
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TUNSETVNETHDRSZ:
|
|
|
|
if (copy_from_user(&vnet_hdr_sz, argp, sizeof(vnet_hdr_sz))) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (vnet_hdr_sz < (int)sizeof(struct virtio_net_hdr)) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
tun->vnet_hdr_sz = vnet_hdr_sz;
|
|
|
|
break;
|
|
|
|
|
2014-12-16 21:05:06 +08:00
|
|
|
case TUNGETVNETLE:
|
|
|
|
le = !!(tun->flags & TUN_VNET_LE);
|
|
|
|
if (put_user(le, (int __user *)argp))
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TUNSETVNETLE:
|
|
|
|
if (get_user(le, (int __user *)argp)) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (le)
|
|
|
|
tun->flags |= TUN_VNET_LE;
|
|
|
|
else
|
|
|
|
tun->flags &= ~TUN_VNET_LE;
|
|
|
|
break;
|
|
|
|
|
2015-04-24 20:50:36 +08:00
|
|
|
case TUNGETVNETBE:
|
|
|
|
ret = tun_get_vnet_be(tun, argp);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TUNSETVNETBE:
|
|
|
|
ret = tun_set_vnet_be(tun, argp);
|
|
|
|
break;
|
|
|
|
|
2010-02-14 09:01:10 +08:00
|
|
|
case TUNATTACHFILTER:
|
|
|
|
/* Can be set only for TAPs */
|
|
|
|
ret = -EINVAL;
|
2014-11-19 21:17:31 +08:00
|
|
|
if ((tun->flags & TUN_TYPE_MASK) != IFF_TAP)
|
2010-02-14 09:01:10 +08:00
|
|
|
break;
|
|
|
|
ret = -EFAULT;
|
2012-11-01 03:45:57 +08:00
|
|
|
if (copy_from_user(&tun->fprog, argp, sizeof(tun->fprog)))
|
2010-02-14 09:01:10 +08:00
|
|
|
break;
|
|
|
|
|
2012-11-01 03:46:00 +08:00
|
|
|
ret = tun_attach_filter(tun);
|
2010-02-14 09:01:10 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case TUNDETACHFILTER:
|
|
|
|
/* Can be set only for TAPs */
|
|
|
|
ret = -EINVAL;
|
2014-11-19 21:17:31 +08:00
|
|
|
if ((tun->flags & TUN_TYPE_MASK) != IFF_TAP)
|
2010-02-14 09:01:10 +08:00
|
|
|
break;
|
2012-11-01 03:46:00 +08:00
|
|
|
ret = 0;
|
|
|
|
tun_detach_filter(tun, tun->numqueues);
|
2010-02-14 09:01:10 +08:00
|
|
|
break;
|
|
|
|
|
2013-08-21 18:32:39 +08:00
|
|
|
case TUNGETFILTER:
|
|
|
|
ret = -EINVAL;
|
2014-11-19 21:17:31 +08:00
|
|
|
if ((tun->flags & TUN_TYPE_MASK) != IFF_TAP)
|
2013-08-21 18:32:39 +08:00
|
|
|
break;
|
|
|
|
ret = -EFAULT;
|
|
|
|
if (copy_to_user(argp, &tun->fprog, sizeof(tun->fprog)))
|
|
|
|
break;
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
|
2017-12-04 17:31:23 +08:00
|
|
|
case TUNSETSTEERINGEBPF:
|
2018-01-16 16:31:01 +08:00
|
|
|
ret = tun_set_ebpf(tun, &tun->steering_prog, argp);
|
2017-12-04 17:31:23 +08:00
|
|
|
break;
|
|
|
|
|
2018-01-16 16:31:02 +08:00
|
|
|
case TUNSETFILTEREBPF:
|
|
|
|
ret = tun_set_ebpf(tun, &tun->filter_prog, argp);
|
|
|
|
break;
|
|
|
|
|
2018-11-29 02:12:56 +08:00
|
|
|
case TUNSETCARRIER:
|
|
|
|
ret = -EFAULT;
|
|
|
|
if (copy_from_user(&carrier, argp, sizeof(carrier)))
|
|
|
|
goto unlock;
|
|
|
|
|
|
|
|
ret = tun_net_change_carrier(tun->dev, (bool)carrier);
|
|
|
|
break;
|
|
|
|
|
2019-03-20 17:16:42 +08:00
|
|
|
case TUNGETDEVNETNS:
|
|
|
|
ret = -EPERM;
|
|
|
|
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
|
|
|
|
goto unlock;
|
|
|
|
ret = open_related_ns(&net->ns, get_net_ns);
|
|
|
|
break;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
default:
|
2009-01-20 19:00:40 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
2010-05-18 13:47:34 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2018-04-10 22:28:56 +08:00
|
|
|
if (do_notify)
|
|
|
|
netdev_state_change(tun->dev);
|
|
|
|
|
2009-08-06 22:22:44 +08:00
|
|
|
unlock:
|
|
|
|
rtnl_unlock();
|
|
|
|
if (tun)
|
|
|
|
tun_put(tun);
|
2009-01-20 19:00:40 +08:00
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-11-07 14:52:32 +08:00
|
|
|
static long tun_chr_ioctl(struct file *file,
|
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
return __tun_chr_ioctl(file, cmd, arg, sizeof (struct ifreq));
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
static long tun_chr_compat_ioctl(struct file *file,
|
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
switch (cmd) {
|
|
|
|
case TUNSETIFF:
|
|
|
|
case TUNGETIFF:
|
|
|
|
case TUNSETTXFILTER:
|
|
|
|
case TUNGETSNDBUF:
|
|
|
|
case TUNSETSNDBUF:
|
|
|
|
case SIOCGIFHWADDR:
|
|
|
|
case SIOCSIFHWADDR:
|
|
|
|
arg = (unsigned long)compat_ptr(arg);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
arg = (compat_ulong_t)arg;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* compat_ifreq is shorter than ifreq, so we must not access beyond
|
|
|
|
* the end of that structure. All fields that are used in this
|
|
|
|
* driver are compatible though, we don't need to convert the
|
|
|
|
* contents.
|
|
|
|
*/
|
|
|
|
return __tun_chr_ioctl(file, cmd, arg, sizeof(struct compat_ifreq));
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_COMPAT */
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int tun_chr_fasync(int fd, struct file *file, int on)
|
|
|
|
{
|
2012-11-01 03:45:57 +08:00
|
|
|
struct tun_file *tfile = file->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
int ret;
|
|
|
|
|
2012-11-01 03:45:57 +08:00
|
|
|
if ((ret = fasync_helper(fd, file, on, &tfile->fasync)) < 0)
|
2008-06-20 05:50:37 +08:00
|
|
|
goto out;
|
2006-09-14 01:24:59 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (on) {
|
2017-07-17 11:05:57 +08:00
|
|
|
__f_setown(file, task_pid(current), PIDTYPE_TGID, 0);
|
2012-11-01 03:45:57 +08:00
|
|
|
tfile->flags |= TUN_FASYNC;
|
2006-09-14 01:24:59 +08:00
|
|
|
} else
|
2012-11-01 03:45:57 +08:00
|
|
|
tfile->flags &= ~TUN_FASYNC;
|
2008-06-20 05:50:37 +08:00
|
|
|
ret = 0;
|
|
|
|
out:
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int tun_chr_open(struct inode *inode, struct file * file)
|
|
|
|
{
|
2015-05-09 10:07:08 +08:00
|
|
|
struct net *net = current->nsproxy->net_ns;
|
2009-01-20 19:00:40 +08:00
|
|
|
struct tun_file *tfile;
|
2009-10-14 16:19:46 +08:00
|
|
|
|
2015-05-09 10:07:08 +08:00
|
|
|
tfile = (struct tun_file *)sk_alloc(net, AF_UNSPEC, GFP_KERNEL,
|
2015-05-09 10:09:13 +08:00
|
|
|
&tun_proto, 0);
|
2009-01-20 19:00:40 +08:00
|
|
|
if (!tfile)
|
|
|
|
return -ENOMEM;
|
2018-05-11 10:49:25 +08:00
|
|
|
if (ptr_ring_init(&tfile->tx_ring, 0, GFP_KERNEL)) {
|
|
|
|
sk_free(&tfile->sk);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2018-09-29 05:51:48 +08:00
|
|
|
mutex_init(&tfile->napi_mutex);
|
2014-03-24 02:32:32 +08:00
|
|
|
RCU_INIT_POINTER(tfile->tun, NULL);
|
2012-11-01 03:45:57 +08:00
|
|
|
tfile->flags = 0;
|
tun: Add ability to create tun device with given index
Tun devices cannot be created with ifidex user wants, but it's
required by checkpoint-restore project.
Long time ago such ability was implemented for rtnl_ops-based
interface for creating links (9c7dafbf net: Allow to create links
with given ifindex), but the only API for creating and managing
tuntap devices is ioctl-based and is evolving with adding new ones
(cde8b15f tuntap: add ioctl to attach or detach a file form tuntap
device).
Following that trend, here's how a new ioctl that sets the ifindex
for device, that _will_ be created by TUNSETIFF ioctl looks like.
So those who want a tuntap device with the ifindex N, should open
the tun device, call ioctl(fd, TUNSETIFINDEX, &N), then call TUNSETIFF.
If the index N is busy, then the register_netdev will find this out
and the ioctl would be failed with -EBUSY.
If setifindex is not called, then it will be generated as before.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-08-21 18:31:38 +08:00
|
|
|
tfile->ifindex = 0;
|
2012-11-01 03:45:57 +08:00
|
|
|
|
2019-07-06 03:14:16 +08:00
|
|
|
init_waitqueue_head(&tfile->socket.wq.wait);
|
2012-11-01 03:45:57 +08:00
|
|
|
|
|
|
|
tfile->socket.file = file;
|
|
|
|
tfile->socket.ops = &tun_socket_ops;
|
|
|
|
|
|
|
|
sock_init_data(&tfile->socket, &tfile->sk);
|
|
|
|
|
|
|
|
tfile->sk.sk_write_space = tun_sock_write_space;
|
|
|
|
tfile->sk.sk_sndbuf = INT_MAX;
|
|
|
|
|
2009-01-20 19:00:40 +08:00
|
|
|
file->private_data = tfile;
|
2012-12-14 07:53:30 +08:00
|
|
|
INIT_LIST_HEAD(&tfile->next);
|
2012-11-01 03:45:57 +08:00
|
|
|
|
2013-06-08 14:17:41 +08:00
|
|
|
sock_set_flag(&tfile->sk, SOCK_ZEROCOPY);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tun_chr_close(struct inode *inode, struct file *file)
|
|
|
|
{
|
2009-01-20 19:00:40 +08:00
|
|
|
struct tun_file *tfile = file->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-11-01 03:46:00 +08:00
|
|
|
tun_detach(tfile, true);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-01-29 15:43:31 +08:00
|
|
|
#ifdef CONFIG_PROC_FS
|
2017-09-23 22:36:52 +08:00
|
|
|
static void tun_chr_show_fdinfo(struct seq_file *m, struct file *file)
|
2014-01-29 15:43:31 +08:00
|
|
|
{
|
2017-09-23 22:36:52 +08:00
|
|
|
struct tun_file *tfile = file->private_data;
|
2014-01-29 15:43:31 +08:00
|
|
|
struct tun_struct *tun;
|
|
|
|
struct ifreq ifr;
|
|
|
|
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
|
|
|
|
|
|
rtnl_lock();
|
2017-09-23 22:36:52 +08:00
|
|
|
tun = tun_get(tfile);
|
2014-01-29 15:43:31 +08:00
|
|
|
if (tun)
|
2019-03-20 17:16:53 +08:00
|
|
|
tun_get_iff(tun, &ifr);
|
2014-01-29 15:43:31 +08:00
|
|
|
rtnl_unlock();
|
|
|
|
|
|
|
|
if (tun)
|
|
|
|
tun_put(tun);
|
|
|
|
|
2014-09-30 07:08:25 +08:00
|
|
|
seq_printf(m, "iff:\t%s\n", ifr.ifr_name);
|
2014-01-29 15:43:31 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-02-12 16:55:34 +08:00
|
|
|
static const struct file_operations tun_fops = {
|
2006-09-14 01:24:59 +08:00
|
|
|
.owner = THIS_MODULE,
|
2005-04-17 06:20:36 +08:00
|
|
|
.llseek = no_llseek,
|
2014-11-08 02:52:07 +08:00
|
|
|
.read_iter = tun_chr_read_iter,
|
2014-06-20 03:36:49 +08:00
|
|
|
.write_iter = tun_chr_write_iter,
|
2005-04-17 06:20:36 +08:00
|
|
|
.poll = tun_chr_poll,
|
2009-11-07 14:52:32 +08:00
|
|
|
.unlocked_ioctl = tun_chr_ioctl,
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
.compat_ioctl = tun_chr_compat_ioctl,
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
.open = tun_chr_open,
|
|
|
|
.release = tun_chr_close,
|
2014-01-29 15:43:31 +08:00
|
|
|
.fasync = tun_chr_fasync,
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
|
|
.show_fdinfo = tun_chr_show_fdinfo,
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct miscdevice tun_miscdev = {
|
|
|
|
.minor = TUN_MINOR,
|
|
|
|
.name = "tun",
|
2009-09-19 05:01:12 +08:00
|
|
|
.nodename = "net/tun",
|
2005-04-17 06:20:36 +08:00
|
|
|
.fops = &tun_fops,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* ethtool interface */
|
|
|
|
|
2018-06-03 05:49:53 +08:00
|
|
|
static void tun_default_link_ksettings(struct net_device *dev,
|
|
|
|
struct ethtool_link_ksettings *cmd)
|
2017-03-12 05:03:50 +08:00
|
|
|
{
|
|
|
|
ethtool_link_ksettings_zero_link_mode(cmd, supported);
|
|
|
|
ethtool_link_ksettings_zero_link_mode(cmd, advertising);
|
|
|
|
cmd->base.speed = SPEED_10;
|
|
|
|
cmd->base.duplex = DUPLEX_FULL;
|
|
|
|
cmd->base.port = PORT_TP;
|
|
|
|
cmd->base.phy_address = 0;
|
|
|
|
cmd->base.autoneg = AUTONEG_DISABLE;
|
2018-06-03 05:49:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int tun_get_link_ksettings(struct net_device *dev,
|
|
|
|
struct ethtool_link_ksettings *cmd)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
|
|
|
memcpy(cmd, &tun->link_ksettings, sizeof(*cmd));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tun_set_link_ksettings(struct net_device *dev,
|
|
|
|
const struct ethtool_link_ksettings *cmd)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
|
|
|
memcpy(&tun->link_ksettings, cmd, sizeof(*cmd));
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
2011-11-15 22:59:53 +08:00
|
|
|
strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
|
|
|
|
strlcpy(info->version, DRV_VERSION, sizeof(info->version));
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
switch (tun->flags & TUN_TYPE_MASK) {
|
2014-11-19 21:17:31 +08:00
|
|
|
case IFF_TUN:
|
2011-11-15 22:59:53 +08:00
|
|
|
strlcpy(info->bus_info, "tun", sizeof(info->bus_info));
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2014-11-19 21:17:31 +08:00
|
|
|
case IFF_TAP:
|
2011-11-15 22:59:53 +08:00
|
|
|
strlcpy(info->bus_info, "tap", sizeof(info->bus_info));
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 tun_get_msglevel(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
2020-03-05 00:24:14 +08:00
|
|
|
|
|
|
|
return tun->msg_enable;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_set_msglevel(struct net_device *dev, u32 value)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
2020-03-05 00:24:14 +08:00
|
|
|
|
|
|
|
tun->msg_enable = value;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2017-01-18 15:02:03 +08:00
|
|
|
static int tun_get_coalesce(struct net_device *dev,
|
|
|
|
struct ethtool_coalesce *ec)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
|
|
|
ec->rx_max_coalesced_frames = tun->rx_batched;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tun_set_coalesce(struct net_device *dev,
|
|
|
|
struct ethtool_coalesce *ec)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
|
|
|
if (ec->rx_max_coalesced_frames > NAPI_POLL_WEIGHT)
|
|
|
|
tun->rx_batched = NAPI_POLL_WEIGHT;
|
|
|
|
else
|
|
|
|
tun->rx_batched = ec->rx_max_coalesced_frames;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-09-14 02:30:00 +08:00
|
|
|
static const struct ethtool_ops tun_ethtool_ops = {
|
2020-03-06 09:05:58 +08:00
|
|
|
.supported_coalesce_params = ETHTOOL_COALESCE_RX_MAX_FRAMES,
|
2005-04-17 06:20:36 +08:00
|
|
|
.get_drvinfo = tun_get_drvinfo,
|
|
|
|
.get_msglevel = tun_get_msglevel,
|
|
|
|
.set_msglevel = tun_set_msglevel,
|
2010-07-27 21:53:43 +08:00
|
|
|
.get_link = ethtool_op_get_link,
|
2013-07-20 01:40:10 +08:00
|
|
|
.get_ts_info = ethtool_op_get_ts_info,
|
2017-01-18 15:02:03 +08:00
|
|
|
.get_coalesce = tun_get_coalesce,
|
|
|
|
.set_coalesce = tun_set_coalesce,
|
2017-03-12 05:03:50 +08:00
|
|
|
.get_link_ksettings = tun_get_link_ksettings,
|
2018-06-03 05:49:53 +08:00
|
|
|
.set_link_ksettings = tun_set_link_ksettings,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2016-06-30 14:45:36 +08:00
|
|
|
static int tun_queue_resize(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
struct net_device *dev = tun->dev;
|
|
|
|
struct tun_file *tfile;
|
2018-01-04 11:14:27 +08:00
|
|
|
struct ptr_ring **rings;
|
2016-06-30 14:45:36 +08:00
|
|
|
int n = tun->numqueues + tun->numdisabled;
|
|
|
|
int ret, i;
|
|
|
|
|
2018-01-04 11:14:27 +08:00
|
|
|
rings = kmalloc_array(n, sizeof(*rings), GFP_KERNEL);
|
|
|
|
if (!rings)
|
2016-06-30 14:45:36 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
for (i = 0; i < tun->numqueues; i++) {
|
|
|
|
tfile = rtnl_dereference(tun->tfiles[i]);
|
2018-01-04 11:14:27 +08:00
|
|
|
rings[i] = &tfile->tx_ring;
|
2016-06-30 14:45:36 +08:00
|
|
|
}
|
|
|
|
list_for_each_entry(tfile, &tun->disabled, next)
|
2018-01-04 11:14:27 +08:00
|
|
|
rings[i++] = &tfile->tx_ring;
|
2016-06-30 14:45:36 +08:00
|
|
|
|
2018-01-04 11:14:27 +08:00
|
|
|
ret = ptr_ring_resize_multiple(rings, n,
|
|
|
|
dev->tx_queue_len, GFP_KERNEL,
|
2018-01-04 11:14:28 +08:00
|
|
|
tun_ptr_free);
|
2016-06-30 14:45:36 +08:00
|
|
|
|
2018-01-04 11:14:27 +08:00
|
|
|
kfree(rings);
|
2016-06-30 14:45:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tun_device_event(struct notifier_block *unused,
|
|
|
|
unsigned long event, void *ptr)
|
|
|
|
{
|
|
|
|
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
2019-06-17 21:26:36 +08:00
|
|
|
int i;
|
2016-06-30 14:45:36 +08:00
|
|
|
|
2016-07-07 06:44:20 +08:00
|
|
|
if (dev->rtnl_link_ops != &tun_link_ops)
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
|
2016-06-30 14:45:36 +08:00
|
|
|
switch (event) {
|
|
|
|
case NETDEV_CHANGE_TX_QUEUE_LEN:
|
|
|
|
if (tun_queue_resize(tun))
|
|
|
|
return NOTIFY_BAD;
|
|
|
|
break;
|
2019-06-17 21:26:36 +08:00
|
|
|
case NETDEV_UP:
|
|
|
|
for (i = 0; i < tun->numqueues; i++) {
|
|
|
|
struct tun_file *tfile;
|
|
|
|
|
|
|
|
tfile = rtnl_dereference(tun->tfiles[i]);
|
|
|
|
tfile->socket.sk->sk_write_space(tfile->socket.sk);
|
|
|
|
}
|
|
|
|
break;
|
2016-06-30 14:45:36 +08:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct notifier_block tun_notifier_block __read_mostly = {
|
|
|
|
.notifier_call = tun_device_event,
|
|
|
|
};
|
2008-04-16 15:40:46 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int __init tun_init(void)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
2011-03-02 15:18:10 +08:00
|
|
|
pr_info("%s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-01-22 08:02:16 +08:00
|
|
|
ret = rtnl_link_register(&tun_link_ops);
|
2008-04-16 15:40:46 +08:00
|
|
|
if (ret) {
|
2011-03-02 15:18:10 +08:00
|
|
|
pr_err("Can't register link_ops\n");
|
2009-01-22 08:02:16 +08:00
|
|
|
goto err_linkops;
|
2008-04-16 15:40:46 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = misc_register(&tun_miscdev);
|
2008-04-16 15:40:46 +08:00
|
|
|
if (ret) {
|
2011-03-02 15:18:10 +08:00
|
|
|
pr_err("Can't register misc device %d\n", TUN_MINOR);
|
2008-04-16 15:40:46 +08:00
|
|
|
goto err_misc;
|
|
|
|
}
|
2016-06-30 14:45:36 +08:00
|
|
|
|
2017-07-20 17:41:34 +08:00
|
|
|
ret = register_netdevice_notifier(&tun_notifier_block);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Can't register netdevice notifier\n");
|
|
|
|
goto err_notifier;
|
|
|
|
}
|
|
|
|
|
2009-01-22 08:02:16 +08:00
|
|
|
return 0;
|
2017-07-20 17:41:34 +08:00
|
|
|
|
|
|
|
err_notifier:
|
|
|
|
misc_deregister(&tun_miscdev);
|
2008-04-16 15:40:46 +08:00
|
|
|
err_misc:
|
2009-01-22 08:02:16 +08:00
|
|
|
rtnl_link_unregister(&tun_link_ops);
|
|
|
|
err_linkops:
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_cleanup(void)
|
|
|
|
{
|
2006-09-14 01:24:59 +08:00
|
|
|
misc_deregister(&tun_miscdev);
|
2009-01-22 08:02:16 +08:00
|
|
|
rtnl_link_unregister(&tun_link_ops);
|
2016-06-30 14:45:36 +08:00
|
|
|
unregister_netdevice_notifier(&tun_notifier_block);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2010-01-14 14:17:09 +08:00
|
|
|
/* Get an underlying socket object from tun file. Returns error unless file is
|
|
|
|
* attached to a device. The returned object works like a packet socket, it
|
|
|
|
* can be used for sock_sendmsg/sock_recvmsg. The caller is responsible for
|
|
|
|
* holding a reference to the file for as long as the socket is in use. */
|
|
|
|
struct socket *tun_get_socket(struct file *file)
|
|
|
|
{
|
2012-11-01 03:45:58 +08:00
|
|
|
struct tun_file *tfile;
|
2010-01-14 14:17:09 +08:00
|
|
|
if (file->f_op != &tun_fops)
|
|
|
|
return ERR_PTR(-EINVAL);
|
2012-11-01 03:45:58 +08:00
|
|
|
tfile = file->private_data;
|
|
|
|
if (!tfile)
|
2010-01-14 14:17:09 +08:00
|
|
|
return ERR_PTR(-EBADFD);
|
2012-11-01 03:45:57 +08:00
|
|
|
return &tfile->socket;
|
2010-01-14 14:17:09 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tun_get_socket);
|
|
|
|
|
2018-01-04 11:14:27 +08:00
|
|
|
struct ptr_ring *tun_get_tx_ring(struct file *file)
|
2017-05-17 12:14:41 +08:00
|
|
|
{
|
|
|
|
struct tun_file *tfile;
|
|
|
|
|
|
|
|
if (file->f_op != &tun_fops)
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
tfile = file->private_data;
|
|
|
|
if (!tfile)
|
|
|
|
return ERR_PTR(-EBADFD);
|
2018-01-04 11:14:27 +08:00
|
|
|
return &tfile->tx_ring;
|
2017-05-17 12:14:41 +08:00
|
|
|
}
|
2018-01-04 11:14:27 +08:00
|
|
|
EXPORT_SYMBOL_GPL(tun_get_tx_ring);
|
2017-05-17 12:14:41 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
module_init(tun_init);
|
|
|
|
module_exit(tun_cleanup);
|
|
|
|
MODULE_DESCRIPTION(DRV_DESCRIPTION);
|
|
|
|
MODULE_AUTHOR(DRV_COPYRIGHT);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_ALIAS_MISCDEV(TUN_MINOR);
|
driver core: add devname module aliases to allow module on-demand auto-loading
This adds:
alias: devname:<name>
to some common kernel modules, which will allow the on-demand loading
of the kernel module when the device node is accessed.
Ideally all these modules would be compiled-in, but distros seems too
much in love with their modularization that we need to cover the common
cases with this new facility. It will allow us to remove a bunch of pretty
useless init scripts and modprobes from init scripts.
The static device node aliases will be carried in the module itself. The
program depmod will extract this information to a file in the module directory:
$ cat /lib/modules/2.6.34-00650-g537b60d-dirty/modules.devname
# Device nodes to trigger on-demand module loading.
microcode cpu/microcode c10:184
fuse fuse c10:229
ppp_generic ppp c108:0
tun net/tun c10:200
dm_mod mapper/control c10:235
Udev will pick up the depmod created file on startup and create all the
static device nodes which the kernel modules specify, so that these modules
get automatically loaded when the device node is accessed:
$ /sbin/udevd --debug
...
static_dev_create_from_modules: mknod '/dev/cpu/microcode' c10:184
static_dev_create_from_modules: mknod '/dev/fuse' c10:229
static_dev_create_from_modules: mknod '/dev/ppp' c108:0
static_dev_create_from_modules: mknod '/dev/net/tun' c10:200
static_dev_create_from_modules: mknod '/dev/mapper/control' c10:235
udev_rules_apply_static_dev_perms: chmod '/dev/net/tun' 0666
udev_rules_apply_static_dev_perms: chmod '/dev/fuse' 0666
A few device nodes are switched to statically allocated numbers, to allow
the static nodes to work. This might also useful for systems which still run
a plain static /dev, which is completely unsafe to use with any dynamic minor
numbers.
Note:
The devname aliases must be limited to the *common* and *single*instance*
device nodes, like the misc devices, and never be used for conceptually limited
systems like the loop devices, which should rather get fixed properly and get a
control node for losetup to talk to, instead of creating a random number of
device nodes in advance, regardless if they are ever used.
This facility is to hide the mess distros are creating with too modualized
kernels, and just to hide that these modules are not compiled-in, and not to
paper-over broken concepts. Thanks! :)
Cc: Greg Kroah-Hartman <gregkh@suse.de>
Cc: David S. Miller <davem@davemloft.net>
Cc: Miklos Szeredi <miklos@szeredi.hu>
Cc: Chris Mason <chris.mason@oracle.com>
Cc: Alasdair G Kergon <agk@redhat.com>
Cc: Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
Cc: Ian Kent <raven@themaw.net>
Signed-Off-By: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2010-05-21 00:07:20 +08:00
|
|
|
MODULE_ALIAS("devname:net/tun");
|