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:
David S. Miller
2024-02-14 11:20:14 +00:00
17 changed files with 112 additions and 145 deletions

View File

@ -252,8 +252,8 @@ ndo_eth_ioctl:
Context: process Context: process
ndo_get_stats: ndo_get_stats:
Synchronization: rtnl_lock() semaphore, dev_base_lock rwlock, or RCU. Synchronization: rtnl_lock() semaphore, or RCU.
Context: atomic (can't sleep under rwlock or RCU) Context: atomic (can't sleep under RCU)
ndo_start_xmit: ndo_start_xmit:
Synchronization: __netif_tx_lock spinlock. Synchronization: __netif_tx_lock spinlock.

View File

@ -872,7 +872,7 @@ error:
return NETDEV_TX_OK; 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, static void enic_get_stats(struct net_device *netdev,
struct rtnl_link_stats64 *net_stats) struct rtnl_link_stats64 *net_stats)
{ {

View File

@ -1761,7 +1761,7 @@ static void nv_get_stats(int cpu, struct fe_priv *np,
/* /*
* nv_get_stats64: dev->ndo_get_stats64 function * nv_get_stats64: dev->ndo_get_stats64 function
* Get latest stats value from the nic. * 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. * only synchronized against unregister_netdevice.
*/ */
static void static void
@ -3090,7 +3090,7 @@ static void set_bufsize(struct net_device *dev)
/* /*
* nv_change_mtu: dev->change_mtu function * 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) static int nv_change_mtu(struct net_device *dev, int new_mtu)
{ {

View File

@ -595,7 +595,7 @@ void efx_stop_all(struct efx_nic *efx)
efx_stop_datapath(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) void efx_net_stats(struct net_device *net_dev, struct rtnl_link_stats64 *stats)
{ {
struct efx_nic *efx = efx_netdev_priv(net_dev); struct efx_nic *efx = efx_netdev_priv(net_dev);

View File

@ -2085,7 +2085,7 @@ int ef4_net_stop(struct net_device *net_dev)
return 0; 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, static void ef4_net_stats(struct net_device *net_dev,
struct rtnl_link_stats64 *stats) struct rtnl_link_stats64 *stats)
{ {

View File

@ -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); 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, void efx_siena_net_stats(struct net_device *net_dev,
struct rtnl_link_stats64 *stats) struct rtnl_link_stats64 *stats)
{ {

View File

@ -1815,6 +1815,15 @@ enum netdev_stat_type {
NETDEV_PCPU_STAT_DSTATS, /* struct pcpu_dstats */ 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. * struct net_device - The DEVICE structure.
* *
@ -2249,7 +2258,7 @@ struct net_device {
const struct tlsdev_ops *tlsdev_ops; const struct tlsdev_ops *tlsdev_ops;
#endif #endif
unsigned char operstate; unsigned int operstate;
unsigned char link_mode; unsigned char link_mode;
unsigned char if_port; unsigned char if_port;
@ -2372,13 +2381,7 @@ struct net_device {
struct list_head link_watch_list; struct list_head link_watch_list;
enum { NETREG_UNINITIALIZED=0, u8 reg_state;
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;
bool dismantle; 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, int call_netdevice_notifiers_info(unsigned long val,
struct netdev_notifier_info *info); struct netdev_notifier_info *info);
extern rwlock_t dev_base_lock; /* Device list lock */
#define for_each_netdev(net, d) \ #define for_each_netdev(net, d) \
list_for_each_entry(d, &(net)->dev_base_head, dev_list) list_for_each_entry(d, &(net)->dev_base_head, dev_list)
#define for_each_netdev_reverse(net, d) \ #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) 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_UNINITIALIZED: return " (uninitialized)";
case NETREG_REGISTERED: return ""; case NETREG_REGISTERED: return "";
case NETREG_UNREGISTERING: return " (unregistering)"; 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)"; 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)"; return " (unknown)";
} }

View File

@ -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); return (nlflags & NLM_F_ECHO) || rtnl_has_listeners(net, group);
} }
void netdev_set_operstate(struct net_device *dev, int newstate);
#endif /* __LINUX_RTNETLINK_H */ #endif /* __LINUX_RTNETLINK_H */

