Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

team: fix nested locking lockdep warning

team interface could be nested and it's lock variable could be nested too.
But this lock uses static lockdep key and there is no nested locking
handling code such as mutex_lock_nested() and so on.
so the Lockdep would warn about the circular locking scenario that
couldn't happen.
In order to fix, this patch makes the team module to use dynamic lock key
instead of static key.

Test commands:
ip link add team0 type team
ip link add team1 type team
ip link set team0 master team1
ip link set team0 nomaster
ip link set team1 master team0
ip link set team1 nomaster

Splat that looks like:
[ 40.364352] WARNING: possible recursive locking detected
[ 40.364964] 5.4.0-rc3+ #96 Not tainted
[ 40.365405] --------------------------------------------
[ 40.365973] ip/750 is trying to acquire lock:
[ 40.366542] ffff888060b34c40 (&team->lock){+.+.}, at: team_set_mac_address+0x151/0x290 [team]
[ 40.367689]
but task is already holding lock:
[ 40.368729] ffff888051201c40 (&team->lock){+.+.}, at: team_del_slave+0x29/0x60 [team]
[ 40.370280]
other info that might help us debug this:
[ 40.371159] Possible unsafe locking scenario:

[ 40.371942] CPU0
[ 40.372338] ----
[ 40.372673] lock(&team->lock);
[ 40.373115] lock(&team->lock);
[ 40.373549]
*** DEADLOCK ***

[ 40.374432] May be due to missing lock nesting notation

[ 40.375338] 2 locks held by ip/750:
[ 40.375851] #0: ffffffffabcc42b0 (rtnl_mutex){+.+.}, at: rtnetlink_rcv_msg+0x466/0x8a0
[ 40.376927] #1: ffff888051201c40 (&team->lock){+.+.}, at: team_del_slave+0x29/0x60 [team]
[ 40.377989]
stack backtrace:
[ 40.378650] CPU: 0 PID: 750 Comm: ip Not tainted 5.4.0-rc3+ #96
[ 40.379368] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[ 40.380574] Call Trace:
[ 40.381208] dump_stack+0x7c/0xbb
[ 40.381959] __lock_acquire+0x269d/0x3de0
[ 40.382817] ? register_lock_class+0x14d0/0x14d0
[ 40.383784] ? check_chain_key+0x236/0x5d0
[ 40.384518] lock_acquire+0x164/0x3b0
[ 40.385074] ? team_set_mac_address+0x151/0x290 [team]
[ 40.385805] __mutex_lock+0x14d/0x14c0
[ 40.386371] ? team_set_mac_address+0x151/0x290 [team]
[ 40.387038] ? team_set_mac_address+0x151/0x290 [team]
[ 40.387632] ? mutex_lock_io_nested+0x1380/0x1380
[ 40.388245] ? team_del_slave+0x60/0x60 [team]
[ 40.388752] ? rcu_read_lock_sched_held+0x90/0xc0
[ 40.389304] ? rcu_read_lock_bh_held+0xa0/0xa0
[ 40.389819] ? lock_acquire+0x164/0x3b0
[ 40.390285] ? lockdep_rtnl_is_held+0x16/0x20
[ 40.390797] ? team_port_get_rtnl+0x90/0xe0 [team]
[ 40.391353] ? __module_text_address+0x13/0x140
[ 40.391886] ? team_set_mac_address+0x151/0x290 [team]
[ 40.392547] team_set_mac_address+0x151/0x290 [team]
[ 40.393111] dev_set_mac_address+0x1f0/0x3f0
[ ... ]

Fixes: 3d249d4ca7d0 ("net: introduce ethernet teaming device")
Signed-off-by: Taehee Yoo <ap420073@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Taehee Yoo and committed by
David S. Miller
369f61be 089bca2c

+14 -3
+13 -3
drivers/net/team/team.c
··· 1615 1615 int err; 1616 1616 1617 1617 team->dev = dev; 1618 - mutex_init(&team->lock); 1619 1618 team_set_no_mode(team); 1620 1619 1621 1620 team->pcpu_stats = netdev_alloc_pcpu_stats(struct team_pcpu_stats); ··· 1640 1641 if (err) 1641 1642 goto err_options_register; 1642 1643 netif_carrier_off(dev); 1644 + 1645 + lockdep_register_key(&team->team_lock_key); 1646 + __mutex_init(&team->lock, "team->team_lock_key", &team->team_lock_key); 1643 1647 1644 1648 return 0; 1645 1649 ··· 1673 1671 team_queue_override_fini(team); 1674 1672 mutex_unlock(&team->lock); 1675 1673 netdev_change_features(dev); 1674 + lockdep_unregister_key(&team->team_lock_key); 1676 1675 } 1677 1676 1678 1677 static void team_destructor(struct net_device *dev) ··· 1977 1974 err = team_port_del(team, port_dev); 1978 1975 mutex_unlock(&team->lock); 1979 1976 1980 - if (!err) 1981 - netdev_change_features(dev); 1977 + if (err) 1978 + return err; 1979 + 1980 + if (netif_is_team_master(port_dev)) { 1981 + lockdep_unregister_key(&team->team_lock_key); 1982 + lockdep_register_key(&team->team_lock_key); 1983 + lockdep_set_class(&team->lock, &team->team_lock_key); 1984 + } 1985 + netdev_change_features(dev); 1982 1986 1983 1987 return err; 1984 1988 }
+1
include/linux/if_team.h
··· 223 223 atomic_t count_pending; 224 224 struct delayed_work dw; 225 225 } mcast_rejoin; 226 + struct lock_class_key team_lock_key; 226 227 long mode_priv[TEAM_MODE_PRIV_LONGS]; 227 228 }; 228 229