net: microchip: sparx5: Add tc matchall filter and enable VCAP lookups
authorSteen Hegelund <steen.hegelund@microchip.com>
Wed, 9 Nov 2022 11:41:15 +0000 (12:41 +0100)
committerDavid S. Miller <davem@davemloft.net>
Fri, 11 Nov 2022 10:39:12 +0000 (10:39 +0000)
Use a tc matchall rule with a goto action to the VCAP specific chain to
enable the VCAP lookups.
If the matchall rule is removed the VCAP lookups will be disabled
again using its cookie as lookup to find the VCAP instance.

To enable the Sparx5 IS2 VCAP on eth0 you would use this command:

    tc filter add dev eth0 ingress prio 5 handle 5 matchall \
        skip_sw action goto chain 8000000

as the first lookup in IS2 has chain id 8000000

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/microchip/sparx5/Makefile
drivers/net/ethernet/microchip/sparx5/sparx5_tc.c
drivers/net/ethernet/microchip/sparx5/sparx5_tc.h
drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c [new file with mode: 0644]
drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c
drivers/net/ethernet/microchip/vcap/vcap_api.c
drivers/net/ethernet/microchip/vcap/vcap_api.h
drivers/net/ethernet/microchip/vcap/vcap_api_client.h

index 38adf917bc0966f25e40064d59dd09ea3762c25c..cff07b8841bdb95d7b9e1fb94cf79bc2d28d3e94 100644 (file)
@@ -9,7 +9,7 @@ sparx5-switch-y  := sparx5_main.o sparx5_packet.o \
  sparx5_netdev.o sparx5_phylink.o sparx5_port.o sparx5_mactable.o sparx5_vlan.o \
  sparx5_switchdev.o sparx5_calendar.o sparx5_ethtool.o sparx5_fdma.o \
  sparx5_ptp.o sparx5_pgid.o sparx5_tc.o sparx5_qos.o \
- sparx5_vcap_impl.o sparx5_vcap_ag_api.o sparx5_tc_flower.o
+ sparx5_vcap_impl.o sparx5_vcap_ag_api.o sparx5_tc_flower.o sparx5_tc_matchall.o
 
 sparx5-switch-$(CONFIG_SPARX5_DCB) += sparx5_dcb.o
 
