]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
net: prestera: flower template support
authorVolodymyr Mytnyk <vmytnyk@marvell.com>
Wed, 15 Dec 2021 10:07:30 +0000 (12:07 +0200)
committerDavid S. Miller <davem@davemloft.net>
Thu, 16 Dec 2021 10:52:53 +0000 (10:52 +0000)
Add user template explicit support. At this moment, max
TCAM rule size is utilized for all rules, doesn't matter
which and how much flower matches are provided by user. It
means that some of TCAM space is wasted, which impacts
the number of filters that can be offloaded.

Introducing the template, allows to have more HW offloaded
filters by specifying the template explicitly.

Example:
  tc qd add dev PORT clsact
  tc chain add dev PORT ingress protocol ip \
    flower dst_ip 0.0.0.0/16
  tc filter add dev PORT ingress protocol ip \
    flower skip_sw dst_ip 1.2.3.4/16 action drop

NOTE: chain 0 is the default chain id for "tc chain" & "tc filter"
      command, so it is omitted in the example above.

This patch adds only template support for default chain 0 suppoerted
by prestera driver at this moment. Chains are not supported yet,
and will be added later.

Signed-off-by: Volodymyr Mytnyk <vmytnyk@marvell.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/marvell/prestera/prestera_acl.c
drivers/net/ethernet/marvell/prestera/prestera_acl.h
drivers/net/ethernet/marvell/prestera/prestera_flow.c
drivers/net/ethernet/marvell/prestera/prestera_flow.h
drivers/net/ethernet/marvell/prestera/prestera_flower.c
drivers/net/ethernet/marvell/prestera/prestera_flower.h

index fc7f2fedafd7c598c9ebdb1fd02820dad72466c7..f0d9f592173bd503042e97f72f7a23b2c534dd53 100644 (file)
@@ -88,8 +88,8 @@ prestera_acl_ruleset_create(struct prestera_acl *acl,
                            struct prestera_flow_block *block)
 {
        struct prestera_acl_ruleset *ruleset;
+       u32 uid = 0;
        int err;
-       u32 uid;
 
        ruleset = kzalloc(sizeof(*ruleset), GFP_KERNEL);
        if (!ruleset)
@@ -125,6 +125,12 @@ err_rhashtable_init:
        return ERR_PTR(err);
 }
 
+void prestera_acl_ruleset_keymask_set(struct prestera_acl_ruleset *ruleset,
+                                     void *keymask)
+{
+       ruleset->keymask = kmemdup(keymask, ACL_KEYMASK_SIZE, GFP_KERNEL);
+}
+
 int prestera_acl_ruleset_offload(struct prestera_acl_ruleset *ruleset)
 {
        u32 vtcam_id;
@@ -556,6 +562,49 @@ err_kzalloc:
        return NULL;
 }
 
+static int __prestera_acl_vtcam_id_try_fit(struct prestera_acl *acl, u8 lookup,
+                                          void *keymask, u32 *vtcam_id)
+{
+       struct prestera_acl_vtcam *vtcam;
+       int i;
+
+       list_for_each_entry(vtcam, &acl->vtcam_list, list) {
+               if (lookup != vtcam->lookup)
+                       continue;
+
+               if (!keymask && !vtcam->is_keymask_set)
+                       goto vtcam_found;
+
+               if (!(keymask && vtcam->is_keymask_set))
+                       continue;
+
+               /* try to fit with vtcam keymask */
+               for (i = 0; i < __PRESTERA_ACL_RULE_MATCH_TYPE_MAX; i++) {
+                       __be32 __keymask = ((__be32 *)keymask)[i];
+
+                       if (!__keymask)
+                               /* vtcam keymask in not interested */
+                               continue;
+
+                       if (__keymask & ~vtcam->keymask[i])
+                               /* keymask does not fit the vtcam keymask */
+                               break;
+               }
+
+               if (i == __PRESTERA_ACL_RULE_MATCH_TYPE_MAX)
+                       /* keymask fits vtcam keymask, return it */
+                       goto vtcam_found;
+       }
+
+       /* nothing is found */
+       return -ENOENT;
+
+vtcam_found:
+       refcount_inc(&vtcam->refcount);
+       *vtcam_id = vtcam->id;
+       return 0;
+}
+
 int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup,
                              void *keymask, u32 *vtcam_id)
 {
@@ -592,7 +641,14 @@ int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup,
                                       PRESTERA_HW_VTCAM_DIR_INGRESS);
        if (err) {
                kfree(vtcam);
-               return err;
+
+               /* cannot create new, try to fit into existing vtcam */
+               if (__prestera_acl_vtcam_id_try_fit(acl, lookup,
+                                                   keymask, &new_vtcam_id))
+                       return err;
+
+               *vtcam_id = new_vtcam_id;
+               return 0;
        }
 
        vtcam->id = new_vtcam_id;
index 4e6006b4531f6e0212e8da56c552af74c2208381..40f6c1d961faa3dccd8bf297e66de553acb834c5 100644 (file)
@@ -151,6 +151,8 @@ prestera_acl_ruleset_get(struct prestera_acl *acl,
 struct prestera_acl_ruleset *
 prestera_acl_ruleset_lookup(struct prestera_acl *acl,
                            struct prestera_flow_block *block);
+void prestera_acl_ruleset_keymask_set(struct prestera_acl_ruleset *ruleset,
+                                     void *keymask);
 bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset);
 int prestera_acl_ruleset_offload(struct prestera_acl_ruleset *ruleset);
 void prestera_acl_ruleset_put(struct prestera_acl_ruleset *ruleset);
