linux/drivers/net/ethernet/marvell/prestera/prestera_acl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
   2/* Copyright (c) 2020 Marvell International Ltd. All rights reserved */
   3
   4#include <linux/rhashtable.h>
   5
   6#include "prestera.h"
   7#include "prestera_hw.h"
   8#include "prestera_acl.h"
   9#include "prestera_span.h"
  10
  11struct prestera_acl {
  12        struct prestera_switch *sw;
  13        struct list_head rules;
  14};
  15
  16struct prestera_acl_ruleset {
  17        struct rhashtable rule_ht;
  18        struct prestera_switch *sw;
  19        u16 id;
  20};
  21
  22struct prestera_acl_rule {
  23        struct rhash_head ht_node;
  24        struct list_head list;
  25        struct list_head match_list;
  26        struct list_head action_list;
  27        struct prestera_flow_block *block;
  28        unsigned long cookie;
  29        u32 priority;
  30        u8 n_actions;
  31        u8 n_matches;
  32        u32 id;
  33};
  34
  35static const struct rhashtable_params prestera_acl_rule_ht_params = {
  36        .key_len = sizeof(unsigned long),
  37        .key_offset = offsetof(struct prestera_acl_rule, cookie),
  38        .head_offset = offsetof(struct prestera_acl_rule, ht_node),
  39        .automatic_shrinking = true,
  40};
  41
  42static struct prestera_acl_ruleset *
  43prestera_acl_ruleset_create(struct prestera_switch *sw)
  44{
  45        struct prestera_acl_ruleset *ruleset;
  46        int err;
  47
  48        ruleset = kzalloc(sizeof(*ruleset), GFP_KERNEL);
  49        if (!ruleset)
  50                return ERR_PTR(-ENOMEM);
  51
  52        err = rhashtable_init(&ruleset->rule_ht, &prestera_acl_rule_ht_params);
  53        if (err)
  54                goto err_rhashtable_init;
  55
  56        err = prestera_hw_acl_ruleset_create(sw, &ruleset->id);
  57        if (err)
  58                goto err_ruleset_create;
  59
  60        ruleset->sw = sw;
  61
  62        return ruleset;
  63
  64err_ruleset_create:
  65        rhashtable_destroy(&ruleset->rule_ht);
  66err_rhashtable_init:
  67        kfree(ruleset);
  68        return ERR_PTR(err);
  69}
  70
  71static void prestera_acl_ruleset_destroy(struct prestera_acl_ruleset *ruleset)
  72{
  73        prestera_hw_acl_ruleset_del(ruleset->sw, ruleset->id);
  74        rhashtable_destroy(&ruleset->rule_ht);
  75        kfree(ruleset);
  76}
  77
  78struct prestera_flow_block *
  79prestera_acl_block_create(struct prestera_switch *sw, struct net *net)
  80{
  81        struct prestera_flow_block *block;
  82
  83        block = kzalloc(sizeof(*block), GFP_KERNEL);
  84        if (!block)
  85                return NULL;
  86        INIT_LIST_HEAD(&block->binding_list);
  87        block->net = net;
  88        block->sw = sw;
  89
  90        block->ruleset = prestera_acl_ruleset_create(sw);
  91        if (IS_ERR(block->ruleset)) {
  92                kfree(block);
  93                return NULL;
  94        }
  95
  96        return block;
  97}
  98
  99void prestera_acl_block_destroy(struct prestera_flow_block *block)
 100{
 101        prestera_acl_ruleset_destroy(block->ruleset);
 102        WARN_ON(!list_empty(&block->binding_list));
 103        kfree(block);
 104}
 105
 106static struct prestera_flow_block_binding *
 107prestera_acl_block_lookup(struct prestera_flow_block *block,
 108                          struct prestera_port *port)
 109{
 110        struct prestera_flow_block_binding *binding;
 111
 112        list_for_each_entry(binding, &block->binding_list, list)
 113                if (binding->port == port)
 114                        return binding;
 115
 116        return NULL;
 117}
 118
 119int prestera_acl_block_bind(struct prestera_flow_block *block,
 120                            struct prestera_port *port)
 121{
 122        struct prestera_flow_block_binding *binding;
 123        int err;
 124
 125        if (WARN_ON(prestera_acl_block_lookup(block, port)))
 126                return -EEXIST;
 127
 128        binding = kzalloc(sizeof(*binding), GFP_KERNEL);
 129        if (!binding)
 130                return -ENOMEM;
 131        binding->span_id = PRESTERA_SPAN_INVALID_ID;
 132        binding->port = port;
 133
 134        err = prestera_hw_acl_port_bind(port, block->ruleset->id);
 135        if (err)
 136                goto err_rules_bind;
 137
 138        list_add(&binding->list, &block->binding_list);
 139        return 0;
 140
 141err_rules_bind:
 142        kfree(binding);
 143        return err;
 144}
 145
 146int prestera_acl_block_unbind(struct prestera_flow_block *block,
 147                              struct prestera_port *port)
 148{
 149        struct prestera_flow_block_binding *binding;
 150
 151        binding = prestera_acl_block_lookup(block, port);
 152        if (!binding)
 153                return -ENOENT;
 154
 155        list_del(&binding->list);
 156
 157        prestera_hw_acl_port_unbind(port, block->ruleset->id);
 158
 159        kfree(binding);
 160        return 0;
 161}
 162
 163struct prestera_acl_ruleset *
 164prestera_acl_block_ruleset_get(struct prestera_flow_block *block)
 165{
 166        return block->ruleset;
 167}
 168
 169u16 prestera_acl_rule_ruleset_id_get(const struct prestera_acl_rule *rule)
 170{
 171        return rule->block->ruleset->id;
 172}
 173
 174struct net *prestera_acl_block_net(struct prestera_flow_block *block)
 175{
 176        return block->net;
 177}
 178
 179struct prestera_switch *prestera_acl_block_sw(struct prestera_flow_block *block)
 180{
 181        return block->sw;
 182}
 183
 184struct prestera_acl_rule *
 185prestera_acl_rule_lookup(struct prestera_acl_ruleset *ruleset,
 186                         unsigned long cookie)
 187{
 188        return rhashtable_lookup_fast(&ruleset->rule_ht, &cookie,
 189                                      prestera_acl_rule_ht_params);
 190}
 191
 192struct prestera_acl_rule *
 193prestera_acl_rule_create(struct prestera_flow_block *block,
 194                         unsigned long cookie)
 195{
 196        struct prestera_acl_rule *rule;
 197
 198        rule = kzalloc(sizeof(*rule), GFP_KERNEL);
 199        if (!rule)
 200                return ERR_PTR(-ENOMEM);
 201
 202        INIT_LIST_HEAD(&rule->match_list);
 203        INIT_LIST_HEAD(&rule->action_list);
 204        rule->cookie = cookie;
 205        rule->block = block;
 206
 207        return rule;
 208}
 209
 210struct list_head *
 211prestera_acl_rule_match_list_get(struct prestera_acl_rule *rule)
 212{
 213        return &rule->match_list;
 214}
 215
 216struct list_head *
 217prestera_acl_rule_action_list_get(struct prestera_acl_rule *rule)
 218{
 219        return &rule->action_list;
 220}
 221
 222int prestera_acl_rule_action_add(struct prestera_acl_rule *rule,
 223                                 struct prestera_acl_rule_action_entry *entry)
 224{
 225        struct prestera_acl_rule_action_entry *a_entry;
 226
 227        a_entry = kmalloc(sizeof(*a_entry), GFP_KERNEL);
 228        if (!a_entry)
 229                return -ENOMEM;
 230
 231        memcpy(a_entry, entry, sizeof(*entry));
 232        list_add(&a_entry->list, &rule->action_list);
 233
 234        rule->n_actions++;
 235        return 0;
 236}
 237
 238u8 prestera_acl_rule_action_len(struct prestera_acl_rule *rule)
 239{
 240        return rule->n_actions;
 241}
 242
 243u32 prestera_acl_rule_priority_get(struct prestera_acl_rule *rule)
 244{
 245        return rule->priority;
 246}
 247
 248void prestera_acl_rule_priority_set(struct prestera_acl_rule *rule,
 249                                    u32 priority)
 250{
 251        rule->priority = priority;
 252}
 253
 254int prestera_acl_rule_match_add(struct prestera_acl_rule *rule,
 255                                struct prestera_acl_rule_match_entry *entry)
 256{
 257        struct prestera_acl_rule_match_entry *m_entry;
 258
 259        m_entry = kmalloc(sizeof(*m_entry), GFP_KERNEL);
 260        if (!m_entry)
 261                return -ENOMEM;
 262
 263        memcpy(m_entry, entry, sizeof(*entry));
 264        list_add(&m_entry->list, &rule->match_list);
 265
 266        rule->n_matches++;
 267        return 0;
 268}
 269
 270u8 prestera_acl_rule_match_len(struct prestera_acl_rule *rule)
 271{
 272        return rule->n_matches;
 273}
 274
 275void prestera_acl_rule_destroy(struct prestera_acl_rule *rule)
 276{
 277        struct prestera_acl_rule_action_entry *a_entry;
 278        struct prestera_acl_rule_match_entry *m_entry;
 279        struct list_head *pos, *n;
 280
 281        list_for_each_safe(pos, n, &rule->match_list) {
 282                m_entry = list_entry(pos, typeof(*m_entry), list);
 283                list_del(pos);
 284                kfree(m_entry);
 285        }
 286
 287        list_for_each_safe(pos, n, &rule->action_list) {
 288                a_entry = list_entry(pos, typeof(*a_entry), list);
 289                list_del(pos);
 290                kfree(a_entry);
 291        }
 292
 293        kfree(rule);
 294}
 295
 296int prestera_acl_rule_add(struct prestera_switch *sw,
 297                          struct prestera_acl_rule *rule)
 298{
 299        u32 rule_id;
 300        int err;
 301
 302        /* try to add rule to hash table first */
 303        err = rhashtable_insert_fast(&rule->block->ruleset->rule_ht,
 304                                     &rule->ht_node,
 305                                     prestera_acl_rule_ht_params);
 306        if (err)
 307                return err;
 308
 309        /* add rule to hw */
 310        err = prestera_hw_acl_rule_add(sw, rule, &rule_id);
 311        if (err)
 312                goto err_rule_add;
 313
 314        rule->id = rule_id;
 315
 316        list_add_tail(&rule->list, &sw->acl->rules);
 317
 318        return 0;
 319
 320err_rule_add:
 321        rhashtable_remove_fast(&rule->block->ruleset->rule_ht, &rule->ht_node,
 322                               prestera_acl_rule_ht_params);
 323        return err;
 324}
 325
 326void prestera_acl_rule_del(struct prestera_switch *sw,
 327                           struct prestera_acl_rule *rule)
 328{
 329        rhashtable_remove_fast(&rule->block->ruleset->rule_ht, &rule->ht_node,
 330                               prestera_acl_rule_ht_params);
 331        list_del(&rule->list);
 332        prestera_hw_acl_rule_del(sw, rule->id);
 333}
 334
 335int prestera_acl_rule_get_stats(struct prestera_switch *sw,
 336                                struct prestera_acl_rule *rule,
 337                                u64 *packets, u64 *bytes, u64 *last_use)
 338{
 339        u64 current_packets;
 340        u64 current_bytes;
 341        int err;
 342
 343        err = prestera_hw_acl_rule_stats_get(sw, rule->id, &current_packets,
 344                                             &current_bytes);
 345        if (err)
 346                return err;
 347
 348        *packets = current_packets;
 349        *bytes = current_bytes;
 350        *last_use = jiffies;
 351
 352        return 0;
 353}
 354
 355int prestera_acl_init(struct prestera_switch *sw)
 356{
 357        struct prestera_acl *acl;
 358
 359        acl = kzalloc(sizeof(*acl), GFP_KERNEL);
 360        if (!acl)
 361                return -ENOMEM;
 362
 363        INIT_LIST_HEAD(&acl->rules);
 364        sw->acl = acl;
 365        acl->sw = sw;
 366
 367        return 0;
 368}
 369
 370void prestera_acl_fini(struct prestera_switch *sw)
 371{
 372        struct prestera_acl *acl = sw->acl;
 373
 374        WARN_ON(!list_empty(&acl->rules));
 375        kfree(acl);
 376}
 377