index 9432251b83226bb741d7eef1398d4de56c844023..edd4c53dcce24f512d6bd6751edd66f4ff8be72f 100644 (file)
@@ -19,9 +19,14 @@ static int sparx5_tc_block_cb(enum tc_setup_type type,
 {
        struct net_device *ndev = cb_priv;
 
-       if (type == TC_SETUP_CLSFLOWER)
+       switch (type) {
+       case TC_SETUP_CLSMATCHALL:
+               return sparx5_tc_matchall(ndev, type_data, ingress);
+       case TC_SETUP_CLSFLOWER:
                return sparx5_tc_flower(ndev, type_data, ingress);
-       return -EOPNOTSUPP;
+       default:
+               return -EOPNOTSUPP;
+       }
 }
 
 static int sparx5_tc_block_cb_ingress(enum tc_setup_type type,
index 2b07a93fc9b72a89d792b99f60a87a130a03eca6..adab88e6b21fb4835bc4382e46b418a3d459f1f7 100644 (file)
@@ -8,6 +8,7 @@
 #define __SPARX5_TC_H__
 
 #include <net/flow_offload.h>
+#include <net/pkt_cls.h>
 #include <linux/netdevice.h>
 
 /* Controls how PORT_MASK is applied */
@@ -23,6 +24,10 @@ enum SPX5_PORT_MASK_MODE {
 int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type,
                         void *type_data);
 
+int sparx5_tc_matchall(struct net_device *ndev,
+                      struct tc_cls_matchall_offload *tmo,
+                      bool ingress);
+
 int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco,
                     bool ingress);
 
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c
new file mode 100644 (file)
index 0000000..30dd61e
--- /dev/null
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip VCAP API
+ *
+ * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include "sparx5_tc.h"
+#include "vcap_api.h"
+#include "vcap_api_client.h"
+#include "sparx5_main_regs.h"
+#include "sparx5_main.h"
+#include "sparx5_vcap_impl.h"
+
+static int sparx5_tc_matchall_replace(struct net_device *ndev,
+                                     struct tc_cls_matchall_offload *tmo,
+                                     bool ingress)
+{
+       struct sparx5_port *port = netdev_priv(ndev);
+       struct flow_action_entry *action;
+       struct sparx5 *sparx5;
+       int err;
+
+       if (!flow_offload_has_one_action(&tmo->rule->action)) {
+               NL_SET_ERR_MSG_MOD(tmo->common.extack,
+                                  "Only one action per filter is supported");
+               return -EOPNOTSUPP;
+       }
+       action = &tmo->rule->action.entries[0];
+
+       sparx5 = port->sparx5;
+       switch (action->id) {
+       case FLOW_ACTION_GOTO:
+               err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev,
+                                         action->chain_index, tmo->cookie,
+                                         true);
+               if (err == -EFAULT) {
+                       NL_SET_ERR_MSG_MOD(tmo->common.extack,
+                                          "Unsupported goto chain");
+                       return -EOPNOTSUPP;
+               }
+               if (err == -EADDRINUSE) {
+                       NL_SET_ERR_MSG_MOD(tmo->common.extack,
+                                          "VCAP already enabled");
+                       return -EOPNOTSUPP;
+               }
+               if (err) {
+                       NL_SET_ERR_MSG_MOD(tmo->common.extack,
+                                          "Could not enable VCAP lookups");
+                       return err;
+               }
+               break;
+       default:
+               NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action");
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+static int sparx5_tc_matchall_destroy(struct net_device *ndev,
+                                     struct tc_cls_matchall_offload *tmo,
+                                     bool ingress)
+{
+       struct sparx5_port *port = netdev_priv(ndev);
+       struct sparx5 *sparx5;
+       int err;
+
+       sparx5 = port->sparx5;
+       if (!tmo->rule && tmo->cookie) {
+               err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev, 0,
+                                         tmo->cookie, false);
+               if (err)
+                       return err;
+               return 0;
+       }
+       NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action");
+       return -EOPNOTSUPP;
+}
+
+int sparx5_tc_matchall(struct net_device *ndev,
+                      struct tc_cls_matchall_offload *tmo,
+                      bool ingress)
+{
+       if (!tc_cls_can_offload_and_chain0(ndev, &tmo->common)) {
+               NL_SET_ERR_MSG_MOD(tmo->common.extack,
+                                  "Only chain zero is supported");
+               return -EOPNOTSUPP;
+       }
+
+       switch (tmo->command) {
+       case TC_CLSMATCHALL_REPLACE:
+               return sparx5_tc_matchall_replace(ndev, tmo, ingress);
+       case TC_CLSMATCHALL_DESTROY:
+               return sparx5_tc_matchall_destroy(ndev, tmo, ingress);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
index 642c27299e22f617fcf60552702371ba38583754..10bc56cd0045a64dae99b820554d57513cdfed13 100644 (file)
@@ -489,6 +489,28 @@ static int sparx5_port_info(struct net_device *ndev, enum vcap_type vtype,
        return 0;
 }
 
+/* Enable all lookups in the VCAP instance */
+static int sparx5_vcap_enable(struct net_device *ndev,
+                             struct vcap_admin *admin,
+                             bool enable)
+{
+       struct sparx5_port *port = netdev_priv(ndev);
+       struct sparx5 *sparx5;
+       int portno;
+
+       sparx5 = port->sparx5;
+       portno = port->portno;
+
+       /* For now we only consider IS2 */
+       if (enable)
+               spx5_wr(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0xf), sparx5,
+                       ANA_ACL_VCAP_S2_CFG(portno));
+       else
+               spx5_wr(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0), sparx5,
+                       ANA_ACL_VCAP_S2_CFG(portno));
+       return 0;
+}
+
 /* API callback operations: only IS2 is supported for now */
 static struct vcap_operations sparx5_vcap_ops = {
        .validate_keyset = sparx5_vcap_validate_keyset,
@@ -500,6 +522,7 @@ static struct vcap_operations sparx5_vcap_ops = {
        .update = sparx5_vcap_update,
        .move = sparx5_vcap_move,
        .port_info = sparx5_port_info,
+       .enable = sparx5_vcap_enable,
 };
 
 /* Enable lookups per port and set the keyset generation: only IS2 for now */
@@ -509,11 +532,6 @@ static void sparx5_vcap_port_key_selection(struct sparx5 *sparx5,
        int portno, lookup;
        u32 keysel;
 
-       /* enable all 4 lookups on all ports */
-       for (portno = 0; portno < SPX5_PORTS; ++portno)
-               spx5_wr(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0xf), sparx5,
-                       ANA_ACL_VCAP_S2_CFG(portno));
-
        /* all traffic types generate the MAC_ETYPE keyset for now in all
         * lookups on all ports
         */
@@ -566,6 +584,7 @@ sparx5_vcap_admin_alloc(struct sparx5 *sparx5, struct vcap_control *ctrl,
                return ERR_PTR(-ENOMEM);
        INIT_LIST_HEAD(&admin->list);
        INIT_LIST_HEAD(&admin->rules);
+       INIT_LIST_HEAD(&admin->enabled);
        admin->vtype = cfg->vtype;
        admin->vinst = cfg->vinst;
        admin->lookups = cfg->lookups;
index 73ec7744c21f47a16a2290a4ec095aa821f18085..b6ab6bae28c0ad6a9abe3e0608668a2e0de90eff 100644 (file)
@@ -44,6 +44,13 @@ struct vcap_stream_iter {
        const struct vcap_typegroup *tg; /* current typegroup */
 };
 
+/* Stores the filter cookie that enabled the port */
+struct vcap_enabled_port {
+       struct list_head list; /* for insertion in enabled ports list */
+       struct net_device *ndev;  /* the enabled port */
+       unsigned long cookie; /* filter that enabled the port */
+};
+
 static void vcap_iter_set(struct vcap_stream_iter *itr, int sw_width,
                          const struct vcap_typegroup *tg, u32 offset)
 {
@@ -516,7 +523,7 @@ static int vcap_api_check(struct vcap_control *ctrl)
            !ctrl->ops->add_default_fields || !ctrl->ops->cache_erase ||
            !ctrl->ops->cache_write || !ctrl->ops->cache_read ||
            !ctrl->ops->init || !ctrl->ops->update || !ctrl->ops->move ||
-           !ctrl->ops->port_info) {
+           !ctrl->ops->port_info || !ctrl->ops->enable) {
                pr_err("%s:%d: client operations are missing\n",
                       __func__, __LINE__);
                return -ENOENT;
@@ -1128,6 +1135,7 @@ EXPORT_SYMBOL_GPL(vcap_del_rule);
 /* Delete all rules in the VCAP instance */
 int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin)
 {
+       struct vcap_enabled_port *eport, *next_eport;
        struct vcap_rule_internal *ri, *next_ri;
        int ret = vcap_api_check(vctrl);
 
@@ -1139,6 +1147,13 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin)
                kfree(ri);
        }
        admin->last_used_addr = admin->last_valid_addr;
+
+       /* Remove list of enabled ports */
+       list_for_each_entry_safe(eport, next_eport, &admin->enabled, list) {
+               list_del(&eport->list);
+               kfree(eport);
+       }
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(vcap_del_rules);
@@ -1459,6 +1474,109 @@ void vcap_set_tc_exterr(struct flow_cls_offload *fco, struct vcap_rule *vrule)
 }
 EXPORT_SYMBOL_GPL(vcap_set_tc_exterr);
 
+/* Check if this port is already enabled for this VCAP instance */
+static bool vcap_is_enabled(struct vcap_admin *admin, struct net_device *ndev,
+                           unsigned long cookie)
+{
+       struct vcap_enabled_port *eport;
+
+       list_for_each_entry(eport, &admin->enabled, list)
+               if (eport->cookie == cookie || eport->ndev == ndev)
+                       return true;
+
+       return false;
+}
+
+/* Enable this port for this VCAP instance */
+static int vcap_enable(struct vcap_admin *admin, struct net_device *ndev,
+                      unsigned long cookie)
+{
+       struct vcap_enabled_port *eport;
+
+       eport = kzalloc(sizeof(*eport), GFP_KERNEL);
+       if (!eport)
+               return -ENOMEM;
+
+       eport->ndev = ndev;
+       eport->cookie = cookie;
+       list_add_tail(&eport->list, &admin->enabled);
+
+       return 0;
+}
+
+/* Disable this port for this VCAP instance */
+static int vcap_disable(struct vcap_admin *admin, struct net_device *ndev,
+                       unsigned long cookie)
+{
+       struct vcap_enabled_port *eport;
+
+       list_for_each_entry(eport, &admin->enabled, list) {
+               if (eport->cookie == cookie && eport->ndev == ndev) {
+                       list_del(&eport->list);
+                       kfree(eport);
+                       return 0;
+               }
+       }
+
+       return -ENOENT;
+}
+
+/* Find the VCAP instance that enabled the port using a specific filter */
+static struct vcap_admin *vcap_find_admin_by_cookie(struct vcap_control *vctrl,
+                                                   unsigned long cookie)
+{
+       struct vcap_enabled_port *eport;
+       struct vcap_admin *admin;
+
+       list_for_each_entry(admin, &vctrl->list, list)
+               list_for_each_entry(eport, &admin->enabled, list)
+                       if (eport->cookie == cookie)
+                               return admin;
+
+       return NULL;
+}
+
+/* Enable/Disable the VCAP instance lookups. Chain id 0 means disable */
+int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev,
+                       int chain_id, unsigned long cookie, bool enable)
+{
+       struct vcap_admin *admin;
+       int err;
+
+       err = vcap_api_check(vctrl);
+       if (err)
+               return err;
+
+       if (!ndev)
+               return -ENODEV;
+
+       if (chain_id)
+               admin = vcap_find_admin(vctrl, chain_id);
+       else
+               admin = vcap_find_admin_by_cookie(vctrl, cookie);
+       if (!admin)
+               return -ENOENT;
+
+       /* first instance and first chain */
+       if (admin->vinst || chain_id > admin->first_cid)
+               return -EFAULT;
+
+       err = vctrl->ops->enable(ndev, admin, enable);
+       if (err)
+               return err;
+
+       if (chain_id) {
+               if (vcap_is_enabled(admin, ndev, cookie))
+                       return -EADDRINUSE;
+               vcap_enable(admin, ndev, cookie);
+       } else {
+               vcap_disable(admin, ndev, cookie);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(vcap_enable_lookups);
+
 #ifdef CONFIG_VCAP_KUNIT_TEST
 #include "vcap_api_kunit.c"
 #endif
index eb2eae75c7e8f9f698cac692f4a69c98e89c57e6..bfb8ad5350742d2b03739e977dbf91753650b14e 100644 (file)
@@ -166,6 +166,7 @@ enum vcap_rule_error {
 struct vcap_admin {
        struct list_head list; /* for insertion in vcap_control */
        struct list_head rules; /* list of rules */
+       struct list_head enabled; /* list of enabled ports */
        enum vcap_type vtype;  /* type of vcap */
        int vinst; /* instance number within the same type */
        int first_cid; /* first chain id in this vcap */
@@ -255,6 +256,11 @@ struct vcap_operations {
                 int (*pf)(void *out, int arg, const char *fmt, ...),
                 void *out,
                 int arg);
+       /* enable/disable the lookups in a vcap instance */
+       int (*enable)
+               (struct net_device *ndev,
+                struct vcap_admin *admin,
+                bool enable);
 };
 
 /* VCAP API Client control interface */
index 077e49c4f3be9b7a3e93fa20af5f4fc53a4e9e03..0ea5ec96adc85e7b4cff3539ef94d21b50e6fb88 100644 (file)
@@ -143,6 +143,10 @@ enum vcap_bit {
        VCAP_BIT_1
 };
 
+/* Enable/Disable the VCAP instance lookups. Chain id 0 means disable */
+int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev,
+                       int chain_id, unsigned long cookie, bool enable);
+
 /* VCAP rule operations */
 /* Allocate a rule and fill in the basic information */
 struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl,