mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
Merge branch 'net-hold-instance-lock-around-netdev_down-and-netdev_going_down'
Stanislav Fomichev says: ==================== net: hold instance lock around NETDEV_DOWN and NETDEV_GOING_DOWN NETDEV_UP and NETDEV_REGISTER already run under the per-device instance lock. The teardown side does not. Make it symmetric so ops-locked drivers can rely on the lock being held in both directions. ==================== Link: https://patch.msgid.link/20260702224150.3730033-1-sdf@fomichev.me Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
commit
4a13f31a92
|
|
@ -421,10 +421,17 @@ running under the lock:
|
|||
* ``NETDEV_CHANGENAME``
|
||||
* ``NETDEV_REGISTER``
|
||||
* ``NETDEV_UP``
|
||||
* ``NETDEV_DOWN``
|
||||
* ``NETDEV_GOING_DOWN``
|
||||
|
||||
The following notifiers are running without the lock:
|
||||
* ``NETDEV_UNREGISTER``
|
||||
|
||||
Many SW devices (uppers) catch their lower's ``NETDEV_UNREGISTER``
|
||||
events and may interact with them via ``dev_*()`` handlers, which take
|
||||
the instance lock. Until we convert these devices to ``netif_*()`` variants,
|
||||
``NETDEV_UNREGISTER`` stays unlocked.
|
||||
|
||||
There are no clear expectations for the remaining notifiers. Notifiers not on
|
||||
the list may run with or without the instance lock, potentially even invoking
|
||||
the same notifier type with and without the lock from different code paths.
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/bitfield.h>
|
||||
#include <net/dsa.h>
|
||||
#include <net/dst_metadata.h>
|
||||
#include <net/netdev_lock.h>
|
||||
#include <net/page_pool/helpers.h>
|
||||
#include <linux/genalloc.h>
|
||||
|
||||
|
|
@ -5030,10 +5031,14 @@ void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev)
|
|||
continue;
|
||||
|
||||
list_add_tail(&dev->close_list, &dev_list);
|
||||
netdev_lock_ops(dev);
|
||||
}
|
||||
|
||||
netif_close_many(&dev_list, false);
|
||||
|
||||
list_for_each_entry(dev, &dev_list, close_list)
|
||||
netdev_unlock_ops(dev);
|
||||
|
||||
eth->dma_dev = dma_dev;
|
||||
|
||||
list_for_each_entry_safe(dev, tmp, &dev_list, close_list) {
|
||||
|
|
|
|||
|
|
@ -1802,6 +1802,7 @@ void netif_close_many(struct list_head *head, bool unlink)
|
|||
__dev_close_many(head);
|
||||
|
||||
list_for_each_entry_safe(dev, tmp, head, close_list) {
|
||||
netdev_assert_locked_ops_compat(dev);
|
||||
rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP | IFF_RUNNING, GFP_KERNEL, 0, NULL);
|
||||
call_netdevice_notifiers(NETDEV_DOWN, dev);
|
||||
if (unlink)
|
||||
|
|
@ -1912,9 +1913,11 @@ static void call_netdevice_unregister_notifiers(struct notifier_block *nb,
|
|||
struct net_device *dev)
|
||||
{
|
||||
if (dev->flags & IFF_UP) {
|
||||
netdev_lock_ops(dev);
|
||||
call_netdevice_notifier(nb, NETDEV_GOING_DOWN,
|
||||
dev);
|
||||
call_netdevice_notifier(nb, NETDEV_DOWN, dev);
|
||||
netdev_unlock_ops(dev);
|
||||
}
|
||||
call_netdevice_notifier(nb, NETDEV_UNREGISTER, dev);
|
||||
}
|
||||
|
|
@ -9785,6 +9788,8 @@ void __dev_notify_flags(struct net_device *dev, unsigned int old_flags,
|
|||
{
|
||||
unsigned int changes = dev->flags ^ old_flags;
|
||||
|
||||
netdev_assert_locked_ops_compat(dev);
|
||||
|
||||
if (gchanges)
|
||||
rtmsg_ifinfo(RTM_NEWLINK, dev, gchanges, GFP_ATOMIC, portid, nlh);
|
||||
|
||||
|
|
|
|||
|
|
@ -24,15 +24,15 @@ int netdev_debug_event(struct notifier_block *nb, unsigned long event,
|
|||
case NETDEV_CHANGE:
|
||||
case NETDEV_REGISTER:
|
||||
case NETDEV_UP:
|
||||
case NETDEV_DOWN:
|
||||
case NETDEV_GOING_DOWN:
|
||||
netdev_assert_locked_ops_compat(dev);
|
||||
fallthrough;
|
||||
case NETDEV_DOWN:
|
||||
case NETDEV_REBOOT:
|
||||
case NETDEV_UNREGISTER:
|
||||
case NETDEV_CHANGEMTU:
|
||||
case NETDEV_CHANGEADDR:
|
||||
case NETDEV_PRE_CHANGEADDR:
|
||||
case NETDEV_GOING_DOWN:
|
||||
case NETDEV_FEAT_CHANGE:
|
||||
case NETDEV_BONDING_FAILOVER:
|
||||
case NETDEV_PRE_UP:
|
||||
|
|
|
|||
|
|
@ -3660,14 +3660,16 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm,
|
|||
u32 portid, const struct nlmsghdr *nlh)
|
||||
{
|
||||
unsigned int old_flags, changed;
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
netdev_lock_ops(dev);
|
||||
|
||||
old_flags = dev->flags;
|
||||
if (ifm && (ifm->ifi_flags || ifm->ifi_change)) {
|
||||
err = __dev_change_flags(dev, rtnl_dev_combine_flags(dev, ifm),
|
||||
NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto out;
|
||||
}
|
||||
|
||||
changed = old_flags ^ dev->flags;
|
||||
|
|
@ -3677,7 +3679,10 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm,
|
|||
}
|
||||
|
||||
__dev_notify_flags(dev, old_flags, changed, portid, nlh);
|
||||
return 0;
|
||||
|
||||
out:
|
||||
netdev_unlock_ops(dev);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(rtnl_configure_link);
|
||||
|
||||
|
|
@ -3918,22 +3923,20 @@ static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm,
|
|||
goto out;
|
||||
}
|
||||
|
||||
netdev_lock_ops(dev);
|
||||
|
||||
err = rtnl_configure_link(dev, ifm, portid, nlh);
|
||||
if (err < 0)
|
||||
goto out_unregister;
|
||||
if (tb[IFLA_MASTER]) {
|
||||
netdev_lock_ops(dev);
|
||||
err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER]), extack);
|
||||
netdev_unlock_ops(dev);
|
||||
if (err)
|
||||
goto out_unregister;
|
||||
}
|
||||
|
||||
netdev_unlock_ops(dev);
|
||||
out:
|
||||
return err;
|
||||
out_unregister:
|
||||
netdev_unlock_ops(dev);
|
||||
if (ops->newlink) {
|
||||
LIST_HEAD(list_kill);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_net.h>
|
||||
#include <net/dsa_stubs.h>
|
||||
#include <net/netdev_lock.h>
|
||||
#include <net/sch_generic.h>
|
||||
|
||||
#include "conduit.h"
|
||||
|
|
@ -1620,10 +1621,23 @@ void dsa_switch_shutdown(struct dsa_switch *ds)
|
|||
|
||||
rtnl_lock();
|
||||
|
||||
dsa_switch_for_each_cpu_port(dp, ds)
|
||||
list_add(&dp->conduit->close_list, &close_list);
|
||||
dsa_switch_for_each_cpu_port(dp, ds) {
|
||||
if (!(dp->conduit->flags & IFF_UP))
|
||||
continue;
|
||||
list_add_tail(&dp->conduit->close_list, &close_list);
|
||||
netdev_lock_ops(dp->conduit);
|
||||
}
|
||||
|
||||
netif_close_many(&close_list, true);
|
||||
netif_close_many(&close_list, false);
|
||||
|
||||
while (!list_empty(&close_list)) {
|
||||
struct net_device *conduit;
|
||||
|
||||
conduit = list_first_entry(&close_list, struct net_device,
|
||||
close_list);
|
||||
netdev_unlock_ops(conduit);
|
||||
list_del_init(&conduit->close_list);
|
||||
}
|
||||
|
||||
dsa_switch_for_each_user_port(dp, ds) {
|
||||
conduit = dsa_port_to_conduit(dp);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/of_net.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include <linux/mdio.h>
|
||||
#include <net/netdev_lock.h>
|
||||
#include <net/rtnetlink.h>
|
||||
#include <net/pkt_cls.h>
|
||||
#include <net/selftests.h>
|
||||
|
|
@ -3599,10 +3600,24 @@ static int dsa_user_netdevice_event(struct notifier_block *nb,
|
|||
if (dp->cpu_dp != cpu_dp)
|
||||
continue;
|
||||
|
||||
list_add(&dp->user->close_list, &close_list);
|
||||
if (!(dp->user->flags & IFF_UP))
|
||||
continue;
|
||||
|
||||
list_add_tail(&dp->user->close_list, &close_list);
|
||||
netdev_lock_ops(dp->user);
|
||||
}
|
||||
|
||||
netif_close_many(&close_list, true);
|
||||
netif_close_many(&close_list, false);
|
||||
|
||||
while (!list_empty(&close_list)) {
|
||||
struct net_device *user_dev;
|
||||
|
||||
user_dev = list_first_entry(&close_list,
|
||||
struct net_device,
|
||||
close_list);
|
||||
netdev_unlock_ops(user_dev);
|
||||
list_del_init(&user_dev->close_list);
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user