generic: 5.15: qca8k: add kernel version tag on backport patch
[openwrt/staging/svanheule.git] / target / linux / generic / backport-5.15 / 761-v5.17-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch
diff --git a/target/linux/generic/backport-5.15/761-v5.17-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch b/target/linux/generic/backport-5.15/761-v5.17-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch
new file mode 100644 (file)
index 0000000..8b97939
--- /dev/null
@@ -0,0 +1,142 @@
+From ba8f870dfa635113ce6e8095a5eb1835ecde2e9e Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:48 +0100
+Subject: net: dsa: qca8k: add support for mdb_add/del
+
+Add support for mdb add/del function. The ARL table is used to insert
+the rule. The rule will be searched, deleted and reinserted with the
+port mask updated. The function will check if the rule has to be updated
+or insert directly with no deletion of the old rule.
+If every port is removed from the port mask, the rule is removed.
+The rule is set STATIC in the ARL table (aka it doesn't age) to not be
+flushed by fast age function.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 99 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -436,6 +436,81 @@ qca8k_fdb_flush(struct qca8k_priv *priv)
+ }
+ static int
++qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask,
++                          const u8 *mac, u16 vid)
++{
++      struct qca8k_fdb fdb = { 0 };
++      int ret;
++
++      mutex_lock(&priv->reg_mutex);
++
++      qca8k_fdb_write(priv, vid, 0, mac, 0);
++      ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
++      if (ret < 0)
++              goto exit;
++
++      ret = qca8k_fdb_read(priv, &fdb);
++      if (ret < 0)
++              goto exit;
++
++      /* Rule exist. Delete first */
++      if (!fdb.aging) {
++              ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
++              if (ret)
++                      goto exit;
++      }
++
++      /* Add port to fdb portmask */
++      fdb.port_mask |= port_mask;
++
++      qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
++      ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
++
++exit:
++      mutex_unlock(&priv->reg_mutex);
++      return ret;
++}
++
++static int
++qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask,
++                       const u8 *mac, u16 vid)
++{
++      struct qca8k_fdb fdb = { 0 };
++      int ret;
++
++      mutex_lock(&priv->reg_mutex);
++
++      qca8k_fdb_write(priv, vid, 0, mac, 0);
++      ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
++      if (ret < 0)
++              goto exit;
++
++      /* Rule doesn't exist. Why delete? */
++      if (!fdb.aging) {
++              ret = -EINVAL;
++              goto exit;
++      }
++
++      ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
++      if (ret)
++              goto exit;
++
++      /* Only port in the rule is this port. Don't re insert */
++      if (fdb.port_mask == port_mask)
++              goto exit;
++
++      /* Remove port from port mask */
++      fdb.port_mask &= ~port_mask;
++
++      qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
++      ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
++
++exit:
++      mutex_unlock(&priv->reg_mutex);
++      return ret;
++}
++
++static int
+ qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
+ {
+       u32 reg;
+@@ -1949,6 +2024,28 @@ qca8k_port_fdb_dump(struct dsa_switch *d
+ }
+ static int
++qca8k_port_mdb_add(struct dsa_switch *ds, int port,
++                 const struct switchdev_obj_port_mdb *mdb)
++{
++      struct qca8k_priv *priv = ds->priv;
++      const u8 *addr = mdb->addr;
++      u16 vid = mdb->vid;
++
++      return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid);
++}
++
++static int
++qca8k_port_mdb_del(struct dsa_switch *ds, int port,
++                 const struct switchdev_obj_port_mdb *mdb)
++{
++      struct qca8k_priv *priv = ds->priv;
++      const u8 *addr = mdb->addr;
++      u16 vid = mdb->vid;
++
++      return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid);
++}
++
++static int
+ qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+                         struct netlink_ext_ack *extack)
+ {
+@@ -2056,6 +2153,8 @@ static const struct dsa_switch_ops qca8k
+       .port_fdb_add           = qca8k_port_fdb_add,
+       .port_fdb_del           = qca8k_port_fdb_del,
+       .port_fdb_dump          = qca8k_port_fdb_dump,
++      .port_mdb_add           = qca8k_port_mdb_add,
++      .port_mdb_del           = qca8k_port_mdb_del,
+       .port_vlan_filtering    = qca8k_port_vlan_filtering,
+       .port_vlan_add          = qca8k_port_vlan_add,
+       .port_vlan_del          = qca8k_port_vlan_del,