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)
        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;
        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)
 {
                                       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;
 
 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);
 
                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;
        }
 {
        struct prestera_flow_block *block = cb_priv;
 
+       prestera_flower_template_cleanup(block);
+
        WARN_ON(!list_empty(&block->binding_list));
 
        kfree(block);
 
 
 struct prestera_port;
 struct prestera_switch;
+struct prestera_flower_template;
 
 struct prestera_flow_block_binding {
        struct list_head list;
 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,
 
 #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,
 
 }
 
+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)
 {
 
                             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_ */