aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/dsa/ocelot/felix.c90
-rw-r--r--include/net/dsa.h6
-rw-r--r--net/dsa/dsa.c60
-rw-r--r--net/dsa/dsa2.c31
-rw-r--r--net/dsa/dsa_priv.h2
-rw-r--r--net/dsa/slave.c7
-rw-r--r--net/dsa/switch.c18
7 files changed, 134 insertions, 80 deletions
diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
index 7cc67097948b..35b436a491e1 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -460,7 +460,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, bool change)
+static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu)
{
struct ocelot *ocelot = ds->priv;
struct dsa_port *dp;
@@ -488,19 +488,15 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu, bool change)
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_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 = dsa_port_walk_mdbs(ds, cpu, felix_migrate_mdbs_to_tag_8021q_port);
+ if (err)
+ goto out_migrate_fdbs;
- felix_migrate_flood_to_tag_8021q_port(ds, cpu);
- }
+ felix_migrate_flood_to_tag_8021q_port(ds, cpu);
err = felix_update_trapping_destinations(ds, true);
if (err)
@@ -518,13 +514,10 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu, bool change)
return 0;
out_migrate_flood:
- if (change)
- felix_migrate_flood_to_npi_port(ds, cpu);
- if (change)
- dsa_port_walk_mdbs(ds, cpu, felix_migrate_mdbs_to_npi_port);
+ felix_migrate_flood_to_npi_port(ds, cpu);
+ 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);
+ dsa_port_walk_fdbs(ds, cpu, felix_migrate_fdbs_to_npi_port);
out_tag_8021q_unregister:
dsa_tag_8021q_unregister(ds);
return err;
@@ -599,33 +592,27 @@ 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, bool change)
+static int felix_setup_tag_npi(struct dsa_switch *ds, int cpu)
{
struct ocelot *ocelot = ds->priv;
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_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;
+ err = dsa_port_walk_mdbs(ds, cpu, felix_migrate_mdbs_to_npi_port);
+ if (err)
+ goto out_migrate_fdbs;
- felix_migrate_flood_to_npi_port(ds, cpu);
- }
+ felix_migrate_flood_to_npi_port(ds, cpu);
felix_npi_port_init(ocelot, cpu);
return 0;
out_migrate_fdbs:
- if (change)
- dsa_port_walk_fdbs(ds, cpu,
- felix_migrate_fdbs_to_tag_8021q_port);
+ dsa_port_walk_fdbs(ds, cpu, felix_migrate_fdbs_to_tag_8021q_port);
return err;
}
@@ -638,17 +625,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, bool change)
+ enum dsa_tag_protocol proto)
{
int err;
switch (proto) {
case DSA_TAG_PROTO_SEVILLE:
case DSA_TAG_PROTO_OCELOT:
- err = felix_setup_tag_npi(ds, cpu, change);
+ err = felix_setup_tag_npi(ds, cpu);
break;
case DSA_TAG_PROTO_OCELOT_8021Q:
- err = felix_setup_tag_8021q(ds, cpu, change);
+ err = felix_setup_tag_8021q(ds, cpu);
break;
default:
err = -EPROTONOSUPPORT;
@@ -692,9 +679,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, true);
+ err = felix_set_tag_protocol(ds, cpu, proto);
if (err) {
- felix_set_tag_protocol(ds, cpu, old_proto, true);
+ felix_set_tag_protocol(ds, cpu, old_proto);
return err;
}
@@ -752,6 +739,10 @@ static int felix_fdb_add(struct dsa_switch *ds, int port,
if (IS_ERR(bridge_dev))
return PTR_ERR(bridge_dev);
+ if (dsa_is_cpu_port(ds, port) && !bridge_dev &&
+ dsa_fdb_present_in_other_db(ds, port, addr, vid, db))
+ return 0;
+
return ocelot_fdb_add(ocelot, port, addr, vid, bridge_dev);
}
@@ -765,6 +756,10 @@ static int felix_fdb_del(struct dsa_switch *ds, int port,
if (IS_ERR(bridge_dev))
return PTR_ERR(bridge_dev);
+ if (dsa_is_cpu_port(ds, port) && !bridge_dev &&
+ dsa_fdb_present_in_other_db(ds, port, addr, vid, db))
+ return 0;
+
return ocelot_fdb_del(ocelot, port, addr, vid, bridge_dev);
}
@@ -804,6 +799,10 @@ static int felix_mdb_add(struct dsa_switch *ds, int port,
if (IS_ERR(bridge_dev))
return PTR_ERR(bridge_dev);
+ if (dsa_is_cpu_port(ds, port) && !bridge_dev &&
+ dsa_mdb_present_in_other_db(ds, port, mdb, db))
+ return 0;
+
return ocelot_port_mdb_add(ocelot, port, mdb, bridge_dev);
}
@@ -817,6 +816,10 @@ static int felix_mdb_del(struct dsa_switch *ds, int port,
if (IS_ERR(bridge_dev))
return PTR_ERR(bridge_dev);
+ if (dsa_is_cpu_port(ds, port) && !bridge_dev &&
+ dsa_mdb_present_in_other_db(ds, port, mdb, db))
+ return 0;
+
return ocelot_port_mdb_del(ocelot, port, mdb, bridge_dev);
}
@@ -1356,6 +1359,7 @@ static int felix_setup(struct dsa_switch *ds)
{
struct ocelot *ocelot = ds->priv;
struct felix *felix = ocelot_to_felix(ocelot);
+ unsigned long cpu_flood;
struct dsa_port *dp;
int err;
@@ -1393,7 +1397,15 @@ 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, false);
+ felix_set_tag_protocol(ds, dp->index, felix->tag_proto);
+
+ /* Start off with flooding disabled towards the NPI port
+ * (actually CPU port module).
+ */
+ cpu_flood = ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports));
+ ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_UC);
+ ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_MC);
+
break;
}
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 759479fe8573..9d16505fc0e2 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -1227,6 +1227,12 @@ typedef int dsa_fdb_walk_cb_t(struct dsa_switch *ds, int port,
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);
+bool dsa_mdb_present_in_other_db(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_mdb *mdb,
+ struct dsa_db db);
/* 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 fe971a2c15cd..89c6c86e746f 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -507,6 +507,66 @@ int dsa_port_walk_mdbs(struct dsa_switch *ds, int port, dsa_fdb_walk_cb_t cb)
}
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)
+ return false;
+
+ switch (a->type) {
+ case DSA_DB_PORT:
+ return a->dp == b->dp;
+ case DSA_DB_LAG:
+ return a->lag.dev == b->lag.dev;
+ case DSA_DB_BRIDGE:
+ return a->bridge.num == b->bridge.num;
+ default:
+ WARN_ON(1);
+ return false;
+ }
+}
+
+bool dsa_fdb_present_in_other_db(struct dsa_switch *ds, int port,
+ const unsigned char *addr, u16 vid,
+ struct dsa_db db)
+{
+ struct dsa_port *dp = dsa_to_port(ds, port);
+ struct dsa_mac_addr *a;
+
+ lockdep_assert_held(&dp->addr_lists_lock);
+
+ list_for_each_entry(a, &dp->fdbs, list) {
+ if (!ether_addr_equal(a->addr, addr) || a->vid != vid)
+ continue;
+
+ if (a->db.type == db.type && !dsa_db_equal(&a->db, &db))
+ return true;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(dsa_fdb_present_in_other_db);
+
+bool dsa_mdb_present_in_other_db(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_mdb *mdb,
+ struct dsa_db db)
+{
+ struct dsa_port *dp = dsa_to_port(ds, port);
+ struct dsa_mac_addr *a;
+
+ lockdep_assert_held(&dp->addr_lists_lock);
+
+ list_for_each_entry(a, &dp->mdbs, list) {
+ if (!ether_addr_equal(a->addr, mdb->addr) || a->vid != mdb->vid)
+ continue;
+
+ if (a->db.type == db.type && !dsa_db_equal(&a->db, &db))
+ return true;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(dsa_mdb_present_in_other_db);
+
static int __init dsa_init_module(void)
{
int rc;
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index d5f21a770689..39e696185720 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -457,12 +457,6 @@ static int dsa_port_setup(struct dsa_port *dp)
if (dp->setup)
return 0;
- mutex_init(&dp->addr_lists_lock);
- mutex_init(&dp->vlans_lock);
- INIT_LIST_HEAD(&dp->fdbs);
- INIT_LIST_HEAD(&dp->mdbs);
- INIT_LIST_HEAD(&dp->vlans);
-
if (ds->ops->port_setup) {
err = ds->ops->port_setup(ds, dp->index);
if (err)
@@ -568,9 +562,7 @@ static void dsa_port_teardown(struct dsa_port *dp)
{
struct devlink_port *dlp = &dp->devlink_port;
struct dsa_switch *ds = dp->ds;
- struct dsa_mac_addr *a, *tmp;
struct net_device *slave;
- struct dsa_vlan *v, *n;
if (!dp->setup)
return;
@@ -601,21 +593,6 @@ static void dsa_port_teardown(struct dsa_port *dp)
break;
}
- list_for_each_entry_safe(a, tmp, &dp->fdbs, list) {
- list_del(&a->list);
- kfree(a);
- }
-
- list_for_each_entry_safe(a, tmp, &dp->mdbs, list) {
- list_del(&a->list);
- kfree(a);
- }
-
- list_for_each_entry_safe(v, n, &dp->vlans, list) {
- list_del(&v->list);
- kfree(v);
- }
-
dp->setup = false;
}
@@ -1374,6 +1351,11 @@ static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index)
dp->ds = ds;
dp->index = index;
+ mutex_init(&dp->addr_lists_lock);
+ mutex_init(&dp->vlans_lock);
+ INIT_LIST_HEAD(&dp->fdbs);
+ INIT_LIST_HEAD(&dp->mdbs);
+ INIT_LIST_HEAD(&dp->vlans);
INIT_LIST_HEAD(&dp->list);
list_add_tail(&dp->list, &dst->ports);
@@ -1712,6 +1694,9 @@ static void dsa_switch_release_ports(struct dsa_switch *ds)
struct dsa_port *dp, *next;
dsa_switch_for_each_port_safe(dp, next, ds) {
+ WARN_ON(!list_empty(&dp->fdbs));
+ WARN_ON(!list_empty(&dp->mdbs));
+ WARN_ON(!list_empty(&dp->vlans));
list_del(&dp->list);
kfree(dp);
}
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index c3c7491ace72..f20bdd8ea0a8 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -182,6 +182,8 @@ const struct dsa_device_ops *dsa_tag_driver_get(int tag_protocol);
void dsa_tag_driver_put(const struct dsa_device_ops *ops);
const struct dsa_device_ops *dsa_find_tagger_by_name(const char *buf);
+bool dsa_db_equal(const struct dsa_db *a, const struct dsa_db *b);
+
bool dsa_schedule_work(struct work_struct *work);
const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops);
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 42436ac6993b..a61a7c54af20 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -305,6 +305,12 @@ static int dsa_slave_set_mac_address(struct net_device *dev, void *a)
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
+ /* If the port is down, the address isn't synced yet to hardware or
+ * to the DSA master, so there is nothing to change.
+ */
+ if (!(dev->flags & IFF_UP))
+ goto out_change_dev_addr;
+
if (dsa_switch_supports_uc_filtering(ds)) {
err = dsa_port_standalone_host_fdb_add(dp, addr->sa_data, 0);
if (err)
@@ -323,6 +329,7 @@ static int dsa_slave_set_mac_address(struct net_device *dev, void *a)
if (dsa_switch_supports_uc_filtering(ds))
dsa_port_standalone_host_fdb_del(dp, dev->dev_addr, 0);
+out_change_dev_addr:
eth_hw_addr_set(dev, addr->sa_data);
return 0;
diff --git a/net/dsa/switch.c b/net/dsa/switch.c
index 327d66bf7b47..d25cd1da3eb3 100644
--- a/net/dsa/switch.c
+++ b/net/dsa/switch.c
@@ -212,24 +212,6 @@ static bool dsa_port_host_address_match(struct dsa_port *dp,
return false;
}
-static bool dsa_db_equal(const struct dsa_db *a, const struct dsa_db *b)
-{
- if (a->type != b->type)
- return false;
-
- switch (a->type) {
- case DSA_DB_PORT:
- return a->dp == b->dp;
- case DSA_DB_LAG:
- return a->lag.dev == b->lag.dev;
- case DSA_DB_BRIDGE:
- return a->bridge.num == b->bridge.num;
- default:
- WARN_ON(1);
- return false;
- }
-}
-
static struct dsa_mac_addr *dsa_mac_addr_find(struct list_head *addr_list,
const unsigned char *addr, u16 vid,
struct dsa_db db)