View File

@ -455,7 +455,8 @@ static int br_fill_ifinfo(struct sk_buff *skb,
u32 filter_mask, const struct net_device *dev, u32 filter_mask, const struct net_device *dev,
bool getlink) 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 nlattr *af = NULL;
struct net_bridge *br; struct net_bridge *br;
struct ifinfomsg *hdr; struct ifinfomsg *hdr;

View File

@ -168,28 +168,6 @@ static int call_netdevice_notifiers_extack(unsigned long val,
struct net_device *dev, struct net_device *dev,
struct netlink_ext_ack *extack); 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); static DEFINE_MUTEX(ifalias_mutex);
/* protects napi_hash addition/deletion and napi_gen_id */ /* protects napi_hash addition/deletion and napi_gen_id */
@ -395,12 +373,10 @@ static void list_netdevice(struct net_device *dev)
ASSERT_RTNL(); ASSERT_RTNL();
write_lock(&dev_base_lock);
list_add_tail_rcu(&dev->dev_list, &net->dev_base_head); list_add_tail_rcu(&dev->dev_list, &net->dev_base_head);
netdev_name_node_add(net, dev->name_node); netdev_name_node_add(net, dev->name_node);
hlist_add_head_rcu(&dev->index_hlist, hlist_add_head_rcu(&dev->index_hlist,
dev_index_hash(net, dev->ifindex)); dev_index_hash(net, dev->ifindex));
write_unlock(&dev_base_lock);
netdev_for_each_altname(dev, name_node) netdev_for_each_altname(dev, name_node)
netdev_name_node_add(net, name_node); netdev_name_node_add(net, name_node);
@ -414,7 +390,7 @@ static void list_netdevice(struct net_device *dev)
/* Device list removal /* Device list removal
* caller must respect a RCU grace period before freeing/reusing dev * 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 netdev_name_node *name_node;
struct net *net = dev_net(dev); 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); netdev_name_node_del(name_node);
/* Unlink dev from the device chain */ /* Unlink dev from the device chain */
if (lock)
write_lock(&dev_base_lock);
list_del_rcu(&dev->dev_list); list_del_rcu(&dev->dev_list);
netdev_name_node_del(dev->name_node); netdev_name_node_del(dev->name_node);
hlist_del_rcu(&dev->index_hlist); hlist_del_rcu(&dev->index_hlist);
if (lock)
write_unlock(&dev_base_lock);
dev_base_seq_inc(dev_net(dev)); dev_base_seq_inc(dev_net(dev));
} }
@ -754,9 +726,9 @@ EXPORT_SYMBOL_GPL(dev_fill_forward_path);
* @net: the applicable net namespace * @net: the applicable net namespace
* @name: name to find * @name: name to find
* *
* Find an interface by name. Must be called under RTNL semaphore * 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 * If the name is found a pointer to the device is returned.
* is returned. If the name is not found then %NULL is returned. The * If the name is not found then %NULL is returned. The
* reference counters are not incremented so the caller must be * reference counters are not incremented so the caller must be
* careful with locks. * careful with locks.
*/ */
@ -837,8 +809,7 @@ EXPORT_SYMBOL(netdev_get_by_name);
* Search for an interface by index. Returns %NULL if the device * Search for an interface by index. Returns %NULL if the device
* is not found or a pointer to the device. The device has not * is not found or a pointer to the device. The device has not
* had its reference counter increased so the caller must be careful * had its reference counter increased so the caller must be careful
* about locking. The caller must hold either the RTNL semaphore * about locking. The caller must hold the RTNL semaphore.
* or @dev_base_lock.
*/ */
struct net_device *__dev_get_by_index(struct net *net, int ifindex) 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)" : ""); dev->flags & IFF_UP ? " (while UP)" : "");
old_assign_type = dev->name_assign_type; old_assign_type = dev->name_assign_type;
dev->name_assign_type = NET_NAME_RENAMED; WRITE_ONCE(dev->name_assign_type, NET_NAME_RENAMED);
rollback: rollback:
ret = device_rename(&dev->dev, dev->name); ret = device_rename(&dev->dev, dev->name);
if (ret) { if (ret) {
memcpy(dev->name, oldname, IFNAMSIZ); 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); up_write(&devnet_rename_sem);
return ret; return ret;
} }
@ -1243,15 +1214,11 @@ rollback:
netdev_adjacent_rename_links(dev, oldname); netdev_adjacent_rename_links(dev, oldname);
write_lock(&dev_base_lock);
netdev_name_node_del(dev->name_node); netdev_name_node_del(dev->name_node);
write_unlock(&dev_base_lock);
synchronize_net(); synchronize_net();
write_lock(&dev_base_lock);
netdev_name_node_add(net, dev->name_node); netdev_name_node_add(net, dev->name_node);
write_unlock(&dev_base_lock);
ret = call_netdevice_notifiers(NETDEV_CHANGENAME, dev); ret = call_netdevice_notifiers(NETDEV_CHANGENAME, dev);
ret = notifier_to_errno(ret); ret = notifier_to_errno(ret);
@ -1263,7 +1230,7 @@ rollback:
down_write(&devnet_rename_sem); down_write(&devnet_rename_sem);
memcpy(dev->name, oldname, IFNAMSIZ); memcpy(dev->name, oldname, IFNAMSIZ);
memcpy(oldname, newname, 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; old_assign_type = NET_NAME_RENAMED;
goto rollback; goto rollback;
} else { } else {
@ -8993,7 +8960,7 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa,
} }
EXPORT_SYMBOL(dev_set_mac_address); 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, int dev_set_mac_address_user(struct net_device *dev, struct sockaddr *sa,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
@ -10338,9 +10305,9 @@ int register_netdevice(struct net_device *dev)
goto err_ifindex_release; goto err_ifindex_release;
ret = netdev_register_kobject(dev); ret = netdev_register_kobject(dev);
write_lock(&dev_base_lock);
dev->reg_state = ret ? NETREG_UNREGISTERED : NETREG_REGISTERED; WRITE_ONCE(dev->reg_state, ret ? NETREG_UNREGISTERED : NETREG_REGISTERED);
write_unlock(&dev_base_lock);
if (ret) if (ret)
goto err_uninit_notify; goto err_uninit_notify;
@ -10629,9 +10596,7 @@ void netdev_run_todo(void)
continue; continue;
} }
write_lock(&dev_base_lock); WRITE_ONCE(dev->reg_state, NETREG_UNREGISTERED);
dev->reg_state = NETREG_UNREGISTERED;
write_unlock(&dev_base_lock);
linkwatch_sync_dev(dev); linkwatch_sync_dev(dev);
} }
@ -11050,7 +11015,7 @@ void free_netdev(struct net_device *dev)
} }
BUG_ON(dev->reg_state != NETREG_UNREGISTERED); BUG_ON(dev->reg_state != NETREG_UNREGISTERED);
dev->reg_state = NETREG_RELEASED; WRITE_ONCE(dev->reg_state, NETREG_RELEASED);
/* will free via device release */ /* will free via device release */
put_device(&dev->dev); 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) { list_for_each_entry(dev, head, unreg_list) {
/* And unlink it from device chain. */ /* And unlink it from device chain. */
write_lock(&dev_base_lock); unlist_netdevice(dev);
unlist_netdevice(dev, false); WRITE_ONCE(dev->reg_state, NETREG_UNREGISTERING);
dev->reg_state = NETREG_UNREGISTERING;
write_unlock(&dev_base_lock);
} }
flush_all_backlogs(); flush_all_backlogs();
@ -11323,7 +11286,7 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,
dev_close(dev); dev_close(dev);
/* And unlink it from device chain */ /* And unlink it from device chain */
unlist_netdevice(dev, true); unlist_netdevice(dev);
synchronize_net(); synchronize_net();

