Commit 19aeeb9f authored by David S. Miller's avatar David S. Miller

Merge branch 'dsa-setup-stage'

Vivien Didelot says:

====================
net: dsa: setup stage

When probing a DSA switch, there is basically two stages.

The first stage is the parsing of the switch device, from either device
tree or platform data. It fetches the DSA tree to which it belongs, and
validates its ports. The switch device is then added to the tree, and
the second stage is called if this was the last switch of the tree.

The second stage is the setup of the tree, which validates that the tree
is complete, sets up the routing tables, the default CPU port for user
ports, sets up the switch drivers and finally the master interfaces,
which makes the whole switch fabric functional.

This patch series covers the second setup stage. The setup and teardown
of a switch tree have been separated into logical steps, and the probing
of a switch now simply parses and adds a switch to a tree.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents b2d0f5d5 b4fbb347
...@@ -122,7 +122,7 @@ struct dsa_switch_tree { ...@@ -122,7 +122,7 @@ struct dsa_switch_tree {
struct kref refcount; struct kref refcount;
/* Has this tree been applied to the hardware? */ /* Has this tree been applied to the hardware? */
bool applied; bool setup;
/* /*
* Configuration data for the platform device that owns * Configuration data for the platform device that owns
...@@ -190,7 +190,7 @@ struct dsa_port { ...@@ -190,7 +190,7 @@ struct dsa_port {
struct dsa_switch *ds; struct dsa_switch *ds;
unsigned int index; unsigned int index;
const char *name; const char *name;
struct dsa_port *cpu_dp; const struct dsa_port *cpu_dp;
struct device_node *dn; struct device_node *dn;
unsigned int ageing_time; unsigned int ageing_time;
u8 stp_state; u8 stp_state;
......
...@@ -94,14 +94,6 @@ static void dsa_tree_put(struct dsa_switch_tree *dst) ...@@ -94,14 +94,6 @@ static void dsa_tree_put(struct dsa_switch_tree *dst)
kref_put(&dst->refcount, dsa_tree_release); kref_put(&dst->refcount, dsa_tree_release);
} }
/* For platform data configurations, we need to have a valid name argument to
* differentiate a disabled port from an enabled one
*/
static bool dsa_port_is_valid(struct dsa_port *port)
{
return port->type != DSA_PORT_TYPE_UNUSED;
}
static bool dsa_port_is_dsa(struct dsa_port *port) static bool dsa_port_is_dsa(struct dsa_port *port)
{ {
return port->type == DSA_PORT_TYPE_DSA; return port->type == DSA_PORT_TYPE_DSA;
...@@ -112,197 +104,214 @@ static bool dsa_port_is_cpu(struct dsa_port *port) ...@@ -112,197 +104,214 @@ static bool dsa_port_is_cpu(struct dsa_port *port)
return port->type == DSA_PORT_TYPE_CPU; return port->type == DSA_PORT_TYPE_CPU;
} }
static bool dsa_ds_find_port_dn(struct dsa_switch *ds, static bool dsa_port_is_user(struct dsa_port *dp)
struct device_node *port)
{ {
u32 index; return dp->type == DSA_PORT_TYPE_USER;
for (index = 0; index < ds->num_ports; index++)
if (ds->ports[index].dn == port)
return true;
return false;
} }
static struct dsa_switch *dsa_dst_find_port_dn(struct dsa_switch_tree *dst, static struct dsa_port *dsa_tree_find_port_by_node(struct dsa_switch_tree *dst,
struct device_node *port) struct device_node *dn)
{ {
struct dsa_switch *ds; struct dsa_switch *ds;
u32 index; struct dsa_port *dp;
int device, port;
for (index = 0; index < DSA_MAX_SWITCHES; index++) { for (device = 0; device < DSA_MAX_SWITCHES; device++) {
ds = dst->ds[index]; ds = dst->ds[device];
if (!ds) if (!ds)
continue; continue;
if (dsa_ds_find_port_dn(ds, port)) for (port = 0; port < ds->num_ports; port++) {
return ds; dp = &ds->ports[port];
if (dp->dn == dn)
return dp;
}
} }
return NULL; return NULL;
} }
static int dsa_port_complete(struct dsa_switch_tree *dst, static bool dsa_port_setup_routing_table(struct dsa_port *dp)
struct dsa_switch *src_ds,
struct dsa_port *port,
u32 src_port)
{ {
struct device_node *link; struct dsa_switch *ds = dp->ds;
int index; struct dsa_switch_tree *dst = ds->dst;
struct dsa_switch *dst_ds; struct device_node *dn = dp->dn;
struct of_phandle_iterator it;
for (index = 0;; index++) { struct dsa_port *link_dp;
link = of_parse_phandle(port->dn, "link", index); int err;
if (!link)
break;
dst_ds = dsa_dst_find_port_dn(dst, link);
of_node_put(link);
if (!dst_ds) of_for_each_phandle(&it, err, dn, "link", NULL, 0) {
return 1; link_dp = dsa_tree_find_port_by_node(dst, it.node);
if (!link_dp) {
of_node_put(it.node);
return false;
}
src_ds->rtable[dst_ds->index] = src_port; ds->rtable[link_dp->ds->index] = dp->index;
} }
return 0; return true;
} }
/* A switch is complete if all the DSA ports phandles point to ports static bool dsa_switch_setup_routing_table(struct dsa_switch *ds)
* known in the tree. A return value of 1 means the tree is not
* complete. This is not an error condition. A value of 0 is
* success.
*/
static int dsa_ds_complete(struct dsa_switch_tree *dst, struct dsa_switch *ds)
{ {
struct dsa_port *port; bool complete = true;
u32 index; struct dsa_port *dp;
int err; int i;
for (index = 0; index < ds->num_ports; index++) { for (i = 0; i < DSA_MAX_SWITCHES; i++)
port = &ds->ports[index]; ds->rtable[i] = DSA_RTABLE_NONE;
if (!dsa_port_is_valid(port))
continue;
if (!dsa_port_is_dsa(port)) for (i = 0; i < ds->num_ports; i++) {
continue; dp = &ds->ports[i];
err = dsa_port_complete(dst, ds, port, index); if (dsa_port_is_dsa(dp)) {
if (err != 0) complete = dsa_port_setup_routing_table(dp);
return err; if (!complete)
break;
}
} }
return 0; return complete;
} }
/* A tree is complete if all the DSA ports phandles point to ports static bool dsa_tree_setup_routing_table(struct dsa_switch_tree *dst)
* known in the tree. A return value of 1 means the tree is not
* complete. This is not an error condition. A value of 0 is
* success.
*/
static int dsa_dst_complete(struct dsa_switch_tree *dst)
{ {
struct dsa_switch *ds; struct dsa_switch *ds;
u32 index; bool complete = true;
int err; int device;
for (index = 0; index < DSA_MAX_SWITCHES; index++) { for (device = 0; device < DSA_MAX_SWITCHES; device++) {
ds = dst->ds[index]; ds = dst->ds[device];
if (!ds) if (!ds)
continue; continue;
err = dsa_ds_complete(dst, ds); complete = dsa_switch_setup_routing_table(ds);
if (err != 0) if (!complete)
return err; break;
} }
return 0; return complete;
} }
static int dsa_dsa_port_apply(struct dsa_port *port) static struct dsa_port *dsa_tree_find_first_cpu(struct dsa_switch_tree *dst)
{ {
struct dsa_switch *ds = port->ds; struct dsa_switch *ds;
int err; struct dsa_port *dp;
int device, port;
err = dsa_port_fixed_link_register_of(port); for (device = 0; device < DSA_MAX_SWITCHES; device++) {
if (err) { ds = dst->ds[device];
dev_warn(ds->dev, "Failed to setup dsa port %d: %d\n", if (!ds)
port->index, err); continue;
return err;
}
memset(&port->devlink_port, 0, sizeof(port->devlink_port)); for (port = 0; port < ds->num_ports; port++) {
dp = &ds->ports[port];
return devlink_port_register(ds->devlink, &port->devlink_port, if (dsa_port_is_cpu(dp))
port->index); return dp;
} }
}
static void dsa_dsa_port_unapply(struct dsa_port *port) return NULL;
{
devlink_port_unregister(&port->devlink_port);
dsa_port_fixed_link_unregister_of(port);
} }
static int dsa_cpu_port_apply(struct dsa_port *port) static int dsa_tree_setup_default_cpu(struct dsa_switch_tree *dst)
{ {
struct dsa_switch *ds = port->ds; struct dsa_switch *ds;
int err; struct dsa_port *dp;
int device, port;
err = dsa_port_fixed_link_register_of(port); /* DSA currently only supports a single CPU port */
if (err) { dst->cpu_dp = dsa_tree_find_first_cpu(dst);
dev_warn(ds->dev, "Failed to setup cpu port %d: %d\n", if (!dst->cpu_dp) {
port->index, err); pr_warn("Tree has no master device\n");
return err; return -EINVAL;
} }
memset(&port->devlink_port, 0, sizeof(port->devlink_port)); /* Assign the default CPU port to all ports of the fabric */
err = devlink_port_register(ds->devlink, &port->devlink_port, for (device = 0; device < DSA_MAX_SWITCHES; device++) {
port->index); ds = dst->ds[device];
return err; if (!ds)
continue;
for (port = 0; port < ds->num_ports; port++) {
dp = &ds->ports[port];
if (dsa_port_is_user(dp))
dp->cpu_dp = dst->cpu_dp;
}
}
return 0;
} }
static void dsa_cpu_port_unapply(struct dsa_port *port) static void dsa_tree_teardown_default_cpu(struct dsa_switch_tree *dst)
{ {
devlink_port_unregister(&port->devlink_port); /* DSA currently only supports a single CPU port */
dsa_port_fixed_link_unregister_of(port); dst->cpu_dp = NULL;
} }
static int dsa_user_port_apply(struct dsa_port *port) static int dsa_port_setup(struct dsa_port *dp)
{ {
struct dsa_switch *ds = port->ds; struct dsa_switch *ds = dp->ds;
int err; int err;
err = dsa_slave_create(port); memset(&dp->devlink_port, 0, sizeof(dp->devlink_port));
err = devlink_port_register(ds->devlink, &dp->devlink_port, dp->index);
if (err)
return err;
switch (dp->type) {
case DSA_PORT_TYPE_UNUSED:
break;
case DSA_PORT_TYPE_CPU:
case DSA_PORT_TYPE_DSA:
err = dsa_port_fixed_link_register_of(dp);
if (err) { if (err) {
dev_warn(ds->dev, "Failed to create slave %d: %d\n", dev_err(ds->dev, "failed to register fixed link for port %d.%d\n",
port->index, err); ds->index, dp->index);
port->slave = NULL;
return err; return err;
} }
memset(&port->devlink_port, 0, sizeof(port->devlink_port)); break;
err = devlink_port_register(ds->devlink, &port->devlink_port, case DSA_PORT_TYPE_USER:
port->index); err = dsa_slave_create(dp);
if (err) if (err)
return err; dev_err(ds->dev, "failed to create slave for port %d.%d\n",
ds->index, dp->index);
devlink_port_type_eth_set(&port->devlink_port, port->slave); else
devlink_port_type_eth_set(&dp->devlink_port, dp->slave);
break;
}
return 0; return 0;
} }
static void dsa_user_port_unapply(struct dsa_port *port) static void dsa_port_teardown(struct dsa_port *dp)
{ {
devlink_port_unregister(&port->devlink_port); devlink_port_unregister(&dp->devlink_port);
if (port->slave) {
dsa_slave_destroy(port->slave); switch (dp->type) {
port->slave = NULL; case DSA_PORT_TYPE_UNUSED:
break;
case DSA_PORT_TYPE_CPU:
case DSA_PORT_TYPE_DSA:
dsa_port_fixed_link_unregister_of(dp);
break;
case DSA_PORT_TYPE_USER:
if (dp->slave) {
dsa_slave_destroy(dp->slave);
dp->slave = NULL;
}
break;
} }
} }
static int dsa_ds_apply(struct dsa_switch_tree *dst, struct dsa_switch *ds) static int dsa_switch_setup(struct dsa_switch *ds)
{ {
struct dsa_port *port;
u32 index;
int err; int err;
/* Initialize ds->phys_mii_mask before registering the slave MDIO bus /* Initialize ds->phys_mii_mask before registering the slave MDIO bus
...@@ -343,136 +352,145 @@ static int dsa_ds_apply(struct dsa_switch_tree *dst, struct dsa_switch *ds) ...@@ -343,136 +352,145 @@ static int dsa_ds_apply(struct dsa_switch_tree *dst, struct dsa_switch *ds)
return err; return err;
} }
for (index = 0; index < ds->num_ports; index++) { return 0;
port = &ds->ports[index]; }
if (!dsa_port_is_valid(port))
static void dsa_switch_teardown(struct dsa_switch *ds)
{
if (ds->slave_mii_bus && ds->ops->phy_read)
mdiobus_unregister(ds->slave_mii_bus);
dsa_switch_unregister_notifier(ds);
if (ds->devlink) {
devlink_unregister(ds->devlink);
devlink_free(ds->devlink);
ds->devlink = NULL;
}
}
static int dsa_tree_setup_switches(struct dsa_switch_tree *dst)
{
struct dsa_switch *ds;
struct dsa_port *dp;
int device, port;
int err;
for (device = 0; device < DSA_MAX_SWITCHES; device++) {
ds = dst->ds[device];
if (!ds)
continue; continue;
if (dsa_port_is_dsa(port)) { err = dsa_switch_setup(ds);
err = dsa_dsa_port_apply(port);
if (err) if (err)
return err; return err;
continue;
}
if (dsa_port_is_cpu(port)) { for (port = 0; port < ds->num_ports; port++) {
err = dsa_cpu_port_apply(port); dp = &ds->ports[port];
err = dsa_port_setup(dp);
if (err) if (err)
return err; return err;
continue;
} }
err = dsa_user_port_apply(port);
if (err)
continue;
} }
return 0; return 0;
} }
static void dsa_ds_unapply(struct dsa_switch_tree *dst, struct dsa_switch *ds) static void dsa_tree_teardown_switches(struct dsa_switch_tree *dst)
{ {
struct dsa_port *port; struct dsa_switch *ds;
u32 index; struct dsa_port *dp;
int device, port;
for (index = 0; index < ds->num_ports; index++) { for (device = 0; device < DSA_MAX_SWITCHES; device++) {
port = &ds->ports[index]; ds = dst->ds[device];
if (!dsa_port_is_valid(port)) if (!ds)
continue; continue;
if (dsa_port_is_dsa(port)) { for (port = 0; port < ds->num_ports; port++) {
dsa_dsa_port_unapply(port); dp = &ds->ports[port];
continue;
}
if (dsa_port_is_cpu(port)) { dsa_port_teardown(dp);
dsa_cpu_port_unapply(port);
continue;
} }
dsa_user_port_unapply(port); dsa_switch_teardown(ds);
} }
}
if (ds->slave_mii_bus && ds->ops->phy_read) static int dsa_tree_setup_master(struct dsa_switch_tree *dst)
mdiobus_unregister(ds->slave_mii_bus); {
struct dsa_port *cpu_dp = dst->cpu_dp;
struct net_device *master = cpu_dp->master;
dsa_switch_unregister_notifier(ds); /* DSA currently supports a single pair of CPU port and master device */
return dsa_master_setup(master, cpu_dp);
}
if (ds->devlink) { static void dsa_tree_teardown_master(struct dsa_switch_tree *dst)
devlink_unregister(ds->devlink); {
devlink_free(ds->devlink); struct dsa_port *cpu_dp = dst->cpu_dp;
ds->devlink = NULL; struct net_device *master = cpu_dp->master;
}
return dsa_master_teardown(master);
} }
static int dsa_dst_apply(struct dsa_switch_tree *dst) static int dsa_tree_setup(struct dsa_switch_tree *dst)
{ {
struct dsa_switch *ds; bool complete;
u32 index;
int err; int err;
for (index = 0; index < DSA_MAX_SWITCHES; index++) { if (dst->setup) {
ds = dst->ds[index]; pr_err("DSA: tree %d already setup! Disjoint trees?\n",
if (!ds) dst->index);
continue; return -EEXIST;
}
err = dsa_ds_apply(dst, ds); complete = dsa_tree_setup_routing_table(dst);
if (!complete)
return 0;
err = dsa_tree_setup_default_cpu(dst);
if (err) if (err)
return err; return err;
}
/* If we use a tagging format that doesn't have an ethertype err = dsa_tree_setup_switches(dst);
* field, make sure that all packets from this point on get if (err)
* sent to the tag format's receive function. return err;
*/
wmb();
dst->cpu_dp->master->dsa_ptr = dst->cpu_dp;
err = dsa_master_ethtool_setup(dst->cpu_dp->master); err = dsa_tree_setup_master(dst);
if (err) if (err)
return err; return err;
dst->applied = true; dst->setup = true;
pr_info("DSA: tree %d setup\n", dst->index);
return 0; return 0;
} }
static void dsa_dst_unapply(struct dsa_switch_tree *dst) static void dsa_tree_teardown(struct dsa_switch_tree *dst)
{ {
struct dsa_switch *ds; if (!dst->setup)
u32 index;
if (!dst->applied)
return; return;
dsa_master_ethtool_restore(dst->cpu_dp->master); dsa_tree_teardown_master(dst);
dst->cpu_dp->master->dsa_ptr = NULL; dsa_tree_teardown_switches(dst);
/* If we used a tagging format that doesn't have an ethertype dsa_tree_teardown_default_cpu(dst);
* field, make sure that all packets from this point get sent
* without the tag and go through the regular receive path.
*/
wmb();
for (index = 0; index < DSA_MAX_SWITCHES; index++) { pr_info("DSA: tree %d torn down\n", dst->index);
ds = dst->ds[index];
if (!ds)
continue;
dsa_ds_unapply(dst, ds); dst->setup = false;
}
dst->cpu_dp = NULL;
pr_info("DSA: tree %d unapplied\n", dst->index);
dst->applied = false;
} }
static void dsa_tree_remove_switch(struct dsa_switch_tree *dst, static void dsa_tree_remove_switch(struct dsa_switch_tree *dst,
unsigned int index) unsigned int index)
{ {
dsa_tree_teardown(dst);
dst->ds[index] = NULL; dst->ds[index] = NULL;
dsa_tree_put(dst); dsa_tree_put(dst);
} }
...@@ -481,6 +499,7 @@ static int dsa_tree_add_switch(struct dsa_switch_tree *dst, ...@@ -481,6 +499,7 @@ static int dsa_tree_add_switch(struct dsa_switch_tree *dst,
struct dsa_switch *ds) struct dsa_switch *ds)
{ {
unsigned int index = ds->index; unsigned int index = ds->index;
int err;
if (dst->ds[index]) if (dst->ds[index])
return -EBUSY; return -EBUSY;
...@@ -488,7 +507,11 @@ static int dsa_tree_add_switch(struct dsa_switch_tree *dst, ...@@ -488,7 +507,11 @@ static int dsa_tree_add_switch(struct dsa_switch_tree *dst,
dsa_tree_get(dst); dsa_tree_get(dst);
dst->ds[index] = ds; dst->ds[index] = ds;
return 0; err = dsa_tree_setup(dst);
if (err)
dsa_tree_remove_switch(dst, index);
return err;
} }
static int dsa_port_parse_user(struct dsa_port *dp, const char *name) static int dsa_port_parse_user(struct dsa_port *dp, const char *name)
...@@ -532,86 +555,6 @@ static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master) ...@@ -532,86 +555,6 @@ static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master)
return 0; return 0;
} }
static int dsa_cpu_parse(struct dsa_port *port, u32 index,
struct dsa_switch_tree *dst,
struct dsa_switch *ds)
{
if (!dst->cpu_dp)
dst->cpu_dp = port;
return 0;
}
static int dsa_ds_parse(struct dsa_switch_tree *dst, struct dsa_switch *ds)
{
struct dsa_port *port;
u32 index;
int err;
for (index = 0; index < ds->num_ports; index++) {
port = &ds->ports[index];
if (!dsa_port_is_valid(port) ||
dsa_port_is_dsa(port))
continue;
if (dsa_port_is_cpu(port)) {
err = dsa_cpu_parse(port, index, dst, ds);
if (err)
return err;
}
}
pr_info("DSA: switch %d %d parsed\n", dst->index, ds->index);
return 0;
}
static int dsa_dst_parse(struct dsa_switch_tree *dst)
{
struct dsa_switch *ds;
struct dsa_port *dp;
u32 index;
int port;
int err;
for (index = 0; index < DSA_MAX_SWITCHES; index++) {
ds = dst->ds[index];
if (!ds)
continue;
err = dsa_ds_parse(dst, ds);
if (err)
return err;
}
if (!dst->cpu_dp) {
pr_warn("Tree has no master device\n");
return -EINVAL;
}
/* Assign the default CPU port to all ports of the fabric */
for (index = 0; index < DSA_MAX_SWITCHES; index++) {
ds = dst->ds[index];
if (!ds)
continue;
for (port = 0; port < ds->num_ports; port++) {
dp = &ds->ports[port];
if (!dsa_port_is_valid(dp) ||
dsa_port_is_dsa(dp) ||
dsa_port_is_cpu(dp))
continue;
dp->cpu_dp = dst->cpu_dp;
}
}
pr_info("DSA: tree %d parsed\n", dst->index);
return 0;
}
static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn)
{ {
struct device_node *ethernet = of_parse_phandle(dn, "ethernet", 0); struct device_node *ethernet = of_parse_phandle(dn, "ethernet", 0);
...@@ -768,13 +711,18 @@ static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd) ...@@ -768,13 +711,18 @@ static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd)
return dsa_switch_parse_ports(ds, cd); return dsa_switch_parse_ports(ds, cd);
} }
static int _dsa_register_switch(struct dsa_switch *ds) static int dsa_switch_add(struct dsa_switch *ds)
{
struct dsa_switch_tree *dst = ds->dst;
return dsa_tree_add_switch(dst, ds);
}
static int dsa_switch_probe(struct dsa_switch *ds)
{ {
struct dsa_chip_data *pdata = ds->dev->platform_data; struct dsa_chip_data *pdata = ds->dev->platform_data;
struct device_node *np = ds->dev->of_node; struct device_node *np = ds->dev->of_node;
struct dsa_switch_tree *dst; int err;
unsigned int index;
int i, err;
if (np) if (np)
err = dsa_switch_parse_of(ds, np); err = dsa_switch_parse_of(ds, np);
...@@ -786,46 +734,7 @@ static int _dsa_register_switch(struct dsa_switch *ds) ...@@ -786,46 +734,7 @@ static int _dsa_register_switch(struct dsa_switch *ds)
if (err) if (err)
return err; return err;
index = ds->index; return dsa_switch_add(ds);
dst = ds->dst;
/* Initialize the routing table */
for (i = 0; i < DSA_MAX_SWITCHES; ++i)
ds->rtable[i] = DSA_RTABLE_NONE;
err = dsa_tree_add_switch(dst, ds);
if (err)
return err;
err = dsa_dst_complete(dst);
if (err < 0)
goto out_del_dst;
/* Not all switches registered yet */
if (err == 1)
return 0;
if (dst->applied) {
pr_info("DSA: Disjoint trees?\n");
return -EINVAL;
}
err = dsa_dst_parse(dst);
if (err)
goto out_del_dst;
err = dsa_dst_apply(dst);
if (err) {
dsa_dst_unapply(dst);
goto out_del_dst;
}
return 0;
out_del_dst:
dsa_tree_remove_switch(dst, index);
return err;
} }
struct dsa_switch *dsa_switch_alloc(struct device *dev, size_t n) struct dsa_switch *dsa_switch_alloc(struct device *dev, size_t n)
...@@ -855,27 +764,25 @@ int dsa_register_switch(struct dsa_switch *ds) ...@@ -855,27 +764,25 @@ int dsa_register_switch(struct dsa_switch *ds)
int err; int err;
mutex_lock(&dsa2_mutex); mutex_lock(&dsa2_mutex);
err = _dsa_register_switch(ds); err = dsa_switch_probe(ds);
mutex_unlock(&dsa2_mutex); mutex_unlock(&dsa2_mutex);
return err; return err;
} }
EXPORT_SYMBOL_GPL(dsa_register_switch); EXPORT_SYMBOL_GPL(dsa_register_switch);
static void _dsa_unregister_switch(struct dsa_switch *ds) static void dsa_switch_remove(struct dsa_switch *ds)
{ {
struct dsa_switch_tree *dst = ds->dst; struct dsa_switch_tree *dst = ds->dst;
unsigned int index = ds->index; unsigned int index = ds->index;
dsa_dst_unapply(dst);
dsa_tree_remove_switch(dst, index); dsa_tree_remove_switch(dst, index);
} }
void dsa_unregister_switch(struct dsa_switch *ds) void dsa_unregister_switch(struct dsa_switch *ds)
{ {
mutex_lock(&dsa2_mutex); mutex_lock(&dsa2_mutex);
_dsa_unregister_switch(ds); dsa_switch_remove(ds);
mutex_unlock(&dsa2_mutex); mutex_unlock(&dsa2_mutex);
} }
EXPORT_SYMBOL_GPL(dsa_unregister_switch); EXPORT_SYMBOL_GPL(dsa_unregister_switch);
...@@ -108,8 +108,8 @@ int dsa_legacy_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], ...@@ -108,8 +108,8 @@ int dsa_legacy_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
const unsigned char *addr, u16 vid); const unsigned char *addr, u16 vid);
/* master.c */ /* master.c */
int dsa_master_ethtool_setup(struct net_device *dev); int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp);
void dsa_master_ethtool_restore(struct net_device *dev); void dsa_master_teardown(struct net_device *dev);
static inline struct net_device *dsa_master_find_slave(struct net_device *dev, static inline struct net_device *dsa_master_find_slave(struct net_device *dev,
int device, int port) int device, int port)
......
...@@ -593,15 +593,7 @@ static int dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev, ...@@ -593,15 +593,7 @@ static int dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev,
if (!configured) if (!configured)
return -EPROBE_DEFER; return -EPROBE_DEFER;
/* return dsa_master_setup(dst->cpu_dp->master, dst->cpu_dp);
* If we use a tagging format that doesn't have an ethertype
* field, make sure that all packets from this point on get
* sent to the tag format's receive function.
*/
wmb();
dev->dsa_ptr = dst->cpu_dp;
return dsa_master_ethtool_setup(dst->cpu_dp->master);
} }
static int dsa_probe(struct platform_device *pdev) static int dsa_probe(struct platform_device *pdev)
...@@ -666,15 +658,7 @@ static void dsa_remove_dst(struct dsa_switch_tree *dst) ...@@ -666,15 +658,7 @@ static void dsa_remove_dst(struct dsa_switch_tree *dst)
{ {
int i; int i;
dsa_master_ethtool_restore(dst->cpu_dp->master); dsa_master_teardown(dst->cpu_dp->master);
dst->cpu_dp->master->dsa_ptr = NULL;
/* If we used a tagging format that doesn't have an ethertype
* field, make sure that all packets from this point get sent
* without the tag and go through the regular receive path.
*/
wmb();
for (i = 0; i < dst->pd->nr_chips; i++) { for (i = 0; i < dst->pd->nr_chips; i++) {
struct dsa_switch *ds = dst->ds[i]; struct dsa_switch *ds = dst->ds[i];
......
...@@ -85,7 +85,7 @@ static void dsa_master_get_strings(struct net_device *dev, uint32_t stringset, ...@@ -85,7 +85,7 @@ static void dsa_master_get_strings(struct net_device *dev, uint32_t stringset,
} }
} }
int dsa_master_ethtool_setup(struct net_device *dev) static int dsa_master_ethtool_setup(struct net_device *dev)
{ {
struct dsa_port *cpu_dp = dev->dsa_ptr; struct dsa_port *cpu_dp = dev->dsa_ptr;
struct dsa_switch *ds = cpu_dp->ds; struct dsa_switch *ds = cpu_dp->ds;
...@@ -108,10 +108,36 @@ int dsa_master_ethtool_setup(struct net_device *dev) ...@@ -108,10 +108,36 @@ int dsa_master_ethtool_setup(struct net_device *dev)
return 0; return 0;
} }
void dsa_master_ethtool_restore(struct net_device *dev) static void dsa_master_ethtool_teardown(struct net_device *dev)
{ {
struct dsa_port *cpu_dp = dev->dsa_ptr; struct dsa_port *cpu_dp = dev->dsa_ptr;
dev->ethtool_ops = cpu_dp->orig_ethtool_ops; dev->ethtool_ops = cpu_dp->orig_ethtool_ops;
cpu_dp->orig_ethtool_ops = NULL; cpu_dp->orig_ethtool_ops = NULL;
} }
int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp)
{
/* If we use a tagging format that doesn't have an ethertype
* field, make sure that all packets from this point on get
* sent to the tag format's receive function.
*/
wmb();
dev->dsa_ptr = cpu_dp;
return dsa_master_ethtool_setup(dev);
}
void dsa_master_teardown(struct net_device *dev)
{
dsa_master_ethtool_teardown(dev);
dev->dsa_ptr = NULL;
/* If we used a tagging format that doesn't have an ethertype
* field, make sure that all packets from this point get sent
* without the tag and go through the regular receive path.
*/
wmb();
}
...@@ -1147,7 +1147,7 @@ static void dsa_slave_notify(struct net_device *dev, unsigned long val) ...@@ -1147,7 +1147,7 @@ static void dsa_slave_notify(struct net_device *dev, unsigned long val)
int dsa_slave_create(struct dsa_port *port) int dsa_slave_create(struct dsa_port *port)
{ {
struct dsa_port *cpu_dp = port->cpu_dp; const struct dsa_port *cpu_dp = port->cpu_dp;
struct net_device *master = cpu_dp->master; struct net_device *master = cpu_dp->master;
struct dsa_switch *ds = port->ds; struct dsa_switch *ds = port->ds;
const char *name = port->name; const char *name = port->name;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment