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

net: ocelot: call ocelot_netdevice_bridge_join when joining a bridged LAG

Similar to the DSA situation, ocelot supports LAG offload but treats
this scenario improperly:

ip link add br0 type bridge
ip link add bond0 type bond
ip link set bond0 master br0
ip link set swp0 master bond0

We do the same thing as we do there, which is to simulate a 'bridge join'
on 'lag join', if we detect that the bonding upper has a bridge upper.

Again, same as DSA, ocelot supports software fallback for LAG, and in
that case, we should avoid calling ocelot_netdevice_changeupper.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Vladimir Oltean and committed by
David S. Miller
81ef35e7 010e269f

+86 -25
+86 -25
drivers/net/ethernet/mscc/ocelot_net.c
··· 1117 1117 return ret; 1118 1118 } 1119 1119 1120 - static int ocelot_netdevice_bridge_join(struct ocelot *ocelot, int port, 1121 - struct net_device *bridge) 1120 + static int ocelot_netdevice_bridge_join(struct net_device *dev, 1121 + struct net_device *bridge, 1122 + struct netlink_ext_ack *extack) 1122 1123 { 1124 + struct ocelot_port_private *priv = netdev_priv(dev); 1125 + struct ocelot_port *ocelot_port = &priv->port; 1126 + struct ocelot *ocelot = ocelot_port->ocelot; 1123 1127 struct switchdev_brport_flags flags; 1128 + int port = priv->chip_port; 1124 1129 int err; 1125 1130 1126 1131 flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; ··· 1140 1135 return 0; 1141 1136 } 1142 1137 1143 - static int ocelot_netdevice_bridge_leave(struct ocelot *ocelot, int port, 1138 + static int ocelot_netdevice_bridge_leave(struct net_device *dev, 1144 1139 struct net_device *bridge) 1145 1140 { 1141 + struct ocelot_port_private *priv = netdev_priv(dev); 1142 + struct ocelot_port *ocelot_port = &priv->port; 1143 + struct ocelot *ocelot = ocelot_port->ocelot; 1146 1144 struct switchdev_brport_flags flags; 1145 + int port = priv->chip_port; 1147 1146 int err; 1148 1147 1149 1148 flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; ··· 1160 1151 return err; 1161 1152 } 1162 1153 1163 - static int ocelot_netdevice_changeupper(struct net_device *dev, 1164 - struct netdev_notifier_changeupper_info *info) 1154 + static int ocelot_netdevice_lag_join(struct net_device *dev, 1155 + struct net_device *bond, 1156 + struct netdev_lag_upper_info *info, 1157 + struct netlink_ext_ack *extack) 1165 1158 { 1166 1159 struct ocelot_port_private *priv = netdev_priv(dev); 1167 1160 struct ocelot_port *ocelot_port = &priv->port; 1168 1161 struct ocelot *ocelot = ocelot_port->ocelot; 1162 + struct net_device *bridge_dev; 1169 1163 int port = priv->chip_port; 1164 + int err; 1165 + 1166 + err = ocelot_port_lag_join(ocelot, port, bond, info); 1167 + if (err == -EOPNOTSUPP) { 1168 + NL_SET_ERR_MSG_MOD(extack, "Offloading not supported"); 1169 + return 0; 1170 + } 1171 + 1172 + bridge_dev = netdev_master_upper_dev_get(bond); 1173 + if (!bridge_dev || !netif_is_bridge_master(bridge_dev)) 1174 + return 0; 1175 + 1176 + err = ocelot_netdevice_bridge_join(dev, bridge_dev, extack); 1177 + if (err) 1178 + goto err_bridge_join; 1179 + 1180 + return 0; 1181 + 1182 + err_bridge_join: 1183 + ocelot_port_lag_leave(ocelot, port, bond); 1184 + return err; 1185 + } 1186 + 1187 + static int ocelot_netdevice_lag_leave(struct net_device *dev, 1188 + struct net_device *bond) 1189 + { 1190 + struct ocelot_port_private *priv = netdev_priv(dev); 1191 + struct ocelot_port *ocelot_port = &priv->port; 1192 + struct ocelot *ocelot = ocelot_port->ocelot; 1193 + struct net_device *bridge_dev; 1194 + int port = priv->chip_port; 1195 + 1196 + ocelot_port_lag_leave(ocelot, port, bond); 1197 + 1198 + bridge_dev = netdev_master_upper_dev_get(bond); 1199 + if (!bridge_dev || !netif_is_bridge_master(bridge_dev)) 1200 + return 0; 1201 + 1202 + return ocelot_netdevice_bridge_leave(dev, bridge_dev); 1203 + } 1204 + 1205 + static int ocelot_netdevice_changeupper(struct net_device *dev, 1206 + struct netdev_notifier_changeupper_info *info) 1207 + { 1208 + struct netlink_ext_ack *extack; 1170 1209 int err = 0; 1171 1210 1211 + extack = netdev_notifier_info_to_extack(&info->info); 1212 + 1172 1213 if (netif_is_bridge_master(info->upper_dev)) { 1173 - if (info->linking) { 1174 - err = ocelot_netdevice_bridge_join(ocelot, port, 1175 - info->upper_dev); 1176 - } else { 1177 - err = ocelot_netdevice_bridge_leave(ocelot, port, 1178 - info->upper_dev); 1179 - } 1214 + if (info->linking) 1215 + err = ocelot_netdevice_bridge_join(dev, info->upper_dev, 1216 + extack); 1217 + else 1218 + err = ocelot_netdevice_bridge_leave(dev, info->upper_dev); 1180 1219 } 1181 1220 if (netif_is_lag_master(info->upper_dev)) { 1182 - if (info->linking) { 1183 - err = ocelot_port_lag_join(ocelot, port, 1184 - info->upper_dev, 1185 - info->upper_info); 1186 - if (err == -EOPNOTSUPP) { 1187 - NL_SET_ERR_MSG_MOD(info->info.extack, 1188 - "Offloading not supported"); 1189 - err = 0; 1190 - } 1191 - } else { 1192 - ocelot_port_lag_leave(ocelot, port, 1193 - info->upper_dev); 1194 - } 1221 + if (info->linking) 1222 + err = ocelot_netdevice_lag_join(dev, info->upper_dev, 1223 + info->upper_info, extack); 1224 + else 1225 + ocelot_netdevice_lag_leave(dev, info->upper_dev); 1195 1226 } 1196 1227 1197 1228 return notifier_from_errno(err); 1198 1229 } 1199 1230 1231 + /* Treat CHANGEUPPER events on an offloaded LAG as individual CHANGEUPPER 1232 + * events for the lower physical ports of the LAG. 1233 + * If the LAG upper isn't offloaded, ignore its CHANGEUPPER events. 1234 + * In case the LAG joined a bridge, notify that we are offloading it and can do 1235 + * forwarding in hardware towards it. 1236 + */ 1200 1237 static int 1201 1238 ocelot_netdevice_lag_changeupper(struct net_device *dev, 1202 1239 struct netdev_notifier_changeupper_info *info) ··· 1252 1197 int err = NOTIFY_DONE; 1253 1198 1254 1199 netdev_for_each_lower_dev(dev, lower, iter) { 1200 + struct ocelot_port_private *priv = netdev_priv(lower); 1201 + struct ocelot_port *ocelot_port = &priv->port; 1202 + 1203 + if (ocelot_port->bond != dev) 1204 + return NOTIFY_OK; 1205 + 1255 1206 err = ocelot_netdevice_changeupper(lower, info); 1256 1207 if (err) 1257 1208 return notifier_from_errno(err);