Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
0002 /* Copyright (c) 2020-2021 Marvell International Ltd. All rights reserved */
0003 
0004 #include <linux/rhashtable.h>
0005 
0006 #include "prestera_acl.h"
0007 #include "prestera_flow.h"
0008 #include "prestera_hw.h"
0009 #include "prestera.h"
0010 
0011 #define ACL_KEYMASK_SIZE    \
0012     (sizeof(__be32) * __PRESTERA_ACL_RULE_MATCH_TYPE_MAX)
0013 
0014 struct prestera_acl {
0015     struct prestera_switch *sw;
0016     struct list_head vtcam_list;
0017     struct list_head rules;
0018     struct rhashtable ruleset_ht;
0019     struct rhashtable acl_rule_entry_ht;
0020     struct idr uid;
0021 };
0022 
0023 struct prestera_acl_ruleset_ht_key {
0024     struct prestera_flow_block *block;
0025     u32 chain_index;
0026 };
0027 
0028 struct prestera_acl_rule_entry {
0029     struct rhash_head ht_node;
0030     struct prestera_acl_rule_entry_key key;
0031     u32 hw_id;
0032     u32 vtcam_id;
0033     struct {
0034         struct {
0035             u8 valid:1;
0036         } accept, drop, trap;
0037         struct {
0038             u8 valid:1;
0039             struct prestera_acl_action_police i;
0040         } police;
0041         struct {
0042             struct prestera_acl_action_jump i;
0043             u8 valid:1;
0044         } jump;
0045         struct {
0046             u32 id;
0047             struct prestera_counter_block *block;
0048         } counter;
0049     };
0050 };
0051 
0052 struct prestera_acl_ruleset {
0053     struct rhash_head ht_node; /* Member of acl HT */
0054     struct prestera_acl_ruleset_ht_key ht_key;
0055     struct rhashtable rule_ht;
0056     struct prestera_acl *acl;
0057     unsigned long rule_count;
0058     refcount_t refcount;
0059     void *keymask;
0060     u32 vtcam_id;
0061     u32 index;
0062     u16 pcl_id;
0063     bool offload;
0064     bool ingress;
0065 };
0066 
0067 struct prestera_acl_vtcam {
0068     struct list_head list;
0069     __be32 keymask[__PRESTERA_ACL_RULE_MATCH_TYPE_MAX];
0070     refcount_t refcount;
0071     u32 id;
0072     bool is_keymask_set;
0073     u8 lookup;
0074     u8 direction;
0075 };
0076 
0077 static const struct rhashtable_params prestera_acl_ruleset_ht_params = {
0078     .key_len = sizeof(struct prestera_acl_ruleset_ht_key),
0079     .key_offset = offsetof(struct prestera_acl_ruleset, ht_key),
0080     .head_offset = offsetof(struct prestera_acl_ruleset, ht_node),
0081     .automatic_shrinking = true,
0082 };
0083 
0084 static const struct rhashtable_params prestera_acl_rule_ht_params = {
0085     .key_len = sizeof(unsigned long),
0086     .key_offset = offsetof(struct prestera_acl_rule, cookie),
0087     .head_offset = offsetof(struct prestera_acl_rule, ht_node),
0088     .automatic_shrinking = true,
0089 };
0090 
0091 static const struct rhashtable_params __prestera_acl_rule_entry_ht_params = {
0092     .key_offset  = offsetof(struct prestera_acl_rule_entry, key),
0093     .head_offset = offsetof(struct prestera_acl_rule_entry, ht_node),
0094     .key_len     = sizeof(struct prestera_acl_rule_entry_key),
0095     .automatic_shrinking = true,
0096 };
0097 
0098 int prestera_acl_chain_to_client(u32 chain_index, bool ingress, u32 *client)
0099 {
0100     static const u32 ingress_client_map[] = {
0101         PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_0,
0102         PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_1,
0103         PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_2
0104     };
0105 
0106     if (!ingress) {
0107         /* prestera supports only one chain on egress */
0108         if (chain_index > 0)
0109             return -EINVAL;
0110 
0111         *client = PRESTERA_HW_COUNTER_CLIENT_EGRESS_LOOKUP;
0112         return 0;
0113     }
0114 
0115     if (chain_index >= ARRAY_SIZE(ingress_client_map))
0116         return -EINVAL;
0117 
0118     *client = ingress_client_map[chain_index];
0119     return 0;
0120 }
0121 
0122 static bool prestera_acl_chain_is_supported(u32 chain_index, bool ingress)
0123 {
0124     if (!ingress)
0125         /* prestera supports only one chain on egress */
0126         return chain_index == 0;
0127 
0128     return (chain_index & ~PRESTERA_ACL_CHAIN_MASK) == 0;
0129 }
0130 
0131 static struct prestera_acl_ruleset *
0132 prestera_acl_ruleset_create(struct prestera_acl *acl,
0133                 struct prestera_flow_block *block,
0134                 u32 chain_index)
0135 {
0136     struct prestera_acl_ruleset *ruleset;
0137     u32 uid = 0;
0138     int err;
0139 
0140     if (!prestera_acl_chain_is_supported(chain_index, block->ingress))
0141         return ERR_PTR(-EINVAL);
0142 
0143     ruleset = kzalloc(sizeof(*ruleset), GFP_KERNEL);
0144     if (!ruleset)
0145         return ERR_PTR(-ENOMEM);
0146 
0147     ruleset->acl = acl;
0148     ruleset->ingress = block->ingress;
0149     ruleset->ht_key.block = block;
0150     ruleset->ht_key.chain_index = chain_index;
0151     refcount_set(&ruleset->refcount, 1);
0152 
0153     err = rhashtable_init(&ruleset->rule_ht, &prestera_acl_rule_ht_params);
0154     if (err)
0155         goto err_rhashtable_init;
0156 
0157     err = idr_alloc_u32(&acl->uid, NULL, &uid, U8_MAX, GFP_KERNEL);
0158     if (err)
0159         goto err_ruleset_create;
0160 
0161     /* make pcl-id based on uid */
0162     ruleset->pcl_id = PRESTERA_ACL_PCL_ID_MAKE((u8)uid, chain_index);
0163     ruleset->index = uid;
0164 
0165     err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node,
0166                      prestera_acl_ruleset_ht_params);
0167     if (err)
0168         goto err_ruleset_ht_insert;
0169 
0170     return ruleset;
0171 
0172 err_ruleset_ht_insert:
0173     idr_remove(&acl->uid, uid);
0174 err_ruleset_create:
0175     rhashtable_destroy(&ruleset->rule_ht);
0176 err_rhashtable_init:
0177     kfree(ruleset);
0178     return ERR_PTR(err);
0179 }
0180 
0181 void prestera_acl_ruleset_keymask_set(struct prestera_acl_ruleset *ruleset,
0182                       void *keymask)
0183 {
0184     ruleset->keymask = kmemdup(keymask, ACL_KEYMASK_SIZE, GFP_KERNEL);
0185 }
0186 
0187 int prestera_acl_ruleset_offload(struct prestera_acl_ruleset *ruleset)
0188 {
0189     struct prestera_acl_iface iface;
0190     u32 vtcam_id;
0191     int dir;
0192     int err;
0193 
0194     dir = ruleset->ingress ?
0195         PRESTERA_HW_VTCAM_DIR_INGRESS : PRESTERA_HW_VTCAM_DIR_EGRESS;
0196 
0197     if (ruleset->offload)
0198         return -EEXIST;
0199 
0200     err = prestera_acl_vtcam_id_get(ruleset->acl,
0201                     ruleset->ht_key.chain_index,
0202                     dir,
0203                     ruleset->keymask, &vtcam_id);
0204     if (err)
0205         goto err_vtcam_create;
0206 
0207     if (ruleset->ht_key.chain_index) {
0208         /* for chain > 0, bind iface index to pcl-id to be able
0209          * to jump from any other ruleset to this one using the index.
0210          */
0211         iface.index = ruleset->index;
0212         iface.type = PRESTERA_ACL_IFACE_TYPE_INDEX;
0213         err = prestera_hw_vtcam_iface_bind(ruleset->acl->sw, &iface,
0214                            vtcam_id, ruleset->pcl_id);
0215         if (err)
0216             goto err_ruleset_bind;
0217     }
0218 
0219     ruleset->vtcam_id = vtcam_id;
0220     ruleset->offload = true;
0221     return 0;
0222 
0223 err_ruleset_bind:
0224     prestera_acl_vtcam_id_put(ruleset->acl, ruleset->vtcam_id);
0225 err_vtcam_create:
0226     return err;
0227 }
0228 
0229 static void prestera_acl_ruleset_destroy(struct prestera_acl_ruleset *ruleset)
0230 {
0231     struct prestera_acl *acl = ruleset->acl;
0232     u8 uid = ruleset->pcl_id & PRESTERA_ACL_KEYMASK_PCL_ID_USER;
0233     int err;
0234 
0235     rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node,
0236                    prestera_acl_ruleset_ht_params);
0237 
0238     if (ruleset->offload) {
0239         if (ruleset->ht_key.chain_index) {
0240             struct prestera_acl_iface iface = {
0241                 .type = PRESTERA_ACL_IFACE_TYPE_INDEX,
0242                 .index = ruleset->index
0243             };
0244             err = prestera_hw_vtcam_iface_unbind(acl->sw, &iface,
0245                                  ruleset->vtcam_id);
0246             WARN_ON(err);
0247         }
0248         WARN_ON(prestera_acl_vtcam_id_put(acl, ruleset->vtcam_id));
0249     }
0250 
0251     idr_remove(&acl->uid, uid);
0252     rhashtable_destroy(&ruleset->rule_ht);
0253     kfree(ruleset->keymask);
0254     kfree(ruleset);
0255 }
0256 
0257 static struct prestera_acl_ruleset *
0258 __prestera_acl_ruleset_lookup(struct prestera_acl *acl,
0259                   struct prestera_flow_block *block,
0260                   u32 chain_index)
0261 {
0262     struct prestera_acl_ruleset_ht_key ht_key;
0263 
0264     memset(&ht_key, 0, sizeof(ht_key));
0265     ht_key.block = block;
0266     ht_key.chain_index = chain_index;
0267     return rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key,
0268                       prestera_acl_ruleset_ht_params);
0269 }
0270 
0271 struct prestera_acl_ruleset *
0272 prestera_acl_ruleset_lookup(struct prestera_acl *acl,
0273                 struct prestera_flow_block *block,
0274                 u32 chain_index)
0275 {
0276     struct prestera_acl_ruleset *ruleset;
0277 
0278     ruleset = __prestera_acl_ruleset_lookup(acl, block, chain_index);
0279     if (!ruleset)
0280         return ERR_PTR(-ENOENT);
0281 
0282     refcount_inc(&ruleset->refcount);
0283     return ruleset;
0284 }
0285 
0286 struct prestera_acl_ruleset *
0287 prestera_acl_ruleset_get(struct prestera_acl *acl,
0288              struct prestera_flow_block *block,
0289              u32 chain_index)
0290 {
0291     struct prestera_acl_ruleset *ruleset;
0292 
0293     ruleset = __prestera_acl_ruleset_lookup(acl, block, chain_index);
0294     if (ruleset) {
0295         refcount_inc(&ruleset->refcount);
0296         return ruleset;
0297     }
0298 
0299     return prestera_acl_ruleset_create(acl, block, chain_index);
0300 }
0301 
0302 void prestera_acl_ruleset_put(struct prestera_acl_ruleset *ruleset)
0303 {
0304     if (!refcount_dec_and_test(&ruleset->refcount))
0305         return;
0306 
0307     prestera_acl_ruleset_destroy(ruleset);
0308 }
0309 
0310 int prestera_acl_ruleset_bind(struct prestera_acl_ruleset *ruleset,
0311                   struct prestera_port *port)
0312 {
0313     struct prestera_acl_iface iface = {
0314         .type = PRESTERA_ACL_IFACE_TYPE_PORT,
0315         .port = port
0316     };
0317 
0318     return prestera_hw_vtcam_iface_bind(port->sw, &iface, ruleset->vtcam_id,
0319                         ruleset->pcl_id);
0320 }
0321 
0322 int prestera_acl_ruleset_unbind(struct prestera_acl_ruleset *ruleset,
0323                 struct prestera_port *port)
0324 {
0325     struct prestera_acl_iface iface = {
0326         .type = PRESTERA_ACL_IFACE_TYPE_PORT,
0327         .port = port
0328     };
0329 
0330     return prestera_hw_vtcam_iface_unbind(port->sw, &iface,
0331                           ruleset->vtcam_id);
0332 }
0333 
0334 static int prestera_acl_ruleset_block_bind(struct prestera_acl_ruleset *ruleset,
0335                        struct prestera_flow_block *block)
0336 {
0337     struct prestera_flow_block_binding *binding;
0338     int err;
0339 
0340     block->ruleset_zero = ruleset;
0341     list_for_each_entry(binding, &block->binding_list, list) {
0342         err = prestera_acl_ruleset_bind(ruleset, binding->port);
0343         if (err)
0344             goto rollback;
0345     }
0346     return 0;
0347 
0348 rollback:
0349     list_for_each_entry_continue_reverse(binding, &block->binding_list,
0350                          list)
0351         err = prestera_acl_ruleset_unbind(ruleset, binding->port);
0352     block->ruleset_zero = NULL;
0353 
0354     return err;
0355 }
0356 
0357 static void
0358 prestera_acl_ruleset_block_unbind(struct prestera_acl_ruleset *ruleset,
0359                   struct prestera_flow_block *block)
0360 {
0361     struct prestera_flow_block_binding *binding;
0362 
0363     list_for_each_entry(binding, &block->binding_list, list)
0364         prestera_acl_ruleset_unbind(ruleset, binding->port);
0365     block->ruleset_zero = NULL;
0366 }
0367 
0368 void
0369 prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule, u16 pcl_id)
0370 {
0371     struct prestera_acl_match *r_match = &rule->re_key.match;
0372     __be16 pcl_id_mask = htons(PRESTERA_ACL_KEYMASK_PCL_ID);
0373     __be16 pcl_id_key = htons(pcl_id);
0374 
0375     rule_match_set(r_match->key, PCL_ID, pcl_id_key);
0376     rule_match_set(r_match->mask, PCL_ID, pcl_id_mask);
0377 }
0378 
0379 struct prestera_acl_rule *
0380 prestera_acl_rule_lookup(struct prestera_acl_ruleset *ruleset,
0381              unsigned long cookie)
0382 {
0383     return rhashtable_lookup_fast(&ruleset->rule_ht, &cookie,
0384                       prestera_acl_rule_ht_params);
0385 }
0386 
0387 u32 prestera_acl_ruleset_index_get(const struct prestera_acl_ruleset *ruleset)
0388 {
0389     return ruleset->index;
0390 }
0391 
0392 bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset)
0393 {
0394     return ruleset->offload;
0395 }
0396 
0397 struct prestera_acl_rule *
0398 prestera_acl_rule_create(struct prestera_acl_ruleset *ruleset,
0399              unsigned long cookie, u32 chain_index)
0400 {
0401     struct prestera_acl_rule *rule;
0402 
0403     rule = kzalloc(sizeof(*rule), GFP_KERNEL);
0404     if (!rule)
0405         return ERR_PTR(-ENOMEM);
0406 
0407     rule->ruleset = ruleset;
0408     rule->cookie = cookie;
0409     rule->chain_index = chain_index;
0410 
0411     refcount_inc(&ruleset->refcount);
0412 
0413     return rule;
0414 }
0415 
0416 void prestera_acl_rule_priority_set(struct prestera_acl_rule *rule,
0417                     u32 priority)
0418 {
0419     rule->priority = priority;
0420 }
0421 
0422 void prestera_acl_rule_destroy(struct prestera_acl_rule *rule)
0423 {
0424     if (rule->jump_ruleset)
0425         /* release ruleset kept by jump action */
0426         prestera_acl_ruleset_put(rule->jump_ruleset);
0427 
0428     prestera_acl_ruleset_put(rule->ruleset);
0429     kfree(rule);
0430 }
0431 
0432 int prestera_acl_rule_add(struct prestera_switch *sw,
0433               struct prestera_acl_rule *rule)
0434 {
0435     int err;
0436     struct prestera_acl_ruleset *ruleset = rule->ruleset;
0437     struct prestera_flow_block *block = ruleset->ht_key.block;
0438 
0439     /* try to add rule to hash table first */
0440     err = rhashtable_insert_fast(&ruleset->rule_ht, &rule->ht_node,
0441                      prestera_acl_rule_ht_params);
0442     if (err)
0443         goto err_ht_insert;
0444 
0445     prestera_acl_rule_keymask_pcl_id_set(rule, ruleset->pcl_id);
0446     rule->re_arg.vtcam_id = ruleset->vtcam_id;
0447     rule->re_key.prio = rule->priority;
0448 
0449     rule->re = prestera_acl_rule_entry_find(sw->acl, &rule->re_key);
0450     err = WARN_ON(rule->re) ? -EEXIST : 0;
0451     if (err)
0452         goto err_rule_add;
0453 
0454     rule->re = prestera_acl_rule_entry_create(sw->acl, &rule->re_key,
0455                           &rule->re_arg);
0456     err = !rule->re ? -EINVAL : 0;
0457     if (err)
0458         goto err_rule_add;
0459 
0460     /* bind the block (all ports) to chain index 0, rest of
0461      * the chains are bound to goto action
0462      */
0463     if (!ruleset->ht_key.chain_index && !ruleset->rule_count) {
0464         err = prestera_acl_ruleset_block_bind(ruleset, block);
0465         if (err)
0466             goto err_acl_block_bind;
0467     }
0468 
0469     list_add_tail(&rule->list, &sw->acl->rules);
0470     ruleset->rule_count++;
0471     return 0;
0472 
0473 err_acl_block_bind:
0474     prestera_acl_rule_entry_destroy(sw->acl, rule->re);
0475 err_rule_add:
0476     rule->re = NULL;
0477     rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
0478                    prestera_acl_rule_ht_params);
0479 err_ht_insert:
0480     return err;
0481 }
0482 
0483 void prestera_acl_rule_del(struct prestera_switch *sw,
0484                struct prestera_acl_rule *rule)
0485 {
0486     struct prestera_acl_ruleset *ruleset = rule->ruleset;
0487     struct prestera_flow_block *block = ruleset->ht_key.block;
0488 
0489     rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
0490                    prestera_acl_rule_ht_params);
0491     ruleset->rule_count--;
0492     list_del(&rule->list);
0493 
0494     prestera_acl_rule_entry_destroy(sw->acl, rule->re);
0495 
0496     /* unbind block (all ports) */
0497     if (!ruleset->ht_key.chain_index && !ruleset->rule_count)
0498         prestera_acl_ruleset_block_unbind(ruleset, block);
0499 }
0500 
0501 int prestera_acl_rule_get_stats(struct prestera_acl *acl,
0502                 struct prestera_acl_rule *rule,
0503                 u64 *packets, u64 *bytes, u64 *last_use)
0504 {
0505     u64 current_packets;
0506     u64 current_bytes;
0507     int err;
0508 
0509     err = prestera_counter_stats_get(acl->sw->counter,
0510                      rule->re->counter.block,
0511                      rule->re->counter.id,
0512                      &current_packets, &current_bytes);
0513     if (err)
0514         return err;
0515 
0516     *packets = current_packets;
0517     *bytes = current_bytes;
0518     *last_use = jiffies;
0519 
0520     return 0;
0521 }
0522 
0523 struct prestera_acl_rule_entry *
0524 prestera_acl_rule_entry_find(struct prestera_acl *acl,
0525                  struct prestera_acl_rule_entry_key *key)
0526 {
0527     return rhashtable_lookup_fast(&acl->acl_rule_entry_ht, key,
0528                       __prestera_acl_rule_entry_ht_params);
0529 }
0530 
0531 static int __prestera_acl_rule_entry2hw_del(struct prestera_switch *sw,
0532                         struct prestera_acl_rule_entry *e)
0533 {
0534     return prestera_hw_vtcam_rule_del(sw, e->vtcam_id, e->hw_id);
0535 }
0536 
0537 static int __prestera_acl_rule_entry2hw_add(struct prestera_switch *sw,
0538                         struct prestera_acl_rule_entry *e)
0539 {
0540     struct prestera_acl_hw_action_info act_hw[PRESTERA_ACL_RULE_ACTION_MAX];
0541     int act_num;
0542 
0543     memset(&act_hw, 0, sizeof(act_hw));
0544     act_num = 0;
0545 
0546     /* accept */
0547     if (e->accept.valid) {
0548         act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_ACCEPT;
0549         act_num++;
0550     }
0551     /* drop */
0552     if (e->drop.valid) {
0553         act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_DROP;
0554         act_num++;
0555     }
0556     /* trap */
0557     if (e->trap.valid) {
0558         act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_TRAP;
0559         act_num++;
0560     }
0561     /* police */
0562     if (e->police.valid) {
0563         act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_POLICE;
0564         act_hw[act_num].police = e->police.i;
0565         act_num++;
0566     }
0567     /* jump */
0568     if (e->jump.valid) {
0569         act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_JUMP;
0570         act_hw[act_num].jump = e->jump.i;
0571         act_num++;
0572     }
0573     /* counter */
0574     if (e->counter.block) {
0575         act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_COUNT;
0576         act_hw[act_num].count.id = e->counter.id;
0577         act_num++;
0578     }
0579 
0580     return prestera_hw_vtcam_rule_add(sw, e->vtcam_id, e->key.prio,
0581                       e->key.match.key, e->key.match.mask,
0582                       act_hw, act_num, &e->hw_id);
0583 }
0584 
0585 static void
0586 __prestera_acl_rule_entry_act_destruct(struct prestera_switch *sw,
0587                        struct prestera_acl_rule_entry *e)
0588 {
0589     /* counter */
0590     prestera_counter_put(sw->counter, e->counter.block, e->counter.id);
0591     /* police */
0592     if (e->police.valid)
0593         prestera_hw_policer_release(sw, e->police.i.id);
0594 }
0595 
0596 void prestera_acl_rule_entry_destroy(struct prestera_acl *acl,
0597                      struct prestera_acl_rule_entry *e)
0598 {
0599     int ret;
0600 
0601     rhashtable_remove_fast(&acl->acl_rule_entry_ht, &e->ht_node,
0602                    __prestera_acl_rule_entry_ht_params);
0603 
0604     ret = __prestera_acl_rule_entry2hw_del(acl->sw, e);
0605     WARN_ON(ret && ret != -ENODEV);
0606 
0607     __prestera_acl_rule_entry_act_destruct(acl->sw, e);
0608     kfree(e);
0609 }
0610 
0611 static int
0612 __prestera_acl_rule_entry_act_construct(struct prestera_switch *sw,
0613                     struct prestera_acl_rule_entry *e,
0614                     struct prestera_acl_rule_entry_arg *arg)
0615 {
0616     int err;
0617 
0618     /* accept */
0619     e->accept.valid = arg->accept.valid;
0620     /* drop */
0621     e->drop.valid = arg->drop.valid;
0622     /* trap */
0623     e->trap.valid = arg->trap.valid;
0624     /* jump */
0625     e->jump.valid = arg->jump.valid;
0626     e->jump.i = arg->jump.i;
0627     /* police */
0628     if (arg->police.valid) {
0629         u8 type = arg->police.ingress ? PRESTERA_POLICER_TYPE_INGRESS :
0630                         PRESTERA_POLICER_TYPE_EGRESS;
0631 
0632         err = prestera_hw_policer_create(sw, type, &e->police.i.id);
0633         if (err)
0634             goto err_out;
0635 
0636         err = prestera_hw_policer_sr_tcm_set(sw, e->police.i.id,
0637                              arg->police.rate,
0638                              arg->police.burst);
0639         if (err) {
0640             prestera_hw_policer_release(sw, e->police.i.id);
0641             goto err_out;
0642         }
0643         e->police.valid = arg->police.valid;
0644     }
0645     /* counter */
0646     if (arg->count.valid) {
0647         err = prestera_counter_get(sw->counter, arg->count.client,
0648                        &e->counter.block,
0649                        &e->counter.id);
0650         if (err)
0651             goto err_out;
0652     }
0653 
0654     return 0;
0655 
0656 err_out:
0657     __prestera_acl_rule_entry_act_destruct(sw, e);
0658     return -EINVAL;
0659 }
0660 
0661 struct prestera_acl_rule_entry *
0662 prestera_acl_rule_entry_create(struct prestera_acl *acl,
0663                    struct prestera_acl_rule_entry_key *key,
0664                    struct prestera_acl_rule_entry_arg *arg)
0665 {
0666     struct prestera_acl_rule_entry *e;
0667     int err;
0668 
0669     e = kzalloc(sizeof(*e), GFP_KERNEL);
0670     if (!e)
0671         goto err_kzalloc;
0672 
0673     memcpy(&e->key, key, sizeof(*key));
0674     e->vtcam_id = arg->vtcam_id;
0675     err = __prestera_acl_rule_entry_act_construct(acl->sw, e, arg);
0676     if (err)
0677         goto err_act_construct;
0678 
0679     err = __prestera_acl_rule_entry2hw_add(acl->sw, e);
0680     if (err)
0681         goto err_hw_add;
0682 
0683     err = rhashtable_insert_fast(&acl->acl_rule_entry_ht, &e->ht_node,
0684                      __prestera_acl_rule_entry_ht_params);
0685     if (err)
0686         goto err_ht_insert;
0687 
0688     return e;
0689 
0690 err_ht_insert:
0691     WARN_ON(__prestera_acl_rule_entry2hw_del(acl->sw, e));
0692 err_hw_add:
0693     __prestera_acl_rule_entry_act_destruct(acl->sw, e);
0694 err_act_construct:
0695     kfree(e);
0696 err_kzalloc:
0697     return NULL;
0698 }
0699 
0700 static int __prestera_acl_vtcam_id_try_fit(struct prestera_acl *acl, u8 lookup,
0701                        void *keymask, u32 *vtcam_id)
0702 {
0703     struct prestera_acl_vtcam *vtcam;
0704     int i;
0705 
0706     list_for_each_entry(vtcam, &acl->vtcam_list, list) {
0707         if (lookup != vtcam->lookup)
0708             continue;
0709 
0710         if (!keymask && !vtcam->is_keymask_set)
0711             goto vtcam_found;
0712 
0713         if (!(keymask && vtcam->is_keymask_set))
0714             continue;
0715 
0716         /* try to fit with vtcam keymask */
0717         for (i = 0; i < __PRESTERA_ACL_RULE_MATCH_TYPE_MAX; i++) {
0718             __be32 __keymask = ((__be32 *)keymask)[i];
0719 
0720             if (!__keymask)
0721                 /* vtcam keymask in not interested */
0722                 continue;
0723 
0724             if (__keymask & ~vtcam->keymask[i])
0725                 /* keymask does not fit the vtcam keymask */
0726                 break;
0727         }
0728 
0729         if (i == __PRESTERA_ACL_RULE_MATCH_TYPE_MAX)
0730             /* keymask fits vtcam keymask, return it */
0731             goto vtcam_found;
0732     }
0733 
0734     /* nothing is found */
0735     return -ENOENT;
0736 
0737 vtcam_found:
0738     refcount_inc(&vtcam->refcount);
0739     *vtcam_id = vtcam->id;
0740     return 0;
0741 }
0742 
0743 int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup, u8 dir,
0744                   void *keymask, u32 *vtcam_id)
0745 {
0746     struct prestera_acl_vtcam *vtcam;
0747     u32 new_vtcam_id;
0748     int err;
0749 
0750     /* find the vtcam that suits keymask. We do not expect to have
0751      * a big number of vtcams, so, the list type for vtcam list is
0752      * fine for now
0753      */
0754     list_for_each_entry(vtcam, &acl->vtcam_list, list) {
0755         if (lookup != vtcam->lookup ||
0756             dir != vtcam->direction)
0757             continue;
0758 
0759         if (!keymask && !vtcam->is_keymask_set) {
0760             refcount_inc(&vtcam->refcount);
0761             goto vtcam_found;
0762         }
0763 
0764         if (keymask && vtcam->is_keymask_set &&
0765             !memcmp(keymask, vtcam->keymask, sizeof(vtcam->keymask))) {
0766             refcount_inc(&vtcam->refcount);
0767             goto vtcam_found;
0768         }
0769     }
0770 
0771     /* vtcam not found, try to create new one */
0772     vtcam = kzalloc(sizeof(*vtcam), GFP_KERNEL);
0773     if (!vtcam)
0774         return -ENOMEM;
0775 
0776     err = prestera_hw_vtcam_create(acl->sw, lookup, keymask, &new_vtcam_id,
0777                        dir);
0778     if (err) {
0779         kfree(vtcam);
0780 
0781         /* cannot create new, try to fit into existing vtcam */
0782         if (__prestera_acl_vtcam_id_try_fit(acl, lookup,
0783                             keymask, &new_vtcam_id))
0784             return err;
0785 
0786         *vtcam_id = new_vtcam_id;
0787         return 0;
0788     }
0789 
0790     vtcam->direction = dir;
0791     vtcam->id = new_vtcam_id;
0792     vtcam->lookup = lookup;
0793     if (keymask) {
0794         memcpy(vtcam->keymask, keymask, sizeof(vtcam->keymask));
0795         vtcam->is_keymask_set = true;
0796     }
0797     refcount_set(&vtcam->refcount, 1);
0798     list_add_rcu(&vtcam->list, &acl->vtcam_list);
0799 
0800 vtcam_found:
0801     *vtcam_id = vtcam->id;
0802     return 0;
0803 }
0804 
0805 int prestera_acl_vtcam_id_put(struct prestera_acl *acl, u32 vtcam_id)
0806 {
0807     struct prestera_acl_vtcam *vtcam;
0808     int err;
0809 
0810     list_for_each_entry(vtcam, &acl->vtcam_list, list) {
0811         if (vtcam_id != vtcam->id)
0812             continue;
0813 
0814         if (!refcount_dec_and_test(&vtcam->refcount))
0815             return 0;
0816 
0817         err = prestera_hw_vtcam_destroy(acl->sw, vtcam->id);
0818         if (err && err != -ENODEV) {
0819             refcount_set(&vtcam->refcount, 1);
0820             return err;
0821         }
0822 
0823         list_del(&vtcam->list);
0824         kfree(vtcam);
0825         return 0;
0826     }
0827 
0828     return -ENOENT;
0829 }
0830 
0831 int prestera_acl_init(struct prestera_switch *sw)
0832 {
0833     struct prestera_acl *acl;
0834     int err;
0835 
0836     acl = kzalloc(sizeof(*acl), GFP_KERNEL);
0837     if (!acl)
0838         return -ENOMEM;
0839 
0840     acl->sw = sw;
0841     INIT_LIST_HEAD(&acl->rules);
0842     INIT_LIST_HEAD(&acl->vtcam_list);
0843     idr_init(&acl->uid);
0844 
0845     err = rhashtable_init(&acl->acl_rule_entry_ht,
0846                   &__prestera_acl_rule_entry_ht_params);
0847     if (err)
0848         goto err_acl_rule_entry_ht_init;
0849 
0850     err = rhashtable_init(&acl->ruleset_ht,
0851                   &prestera_acl_ruleset_ht_params);
0852     if (err)
0853         goto err_ruleset_ht_init;
0854 
0855     sw->acl = acl;
0856 
0857     return 0;
0858 
0859 err_ruleset_ht_init:
0860     rhashtable_destroy(&acl->acl_rule_entry_ht);
0861 err_acl_rule_entry_ht_init:
0862     kfree(acl);
0863     return err;
0864 }
0865 
0866 void prestera_acl_fini(struct prestera_switch *sw)
0867 {
0868     struct prestera_acl *acl = sw->acl;
0869 
0870     WARN_ON(!idr_is_empty(&acl->uid));
0871     idr_destroy(&acl->uid);
0872 
0873     WARN_ON(!list_empty(&acl->vtcam_list));
0874     WARN_ON(!list_empty(&acl->rules));
0875 
0876     rhashtable_destroy(&acl->ruleset_ht);
0877     rhashtable_destroy(&acl->acl_rule_entry_ht);
0878 
0879     kfree(acl);
0880 }