View File

@ -3,6 +3,7 @@
#define _NET_CORE_DEV_H #define _NET_CORE_DEV_H
#include <linux/types.h> #include <linux/types.h>
#include <linux/rwsem.h>
struct net; struct net;
struct net_device; struct net_device;
@ -46,6 +47,8 @@ extern int weight_p;
extern int dev_weight_rx_bias; extern int dev_weight_rx_bias;
extern int dev_weight_tx_bias; extern int dev_weight_tx_bias;
extern struct rw_semaphore dev_addr_sem;
/* rtnl helpers */ /* rtnl helpers */
extern struct list_head net_todo_list; extern struct list_head net_todo_list;
void netdev_run_todo(void); void netdev_run_todo(void);

View File

@ -33,7 +33,7 @@ static DECLARE_DELAYED_WORK(linkwatch_work, linkwatch_event);
static LIST_HEAD(lweventlist); static LIST_HEAD(lweventlist);
static DEFINE_SPINLOCK(lweventlist_lock); 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)) if (netif_testing(dev))
return IF_OPER_TESTING; return IF_OPER_TESTING;
@ -62,16 +62,13 @@ static unsigned char default_operstate(const struct net_device *dev)
return IF_OPER_UP; return IF_OPER_UP;
} }
static void rfc2863_policy(struct net_device *dev) 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; return;
write_lock(&dev_base_lock);
switch(dev->link_mode) { switch(dev->link_mode) {
case IF_LINK_MODE_TESTING: case IF_LINK_MODE_TESTING:
if (operstate == IF_OPER_UP) if (operstate == IF_OPER_UP)
@ -87,9 +84,7 @@ static void rfc2863_policy(struct net_device *dev)
break; break;
} }
dev->operstate = operstate; WRITE_ONCE(dev->operstate, operstate);
write_unlock(&dev_base_lock);
} }

