Some boards have two CPU interfaces connected to the switch, e.g. WiFi access points, with 1 port labeled WAN, 4 ports labeled lan1-lan4, and two port connected to the SoC.
This patch extends DSA to allows both CPU ports to be used. The "cpu" node in the DSA tree can now have a phandle to the host interface it connects to. Each user port can have a phandle to a cpu port which should be used for traffic between the port and the CPU. Thus simple load sharing over the two CPU ports can be achieved. Signed-off-by: John Crispin <j...@phrozen.org> --- include/net/dsa.h | 21 ++++++++++++++++++++- net/dsa/dsa2.c | 35 +++++++++++++++++++++++++++++++---- net/dsa/dsa_priv.h | 1 + net/dsa/slave.c | 26 ++++++++++++++++---------- 4 files changed, 68 insertions(+), 15 deletions(-) diff --git a/include/net/dsa.h b/include/net/dsa.h index c0e567c0c824..d2994bd2c507 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -186,6 +186,8 @@ struct dsa_port { u8 stp_state; struct net_device *bridge_dev; struct devlink_port devlink_port; + struct net_device *ethernet; + int upstream; }; struct dsa_switch { @@ -251,7 +253,7 @@ struct dsa_switch { static inline bool dsa_is_cpu_port(struct dsa_switch *ds, int p) { - return ds->dst->cpu_dp == &ds->ports[p]; + return !!(ds->cpu_port_mask & (1 << p)); } static inline bool dsa_is_dsa_port(struct dsa_switch *ds, int p) @@ -269,6 +271,11 @@ static inline bool dsa_is_port_initialized(struct dsa_switch *ds, int p) return ds->enabled_port_mask & (1 << p) && ds->ports[p].netdev; } +static inline bool dsa_is_upstream_port(struct dsa_switch *ds, int p) +{ + return dsa_is_cpu_port(ds, p) || dsa_is_dsa_port(ds, p); +} + static inline u8 dsa_upstream_port(struct dsa_switch *ds) { struct dsa_switch_tree *dst = ds->dst; @@ -285,6 +292,18 @@ static inline u8 dsa_upstream_port(struct dsa_switch *ds) return ds->rtable[dst->cpu_dp->ds->index]; } +static inline u8 dsa_port_upstream_port(struct dsa_switch *ds, int port) +{ + /* + * If this port has a specific upstream cpu port, use it, + * otherwise use the switch default. + */ + if (ds->ports[port].upstream) + return ds->ports[port].upstream; + else + return dsa_upstream_port(ds); +} + struct dsa_switch_ops { /* * Legacy probing. diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 4301f52e4f5a..8b13aa735c40 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -253,8 +253,6 @@ static int dsa_cpu_port_apply(struct dsa_port *port, u32 index, return err; } - ds->cpu_port_mask |= BIT(index); - memset(&ds->ports[index].devlink_port, 0, sizeof(ds->ports[index].devlink_port)); err = devlink_port_register(ds->devlink, &ds->ports[index].devlink_port, @@ -269,6 +267,10 @@ static void dsa_cpu_port_unapply(struct dsa_port *port, u32 index, dsa_cpu_dsa_destroy(port); ds->cpu_port_mask &= ~BIT(index); + if (ds->ports[index].ethernet) { + dev_put(ds->ports[index].ethernet); + ds->ports[index].ethernet = NULL; + } } static int dsa_user_port_apply(struct dsa_port *port, u32 index, @@ -530,6 +532,29 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, dst->rcv = dst->tag_ops->rcv; + dev_hold(ethernet_dev); + ds->ports[index].ethernet = ethernet_dev; + ds->cpu_port_mask |= BIT(index); + + return 0; +} + +static int dsa_user_parse(struct device_node *port, u32 index, + struct dsa_switch *ds) +{ + struct device_node *cpu_port; + const unsigned int *cpu_port_reg; + int cpu_port_index; + + cpu_port = of_parse_phandle(port, "cpu", 0); + if (cpu_port) { + cpu_port_reg = of_get_property(cpu_port, "reg", NULL); + if (!cpu_port_reg) + return -EINVAL; + cpu_port_index = be32_to_cpup(cpu_port_reg); + ds->ports[index].upstream = cpu_port_index; + } + return 0; } @@ -544,11 +569,13 @@ static int dsa_ds_parse(struct dsa_switch_tree *dst, struct dsa_switch *ds) if (!dsa_port_is_valid(port)) continue; - if (dsa_port_is_cpu(port)) { + if (dsa_port_is_cpu(port)) err = dsa_cpu_parse(port, index, dst, ds); + else if (dsa_is_normal_port(port)) + err = dsa_user_parse(port->dn, index, ds); + if (err) return err; - } } pr_info("DSA: switch %d %d parsed\n", dst->tree, ds->index); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index c1d4180651af..91fdc16befb2 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -78,6 +78,7 @@ struct dsa_slave_priv { /* DSA port data, such as switch, port index, etc. */ struct dsa_port *dp; + struct net_device *master; /* * The phylib phy_device pointer for the PHY connected diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 887e26695519..45f17f35ced1 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -66,7 +66,7 @@ static int dsa_slave_get_iflink(const struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); - return p->dp->ds->dst->master_netdev->ifindex; + return p->master->ifindex; } static inline bool dsa_port_is_bridged(struct dsa_port *dp) @@ -77,7 +77,7 @@ static inline bool dsa_port_is_bridged(struct dsa_port *dp) static int dsa_slave_open(struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); - struct net_device *master = p->dp->ds->dst->master_netdev; + struct net_device *master = p->master; struct dsa_switch *ds = p->dp->ds; u8 stp_state = dsa_port_is_bridged(p->dp) ? BR_STATE_BLOCKING : BR_STATE_FORWARDING; @@ -132,7 +132,7 @@ static int dsa_slave_open(struct net_device *dev) static int dsa_slave_close(struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); - struct net_device *master = p->dp->ds->dst->master_netdev; + struct net_device *master = p->master; struct dsa_switch *ds = p->dp->ds; if (p->phy) @@ -159,7 +159,7 @@ static int dsa_slave_close(struct net_device *dev) static void dsa_slave_change_rx_flags(struct net_device *dev, int change) { struct dsa_slave_priv *p = netdev_priv(dev); - struct net_device *master = p->dp->ds->dst->master_netdev; + struct net_device *master = p->master; if (change & IFF_ALLMULTI) dev_set_allmulti(master, dev->flags & IFF_ALLMULTI ? 1 : -1); @@ -170,7 +170,7 @@ static void dsa_slave_change_rx_flags(struct net_device *dev, int change) static void dsa_slave_set_rx_mode(struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); - struct net_device *master = p->dp->ds->dst->master_netdev; + struct net_device *master = p->master; dev_mc_sync(master, dev); dev_uc_sync(master, dev); @@ -179,7 +179,7 @@ static void dsa_slave_set_rx_mode(struct net_device *dev) static int dsa_slave_set_mac_address(struct net_device *dev, void *a) { struct dsa_slave_priv *p = netdev_priv(dev); - struct net_device *master = p->dp->ds->dst->master_netdev; + struct net_device *master = p->master; struct sockaddr *addr = a; int err; @@ -376,7 +376,7 @@ static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev) /* Queue the SKB for transmission on the parent interface, but * do not modify its EtherType */ - nskb->dev = p->dp->ds->dst->master_netdev; + nskb->dev = p->master; dev_queue_xmit(nskb); return NETDEV_TX_OK; @@ -685,7 +685,7 @@ static int dsa_slave_netpoll_setup(struct net_device *dev, { struct dsa_slave_priv *p = netdev_priv(dev); struct dsa_switch *ds = p->dp->ds; - struct net_device *master = ds->dst->master_netdev; + struct net_device *master = p->master; struct netpoll *netpoll; int err = 0; @@ -1140,11 +1140,16 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent, struct net_device *master; struct net_device *slave_dev; struct dsa_slave_priv *p; + int port_cpu = ds->ports[port].upstream; int ret; - master = ds->dst->master_netdev; - if (ds->master_netdev) + if (port_cpu && ds->ports[port_cpu].ethernet) + master = ds->ports[port_cpu].ethernet; + else if (ds->master_netdev) master = ds->master_netdev; + else + master = ds->dst->master_netdev; + master->dsa_ptr = (void *)ds->dst; slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv), name, NET_NAME_UNKNOWN, ether_setup); @@ -1173,6 +1178,7 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent, p->dp = &ds->ports[port]; INIT_LIST_HEAD(&p->mall_tc_list); p->xmit = dst->tag_ops->xmit; + p->master = master; p->old_pause = -1; p->old_link = -1; -- 2.11.0