b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | From 90dc8fd36078a536671adae884d0b929cce6480a Mon Sep 17 00:00:00 2001 |
| 2 | From: Vladimir Oltean <vladimir.oltean@nxp.com> |
| 3 | Date: Wed, 6 Jan 2021 11:51:30 +0200 |
| 4 | Subject: [PATCH] net: bridge: notify switchdev of disappearance of old FDB |
| 5 | entry upon migration |
| 6 | |
| 7 | Currently the bridge emits atomic switchdev notifications for |
| 8 | dynamically learnt FDB entries. Monitoring these notifications works |
| 9 | wonders for switchdev drivers that want to keep their hardware FDB in |
| 10 | sync with the bridge's FDB. |
| 11 | |
| 12 | For example station A wants to talk to station B in the diagram below, |
| 13 | and we are concerned with the behavior of the bridge on the DUT device: |
| 14 | |
| 15 | DUT |
| 16 | +-------------------------------------+ |
| 17 | | br0 | |
| 18 | | +------+ +------+ +------+ +------+ | |
| 19 | | | | | | | | | | | |
| 20 | | | swp0 | | swp1 | | swp2 | | eth0 | | |
| 21 | +-------------------------------------+ |
| 22 | | | | |
| 23 | Station A | | |
| 24 | | | |
| 25 | +--+------+--+ +--+------+--+ |
| 26 | | | | | | | | | |
| 27 | | | swp0 | | | | swp0 | | |
| 28 | Another | +------+ | | +------+ | Another |
| 29 | switch | br0 | | br0 | switch |
| 30 | | +------+ | | +------+ | |
| 31 | | | | | | | | | |
| 32 | | | swp1 | | | | swp1 | | |
| 33 | +--+------+--+ +--+------+--+ |
| 34 | | |
| 35 | Station B |
| 36 | |
| 37 | Interfaces swp0, swp1, swp2 are handled by a switchdev driver that has |
| 38 | the following property: frames injected from its control interface bypass |
| 39 | the internal address analyzer logic, and therefore, this hardware does |
| 40 | not learn from the source address of packets transmitted by the network |
| 41 | stack through it. So, since bridging between eth0 (where Station B is |
| 42 | attached) and swp0 (where Station A is attached) is done in software, |
| 43 | the switchdev hardware will never learn the source address of Station B. |
| 44 | So the traffic towards that destination will be treated as unknown, i.e. |
| 45 | flooded. |
| 46 | |
| 47 | This is where the bridge notifications come in handy. When br0 on the |
| 48 | DUT sees frames with Station B's MAC address on eth0, the switchdev |
| 49 | driver gets these notifications and can install a rule to send frames |
| 50 | towards Station B's address that are incoming from swp0, swp1, swp2, |
| 51 | only towards the control interface. This is all switchdev driver private |
| 52 | business, which the notification makes possible. |
| 53 | |
| 54 | All is fine until someone unplugs Station B's cable and moves it to the |
| 55 | other switch: |
| 56 | |
| 57 | DUT |
| 58 | +-------------------------------------+ |
| 59 | | br0 | |
| 60 | | +------+ +------+ +------+ +------+ | |
| 61 | | | | | | | | | | | |
| 62 | | | swp0 | | swp1 | | swp2 | | eth0 | | |
| 63 | +-------------------------------------+ |
| 64 | | | | |
| 65 | Station A | | |
| 66 | | | |
| 67 | +--+------+--+ +--+------+--+ |
| 68 | | | | | | | | | |
| 69 | | | swp0 | | | | swp0 | | |
| 70 | Another | +------+ | | +------+ | Another |
| 71 | switch | br0 | | br0 | switch |
| 72 | | +------+ | | +------+ | |
| 73 | | | | | | | | | |
| 74 | | | swp1 | | | | swp1 | | |
| 75 | +--+------+--+ +--+------+--+ |
| 76 | | |
| 77 | Station B |
| 78 | |
| 79 | Luckily for the use cases we care about, Station B is noisy enough that |
| 80 | the DUT hears it (on swp1 this time). swp1 receives the frames and |
| 81 | delivers them to the bridge, who enters the unlikely path in br_fdb_update |
| 82 | of updating an existing entry. It moves the entry in the software bridge |
| 83 | to swp1 and emits an addition notification towards that. |
| 84 | |
| 85 | As far as the switchdev driver is concerned, all that it needs to ensure |
| 86 | is that traffic between Station A and Station B is not forever broken. |
| 87 | If it does nothing, then the stale rule to send frames for Station B |
| 88 | towards the control interface remains in place. But Station B is no |
| 89 | longer reachable via the control interface, but via a port that can |
| 90 | offload the bridge port learning attribute. It's just that the port is |
| 91 | prevented from learning this address, since the rule overrides FDB |
| 92 | updates. So the rule needs to go. The question is via what mechanism. |
| 93 | |
| 94 | It sure would be possible for this switchdev driver to keep track of all |
| 95 | addresses which are sent to the control interface, and then also listen |
| 96 | for bridge notifier events on its own ports, searching for the ones that |
| 97 | have a MAC address which was previously sent to the control interface. |
| 98 | But this is cumbersome and inefficient. Instead, with one small change, |
| 99 | the bridge could notify of the address deletion from the old port, in a |
| 100 | symmetrical manner with how it did for the insertion. Then the switchdev |
| 101 | driver would not be required to monitor learn/forget events for its own |
| 102 | ports. It could just delete the rule towards the control interface upon |
| 103 | bridge entry migration. This would make hardware address learning be |
| 104 | possible again. Then it would take a few more packets until the hardware |
| 105 | and software FDB would be in sync again. |
| 106 | |
| 107 | Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> |
| 108 | Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> |
| 109 | Reviewed-by: Ido Schimmel <idosch@nvidia.com> |
| 110 | Reviewed-by: Andrew Lunn <andrew@lunn.ch> |
| 111 | Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> |
| 112 | Signed-off-by: Jakub Kicinski <kuba@kernel.org> |
| 113 | --- |
| 114 | net/bridge/br_fdb.c | 1 + |
| 115 | 1 file changed, 1 insertion(+) |
| 116 | |
| 117 | --- a/net/bridge/br_fdb.c |
| 118 | +++ b/net/bridge/br_fdb.c |
| 119 | @@ -581,6 +581,7 @@ void br_fdb_update(struct net_bridge *br |
| 120 | |
| 121 | /* fastpath: update of existing entry */ |
| 122 | if (unlikely(source != fdb->dst && !fdb->is_sticky)) { |
| 123 | + br_switchdev_fdb_notify(fdb, RTM_DELNEIGH); |
| 124 | fdb->dst = source; |
| 125 | fdb_modified = true; |
| 126 | /* Take over HW learned entry */ |