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

bonding: use dynamic lockdep key instead of subclass

All bonding device has same lockdep key and subclass is initialized with
nest_level.
But actual nest_level value can be changed when a lower device is attached.
And at this moment, the subclass should be updated but it seems to be
unsafe.
So this patch makes bonding use dynamic lockdep key instead of the
subclass.

Test commands:
ip link add bond0 type bond

for i in {1..5}
do
let A=$i-1
ip link add bond$i type bond
ip link set bond$i master bond$A
done
ip link set bond5 master bond0

Splat looks like:
[ 307.992912] WARNING: possible recursive locking detected
[ 307.993656] 5.4.0-rc3+ #96 Tainted: G W
[ 307.994367] --------------------------------------------
[ 307.995092] ip/761 is trying to acquire lock:
[ 307.995710] ffff8880513aac60 (&(&bond->stats_lock)->rlock#2/2){+.+.}, at: bond_get_stats+0xb8/0x500 [bonding]
[ 307.997045]
but task is already holding lock:
[ 307.997923] ffff88805fcbac60 (&(&bond->stats_lock)->rlock#2/2){+.+.}, at: bond_get_stats+0xb8/0x500 [bonding]
[ 307.999215]
other info that might help us debug this:
[ 308.000251] Possible unsafe locking scenario:

[ 308.001137] CPU0
[ 308.001533] ----
[ 308.001915] lock(&(&bond->stats_lock)->rlock#2/2);
[ 308.002609] lock(&(&bond->stats_lock)->rlock#2/2);
[ 308.003302]
*** DEADLOCK ***

[ 308.004310] May be due to missing lock nesting notation

[ 308.005319] 3 locks held by ip/761:
[ 308.005830] #0: ffffffff9fcc42b0 (rtnl_mutex){+.+.}, at: rtnetlink_rcv_msg+0x466/0x8a0
[ 308.006894] #1: ffff88805fcbac60 (&(&bond->stats_lock)->rlock#2/2){+.+.}, at: bond_get_stats+0xb8/0x500 [bonding]
[ 308.008243] #2: ffffffff9f9219c0 (rcu_read_lock){....}, at: bond_get_stats+0x9f/0x500 [bonding]
[ 308.009422]
stack backtrace:
[ 308.010124] CPU: 0 PID: 761 Comm: ip Tainted: G W 5.4.0-rc3+ #96
[ 308.011097] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[ 308.012179] Call Trace:
[ 308.012601] dump_stack+0x7c/0xbb
[ 308.013089] __lock_acquire+0x269d/0x3de0
[ 308.013669] ? register_lock_class+0x14d0/0x14d0
[ 308.014318] lock_acquire+0x164/0x3b0
[ 308.014858] ? bond_get_stats+0xb8/0x500 [bonding]
[ 308.015520] _raw_spin_lock_nested+0x2e/0x60
[ 308.016129] ? bond_get_stats+0xb8/0x500 [bonding]
[ 308.017215] bond_get_stats+0xb8/0x500 [bonding]
[ 308.018454] ? bond_arp_rcv+0xf10/0xf10 [bonding]
[ 308.019710] ? rcu_read_lock_held+0x90/0xa0
[ 308.020605] ? rcu_read_lock_sched_held+0xc0/0xc0
[ 308.021286] ? bond_get_stats+0x9f/0x500 [bonding]
[ 308.021953] dev_get_stats+0x1ec/0x270
[ 308.022508] bond_get_stats+0x1d1/0x500 [bonding]

Fixes: d3fff6c443fe ("net: add netdev_lockdep_set_classes() helper")
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
089bca2c 65de65d9

+8 -3
+7 -3
drivers/net/bonding/bond_main.c
··· 3459 3459 struct list_head *iter; 3460 3460 struct slave *slave; 3461 3461 3462 - spin_lock_nested(&bond->stats_lock, bond_get_nest_level(bond_dev)); 3462 + spin_lock(&bond->stats_lock); 3463 3463 memcpy(stats, &bond->bond_stats, sizeof(*stats)); 3464 3464 3465 3465 rcu_read_lock(); ··· 4297 4297 { 4298 4298 struct bonding *bond = netdev_priv(bond_dev); 4299 4299 4300 - spin_lock_init(&bond->mode_lock); 4301 - spin_lock_init(&bond->stats_lock); 4302 4300 bond->params = bonding_defaults; 4303 4301 4304 4302 /* Initialize pointers */ ··· 4365 4367 4366 4368 list_del(&bond->bond_list); 4367 4369 4370 + lockdep_unregister_key(&bond->stats_lock_key); 4368 4371 bond_debug_unregister(bond); 4369 4372 } 4370 4373 ··· 4770 4771 return -ENOMEM; 4771 4772 4772 4773 bond->nest_level = SINGLE_DEPTH_NESTING; 4774 + 4775 + spin_lock_init(&bond->mode_lock); 4776 + spin_lock_init(&bond->stats_lock); 4777 + lockdep_register_key(&bond->stats_lock_key); 4778 + lockdep_set_class(&bond->stats_lock, &bond->stats_lock_key); 4773 4779 4774 4780 list_add_tail(&bond->bond_list, &bn->dev_list); 4775 4781
+1
include/net/bonding.h
··· 239 239 struct dentry *debug_dir; 240 240 #endif /* CONFIG_DEBUG_FS */ 241 241 struct rtnl_link_stats64 bond_stats; 242 + struct lock_class_key stats_lock_key; 242 243 }; 243 244 244 245 #define bond_slave_get_rcu(dev) \