diff --git a/drivers/net/bareudp.c b/drivers/net/bareudp.c index 5ef841c85526..c3b5ed52d877 100644 --- a/drivers/net/bareudp.c +++ b/drivers/net/bareudp.c @@ -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), }; diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h index 80f84fc87008..8d05ad480438 100644 --- a/drivers/net/ipvlan/ipvlan.h +++ b/drivers/net/ipvlan/ipvlan.h @@ -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 */ diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index ed46439a9f4e..ee46a55f73d1 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -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 */ diff --git a/drivers/net/ipvlan/ipvtap.c b/drivers/net/ipvlan/ipvtap.c index 2d6bbddd1edd..66c949d94261 100644 --- a/drivers/net/ipvlan/ipvtap.c +++ b/drivers/net/ipvlan/ipvtap.c @@ -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"); diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 1c5142149175..8170bf33ccf9 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -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] = { diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9981d637f8b5..8db25b79573e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -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); diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index ea39dd23a197..95729339e7a5 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -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) \ diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 80de5e98a66d..501af1999fe8 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -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; diff --git a/net/core/dev.c b/net/core/dev.c index 714d05283500..7c21bc0a1e34 100644 --- a/net/core/dev.c +++ b/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 = { diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index d9dafe24f57e..a91d2b58aadd 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -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); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 1b7d6f6b8b68..31c65a545a10 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -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 }