View File

@ -34,10 +34,10 @@ static const char fmt_dec[] = "%d\n";
static const char fmt_ulong[] = "%lu\n"; static const char fmt_ulong[] = "%lu\n";
static const char fmt_u64[] = "%llu\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) 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 */ /* 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); struct net_device *ndev = to_net_dev(dev);
ssize_t ret = -EINVAL; ssize_t ret = -EINVAL;
read_lock(&dev_base_lock); rcu_read_lock();
if (dev_isalive(ndev)) if (dev_isalive(ndev))
ret = (*format)(ndev, buf); ret = (*format)(ndev, buf);
read_unlock(&dev_base_lock); rcu_read_unlock();
return ret; return ret;
} }
@ -60,7 +60,7 @@ static ssize_t netdev_show(const struct device *dev,
#define NETDEVICE_SHOW(field, format_string) \ #define NETDEVICE_SHOW(field, format_string) \
static ssize_t format_##field(const struct net_device *dev, char *buf) \ 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, \ static ssize_t field##_show(struct device *dev, \
struct device_attribute *attr, char *buf) \ 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) 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, 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); struct net_device *ndev = to_net_dev(dev);
ssize_t ret = -EINVAL; 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); ret = netdev_show(dev, attr, buf, format_name_assign_type);
return ret; return ret;
} }
static DEVICE_ATTR_RO(name_assign_type); 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, static ssize_t address_show(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
{ {
struct net_device *ndev = to_net_dev(dev); struct net_device *ndev = to_net_dev(dev);
ssize_t ret = -EINVAL; ssize_t ret = -EINVAL;
read_lock(&dev_base_lock); down_read(&dev_addr_sem);
rcu_read_lock();
if (dev_isalive(ndev)) if (dev_isalive(ndev))
ret = sysfs_format_mac(buf, ndev->dev_addr, ndev->addr_len); 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; return ret;
} }
static DEVICE_ATTR_RO(address); static DEVICE_ATTR_RO(address);
@ -161,10 +165,13 @@ static ssize_t broadcast_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
struct net_device *ndev = to_net_dev(dev); struct net_device *ndev = to_net_dev(dev);
int ret = -EINVAL;
rcu_read_lock();
if (dev_isalive(ndev)) if (dev_isalive(ndev))
return sysfs_format_mac(buf, ndev->broadcast, ndev->addr_len); ret = sysfs_format_mac(buf, ndev->broadcast, ndev->addr_len);
return -EINVAL; rcu_read_unlock();
return ret;
} }
static DEVICE_ATTR_RO(broadcast); 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); const struct net_device *netdev = to_net_dev(dev);
unsigned char operstate; unsigned char operstate;
read_lock(&dev_base_lock); operstate = READ_ONCE(netdev->operstate);
operstate = netdev->operstate;
if (!netif_running(netdev)) if (!netif_running(netdev))
operstate = IF_OPER_DOWN; operstate = IF_OPER_DOWN;
read_unlock(&dev_base_lock);
if (operstate >= ARRAY_SIZE(operstates)) if (operstate >= ARRAY_SIZE(operstates))
return -EINVAL; /* should not happen */ 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) || WARN_ON(offset > sizeof(struct rtnl_link_stats64) ||
offset % sizeof(u64) != 0); offset % sizeof(u64) != 0);
read_lock(&dev_base_lock); rcu_read_lock();
if (dev_isalive(dev)) { if (dev_isalive(dev)) {
struct rtnl_link_stats64 temp; struct rtnl_link_stats64 temp;
const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp); const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp);
ret = sysfs_emit(buf, fmt_u64, *(u64 *)(((u8 *)stats) + offset)); ret = sysfs_emit(buf, fmt_u64, *(u64 *)(((u8 *)stats) + offset));
} }
read_unlock(&dev_base_lock); rcu_read_unlock();
return ret; return ret;
} }

