Allow for multiple CPU ports in a DSA switch tree. By default assign the CPU ports to user ports in a round robin way, ie. if there are two CPU ports connected to eth0 and eth1, and five user ports (lan1..lan5), assign them as: lan1 <-> eth0 lan2 <-> eth1 lan3 <-> eth0 lan4 <-> eth1 lan5 <-> eth0
Signed-off-by: Marek Behún <marek.be...@nic.cz> --- include/net/dsa.h | 5 +-- net/dsa/dsa2.c | 84 +++++++++++++++++++++++++++++++---------------- 2 files changed, 58 insertions(+), 31 deletions(-) diff --git a/include/net/dsa.h b/include/net/dsa.h index 147b757ef8ea..64bd70608f2f 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -123,9 +123,10 @@ struct dsa_switch_tree { struct dsa_platform_data *pd; /* - * The switch port to which the CPU is attached. + * The switch ports to which the CPU is attached. */ - struct dsa_port *cpu_dp; + size_t num_cpu_dps; + struct dsa_port *cpu_dps[DSA_MAX_PORTS]; /* * Data for the individual switch chips. diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 8c4eccb0cfe6..c5af89079a6b 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -194,11 +194,12 @@ static bool dsa_tree_setup_routing_table(struct dsa_switch_tree *dst) return complete; } -static struct dsa_port *dsa_tree_find_first_cpu(struct dsa_switch_tree *dst) +static void dsa_tree_fill_cpu_ports(struct dsa_switch_tree *dst) { struct dsa_switch *ds; struct dsa_port *dp; int device, port; + int count = 0; for (device = 0; device < DSA_MAX_SWITCHES; device++) { ds = dst->ds[device]; @@ -208,28 +209,38 @@ static struct dsa_port *dsa_tree_find_first_cpu(struct dsa_switch_tree *dst) for (port = 0; port < ds->num_ports; port++) { dp = &ds->ports[port]; - if (dsa_port_is_cpu(dp)) - return dp; + if (dsa_port_is_cpu(dp)) { + if (count == ARRAY_SIZE(dst->cpu_dps)) { + pr_warn("Tree has too many CPU ports\n"); + dst->num_cpu_dps = count; + return; + } + + dst->cpu_dps[count++] = dp; + } } } - return NULL; + dst->num_cpu_dps = count; } -static int dsa_tree_setup_default_cpu(struct dsa_switch_tree *dst) +static int dsa_tree_setup_default_cpus(struct dsa_switch_tree *dst) { struct dsa_switch *ds; struct dsa_port *dp; - int device, port; + int device, port, i; - /* DSA currently only supports a single CPU port */ - dst->cpu_dp = dsa_tree_find_first_cpu(dst); - if (!dst->cpu_dp) { + dsa_tree_fill_cpu_ports(dst); + if (!dst->num_cpu_dps) { pr_warn("Tree has no master device\n"); return -EINVAL; } - /* Assign the default CPU port to all ports of the fabric */ + /* Assign the default CPU port to all ports of the fabric in a round + * robin way. This should work nicely for all sane switch tree designs. + */ + i = 0; + for (device = 0; device < DSA_MAX_SWITCHES; device++) { ds = dst->ds[device]; if (!ds) @@ -238,18 +249,20 @@ static int dsa_tree_setup_default_cpu(struct dsa_switch_tree *dst) for (port = 0; port < ds->num_ports; port++) { dp = &ds->ports[port]; - if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp)) - dp->cpu_dp = dst->cpu_dp; + if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp)) { + dp->cpu_dp = dst->cpu_dps[i++]; + if (i == dst->num_cpu_dps) + i = 0; + } } } return 0; } -static void dsa_tree_teardown_default_cpu(struct dsa_switch_tree *dst) +static void dsa_tree_teardown_default_cpus(struct dsa_switch_tree *dst) { - /* DSA currently only supports a single CPU port */ - dst->cpu_dp = NULL; + dst->num_cpu_dps = 0; } static int dsa_port_setup(struct dsa_port *dp) @@ -493,21 +506,34 @@ static void dsa_tree_teardown_switches(struct dsa_switch_tree *dst) } } -static int dsa_tree_setup_master(struct dsa_switch_tree *dst) +static int dsa_tree_setup_masters(struct dsa_switch_tree *dst) { - struct dsa_port *cpu_dp = dst->cpu_dp; - struct net_device *master = cpu_dp->master; + int i; + int err; - /* DSA currently supports a single pair of CPU port and master device */ - return dsa_master_setup(master, cpu_dp); + for (i = 0; i < dst->num_cpu_dps; ++i) { + struct dsa_port *cpu_dp = dst->cpu_dps[i]; + struct net_device *master = cpu_dp->master; + + err = dsa_master_setup(master, cpu_dp); + if (err) + goto teardown; + } + + return 0; +teardown: + for (--i; i >= 0; --i) + dsa_master_teardown(dst->cpu_dps[i]->master); + + return err; } -static void dsa_tree_teardown_master(struct dsa_switch_tree *dst) +static void dsa_tree_teardown_masters(struct dsa_switch_tree *dst) { - struct dsa_port *cpu_dp = dst->cpu_dp; - struct net_device *master = cpu_dp->master; + int i; - return dsa_master_teardown(master); + for (i = 0; i < dst->num_cpu_dps; ++i) + dsa_master_teardown(dst->cpu_dps[i]->master); } static int dsa_tree_setup(struct dsa_switch_tree *dst) @@ -525,7 +551,7 @@ static int dsa_tree_setup(struct dsa_switch_tree *dst) if (!complete) return 0; - err = dsa_tree_setup_default_cpu(dst); + err = dsa_tree_setup_default_cpus(dst); if (err) return err; @@ -533,7 +559,7 @@ static int dsa_tree_setup(struct dsa_switch_tree *dst) if (err) goto teardown_default_cpu; - err = dsa_tree_setup_master(dst); + err = dsa_tree_setup_masters(dst); if (err) goto teardown_switches; @@ -546,7 +572,7 @@ static int dsa_tree_setup(struct dsa_switch_tree *dst) teardown_switches: dsa_tree_teardown_switches(dst); teardown_default_cpu: - dsa_tree_teardown_default_cpu(dst); + dsa_tree_teardown_default_cpus(dst); return err; } @@ -556,11 +582,11 @@ static void dsa_tree_teardown(struct dsa_switch_tree *dst) if (!dst->setup) return; - dsa_tree_teardown_master(dst); + dsa_tree_teardown_masters(dst); dsa_tree_teardown_switches(dst); - dsa_tree_teardown_default_cpu(dst); + dsa_tree_teardown_default_cpus(dst); pr_info("DSA: tree %d torn down\n", dst->index); -- 2.21.0