• Taehee Yoo's avatar
    net: team: do not use dynamic lockdep key · 39285e12
    Taehee Yoo authored
    team interface has used a dynamic lockdep key to avoid false-positive
    lockdep deadlock detection. Virtual interfaces such as team usually
    have their own lock for protecting private data.
    These interfaces can be nested.
    team0
      |
    team1
    
    Each interface's lock is actually different(team0->lock and team1->lock).
    So,
    mutex_lock(&team0->lock);
    mutex_lock(&team1->lock);
    mutex_unlock(&team1->lock);
    mutex_unlock(&team0->lock);
    The above case is absolutely safe. But lockdep warns about deadlock.
    Because the lockdep understands these two locks are same. This is a
    false-positive lockdep warning.
    
    So, in order to avoid this problem, the team interfaces started to use
    dynamic lockdep key. The false-positive problem was fixed, but it
    introduced a new problem.
    
    When the new team virtual interface is created, it registers a dynamic
    lockdep key(creates dynamic lockdep key) and uses it. But there is the
    limitation of the number of lockdep keys.
    So, If so many team interfaces are created, it consumes all lockdep keys.
    Then, the lockdep stops to work and warns about it.
    
    In order to fix this problem, team interfaces use the subclass instead
    of the dynamic key. So, when a new team interface is created, it doesn't
    register(create) a new lockdep, but uses existed subclass key instead.
    It is already used by the bonding interface for a similar case.
    
    As the bonding interface does, the subclass variable is the same as
    the 'dev->nested_level'. This variable indicates the depth in the stacked
    interface graph.
    
    The 'dev->nested_level' is protected by RTNL and RCU.
    So, 'mutex_lock_nested()' for 'team->lock' requires RTNL or RCU.
    In the current code, 'team->lock' is usually acquired under RTNL, there is
    no problem with using 'dev->nested_level'.
    
    The 'team_nl_team_get()' and The 'lb_stats_refresh()' functions acquire
    'team->lock' without RTNL.
    But these don't iterate their own ports nested so they don't need nested
    lock.
    
    Reproducer:
       for i in {0..1000}
       do
               ip link add team$i type team
               ip link add dummy$i master team$i type dummy
               ip link set dummy$i up
               ip link set team$i up
       done
    
    Splat looks like:
       BUG: MAX_LOCKDEP_ENTRIES too low!
       turning off the locking correctness validator.
       Please attach the output of /proc/lock_stat to the bug report
       CPU: 0 PID: 4104 Comm: ip Not tainted 6.5.0-rc7+ #45
       Call Trace:
        <TASK>
       dump_stack_lvl+0x64/0xb0
       add_lock_to_list+0x30d/0x5e0
       check_prev_add+0x73a/0x23a0
       ...
       sock_def_readable+0xfe/0x4f0
       netlink_broadcast+0x76b/0xac0
       nlmsg_notify+0x69/0x1d0
       dev_open+0xed/0x130
       ...
    
    Reported-by: syzbot+9bbbacfbf1e04d5221f7@syzkaller.appspotmail.com
    Fixes: 369f61be ("team: fix nested locking lockdep warning")
    Signed-off-by: default avatarTaehee Yoo <ap420073@gmail.com>
    Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
    39285e12
team.c 73.4 KB