mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 04:34:03 +02:00
Merge branch 'net-support-per-netns-device-unregistration'
Kuniyuki Iwashima says:
====================
net: Support per-netns device unregistration
The biggest blocker to per-netns RTNL is netdev unregistration.
It starts within a single netns, but it can eventually involve
multiple namespaces.
There are three types of such cross-netns devices:
1. Paired devices (e.g., netkit, veth, vxcan)
-> Unregistering one device also deletes its peer, which
may reside in another netns.
2. Tunnel devices (e.g., bareudp, geneve, etc)
-> Destroying a netns removes devices in another netns if
their backend sockets reside in the dying netns
3. Stacked devices (e.g., ipvlan, macvlan, etc)
-> Removing the lower device also removes multiple upper
devices, each of which may reside in different namespaces.
While the first two device types require at most two rtnl_net_lock()s,
the stacked type has no upper limit. This makes it impossible to
freeze all necessary namespaces in advance.
This series introduces per-netns work, initially suggested at
NetConf 2024, to delegate the unregistration of such cross-netns
devices.
https://netdev.bots.linux.dev/netconf/2024/kuniyu.pdf#page=62
The first half of the series wraps NETDEV_UNREGISTER (in core) with
per-netns RTNL, adds a helper for per-netns device unregistration,
and forces per-netns device unregistration in the core code when
CONFIG_DEBUG_NET_SMALL_RTNL=y.
The latter half picks out one from each type (veth, bareudp, ipvlan)
and converts them to support per-netns device unregistration,
although the operations are **still serialised under RTNL** for now.
Please note that this series focuses only on the device unregistration
paths. For example, there are ASSERT_RTNL() left in other paths, and
Sashiko may point it out, but they are out of scope.
This is just the first step, and we need more incremental changes to
completely remove RTNL anyway.
Now, we can see that unregistering a lower device (veth0 below)
removes upper devices (ipvl2, ipvl3) in different namespaces using
per-netns work with a different PID. The lower device (veth0) is
freed only after all upper ipvlan devices have called netdev_put()
in ipvlan_uninit().
# ip netns add ns1
# ip netns add ns2
# ip netns add ns3
# ip -n ns1 link add veth0 type veth peer veth1
# ip -n ns2 link add ipvl2 link veth0 link-netns ns1 type ipvlan mode l2
# ip -n ns3 link add ipvl3 link veth0 link-netns ns1 type ipvlan mode l2
# ip -n ns1 link del veth0
# bpftrace -e '#include <linux/netdevice.h>
kprobe:ipvlan_uninit,
kprobe:veth_dellink,
kprobe:free_netdev {
$dev = (struct net_device *)arg0;
printf("PID: %d | DEV: %s%s\n", pid, $dev->name, kstack());
}'
PID: 2010 | DEV: veth0
veth_dellink+5
rtnl_dellink+1213
rtnetlink_rcv_msg+1791
...
PID: 440 | DEV: ipvl2
ipvlan_uninit+5
unregister_netdevice_many_notify+7129
unregister_netdevice_many_net+1050
rtnl_net_work_func+136
...
PID: 440 | DEV: ipvl2
free_netdev+5
netdev_run_todo+4798
process_scheduled_works+2538
...
PID: 440 | DEV: ipvl3
ipvlan_uninit+5
unregister_netdevice_many_notify+7129
unregister_netdevice_many_net+1050
rtnl_net_work_func+136
process_scheduled_works+2538
...
PID: 2010 | DEV: veth0
free_netdev+5
netdev_run_todo+4798
rtnl_dellink+1507
rtnetlink_rcv_msg+1791
...
PID: 440 | DEV: ipvl3
free_netdev+5
netdev_run_todo+4798
process_scheduled_works+2538
...
v1: https://lore.kernel.org/netdev/20260701214334.266991-1-kuniyu@google.com/
====================
Link: https://patch.msgid.link/20260703001009.1572444-1-kuniyu@google.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
commit
71ac90cca6
|
|
@ -36,6 +36,7 @@ static unsigned int bareudp_net_id;
|
|||
|
||||
struct bareudp_net {
|
||||
struct list_head bareudp_list;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
struct bareudp_conf {
|
||||
|
|
@ -636,10 +637,15 @@ static struct bareudp_dev *bareudp_find_dev(struct bareudp_net *bn,
|
|||
{
|
||||
struct bareudp_dev *bareudp, *t = NULL;
|
||||
|
||||
mutex_lock(&bn->lock);
|
||||
|
||||
list_for_each_entry(bareudp, &bn->bareudp_list, next) {
|
||||
if (conf->port == bareudp->port)
|
||||
t = bareudp;
|
||||
}
|
||||
|
||||
mutex_unlock(&bn->lock);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
|
|
@ -675,7 +681,10 @@ static int bareudp_configure(struct net *net, struct net_device *dev,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&bn->lock);
|
||||
list_add(&bareudp->next, &bn->bareudp_list);
|
||||
mutex_unlock(&bn->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -692,12 +701,26 @@ static int bareudp_link_config(struct net_device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void bareudp_dellink(struct net_device *dev, struct list_head *head)
|
||||
static void __bareudp_dellink(struct net *net, struct net_device *dev,
|
||||
struct list_head *head)
|
||||
{
|
||||
struct bareudp_dev *bareudp = netdev_priv(dev);
|
||||
|
||||
list_del(&bareudp->next);
|
||||
unregister_netdevice_queue(dev, head);
|
||||
list_del_init(&bareudp->next);
|
||||
unregister_netdevice_queue_net(net, dev, head);
|
||||
}
|
||||
|
||||
static void bareudp_dellink(struct net_device *dev, struct list_head *head)
|
||||
{
|
||||
struct bareudp_dev *bareudp = netdev_priv(dev);
|
||||
struct bareudp_net *bn;
|
||||
|
||||
bn = net_generic(bareudp->net, bareudp_net_id);
|
||||
|
||||
mutex_lock(&bn->lock);
|
||||
if (!list_empty(&bareudp->next))
|
||||
__bareudp_dellink(dev_net(dev), dev, head);
|
||||
mutex_unlock(&bn->lock);
|
||||
}
|
||||
|
||||
static int bareudp_newlink(struct net_device *dev,
|
||||
|
|
@ -776,6 +799,8 @@ static __net_init int bareudp_init_net(struct net *net)
|
|||
struct bareudp_net *bn = net_generic(net, bareudp_net_id);
|
||||
|
||||
INIT_LIST_HEAD(&bn->bareudp_list);
|
||||
mutex_init(&bn->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -785,13 +810,25 @@ static void __net_exit bareudp_exit_rtnl_net(struct net *net,
|
|||
struct bareudp_net *bn = net_generic(net, bareudp_net_id);
|
||||
struct bareudp_dev *bareudp, *next;
|
||||
|
||||
mutex_lock(&bn->lock);
|
||||
|
||||
list_for_each_entry_safe(bareudp, next, &bn->bareudp_list, next)
|
||||
bareudp_dellink(bareudp->dev, dev_kill_list);
|
||||
__bareudp_dellink(net, bareudp->dev, dev_kill_list);
|
||||
|
||||
mutex_unlock(&bn->lock);
|
||||
}
|
||||
|
||||
static void __net_exit bareudp_exit_net(struct net *net)
|
||||
{
|
||||
struct bareudp_net *bn = net_generic(net, bareudp_net_id);
|
||||
|
||||
WARN_ON_ONCE(!list_empty(&bn->bareudp_list));
|
||||
}
|
||||
|
||||
static struct pernet_operations bareudp_net_ops = {
|
||||
.init = bareudp_init_net,
|
||||
.exit_rtnl = bareudp_exit_rtnl_net,
|
||||
.exit = bareudp_exit_net,
|
||||
.id = &bareudp_net_id,
|
||||
.size = sizeof(struct bareudp_net),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ struct ipvl_dev {
|
|||
DECLARE_BITMAP(mac_filters, IPVLAN_MAC_FILTER_SIZE);
|
||||
netdev_features_t sfeatures;
|
||||
u32 msg_enable;
|
||||
bool dying;
|
||||
};
|
||||
|
||||
struct ipvl_addr {
|
||||
|
|
@ -91,12 +92,13 @@ struct ipvl_port {
|
|||
struct hlist_head hlhead[IPVLAN_HASH_SIZE];
|
||||
spinlock_t addrs_lock; /* guards hash-table and addrs */
|
||||
struct list_head ipvlans;
|
||||
struct mutex pnodes_lock;
|
||||
u16 mode;
|
||||
u16 flags;
|
||||
u16 dev_id_start;
|
||||
struct work_struct wq;
|
||||
struct sk_buff_head backlog;
|
||||
int count;
|
||||
refcount_t count;
|
||||
struct ida ida;
|
||||
netdevice_tracker dev_tracker;
|
||||
};
|
||||
|
|
@ -168,7 +170,8 @@ void ipvlan_count_rx(const struct ipvl_dev *ipvlan,
|
|||
unsigned int len, bool success, bool mcast);
|
||||
int ipvlan_link_new(struct net_device *dev, struct rtnl_newlink_params *params,
|
||||
struct netlink_ext_ack *extack);
|
||||
void ipvlan_link_delete(struct net_device *dev, struct list_head *head);
|
||||
void __ipvlan_link_delete(struct net *net, struct net_device *dev,
|
||||
struct list_head *head);
|
||||
void ipvlan_link_setup(struct net_device *dev);
|
||||
int ipvlan_link_register(struct rtnl_link_ops *ops);
|
||||
#ifdef CONFIG_IPVLAN_L3S
|
||||
|
|
@ -207,4 +210,9 @@ static inline bool netif_is_ipvlan_port(const struct net_device *dev)
|
|||
return rcu_access_pointer(dev->rx_handler) == ipvlan_handle_frame;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPVTAP)
|
||||
extern void (*__ipvtap_dellink_ptr)(struct net *net, struct net_device *dev,
|
||||
struct list_head *head);
|
||||
#endif
|
||||
|
||||
#endif /* __IPVLAN_H */
|
||||
|
|
|
|||
|
|
@ -7,6 +7,12 @@
|
|||
|
||||
#include "ipvlan.h"
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPVTAP)
|
||||
void (*__ipvtap_dellink_ptr)(struct net *net, struct net_device *dev,
|
||||
struct list_head *head);
|
||||
EXPORT_SYMBOL(__ipvtap_dellink_ptr);
|
||||
#endif
|
||||
|
||||
static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
|
|
@ -16,6 +22,8 @@ static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval,
|
|||
|
||||
ASSERT_RTNL();
|
||||
if (port->mode != nval) {
|
||||
mutex_lock(&port->pnodes_lock);
|
||||
|
||||
list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
|
||||
flags = ipvlan->dev->flags;
|
||||
if (nval == IPVLAN_MODE_L3 || nval == IPVLAN_MODE_L3S) {
|
||||
|
|
@ -40,6 +48,8 @@ static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval,
|
|||
ipvlan_l3s_unregister(port);
|
||||
}
|
||||
port->mode = nval;
|
||||
|
||||
mutex_unlock(&port->pnodes_lock);
|
||||
}
|
||||
return 0;
|
||||
|
||||
|
|
@ -56,6 +66,8 @@ static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval,
|
|||
NULL);
|
||||
}
|
||||
|
||||
mutex_unlock(&port->pnodes_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -76,6 +88,7 @@ static int ipvlan_port_create(struct net_device *dev)
|
|||
INIT_HLIST_HEAD(&port->hlhead[idx]);
|
||||
|
||||
spin_lock_init(&port->addrs_lock);
|
||||
mutex_init(&port->pnodes_lock);
|
||||
skb_queue_head_init(&port->backlog);
|
||||
INIT_WORK(&port->wq, ipvlan_process_multicast);
|
||||
ida_init(&port->ida);
|
||||
|
|
@ -86,6 +99,7 @@ static int ipvlan_port_create(struct net_device *dev)
|
|||
goto err;
|
||||
|
||||
netdev_hold(dev, &port->dev_tracker, GFP_KERNEL);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
|
|
@ -93,16 +107,18 @@ static int ipvlan_port_create(struct net_device *dev)
|
|||
return err;
|
||||
}
|
||||
|
||||
static void ipvlan_port_destroy(struct net_device *dev)
|
||||
static void ipvlan_port_destroy(struct ipvl_port *port)
|
||||
{
|
||||
struct ipvl_port *port = ipvlan_port_get_rtnl(dev);
|
||||
struct net_device *dev = port->dev;
|
||||
struct sk_buff *skb;
|
||||
|
||||
netdev_put(dev, &port->dev_tracker);
|
||||
if (port->mode == IPVLAN_MODE_L3S)
|
||||
ipvlan_l3s_unregister(port);
|
||||
|
||||
netdev_rx_handler_unregister(dev);
|
||||
cancel_work_sync(&port->wq);
|
||||
netdev_put(dev, &port->dev_tracker);
|
||||
|
||||
while ((skb = __skb_dequeue(&port->backlog)) != NULL) {
|
||||
dev_put(skb->dev);
|
||||
kfree_skb(skb);
|
||||
|
|
@ -111,6 +127,27 @@ static void ipvlan_port_destroy(struct net_device *dev)
|
|||
kfree(port);
|
||||
}
|
||||
|
||||
static void ipvlan_port_put(struct ipvl_port *port)
|
||||
{
|
||||
if (refcount_dec_and_test(&port->count))
|
||||
ipvlan_port_destroy(port);
|
||||
}
|
||||
|
||||
static struct ipvl_port *ipvlan_port_get(struct net_device *dev)
|
||||
{
|
||||
struct ipvl_port *port = NULL;
|
||||
|
||||
rcu_read_lock();
|
||||
if (netif_is_ipvlan_port(dev)) {
|
||||
port = ipvlan_port_get_rcu(dev);
|
||||
if (!refcount_inc_not_zero(&port->count))
|
||||
port = NULL;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
#define IPVLAN_ALWAYS_ON_OFLOADS \
|
||||
(NETIF_F_SG | NETIF_F_HW_CSUM | \
|
||||
NETIF_F_GSO_ROBUST | NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_ENCAP_ALL)
|
||||
|
|
@ -153,30 +190,45 @@ static int ipvlan_init(struct net_device *dev)
|
|||
if (!ipvlan->pcpu_stats)
|
||||
return -ENOMEM;
|
||||
|
||||
netdev_lock(phy_dev);
|
||||
|
||||
if (!netif_is_ipvlan_port(phy_dev)) {
|
||||
err = ipvlan_port_create(phy_dev);
|
||||
if (err < 0) {
|
||||
netdev_unlock(phy_dev);
|
||||
free_percpu(ipvlan->pcpu_stats);
|
||||
return err;
|
||||
}
|
||||
port = ipvlan_port_get_rtnl(phy_dev);
|
||||
refcount_set(&port->count, 1);
|
||||
} else {
|
||||
port = ipvlan_port_get_rtnl(phy_dev);
|
||||
refcount_inc(&port->count);
|
||||
}
|
||||
port = ipvlan_port_get_rtnl(phy_dev);
|
||||
port->count += 1;
|
||||
|
||||
netdev_unlock(phy_dev);
|
||||
|
||||
ipvlan->port = port;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipvlan_uninit(struct net_device *dev)
|
||||
{
|
||||
struct ipvl_dev *ipvlan = netdev_priv(dev);
|
||||
struct net_device *phy_dev = ipvlan->phy_dev;
|
||||
struct ipvl_port *port;
|
||||
netdevice_tracker dev_tracker;
|
||||
struct net_device *phy_dev;
|
||||
|
||||
free_percpu(ipvlan->pcpu_stats);
|
||||
|
||||
port = ipvlan_port_get_rtnl(phy_dev);
|
||||
port->count -= 1;
|
||||
if (!port->count)
|
||||
ipvlan_port_destroy(port->dev);
|
||||
phy_dev = ipvlan->phy_dev;
|
||||
netdev_hold(phy_dev, &dev_tracker, GFP_KERNEL);
|
||||
netdev_lock(phy_dev);
|
||||
|
||||
ipvlan_port_put(ipvlan->port);
|
||||
|
||||
netdev_unlock(phy_dev);
|
||||
netdev_put(phy_dev, &dev_tracker);
|
||||
}
|
||||
|
||||
static int ipvlan_open(struct net_device *dev)
|
||||
|
|
@ -594,9 +646,7 @@ int ipvlan_link_new(struct net_device *dev, struct rtnl_newlink_params *params,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* ipvlan_init() would have created the port, if required */
|
||||
port = ipvlan_port_get_rtnl(phy_dev);
|
||||
ipvlan->port = port;
|
||||
port = ipvlan->port;
|
||||
|
||||
/* If the port-id base is at the MAX value, then wrap it around and
|
||||
* begin from 0x1 again. This may be due to a busy system where lots
|
||||
|
|
@ -639,7 +689,10 @@ int ipvlan_link_new(struct net_device *dev, struct rtnl_newlink_params *params,
|
|||
if (err)
|
||||
goto unlink_netdev;
|
||||
|
||||
mutex_lock(&port->pnodes_lock);
|
||||
list_add_tail_rcu(&ipvlan->pnode, &port->ipvlans);
|
||||
mutex_unlock(&port->pnodes_lock);
|
||||
|
||||
netif_stacked_transfer_operstate(phy_dev, dev);
|
||||
return 0;
|
||||
|
||||
|
|
@ -653,7 +706,8 @@ int ipvlan_link_new(struct net_device *dev, struct rtnl_newlink_params *params,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(ipvlan_link_new);
|
||||
|
||||
void ipvlan_link_delete(struct net_device *dev, struct list_head *head)
|
||||
void __ipvlan_link_delete(struct net *net, struct net_device *dev,
|
||||
struct list_head *head)
|
||||
{
|
||||
struct ipvl_dev *ipvlan = netdev_priv(dev);
|
||||
struct ipvl_addr *addr, *next;
|
||||
|
|
@ -668,10 +722,20 @@ void ipvlan_link_delete(struct net_device *dev, struct list_head *head)
|
|||
|
||||
ida_free(&ipvlan->port->ida, dev->dev_id);
|
||||
list_del_rcu(&ipvlan->pnode);
|
||||
unregister_netdevice_queue(dev, head);
|
||||
unregister_netdevice_queue_net(net, dev, head);
|
||||
netdev_upper_dev_unlink(ipvlan->phy_dev, dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipvlan_link_delete);
|
||||
EXPORT_SYMBOL(__ipvlan_link_delete);
|
||||
|
||||
static void ipvlan_link_delete(struct net_device *dev, struct list_head *head)
|
||||
{
|
||||
struct ipvl_dev *ipvlan = netdev_priv(dev);
|
||||
|
||||
mutex_lock(&ipvlan->port->pnodes_lock);
|
||||
if (!ipvlan->dying)
|
||||
__ipvlan_link_delete(dev_net(dev), dev, head);
|
||||
mutex_unlock(&ipvlan->port->pnodes_lock);
|
||||
}
|
||||
|
||||
void ipvlan_link_setup(struct net_device *dev)
|
||||
{
|
||||
|
|
@ -729,14 +793,19 @@ static int ipvlan_device_event(struct notifier_block *unused,
|
|||
struct netdev_notifier_pre_changeaddr_info *prechaddr_info;
|
||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
struct ipvl_dev *ipvlan, *next;
|
||||
int err, ret = NOTIFY_DONE;
|
||||
struct ipvl_port *port;
|
||||
LIST_HEAD(lst_kill);
|
||||
int err;
|
||||
|
||||
if (!netif_is_ipvlan_port(dev))
|
||||
return NOTIFY_DONE;
|
||||
if (event == NETDEV_PRECHANGEUPPER ||
|
||||
event == NETDEV_CHANGEUPPER)
|
||||
return ret;
|
||||
|
||||
port = ipvlan_port_get_rtnl(dev);
|
||||
port = ipvlan_port_get(dev);
|
||||
if (!port)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&port->pnodes_lock);
|
||||
|
||||
switch (event) {
|
||||
case NETDEV_UP:
|
||||
|
|
@ -760,16 +829,26 @@ static int ipvlan_device_event(struct notifier_block *unused,
|
|||
ipvlan_migrate_l3s_hook(oldnet, newnet);
|
||||
break;
|
||||
}
|
||||
case NETDEV_UNREGISTER:
|
||||
case NETDEV_UNREGISTER: {
|
||||
struct net *net = dev_net(dev);
|
||||
|
||||
if (dev->reg_state != NETREG_UNREGISTERING)
|
||||
break;
|
||||
|
||||
list_for_each_entry_safe(ipvlan, next, &port->ipvlans, pnode)
|
||||
ipvlan->dev->rtnl_link_ops->dellink(ipvlan->dev,
|
||||
&lst_kill);
|
||||
list_for_each_entry_safe(ipvlan, next, &port->ipvlans, pnode) {
|
||||
ipvlan->dying = true;
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPVTAP)
|
||||
if (ipvlan->dev->rtnl_link_ops != &ipvlan_link_ops)
|
||||
__ipvtap_dellink_ptr(net, ipvlan->dev, &lst_kill);
|
||||
else
|
||||
#endif
|
||||
__ipvlan_link_delete(net, ipvlan->dev, &lst_kill);
|
||||
}
|
||||
|
||||
unregister_netdevice_many(&lst_kill);
|
||||
break;
|
||||
|
||||
}
|
||||
case NETDEV_FEAT_CHANGE:
|
||||
list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
|
||||
netif_inherit_tso_max(ipvlan->dev, dev);
|
||||
|
|
@ -788,8 +867,10 @@ static int ipvlan_device_event(struct notifier_block *unused,
|
|||
err = netif_pre_changeaddr_notify(ipvlan->dev,
|
||||
prechaddr_info->dev_addr,
|
||||
extack);
|
||||
if (err)
|
||||
return notifier_from_errno(err);
|
||||
if (err) {
|
||||
ret = notifier_from_errno(err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -802,7 +883,8 @@ static int ipvlan_device_event(struct notifier_block *unused,
|
|||
|
||||
case NETDEV_PRE_TYPE_CHANGE:
|
||||
/* Forbid underlying device to change its type. */
|
||||
return NOTIFY_BAD;
|
||||
ret = NOTIFY_BAD;
|
||||
break;
|
||||
|
||||
case NETDEV_NOTIFY_PEERS:
|
||||
case NETDEV_BONDING_FAILOVER:
|
||||
|
|
@ -810,7 +892,12 @@ static int ipvlan_device_event(struct notifier_block *unused,
|
|||
list_for_each_entry(ipvlan, &port->ipvlans, pnode)
|
||||
call_netdevice_notifiers(event, ipvlan->dev);
|
||||
}
|
||||
return NOTIFY_DONE;
|
||||
|
||||
mutex_unlock(&port->pnodes_lock);
|
||||
|
||||
ipvlan_port_put(port);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* the caller must held the addrs lock */
|
||||
|
|
|
|||
|
|
@ -109,14 +109,26 @@ static int ipvtap_newlink(struct net_device *dev,
|
|||
return err;
|
||||
}
|
||||
|
||||
static void __ipvtap_dellink(struct net *net, struct net_device *dev,
|
||||
struct list_head *head)
|
||||
{
|
||||
struct ipvtap_dev *vlantap = netdev_priv(dev);
|
||||
|
||||
netdev_rx_handler_unregister(dev);
|
||||
tap_del_queues(&vlantap->tap);
|
||||
__ipvlan_link_delete(net, dev, head);
|
||||
}
|
||||
|
||||
static void ipvtap_dellink(struct net_device *dev,
|
||||
struct list_head *head)
|
||||
{
|
||||
struct ipvtap_dev *vlan = netdev_priv(dev);
|
||||
struct ipvtap_dev *vlantap = netdev_priv(dev);
|
||||
struct ipvl_port *port = vlantap->vlan.port;
|
||||
|
||||
netdev_rx_handler_unregister(dev);
|
||||
tap_del_queues(&vlan->tap);
|
||||
ipvlan_link_delete(dev, head);
|
||||
mutex_lock(&port->pnodes_lock);
|
||||
if (!vlantap->vlan.dying)
|
||||
__ipvtap_dellink(dev_net(dev), dev, head);
|
||||
mutex_unlock(&port->pnodes_lock);
|
||||
}
|
||||
|
||||
static void ipvtap_setup(struct net_device *dev)
|
||||
|
|
@ -198,6 +210,8 @@ static int __init ipvtap_init(void)
|
|||
{
|
||||
int err;
|
||||
|
||||
__ipvtap_dellink_ptr = __ipvtap_dellink;
|
||||
|
||||
err = tap_create_cdev(&ipvtap_cdev, &ipvtap_major, "ipvtap",
|
||||
THIS_MODULE);
|
||||
if (err)
|
||||
|
|
@ -224,6 +238,8 @@ static int __init ipvtap_init(void)
|
|||
out2:
|
||||
tap_destroy_cdev(ipvtap_major, &ipvtap_cdev);
|
||||
out1:
|
||||
__ipvtap_dellink_ptr = NULL;
|
||||
|
||||
return err;
|
||||
}
|
||||
module_init(ipvtap_init);
|
||||
|
|
@ -234,6 +250,7 @@ static void __exit ipvtap_exit(void)
|
|||
unregister_netdevice_notifier(&ipvtap_notifier_block);
|
||||
class_unregister(&ipvtap_class);
|
||||
tap_destroy_cdev(ipvtap_major, &ipvtap_cdev);
|
||||
__ipvtap_dellink_ptr = NULL;
|
||||
}
|
||||
module_exit(ipvtap_exit);
|
||||
MODULE_ALIAS_RTNL_LINK("ipvtap");
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ struct veth_priv {
|
|||
struct bpf_prog *_xdp_prog;
|
||||
struct veth_rq *rq;
|
||||
unsigned int requested_headroom;
|
||||
netdevice_tracker peer_tracker;
|
||||
};
|
||||
|
||||
struct veth_xdp_tx_bq {
|
||||
|
|
@ -1901,15 +1902,17 @@ static int veth_newlink(struct net_device *dev,
|
|||
|
||||
priv = netdev_priv(dev);
|
||||
rcu_assign_pointer(priv->peer, peer);
|
||||
netdev_hold(peer, &priv->peer_tracker, GFP_KERNEL);
|
||||
err = veth_init_queues(dev, tb);
|
||||
if (err)
|
||||
goto err_queues;
|
||||
|
||||
priv = netdev_priv(peer);
|
||||
rcu_assign_pointer(priv->peer, dev);
|
||||
netdev_hold(dev, &priv->peer_tracker, GFP_KERNEL);
|
||||
err = veth_init_queues(peer, tb);
|
||||
if (err)
|
||||
goto err_queues;
|
||||
goto err_peer_queues;
|
||||
|
||||
veth_disable_gro(dev);
|
||||
/* update XDP supported features */
|
||||
|
|
@ -1918,7 +1921,11 @@ static int veth_newlink(struct net_device *dev,
|
|||
|
||||
return 0;
|
||||
|
||||
err_peer_queues:
|
||||
netdev_put(dev, &priv->peer_tracker);
|
||||
priv = netdev_priv(dev);
|
||||
err_queues:
|
||||
netdev_put(peer, &priv->peer_tracker);
|
||||
unregister_netdevice(dev);
|
||||
err_register_dev:
|
||||
/* nothing to do */
|
||||
|
|
@ -1933,24 +1940,25 @@ static int veth_newlink(struct net_device *dev,
|
|||
|
||||
static void veth_dellink(struct net_device *dev, struct list_head *head)
|
||||
{
|
||||
struct veth_priv *priv;
|
||||
netdevice_tracker *peer_tracker;
|
||||
struct net_device *peer;
|
||||
struct veth_priv *priv;
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
peer = rtnl_dereference(priv->peer);
|
||||
peer_tracker = &priv->peer_tracker;
|
||||
peer = unrcu_pointer(xchg(&priv->peer, NULL));
|
||||
if (!peer)
|
||||
return;
|
||||
|
||||
/* Note : dellink() is called from default_device_exit_batch(),
|
||||
* before a rcu_synchronize() point. The devices are guaranteed
|
||||
* not being freed before one RCU grace period.
|
||||
*/
|
||||
RCU_INIT_POINTER(priv->peer, NULL);
|
||||
unregister_netdevice_queue(dev, head);
|
||||
|
||||
if (peer) {
|
||||
priv = netdev_priv(peer);
|
||||
RCU_INIT_POINTER(priv->peer, NULL);
|
||||
unregister_netdevice_queue(peer, head);
|
||||
}
|
||||
priv = netdev_priv(peer);
|
||||
dev = unrcu_pointer(xchg(&priv->peer, NULL));
|
||||
if (dev)
|
||||
unregister_netdevice_queue_net(dev_net(dev), peer, head);
|
||||
|
||||
netdev_put(peer, peer_tracker);
|
||||
netdev_put(dev, &priv->peer_tracker);
|
||||
}
|
||||
|
||||
static const struct nla_policy veth_policy[VETH_INFO_MAX + 1] = {
|
||||
|
|
|
|||
|
|
@ -1845,6 +1845,8 @@ enum netdev_reg_state {
|
|||
* @napi_list: List entry used for polling NAPI devices
|
||||
* @unreg_list: List entry when we are unregistering the
|
||||
* device; see the function unregister_netdev
|
||||
* @unreg_list_net:List entry when we are unregistering the cross-netns
|
||||
* device; see the function unregister_netdevice_queue_net()
|
||||
* @close_list: List entry used when we are closing the device
|
||||
* @ptype_all: Device-specific packet handlers for all protocols
|
||||
* @ptype_specific: Device-specific, protocol-specific packet handlers
|
||||
|
|
@ -2241,6 +2243,9 @@ struct net_device {
|
|||
struct list_head dev_list;
|
||||
struct list_head napi_list;
|
||||
struct list_head unreg_list;
|
||||
#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
|
||||
struct list_head unreg_list_net;
|
||||
#endif
|
||||
struct list_head close_list;
|
||||
struct list_head ptype_all;
|
||||
|
||||
|
|
@ -3472,6 +3477,25 @@ static inline void unregister_netdevice(struct net_device *dev)
|
|||
unregister_netdevice_queue(dev, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
|
||||
void unregister_netdevice_queue_net(struct net *net, struct net_device *dev,
|
||||
struct list_head *head);
|
||||
void unregister_netdevice_many_net(struct net *net);
|
||||
void unregister_netdevice_queue_many_net(struct net *net, struct list_head *head);
|
||||
#else
|
||||
static inline void unregister_netdevice_queue_net(struct net *net,
|
||||
struct net_device *dev,
|
||||
struct list_head *head)
|
||||
{
|
||||
unregister_netdevice_queue(dev, head);
|
||||
}
|
||||
|
||||
static inline void unregister_netdevice_queue_many_net(struct net *net,
|
||||
struct list_head *head)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
int netdev_refcnt_read(const struct net_device *dev);
|
||||
void free_netdev(struct net_device *dev);
|
||||
|
||||
|
|
|
|||
|
|
@ -115,6 +115,10 @@ bool rtnl_net_is_locked(struct net *net);
|
|||
|
||||
bool lockdep_rtnl_net_is_held(struct net *net);
|
||||
|
||||
void rtnl_net_queue_work(struct net *net);
|
||||
void rtnl_net_flush_workqueue(void);
|
||||
void rtnl_net_work_func(struct work_struct *work);
|
||||
|
||||
#define rcu_dereference_rtnl_net(net, p) \
|
||||
rcu_dereference_check(p, lockdep_rtnl_net_is_held(net))
|
||||
#define rtnl_net_dereference(net, p) \
|
||||
|
|
@ -150,6 +154,10 @@ static inline void ASSERT_RTNL_NET(struct net *net)
|
|||
ASSERT_RTNL();
|
||||
}
|
||||
|
||||
static inline void rtnl_net_flush_workqueue(void)
|
||||
{
|
||||
}
|
||||
|
||||
#define rcu_dereference_rtnl_net(net, p) \
|
||||
rcu_dereference_rtnl(p)
|
||||
#define rtnl_net_dereference(net, p) \
|
||||
|
|
|
|||
|
|
@ -197,6 +197,9 @@ struct net {
|
|||
#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
|
||||
/* Move to a better place when the config guard is removed. */
|
||||
struct mutex rtnl_mutex;
|
||||
struct work_struct rtnl_work;
|
||||
struct list_head dev_unreg_head;
|
||||
spinlock_t dev_unreg_lock;
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_VSOCKETS)
|
||||
struct netns_vsock vsock;
|
||||
|
|
|
|||
135
net/core/dev.c
135
net/core/dev.c
|
|
@ -11613,8 +11613,13 @@ static struct net_device *netdev_wait_allrefs_any(struct list_head *list)
|
|||
rtnl_lock();
|
||||
|
||||
/* Rebroadcast unregister notification */
|
||||
list_for_each_entry(dev, list, todo_list)
|
||||
list_for_each_entry(dev, list, todo_list) {
|
||||
struct net *net = dev_net(dev);
|
||||
|
||||
__rtnl_net_lock(net);
|
||||
call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
|
||||
__rtnl_net_unlock(net);
|
||||
}
|
||||
|
||||
__rtnl_unlock();
|
||||
rcu_barrier();
|
||||
|
|
@ -12092,6 +12097,9 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
|
|||
|
||||
INIT_LIST_HEAD(&dev->napi_list);
|
||||
INIT_LIST_HEAD(&dev->unreg_list);
|
||||
#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
|
||||
INIT_LIST_HEAD(&dev->unreg_list_net);
|
||||
#endif
|
||||
INIT_LIST_HEAD(&dev->close_list);
|
||||
INIT_LIST_HEAD(&dev->link_watch_list);
|
||||
INIT_LIST_HEAD(&dev->adj_list.upper);
|
||||
|
|
@ -12309,6 +12317,10 @@ void unregister_netdevice_queue(struct net_device *dev, struct list_head *head)
|
|||
{
|
||||
ASSERT_RTNL();
|
||||
|
||||
#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
|
||||
DEBUG_NET_WARN_ON_ONCE(!list_empty(&dev->unreg_list_net));
|
||||
#endif
|
||||
|
||||
if (head) {
|
||||
list_move_tail(&dev->unreg_list, head);
|
||||
} else {
|
||||
|
|
@ -12485,6 +12497,16 @@ void unregister_netdevice_many_notify(struct list_head *head,
|
|||
synchronize_net();
|
||||
|
||||
list_for_each_entry(dev, head, unreg_list) {
|
||||
#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
|
||||
struct net *net = dev_net(dev);
|
||||
|
||||
/* spin_lock() can be moved outside of the loop
|
||||
* once the per-netns RTNL conversion completes.
|
||||
*/
|
||||
spin_lock(&net->dev_unreg_lock);
|
||||
list_del(&dev->unreg_list_net);
|
||||
spin_unlock(&net->dev_unreg_lock);
|
||||
#endif
|
||||
netdev_put(dev, &dev->dev_registered_tracker);
|
||||
net_set_todo(dev);
|
||||
cnt++;
|
||||
|
|
@ -12507,6 +12529,96 @@ void unregister_netdevice_many(struct list_head *head)
|
|||
}
|
||||
EXPORT_SYMBOL(unregister_netdevice_many);
|
||||
|
||||
#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
|
||||
void unregister_netdevice_queue_net(struct net *net, struct net_device *dev,
|
||||
struct list_head *head)
|
||||
{
|
||||
netdev_lock(dev);
|
||||
|
||||
if (net_eq(dev_net(dev), net)) {
|
||||
netdev_unlock(dev);
|
||||
unregister_netdevice_queue(dev, head);
|
||||
return;
|
||||
}
|
||||
|
||||
net = dev_net(dev);
|
||||
|
||||
spin_lock(&net->dev_unreg_lock);
|
||||
|
||||
DEBUG_NET_WARN_ON_ONCE(!list_empty(&dev->unreg_list));
|
||||
DEBUG_NET_WARN_ON_ONCE(!list_empty(&dev->unreg_list_net));
|
||||
|
||||
list_add_tail(&dev->unreg_list_net, &net->dev_unreg_head);
|
||||
rtnl_net_queue_work(net);
|
||||
|
||||
spin_unlock(&net->dev_unreg_lock);
|
||||
|
||||
netdev_unlock(dev);
|
||||
}
|
||||
EXPORT_SYMBOL(unregister_netdevice_queue_net);
|
||||
|
||||
void unregister_netdevice_queue_many_net(struct net *net, struct list_head *head)
|
||||
{
|
||||
struct net_device *dev, *tmp;
|
||||
|
||||
spin_lock(&net->dev_unreg_lock);
|
||||
list_for_each_entry_safe(dev, tmp, head, unreg_list) {
|
||||
/* Once all cross-netns unregister_netdevice_queue() is
|
||||
* converted to _net() (or for debugging), remove this check.
|
||||
*/
|
||||
if (!net_eq(dev_net(dev), net))
|
||||
continue;
|
||||
|
||||
DEBUG_NET_WARN_ONCE(!net_eq(dev_net(dev), net),
|
||||
"%s was unregistered from a different netns.\n",
|
||||
dev->name);
|
||||
|
||||
list_del_init(&dev->unreg_list);
|
||||
list_move_tail(&dev->unreg_list_net, &net->dev_unreg_head);
|
||||
}
|
||||
spin_unlock(&net->dev_unreg_lock);
|
||||
}
|
||||
|
||||
static void unregister_netdevice_move_net(struct net *net_old,
|
||||
struct net *net,
|
||||
struct net_device *dev)
|
||||
{
|
||||
if (net_old > net) {
|
||||
spin_lock(&net->dev_unreg_lock);
|
||||
spin_lock_nested(&net_old->dev_unreg_lock, SINGLE_DEPTH_NESTING);
|
||||
} else {
|
||||
spin_lock(&net_old->dev_unreg_lock);
|
||||
spin_lock_nested(&net->dev_unreg_lock, SINGLE_DEPTH_NESTING);
|
||||
}
|
||||
|
||||
if (!list_empty(&dev->unreg_list_net)) {
|
||||
list_del(&dev->unreg_list_net);
|
||||
list_add_tail(&dev->unreg_list_net, &net->dev_unreg_head);
|
||||
}
|
||||
|
||||
spin_unlock(&net_old->dev_unreg_lock);
|
||||
spin_unlock(&net->dev_unreg_lock);
|
||||
}
|
||||
|
||||
void unregister_netdevice_many_net(struct net *net)
|
||||
{
|
||||
struct net_device *dev, *tmp;
|
||||
LIST_HEAD(unreg_head_net);
|
||||
LIST_HEAD(unreg_head);
|
||||
|
||||
spin_lock(&net->dev_unreg_lock);
|
||||
list_splice_init(&net->dev_unreg_head, &unreg_head_net);
|
||||
spin_unlock(&net->dev_unreg_lock);
|
||||
|
||||
list_for_each_entry_safe(dev, tmp, &unreg_head_net, unreg_list_net) {
|
||||
list_del_init(&dev->unreg_list_net);
|
||||
list_add_tail(&dev->unreg_list, &unreg_head);
|
||||
}
|
||||
|
||||
unregister_netdevice_many(&unreg_head);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* unregister_netdev - remove device from the kernel
|
||||
* @dev: device
|
||||
|
|
@ -12663,6 +12775,10 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,
|
|||
netdev_unlock(dev);
|
||||
dev->ifindex = new_ifindex;
|
||||
|
||||
#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
|
||||
unregister_netdevice_move_net(net_old, net, dev);
|
||||
#endif
|
||||
|
||||
if (new_name[0]) {
|
||||
/* Rename the netdev to prepared name */
|
||||
write_seqlock_bh(&netdev_rename_lock);
|
||||
|
|
@ -13039,7 +13155,7 @@ static void __net_exit default_device_exit_net(struct net *net)
|
|||
* Push all migratable network devices back to the
|
||||
* initial network namespace
|
||||
*/
|
||||
ASSERT_RTNL();
|
||||
|
||||
for_each_netdev_safe(net, dev, aux) {
|
||||
int err;
|
||||
char fb_name[IFNAMSIZ];
|
||||
|
|
@ -13082,21 +13198,36 @@ static void __net_exit default_device_exit_batch(struct list_head *net_list)
|
|||
LIST_HEAD(dev_kill_list);
|
||||
|
||||
rtnl_lock();
|
||||
|
||||
__rtnl_net_lock(&init_net);
|
||||
|
||||
list_for_each_entry(net, net_list, exit_list) {
|
||||
__rtnl_net_lock(net);
|
||||
default_device_exit_net(net);
|
||||
__rtnl_net_unlock(net);
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
__rtnl_net_unlock(&init_net);
|
||||
|
||||
list_for_each_entry(net, net_list, exit_list) {
|
||||
__rtnl_net_lock(net);
|
||||
|
||||
for_each_netdev_reverse(net, dev) {
|
||||
if (dev->rtnl_link_ops && dev->rtnl_link_ops->dellink)
|
||||
dev->rtnl_link_ops->dellink(dev, &dev_kill_list);
|
||||
else
|
||||
unregister_netdevice_queue(dev, &dev_kill_list);
|
||||
}
|
||||
|
||||
unregister_netdevice_queue_many_net(net, &dev_kill_list);
|
||||
__rtnl_net_unlock(net);
|
||||
}
|
||||
unregister_netdevice_many(&dev_kill_list);
|
||||
rtnl_unlock();
|
||||
|
||||
rtnl_net_flush_workqueue();
|
||||
}
|
||||
|
||||
static struct pernet_operations __net_initdata default_device_ops = {
|
||||
|
|
|
|||
|
|
@ -181,6 +181,7 @@ static void ops_exit_rtnl_list(const struct list_head *ops_list,
|
|||
ops->exit_rtnl(net, &dev_kill_list);
|
||||
}
|
||||
|
||||
unregister_netdevice_queue_many_net(net, &dev_kill_list);
|
||||
__rtnl_net_unlock(net);
|
||||
}
|
||||
|
||||
|
|
@ -422,6 +423,9 @@ static __net_init int preinit_net(struct net *net, struct user_namespace *user_n
|
|||
#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
|
||||
mutex_init(&net->rtnl_mutex);
|
||||
lock_set_cmp_fn(&net->rtnl_mutex, rtnl_net_lock_cmp_fn, NULL);
|
||||
INIT_WORK(&net->rtnl_work, rtnl_net_work_func);
|
||||
INIT_LIST_HEAD(&net->dev_unreg_head);
|
||||
spin_lock_init(&net->dev_unreg_lock);
|
||||
#endif
|
||||
|
||||
INIT_LIST_HEAD(&net->ptype_all);
|
||||
|
|
|
|||
|
|
@ -197,6 +197,7 @@ void __rtnl_net_unlock(struct net *net)
|
|||
{
|
||||
ASSERT_RTNL();
|
||||
|
||||
unregister_netdevice_many_net(net);
|
||||
mutex_unlock(&net->rtnl_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(__rtnl_net_unlock);
|
||||
|
|
@ -273,6 +274,29 @@ bool lockdep_rtnl_net_is_held(struct net *net)
|
|||
return lockdep_rtnl_is_held() && lockdep_is_held(&net->rtnl_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(lockdep_rtnl_net_is_held);
|
||||
|
||||
static struct workqueue_struct *rtnl_net_wq;
|
||||
|
||||
void rtnl_net_queue_work(struct net *net)
|
||||
{
|
||||
queue_work(rtnl_net_wq, &net->rtnl_work);
|
||||
}
|
||||
|
||||
void rtnl_net_flush_workqueue(void)
|
||||
{
|
||||
flush_workqueue(rtnl_net_wq);
|
||||
}
|
||||
|
||||
void rtnl_net_work_func(struct work_struct *work)
|
||||
{
|
||||
struct net *net = container_of(work, struct net, rtnl_work);
|
||||
|
||||
if (list_empty(&net->dev_unreg_head))
|
||||
return;
|
||||
|
||||
rtnl_net_lock(net);
|
||||
rtnl_net_unlock(net);
|
||||
}
|
||||
#else
|
||||
static int rtnl_net_cmp_locks(const struct net *net_a, const struct net *net_b)
|
||||
{
|
||||
|
|
@ -282,10 +306,11 @@ static int rtnl_net_cmp_locks(const struct net *net_a, const struct net *net_b)
|
|||
#endif
|
||||
|
||||
struct rtnl_nets {
|
||||
/* ->newlink() needs to freeze 3 netns at most;
|
||||
* 2 for the new device, 1 for its peer.
|
||||
/* ->newlink() needs to freeze 4 netns at most;
|
||||
* 2 for the new device, 1 for its peer, 1 for
|
||||
* an existing device (do_setlink() path).
|
||||
*/
|
||||
struct net *net[3];
|
||||
struct net *net[4];
|
||||
unsigned char len;
|
||||
};
|
||||
|
||||
|
|
@ -636,16 +661,15 @@ int rtnl_link_register(struct rtnl_link_ops *ops)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(rtnl_link_register);
|
||||
|
||||
static void __rtnl_kill_links(struct net *net, struct rtnl_link_ops *ops)
|
||||
static void __rtnl_kill_links(struct net *net, struct rtnl_link_ops *ops,
|
||||
struct list_head *dev_kill_list)
|
||||
{
|
||||
struct net_device *dev;
|
||||
LIST_HEAD(list_kill);
|
||||
|
||||
for_each_netdev(net, dev) {
|
||||
if (dev->rtnl_link_ops == ops)
|
||||
ops->dellink(dev, &list_kill);
|
||||
ops->dellink(dev, dev_kill_list);
|
||||
}
|
||||
unregister_netdevice_many(&list_kill);
|
||||
}
|
||||
|
||||
/* Return with the rtnl_lock held when there are no network
|
||||
|
|
@ -676,6 +700,7 @@ static void rtnl_lock_unregistering_all(void)
|
|||
*/
|
||||
void rtnl_link_unregister(struct rtnl_link_ops *ops)
|
||||
{
|
||||
LIST_HEAD(dev_kill_list);
|
||||
struct net *net;
|
||||
|
||||
mutex_lock(&link_ops_mutex);
|
||||
|
|
@ -689,8 +714,14 @@ void rtnl_link_unregister(struct rtnl_link_ops *ops)
|
|||
down_write(&pernet_ops_rwsem);
|
||||
rtnl_lock_unregistering_all();
|
||||
|
||||
for_each_net(net)
|
||||
__rtnl_kill_links(net, ops);
|
||||
for_each_net(net) {
|
||||
__rtnl_net_lock(net);
|
||||
__rtnl_kill_links(net, ops, &dev_kill_list);
|
||||
unregister_netdevice_queue_many_net(net, &dev_kill_list);
|
||||
__rtnl_net_unlock(net);
|
||||
}
|
||||
|
||||
unregister_netdevice_many(&dev_kill_list);
|
||||
|
||||
rtnl_unlock();
|
||||
up_write(&pernet_ops_rwsem);
|
||||
|
|
@ -4158,6 +4189,8 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|||
}
|
||||
}
|
||||
|
||||
rtnl_nets_add(&rtnl_nets, get_net(sock_net(skb->sk)));
|
||||
|
||||
rtnl_nets_lock(&rtnl_nets);
|
||||
ret = __rtnl_newlink(skb, nlh, ops, tgt_net, link_net, peer_net, tbs, data, extack);
|
||||
rtnl_nets_unlock(&rtnl_nets);
|
||||
|
|
@ -7224,4 +7257,10 @@ void __init rtnetlink_init(void)
|
|||
register_netdevice_notifier(&rtnetlink_dev_notifier);
|
||||
|
||||
rtnl_register_many(rtnetlink_rtnl_msg_handlers);
|
||||
|
||||
#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
|
||||
rtnl_net_wq = create_workqueue("rtnl_net");
|
||||
if (!rtnl_net_wq)
|
||||
panic("Could not create rtnl_net workq");
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user