Commit d7d3c05135f37d8fdf73f9966d27155cada36e56

Authored by Jiri Pirko
Committed by David S. Miller
1 parent 72b603ee8c

team: set IFF_TEAM_PORT priv_flag after rx_handler is registered

When one tries to add eth as a port into team and that eth is already in
use by other rx_handler device (macvlan, bond, bridge, ...) a bug in
team_port_add() causes that IFF_TEAM_PORT flag is set before rx_handler
is registered. In between, netdev nofifier is called and
team_device_event() sees IFF_TEAM_PORT and thinks that rx_handler_data
pointer is set to team_port. But it isn't.

Fix this by reordering rx_handler register and IFF_TEAM_PORT priv flag
set so it is very similar to how bonding does this.

Reported-by: Erik Hugne <erik.hugne@ericsson.com>
Fixes: 3d249d4ca7 "net: introduce ethernet teaming device"
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 1 changed file with 30 additions and 14 deletions Side-by-side Diff

drivers/net/team/team.c
... ... @@ -1003,7 +1003,6 @@
1003 1003 int err = 0;
1004 1004  
1005 1005 dev_hold(team->dev);
1006   - port->dev->priv_flags |= IFF_TEAM_PORT;
1007 1006 if (team->ops.port_enter) {
1008 1007 err = team->ops.port_enter(team, port);
1009 1008 if (err) {
... ... @@ -1016,7 +1015,6 @@
1016 1015 return 0;
1017 1016  
1018 1017 err_port_enter:
1019   - port->dev->priv_flags &= ~IFF_TEAM_PORT;
1020 1018 dev_put(team->dev);
1021 1019  
1022 1020 return err;
... ... @@ -1026,7 +1024,6 @@
1026 1024 {
1027 1025 if (team->ops.port_leave)
1028 1026 team->ops.port_leave(team, port);
1029   - port->dev->priv_flags &= ~IFF_TEAM_PORT;
1030 1027 dev_put(team->dev);
1031 1028 }
1032 1029  
... ... @@ -1075,6 +1072,25 @@
1075 1072 }
1076 1073 #endif
1077 1074  
  1075 +static int team_upper_dev_link(struct net_device *dev,
  1076 + struct net_device *port_dev)
  1077 +{
  1078 + int err;
  1079 +
  1080 + err = netdev_master_upper_dev_link(port_dev, dev);
  1081 + if (err)
  1082 + return err;
  1083 + port_dev->priv_flags |= IFF_TEAM_PORT;
  1084 + return 0;
  1085 +}
  1086 +
  1087 +static void team_upper_dev_unlink(struct net_device *dev,
  1088 + struct net_device *port_dev)
  1089 +{
  1090 + netdev_upper_dev_unlink(port_dev, dev);
  1091 + port_dev->priv_flags &= ~IFF_TEAM_PORT;
  1092 +}
  1093 +
1078 1094 static void __team_port_change_port_added(struct team_port *port, bool linkup);
1079 1095 static int team_dev_type_check_change(struct net_device *dev,
1080 1096 struct net_device *port_dev);
... ... @@ -1161,13 +1177,6 @@
1161 1177 goto err_enable_netpoll;
1162 1178 }
1163 1179  
1164   - err = netdev_master_upper_dev_link(port_dev, dev);
1165   - if (err) {
1166   - netdev_err(dev, "Device %s failed to set upper link\n",
1167   - portname);
1168   - goto err_set_upper_link;
1169   - }
1170   -
1171 1180 err = netdev_rx_handler_register(port_dev, team_handle_frame,
1172 1181 port);
1173 1182 if (err) {
... ... @@ -1176,6 +1185,13 @@
1176 1185 goto err_handler_register;
1177 1186 }
1178 1187  
  1188 + err = team_upper_dev_link(dev, port_dev);
  1189 + if (err) {
  1190 + netdev_err(dev, "Device %s failed to set upper link\n",
  1191 + portname);
  1192 + goto err_set_upper_link;
  1193 + }
  1194 +
1179 1195 err = __team_option_inst_add_port(team, port);
1180 1196 if (err) {
1181 1197 netdev_err(dev, "Device %s failed to add per-port options\n",
1182 1198  
... ... @@ -1195,12 +1211,12 @@
1195 1211 return 0;
1196 1212  
1197 1213 err_option_port_add:
  1214 + team_upper_dev_unlink(dev, port_dev);
  1215 +
  1216 +err_set_upper_link:
1198 1217 netdev_rx_handler_unregister(port_dev);
1199 1218  
1200 1219 err_handler_register:
1201   - netdev_upper_dev_unlink(port_dev, dev);
1202   -
1203   -err_set_upper_link:
1204 1220 team_port_disable_netpoll(port);
1205 1221  
1206 1222 err_enable_netpoll:
1207 1223  
... ... @@ -1239,8 +1255,8 @@
1239 1255  
1240 1256 team_port_disable(team, port);
1241 1257 list_del_rcu(&port->list);
  1258 + team_upper_dev_unlink(dev, port_dev);
1242 1259 netdev_rx_handler_unregister(port_dev);
1243   - netdev_upper_dev_unlink(port_dev, dev);
1244 1260 team_port_disable_netpoll(port);
1245 1261 vlan_vids_del_by_dev(port_dev, dev);
1246 1262 dev_uc_unsync(port_dev, dev);