From 3bacfccdcb2d3268adaf66d2ff4024251a93b916 Mon Sep 17 00:00:00 2001 From: Casper Andersson Date: Mon, 21 Mar 2022 11:14:46 +0100 Subject: [PATCH] net: sparx5: Add mdb handlers Adds mdb handlers. Uses the PGID arbiter to find a free entry in the PGID table for the multicast group port mask. Signed-off-by: Casper Andersson Signed-off-by: David S. Miller --- .../ethernet/microchip/sparx5/sparx5_mactable.c | 33 ++++-- .../net/ethernet/microchip/sparx5/sparx5_main.h | 2 + .../ethernet/microchip/sparx5/sparx5_switchdev.c | 111 +++++++++++++++++++++ 3 files changed, 136 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c index 82b1b3c9a065..35abb3d0ce19 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c @@ -186,11 +186,11 @@ bool sparx5_mact_getnext(struct sparx5 *sparx5, return ret == 0; } -static int sparx5_mact_lookup(struct sparx5 *sparx5, - const unsigned char mac[ETH_ALEN], - u16 vid) +bool sparx5_mact_find(struct sparx5 *sparx5, + const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2) { int ret; + u32 cfg2; mutex_lock(&sparx5->lock); @@ -202,16 +202,29 @@ static int sparx5_mact_lookup(struct sparx5 *sparx5, sparx5, LRN_COMMON_ACCESS_CTRL); ret = sparx5_mact_wait_for_completion(sparx5); - if (ret) - goto out; - - ret = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET - (spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2)); + if (ret == 0) { + cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2); + if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2)) + *pcfg2 = cfg2; + else + ret = -ENOENT; + } -out: mutex_unlock(&sparx5->lock); - return ret; + return ret == 0; +} + +static int sparx5_mact_lookup(struct sparx5 *sparx5, + const unsigned char mac[ETH_ALEN], + u16 vid) +{ + u32 pcfg2; + + if (sparx5_mact_find(sparx5, mac, vid, &pcfg2)) + return 1; + + return 0; } int sparx5_mact_forget(struct sparx5 *sparx5, diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h index e97fa091c740..7a04b8f2a546 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h @@ -310,6 +310,8 @@ int sparx5_mact_learn(struct sparx5 *sparx5, int port, const unsigned char mac[ETH_ALEN], u16 vid); bool sparx5_mact_getnext(struct sparx5 *sparx5, unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2); +bool sparx5_mact_find(struct sparx5 *sparx5, + const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2); int sparx5_mact_forget(struct sparx5 *sparx5, const unsigned char mac[ETH_ALEN], u16 vid); int sparx5_add_mact_entry(struct sparx5 *sparx5, diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c index 8b69c72ff807..2d8e0b81c839 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c @@ -386,6 +386,109 @@ static int sparx5_handle_port_vlan_add(struct net_device *dev, v->flags & BRIDGE_VLAN_INFO_UNTAGGED); } +static int sparx5_handle_port_mdb_add(struct net_device *dev, + struct notifier_block *nb, + const struct switchdev_obj_port_mdb *v) +{ + struct sparx5_port *port = netdev_priv(dev); + struct sparx5 *spx5 = port->sparx5; + u16 pgid_idx, vid; + u32 mact_entry; + int res, err; + + /* When VLAN unaware the vlan value is not parsed and we receive vid 0. + * Fall back to bridge vid 1. + */ + if (!br_vlan_enabled(spx5->hw_bridge_dev)) + vid = 1; + else + vid = v->vid; + + res = sparx5_mact_find(spx5, v->addr, vid, &mact_entry); + + if (res) { + pgid_idx = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(mact_entry); + + /* MC_IDX has an offset of 65 in the PGID table. */ + pgid_idx += PGID_MCAST_START; + sparx5_pgid_update_mask(port, pgid_idx, true); + } else { + err = sparx5_pgid_alloc_mcast(spx5, &pgid_idx); + if (err) { + netdev_warn(dev, "multicast pgid table full\n"); + return err; + } + sparx5_pgid_update_mask(port, pgid_idx, true); + err = sparx5_mact_learn(spx5, pgid_idx, v->addr, vid); + if (err) { + netdev_warn(dev, "could not learn mac address %pM\n", v->addr); + sparx5_pgid_update_mask(port, pgid_idx, false); + return err; + } + } + + return 0; +} + +static int sparx5_mdb_del_entry(struct net_device *dev, + struct sparx5 *spx5, + const unsigned char mac[ETH_ALEN], + const u16 vid, + u16 pgid_idx) +{ + int err; + + err = sparx5_mact_forget(spx5, mac, vid); + if (err) { + netdev_warn(dev, "could not forget mac address %pM", mac); + return err; + } + err = sparx5_pgid_free(spx5, pgid_idx); + if (err) { + netdev_err(dev, "attempted to free already freed pgid\n"); + return err; + } + return 0; +} + +static int sparx5_handle_port_mdb_del(struct net_device *dev, + struct notifier_block *nb, + const struct switchdev_obj_port_mdb *v) +{ + struct sparx5_port *port = netdev_priv(dev); + struct sparx5 *spx5 = port->sparx5; + u16 pgid_idx, vid; + u32 mact_entry, res, pgid_entry[3]; + int err; + + if (!br_vlan_enabled(spx5->hw_bridge_dev)) + vid = 1; + else + vid = v->vid; + + res = sparx5_mact_find(spx5, v->addr, vid, &mact_entry); + + if (res) { + pgid_idx = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(mact_entry); + + /* MC_IDX has an offset of 65 in the PGID table. */ + pgid_idx += PGID_MCAST_START; + sparx5_pgid_update_mask(port, pgid_idx, false); + + pgid_entry[0] = spx5_rd(spx5, ANA_AC_PGID_CFG(pgid_idx)); + pgid_entry[1] = spx5_rd(spx5, ANA_AC_PGID_CFG1(pgid_idx)); + pgid_entry[2] = spx5_rd(spx5, ANA_AC_PGID_CFG2(pgid_idx)); + if (pgid_entry[0] == 0 && pgid_entry[1] == 0 && pgid_entry[2] == 0) { + /* No ports are in MC group. Remove entry */ + err = sparx5_mdb_del_entry(dev, spx5, v->addr, vid, pgid_idx); + if (err) + return err; + } + } + + return 0; +} + static int sparx5_handle_port_obj_add(struct net_device *dev, struct notifier_block *nb, struct switchdev_notifier_port_obj_info *info) @@ -398,6 +501,10 @@ static int sparx5_handle_port_obj_add(struct net_device *dev, err = sparx5_handle_port_vlan_add(dev, nb, SWITCHDEV_OBJ_PORT_VLAN(obj)); break; + case SWITCHDEV_OBJ_ID_PORT_MDB: + err = sparx5_handle_port_mdb_add(dev, nb, + SWITCHDEV_OBJ_PORT_MDB(obj)); + break; default: err = -EOPNOTSUPP; break; @@ -446,6 +553,10 @@ static int sparx5_handle_port_obj_del(struct net_device *dev, err = sparx5_handle_port_vlan_del(dev, nb, SWITCHDEV_OBJ_PORT_VLAN(obj)->vid); break; + case SWITCHDEV_OBJ_ID_PORT_MDB: + err = sparx5_handle_port_mdb_del(dev, nb, + SWITCHDEV_OBJ_PORT_MDB(obj)); + break; default: err = -EOPNOTSUPP; break; -- 2.11.0