Merge branch 'dev_base_lock-remove'
Eric Dumazet says: ==================== net: complete dev_base_lock removal Back in 2009 we started an effort to get rid of dev_base_lock in favor of RCU. It is time to finish this work. Say goodbye to dev_base_lock ! v4: rebase, and move dev_addr_sem to net/core/dev.h in patch 06/13 (Jakub) v3: I misread kbot reports, the issue was with dev->operstate (patch 10/13) So dev->reg_state is back to u8, and dev->operstate becomes an u32. Sorry for the noise. v2: dev->reg_state must be a standard enum, some arches do not support cmpxchg() on u8. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
7c754e6a6c
@ -252,8 +252,8 @@ ndo_eth_ioctl:
|
||||
Context: process
|
||||
|
||||
ndo_get_stats:
|
||||
Synchronization: rtnl_lock() semaphore, dev_base_lock rwlock, or RCU.
|
||||
Context: atomic (can't sleep under rwlock or RCU)
|
||||
Synchronization: rtnl_lock() semaphore, or RCU.
|
||||
Context: atomic (can't sleep under RCU)
|
||||
|
||||
ndo_start_xmit:
|
||||
Synchronization: __netif_tx_lock spinlock.
|
||||
|
@ -872,7 +872,7 @@ error:
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/* dev_base_lock rwlock held, nominally process context */
|
||||
/* rcu_read_lock potentially held, nominally process context */
|
||||
static void enic_get_stats(struct net_device *netdev,
|
||||
struct rtnl_link_stats64 *net_stats)
|
||||
{
|
||||
|
@ -1761,7 +1761,7 @@ static void nv_get_stats(int cpu, struct fe_priv *np,
|
||||
/*
|
||||
* nv_get_stats64: dev->ndo_get_stats64 function
|
||||
* Get latest stats value from the nic.
|
||||
* Called with read_lock(&dev_base_lock) held for read -
|
||||
* Called with rcu_read_lock() held -
|
||||
* only synchronized against unregister_netdevice.
|
||||
*/
|
||||
static void
|
||||
@ -3090,7 +3090,7 @@ static void set_bufsize(struct net_device *dev)
|
||||
|
||||
/*
|
||||
* nv_change_mtu: dev->change_mtu function
|
||||
* Called with dev_base_lock held for read.
|
||||
* Called with RTNL held for read.
|
||||
*/
|
||||
static int nv_change_mtu(struct net_device *dev, int new_mtu)
|
||||
{
|
||||
|
@ -595,7 +595,7 @@ void efx_stop_all(struct efx_nic *efx)
|
||||
efx_stop_datapath(efx);
|
||||
}
|
||||
|
||||
/* Context: process, dev_base_lock or RTNL held, non-blocking. */
|
||||
/* Context: process, rcu_read_lock or RTNL held, non-blocking. */
|
||||
void efx_net_stats(struct net_device *net_dev, struct rtnl_link_stats64 *stats)
|
||||
{
|
||||
struct efx_nic *efx = efx_netdev_priv(net_dev);
|
||||
|
@ -2085,7 +2085,7 @@ int ef4_net_stop(struct net_device *net_dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Context: process, dev_base_lock or RTNL held, non-blocking. */
|
||||
/* Context: process, rcu_read_lock or RTNL held, non-blocking. */
|
||||
static void ef4_net_stats(struct net_device *net_dev,
|
||||
struct rtnl_link_stats64 *stats)
|
||||
{
|
||||
|
@ -605,7 +605,7 @@ static size_t efx_siena_update_stats_atomic(struct efx_nic *efx, u64 *full_stats
|
||||
return efx->type->update_stats(efx, full_stats, core_stats);
|
||||
}
|
||||
|
||||
/* Context: process, dev_base_lock or RTNL held, non-blocking. */
|
||||
/* Context: process, rcu_read_lock or RTNL held, non-blocking. */
|
||||
void efx_siena_net_stats(struct net_device *net_dev,
|
||||
struct rtnl_link_stats64 *stats)
|
||||
{
|
||||
|
@ -1815,6 +1815,15 @@ enum netdev_stat_type {
|
||||
NETDEV_PCPU_STAT_DSTATS, /* struct pcpu_dstats */
|
||||
};
|
||||
|
||||
enum netdev_reg_state {
|
||||
NETREG_UNINITIALIZED = 0,
|
||||
NETREG_REGISTERED, /* completed register_netdevice */
|
||||
NETREG_UNREGISTERING, /* called unregister_netdevice */
|
||||
NETREG_UNREGISTERED, /* completed unregister todo */
|
||||
NETREG_RELEASED, /* called free_netdev */
|
||||
NETREG_DUMMY, /* dummy device for NAPI poll */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct net_device - The DEVICE structure.
|
||||
*
|
||||
@ -2249,7 +2258,7 @@ struct net_device {
|
||||
const struct tlsdev_ops *tlsdev_ops;
|
||||
#endif
|
||||
|
||||
unsigned char operstate;
|
||||
unsigned int operstate;
|
||||
unsigned char link_mode;
|
||||
|
||||
unsigned char if_port;
|
||||
@ -2372,13 +2381,7 @@ struct net_device {
|
||||
|
||||
struct list_head link_watch_list;
|
||||
|
||||
enum { NETREG_UNINITIALIZED=0,
|
||||
NETREG_REGISTERED, /* completed register_netdevice */
|
||||
NETREG_UNREGISTERING, /* called unregister_netdevice */
|
||||
NETREG_UNREGISTERED, /* completed unregister todo */
|
||||
NETREG_RELEASED, /* called free_netdev */
|
||||
NETREG_DUMMY, /* dummy device for NAPI poll */
|
||||
} reg_state:8;
|
||||
u8 reg_state;
|
||||
|
||||
bool dismantle;
|
||||
|
||||
@ -3074,8 +3077,6 @@ int call_netdevice_notifiers(unsigned long val, struct net_device *dev);
|
||||
int call_netdevice_notifiers_info(unsigned long val,
|
||||
struct netdev_notifier_info *info);
|
||||
|
||||
extern rwlock_t dev_base_lock; /* Device list lock */
|
||||
|
||||
#define for_each_netdev(net, d) \
|
||||
list_for_each_entry(d, &(net)->dev_base_head, dev_list)
|
||||
#define for_each_netdev_reverse(net, d) \
|
||||
@ -5254,7 +5255,9 @@ static inline const char *netdev_name(const struct net_device *dev)
|
||||
|
||||
static inline const char *netdev_reg_state(const struct net_device *dev)
|
||||
{
|
||||
switch (dev->reg_state) {
|
||||
u8 reg_state = READ_ONCE(dev->reg_state);
|
||||
|
||||
switch (reg_state) {
|
||||
case NETREG_UNINITIALIZED: return " (uninitialized)";
|
||||
case NETREG_REGISTERED: return "";
|
||||
case NETREG_UNREGISTERING: return " (unregistering)";
|
||||
@ -5263,7 +5266,7 @@ static inline const char *netdev_reg_state(const struct net_device *dev)
|
||||
case NETREG_DUMMY: return " (dummy)";
|
||||
}
|
||||
|
||||
WARN_ONCE(1, "%s: unknown reg_state %d\n", dev->name, dev->reg_state);
|
||||
WARN_ONCE(1, "%s: unknown reg_state %d\n", dev->name, reg_state);
|
||||
return " (unknown)";
|
||||
}
|
||||
|
||||
|
@ -172,4 +172,6 @@ rtnl_notify_needed(const struct net *net, u16 nlflags, u32 group)
|
||||
return (nlflags & NLM_F_ECHO) || rtnl_has_listeners(net, group);
|
||||
}
|
||||
|
||||
void netdev_set_operstate(struct net_device *dev, int newstate);
|
||||
|
||||
#endif /* __LINUX_RTNETLINK_H */
|
||||
|
@ -455,7 +455,8 @@ static int br_fill_ifinfo(struct sk_buff *skb,
|
||||
u32 filter_mask, const struct net_device *dev,
|
||||
bool getlink)
|
||||
{
|
||||
u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN;
|
||||
u8 operstate = netif_running(dev) ? READ_ONCE(dev->operstate) :
|
||||
IF_OPER_DOWN;
|
||||
struct nlattr *af = NULL;
|
||||
struct net_bridge *br;
|
||||
struct ifinfomsg *hdr;
|
||||
|
@ -168,28 +168,6 @@ static int call_netdevice_notifiers_extack(unsigned long val,
|
||||
struct net_device *dev,
|
||||
struct netlink_ext_ack *extack);
|
||||
|
||||
/*
|
||||
* The @dev_base_head list is protected by @dev_base_lock and the rtnl
|
||||
* semaphore.
|
||||
*
|
||||
* Pure readers hold dev_base_lock for reading, or rcu_read_lock()
|
||||
*
|
||||
* Writers must hold the rtnl semaphore while they loop through the
|
||||
* dev_base_head list, and hold dev_base_lock for writing when they do the
|
||||
* actual updates. This allows pure readers to access the list even
|
||||
* while a writer is preparing to update it.
|
||||
*
|
||||
* To put it another way, dev_base_lock is held for writing only to
|
||||
* protect against pure readers; the rtnl semaphore provides the
|
||||
* protection against other writers.
|
||||
*
|
||||
* See, for example usages, register_netdevice() and
|
||||
* unregister_netdevice(), which must be called with the rtnl
|
||||
* semaphore held.
|
||||
*/
|
||||
DEFINE_RWLOCK(dev_base_lock);
|
||||
EXPORT_SYMBOL(dev_base_lock);
|
||||
|
||||
static DEFINE_MUTEX(ifalias_mutex);
|
||||
|
||||
/* protects napi_hash addition/deletion and napi_gen_id */
|
||||
@ -395,12 +373,10 @@ static void list_netdevice(struct net_device *dev)
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
write_lock(&dev_base_lock);
|
||||
list_add_tail_rcu(&dev->dev_list, &net->dev_base_head);
|
||||
netdev_name_node_add(net, dev->name_node);
|
||||
hlist_add_head_rcu(&dev->index_hlist,
|
||||
dev_index_hash(net, dev->ifindex));
|
||||
write_unlock(&dev_base_lock);
|
||||
|
||||
netdev_for_each_altname(dev, name_node)
|
||||
netdev_name_node_add(net, name_node);
|
||||
@ -414,7 +390,7 @@ static void list_netdevice(struct net_device *dev)
|
||||
/* Device list removal
|
||||
* caller must respect a RCU grace period before freeing/reusing dev
|
||||
*/
|
||||
static void unlist_netdevice(struct net_device *dev, bool lock)
|
||||
static void unlist_netdevice(struct net_device *dev)
|
||||
{
|
||||
struct netdev_name_node *name_node;
|
||||
struct net *net = dev_net(dev);
|
||||
@ -427,13 +403,9 @@ static void unlist_netdevice(struct net_device *dev, bool lock)
|
||||
netdev_name_node_del(name_node);
|
||||
|
||||
/* Unlink dev from the device chain */
|
||||
if (lock)
|
||||
write_lock(&dev_base_lock);
|
||||
list_del_rcu(&dev->dev_list);
|
||||
netdev_name_node_del(dev->name_node);
|
||||
hlist_del_rcu(&dev->index_hlist);
|
||||
if (lock)
|
||||
write_unlock(&dev_base_lock);
|
||||
|
||||
dev_base_seq_inc(dev_net(dev));
|
||||
}
|
||||
@ -754,9 +726,9 @@ EXPORT_SYMBOL_GPL(dev_fill_forward_path);
|
||||
* @net: the applicable net namespace
|
||||
* @name: name to find
|
||||
*
|
||||
* Find an interface by name. Must be called under RTNL semaphore
|
||||
* or @dev_base_lock. If the name is found a pointer to the device
|
||||
* is returned. If the name is not found then %NULL is returned. The
|
||||
* Find an interface by name. Must be called under RTNL semaphore.
|
||||
* If the name is found a pointer to the device is returned.
|
||||
* If the name is not found then %NULL is returned. The
|
||||
* reference counters are not incremented so the caller must be
|
||||
* careful with locks.
|
||||
*/
|
||||
@ -837,8 +809,7 @@ EXPORT_SYMBOL(netdev_get_by_name);
|
||||
* Search for an interface by index. Returns %NULL if the device
|
||||
* is not found or a pointer to the device. The device has not
|
||||
* had its reference counter increased so the caller must be careful
|
||||
* about locking. The caller must hold either the RTNL semaphore
|
||||
* or @dev_base_lock.
|
||||
* about locking. The caller must hold the RTNL semaphore.
|
||||
*/
|
||||
|
||||
struct net_device *__dev_get_by_index(struct net *net, int ifindex)
|
||||
@ -1228,13 +1199,13 @@ int dev_change_name(struct net_device *dev, const char *newname)
|
||||
dev->flags & IFF_UP ? " (while UP)" : "");
|
||||
|
||||
old_assign_type = dev->name_assign_type;
|
||||
dev->name_assign_type = NET_NAME_RENAMED;
|
||||
WRITE_ONCE(dev->name_assign_type, NET_NAME_RENAMED);
|
||||
|
||||
rollback:
|
||||
ret = device_rename(&dev->dev, dev->name);
|
||||
if (ret) {
|
||||
memcpy(dev->name, oldname, IFNAMSIZ);
|
||||
dev->name_assign_type = old_assign_type;
|
||||
WRITE_ONCE(dev->name_assign_type, old_assign_type);
|
||||
up_write(&devnet_rename_sem);
|
||||
return ret;
|
||||
}
|
||||
@ -1243,15 +1214,11 @@ rollback:
|
||||
|
||||
netdev_adjacent_rename_links(dev, oldname);
|
||||
|
||||
write_lock(&dev_base_lock);
|
||||
netdev_name_node_del(dev->name_node);
|
||||
write_unlock(&dev_base_lock);
|
||||
|
||||
synchronize_net();
|
||||
|
||||
write_lock(&dev_base_lock);
|
||||
netdev_name_node_add(net, dev->name_node);
|
||||
write_unlock(&dev_base_lock);
|
||||
|
||||
ret = call_netdevice_notifiers(NETDEV_CHANGENAME, dev);
|
||||
ret = notifier_to_errno(ret);
|
||||
@ -1263,7 +1230,7 @@ rollback:
|
||||
down_write(&devnet_rename_sem);
|
||||
memcpy(dev->name, oldname, IFNAMSIZ);
|
||||
memcpy(oldname, newname, IFNAMSIZ);
|
||||
dev->name_assign_type = old_assign_type;
|
||||
WRITE_ONCE(dev->name_assign_type, old_assign_type);
|
||||
old_assign_type = NET_NAME_RENAMED;
|
||||
goto rollback;
|
||||
} else {
|
||||
@ -8993,7 +8960,7 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa,
|
||||
}
|
||||
EXPORT_SYMBOL(dev_set_mac_address);
|
||||
|
||||
static DECLARE_RWSEM(dev_addr_sem);
|
||||
DECLARE_RWSEM(dev_addr_sem);
|
||||
|
||||
int dev_set_mac_address_user(struct net_device *dev, struct sockaddr *sa,
|
||||
struct netlink_ext_ack *extack)
|
||||
@ -10338,9 +10305,9 @@ int register_netdevice(struct net_device *dev)
|
||||
goto err_ifindex_release;
|
||||
|
||||
ret = netdev_register_kobject(dev);
|
||||
write_lock(&dev_base_lock);
|
||||
dev->reg_state = ret ? NETREG_UNREGISTERED : NETREG_REGISTERED;
|
||||
write_unlock(&dev_base_lock);
|
||||
|
||||
WRITE_ONCE(dev->reg_state, ret ? NETREG_UNREGISTERED : NETREG_REGISTERED);
|
||||
|
||||
if (ret)
|
||||
goto err_uninit_notify;
|
||||
|
||||
@ -10629,9 +10596,7 @@ void netdev_run_todo(void)
|
||||
continue;
|
||||
}
|
||||
|
||||
write_lock(&dev_base_lock);
|
||||
dev->reg_state = NETREG_UNREGISTERED;
|
||||
write_unlock(&dev_base_lock);
|
||||
WRITE_ONCE(dev->reg_state, NETREG_UNREGISTERED);
|
||||
linkwatch_sync_dev(dev);
|
||||
}
|
||||
|
||||
@ -11050,7 +11015,7 @@ void free_netdev(struct net_device *dev)
|
||||
}
|
||||
|
||||
BUG_ON(dev->reg_state != NETREG_UNREGISTERED);
|
||||
dev->reg_state = NETREG_RELEASED;
|
||||
WRITE_ONCE(dev->reg_state, NETREG_RELEASED);
|
||||
|
||||
/* will free via device release */
|
||||
put_device(&dev->dev);
|
||||
@ -11138,10 +11103,8 @@ void unregister_netdevice_many_notify(struct list_head *head,
|
||||
|
||||
list_for_each_entry(dev, head, unreg_list) {
|
||||
/* And unlink it from device chain. */
|
||||
write_lock(&dev_base_lock);
|
||||
unlist_netdevice(dev, false);
|
||||
dev->reg_state = NETREG_UNREGISTERING;
|
||||
write_unlock(&dev_base_lock);
|
||||
unlist_netdevice(dev);
|
||||
WRITE_ONCE(dev->reg_state, NETREG_UNREGISTERING);
|
||||
}
|
||||
flush_all_backlogs();
|
||||
|
||||
@ -11323,7 +11286,7 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,
|
||||
dev_close(dev);
|
||||
|
||||
/* And unlink it from device chain */
|
||||
unlist_netdevice(dev, true);
|
||||
unlist_netdevice(dev);
|
||||
|
||||
synchronize_net();
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#define _NET_CORE_DEV_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/rwsem.h>
|
||||
|
||||
struct net;
|
||||
struct net_device;
|
||||
@ -46,6 +47,8 @@ extern int weight_p;
|
||||
extern int dev_weight_rx_bias;
|
||||
extern int dev_weight_tx_bias;
|
||||
|
||||
extern struct rw_semaphore dev_addr_sem;
|
||||
|
||||
/* rtnl helpers */
|
||||
extern struct list_head net_todo_list;
|
||||
void netdev_run_todo(void);
|
||||
|
@ -33,7 +33,7 @@ static DECLARE_DELAYED_WORK(linkwatch_work, linkwatch_event);
|
||||
static LIST_HEAD(lweventlist);
|
||||
static DEFINE_SPINLOCK(lweventlist_lock);
|
||||
|
||||
static unsigned char default_operstate(const struct net_device *dev)
|
||||
static unsigned int default_operstate(const struct net_device *dev)
|
||||
{
|
||||
if (netif_testing(dev))
|
||||
return IF_OPER_TESTING;
|
||||
@ -62,16 +62,13 @@ static unsigned char default_operstate(const struct net_device *dev)
|
||||
return IF_OPER_UP;
|
||||
}
|
||||
|
||||
|
||||
static void rfc2863_policy(struct net_device *dev)
|
||||
{
|
||||
unsigned char operstate = default_operstate(dev);
|
||||
unsigned int operstate = default_operstate(dev);
|
||||
|
||||
if (operstate == dev->operstate)
|
||||
if (operstate == READ_ONCE(dev->operstate))
|
||||
return;
|
||||
|
||||
write_lock(&dev_base_lock);
|
||||
|
||||
switch(dev->link_mode) {
|
||||
case IF_LINK_MODE_TESTING:
|
||||
if (operstate == IF_OPER_UP)
|
||||
@ -87,9 +84,7 @@ static void rfc2863_policy(struct net_device *dev)
|
||||
break;
|
||||
}
|
||||
|
||||
dev->operstate = operstate;
|
||||
|
||||
write_unlock(&dev_base_lock);
|
||||
WRITE_ONCE(dev->operstate, operstate);
|
||||
}
|
||||
|
||||
|
||||
|
@ -34,10 +34,10 @@ static const char fmt_dec[] = "%d\n";
|
||||
static const char fmt_ulong[] = "%lu\n";
|
||||
static const char fmt_u64[] = "%llu\n";
|
||||
|
||||
/* Caller holds RTNL or dev_base_lock */
|
||||
/* Caller holds RTNL or RCU */
|
||||
static inline int dev_isalive(const struct net_device *dev)
|
||||
{
|
||||
return dev->reg_state <= NETREG_REGISTERED;
|
||||
return READ_ONCE(dev->reg_state) <= NETREG_REGISTERED;
|
||||
}
|
||||
|
||||
/* use same locking rules as GIF* ioctl's */
|
||||
@ -48,10 +48,10 @@ static ssize_t netdev_show(const struct device *dev,
|
||||
struct net_device *ndev = to_net_dev(dev);
|
||||
ssize_t ret = -EINVAL;
|
||||
|
||||
read_lock(&dev_base_lock);
|
||||
rcu_read_lock();
|
||||
if (dev_isalive(ndev))
|
||||
ret = (*format)(ndev, buf);
|
||||
read_unlock(&dev_base_lock);
|
||||
rcu_read_unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -60,7 +60,7 @@ static ssize_t netdev_show(const struct device *dev,
|
||||
#define NETDEVICE_SHOW(field, format_string) \
|
||||
static ssize_t format_##field(const struct net_device *dev, char *buf) \
|
||||
{ \
|
||||
return sysfs_emit(buf, format_string, dev->field); \
|
||||
return sysfs_emit(buf, format_string, READ_ONCE(dev->field)); \
|
||||
} \
|
||||
static ssize_t field##_show(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
@ -125,7 +125,7 @@ static DEVICE_ATTR_RO(iflink);
|
||||
|
||||
static ssize_t format_name_assign_type(const struct net_device *dev, char *buf)
|
||||
{
|
||||
return sysfs_emit(buf, fmt_dec, dev->name_assign_type);
|
||||
return sysfs_emit(buf, fmt_dec, READ_ONCE(dev->name_assign_type));
|
||||
}
|
||||
|
||||
static ssize_t name_assign_type_show(struct device *dev,
|
||||
@ -135,24 +135,28 @@ static ssize_t name_assign_type_show(struct device *dev,
|
||||
struct net_device *ndev = to_net_dev(dev);
|
||||
ssize_t ret = -EINVAL;
|
||||
|
||||
if (ndev->name_assign_type != NET_NAME_UNKNOWN)
|
||||
if (READ_ONCE(ndev->name_assign_type) != NET_NAME_UNKNOWN)
|
||||
ret = netdev_show(dev, attr, buf, format_name_assign_type);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR_RO(name_assign_type);
|
||||
|
||||
/* use same locking rules as GIFHWADDR ioctl's */
|
||||
/* use same locking rules as GIFHWADDR ioctl's (dev_get_mac_address()) */
|
||||
static ssize_t address_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct net_device *ndev = to_net_dev(dev);
|
||||
ssize_t ret = -EINVAL;
|
||||
|
||||
read_lock(&dev_base_lock);
|
||||
down_read(&dev_addr_sem);
|
||||
|
||||
rcu_read_lock();
|
||||
if (dev_isalive(ndev))
|
||||
ret = sysfs_format_mac(buf, ndev->dev_addr, ndev->addr_len);
|
||||
read_unlock(&dev_base_lock);
|
||||
rcu_read_unlock();
|
||||
|
||||
up_read(&dev_addr_sem);
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR_RO(address);
|
||||
@ -161,10 +165,13 @@ static ssize_t broadcast_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct net_device *ndev = to_net_dev(dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
rcu_read_lock();
|
||||
if (dev_isalive(ndev))
|
||||
return sysfs_format_mac(buf, ndev->broadcast, ndev->addr_len);
|
||||
return -EINVAL;
|
||||
ret = sysfs_format_mac(buf, ndev->broadcast, ndev->addr_len);
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR_RO(broadcast);
|
||||
|
||||
@ -318,11 +325,9 @@ static ssize_t operstate_show(struct device *dev,
|
||||
const struct net_device *netdev = to_net_dev(dev);
|
||||
unsigned char operstate;
|
||||
|
||||
read_lock(&dev_base_lock);
|
||||
operstate = netdev->operstate;
|
||||
operstate = READ_ONCE(netdev->operstate);
|
||||
if (!netif_running(netdev))
|
||||
operstate = IF_OPER_DOWN;
|
||||
read_unlock(&dev_base_lock);
|
||||
|
||||
if (operstate >= ARRAY_SIZE(operstates))
|
||||
return -EINVAL; /* should not happen */
|
||||
@ -680,14 +685,14 @@ static ssize_t netstat_show(const struct device *d,
|
||||
WARN_ON(offset > sizeof(struct rtnl_link_stats64) ||
|
||||
offset % sizeof(u64) != 0);
|
||||
|
||||
read_lock(&dev_base_lock);
|
||||
rcu_read_lock();
|
||||
if (dev_isalive(dev)) {
|
||||
struct rtnl_link_stats64 temp;
|
||||
const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp);
|
||||
|
||||
ret = sysfs_emit(buf, fmt_u64, *(u64 *)(((u8 *)stats) + offset));
|
||||
}
|
||||
read_unlock(&dev_base_lock);
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -842,9 +842,22 @@ int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rtnl_put_cacheinfo);
|
||||
|
||||
void netdev_set_operstate(struct net_device *dev, int newstate)
|
||||
{
|
||||
unsigned int old = READ_ONCE(dev->operstate);
|
||||
|
||||
do {
|
||||
if (old == newstate)
|
||||
return;
|
||||
} while (!try_cmpxchg(&dev->operstate, &old, newstate));
|
||||
|
||||
netdev_state_change(dev);
|
||||
}
|
||||
EXPORT_SYMBOL(netdev_set_operstate);
|
||||
|
||||
static void set_operstate(struct net_device *dev, unsigned char transition)
|
||||
{
|
||||
unsigned char operstate = dev->operstate;
|
||||
unsigned char operstate = READ_ONCE(dev->operstate);
|
||||
|
||||
switch (transition) {
|
||||
case IF_OPER_UP:
|
||||
@ -866,12 +879,7 @@ static void set_operstate(struct net_device *dev, unsigned char transition)
|
||||
break;
|
||||
}
|
||||
|
||||
if (dev->operstate != operstate) {
|
||||
write_lock(&dev_base_lock);
|
||||
dev->operstate = operstate;
|
||||
write_unlock(&dev_base_lock);
|
||||
netdev_state_change(dev);
|
||||
}
|
||||
netdev_set_operstate(dev, operstate);
|
||||
}
|
||||
|
||||
static unsigned int rtnl_dev_get_flags(const struct net_device *dev)
|
||||
@ -2953,11 +2961,9 @@ static int do_setlink(const struct sk_buff *skb,
|
||||
if (tb[IFLA_LINKMODE]) {
|
||||
unsigned char value = nla_get_u8(tb[IFLA_LINKMODE]);
|
||||
|
||||
write_lock(&dev_base_lock);
|
||||
if (dev->link_mode ^ value)
|
||||
status |= DO_SETLINK_NOTIFY;
|
||||
dev->link_mode = value;
|
||||
write_unlock(&dev_base_lock);
|
||||
WRITE_ONCE(dev->link_mode, value);
|
||||
}
|
||||
|
||||
if (tb[IFLA_VFINFO_LIST]) {
|
||||
|
@ -28,29 +28,19 @@ static bool is_slave_up(struct net_device *dev)
|
||||
return dev && is_admin_up(dev) && netif_oper_up(dev);
|
||||
}
|
||||
|
||||
static void __hsr_set_operstate(struct net_device *dev, int transition)
|
||||
{
|
||||
write_lock(&dev_base_lock);
|
||||
if (dev->operstate != transition) {
|
||||
dev->operstate = transition;
|
||||
write_unlock(&dev_base_lock);
|
||||
netdev_state_change(dev);
|
||||
} else {
|
||||
write_unlock(&dev_base_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void hsr_set_operstate(struct hsr_port *master, bool has_carrier)
|
||||
{
|
||||
if (!is_admin_up(master->dev)) {
|
||||
__hsr_set_operstate(master->dev, IF_OPER_DOWN);
|
||||
struct net_device *dev = master->dev;
|
||||
|
||||
if (!is_admin_up(dev)) {
|
||||
netdev_set_operstate(dev, IF_OPER_DOWN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (has_carrier)
|
||||
__hsr_set_operstate(master->dev, IF_OPER_UP);
|
||||
netdev_set_operstate(dev, IF_OPER_UP);
|
||||
else
|
||||
__hsr_set_operstate(master->dev, IF_OPER_LOWERLAYERDOWN);
|
||||
netdev_set_operstate(dev, IF_OPER_LOWERLAYERDOWN);
|
||||
}
|
||||
|
||||
static bool hsr_check_carrier(struct hsr_port *master)
|
||||
@ -78,14 +68,14 @@ static void hsr_check_announce(struct net_device *hsr_dev,
|
||||
|
||||
hsr = netdev_priv(hsr_dev);
|
||||
|
||||
if (hsr_dev->operstate == IF_OPER_UP && old_operstate != IF_OPER_UP) {
|
||||
if (READ_ONCE(hsr_dev->operstate) == IF_OPER_UP && old_operstate != IF_OPER_UP) {
|
||||
/* Went up */
|
||||
hsr->announce_count = 0;
|
||||
mod_timer(&hsr->announce_timer,
|
||||
jiffies + msecs_to_jiffies(HSR_ANNOUNCE_INTERVAL));
|
||||
}
|
||||
|
||||
if (hsr_dev->operstate != IF_OPER_UP && old_operstate == IF_OPER_UP)
|
||||
if (READ_ONCE(hsr_dev->operstate) != IF_OPER_UP && old_operstate == IF_OPER_UP)
|
||||
/* Went down */
|
||||
del_timer(&hsr->announce_timer);
|
||||
}
|
||||
@ -100,7 +90,7 @@ void hsr_check_carrier_and_operstate(struct hsr_priv *hsr)
|
||||
/* netif_stacked_transfer_operstate() cannot be used here since
|
||||
* it doesn't set IF_OPER_LOWERLAYERDOWN (?)
|
||||
*/
|
||||
old_operstate = master->dev->operstate;
|
||||
old_operstate = READ_ONCE(master->dev->operstate);
|
||||
has_carrier = hsr_check_carrier(master);
|
||||
hsr_set_operstate(master, has_carrier);
|
||||
hsr_check_announce(master->dev, old_operstate);
|
||||
|
@ -102,10 +102,9 @@ struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn,
|
||||
if (!ip_tunnel_key_match(&t->parms, flags, key))
|
||||
continue;
|
||||
|
||||
if (t->parms.link == link)
|
||||
if (READ_ONCE(t->parms.link) == link)
|
||||
return t;
|
||||
else
|
||||
cand = t;
|
||||
cand = t;
|
||||
}
|
||||
|
||||
hlist_for_each_entry_rcu(t, head, hash_node) {
|
||||
@ -117,9 +116,9 @@ struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn,
|
||||
if (!ip_tunnel_key_match(&t->parms, flags, key))
|
||||
continue;
|
||||
|
||||
if (t->parms.link == link)
|
||||
if (READ_ONCE(t->parms.link) == link)
|
||||
return t;
|
||||
else if (!cand)
|
||||
if (!cand)
|
||||
cand = t;
|
||||
}
|
||||
|
||||
@ -137,9 +136,9 @@ struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn,
|
||||
if (!ip_tunnel_key_match(&t->parms, flags, key))
|
||||
continue;
|
||||
|
||||
if (t->parms.link == link)
|
||||
if (READ_ONCE(t->parms.link) == link)
|
||||
return t;
|
||||
else if (!cand)
|
||||
if (!cand)
|
||||
cand = t;
|
||||
}
|
||||
|
||||
@ -150,9 +149,9 @@ struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn,
|
||||
!(t->dev->flags & IFF_UP))
|
||||
continue;
|
||||
|
||||
if (t->parms.link == link)
|
||||
if (READ_ONCE(t->parms.link) == link)
|
||||
return t;
|
||||
else if (!cand)
|
||||
if (!cand)
|
||||
cand = t;
|
||||
}
|
||||
|
||||
@ -221,7 +220,7 @@ static struct ip_tunnel *ip_tunnel_find(struct ip_tunnel_net *itn,
|
||||
hlist_for_each_entry_rcu(t, head, hash_node) {
|
||||
if (local == t->parms.iph.saddr &&
|
||||
remote == t->parms.iph.daddr &&
|
||||
link == t->parms.link &&
|
||||
link == READ_ONCE(t->parms.link) &&
|
||||
type == t->dev->type &&
|
||||
ip_tunnel_key_match(&t->parms, flags, key))
|
||||
break;
|
||||
@ -747,7 +746,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
|
||||
|
||||
ip_tunnel_init_flow(&fl4, protocol, dst, tnl_params->saddr,
|
||||
tunnel->parms.o_key, RT_TOS(tos),
|
||||
dev_net(dev), tunnel->parms.link,
|
||||
dev_net(dev), READ_ONCE(tunnel->parms.link),
|
||||
tunnel->fwmark, skb_get_hash(skb), 0);
|
||||
|
||||
if (ip_tunnel_encap(skb, &tunnel->encap, &protocol, &fl4) < 0)
|
||||
@ -867,7 +866,7 @@ static void ip_tunnel_update(struct ip_tunnel_net *itn,
|
||||
if (t->parms.link != p->link || t->fwmark != fwmark) {
|
||||
int mtu;
|
||||
|
||||
t->parms.link = p->link;
|
||||
WRITE_ONCE(t->parms.link, p->link);
|
||||
t->fwmark = fwmark;
|
||||
mtu = ip_tunnel_bind_dev(dev);
|
||||
if (set_mtu)
|
||||
@ -1057,9 +1056,9 @@ EXPORT_SYMBOL(ip_tunnel_get_link_net);
|
||||
|
||||
int ip_tunnel_get_iflink(const struct net_device *dev)
|
||||
{
|
||||
struct ip_tunnel *tunnel = netdev_priv(dev);
|
||||
const struct ip_tunnel *tunnel = netdev_priv(dev);
|
||||
|
||||
return tunnel->parms.link;
|
||||
return READ_ONCE(tunnel->parms.link);
|
||||
}
|
||||
EXPORT_SYMBOL(ip_tunnel_get_iflink);
|
||||
|
||||
|
@ -6025,7 +6025,7 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
|
||||
(dev->ifindex != dev_get_iflink(dev) &&
|
||||
nla_put_u32(skb, IFLA_LINK, dev_get_iflink(dev))) ||
|
||||
nla_put_u8(skb, IFLA_OPERSTATE,
|
||||
netif_running(dev) ? dev->operstate : IF_OPER_DOWN))
|
||||
netif_running(dev) ? READ_ONCE(dev->operstate) : IF_OPER_DOWN))
|
||||
goto nla_put_failure;
|
||||
protoinfo = nla_nest_start_noflag(skb, IFLA_PROTINFO);
|
||||
if (!protoinfo)
|
||||
|
Loading…
x
Reference in New Issue
Block a user