From 68bb8ea8ad0d497c28ed47423246b1ab20f26976 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 21 Oct 2019 16:51:15 -0400 Subject: [PATCH 01/16] net: dsa: use dsa_to_port helper everywhere Do not let the drivers access the ds->ports static array directly while there is a dsa_to_port helper for this purpose. At the same time, un-const this helper since the SJA1105 driver assigns the priv member of the returned dsa_port structure. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- drivers/net/dsa/b53/b53_common.c | 6 +++--- drivers/net/dsa/bcm_sf2.c | 8 ++++---- drivers/net/dsa/bcm_sf2_cfp.c | 6 +++--- drivers/net/dsa/mt7530.c | 12 ++++++------ drivers/net/dsa/mv88e6xxx/chip.c | 10 +++++----- drivers/net/dsa/qca8k.c | 2 +- drivers/net/dsa/sja1105/sja1105_main.c | 18 +++++++++--------- include/net/dsa.h | 2 +- net/dsa/dsa.c | 8 +++++--- net/dsa/dsa2.c | 4 ++-- net/dsa/switch.c | 4 ++-- net/dsa/tag_8021q.c | 6 +++--- 12 files changed, 44 insertions(+), 42 deletions(-) diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index cc3536315eff..aef9b56781ef 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -524,7 +524,7 @@ int b53_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) if (!dsa_is_user_port(ds, port)) return 0; - cpu_port = ds->ports[port].cpu_dp->index; + cpu_port = dsa_to_port(ds, port)->cpu_dp->index; if (dev->ops->irq_enable) ret = dev->ops->irq_enable(dev, port); @@ -1629,7 +1629,7 @@ EXPORT_SYMBOL(b53_fdb_dump); int b53_br_join(struct dsa_switch *ds, int port, struct net_device *br) { struct b53_device *dev = ds->priv; - s8 cpu_port = ds->ports[port].cpu_dp->index; + s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; u16 pvlan, reg; unsigned int i; @@ -1675,7 +1675,7 @@ void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *br) { struct b53_device *dev = ds->priv; struct b53_vlan *vl = &dev->vlans[0]; - s8 cpu_port = ds->ports[port].cpu_dp->index; + s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; unsigned int i; u16 pvlan, reg, pvid; diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 26509fa37a50..c068a3b7207b 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -662,7 +662,7 @@ static void bcm_sf2_sw_fixed_state(struct dsa_switch *ds, int port, * state machine and make it go in PHY_FORCING state instead. */ if (!status->link) - netif_carrier_off(ds->ports[port].slave); + netif_carrier_off(dsa_to_port(ds, port)->slave); status->duplex = DUPLEX_FULL; } else { status->link = true; @@ -728,7 +728,7 @@ static int bcm_sf2_sw_resume(struct dsa_switch *ds) static void bcm_sf2_sw_get_wol(struct dsa_switch *ds, int port, struct ethtool_wolinfo *wol) { - struct net_device *p = ds->ports[port].cpu_dp->master; + struct net_device *p = dsa_to_port(ds, port)->cpu_dp->master; struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); struct ethtool_wolinfo pwol = { }; @@ -752,9 +752,9 @@ static void bcm_sf2_sw_get_wol(struct dsa_switch *ds, int port, static int bcm_sf2_sw_set_wol(struct dsa_switch *ds, int port, struct ethtool_wolinfo *wol) { - struct net_device *p = ds->ports[port].cpu_dp->master; + struct net_device *p = dsa_to_port(ds, port)->cpu_dp->master; struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); - s8 cpu_port = ds->ports[port].cpu_dp->index; + s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; struct ethtool_wolinfo pwol = { }; if (p->ethtool_ops->get_wol) diff --git a/drivers/net/dsa/bcm_sf2_cfp.c b/drivers/net/dsa/bcm_sf2_cfp.c index d264776a95a3..f3f0c3f07391 100644 --- a/drivers/net/dsa/bcm_sf2_cfp.c +++ b/drivers/net/dsa/bcm_sf2_cfp.c @@ -821,7 +821,7 @@ static int bcm_sf2_cfp_rule_insert(struct dsa_switch *ds, int port, struct ethtool_rx_flow_spec *fs) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); - s8 cpu_port = ds->ports[port].cpu_dp->index; + s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; __u64 ring_cookie = fs->ring_cookie; unsigned int queue_num, port_num; int ret; @@ -1049,7 +1049,7 @@ static int bcm_sf2_cfp_rule_get_all(struct bcm_sf2_priv *priv, int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port, struct ethtool_rxnfc *nfc, u32 *rule_locs) { - struct net_device *p = ds->ports[port].cpu_dp->master; + struct net_device *p = dsa_to_port(ds, port)->cpu_dp->master; struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); int ret = 0; @@ -1092,7 +1092,7 @@ int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port, int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port, struct ethtool_rxnfc *nfc) { - struct net_device *p = ds->ports[port].cpu_dp->master; + struct net_device *p = dsa_to_port(ds, port)->cpu_dp->master; struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); int ret = 0; diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 1d8d36de4d20..a91293e47a57 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -862,7 +862,7 @@ mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port) for (i = 0; i < MT7530_NUM_PORTS; i++) { if (dsa_is_user_port(ds, i) && - dsa_port_is_vlan_filtering(&ds->ports[i])) { + dsa_port_is_vlan_filtering(dsa_to_port(ds, i))) { all_user_ports_removed = false; break; } @@ -922,7 +922,7 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port, * other port is still a VLAN-aware port. */ if (dsa_is_user_port(ds, i) && i != port && - !dsa_port_is_vlan_filtering(&ds->ports[i])) { + !dsa_port_is_vlan_filtering(dsa_to_port(ds, i))) { if (dsa_to_port(ds, i)->bridge_dev != bridge) continue; if (priv->ports[i].enable) @@ -1165,7 +1165,7 @@ mt7530_port_vlan_add(struct dsa_switch *ds, int port, /* The port is kept as VLAN-unaware if bridge with vlan_filtering not * being set. */ - if (!dsa_port_is_vlan_filtering(&ds->ports[port])) + if (!dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) return; mutex_lock(&priv->reg_mutex); @@ -1196,7 +1196,7 @@ mt7530_port_vlan_del(struct dsa_switch *ds, int port, /* The port is kept as VLAN-unaware if bridge with vlan_filtering not * being set. */ - if (!dsa_port_is_vlan_filtering(&ds->ports[port])) + if (!dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) return 0; mutex_lock(&priv->reg_mutex); @@ -1252,7 +1252,7 @@ mt7530_setup(struct dsa_switch *ds) * controller also is the container for two GMACs nodes representing * as two netdev instances. */ - dn = ds->ports[MT7530_CPU_PORT].master->dev.of_node->parent; + dn = dsa_to_port(ds, MT7530_CPU_PORT)->master->dev.of_node->parent; if (priv->id == ID_MT7530) { priv->ethernet = syscon_node_to_regmap(dn); @@ -1340,7 +1340,7 @@ mt7530_setup(struct dsa_switch *ds) if (!dsa_is_unused_port(ds, 5)) { priv->p5_intf_sel = P5_INTF_SEL_GMAC5; - interface = of_get_phy_mode(ds->ports[5].dn); + interface = of_get_phy_mode(dsa_to_port(ds, 5)->dn); } else { /* Scan the ethernet nodes. look for GMAC1, lookup used phy */ for_each_child_of_node(dn, mac_np) { diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 6787d560e9e3..d67deec77452 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1075,7 +1075,7 @@ static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port) if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) return mv88e6xxx_port_mask(chip); - br = ds->ports[port].bridge_dev; + br = dsa_to_port(ds, port)->bridge_dev; pvlan = 0; /* Frames from user ports can egress any local DSA links and CPU ports, @@ -1402,7 +1402,7 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i)) continue; - if (!ds->ports[i].slave) + if (!dsa_to_port(ds, i)->slave) continue; if (vlan.member[i] == @@ -1410,7 +1410,7 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, continue; if (dsa_to_port(ds, i)->bridge_dev == - ds->ports[port].bridge_dev) + dsa_to_port(ds, port)->bridge_dev) break; /* same bridge, check next VLAN */ if (!dsa_to_port(ds, i)->bridge_dev) @@ -2042,7 +2042,7 @@ static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip, /* Remap the Port VLAN of each local bridge group member */ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) { - if (chip->ds->ports[port].bridge_dev == br) { + if (dsa_to_port(chip->ds, port)->bridge_dev == br) { err = mv88e6xxx_port_vlan_map(chip, port); if (err) return err; @@ -2059,7 +2059,7 @@ static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip, break; for (port = 0; port < ds->num_ports; ++port) { - if (ds->ports[port].bridge_dev == br) { + if (dsa_to_port(ds, port)->bridge_dev == br) { err = mv88e6xxx_pvt_map(chip, dev, port); if (err) return err; diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index b00274caae4f..71e44c8763b8 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -661,7 +661,7 @@ qca8k_setup(struct dsa_switch *ds) return ret; /* Initialize CPU port pad mode (xMII type, delays...) */ - phy_mode = of_get_phy_mode(ds->ports[QCA8K_CPU_PORT].dn); + phy_mode = of_get_phy_mode(dsa_to_port(ds, QCA8K_CPU_PORT)->dn); if (phy_mode < 0) { pr_err("Can't find phy-mode for master device\n"); return phy_mode; diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 2ffe642cf54b..4b0cb779f187 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -1058,7 +1058,7 @@ int sja1105pqrs_fdb_add(struct dsa_switch *ds, int port, l2_lookup.vlanid = vid; l2_lookup.iotag = SJA1105_S_TAG; l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0); - if (dsa_port_is_vlan_filtering(&ds->ports[port])) { + if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) { l2_lookup.mask_vlanid = VLAN_VID_MASK; l2_lookup.mask_iotag = BIT(0); } else { @@ -1121,7 +1121,7 @@ int sja1105pqrs_fdb_del(struct dsa_switch *ds, int port, l2_lookup.vlanid = vid; l2_lookup.iotag = SJA1105_S_TAG; l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0); - if (dsa_port_is_vlan_filtering(&ds->ports[port])) { + if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) { l2_lookup.mask_vlanid = VLAN_VID_MASK; l2_lookup.mask_iotag = BIT(0); } else { @@ -1167,7 +1167,7 @@ static int sja1105_fdb_add(struct dsa_switch *ds, int port, * for what gets printed in 'bridge fdb show'. In the case of zero, * no VID gets printed at all. */ - if (!dsa_port_is_vlan_filtering(&ds->ports[port])) + if (!dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) vid = 0; return priv->info->fdb_add_cmd(ds, port, addr, vid); @@ -1178,7 +1178,7 @@ static int sja1105_fdb_del(struct dsa_switch *ds, int port, { struct sja1105_private *priv = ds->priv; - if (!dsa_port_is_vlan_filtering(&ds->ports[port])) + if (!dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) vid = 0; return priv->info->fdb_del_cmd(ds, port, addr, vid); @@ -1217,7 +1217,7 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port, u64_to_ether_addr(l2_lookup.macaddr, macaddr); /* We need to hide the dsa_8021q VLANs from the user. */ - if (!dsa_port_is_vlan_filtering(&ds->ports[port])) + if (!dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) l2_lookup.vlanid = 0; cb(macaddr, l2_lookup.vlanid, l2_lookup.lockeds, data); } @@ -1704,7 +1704,7 @@ static int sja1105_port_enable(struct dsa_switch *ds, int port, if (!dsa_is_user_port(ds, port)) return 0; - slave = ds->ports[port].slave; + slave = dsa_to_port(ds, port)->slave; slave->features &= ~NETIF_F_HW_VLAN_CTAG_FILTER; @@ -1736,7 +1736,7 @@ static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot, } /* Transfer skb to the host port. */ - dsa_enqueue_skb(skb, ds->ports[port].slave); + dsa_enqueue_skb(skb, dsa_to_port(ds, port)->slave); /* Wait until the switch has processed the frame */ do { @@ -2061,8 +2061,8 @@ static int sja1105_probe(struct spi_device *spi) for (i = 0; i < SJA1105_NUM_PORTS; i++) { struct sja1105_port *sp = &priv->ports[i]; - ds->ports[i].priv = sp; - sp->dp = &ds->ports[i]; + dsa_to_port(ds, i)->priv = sp; + sp->dp = dsa_to_port(ds, i); sp->data = tagger_data; } mutex_init(&priv->ptp_data.lock); diff --git a/include/net/dsa.h b/include/net/dsa.h index 8c3ea0530f65..2e4fe2f8962b 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -278,7 +278,7 @@ struct dsa_switch { struct dsa_port ports[]; }; -static inline const struct dsa_port *dsa_to_port(struct dsa_switch *ds, int p) +static inline struct dsa_port *dsa_to_port(struct dsa_switch *ds, int p) { return &ds->ports[p]; } diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 43120a3fb06f..a5545762f5e7 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -246,7 +246,9 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, #ifdef CONFIG_PM_SLEEP static bool dsa_is_port_initialized(struct dsa_switch *ds, int p) { - return dsa_is_user_port(ds, p) && ds->ports[p].slave; + const struct dsa_port *dp = dsa_to_port(ds, p); + + return dp->type == DSA_PORT_TYPE_USER && dp->slave; } int dsa_switch_suspend(struct dsa_switch *ds) @@ -258,7 +260,7 @@ int dsa_switch_suspend(struct dsa_switch *ds) if (!dsa_is_port_initialized(ds, i)) continue; - ret = dsa_slave_suspend(ds->ports[i].slave); + ret = dsa_slave_suspend(dsa_to_port(ds, i)->slave); if (ret) return ret; } @@ -285,7 +287,7 @@ int dsa_switch_resume(struct dsa_switch *ds) if (!dsa_is_port_initialized(ds, i)) continue; - ret = dsa_slave_resume(ds->ports[i].slave); + ret = dsa_slave_resume(dsa_to_port(ds, i)->slave); if (ret) return ret; } diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 716d265ba8ca..1716535167ee 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -708,7 +708,7 @@ static int dsa_switch_parse_ports_of(struct dsa_switch *ds, goto out_put_node; } - dp = &ds->ports[reg]; + dp = dsa_to_port(ds, reg); err = dsa_port_parse_of(dp, port); if (err) @@ -787,7 +787,7 @@ static int dsa_switch_parse_ports(struct dsa_switch *ds, for (i = 0; i < DSA_MAX_PORTS; i++) { name = cd->port_names[i]; dev = cd->netdev[i]; - dp = &ds->ports[i]; + dp = dsa_to_port(ds, i); if (!name) continue; diff --git a/net/dsa/switch.c b/net/dsa/switch.c index 6a9607518823..df4abe897ed6 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -20,7 +20,7 @@ static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds, int i; for (i = 0; i < ds->num_ports; ++i) { - struct dsa_port *dp = &ds->ports[i]; + struct dsa_port *dp = dsa_to_port(ds, i); if (dp->ageing_time && dp->ageing_time < ageing_time) ageing_time = dp->ageing_time; @@ -98,7 +98,7 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds, if (unset_vlan_filtering) { struct switchdev_trans trans = {0}; - err = dsa_port_vlan_filtering(&ds->ports[info->port], + err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port), false, &trans); if (err && err != EOPNOTSUPP) return err; diff --git a/net/dsa/tag_8021q.c b/net/dsa/tag_8021q.c index 9c1cc2482b68..bf91fc55fc44 100644 --- a/net/dsa/tag_8021q.c +++ b/net/dsa/tag_8021q.c @@ -103,7 +103,7 @@ static int dsa_8021q_restore_pvid(struct dsa_switch *ds, int port) if (!dsa_is_user_port(ds, port)) return 0; - slave = ds->ports[port].slave; + slave = dsa_to_port(ds, port)->slave; err = br_vlan_get_pvid(slave, &pvid); if (err < 0) @@ -118,7 +118,7 @@ static int dsa_8021q_restore_pvid(struct dsa_switch *ds, int port) return err; } - return dsa_port_vid_add(&ds->ports[port], pvid, vinfo.flags); + return dsa_port_vid_add(dsa_to_port(ds, port), pvid, vinfo.flags); } /* If @enabled is true, installs @vid with @flags into the switch port's HW @@ -130,7 +130,7 @@ static int dsa_8021q_restore_pvid(struct dsa_switch *ds, int port) static int dsa_8021q_vid_apply(struct dsa_switch *ds, int port, u16 vid, u16 flags, bool enabled) { - struct dsa_port *dp = &ds->ports[port]; + struct dsa_port *dp = dsa_to_port(ds, port); struct bridge_vlan_info vinfo; int err; From ab8ccae122a41530a89bc899ace0e46defb156a8 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 21 Oct 2019 16:51:16 -0400 Subject: [PATCH 02/16] net: dsa: add ports list in the switch fabric Add a list of switch ports within the switch fabric. This will help the lookup of a port inside the whole fabric, and it is the first step towards supporting multiple CPU ports, before deprecating the usage of the unique dst->cpu_dp pointer. In preparation for a future allocation of the dsa_port structures, return -ENOMEM in case no structure is returned, even though this error cannot be reached yet. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- include/net/dsa.h | 5 +++++ net/dsa/dsa2.c | 48 +++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/include/net/dsa.h b/include/net/dsa.h index 2e4fe2f8962b..6ff6dfcdc61d 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -125,6 +125,9 @@ struct dsa_switch_tree { */ struct dsa_port *cpu_dp; + /* List of switch ports */ + struct list_head ports; + /* * Data for the individual switch chips. */ @@ -195,6 +198,8 @@ struct dsa_port { struct work_struct xmit_work; struct sk_buff_head xmit_queue; + struct list_head list; + /* * Give the switch driver somewhere to hang its per-port private data * structures (accessible from the tagger). diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 1716535167ee..ba27ff8b4445 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -45,6 +45,8 @@ static struct dsa_switch_tree *dsa_tree_alloc(int index) dst->index = index; + INIT_LIST_HEAD(&dst->ports); + INIT_LIST_HEAD(&dst->list); list_add_tail(&dst->list, &dsa_tree_list); @@ -616,6 +618,22 @@ static int dsa_tree_add_switch(struct dsa_switch_tree *dst, return err; } +static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index) +{ + struct dsa_switch_tree *dst = ds->dst; + struct dsa_port *dp; + + dp = &ds->ports[index]; + + dp->ds = ds; + dp->index = index; + + INIT_LIST_HEAD(&dp->list); + list_add_tail(&dp->list, &dst->ports); + + return dp; +} + static int dsa_port_parse_user(struct dsa_port *dp, const char *name) { if (!name) @@ -742,6 +760,20 @@ static int dsa_switch_parse_member_of(struct dsa_switch *ds, return 0; } +static int dsa_switch_touch_ports(struct dsa_switch *ds) +{ + struct dsa_port *dp; + int port; + + for (port = 0; port < ds->num_ports; port++) { + dp = dsa_port_touch(ds, port); + if (!dp) + return -ENOMEM; + } + + return 0; +} + static int dsa_switch_parse_of(struct dsa_switch *ds, struct device_node *dn) { int err; @@ -750,6 +782,10 @@ static int dsa_switch_parse_of(struct dsa_switch *ds, struct device_node *dn) if (err) return err; + err = dsa_switch_touch_ports(ds); + if (err) + return err; + return dsa_switch_parse_ports_of(ds, dn); } @@ -807,6 +843,8 @@ static int dsa_switch_parse_ports(struct dsa_switch *ds, static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd) { + int err; + ds->cd = cd; /* We don't support interconnected switches nor multiple trees via @@ -817,6 +855,10 @@ static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd) if (!ds->dst) return -ENOMEM; + err = dsa_switch_touch_ports(ds); + if (err) + return err; + return dsa_switch_parse_ports(ds, cd); } @@ -849,7 +891,6 @@ static int dsa_switch_probe(struct dsa_switch *ds) struct dsa_switch *dsa_switch_alloc(struct device *dev, size_t n) { struct dsa_switch *ds; - int i; ds = devm_kzalloc(dev, struct_size(ds, ports, n), GFP_KERNEL); if (!ds) @@ -858,11 +899,6 @@ struct dsa_switch *dsa_switch_alloc(struct device *dev, size_t n) ds->dev = dev; ds->num_ports = n; - for (i = 0; i < ds->num_ports; ++i) { - ds->ports[i].index = i; - ds->ports[i].ds = ds; - } - return ds; } EXPORT_SYMBOL_GPL(dsa_switch_alloc); From b96ddf254b09447c6b79632cdc02dae3f2454a82 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 21 Oct 2019 16:51:17 -0400 Subject: [PATCH 03/16] net: dsa: use ports list in dsa_to_port Use the new ports list instead of accessing the dsa_switch array of ports in the dsa_to_port helper. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- include/net/dsa.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/include/net/dsa.h b/include/net/dsa.h index 6ff6dfcdc61d..d2b7ee28f3fd 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -285,7 +285,14 @@ struct dsa_switch { static inline struct dsa_port *dsa_to_port(struct dsa_switch *ds, int p) { - return &ds->ports[p]; + struct dsa_switch_tree *dst = ds->dst; + struct dsa_port *dp = NULL; + + list_for_each_entry(dp, &dst->ports, list) + if (dp->ds == ds && dp->index == p) + break; + + return dp; } static inline bool dsa_is_unused_port(struct dsa_switch *ds, int p) From 7b9a2f4bac68e1dcc77baebd8c1e32d43710bafa Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 21 Oct 2019 16:51:18 -0400 Subject: [PATCH 04/16] net: dsa: use ports list to find slave Use the new ports list instead of iterating over switches and their ports when looking for a slave device from a given master interface. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- net/dsa/dsa_priv.h | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 12f8c7ee4dd8..53e7577896b6 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -104,25 +104,14 @@ static inline struct net_device *dsa_master_find_slave(struct net_device *dev, { struct dsa_port *cpu_dp = dev->dsa_ptr; struct dsa_switch_tree *dst = cpu_dp->dst; - struct dsa_switch *ds; - struct dsa_port *slave_port; + struct dsa_port *dp; - if (device < 0 || device >= DSA_MAX_SWITCHES) - return NULL; + list_for_each_entry(dp, &dst->ports, list) + if (dp->ds->index == device && dp->index == port && + dp->type == DSA_PORT_TYPE_USER) + return dp->slave; - ds = dst->ds[device]; - if (!ds) - return NULL; - - if (port < 0 || port >= ds->num_ports) - return NULL; - - slave_port = &ds->ports[port]; - - if (unlikely(slave_port->type != DSA_PORT_TYPE_USER)) - return NULL; - - return slave_port->slave; + return NULL; } /* port.c */ From fb35c60cbacc67a6075fb8e3d98fa348665662fe Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 21 Oct 2019 16:51:19 -0400 Subject: [PATCH 05/16] net: dsa: use ports list to setup switches Use the new ports list instead of iterating over switches and their ports when setting up the switches and their ports. At the same time, provide setup states and messages for ports and switches as it is done for the trees. Signed-off-by: Vivien Didelot Signed-off-by: Jakub Kicinski --- include/net/dsa.h | 4 ++ net/dsa/dsa2.c | 93 +++++++++++++++++++++-------------------------- 2 files changed, 45 insertions(+), 52 deletions(-) diff --git a/include/net/dsa.h b/include/net/dsa.h index d2b7ee28f3fd..bd08bdee8341 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -215,9 +215,13 @@ struct dsa_port { * Original copy of the master netdev net_device_ops */ const struct net_device_ops *orig_ndo_ops; + + bool setup; }; struct dsa_switch { + bool setup; + struct device *dev; /* diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index ba27ff8b4445..01b6047d9b7b 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -267,6 +267,9 @@ static int dsa_port_setup(struct dsa_port *dp) bool dsa_port_enabled = false; int err = 0; + if (dp->setup) + return 0; + switch (dp->type) { case DSA_PORT_TYPE_UNUSED: dsa_port_disable(dp); @@ -335,14 +338,21 @@ static int dsa_port_setup(struct dsa_port *dp) dsa_port_link_unregister_of(dp); if (err && devlink_port_registered) devlink_port_unregister(dlp); + if (err) + return err; - return err; + dp->setup = true; + + return 0; } static void dsa_port_teardown(struct dsa_port *dp) { struct devlink_port *dlp = &dp->devlink_port; + if (!dp->setup) + return; + switch (dp->type) { case DSA_PORT_TYPE_UNUSED: break; @@ -365,11 +375,16 @@ static void dsa_port_teardown(struct dsa_port *dp) } break; } + + dp->setup = false; } static int dsa_switch_setup(struct dsa_switch *ds) { - int err = 0; + int err; + + if (ds->setup) + return 0; /* Initialize ds->phys_mii_mask before registering the slave MDIO bus * driver and before ops->setup() has run, since the switch drivers and @@ -411,6 +426,8 @@ static int dsa_switch_setup(struct dsa_switch *ds) goto unregister_notifier; } + ds->setup = true; + return 0; unregister_notifier: @@ -426,6 +443,9 @@ free_devlink: static void dsa_switch_teardown(struct dsa_switch *ds) { + if (!ds->setup) + return; + if (ds->slave_mii_bus && ds->ops->phy_read) mdiobus_unregister(ds->slave_mii_bus); @@ -440,78 +460,47 @@ static void dsa_switch_teardown(struct dsa_switch *ds) ds->devlink = NULL; } + ds->setup = false; } static int dsa_tree_setup_switches(struct dsa_switch_tree *dst) { - struct dsa_switch *ds; struct dsa_port *dp; - int device, port, i; - int err = 0; + int err; - for (device = 0; device < DSA_MAX_SWITCHES; device++) { - ds = dst->ds[device]; - if (!ds) - continue; - - err = dsa_switch_setup(ds); + list_for_each_entry(dp, &dst->ports, list) { + err = dsa_switch_setup(dp->ds); if (err) - goto switch_teardown; + goto teardown; + } - for (port = 0; port < ds->num_ports; port++) { - dp = &ds->ports[port]; - - err = dsa_port_setup(dp); - if (err) - goto ports_teardown; - } + list_for_each_entry(dp, &dst->ports, list) { + err = dsa_port_setup(dp); + if (err) + goto teardown; } return 0; -ports_teardown: - for (i = 0; i < port; i++) - dsa_port_teardown(&ds->ports[i]); +teardown: + list_for_each_entry(dp, &dst->ports, list) + dsa_port_teardown(dp); - dsa_switch_teardown(ds); - -switch_teardown: - for (i = 0; i < device; i++) { - ds = dst->ds[i]; - if (!ds) - continue; - - for (port = 0; port < ds->num_ports; port++) { - dp = &ds->ports[port]; - - dsa_port_teardown(dp); - } - - dsa_switch_teardown(ds); - } + list_for_each_entry(dp, &dst->ports, list) + dsa_switch_teardown(dp->ds); return err; } static void dsa_tree_teardown_switches(struct dsa_switch_tree *dst) { - struct dsa_switch *ds; struct dsa_port *dp; - int device, port; - for (device = 0; device < DSA_MAX_SWITCHES; device++) { - ds = dst->ds[device]; - if (!ds) - continue; + list_for_each_entry(dp, &dst->ports, list) + dsa_port_teardown(dp); - for (port = 0; port < ds->num_ports; port++) { - dp = &ds->ports[port]; - - dsa_port_teardown(dp); - } - - dsa_switch_teardown(ds); - } + list_for_each_entry(dp, &dst->ports, list) + dsa_switch_teardown(dp->ds); } static int dsa_tree_setup_master(struct dsa_switch_tree *dst) From 86bfb2c1f4337d3306d235f615d35ba8bbbe4650 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 21 Oct 2019 16:51:20 -0400 Subject: [PATCH 06/16] net: dsa: use ports list for routing table setup Use the new ports list instead of accessing the dsa_switch array of ports when iterating over DSA ports of a switch to set up the routing table. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- net/dsa/dsa2.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 01b6047d9b7b..623805ba8e1a 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -157,6 +157,7 @@ static bool dsa_port_setup_routing_table(struct dsa_port *dp) static bool dsa_switch_setup_routing_table(struct dsa_switch *ds) { + struct dsa_switch_tree *dst = ds->dst; bool complete = true; struct dsa_port *dp; int i; @@ -164,10 +165,8 @@ static bool dsa_switch_setup_routing_table(struct dsa_switch *ds) for (i = 0; i < DSA_MAX_SWITCHES; i++) ds->rtable[i] = DSA_RTABLE_NONE; - for (i = 0; i < ds->num_ports; i++) { - dp = &ds->ports[i]; - - if (dsa_port_is_dsa(dp)) { + list_for_each_entry(dp, &dst->ports, list) { + if (dp->ds == ds && dsa_port_is_dsa(dp)) { complete = dsa_port_setup_routing_table(dp); if (!complete) break; From 764b7e624284c3f41bdd15bd4e077d8ec5b8c686 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 21 Oct 2019 16:51:21 -0400 Subject: [PATCH 07/16] net: dsa: use ports list to find a port by node Use the new ports list instead of iterating over switches and their ports to find a port from a given node. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/dsa2.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 623805ba8e1a..a4de7ff8b19b 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -113,22 +113,11 @@ static bool dsa_port_is_user(struct dsa_port *dp) static struct dsa_port *dsa_tree_find_port_by_node(struct dsa_switch_tree *dst, struct device_node *dn) { - struct dsa_switch *ds; struct dsa_port *dp; - int device, port; - for (device = 0; device < DSA_MAX_SWITCHES; device++) { - ds = dst->ds[device]; - if (!ds) - continue; - - for (port = 0; port < ds->num_ports; port++) { - dp = &ds->ports[port]; - - if (dp->dn == dn) - return dp; - } - } + list_for_each_entry(dp, &dst->ports, list) + if (dp->dn == dn) + return dp; return NULL; } From 0cfec588ec210e82e6572d1fb10db195fcc41a87 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 21 Oct 2019 16:51:22 -0400 Subject: [PATCH 08/16] net: dsa: use ports list to setup multiple master devices Now that we have a potential list of CPU ports, make use of it instead of only configuring the master device of an unique CPU port. Signed-off-by: Vivien Didelot Signed-off-by: Jakub Kicinski --- net/dsa/dsa2.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index a4de7ff8b19b..514c0195e2e8 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -493,19 +493,27 @@ static void dsa_tree_teardown_switches(struct dsa_switch_tree *dst) static int dsa_tree_setup_master(struct dsa_switch_tree *dst) { - struct dsa_port *cpu_dp = dst->cpu_dp; - struct net_device *master = cpu_dp->master; + struct dsa_port *dp; + int err; - /* DSA currently supports a single pair of CPU port and master device */ - return dsa_master_setup(master, cpu_dp); + list_for_each_entry(dp, &dst->ports, list) { + if (dsa_port_is_cpu(dp)) { + err = dsa_master_setup(dp->master, dp); + if (err) + return err; + } + } + + return 0; } static void dsa_tree_teardown_master(struct dsa_switch_tree *dst) { - struct dsa_port *cpu_dp = dst->cpu_dp; - struct net_device *master = cpu_dp->master; + struct dsa_port *dp; - return dsa_master_teardown(master); + list_for_each_entry(dp, &dst->ports, list) + if (dsa_port_is_cpu(dp)) + dsa_master_teardown(dp->master); } static int dsa_tree_setup(struct dsa_switch_tree *dst) From c0b736282ccf6d9450f3bed55a134f2123a7a565 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 21 Oct 2019 16:51:23 -0400 Subject: [PATCH 09/16] net: dsa: use ports list to find first CPU port Use the new ports list instead of iterating over switches and their ports when looking up the first CPU port in the tree. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- net/dsa/dsa2.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 514c0195e2e8..80191c7702a9 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -186,22 +186,11 @@ static bool dsa_tree_setup_routing_table(struct dsa_switch_tree *dst) static struct dsa_port *dsa_tree_find_first_cpu(struct dsa_switch_tree *dst) { - struct dsa_switch *ds; struct dsa_port *dp; - int device, port; - for (device = 0; device < DSA_MAX_SWITCHES; device++) { - ds = dst->ds[device]; - if (!ds) - continue; - - for (port = 0; port < ds->num_ports; port++) { - dp = &ds->ports[port]; - - if (dsa_port_is_cpu(dp)) - return dp; - } - } + list_for_each_entry(dp, &dst->ports, list) + if (dsa_port_is_cpu(dp)) + return dp; return NULL; } From da4561cda2ea6240fc61442eeb2acc47e2e0cae3 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 21 Oct 2019 16:51:24 -0400 Subject: [PATCH 10/16] net: dsa: use ports list to setup default CPU port Use the new ports list instead of iterating over switches and their ports when setting up the default CPU port. Unassign it on teardown. Now that we can iterate over multiple CPU ports, remove dst->cpu_dp. At the same time, provide a better error message for CPU-less tree. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- include/net/dsa.h | 5 ----- net/dsa/dsa2.c | 33 ++++++++++++--------------------- 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/include/net/dsa.h b/include/net/dsa.h index bd08bdee8341..f572134eb5de 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -120,11 +120,6 @@ struct dsa_switch_tree { */ struct dsa_platform_data *pd; - /* - * The switch port to which the CPU is attached. - */ - struct dsa_port *cpu_dp; - /* List of switch ports */ struct list_head ports; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 80191c7702a9..bf8b4e0fcb4f 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -197,38 +197,29 @@ static struct dsa_port *dsa_tree_find_first_cpu(struct dsa_switch_tree *dst) static int dsa_tree_setup_default_cpu(struct dsa_switch_tree *dst) { - struct dsa_switch *ds; - struct dsa_port *dp; - int device, port; + struct dsa_port *cpu_dp, *dp; - /* DSA currently only supports a single CPU port */ - dst->cpu_dp = dsa_tree_find_first_cpu(dst); - if (!dst->cpu_dp) { - pr_warn("Tree has no master device\n"); + cpu_dp = dsa_tree_find_first_cpu(dst); + if (!cpu_dp) { + pr_err("DSA: tree %d has no CPU port\n", dst->index); return -EINVAL; } /* Assign the default CPU port to all ports of the fabric */ - for (device = 0; device < DSA_MAX_SWITCHES; device++) { - ds = dst->ds[device]; - if (!ds) - continue; - - 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; - } - } + list_for_each_entry(dp, &dst->ports, list) + if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp)) + dp->cpu_dp = cpu_dp; return 0; } static void dsa_tree_teardown_default_cpu(struct dsa_switch_tree *dst) { - /* DSA currently only supports a single CPU port */ - dst->cpu_dp = NULL; + struct dsa_port *dp; + + list_for_each_entry(dp, &dst->ports, list) + if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp)) + dp->cpu_dp = NULL; } static int dsa_port_setup(struct dsa_port *dp) From d14939be8bfc4b7493007c365cce5d707d4fbfa1 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 21 Oct 2019 16:51:25 -0400 Subject: [PATCH 11/16] net: dsa: mv88e6xxx: silently skip PVT ops Since mv88e6xxx_pvt_map is a static helper, no need to return -EOPNOTSUPP if the chip has no PVT, simply silently skip the operation. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- drivers/net/dsa/mv88e6xxx/chip.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index d67deec77452..510ccdc2d03c 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1253,7 +1253,7 @@ static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port) u16 pvlan = 0; if (!mv88e6xxx_has_pvt(chip)) - return -EOPNOTSUPP; + return 0; /* Skip the local source device, which uses in-chip port VLAN */ if (dev != chip->ds->index) @@ -2049,9 +2049,6 @@ static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip, } } - if (!mv88e6xxx_has_pvt(chip)) - return 0; - /* Remap the Port VLAN of each cross-chip bridge group member */ for (dev = 0; dev < DSA_MAX_SWITCHES; ++dev) { ds = chip->ds->dst->ds[dev]; @@ -2101,9 +2098,6 @@ static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds, int dev, struct mv88e6xxx_chip *chip = ds->priv; int err; - if (!mv88e6xxx_has_pvt(chip)) - return 0; - mv88e6xxx_reg_lock(chip); err = mv88e6xxx_pvt_map(chip, dev, port); mv88e6xxx_reg_unlock(chip); @@ -2116,9 +2110,6 @@ static void mv88e6xxx_crosschip_bridge_leave(struct dsa_switch *ds, int dev, { struct mv88e6xxx_chip *chip = ds->priv; - if (!mv88e6xxx_has_pvt(chip)) - return; - mv88e6xxx_reg_lock(chip); if (mv88e6xxx_pvt_map(chip, dev, port)) dev_err(ds->dev, "failed to remap cross-chip Port VLAN\n"); From 9dc8b13e230a802f2510de5e37bfceaa59c9e281 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 21 Oct 2019 16:51:26 -0400 Subject: [PATCH 12/16] net: dsa: mv88e6xxx: use ports list to map port VLAN Instead of digging into the other dsa_switch structures of the fabric and relying too much on the dsa_to_port helper, use the new list of switch fabric ports to define the mask of the local ports allowed to receive frames from another port of the fabric. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- drivers/net/dsa/mv88e6xxx/chip.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 510ccdc2d03c..af8943142053 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1057,35 +1057,43 @@ static int mv88e6xxx_set_mac_eee(struct dsa_switch *ds, int port, return 0; } +/* Mask of the local ports allowed to receive frames from a given fabric port */ static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port) { - struct dsa_switch *ds = NULL; + struct dsa_switch *ds = chip->ds; + struct dsa_switch_tree *dst = ds->dst; struct net_device *br; + struct dsa_port *dp; + bool found = false; u16 pvlan; - int i; - if (dev < DSA_MAX_SWITCHES) - ds = chip->ds->dst->ds[dev]; + list_for_each_entry(dp, &dst->ports, list) { + if (dp->ds->index == dev && dp->index == port) { + found = true; + break; + } + } /* Prevent frames from unknown switch or port */ - if (!ds || port >= ds->num_ports) + if (!found) return 0; /* Frames from DSA links and CPU ports can egress any local port */ - if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) + if (dp->type == DSA_PORT_TYPE_CPU || dp->type == DSA_PORT_TYPE_DSA) return mv88e6xxx_port_mask(chip); - br = dsa_to_port(ds, port)->bridge_dev; + br = dp->bridge_dev; pvlan = 0; /* Frames from user ports can egress any local DSA links and CPU ports, * as well as any local member of their bridge group. */ - for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) - if (dsa_is_cpu_port(chip->ds, i) || - dsa_is_dsa_port(chip->ds, i) || - (br && dsa_to_port(chip->ds, i)->bridge_dev == br)) - pvlan |= BIT(i); + list_for_each_entry(dp, &dst->ports, list) + if (dp->ds == ds && + (dp->type == DSA_PORT_TYPE_CPU || + dp->type == DSA_PORT_TYPE_DSA || + (br && dp->bridge_dev == br))) + pvlan |= BIT(dp->index); return pvlan; } From ef2025ec0acc92fa15ad3df386cb18fefe880643 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 21 Oct 2019 16:51:27 -0400 Subject: [PATCH 13/16] net: dsa: mv88e6xxx: use ports list to map bridge Instead of digging into the other dsa_switch structures of the fabric and relying too much on the dsa_to_port helper, use the new list of switch fabric ports to remap the Port VLAN Map of local bridge group members or remap the Port VLAN Table entry of external bridge group members. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- drivers/net/dsa/mv88e6xxx/chip.c | 39 +++++++++++++++----------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index af8943142053..c53d4dc88e90 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2043,29 +2043,26 @@ static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip, struct net_device *br) { - struct dsa_switch *ds; - int port; - int dev; + struct dsa_switch *ds = chip->ds; + struct dsa_switch_tree *dst = ds->dst; + struct dsa_port *dp; int err; - /* Remap the Port VLAN of each local bridge group member */ - for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) { - if (dsa_to_port(chip->ds, port)->bridge_dev == br) { - err = mv88e6xxx_port_vlan_map(chip, port); - if (err) - return err; - } - } - - /* Remap the Port VLAN of each cross-chip bridge group member */ - for (dev = 0; dev < DSA_MAX_SWITCHES; ++dev) { - ds = chip->ds->dst->ds[dev]; - if (!ds) - break; - - for (port = 0; port < ds->num_ports; ++port) { - if (dsa_to_port(ds, port)->bridge_dev == br) { - err = mv88e6xxx_pvt_map(chip, dev, port); + list_for_each_entry(dp, &dst->ports, list) { + if (dp->bridge_dev == br) { + if (dp->ds == ds) { + /* This is a local bridge group member, + * remap its Port VLAN Map. + */ + err = mv88e6xxx_port_vlan_map(chip, dp->index); + if (err) + return err; + } else { + /* This is an external bridge group member, + * remap its cross-chip Port VLAN Table entry. + */ + err = mv88e6xxx_pvt_map(chip, dp->ds->index, + dp->index); if (err) return err; } From d5a619bf60ecf910175606921b4610a2842c635e Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 21 Oct 2019 16:51:28 -0400 Subject: [PATCH 14/16] net: dsa: sja1105: register switch before assigning port private data Like the dsa_switch_tree structures, the dsa_port structures will be allocated on switch registration. The SJA1105 driver is the only one accessing the dsa_port structure after the switch allocation and before the switch registration. For that reason, move switch registration prior to assigning the priv member of the dsa_port structures. Signed-off-by: Vivien Didelot Signed-off-by: Jakub Kicinski --- drivers/net/dsa/sja1105/sja1105_main.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 4b0cb779f187..0ebbda5ca665 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -2057,6 +2057,15 @@ static int sja1105_probe(struct spi_device *spi) tagger_data = &priv->tagger_data; + mutex_init(&priv->ptp_data.lock); + mutex_init(&priv->mgmt_lock); + + sja1105_tas_setup(ds); + + rc = dsa_register_switch(priv->ds); + if (rc) + return rc; + /* Connections between dsa_port and sja1105_port */ for (i = 0; i < SJA1105_NUM_PORTS; i++) { struct sja1105_port *sp = &priv->ports[i]; @@ -2065,12 +2074,8 @@ static int sja1105_probe(struct spi_device *spi) sp->dp = dsa_to_port(ds, i); sp->data = tagger_data; } - mutex_init(&priv->ptp_data.lock); - mutex_init(&priv->mgmt_lock); - sja1105_tas_setup(ds); - - return dsa_register_switch(priv->ds); + return 0; } static int sja1105_remove(struct spi_device *spi) From 05f294a852358a46d9236cc777901f49a4f0ae85 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 21 Oct 2019 16:51:29 -0400 Subject: [PATCH 15/16] net: dsa: allocate ports on touch Allocate the struct dsa_port the first time it is accessed with dsa_port_touch, and remove the static dsa_port array from the dsa_switch structure. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- include/net/dsa.h | 2 -- net/dsa/dsa2.c | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/include/net/dsa.h b/include/net/dsa.h index f572134eb5de..9bc1d3f71f89 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -277,9 +277,7 @@ struct dsa_switch { */ bool vlan_filtering; - /* Dynamically allocated ports, keep last */ size_t num_ports; - struct dsa_port ports[]; }; static inline struct dsa_port *dsa_to_port(struct dsa_switch *ds, int p) diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index bf8b4e0fcb4f..83cba4623698 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -588,7 +588,13 @@ static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index) struct dsa_switch_tree *dst = ds->dst; struct dsa_port *dp; - dp = &ds->ports[index]; + list_for_each_entry(dp, &dst->ports, list) + if (dp->ds == ds && dp->index == index) + return dp; + + dp = kzalloc(sizeof(*dp), GFP_KERNEL); + if (!dp) + return NULL; dp->ds = ds; dp->index = index; @@ -857,7 +863,7 @@ struct dsa_switch *dsa_switch_alloc(struct device *dev, size_t n) { struct dsa_switch *ds; - ds = devm_kzalloc(dev, struct_size(ds, ports, n), GFP_KERNEL); + ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); if (!ds) return NULL; @@ -885,6 +891,12 @@ static void dsa_switch_remove(struct dsa_switch *ds) { struct dsa_switch_tree *dst = ds->dst; unsigned int index = ds->index; + struct dsa_port *dp, *next; + + list_for_each_entry_safe(dp, next, &dst->ports, list) { + list_del(&dp->list); + kfree(dp); + } dsa_tree_remove_switch(dst, index); } From 7e99e34701728d54ccd0466eccf377a42b9db215 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 21 Oct 2019 16:51:30 -0400 Subject: [PATCH 16/16] net: dsa: remove dsa_switch_alloc helper Now that ports are dynamically listed in the fabric, there is no need to provide a special helper to allocate the dsa_switch structure. This will give more flexibility to drivers to embed this structure as they wish in their private structure. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- drivers/net/dsa/b53/b53_common.c | 5 ++++- drivers/net/dsa/dsa_loop.c | 5 ++++- drivers/net/dsa/lan9303-core.c | 4 +++- drivers/net/dsa/lantiq_gswip.c | 4 +++- drivers/net/dsa/microchip/ksz_common.c | 5 ++++- drivers/net/dsa/mt7530.c | 5 ++++- drivers/net/dsa/mv88e6060.c | 4 +++- drivers/net/dsa/mv88e6xxx/chip.c | 4 +++- drivers/net/dsa/qca8k.c | 5 ++++- drivers/net/dsa/realtek-smi-core.c | 5 ++++- drivers/net/dsa/sja1105/sja1105_main.c | 4 +++- drivers/net/dsa/vitesse-vsc73xx-core.c | 5 ++++- include/net/dsa.h | 1 - net/dsa/dsa2.c | 21 ++++++--------------- 14 files changed, 49 insertions(+), 28 deletions(-) diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index aef9b56781ef..baadf622ac55 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -2341,10 +2341,13 @@ struct b53_device *b53_switch_alloc(struct device *base, struct dsa_switch *ds; struct b53_device *dev; - ds = dsa_switch_alloc(base, DSA_MAX_PORTS); + ds = devm_kzalloc(base, sizeof(*ds), GFP_KERNEL); if (!ds) return NULL; + ds->dev = base; + ds->num_ports = DSA_MAX_PORTS; + dev = devm_kzalloc(base, sizeof(*dev), GFP_KERNEL); if (!dev) return NULL; diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index 925ed135a4d9..c8d7ef27fd72 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -286,10 +286,13 @@ static int dsa_loop_drv_probe(struct mdio_device *mdiodev) dev_info(&mdiodev->dev, "%s: 0x%0x\n", pdata->name, pdata->enabled_ports); - ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS); + ds = devm_kzalloc(&mdiodev->dev, sizeof(*ds), GFP_KERNEL); if (!ds) return -ENOMEM; + ds->dev = &mdiodev->dev; + ds->num_ports = DSA_MAX_PORTS; + ps = devm_kzalloc(&mdiodev->dev, sizeof(*ps), GFP_KERNEL); if (!ps) return -ENOMEM; diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index bbec86b9418e..e3c333a8f45d 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -1283,10 +1283,12 @@ static int lan9303_register_switch(struct lan9303 *chip) { int base; - chip->ds = dsa_switch_alloc(chip->dev, LAN9303_NUM_PORTS); + chip->ds = devm_kzalloc(chip->dev, sizeof(*chip->ds), GFP_KERNEL); if (!chip->ds) return -ENOMEM; + chip->ds->dev = chip->dev; + chip->ds->num_ports = LAN9303_NUM_PORTS; chip->ds->priv = chip; chip->ds->ops = &lan9303_switch_ops; base = chip->phy_addr_base; diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index a69c9b9878b7..955324968b74 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -1854,10 +1854,12 @@ static int gswip_probe(struct platform_device *pdev) if (!priv->hw_info) return -EINVAL; - priv->ds = dsa_switch_alloc(dev, priv->hw_info->max_ports); + priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); if (!priv->ds) return -ENOMEM; + priv->ds->dev = dev; + priv->ds->num_ports = priv->hw_info->max_ports; priv->ds->priv = priv; priv->ds->ops = &gswip_switch_ops; priv->dev = dev; diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index fe47180c908b..5d08e4430824 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -398,10 +398,13 @@ struct ksz_device *ksz_switch_alloc(struct device *base, void *priv) struct dsa_switch *ds; struct ksz_device *swdev; - ds = dsa_switch_alloc(base, DSA_MAX_PORTS); + ds = devm_kzalloc(base, sizeof(*ds), GFP_KERNEL); if (!ds) return NULL; + ds->dev = base; + ds->num_ports = DSA_MAX_PORTS; + swdev = devm_kzalloc(base, sizeof(*swdev), GFP_KERNEL); if (!swdev) return NULL; diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index a91293e47a57..add9e4279176 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -1632,10 +1632,13 @@ mt7530_probe(struct mdio_device *mdiodev) if (!priv) return -ENOMEM; - priv->ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS); + priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); if (!priv->ds) return -ENOMEM; + priv->ds->dev = &mdiodev->dev; + priv->ds->num_ports = DSA_MAX_PORTS; + /* Use medatek,mcm property to distinguish hardware type that would * casues a little bit differences on power-on sequence. */ diff --git a/drivers/net/dsa/mv88e6060.c b/drivers/net/dsa/mv88e6060.c index 2a2489b5196d..a5a37f47b320 100644 --- a/drivers/net/dsa/mv88e6060.c +++ b/drivers/net/dsa/mv88e6060.c @@ -270,10 +270,12 @@ static int mv88e6060_probe(struct mdio_device *mdiodev) dev_info(dev, "switch %s detected\n", name); - ds = dsa_switch_alloc(dev, MV88E6060_PORTS); + ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); if (!ds) return -ENOMEM; + ds->dev = dev; + ds->num_ports = MV88E6060_PORTS; ds->priv = priv; ds->dev = dev; ds->ops = &mv88e6060_switch_ops; diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index c53d4dc88e90..5fdf6d6ebe27 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -4978,10 +4978,12 @@ static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip) struct device *dev = chip->dev; struct dsa_switch *ds; - ds = dsa_switch_alloc(dev, mv88e6xxx_num_ports(chip)); + ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); if (!ds) return -ENOMEM; + ds->dev = dev; + ds->num_ports = mv88e6xxx_num_ports(chip); ds->priv = chip; ds->dev = dev; ds->ops = &mv88e6xxx_switch_ops; diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 71e44c8763b8..7e742cd491e8 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -1077,10 +1077,13 @@ qca8k_sw_probe(struct mdio_device *mdiodev) if (id != QCA8K_ID_QCA8337) return -ENODEV; - priv->ds = dsa_switch_alloc(&mdiodev->dev, QCA8K_NUM_PORTS); + priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), + QCA8K_NUM_PORTS); if (!priv->ds) return -ENOMEM; + priv->ds->dev = &mdiodev->dev; + priv->ds->num_ports = DSA_MAX_PORTS; priv->ds->priv = priv; priv->ops = qca8k_switch_ops; priv->ds->ops = &priv->ops; diff --git a/drivers/net/dsa/realtek-smi-core.c b/drivers/net/dsa/realtek-smi-core.c index dc0509c02d29..fae188c60191 100644 --- a/drivers/net/dsa/realtek-smi-core.c +++ b/drivers/net/dsa/realtek-smi-core.c @@ -444,9 +444,12 @@ static int realtek_smi_probe(struct platform_device *pdev) return ret; } - smi->ds = dsa_switch_alloc(dev, smi->num_ports); + smi->ds = devm_kzalloc(dev, sizeof(*smi->ds), GFP_KERNEL); if (!smi->ds) return -ENOMEM; + + smi->ds->dev = dev; + smi->ds->num_ports = smi->num_ports; smi->ds->priv = smi; smi->ds->ops = var->ds_ops; diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 0ebbda5ca665..2ae84a9dea59 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -2047,10 +2047,12 @@ static int sja1105_probe(struct spi_device *spi) dev_info(dev, "Probed switch chip: %s\n", priv->info->name); - ds = dsa_switch_alloc(dev, SJA1105_NUM_PORTS); + ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); if (!ds) return -ENOMEM; + ds->dev = dev; + ds->num_ports = SJA1105_NUM_PORTS; ds->ops = &sja1105_switch_ops; ds->priv = priv; priv->ds = ds; diff --git a/drivers/net/dsa/vitesse-vsc73xx-core.c b/drivers/net/dsa/vitesse-vsc73xx-core.c index 614377ef7956..42c1574d45f2 100644 --- a/drivers/net/dsa/vitesse-vsc73xx-core.c +++ b/drivers/net/dsa/vitesse-vsc73xx-core.c @@ -1178,9 +1178,12 @@ int vsc73xx_probe(struct vsc73xx *vsc) * We allocate 8 ports and avoid access to the nonexistant * ports. */ - vsc->ds = dsa_switch_alloc(dev, 8); + vsc->ds = devm_kzalloc(dev, sizeof(*vsc->ds), GFP_KERNEL); if (!vsc->ds) return -ENOMEM; + + vsc->ds->dev = dev; + vsc->ds->num_ports = 8; vsc->ds->priv = vsc; vsc->ds->ops = &vsc73xx_ds_ops; diff --git a/include/net/dsa.h b/include/net/dsa.h index 9bc1d3f71f89..e3c14dc3bab9 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -577,7 +577,6 @@ static inline bool dsa_can_decode(const struct sk_buff *skb, return false; } -struct dsa_switch *dsa_switch_alloc(struct device *dev, size_t n); void dsa_unregister_switch(struct dsa_switch *ds); int dsa_register_switch(struct dsa_switch *ds); #ifdef CONFIG_PM_SLEEP diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 83cba4623698..1e3ac9b56c89 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -846,6 +846,12 @@ static int dsa_switch_probe(struct dsa_switch *ds) struct device_node *np = ds->dev->of_node; int err; + if (!ds->dev) + return -ENODEV; + + if (!ds->num_ports) + return -EINVAL; + if (np) err = dsa_switch_parse_of(ds, np); else if (pdata) @@ -859,21 +865,6 @@ static int dsa_switch_probe(struct dsa_switch *ds) return dsa_switch_add(ds); } -struct dsa_switch *dsa_switch_alloc(struct device *dev, size_t n) -{ - struct dsa_switch *ds; - - ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); - if (!ds) - return NULL; - - ds->dev = dev; - ds->num_ports = n; - - return ds; -} -EXPORT_SYMBOL_GPL(dsa_switch_alloc); - int dsa_register_switch(struct dsa_switch *ds) { int err;