net: dsa: setup master before ports
It is said that as soon as a network interface is registered, all its resources should have already been prepared, so that it is available for sending and receiving traffic. One of the resources needed by a DSA slave interface is the master. dsa_tree_setup -> dsa_tree_setup_ports -> dsa_port_setup -> dsa_slave_create -> register_netdevice -> dsa_tree_setup_master -> dsa_master_setup -> sets up master->dsa_ptr, which enables reception Therefore, there is a short period of time after register_netdevice() during which the master isn't prepared to pass traffic to the DSA layer (master->dsa_ptr is checked by eth_type_trans). Same thing during unregistration, there is a time frame in which packets might be missed. Note that this change opens us to another race: dsa_master_find_slave() will get invoked potentially earlier than the slave creation, and later than the slave deletion. Since dp->slave starts off as a NULL pointer, the earlier calls aren't a problem, but the later calls are. To avoid use-after-free, we should zeroize dp->slave before calling dsa_slave_destroy(). In practice I cannot really test real life improvements brought by this change, since in my systems, netdevice creation races with PHY autoneg which takes a few seconds to complete, and that masks quite a few races. Effects might be noticeable in a setup with fixed links all the way to an external system. Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
1e3f407f3c
Коммит
11fd667dac
|
@ -561,6 +561,7 @@ static void dsa_port_teardown(struct dsa_port *dp)
|
|||
struct devlink_port *dlp = &dp->devlink_port;
|
||||
struct dsa_switch *ds = dp->ds;
|
||||
struct dsa_mac_addr *a, *tmp;
|
||||
struct net_device *slave;
|
||||
|
||||
if (!dp->setup)
|
||||
return;
|
||||
|
@ -582,9 +583,11 @@ static void dsa_port_teardown(struct dsa_port *dp)
|
|||
dsa_port_link_unregister_of(dp);
|
||||
break;
|
||||
case DSA_PORT_TYPE_USER:
|
||||
if (dp->slave) {
|
||||
dsa_slave_destroy(dp->slave);
|
||||
slave = dp->slave;
|
||||
|
||||
if (slave) {
|
||||
dp->slave = NULL;
|
||||
dsa_slave_destroy(slave);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1134,17 +1137,17 @@ static int dsa_tree_setup(struct dsa_switch_tree *dst)
|
|||
if (err)
|
||||
goto teardown_cpu_ports;
|
||||
|
||||
err = dsa_tree_setup_ports(dst);
|
||||
err = dsa_tree_setup_master(dst);
|
||||
if (err)
|
||||
goto teardown_switches;
|
||||
|
||||
err = dsa_tree_setup_master(dst);
|
||||
err = dsa_tree_setup_ports(dst);
|
||||
if (err)
|
||||
goto teardown_ports;
|
||||
goto teardown_master;
|
||||
|
||||
err = dsa_tree_setup_lags(dst);
|
||||
if (err)
|
||||
goto teardown_master;
|
||||
goto teardown_ports;
|
||||
|
||||
dst->setup = true;
|
||||
|
||||
|
@ -1152,10 +1155,10 @@ static int dsa_tree_setup(struct dsa_switch_tree *dst)
|
|||
|
||||
return 0;
|
||||
|
||||
teardown_master:
|
||||
dsa_tree_teardown_master(dst);
|
||||
teardown_ports:
|
||||
dsa_tree_teardown_ports(dst);
|
||||
teardown_master:
|
||||
dsa_tree_teardown_master(dst);
|
||||
teardown_switches:
|
||||
dsa_tree_teardown_switches(dst);
|
||||
teardown_cpu_ports:
|
||||
|
@ -1173,10 +1176,10 @@ static void dsa_tree_teardown(struct dsa_switch_tree *dst)
|
|||
|
||||
dsa_tree_teardown_lags(dst);
|
||||
|
||||
dsa_tree_teardown_master(dst);
|
||||
|
||||
dsa_tree_teardown_ports(dst);
|
||||
|
||||
dsa_tree_teardown_master(dst);
|
||||
|
||||
dsa_tree_teardown_switches(dst);
|
||||
|
||||
dsa_tree_teardown_cpu_ports(dst);
|
||||
|
|
Загрузка…
Ссылка в новой задаче