• Vladimir Oltean's avatar
    net: switchdev: introduce a fanout helper for SWITCHDEV_FDB_{ADD,DEL}_TO_DEVICE · 8ca07176
    Vladimir Oltean authored
    Currently DSA has an issue with FDB entries pointing towards the bridge
    in the presence of br_fdb_replay() being called at port join and leave
    time.
    
    In particular, each bridge port will ask for a replay for the FDB
    entries pointing towards the bridge when it joins, and for another
    replay when it leaves.
    
    This means that for example, a bridge with 4 switch ports will notify
    DSA 4 times of the bridge MAC address.
    
    But if the MAC address of the bridge changes during the normal runtime
    of the system, the bridge notifies switchdev [ once ] of the deletion of
    the old MAC address as a local FDB towards the bridge, and of the
    insertion [ again once ] of the new MAC address as a local FDB.
    
    This is a problem, because DSA keeps the old MAC address as a host FDB
    entry with refcount 4 (4 ports asked for it using br_fdb_replay). So the
    old MAC address will not be deleted. Additionally, the new MAC address
    will only be installed with refcount 1, and when the first switch port
    leaves the bridge (leaving 3 others as still members), it will delete
    with it the new MAC address of the bridge from the local FDB entries
    kept by DSA (because the br_fdb_replay call on deletion will bring the
    entry's refcount from 1 to 0).
    
    So the problem, really, is that the number of br_fdb_replay() calls is
    not matched with the refcount that a host FDB is offloaded to DSA during
    normal runtime.
    
    An elegant way to solve the problem would be to make the switchdev
    notification emitted by br_fdb_change_mac_address() result in a host FDB
    kept by DSA which has a refcount exactly equal to the number of ports
    under that bridge. Then, no matter how many DSA ports join or leave that
    bridge, the host FDB entry will always be deleted when there are exactly
    zero remaining DSA switch ports members of the bridge.
    
    To implement the proposed solution, we remember that the switchdev
    objects and port attributes have some helpers provided by switchdev,
    which can be optionally called by drivers:
    switchdev_handle_port_obj_{add,del} and switchdev_handle_port_attr_set.
    These helpers:
    - fan out a switchdev object/attribute emitted for the bridge towards
      all the lower interfaces that pass the check_cb().
    - fan out a switchdev object/attribute emitted for a bridge port that is
      a LAG towards all the lower interfaces that pass the check_cb().
    
    In other words, this is the model we need for the FDB events too:
    something that will keep an FDB entry emitted towards a physical port as
    it is, but translate an FDB entry emitted towards the bridge into N FDB
    entries, one per physical port.
    
    Of course, there are many differences between fanning out a switchdev
    object (VLAN) on 3 lower interfaces of a LAG and fanning out an FDB
    entry on 3 lower interfaces of a LAG. Intuitively, an FDB entry towards
    a LAG should be treated specially, because FDB entries are unicast, we
    can't just install the same address towards 3 destinations. It is
    imaginable that drivers might want to treat this case specifically, so
    create some methods for this case and do not recurse into the LAG lower
    ports, just the bridge ports.
    
    DSA also listens for FDB entries on "foreign" interfaces, aka interfaces
    bridged with us which are not part of our hardware domain: think an
    Ethernet switch bridged with a Wi-Fi AP. For those addresses, DSA
    installs host FDB entries. However, there we have the same problem
    (those host FDB entries are installed with a refcount of only 1) and an
    even bigger one which we did not have with FDB entries towards the
    bridge:
    
    br_fdb_replay() is currently not called for FDB entries on foreign
    interfaces, just for the physical port and for the bridge itself.
    
    So when DSA sniffs an address learned by the software bridge towards a
    foreign interface like an e1000 port, and then that e1000 leaves the
    bridge, DSA remains with the dangling host FDB address. That will be
    fixed separately by replaying all FDB entries and not just the ones
    towards the port and the bridge.
    Signed-off-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
    Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
    8ca07176
switchdev.h 12.7 KB