index 94a1feb3d9e144e8da4e85ad97181fa8d5fd78f5..d849f046ece790934355902fa33090120753aa5b 100644 (file)
@@ -40,6 +40,11 @@ static int prestera_flow_block_flower_cb(struct prestera_flow_block *block,
                return 0;
        case FLOW_CLS_STATS:
                return prestera_flower_stats(block, f);
+       case FLOW_CLS_TMPLT_CREATE:
+               return prestera_flower_tmplt_create(block, f);
+       case FLOW_CLS_TMPLT_DESTROY:
+               prestera_flower_tmplt_destroy(block, f);
+               return 0;
        default:
                return -EOPNOTSUPP;
        }
@@ -64,6 +69,8 @@ static void prestera_flow_block_destroy(void *cb_priv)
 {
        struct prestera_flow_block *block = cb_priv;
 
+       prestera_flower_template_cleanup(block);
+
        WARN_ON(!list_empty(&block->binding_list));
 
        kfree(block);
index 5863acf0600594d2b6d36857e160595fe4c33fb0..1ea5b745bf7253adda57796c8640a7886ae90153 100644 (file)
@@ -8,6 +8,7 @@
 
 struct prestera_port;
 struct prestera_switch;
+struct prestera_flower_template;
 
 struct prestera_flow_block_binding {
        struct list_head list;
@@ -18,10 +19,11 @@ struct prestera_flow_block_binding {
 struct prestera_flow_block {
        struct list_head binding_list;
        struct prestera_switch *sw;
-       unsigned int rule_count;
        struct net *net;
        struct prestera_acl_ruleset *ruleset_zero;
        struct flow_block_cb *block_cb;
+       struct prestera_flower_template *tmplt;
+       unsigned int rule_count;
 };
 
 int prestera_flow_block_setup(struct prestera_port *port,
index c1dc4e49b07f10584d0682483b686648dc46ce58..19c1417fd05fd944dd7b894936253bd62c9425aa 100644 (file)
@@ -6,6 +6,21 @@
 #include "prestera_flow.h"
 #include "prestera_flower.h"
 
+struct prestera_flower_template {
+       struct prestera_acl_ruleset *ruleset;
+};
+
+void prestera_flower_template_cleanup(struct prestera_flow_block *block)
+{
+       if (block->tmplt) {
+               /* put the reference to the ruleset kept in create */
+               prestera_acl_ruleset_put(block->tmplt->ruleset);
+               kfree(block->tmplt);
+               block->tmplt = NULL;
+               return;
+       }
+}
+
 static int prestera_flower_parse_actions(struct prestera_flow_block *block,
                                         struct prestera_acl_rule *rule,
                                         struct flow_action *flow_action,
@@ -310,6 +325,61 @@ void prestera_flower_destroy(struct prestera_flow_block *block,
 
 }
 
+int prestera_flower_tmplt_create(struct prestera_flow_block *block,
+                                struct flow_cls_offload *f)
+{
+       struct prestera_flower_template *template;
+       struct prestera_acl_ruleset *ruleset;
+       struct prestera_acl_rule rule;
+       int err;
+
+       memset(&rule, 0, sizeof(rule));
+       err = prestera_flower_parse(block, &rule, f);
+       if (err)
+               return err;
+
+       template = kmalloc(sizeof(*template), GFP_KERNEL);
+       if (!template) {
+               err = -ENOMEM;
+               goto err_malloc;
+       }
+
+       prestera_acl_rule_keymask_pcl_id_set(&rule, 0);
+       ruleset = prestera_acl_ruleset_get(block->sw->acl, block);
+       if (IS_ERR_OR_NULL(ruleset)) {
+               err = -EINVAL;
+               goto err_ruleset_get;
+       }
+
+       /* preserve keymask/template to this ruleset */
+       prestera_acl_ruleset_keymask_set(ruleset, rule.re_key.match.mask);
+
+       /* skip error, as it is not possible to reject template operation,
+        * so, keep the reference to the ruleset for rules to be added
+        * to that ruleset later. In case of offload fail, the ruleset
+        * will be offloaded again during adding a new rule. Also,
+        * unlikly possble that ruleset is already offloaded at this staage.
+        */
+       prestera_acl_ruleset_offload(ruleset);
+
+       /* keep the reference to the ruleset */
+       template->ruleset = ruleset;
+       block->tmplt = template;
+       return 0;
+
+err_ruleset_get:
+       kfree(template);
+err_malloc:
+       NL_SET_ERR_MSG_MOD(f->common.extack, "Create chain template failed");
+       return err;
+}
+
+void prestera_flower_tmplt_destroy(struct prestera_flow_block *block,
+                                  struct flow_cls_offload *f)
+{
+       prestera_flower_template_cleanup(block);
+}
+
 int prestera_flower_stats(struct prestera_flow_block *block,
                          struct flow_cls_offload *f)
 {
index c6182473efa5227512b7f13a71a8ba8277457303..dc3aa4280e9f07e02a03bf7ee266799f1d786eb6 100644 (file)
@@ -15,5 +15,10 @@ void prestera_flower_destroy(struct prestera_flow_block *block,
                             struct flow_cls_offload *f);
 int prestera_flower_stats(struct prestera_flow_block *block,
                          struct flow_cls_offload *f);
+int prestera_flower_tmplt_create(struct prestera_flow_block *block,
+                                struct flow_cls_offload *f);
+void prestera_flower_tmplt_destroy(struct prestera_flow_block *block,
+                                  struct flow_cls_offload *f);
+void prestera_flower_template_cleanup(struct prestera_flow_block *block);
 
 #endif /* _PRESTERA_FLOWER_H_ */