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

Merge branch 'offload-software-learnt-bridge-addresses-to-dsa'

Vladimir Oltean says:

====================
Offload software learnt bridge addresses to DSA

This series tries to make DSA behave a bit more sanely when bridged with
"foreign" (non-DSA) interfaces and source address learning is not
supported on the hardware CPU port (which would make things work more
seamlessly without software intervention). When a station A connected to
a DSA switch port needs to talk to another station B connected to a
non-DSA port through the Linux bridge, DSA must explicitly add a route
for station B towards its CPU port.

Initial RFC was posted here:
https://patchwork.ozlabs.org/project/netdev/cover/20201108131953.2462644-1-olteanv@gmail.com/

v2 was posted here:
https://patchwork.kernel.org/project/netdevbpf/cover/20201213024018.772586-1-vladimir.oltean@nxp.com/

v3 was posted here:
https://patchwork.kernel.org/project/netdevbpf/cover/20201213140710.1198050-1-vladimir.oltean@nxp.com/

This is a resend of the previous v3 with some added Reviewed-by tags.
====================

Link: https://lore.kernel.org/r/20210106095136.224739-1-olteanv@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+131 -64
+1
drivers/net/dsa/ocelot/felix.c
··· 629 629 630 630 ds->mtu_enforcement_ingress = true; 631 631 ds->configure_vlan_while_not_filtering = true; 632 + ds->assisted_learning_on_cpu_port = true; 632 633 633 634 return 0; 634 635 }
+5
include/net/dsa.h
··· 319 319 */ 320 320 bool untag_bridge_pvid; 321 321 322 + /* Let DSA manage the FDB entries towards the CPU, based on the 323 + * software bridge database. 324 + */ 325 + bool assisted_learning_on_cpu_port; 326 + 322 327 /* In case vlan_filtering_is_global is set, the VLAN awareness state 323 328 * should be retrieved from here and not from the per-port settings. 324 329 */
+1
net/bridge/br_fdb.c
··· 602 602 /* fastpath: update of existing entry */ 603 603 if (unlikely(source != fdb->dst && 604 604 !test_bit(BR_FDB_STICKY, &fdb->flags))) { 605 + br_switchdev_fdb_notify(fdb, RTM_DELNEIGH); 605 606 fdb->dst = source; 606 607 fdb_modified = true; 607 608 /* Take over HW learned entry */
+12
net/dsa/dsa_priv.h
··· 73 73 int mtu; 74 74 }; 75 75 76 + struct dsa_switchdev_event_work { 77 + struct dsa_switch *ds; 78 + int port; 79 + struct work_struct work; 80 + unsigned long event; 81 + /* Specific for SWITCHDEV_FDB_ADD_TO_DEVICE and 82 + * SWITCHDEV_FDB_DEL_TO_DEVICE 83 + */ 84 + unsigned char addr[ETH_ALEN]; 85 + u16 vid; 86 + }; 87 + 76 88 struct dsa_slave_priv { 77 89 /* Copy of CPU port xmit for faster access in slave transmit hot path */ 78 90 struct sk_buff * (*xmit)(struct sk_buff *skb,
+112 -64
net/dsa/slave.c
··· 2047 2047 return NOTIFY_DONE; 2048 2048 } 2049 2049 2050 - struct dsa_switchdev_event_work { 2051 - struct work_struct work; 2052 - struct switchdev_notifier_fdb_info fdb_info; 2053 - struct net_device *dev; 2054 - unsigned long event; 2055 - }; 2050 + static void 2051 + dsa_fdb_offload_notify(struct dsa_switchdev_event_work *switchdev_work) 2052 + { 2053 + struct dsa_switch *ds = switchdev_work->ds; 2054 + struct switchdev_notifier_fdb_info info; 2055 + struct dsa_port *dp; 2056 + 2057 + if (!dsa_is_user_port(ds, switchdev_work->port)) 2058 + return; 2059 + 2060 + info.addr = switchdev_work->addr; 2061 + info.vid = switchdev_work->vid; 2062 + info.offloaded = true; 2063 + dp = dsa_to_port(ds, switchdev_work->port); 2064 + call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, 2065 + dp->slave, &info.info, NULL); 2066 + } 2056 2067 2057 2068 static void dsa_slave_switchdev_event_work(struct work_struct *work) 2058 2069 { 2059 2070 struct dsa_switchdev_event_work *switchdev_work = 2060 2071 container_of(work, struct dsa_switchdev_event_work, work); 2061 - struct net_device *dev = switchdev_work->dev; 2062 - struct switchdev_notifier_fdb_info *fdb_info; 2063 - struct dsa_port *dp = dsa_slave_to_port(dev); 2072 + struct dsa_switch *ds = switchdev_work->ds; 2073 + struct dsa_port *dp; 2064 2074 int err; 2075 + 2076 + dp = dsa_to_port(ds, switchdev_work->port); 2065 2077 2066 2078 rtnl_lock(); 2067 2079 switch (switchdev_work->event) { 2068 2080 case SWITCHDEV_FDB_ADD_TO_DEVICE: 2069 - fdb_info = &switchdev_work->fdb_info; 2070 - if (!fdb_info->added_by_user) 2071 - break; 2072 - 2073 - err = dsa_port_fdb_add(dp, fdb_info->addr, fdb_info->vid); 2081 + err = dsa_port_fdb_add(dp, switchdev_work->addr, 2082 + switchdev_work->vid); 2074 2083 if (err) { 2075 - netdev_dbg(dev, "fdb add failed err=%d\n", err); 2084 + dev_err(ds->dev, 2085 + "port %d failed to add %pM vid %d to fdb: %d\n", 2086 + dp->index, switchdev_work->addr, 2087 + switchdev_work->vid, err); 2076 2088 break; 2077 2089 } 2078 - fdb_info->offloaded = true; 2079 - call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev, 2080 - &fdb_info->info, NULL); 2090 + dsa_fdb_offload_notify(switchdev_work); 2081 2091 break; 2082 2092 2083 2093 case SWITCHDEV_FDB_DEL_TO_DEVICE: 2084 - fdb_info = &switchdev_work->fdb_info; 2085 - if (!fdb_info->added_by_user) 2086 - break; 2087 - 2088 - err = dsa_port_fdb_del(dp, fdb_info->addr, fdb_info->vid); 2094 + err = dsa_port_fdb_del(dp, switchdev_work->addr, 2095 + switchdev_work->vid); 2089 2096 if (err) { 2090 - netdev_dbg(dev, "fdb del failed err=%d\n", err); 2091 - dev_close(dev); 2097 + dev_err(ds->dev, 2098 + "port %d failed to delete %pM vid %d from fdb: %d\n", 2099 + dp->index, switchdev_work->addr, 2100 + switchdev_work->vid, err); 2092 2101 } 2102 + 2093 2103 break; 2094 2104 } 2095 2105 rtnl_unlock(); 2096 2106 2097 - kfree(switchdev_work->fdb_info.addr); 2098 2107 kfree(switchdev_work); 2099 - dev_put(dev); 2108 + if (dsa_is_user_port(ds, dp->index)) 2109 + dev_put(dp->slave); 2100 2110 } 2101 2111 2102 - static int 2103 - dsa_slave_switchdev_fdb_work_init(struct dsa_switchdev_event_work * 2104 - switchdev_work, 2105 - const struct switchdev_notifier_fdb_info * 2106 - fdb_info) 2112 + static int dsa_lower_dev_walk(struct net_device *lower_dev, 2113 + struct netdev_nested_priv *priv) 2107 2114 { 2108 - memcpy(&switchdev_work->fdb_info, fdb_info, 2109 - sizeof(switchdev_work->fdb_info)); 2110 - switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); 2111 - if (!switchdev_work->fdb_info.addr) 2112 - return -ENOMEM; 2113 - ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, 2114 - fdb_info->addr); 2115 + if (dsa_slave_dev_check(lower_dev)) { 2116 + priv->data = (void *)netdev_priv(lower_dev); 2117 + return 1; 2118 + } 2119 + 2115 2120 return 0; 2121 + } 2122 + 2123 + static struct dsa_slave_priv *dsa_slave_dev_lower_find(struct net_device *dev) 2124 + { 2125 + struct netdev_nested_priv priv = { 2126 + .data = NULL, 2127 + }; 2128 + 2129 + netdev_walk_all_lower_dev_rcu(dev, dsa_lower_dev_walk, &priv); 2130 + 2131 + return (struct dsa_slave_priv *)priv.data; 2116 2132 } 2117 2133 2118 2134 /* Called under rcu_read_lock() */ ··· 2136 2120 unsigned long event, void *ptr) 2137 2121 { 2138 2122 struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 2123 + const struct switchdev_notifier_fdb_info *fdb_info; 2139 2124 struct dsa_switchdev_event_work *switchdev_work; 2125 + struct dsa_port *dp; 2140 2126 int err; 2141 2127 2142 - if (event == SWITCHDEV_PORT_ATTR_SET) { 2128 + switch (event) { 2129 + case SWITCHDEV_PORT_ATTR_SET: 2143 2130 err = switchdev_handle_port_attr_set(dev, ptr, 2144 2131 dsa_slave_dev_check, 2145 2132 dsa_slave_port_attr_set); 2146 2133 return notifier_from_errno(err); 2147 - } 2148 - 2149 - if (!dsa_slave_dev_check(dev)) 2150 - return NOTIFY_DONE; 2151 - 2152 - switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); 2153 - if (!switchdev_work) 2154 - return NOTIFY_BAD; 2155 - 2156 - INIT_WORK(&switchdev_work->work, 2157 - dsa_slave_switchdev_event_work); 2158 - switchdev_work->dev = dev; 2159 - switchdev_work->event = event; 2160 - 2161 - switch (event) { 2162 2134 case SWITCHDEV_FDB_ADD_TO_DEVICE: 2163 2135 case SWITCHDEV_FDB_DEL_TO_DEVICE: 2164 - if (dsa_slave_switchdev_fdb_work_init(switchdev_work, ptr)) 2165 - goto err_fdb_work_init; 2166 - dev_hold(dev); 2136 + fdb_info = ptr; 2137 + 2138 + if (dsa_slave_dev_check(dev)) { 2139 + if (!fdb_info->added_by_user) 2140 + return NOTIFY_OK; 2141 + 2142 + dp = dsa_slave_to_port(dev); 2143 + } else { 2144 + /* Snoop addresses learnt on foreign interfaces 2145 + * bridged with us, for switches that don't 2146 + * automatically learn SA from CPU-injected traffic 2147 + */ 2148 + struct net_device *br_dev; 2149 + struct dsa_slave_priv *p; 2150 + 2151 + br_dev = netdev_master_upper_dev_get_rcu(dev); 2152 + if (!br_dev) 2153 + return NOTIFY_DONE; 2154 + 2155 + if (!netif_is_bridge_master(br_dev)) 2156 + return NOTIFY_DONE; 2157 + 2158 + p = dsa_slave_dev_lower_find(br_dev); 2159 + if (!p) 2160 + return NOTIFY_DONE; 2161 + 2162 + dp = p->dp->cpu_dp; 2163 + 2164 + if (!dp->ds->assisted_learning_on_cpu_port) 2165 + return NOTIFY_DONE; 2166 + } 2167 + 2168 + if (!dp->ds->ops->port_fdb_add || !dp->ds->ops->port_fdb_del) 2169 + return NOTIFY_DONE; 2170 + 2171 + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); 2172 + if (!switchdev_work) 2173 + return NOTIFY_BAD; 2174 + 2175 + INIT_WORK(&switchdev_work->work, 2176 + dsa_slave_switchdev_event_work); 2177 + switchdev_work->ds = dp->ds; 2178 + switchdev_work->port = dp->index; 2179 + switchdev_work->event = event; 2180 + 2181 + ether_addr_copy(switchdev_work->addr, 2182 + fdb_info->addr); 2183 + switchdev_work->vid = fdb_info->vid; 2184 + 2185 + /* Hold a reference on the slave for dsa_fdb_offload_notify */ 2186 + if (dsa_is_user_port(dp->ds, dp->index)) 2187 + dev_hold(dev); 2188 + dsa_schedule_work(&switchdev_work->work); 2167 2189 break; 2168 2190 default: 2169 - kfree(switchdev_work); 2170 2191 return NOTIFY_DONE; 2171 2192 } 2172 2193 2173 - dsa_schedule_work(&switchdev_work->work); 2174 2194 return NOTIFY_OK; 2175 - 2176 - err_fdb_work_init: 2177 - kfree(switchdev_work); 2178 - return NOTIFY_BAD; 2179 2195 } 2180 2196 2181 2197 static int dsa_slave_switchdev_blocking_event(struct notifier_block *unused,