diff options
-rw-r--r-- | drivers/net/dsa/ocelot/felix.c | 171 | ||||
-rw-r--r-- | include/net/dsa.h | 7 | ||||
-rw-r--r-- | net/dsa/dsa.c | 40 |
3 files changed, 192 insertions, 26 deletions
diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index badb5b9ba790..47320bfbaac1 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -25,6 +25,104 @@ #include <net/dsa.h> #include "felix.h" +/* Translate the DSA database API into the ocelot switch library API, + * which uses VID 0 for all ports that aren't part of a bridge, + * and expects the bridge_dev to be NULL in that case. + */ +static struct net_device *felix_classify_db(struct dsa_db db) +{ + switch (db.type) { + case DSA_DB_PORT: + case DSA_DB_LAG: + return NULL; + case DSA_DB_BRIDGE: + return db.bridge.dev; + default: + return ERR_PTR(-EOPNOTSUPP); + } +} + +/* 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 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); +} + +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); +} + +/* 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 cpu = ocelot->num_phys_ports; + int err; + + err = ocelot_fdb_del(ocelot, 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, + 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. */ @@ -327,7 +425,7 @@ static int felix_update_trapping_destinations(struct dsa_switch *ds, return 0; } -static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) +static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu, bool change) { struct ocelot *ocelot = ds->priv; unsigned long cpu_flood; @@ -365,9 +463,21 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) if (err) return err; + if (change) { + 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; + } + err = felix_update_trapping_destinations(ds, true); if (err) - goto out_tag_8021q_unregister; + goto out_migrate_mdbs; /* The ownership of the CPU port module's queues might have just been * transferred to the tag_8021q tagger from the NPI-based tagger. @@ -380,6 +490,12 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) return 0; +out_migrate_mdbs: + if (change) + dsa_port_walk_mdbs(ds, cpu, felix_migrate_mdbs_to_npi_port); +out_migrate_fdbs: + if (change) + dsa_port_walk_fdbs(ds, cpu, felix_migrate_fdbs_to_npi_port); out_tag_8021q_unregister: dsa_tag_8021q_unregister(ds); return err; @@ -454,10 +570,23 @@ static void felix_npi_port_deinit(struct ocelot *ocelot, int port) ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, 1); } -static int felix_setup_tag_npi(struct dsa_switch *ds, int cpu) +static int felix_setup_tag_npi(struct dsa_switch *ds, int cpu, bool change) { struct ocelot *ocelot = ds->priv; unsigned long cpu_flood; + int err; + + if (change) { + 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; + } felix_npi_port_init(ocelot, cpu); @@ -478,6 +607,13 @@ static int felix_setup_tag_npi(struct dsa_switch *ds, int cpu) ocelot_rmw_rix(ocelot, cpu_flood, cpu_flood, ANA_PGID_PGID, PGID_BC); return 0; + +out_migrate_fdbs: + if (change) + 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) @@ -488,17 +624,17 @@ static void felix_teardown_tag_npi(struct dsa_switch *ds, int cpu) } static int felix_set_tag_protocol(struct dsa_switch *ds, int cpu, - enum dsa_tag_protocol proto) + enum dsa_tag_protocol proto, bool change) { int err; switch (proto) { case DSA_TAG_PROTO_SEVILLE: case DSA_TAG_PROTO_OCELOT: - err = felix_setup_tag_npi(ds, cpu); + err = felix_setup_tag_npi(ds, cpu, change); break; case DSA_TAG_PROTO_OCELOT_8021Q: - err = felix_setup_tag_8021q(ds, cpu); + err = felix_setup_tag_8021q(ds, cpu, change); break; default: err = -EPROTONOSUPPORT; @@ -542,9 +678,9 @@ static int felix_change_tag_protocol(struct dsa_switch *ds, int cpu, felix_del_tag_protocol(ds, cpu, old_proto); - err = felix_set_tag_protocol(ds, cpu, proto); + err = felix_set_tag_protocol(ds, cpu, proto, true); if (err) { - felix_set_tag_protocol(ds, cpu, old_proto); + felix_set_tag_protocol(ds, cpu, old_proto, true); return err; } @@ -592,23 +728,6 @@ static int felix_fdb_dump(struct dsa_switch *ds, int port, return ocelot_fdb_dump(ocelot, port, cb, data); } -/* Translate the DSA database API into the ocelot switch library API, - * which uses VID 0 for all ports that aren't part of a bridge, - * and expects the bridge_dev to be NULL in that case. - */ -static struct net_device *felix_classify_db(struct dsa_db db) -{ - switch (db.type) { - case DSA_DB_PORT: - case DSA_DB_LAG: - return NULL; - case DSA_DB_BRIDGE: - return db.bridge.dev; - default: - return ERR_PTR(-EOPNOTSUPP); - } -} - static int felix_fdb_add(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid, struct dsa_db db) @@ -1260,7 +1379,7 @@ static int felix_setup(struct dsa_switch *ds) /* The initial tag protocol is NPI which always returns 0, so * there's no real point in checking for errors. */ - felix_set_tag_protocol(ds, dp->index, felix->tag_proto); + felix_set_tag_protocol(ds, dp->index, felix->tag_proto, false); break; } diff --git a/include/net/dsa.h b/include/net/dsa.h index cfedcfb86350..71cc363dbbd4 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -1219,6 +1219,13 @@ 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); + /* Keep inline for faster access in hot path */ static inline bool netdev_uses_dsa(const struct net_device *dev) { diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index c43f7446a75d..06d5de28a43e 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -467,6 +467,46 @@ 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; + + 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; + + 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); + static int __init dsa_init_module(void) { int rc; |