diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index e475186b70c7..35b436a491e1 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -739,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); } @@ -752,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); } @@ -791,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); } @@ -804,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); } 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/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/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)