From 2c110abc46168c3a8cc9f855c986a55650c777dd Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 5 May 2022 19:22:10 +0300 Subject: [PATCH 1/4] net: dsa: felix: use PGID_CPU for FDB entry migration on NPI port ocelot_fdb_add() redirects FDB entries installed on the NPI port towards the special reserved PGID_CPU used for host-filtered addresses. PGID_CPU contains BIT(ocelot->num_phys_ports) in the destination port mask, which is code name for the CPU port module. Whereas felix_migrate_fdbs_to_*_port() uses the ocelot->num_phys_ports PGID directly, and it appears that this works too. Even if this PGID is set to zero, apparently its number is special and packets still reach the CPU port module. Nonetheless, in the end, these addresses end up in the same place regardless of whether they go through an extra indirection layer or not. Use PGID_CPU across to have more uniformity. Signed-off-by: Vladimir Oltean Signed-off-by: Jakub Kicinski --- drivers/net/dsa/ocelot/felix.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 33cb124ca912..8abe48ae8c3a 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -49,14 +49,13 @@ static int felix_migrate_fdbs_to_npi_port(struct dsa_switch *ds, int port, { struct net_device *bridge_dev = felix_classify_db(db); struct ocelot *ocelot = ds->priv; - int cpu = ocelot->num_phys_ports; int err; err = ocelot_fdb_del(ocelot, port, addr, vid, bridge_dev); if (err) return err; - return ocelot_fdb_add(ocelot, cpu, addr, vid, bridge_dev); + return ocelot_fdb_add(ocelot, PGID_CPU, addr, vid, bridge_dev); } static int felix_migrate_mdbs_to_npi_port(struct dsa_switch *ds, int port, @@ -128,10 +127,9 @@ felix_migrate_fdbs_to_tag_8021q_port(struct dsa_switch *ds, int port, { struct net_device *bridge_dev = felix_classify_db(db); struct ocelot *ocelot = ds->priv; - int cpu = ocelot->num_phys_ports; int err; - err = ocelot_fdb_del(ocelot, cpu, addr, vid, bridge_dev); + err = ocelot_fdb_del(ocelot, PGID_CPU, addr, vid, bridge_dev); if (err) return err; From a51c1c3f3218c74c07fed55ea2ad5b18c374dc25 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 5 May 2022 19:22:11 +0300 Subject: [PATCH 2/4] net: dsa: felix: stop migrating FDBs back and forth on tag proto change I just realized we don't need to migrate the host-filtered FDB entries when the tagging protocol changes from "ocelot" to "ocelot-8021q". Host-filtered addresses are learned towards the PGID_CPU "multicast" port group, reserved by software, which contains BIT(ocelot->num_phys_ports). That is the "special" port entry in the analyzer block for the CPU port module. In "ocelot" mode, the CPU port module's packets are redirected to the NPI port. In "ocelot-8021q" mode, felix_8021q_cpu_port_init() does something funny anyway, and changes PGID_CPU to stop pointing at the CPU port module and start pointing at the physical port where the DSA master is attached. The fact that we can alter the destination of packets learned towards PGID_CPU without altering the MAC table entries themselves means that it is pointless to walk through the FDB entries, forget that they were learned towards PGID_CPU, and re-learn them towards the "unicast" PGID associated with the physical port connected to the DSA master. We can let the PGID_CPU value change simply alter the destination of the host-filtered unicast packets in one fell swoop. Signed-off-by: Vladimir Oltean Signed-off-by: Jakub Kicinski --- drivers/net/dsa/ocelot/felix.c | 55 ++-------------------------------- 1 file changed, 2 insertions(+), 53 deletions(-) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 8abe48ae8c3a..6443cd573040 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -42,22 +42,6 @@ static struct net_device *felix_classify_db(struct dsa_db db) } } -/* We are called before felix_npi_port_init(), so ocelot->npi is -1. */ -static int felix_migrate_fdbs_to_npi_port(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid, - struct dsa_db db) -{ - struct net_device *bridge_dev = felix_classify_db(db); - struct ocelot *ocelot = ds->priv; - int err; - - err = ocelot_fdb_del(ocelot, port, addr, vid, bridge_dev); - if (err) - return err; - - return ocelot_fdb_add(ocelot, PGID_CPU, addr, vid, bridge_dev); -} - static int felix_migrate_mdbs_to_npi_port(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid, struct dsa_db db) @@ -116,26 +100,6 @@ felix_migrate_flood_to_tag_8021q_port(struct dsa_switch *ds, int port) felix_migrate_pgid_bit(ds, ocelot->num_phys_ports, port, PGID_BC); } -/* ocelot->npi was already set to -1 by felix_npi_port_deinit, so - * ocelot_fdb_add() will not redirect FDB entries towards the - * CPU port module here, which is what we want. - */ -static int -felix_migrate_fdbs_to_tag_8021q_port(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid, - struct dsa_db db) -{ - struct net_device *bridge_dev = felix_classify_db(db); - struct ocelot *ocelot = ds->priv; - int err; - - err = ocelot_fdb_del(ocelot, PGID_CPU, addr, vid, bridge_dev); - if (err) - return err; - - return ocelot_fdb_add(ocelot, port, addr, vid, bridge_dev); -} - static int felix_migrate_mdbs_to_tag_8021q_port(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid, @@ -486,13 +450,9 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) if (err) return err; - err = dsa_port_walk_fdbs(ds, cpu, felix_migrate_fdbs_to_tag_8021q_port); - if (err) - goto out_tag_8021q_unregister; - err = dsa_port_walk_mdbs(ds, cpu, felix_migrate_mdbs_to_tag_8021q_port); if (err) - goto out_migrate_fdbs; + goto out_tag_8021q_unregister; felix_migrate_flood_to_tag_8021q_port(ds, cpu); @@ -514,8 +474,6 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) out_migrate_flood: felix_migrate_flood_to_npi_port(ds, cpu); dsa_port_walk_mdbs(ds, cpu, felix_migrate_mdbs_to_npi_port); -out_migrate_fdbs: - dsa_port_walk_fdbs(ds, cpu, felix_migrate_fdbs_to_npi_port); out_tag_8021q_unregister: dsa_tag_8021q_unregister(ds); return err; @@ -595,24 +553,15 @@ static int felix_setup_tag_npi(struct dsa_switch *ds, int cpu) struct ocelot *ocelot = ds->priv; int err; - err = dsa_port_walk_fdbs(ds, cpu, felix_migrate_fdbs_to_npi_port); - if (err) - return err; - err = dsa_port_walk_mdbs(ds, cpu, felix_migrate_mdbs_to_npi_port); if (err) - goto out_migrate_fdbs; + return err; felix_migrate_flood_to_npi_port(ds, cpu); felix_npi_port_init(ocelot, cpu); return 0; - -out_migrate_fdbs: - dsa_port_walk_fdbs(ds, cpu, felix_migrate_fdbs_to_tag_8021q_port); - - return err; } static void felix_teardown_tag_npi(struct dsa_switch *ds, int cpu) From 28de0f9fec5a84bd5b56d6364432a8730eac410a Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 5 May 2022 19:22:12 +0300 Subject: [PATCH 3/4] net: dsa: felix: perform MDB migration based on ocelot->multicast list The felix driver is the only user of dsa_port_walk_mdbs(), and there isn't even a good reason for it, considering that the host MDB entries are already saved by the ocelot switch lib in the ocelot->multicast list. Rewrite the multicast entry migration procedure around the ocelot->multicast list so we can delete dsa_port_walk_mdbs(). Signed-off-by: Vladimir Oltean Signed-off-by: Jakub Kicinski --- drivers/net/dsa/ocelot/felix.c | 51 +++---------------------- drivers/net/ethernet/mscc/ocelot.c | 61 ++++++++++++++++++++++++++++++ include/soc/mscc/ocelot.h | 3 ++ 3 files changed, 69 insertions(+), 46 deletions(-) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 6443cd573040..a4882e493cf4 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -42,27 +42,6 @@ static struct net_device *felix_classify_db(struct dsa_db db) } } -static int felix_migrate_mdbs_to_npi_port(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid, - struct dsa_db db) -{ - struct net_device *bridge_dev = felix_classify_db(db); - struct switchdev_obj_port_mdb mdb; - struct ocelot *ocelot = ds->priv; - int cpu = ocelot->num_phys_ports; - int err; - - memset(&mdb, 0, sizeof(mdb)); - ether_addr_copy(mdb.addr, addr); - mdb.vid = vid; - - err = ocelot_port_mdb_del(ocelot, port, &mdb, bridge_dev); - if (err) - return err; - - return ocelot_port_mdb_add(ocelot, cpu, &mdb, bridge_dev); -} - static void felix_migrate_pgid_bit(struct dsa_switch *ds, int from, int to, int pgid) { @@ -100,28 +79,6 @@ felix_migrate_flood_to_tag_8021q_port(struct dsa_switch *ds, int port) felix_migrate_pgid_bit(ds, ocelot->num_phys_ports, port, PGID_BC); } -static int -felix_migrate_mdbs_to_tag_8021q_port(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid, - struct dsa_db db) -{ - struct net_device *bridge_dev = felix_classify_db(db); - struct switchdev_obj_port_mdb mdb; - struct ocelot *ocelot = ds->priv; - int cpu = ocelot->num_phys_ports; - int err; - - memset(&mdb, 0, sizeof(mdb)); - ether_addr_copy(mdb.addr, addr); - mdb.vid = vid; - - err = ocelot_port_mdb_del(ocelot, cpu, &mdb, bridge_dev); - if (err) - return err; - - return ocelot_port_mdb_add(ocelot, port, &mdb, bridge_dev); -} - /* Set up VCAP ES0 rules for pushing a tag_8021q VLAN towards the CPU such that * the tagger can perform RX source port identification. */ @@ -450,7 +407,8 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) if (err) return err; - err = dsa_port_walk_mdbs(ds, cpu, felix_migrate_mdbs_to_tag_8021q_port); + err = ocelot_migrate_mdbs(ocelot, BIT(ocelot->num_phys_ports), + BIT(cpu)); if (err) goto out_tag_8021q_unregister; @@ -473,7 +431,7 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) out_migrate_flood: felix_migrate_flood_to_npi_port(ds, cpu); - dsa_port_walk_mdbs(ds, cpu, felix_migrate_mdbs_to_npi_port); + ocelot_migrate_mdbs(ocelot, BIT(cpu), BIT(ocelot->num_phys_ports)); out_tag_8021q_unregister: dsa_tag_8021q_unregister(ds); return err; @@ -553,7 +511,8 @@ static int felix_setup_tag_npi(struct dsa_switch *ds, int cpu) struct ocelot *ocelot = ds->priv; int err; - err = dsa_port_walk_mdbs(ds, cpu, felix_migrate_mdbs_to_npi_port); + err = ocelot_migrate_mdbs(ocelot, BIT(cpu), + BIT(ocelot->num_phys_ports)); if (err) return err; diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 0825a92599a5..9336f3b00c6e 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -2610,6 +2610,67 @@ static void ocelot_setup_logical_port_ids(struct ocelot *ocelot) } } +static int ocelot_migrate_mc(struct ocelot *ocelot, struct ocelot_multicast *mc, + unsigned long from_mask, unsigned long to_mask) +{ + unsigned char addr[ETH_ALEN]; + struct ocelot_pgid *pgid; + u16 vid = mc->vid; + + dev_dbg(ocelot->dev, + "Migrating multicast %pM vid %d from port mask 0x%lx to 0x%lx\n", + mc->addr, mc->vid, from_mask, to_mask); + + /* First clean up the current port mask from hardware, because + * we'll be modifying it. + */ + ocelot_pgid_free(ocelot, mc->pgid); + ocelot_encode_ports_to_mdb(addr, mc); + ocelot_mact_forget(ocelot, addr, vid); + + mc->ports &= ~from_mask; + mc->ports |= to_mask; + + pgid = ocelot_mdb_get_pgid(ocelot, mc); + if (IS_ERR(pgid)) { + dev_err(ocelot->dev, + "Cannot allocate PGID for mdb %pM vid %d\n", + mc->addr, mc->vid); + devm_kfree(ocelot->dev, mc); + return PTR_ERR(pgid); + } + mc->pgid = pgid; + + ocelot_encode_ports_to_mdb(addr, mc); + + if (mc->entry_type != ENTRYTYPE_MACv4 && + mc->entry_type != ENTRYTYPE_MACv6) + ocelot_write_rix(ocelot, pgid->ports, ANA_PGID_PGID, + pgid->index); + + return ocelot_mact_learn(ocelot, pgid->index, addr, vid, + mc->entry_type); +} + +int ocelot_migrate_mdbs(struct ocelot *ocelot, unsigned long from_mask, + unsigned long to_mask) +{ + struct ocelot_multicast *mc; + int err; + + list_for_each_entry(mc, &ocelot->multicast, list) { + if (!(mc->ports & from_mask)) + continue; + + err = ocelot_migrate_mc(ocelot, mc, from_mask, to_mask); + if (err) + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ocelot_migrate_mdbs); + /* Documentation for PORTID_VAL says: * Logical port number for front port. If port is not a member of a LLAG, * then PORTID must be set to the physical port number. diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 8d8d46778f7e..e88bcfe4b2cd 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -998,6 +998,9 @@ int ocelot_mact_learn_streamdata(struct ocelot *ocelot, int dst_idx, enum macaccess_entry_type type, int sfid, int ssid); +int ocelot_migrate_mdbs(struct ocelot *ocelot, unsigned long from_mask, + unsigned long to_mask); + int ocelot_vcap_policer_add(struct ocelot *ocelot, u32 pol_ix, struct ocelot_policer *pol); int ocelot_vcap_policer_del(struct ocelot *ocelot, u32 pol_ix); From fe5233b0ba0d2216d549f93e4540542b99b97642 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 5 May 2022 19:22:13 +0300 Subject: [PATCH 4/4] net: dsa: delete dsa_port_walk_{fdbs,mdbs} All the users of these functions are gone, delete them before they gain new ones. Signed-off-by: Vladimir Oltean Signed-off-by: Jakub Kicinski --- include/net/dsa.h | 6 ------ net/dsa/dsa.c | 40 ---------------------------------------- 2 files changed, 46 deletions(-) diff --git a/include/net/dsa.h b/include/net/dsa.h index 934958fda962..efd33956df37 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -1239,12 +1239,6 @@ struct dsa_switch_driver { struct net_device *dsa_dev_to_net_device(struct device *dev); -typedef int dsa_fdb_walk_cb_t(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid, - struct dsa_db db); - -int dsa_port_walk_fdbs(struct dsa_switch *ds, int port, dsa_fdb_walk_cb_t cb); -int dsa_port_walk_mdbs(struct dsa_switch *ds, int port, dsa_fdb_walk_cb_t cb); bool dsa_fdb_present_in_other_db(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid, struct dsa_db db); diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 0c6ae32742ec..be7b320cda76 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -458,46 +458,6 @@ struct dsa_port *dsa_port_from_netdev(struct net_device *netdev) } EXPORT_SYMBOL_GPL(dsa_port_from_netdev); -int dsa_port_walk_fdbs(struct dsa_switch *ds, int port, dsa_fdb_walk_cb_t cb) -{ - struct dsa_port *dp = dsa_to_port(ds, port); - struct dsa_mac_addr *a; - int err = 0; - - mutex_lock(&dp->addr_lists_lock); - - list_for_each_entry(a, &dp->fdbs, list) { - err = cb(ds, port, a->addr, a->vid, a->db); - if (err) - break; - } - - mutex_unlock(&dp->addr_lists_lock); - - return err; -} -EXPORT_SYMBOL_GPL(dsa_port_walk_fdbs); - -int dsa_port_walk_mdbs(struct dsa_switch *ds, int port, dsa_fdb_walk_cb_t cb) -{ - struct dsa_port *dp = dsa_to_port(ds, port); - struct dsa_mac_addr *a; - int err = 0; - - mutex_lock(&dp->addr_lists_lock); - - list_for_each_entry(a, &dp->mdbs, list) { - err = cb(ds, port, a->addr, a->vid, a->db); - if (err) - break; - } - - mutex_unlock(&dp->addr_lists_lock); - - return err; -} -EXPORT_SYMBOL_GPL(dsa_port_walk_mdbs); - bool dsa_db_equal(const struct dsa_db *a, const struct dsa_db *b) { if (a->type != b->type)