View File

@ -842,9 +842,22 @@ int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id,
} }
EXPORT_SYMBOL_GPL(rtnl_put_cacheinfo); 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) 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) { switch (transition) {
case IF_OPER_UP: case IF_OPER_UP:
@ -866,12 +879,7 @@ static void set_operstate(struct net_device *dev, unsigned char transition)
break; break;
} }
if (dev->operstate != operstate) { netdev_set_operstate(dev, operstate);
write_lock(&dev_base_lock);
dev->operstate = operstate;
write_unlock(&dev_base_lock);
netdev_state_change(dev);
}
} }
static unsigned int rtnl_dev_get_flags(const struct net_device *dev) 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]) { if (tb[IFLA_LINKMODE]) {
unsigned char value = nla_get_u8(tb[IFLA_LINKMODE]); unsigned char value = nla_get_u8(tb[IFLA_LINKMODE]);
write_lock(&dev_base_lock);
if (dev->link_mode ^ value) if (dev->link_mode ^ value)
status |= DO_SETLINK_NOTIFY; status |= DO_SETLINK_NOTIFY;
dev->link_mode = value; WRITE_ONCE(dev->link_mode, value);
write_unlock(&dev_base_lock);
} }
if (tb[IFLA_VFINFO_LIST]) { if (tb[IFLA_VFINFO_LIST]) {

View File

@ -28,29 +28,19 @@ static bool is_slave_up(struct net_device *dev)
return dev && is_admin_up(dev) && netif_oper_up(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) static void hsr_set_operstate(struct hsr_port *master, bool has_carrier)
{ {
if (!is_admin_up(master->dev)) { struct net_device *dev = master->dev;
__hsr_set_operstate(master->dev, IF_OPER_DOWN);
if (!is_admin_up(dev)) {
netdev_set_operstate(dev, IF_OPER_DOWN);
return; return;
} }
if (has_carrier) if (has_carrier)
__hsr_set_operstate(master->dev, IF_OPER_UP); netdev_set_operstate(dev, IF_OPER_UP);
else 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) 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); 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 */ /* Went up */
hsr->announce_count = 0; hsr->announce_count = 0;
mod_timer(&hsr->announce_timer, mod_timer(&hsr->announce_timer,
jiffies + msecs_to_jiffies(HSR_ANNOUNCE_INTERVAL)); 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 */ /* Went down */
del_timer(&hsr->announce_timer); 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 /* netif_stacked_transfer_operstate() cannot be used here since
* it doesn't set IF_OPER_LOWERLAYERDOWN (?) * 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); has_carrier = hsr_check_carrier(master);
hsr_set_operstate(master, has_carrier); hsr_set_operstate(master, has_carrier);
hsr_check_announce(master->dev, old_operstate); hsr_check_announce(master->dev, old_operstate);

View File

@ -102,10 +102,9 @@ struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn,
if (!ip_tunnel_key_match(&t->parms, flags, key)) if (!ip_tunnel_key_match(&t->parms, flags, key))
continue; continue;
if (t->parms.link == link) if (READ_ONCE(t->parms.link) == link)
return t; return t;
else cand = t;
cand = t;
} }
hlist_for_each_entry_rcu(t, head, hash_node) { 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)) if (!ip_tunnel_key_match(&t->parms, flags, key))
continue; continue;
if (t->parms.link == link) if (READ_ONCE(t->parms.link) == link)
return t; return t;
else if (!cand) if (!cand)
cand = t; 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)) if (!ip_tunnel_key_match(&t->parms, flags, key))
continue; continue;
if (t->parms.link == link) if (READ_ONCE(t->parms.link) == link)
return t; return t;
else if (!cand) if (!cand)
cand = t; cand = t;
} }
@ -150,9 +149,9 @@ struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn,
!(t->dev->flags & IFF_UP)) !(t->dev->flags & IFF_UP))
continue; continue;
if (t->parms.link == link) if (READ_ONCE(t->parms.link) == link)
return t; return t;
else if (!cand) if (!cand)
cand = t; 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) { hlist_for_each_entry_rcu(t, head, hash_node) {
if (local == t->parms.iph.saddr && if (local == t->parms.iph.saddr &&
remote == t->parms.iph.daddr && remote == t->parms.iph.daddr &&
link == t->parms.link && link == READ_ONCE(t->parms.link) &&
type == t->dev->type && type == t->dev->type &&
ip_tunnel_key_match(&t->parms, flags, key)) ip_tunnel_key_match(&t->parms, flags, key))
break; 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, ip_tunnel_init_flow(&fl4, protocol, dst, tnl_params->saddr,
tunnel->parms.o_key, RT_TOS(tos), 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); tunnel->fwmark, skb_get_hash(skb), 0);
if (ip_tunnel_encap(skb, &tunnel->encap, &protocol, &fl4) < 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) { if (t->parms.link != p->link || t->fwmark != fwmark) {
int mtu; int mtu;
t->parms.link = p->link; WRITE_ONCE(t->parms.link, p->link);
t->fwmark = fwmark; t->fwmark = fwmark;
mtu = ip_tunnel_bind_dev(dev); mtu = ip_tunnel_bind_dev(dev);
if (set_mtu) if (set_mtu)
@ -1057,9 +1056,9 @@ EXPORT_SYMBOL(ip_tunnel_get_link_net);
int ip_tunnel_get_iflink(const struct net_device *dev) 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); EXPORT_SYMBOL(ip_tunnel_get_iflink);

View File

@ -6025,7 +6025,7 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
(dev->ifindex != dev_get_iflink(dev) && (dev->ifindex != dev_get_iflink(dev) &&
nla_put_u32(skb, IFLA_LINK, dev_get_iflink(dev))) || nla_put_u32(skb, IFLA_LINK, dev_get_iflink(dev))) ||
nla_put_u8(skb, IFLA_OPERSTATE, 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; goto nla_put_failure;
protoinfo = nla_nest_start_noflag(skb, IFLA_PROTINFO); protoinfo = nla_nest_start_noflag(skb, IFLA_PROTINFO);
if (!protoinfo) if (!protoinfo)