Commit 8965c160 authored by Stanislav Fomichev's avatar Stanislav Fomichev Committed by Jakub Kicinski
Browse files

net: use netif_disable_lro in ipv6_add_dev



ipv6_add_dev might call dev_disable_lro which unconditionally grabs
instance lock, so it will deadlock during NETDEV_REGISTER. Switch
to netif_disable_lro.

Make sure all callers hold the instance lock as well.

Cc: Cosmin Ratiu <cratiu@nvidia.com>
Fixes: ad7c7b21 ("net: hold netdev instance lock during sysfs operations")
Signed-off-by: default avatarStanislav Fomichev <sdf@fomichev.me>
Link: https://patch.msgid.link/20250401163452.622454-4-sdf@fomichev.me


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 4c975fd7
Loading
Loading
Loading
Loading
+8 −8
Original line number Diff line number Diff line
@@ -667,14 +667,6 @@ static inline void ip_ipgre_mc_map(__be32 naddr, const unsigned char *broadcast,
		memcpy(buf, &naddr, sizeof(naddr));
}

#if IS_MODULE(CONFIG_IPV6)
#define EXPORT_IPV6_MOD(X) EXPORT_SYMBOL(X)
#define EXPORT_IPV6_MOD_GPL(X) EXPORT_SYMBOL_GPL(X)
#else
#define EXPORT_IPV6_MOD(X)
#define EXPORT_IPV6_MOD_GPL(X)
#endif

#if IS_ENABLED(CONFIG_IPV6)
#include <linux/ipv6.h>
#endif
@@ -694,6 +686,14 @@ static __inline__ void inet_reset_saddr(struct sock *sk)

#endif

#if IS_MODULE(CONFIG_IPV6)
#define EXPORT_IPV6_MOD(X) EXPORT_SYMBOL(X)
#define EXPORT_IPV6_MOD_GPL(X) EXPORT_SYMBOL_GPL(X)
#else
#define EXPORT_IPV6_MOD(X)
#define EXPORT_IPV6_MOD_GPL(X)
#endif

static inline unsigned int ipv4_addr_hash(__be32 ip)
{
	return (__force unsigned int) ip;
+1 −0
Original line number Diff line number Diff line
@@ -1771,6 +1771,7 @@ void netif_disable_lro(struct net_device *dev)
		netdev_unlock_ops(lower_dev);
	}
}
EXPORT_IPV6_MOD(netif_disable_lro);

/**
 *	dev_disable_gro_hw - disable HW Generic Receive Offload on a device
+13 −2
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@
#include <net/netlink.h>
#include <net/pkt_sched.h>
#include <net/l3mdev.h>
#include <net/netdev_lock.h>
#include <linux/if_tunnel.h>
#include <linux/rtnetlink.h>
#include <linux/netconf.h>
@@ -377,6 +378,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
	int err = -ENOMEM;

	ASSERT_RTNL();
	netdev_ops_assert_locked(dev);

	if (dev->mtu < IPV6_MIN_MTU && dev != blackhole_netdev)
		return ERR_PTR(-EINVAL);
@@ -402,7 +404,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
		return ERR_PTR(err);
	}
	if (ndev->cnf.forwarding)
		dev_disable_lro(dev);
		netif_disable_lro(dev);
	/* We refer to the device */
	netdev_hold(dev, &ndev->dev_tracker, GFP_KERNEL);

@@ -3152,10 +3154,12 @@ int addrconf_add_ifaddr(struct net *net, void __user *arg)

	rtnl_net_lock(net);
	dev = __dev_get_by_index(net, ireq.ifr6_ifindex);
	netdev_lock_ops(dev);
	if (dev)
		err = inet6_addr_add(net, dev, &cfg, 0, 0, NULL);
	else
		err = -ENODEV;
	netdev_unlock_ops(dev);
	rtnl_net_unlock(net);
	return err;
}
@@ -5026,9 +5030,10 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
	if (!dev) {
		NL_SET_ERR_MSG_MOD(extack, "Unable to find the interface");
		err = -ENODEV;
		goto unlock;
		goto unlock_rtnl;
	}

	netdev_lock_ops(dev);
	idev = ipv6_find_idev(dev);
	if (IS_ERR(idev)) {
		err = PTR_ERR(idev);
@@ -5065,6 +5070,8 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,

	in6_ifa_put(ifa);
unlock:
	netdev_unlock_ops(dev);
unlock_rtnl:
	rtnl_net_unlock(net);

	return err;
@@ -6516,7 +6523,9 @@ static int addrconf_sysctl_addr_gen_mode(const struct ctl_table *ctl, int write,

			if (idev->cnf.addr_gen_mode != new_val) {
				WRITE_ONCE(idev->cnf.addr_gen_mode, new_val);
				netdev_lock_ops(idev->dev);
				addrconf_init_auto_addrs(idev->dev);
				netdev_unlock_ops(idev->dev);
			}
		} else if (&net->ipv6.devconf_all->addr_gen_mode == ctl->data) {
			struct net_device *dev;
@@ -6528,7 +6537,9 @@ static int addrconf_sysctl_addr_gen_mode(const struct ctl_table *ctl, int write,
				    idev->cnf.addr_gen_mode != new_val) {
					WRITE_ONCE(idev->cnf.addr_gen_mode,
						  new_val);
					netdev_lock_ops(idev->dev);
					addrconf_init_auto_addrs(idev->dev);
					netdev_unlock_ops(idev->dev);
				}
			}
		}