In the current code, all team devices have same static lockdep key and team devices could be nested so that it makes unnecessary lockdep warning.
Test commands: ip link add team0 type team for i in {1..7} do let A=$i-1 ip link add team$i type team ip link set team$i master team$A done ip link del team0 Splat looks like: [ 52.518420] WARNING: possible recursive locking detected [ 52.518955] 5.3.0-rc8+ #179 Not tainted [ 52.519373] -------------------------------------------- [ 52.519925] ip/1005 is trying to acquire lock: [ 52.520893] 00000000c84e69ac (&dev_addr_list_lock_key/1){+...}, at: dev_uc_sync_multiple+0xfa/0x1a0 [ 52.522194] [ 52.522194] but task is already holding lock: [ 52.523038] 0000000025864f52 (&dev_addr_list_lock_key/1){+...}, at: dev_uc_unsync+0x10c/0x1b0 [ 52.524609] [ 52.524609] other info that might help us debug this: [ 52.525435] Possible unsafe locking scenario: [ 52.525435] [ 52.526154] CPU0 [ 52.526460] ---- [ 52.526766] lock(&dev_addr_list_lock_key/1); [ 52.527311] lock(&dev_addr_list_lock_key/1); [ 52.527854] [ 52.527854] *** DEADLOCK *** [ 52.527854] [ 52.528573] May be due to missing lock nesting notation [ 52.528573] [ 52.529527] 5 locks held by ip/1005: [ 52.529968] #0: 0000000080a68bb2 (rtnl_mutex){+.+.}, at: rtnetlink_rcv_msg+0x466/0x8a0 [ 52.530940] #1: 0000000035d90450 (&team->lock){+.+.}, at: team_uninit+0x3a/0x1a0 [team] [ 52.531933] #2: 00000000c75d8f70 (&dev_addr_list_lock_key){+...}, at: dev_uc_unsync+0x98/0x1b0 [ 52.532991] #3: 0000000025864f52 (&dev_addr_list_lock_key/1){+...}, at: dev_uc_unsync+0x10c/0x1b0 [ 52.534142] #4: 00000000efa4d642 (rcu_read_lock){....}, at: team_set_rx_mode+0x5/0x1d0 [team] [ 52.535195] [ 52.535195] stack backtrace: [ 52.535727] CPU: 0 PID: 1005 Comm: ip Not tainted 5.3.0-rc8+ #179 [ 52.536376] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 [ 52.537254] Call Trace: [ 52.537549] dump_stack+0x7c/0xbb [ 52.537917] __lock_acquire+0x26a9/0x3de0 [ 52.538340] ? register_lock_class+0x14d0/0x14d0 [ 52.538872] ? register_lock_class+0x14d0/0x14d0 [ 52.539559] lock_acquire+0x164/0x3b0 [ 52.540043] ? dev_uc_sync_multiple+0xfa/0x1a0 [ 52.540663] _raw_spin_lock_nested+0x2e/0x60 [ 52.541241] ? dev_uc_sync_multiple+0xfa/0x1a0 [ 52.541867] dev_uc_sync_multiple+0xfa/0x1a0 [ 52.542435] team_set_rx_mode+0xa9/0x1d0 [team] [ 52.543033] dev_uc_unsync+0x151/0x1b0 [ 52.543534] team_port_del+0x304/0x790 [team] [ 52.544110] team_uninit+0xb0/0x1a0 [team] [ 52.544653] rollback_registered_many+0x728/0xda0 [ 52.545271] ? generic_xdp_install+0x310/0x310 [ 52.545865] ? check_chain_key+0x236/0x5d0 [ 52.546425] ? __nla_validate_parse+0x98/0x1ad0 [ 52.547023] unregister_netdevice_many.part.124+0x13/0x1b0 [ 52.547741] rtnl_delete_link+0xbc/0x100 [ 52.548262] ? rtnl_af_register+0xc0/0xc0 [ 52.548793] rtnl_dellink+0x30e/0x8a0 [ ... ] Fixes: 3d249d4ca7d0 ("net: introduce ethernet teaming device") Signed-off-by: Taehee Yoo <ap420...@gmail.com> --- v2 -> v3 : - This patch is not changed v1 -> v2 : - This patch is not changed drivers/net/team/team.c | 61 ++++++++++++++++++++++++++++++++++++++--- include/linux/if_team.h | 5 ++++ 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index e8089def5a46..bfcd6ed57493 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1607,6 +1607,34 @@ static const struct team_option team_options[] = { }, }; +static void team_dev_set_lockdep_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) +{ + struct team *team = netdev_priv(dev); + + lockdep_set_class(&txq->_xmit_lock, &team->xmit_lock_key); +} + +static struct lock_class_key qdisc_tx_busylock_key; +static struct lock_class_key qdisc_running_key; + +static void team_dev_set_lockdep_class(struct net_device *dev) +{ + struct team *team = netdev_priv(dev); + + dev->qdisc_tx_busylock = &qdisc_tx_busylock_key; + dev->qdisc_running_key = &qdisc_running_key; + + lockdep_register_key(&team->team_lock_key); + __mutex_init(&team->lock, "team->team_lock_key", &team->team_lock_key); + + lockdep_register_key(&team->addr_lock_key); + lockdep_set_class(&dev->addr_list_lock, &team->addr_lock_key); + + lockdep_register_key(&team->xmit_lock_key); + netdev_for_each_tx_queue(dev, team_dev_set_lockdep_one, NULL); +} static int team_init(struct net_device *dev) { @@ -1615,7 +1643,6 @@ static int team_init(struct net_device *dev) int err; team->dev = dev; - mutex_init(&team->lock); team_set_no_mode(team); team->pcpu_stats = netdev_alloc_pcpu_stats(struct team_pcpu_stats); @@ -1642,7 +1669,7 @@ static int team_init(struct net_device *dev) goto err_options_register; netif_carrier_off(dev); - netdev_lockdep_set_classes(dev); + team_dev_set_lockdep_class(dev); return 0; @@ -1673,6 +1700,11 @@ static void team_uninit(struct net_device *dev) team_queue_override_fini(team); mutex_unlock(&team->lock); netdev_change_features(dev); + + lockdep_unregister_key(&team->team_lock_key); + lockdep_unregister_key(&team->addr_lock_key); + lockdep_unregister_key(&team->xmit_lock_key); + } static void team_destructor(struct net_device *dev) @@ -1967,6 +1999,23 @@ static int team_add_slave(struct net_device *dev, struct net_device *port_dev, return err; } +static void team_update_lock_key(struct net_device *dev) +{ + struct team *team = netdev_priv(dev); + + lockdep_unregister_key(&team->team_lock_key); + lockdep_unregister_key(&team->addr_lock_key); + lockdep_unregister_key(&team->xmit_lock_key); + + lockdep_register_key(&team->team_lock_key); + lockdep_register_key(&team->addr_lock_key); + lockdep_register_key(&team->xmit_lock_key); + + lockdep_set_class(&team->lock, &team->team_lock_key); + lockdep_set_class(&dev->addr_list_lock, &team->addr_lock_key); + netdev_for_each_tx_queue(dev, team_dev_set_lockdep_one, NULL); +} + static int team_del_slave(struct net_device *dev, struct net_device *port_dev) { struct team *team = netdev_priv(dev); @@ -1976,8 +2025,12 @@ static int team_del_slave(struct net_device *dev, struct net_device *port_dev) err = team_port_del(team, port_dev); mutex_unlock(&team->lock); - if (!err) - netdev_change_features(dev); + if (err) + return err; + + if (netif_is_team_master(port_dev)) + team_update_lock_key(port_dev); + netdev_change_features(dev); return err; } diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 06faa066496f..9c97bb19ed34 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -223,6 +223,11 @@ struct team { atomic_t count_pending; struct delayed_work dw; } mcast_rejoin; + + struct lock_class_key team_lock_key; + struct lock_class_key xmit_lock_key; + struct lock_class_key addr_lock_key; + long mode_priv[TEAM_MODE_PRIV_LONGS]; }; -- 2.17.1