Merge branch 'prestera-next'
Volodymyr Mytnyk says: ==================== net: prestera: acl: migrate to new vTcam/counter api This patch series aims to use new vTcam and Counter API provided by latest fw version. The advantage of using this API is the following: - provides a way to have a rule with desired Tcam size (improves Tcam memory utilization). - batch support for acl counters gathering (improves performance) - gives more control over HW ACL engine (actions/matches/bindings) to be able to support more features in the future driver versions Note: the feature set left the same as was before this patch. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
9ace2300fc
@ -3,6 +3,6 @@ obj-$(CONFIG_PRESTERA) += prestera.o
|
||||
prestera-objs := prestera_main.o prestera_hw.o prestera_dsa.o \
|
||||
prestera_rxtx.o prestera_devlink.o prestera_ethtool.o \
|
||||
prestera_switchdev.o prestera_acl.o prestera_flow.o \
|
||||
prestera_flower.o prestera_span.o
|
||||
prestera_flower.o prestera_span.o prestera_counter.o
|
||||
|
||||
obj-$(CONFIG_PRESTERA_PCI) += prestera_pci.o
|
||||
|
@ -248,6 +248,7 @@ struct prestera_switch {
|
||||
u32 mtu_max;
|
||||
u8 id;
|
||||
struct prestera_lag *lags;
|
||||
struct prestera_counter *counter;
|
||||
u8 lag_member_max;
|
||||
u8 lag_max;
|
||||
};
|
||||
|
@ -1,35 +1,72 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
|
||||
/* Copyright (c) 2020 Marvell International Ltd. All rights reserved */
|
||||
/* Copyright (c) 2020-2021 Marvell International Ltd. All rights reserved */
|
||||
|
||||
#include <linux/rhashtable.h>
|
||||
|
||||
#include "prestera.h"
|
||||
#include "prestera_hw.h"
|
||||
#include "prestera_acl.h"
|
||||
#include "prestera_span.h"
|
||||
#include "prestera_flow.h"
|
||||
#include "prestera_hw.h"
|
||||
#include "prestera.h"
|
||||
|
||||
#define ACL_KEYMASK_SIZE \
|
||||
(sizeof(__be32) * __PRESTERA_ACL_RULE_MATCH_TYPE_MAX)
|
||||
|
||||
struct prestera_acl {
|
||||
struct prestera_switch *sw;
|
||||
struct list_head vtcam_list;
|
||||
struct list_head rules;
|
||||
struct rhashtable ruleset_ht;
|
||||
struct rhashtable acl_rule_entry_ht;
|
||||
struct idr uid;
|
||||
};
|
||||
|
||||
struct prestera_acl_ruleset_ht_key {
|
||||
struct prestera_flow_block *block;
|
||||
};
|
||||
|
||||
struct prestera_acl_rule_entry {
|
||||
struct rhash_head ht_node;
|
||||
struct prestera_acl_rule_entry_key key;
|
||||
u32 hw_id;
|
||||
u32 vtcam_id;
|
||||
struct {
|
||||
struct {
|
||||
u8 valid:1;
|
||||
} accept, drop, trap;
|
||||
struct {
|
||||
u32 id;
|
||||
struct prestera_counter_block *block;
|
||||
} counter;
|
||||
};
|
||||
};
|
||||
|
||||
struct prestera_acl_ruleset {
|
||||
struct rhash_head ht_node; /* Member of acl HT */
|
||||
struct prestera_acl_ruleset_ht_key ht_key;
|
||||
struct rhashtable rule_ht;
|
||||
struct prestera_switch *sw;
|
||||
u16 id;
|
||||
struct prestera_acl *acl;
|
||||
unsigned long rule_count;
|
||||
refcount_t refcount;
|
||||
void *keymask;
|
||||
u32 vtcam_id;
|
||||
u16 pcl_id;
|
||||
bool offload;
|
||||
};
|
||||
|
||||
struct prestera_acl_rule {
|
||||
struct rhash_head ht_node;
|
||||
struct prestera_acl_vtcam {
|
||||
struct list_head list;
|
||||
struct list_head match_list;
|
||||
struct list_head action_list;
|
||||
struct prestera_flow_block *block;
|
||||
unsigned long cookie;
|
||||
u32 priority;
|
||||
u8 n_actions;
|
||||
u8 n_matches;
|
||||
__be32 keymask[__PRESTERA_ACL_RULE_MATCH_TYPE_MAX];
|
||||
refcount_t refcount;
|
||||
u32 id;
|
||||
bool is_keymask_set;
|
||||
u8 lookup;
|
||||
};
|
||||
|
||||
static const struct rhashtable_params prestera_acl_ruleset_ht_params = {
|
||||
.key_len = sizeof(struct prestera_acl_ruleset_ht_key),
|
||||
.key_offset = offsetof(struct prestera_acl_ruleset, ht_key),
|
||||
.head_offset = offsetof(struct prestera_acl_ruleset, ht_node),
|
||||
.automatic_shrinking = true,
|
||||
};
|
||||
|
||||
static const struct rhashtable_params prestera_acl_rule_ht_params = {
|
||||
@ -39,28 +76,48 @@ static const struct rhashtable_params prestera_acl_rule_ht_params = {
|
||||
.automatic_shrinking = true,
|
||||
};
|
||||
|
||||
static const struct rhashtable_params __prestera_acl_rule_entry_ht_params = {
|
||||
.key_offset = offsetof(struct prestera_acl_rule_entry, key),
|
||||
.head_offset = offsetof(struct prestera_acl_rule_entry, ht_node),
|
||||
.key_len = sizeof(struct prestera_acl_rule_entry_key),
|
||||
.automatic_shrinking = true,
|
||||
};
|
||||
|
||||
static struct prestera_acl_ruleset *
|
||||
prestera_acl_ruleset_create(struct prestera_switch *sw)
|
||||
prestera_acl_ruleset_create(struct prestera_acl *acl,
|
||||
struct prestera_flow_block *block)
|
||||
{
|
||||
struct prestera_acl_ruleset *ruleset;
|
||||
int err;
|
||||
u32 uid;
|
||||
|
||||
ruleset = kzalloc(sizeof(*ruleset), GFP_KERNEL);
|
||||
if (!ruleset)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ruleset->acl = acl;
|
||||
ruleset->ht_key.block = block;
|
||||
refcount_set(&ruleset->refcount, 1);
|
||||
|
||||
err = rhashtable_init(&ruleset->rule_ht, &prestera_acl_rule_ht_params);
|
||||
if (err)
|
||||
goto err_rhashtable_init;
|
||||
|
||||
err = prestera_hw_acl_ruleset_create(sw, &ruleset->id);
|
||||
err = idr_alloc_u32(&acl->uid, NULL, &uid, U8_MAX, GFP_KERNEL);
|
||||
if (err)
|
||||
goto err_ruleset_create;
|
||||
|
||||
ruleset->sw = sw;
|
||||
/* make pcl-id based on uid */
|
||||
ruleset->pcl_id = (u8)uid;
|
||||
err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node,
|
||||
prestera_acl_ruleset_ht_params);
|
||||
if (err)
|
||||
goto err_ruleset_ht_insert;
|
||||
|
||||
return ruleset;
|
||||
|
||||
err_ruleset_ht_insert:
|
||||
idr_remove(&acl->uid, uid);
|
||||
err_ruleset_create:
|
||||
rhashtable_destroy(&ruleset->rule_ht);
|
||||
err_rhashtable_init:
|
||||
@ -68,117 +125,158 @@ err_rhashtable_init:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
int prestera_acl_ruleset_offload(struct prestera_acl_ruleset *ruleset)
|
||||
{
|
||||
u32 vtcam_id;
|
||||
int err;
|
||||
|
||||
if (ruleset->offload)
|
||||
return -EEXIST;
|
||||
|
||||
err = prestera_acl_vtcam_id_get(ruleset->acl, 0,
|
||||
ruleset->keymask, &vtcam_id);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ruleset->vtcam_id = vtcam_id;
|
||||
ruleset->offload = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void prestera_acl_ruleset_destroy(struct prestera_acl_ruleset *ruleset)
|
||||
{
|
||||
prestera_hw_acl_ruleset_del(ruleset->sw, ruleset->id);
|
||||
struct prestera_acl *acl = ruleset->acl;
|
||||
u8 uid = ruleset->pcl_id & PRESTERA_ACL_KEYMASK_PCL_ID_USER;
|
||||
|
||||
rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node,
|
||||
prestera_acl_ruleset_ht_params);
|
||||
|
||||
if (ruleset->offload)
|
||||
WARN_ON(prestera_acl_vtcam_id_put(acl, ruleset->vtcam_id));
|
||||
|
||||
idr_remove(&acl->uid, uid);
|
||||
|
||||
rhashtable_destroy(&ruleset->rule_ht);
|
||||
kfree(ruleset->keymask);
|
||||
kfree(ruleset);
|
||||
}
|
||||
|
||||
struct prestera_flow_block *
|
||||
prestera_acl_block_create(struct prestera_switch *sw, struct net *net)
|
||||
static struct prestera_acl_ruleset *
|
||||
__prestera_acl_ruleset_lookup(struct prestera_acl *acl,
|
||||
struct prestera_flow_block *block)
|
||||
{
|
||||
struct prestera_flow_block *block;
|
||||
struct prestera_acl_ruleset_ht_key ht_key;
|
||||
|
||||
block = kzalloc(sizeof(*block), GFP_KERNEL);
|
||||
if (!block)
|
||||
return NULL;
|
||||
INIT_LIST_HEAD(&block->binding_list);
|
||||
block->net = net;
|
||||
block->sw = sw;
|
||||
memset(&ht_key, 0, sizeof(ht_key));
|
||||
ht_key.block = block;
|
||||
return rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key,
|
||||
prestera_acl_ruleset_ht_params);
|
||||
}
|
||||
|
||||
block->ruleset = prestera_acl_ruleset_create(sw);
|
||||
if (IS_ERR(block->ruleset)) {
|
||||
kfree(block);
|
||||
return NULL;
|
||||
struct prestera_acl_ruleset *
|
||||
prestera_acl_ruleset_lookup(struct prestera_acl *acl,
|
||||
struct prestera_flow_block *block)
|
||||
{
|
||||
struct prestera_acl_ruleset *ruleset;
|
||||
|
||||
ruleset = __prestera_acl_ruleset_lookup(acl, block);
|
||||
if (!ruleset)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
refcount_inc(&ruleset->refcount);
|
||||
return ruleset;
|
||||
}
|
||||
|
||||
struct prestera_acl_ruleset *
|
||||
prestera_acl_ruleset_get(struct prestera_acl *acl,
|
||||
struct prestera_flow_block *block)
|
||||
{
|
||||
struct prestera_acl_ruleset *ruleset;
|
||||
|
||||
ruleset = __prestera_acl_ruleset_lookup(acl, block);
|
||||
if (ruleset) {
|
||||
refcount_inc(&ruleset->refcount);
|
||||
return ruleset;
|
||||
}
|
||||
|
||||
return block;
|
||||
return prestera_acl_ruleset_create(acl, block);
|
||||
}
|
||||
|
||||
void prestera_acl_block_destroy(struct prestera_flow_block *block)
|
||||
void prestera_acl_ruleset_put(struct prestera_acl_ruleset *ruleset)
|
||||
{
|
||||
prestera_acl_ruleset_destroy(block->ruleset);
|
||||
WARN_ON(!list_empty(&block->binding_list));
|
||||
kfree(block);
|
||||
if (!refcount_dec_and_test(&ruleset->refcount))
|
||||
return;
|
||||
|
||||
prestera_acl_ruleset_destroy(ruleset);
|
||||
}
|
||||
|
||||
static struct prestera_flow_block_binding *
|
||||
prestera_acl_block_lookup(struct prestera_flow_block *block,
|
||||
struct prestera_port *port)
|
||||
int prestera_acl_ruleset_bind(struct prestera_acl_ruleset *ruleset,
|
||||
struct prestera_port *port)
|
||||
{
|
||||
struct prestera_flow_block_binding *binding;
|
||||
struct prestera_acl_iface iface = {
|
||||
.type = PRESTERA_ACL_IFACE_TYPE_PORT,
|
||||
.port = port
|
||||
};
|
||||
|
||||
list_for_each_entry(binding, &block->binding_list, list)
|
||||
if (binding->port == port)
|
||||
return binding;
|
||||
|
||||
return NULL;
|
||||
return prestera_hw_vtcam_iface_bind(port->sw, &iface, ruleset->vtcam_id,
|
||||
ruleset->pcl_id);
|
||||
}
|
||||
|
||||
int prestera_acl_block_bind(struct prestera_flow_block *block,
|
||||
struct prestera_port *port)
|
||||
int prestera_acl_ruleset_unbind(struct prestera_acl_ruleset *ruleset,
|
||||
struct prestera_port *port)
|
||||
{
|
||||
struct prestera_acl_iface iface = {
|
||||
.type = PRESTERA_ACL_IFACE_TYPE_PORT,
|
||||
.port = port
|
||||
};
|
||||
|
||||
return prestera_hw_vtcam_iface_unbind(port->sw, &iface,
|
||||
ruleset->vtcam_id);
|
||||
}
|
||||
|
||||
static int prestera_acl_ruleset_block_bind(struct prestera_acl_ruleset *ruleset,
|
||||
struct prestera_flow_block *block)
|
||||
{
|
||||
struct prestera_flow_block_binding *binding;
|
||||
int err;
|
||||
|
||||
if (WARN_ON(prestera_acl_block_lookup(block, port)))
|
||||
return -EEXIST;
|
||||
|
||||
binding = kzalloc(sizeof(*binding), GFP_KERNEL);
|
||||
if (!binding)
|
||||
return -ENOMEM;
|
||||
binding->span_id = PRESTERA_SPAN_INVALID_ID;
|
||||
binding->port = port;
|
||||
|
||||
err = prestera_hw_acl_port_bind(port, block->ruleset->id);
|
||||
if (err)
|
||||
goto err_rules_bind;
|
||||
|
||||
list_add(&binding->list, &block->binding_list);
|
||||
block->ruleset_zero = ruleset;
|
||||
list_for_each_entry(binding, &block->binding_list, list) {
|
||||
err = prestera_acl_ruleset_bind(ruleset, binding->port);
|
||||
if (err)
|
||||
goto rollback;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_rules_bind:
|
||||
kfree(binding);
|
||||
rollback:
|
||||
list_for_each_entry_continue_reverse(binding, &block->binding_list,
|
||||
list)
|
||||
err = prestera_acl_ruleset_unbind(ruleset, binding->port);
|
||||
block->ruleset_zero = NULL;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int prestera_acl_block_unbind(struct prestera_flow_block *block,
|
||||
struct prestera_port *port)
|
||||
static void
|
||||
prestera_acl_ruleset_block_unbind(struct prestera_acl_ruleset *ruleset,
|
||||
struct prestera_flow_block *block)
|
||||
{
|
||||
struct prestera_flow_block_binding *binding;
|
||||
|
||||
binding = prestera_acl_block_lookup(block, port);
|
||||
if (!binding)
|
||||
return -ENOENT;
|
||||
|
||||
list_del(&binding->list);
|
||||
|
||||
prestera_hw_acl_port_unbind(port, block->ruleset->id);
|
||||
|
||||
kfree(binding);
|
||||
return 0;
|
||||
list_for_each_entry(binding, &block->binding_list, list)
|
||||
prestera_acl_ruleset_unbind(ruleset, binding->port);
|
||||
block->ruleset_zero = NULL;
|
||||
}
|
||||
|
||||
struct prestera_acl_ruleset *
|
||||
prestera_acl_block_ruleset_get(struct prestera_flow_block *block)
|
||||
void
|
||||
prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule, u16 pcl_id)
|
||||
{
|
||||
return block->ruleset;
|
||||
}
|
||||
struct prestera_acl_match *r_match = &rule->re_key.match;
|
||||
__be16 pcl_id_mask = htons(PRESTERA_ACL_KEYMASK_PCL_ID);
|
||||
__be16 pcl_id_key = htons(pcl_id);
|
||||
|
||||
u16 prestera_acl_rule_ruleset_id_get(const struct prestera_acl_rule *rule)
|
||||
{
|
||||
return rule->block->ruleset->id;
|
||||
}
|
||||
|
||||
struct net *prestera_acl_block_net(struct prestera_flow_block *block)
|
||||
{
|
||||
return block->net;
|
||||
}
|
||||
|
||||
struct prestera_switch *prestera_acl_block_sw(struct prestera_flow_block *block)
|
||||
{
|
||||
return block->sw;
|
||||
rule_match_set(r_match->key, PCL_ID, pcl_id_key);
|
||||
rule_match_set(r_match->mask, PCL_ID, pcl_id_mask);
|
||||
}
|
||||
|
||||
struct prestera_acl_rule *
|
||||
@ -189,8 +287,13 @@ prestera_acl_rule_lookup(struct prestera_acl_ruleset *ruleset,
|
||||
prestera_acl_rule_ht_params);
|
||||
}
|
||||
|
||||
bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset)
|
||||
{
|
||||
return ruleset->offload;
|
||||
}
|
||||
|
||||
struct prestera_acl_rule *
|
||||
prestera_acl_rule_create(struct prestera_flow_block *block,
|
||||
prestera_acl_rule_create(struct prestera_acl_ruleset *ruleset,
|
||||
unsigned long cookie)
|
||||
{
|
||||
struct prestera_acl_rule *rule;
|
||||
@ -199,140 +302,98 @@ prestera_acl_rule_create(struct prestera_flow_block *block,
|
||||
if (!rule)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
INIT_LIST_HEAD(&rule->match_list);
|
||||
INIT_LIST_HEAD(&rule->action_list);
|
||||
rule->ruleset = ruleset;
|
||||
rule->cookie = cookie;
|
||||
rule->block = block;
|
||||
|
||||
refcount_inc(&ruleset->refcount);
|
||||
|
||||
return rule;
|
||||
}
|
||||
|
||||
struct list_head *
|
||||
prestera_acl_rule_match_list_get(struct prestera_acl_rule *rule)
|
||||
{
|
||||
return &rule->match_list;
|
||||
}
|
||||
|
||||
struct list_head *
|
||||
prestera_acl_rule_action_list_get(struct prestera_acl_rule *rule)
|
||||
{
|
||||
return &rule->action_list;
|
||||
}
|
||||
|
||||
int prestera_acl_rule_action_add(struct prestera_acl_rule *rule,
|
||||
struct prestera_acl_rule_action_entry *entry)
|
||||
{
|
||||
struct prestera_acl_rule_action_entry *a_entry;
|
||||
|
||||
a_entry = kmalloc(sizeof(*a_entry), GFP_KERNEL);
|
||||
if (!a_entry)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(a_entry, entry, sizeof(*entry));
|
||||
list_add(&a_entry->list, &rule->action_list);
|
||||
|
||||
rule->n_actions++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 prestera_acl_rule_action_len(struct prestera_acl_rule *rule)
|
||||
{
|
||||
return rule->n_actions;
|
||||
}
|
||||
|
||||
u32 prestera_acl_rule_priority_get(struct prestera_acl_rule *rule)
|
||||
{
|
||||
return rule->priority;
|
||||
}
|
||||
|
||||
void prestera_acl_rule_priority_set(struct prestera_acl_rule *rule,
|
||||
u32 priority)
|
||||
{
|
||||
rule->priority = priority;
|
||||
}
|
||||
|
||||
int prestera_acl_rule_match_add(struct prestera_acl_rule *rule,
|
||||
struct prestera_acl_rule_match_entry *entry)
|
||||
{
|
||||
struct prestera_acl_rule_match_entry *m_entry;
|
||||
|
||||
m_entry = kmalloc(sizeof(*m_entry), GFP_KERNEL);
|
||||
if (!m_entry)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(m_entry, entry, sizeof(*entry));
|
||||
list_add(&m_entry->list, &rule->match_list);
|
||||
|
||||
rule->n_matches++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 prestera_acl_rule_match_len(struct prestera_acl_rule *rule)
|
||||
{
|
||||
return rule->n_matches;
|
||||
}
|
||||
|
||||
void prestera_acl_rule_destroy(struct prestera_acl_rule *rule)
|
||||
{
|
||||
struct prestera_acl_rule_action_entry *a_entry;
|
||||
struct prestera_acl_rule_match_entry *m_entry;
|
||||
struct list_head *pos, *n;
|
||||
|
||||
list_for_each_safe(pos, n, &rule->match_list) {
|
||||
m_entry = list_entry(pos, typeof(*m_entry), list);
|
||||
list_del(pos);
|
||||
kfree(m_entry);
|
||||
}
|
||||
|
||||
list_for_each_safe(pos, n, &rule->action_list) {
|
||||
a_entry = list_entry(pos, typeof(*a_entry), list);
|
||||
list_del(pos);
|
||||
kfree(a_entry);
|
||||
}
|
||||
|
||||
prestera_acl_ruleset_put(rule->ruleset);
|
||||
kfree(rule);
|
||||
}
|
||||
|
||||
int prestera_acl_rule_add(struct prestera_switch *sw,
|
||||
struct prestera_acl_rule *rule)
|
||||
{
|
||||
u32 rule_id;
|
||||
int err;
|
||||
struct prestera_acl_ruleset *ruleset = rule->ruleset;
|
||||
struct prestera_flow_block *block = ruleset->ht_key.block;
|
||||
|
||||
/* try to add rule to hash table first */
|
||||
err = rhashtable_insert_fast(&rule->block->ruleset->rule_ht,
|
||||
&rule->ht_node,
|
||||
err = rhashtable_insert_fast(&ruleset->rule_ht, &rule->ht_node,
|
||||
prestera_acl_rule_ht_params);
|
||||
if (err)
|
||||
return err;
|
||||
goto err_ht_insert;
|
||||
|
||||
/* add rule to hw */
|
||||
err = prestera_hw_acl_rule_add(sw, rule, &rule_id);
|
||||
prestera_acl_rule_keymask_pcl_id_set(rule, ruleset->pcl_id);
|
||||
rule->re_arg.vtcam_id = ruleset->vtcam_id;
|
||||
rule->re_key.prio = rule->priority;
|
||||
|
||||
/* setup counter */
|
||||
rule->re_arg.count.valid = true;
|
||||
rule->re_arg.count.client = PRESTERA_HW_COUNTER_CLIENT_LOOKUP_0;
|
||||
|
||||
rule->re = prestera_acl_rule_entry_find(sw->acl, &rule->re_key);
|
||||
err = WARN_ON(rule->re) ? -EEXIST : 0;
|
||||
if (err)
|
||||
goto err_rule_add;
|
||||
|
||||
rule->id = rule_id;
|
||||
rule->re = prestera_acl_rule_entry_create(sw->acl, &rule->re_key,
|
||||
&rule->re_arg);
|
||||
err = !rule->re ? -EINVAL : 0;
|
||||
if (err)
|
||||
goto err_rule_add;
|
||||
|
||||
/* bind the block (all ports) to chain index 0 */
|
||||
if (!ruleset->rule_count) {
|
||||
err = prestera_acl_ruleset_block_bind(ruleset, block);
|
||||
if (err)
|
||||
goto err_acl_block_bind;
|
||||
}
|
||||
|
||||
list_add_tail(&rule->list, &sw->acl->rules);
|
||||
|
||||
ruleset->rule_count++;
|
||||
return 0;
|
||||
|
||||
err_acl_block_bind:
|
||||
prestera_acl_rule_entry_destroy(sw->acl, rule->re);
|
||||
err_rule_add:
|
||||
rhashtable_remove_fast(&rule->block->ruleset->rule_ht, &rule->ht_node,
|
||||
rule->re = NULL;
|
||||
rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
|
||||
prestera_acl_rule_ht_params);
|
||||
err_ht_insert:
|
||||
return err;
|
||||
}
|
||||
|
||||
void prestera_acl_rule_del(struct prestera_switch *sw,
|
||||
struct prestera_acl_rule *rule)
|
||||
{
|
||||
rhashtable_remove_fast(&rule->block->ruleset->rule_ht, &rule->ht_node,
|
||||
struct prestera_acl_ruleset *ruleset = rule->ruleset;
|
||||
struct prestera_flow_block *block = ruleset->ht_key.block;
|
||||
|
||||
rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
|
||||
prestera_acl_rule_ht_params);
|
||||
ruleset->rule_count--;
|
||||
list_del(&rule->list);
|
||||
prestera_hw_acl_rule_del(sw, rule->id);
|
||||
|
||||
prestera_acl_rule_entry_destroy(sw->acl, rule->re);
|
||||
|
||||
/* unbind block (all ports) */
|
||||
if (!ruleset->rule_count)
|
||||
prestera_acl_ruleset_block_unbind(ruleset, block);
|
||||
}
|
||||
|
||||
int prestera_acl_rule_get_stats(struct prestera_switch *sw,
|
||||
int prestera_acl_rule_get_stats(struct prestera_acl *acl,
|
||||
struct prestera_acl_rule *rule,
|
||||
u64 *packets, u64 *bytes, u64 *last_use)
|
||||
{
|
||||
@ -340,8 +401,10 @@ int prestera_acl_rule_get_stats(struct prestera_switch *sw,
|
||||
u64 current_bytes;
|
||||
int err;
|
||||
|
||||
err = prestera_hw_acl_rule_stats_get(sw, rule->id, ¤t_packets,
|
||||
¤t_bytes);
|
||||
err = prestera_counter_stats_get(acl->sw->counter,
|
||||
rule->re->counter.block,
|
||||
rule->re->counter.id,
|
||||
¤t_packets, ¤t_bytes);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -352,25 +415,276 @@ int prestera_acl_rule_get_stats(struct prestera_switch *sw,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct prestera_acl_rule_entry *
|
||||
prestera_acl_rule_entry_find(struct prestera_acl *acl,
|
||||
struct prestera_acl_rule_entry_key *key)
|
||||
{
|
||||
struct prestera_acl_rule_entry *e;
|
||||
|
||||
e = rhashtable_lookup_fast(&acl->acl_rule_entry_ht, key,
|
||||
__prestera_acl_rule_entry_ht_params);
|
||||
return IS_ERR(e) ? NULL : e;
|
||||
}
|
||||
|
||||
static int __prestera_acl_rule_entry2hw_del(struct prestera_switch *sw,
|
||||
struct prestera_acl_rule_entry *e)
|
||||
{
|
||||
return prestera_hw_vtcam_rule_del(sw, e->vtcam_id, e->hw_id);
|
||||
}
|
||||
|
||||
static int __prestera_acl_rule_entry2hw_add(struct prestera_switch *sw,
|
||||
struct prestera_acl_rule_entry *e)
|
||||
{
|
||||
struct prestera_acl_hw_action_info act_hw[PRESTERA_ACL_RULE_ACTION_MAX];
|
||||
int act_num;
|
||||
|
||||
memset(&act_hw, 0, sizeof(act_hw));
|
||||
act_num = 0;
|
||||
|
||||
/* accept */
|
||||
if (e->accept.valid) {
|
||||
act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_ACCEPT;
|
||||
act_num++;
|
||||
}
|
||||
/* drop */
|
||||
if (e->drop.valid) {
|
||||
act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_DROP;
|
||||
act_num++;
|
||||
}
|
||||
/* trap */
|
||||
if (e->trap.valid) {
|
||||
act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_TRAP;
|
||||
act_num++;
|
||||
}
|
||||
/* counter */
|
||||
if (e->counter.block) {
|
||||
act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_COUNT;
|
||||
act_hw[act_num].count.id = e->counter.id;
|
||||
act_num++;
|
||||
}
|
||||
|
||||
return prestera_hw_vtcam_rule_add(sw, e->vtcam_id, e->key.prio,
|
||||
e->key.match.key, e->key.match.mask,
|
||||
act_hw, act_num, &e->hw_id);
|
||||
}
|
||||
|
||||
static void
|
||||
__prestera_acl_rule_entry_act_destruct(struct prestera_switch *sw,
|
||||
struct prestera_acl_rule_entry *e)
|
||||
{
|
||||
/* counter */
|
||||
prestera_counter_put(sw->counter, e->counter.block, e->counter.id);
|
||||
}
|
||||
|
||||
void prestera_acl_rule_entry_destroy(struct prestera_acl *acl,
|
||||
struct prestera_acl_rule_entry *e)
|
||||
{
|
||||
int ret;
|
||||
|
||||
rhashtable_remove_fast(&acl->acl_rule_entry_ht, &e->ht_node,
|
||||
__prestera_acl_rule_entry_ht_params);
|
||||
|
||||
ret = __prestera_acl_rule_entry2hw_del(acl->sw, e);
|
||||
WARN_ON(ret && ret != -ENODEV);
|
||||
|
||||
__prestera_acl_rule_entry_act_destruct(acl->sw, e);
|
||||
kfree(e);
|
||||
}
|
||||
|
||||
static int
|
||||
__prestera_acl_rule_entry_act_construct(struct prestera_switch *sw,
|
||||
struct prestera_acl_rule_entry *e,
|
||||
struct prestera_acl_rule_entry_arg *arg)
|
||||
{
|
||||
/* accept */
|
||||
e->accept.valid = arg->accept.valid;
|
||||
/* drop */
|
||||
e->drop.valid = arg->drop.valid;
|
||||
/* trap */
|
||||
e->trap.valid = arg->trap.valid;
|
||||
/* counter */
|
||||
if (arg->count.valid) {
|
||||
int err;
|
||||
|
||||
err = prestera_counter_get(sw->counter, arg->count.client,
|
||||
&e->counter.block,
|
||||
&e->counter.id);
|
||||
if (err)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
__prestera_acl_rule_entry_act_destruct(sw, e);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct prestera_acl_rule_entry *
|
||||
prestera_acl_rule_entry_create(struct prestera_acl *acl,
|
||||
struct prestera_acl_rule_entry_key *key,
|
||||
struct prestera_acl_rule_entry_arg *arg)
|
||||
{
|
||||
struct prestera_acl_rule_entry *e;
|
||||
int err;
|
||||
|
||||
e = kzalloc(sizeof(*e), GFP_KERNEL);
|
||||
if (!e)
|
||||
goto err_kzalloc;
|
||||
|
||||
memcpy(&e->key, key, sizeof(*key));
|
||||
e->vtcam_id = arg->vtcam_id;
|
||||
err = __prestera_acl_rule_entry_act_construct(acl->sw, e, arg);
|
||||
if (err)
|
||||
goto err_act_construct;
|
||||
|
||||
err = __prestera_acl_rule_entry2hw_add(acl->sw, e);
|
||||
if (err)
|
||||
goto err_hw_add;
|
||||
|
||||
err = rhashtable_insert_fast(&acl->acl_rule_entry_ht, &e->ht_node,
|
||||
__prestera_acl_rule_entry_ht_params);
|
||||
if (err)
|
||||
goto err_ht_insert;
|
||||
|
||||
return e;
|
||||
|
||||
err_ht_insert:
|
||||
WARN_ON(__prestera_acl_rule_entry2hw_del(acl->sw, e));
|
||||
err_hw_add:
|
||||
__prestera_acl_rule_entry_act_destruct(acl->sw, e);
|
||||
err_act_construct:
|
||||
kfree(e);
|
||||
err_kzalloc:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup,
|
||||
void *keymask, u32 *vtcam_id)
|
||||
{
|
||||
struct prestera_acl_vtcam *vtcam;
|
||||
u32 new_vtcam_id;
|
||||
int err;
|
||||
|
||||
/* find the vtcam that suits keymask. We do not expect to have
|
||||
* a big number of vtcams, so, the list type for vtcam list is
|
||||
* fine for now
|
||||
*/
|
||||
list_for_each_entry(vtcam, &acl->vtcam_list, list) {
|
||||
if (lookup != vtcam->lookup)
|
||||
continue;
|
||||
|
||||
if (!keymask && !vtcam->is_keymask_set) {
|
||||
refcount_inc(&vtcam->refcount);
|
||||
goto vtcam_found;
|
||||
}
|
||||
|
||||
if (keymask && vtcam->is_keymask_set &&
|
||||
!memcmp(keymask, vtcam->keymask, sizeof(vtcam->keymask))) {
|
||||
refcount_inc(&vtcam->refcount);
|
||||
goto vtcam_found;
|
||||
}
|
||||
}
|
||||
|
||||
/* vtcam not found, try to create new one */
|
||||
vtcam = kzalloc(sizeof(*vtcam), GFP_KERNEL);
|
||||
if (!vtcam)
|
||||
return -ENOMEM;
|
||||
|
||||
err = prestera_hw_vtcam_create(acl->sw, lookup, keymask, &new_vtcam_id,
|
||||
PRESTERA_HW_VTCAM_DIR_INGRESS);
|
||||
if (err) {
|
||||
kfree(vtcam);
|
||||
return err;
|
||||
}
|
||||
|
||||
vtcam->id = new_vtcam_id;
|
||||
vtcam->lookup = lookup;
|
||||
if (keymask) {
|
||||
memcpy(vtcam->keymask, keymask, sizeof(vtcam->keymask));
|
||||
vtcam->is_keymask_set = true;
|
||||
}
|
||||
refcount_set(&vtcam->refcount, 1);
|
||||
list_add_rcu(&vtcam->list, &acl->vtcam_list);
|
||||
|
||||
vtcam_found:
|
||||
*vtcam_id = vtcam->id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prestera_acl_vtcam_id_put(struct prestera_acl *acl, u32 vtcam_id)
|
||||
{
|
||||
struct prestera_acl_vtcam *vtcam;
|
||||
int err;
|
||||
|
||||
list_for_each_entry(vtcam, &acl->vtcam_list, list) {
|
||||
if (vtcam_id != vtcam->id)
|
||||
continue;
|
||||
|
||||
if (!refcount_dec_and_test(&vtcam->refcount))
|
||||
return 0;
|
||||
|
||||
err = prestera_hw_vtcam_destroy(acl->sw, vtcam->id);
|
||||
if (err && err != -ENODEV) {
|
||||
refcount_set(&vtcam->refcount, 1);
|
||||
return err;
|
||||
}
|
||||
|
||||
list_del(&vtcam->list);
|
||||
kfree(vtcam);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int prestera_acl_init(struct prestera_switch *sw)
|
||||
{
|
||||
struct prestera_acl *acl;
|
||||
int err;
|
||||
|
||||
acl = kzalloc(sizeof(*acl), GFP_KERNEL);
|
||||
if (!acl)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&acl->rules);
|
||||
sw->acl = acl;
|
||||
acl->sw = sw;
|
||||
INIT_LIST_HEAD(&acl->rules);
|
||||
INIT_LIST_HEAD(&acl->vtcam_list);
|
||||
idr_init(&acl->uid);
|
||||
|
||||
err = rhashtable_init(&acl->acl_rule_entry_ht,
|
||||
&__prestera_acl_rule_entry_ht_params);
|
||||
if (err)
|
||||
goto err_acl_rule_entry_ht_init;
|
||||
|
||||
err = rhashtable_init(&acl->ruleset_ht,
|
||||
&prestera_acl_ruleset_ht_params);
|
||||
if (err)
|
||||
goto err_ruleset_ht_init;
|
||||
|
||||
sw->acl = acl;
|
||||
|
||||
return 0;
|
||||
|
||||
err_ruleset_ht_init:
|
||||
rhashtable_destroy(&acl->acl_rule_entry_ht);
|
||||
err_acl_rule_entry_ht_init:
|
||||
kfree(acl);
|
||||
return err;
|
||||
}
|
||||
|
||||
void prestera_acl_fini(struct prestera_switch *sw)
|
||||
{
|
||||
struct prestera_acl *acl = sw->acl;
|
||||
|
||||
WARN_ON(!idr_is_empty(&acl->uid));
|
||||
idr_destroy(&acl->uid);
|
||||
|
||||
WARN_ON(!list_empty(&acl->vtcam_list));
|
||||
WARN_ON(!list_empty(&acl->rules));
|
||||
|
||||
rhashtable_destroy(&acl->ruleset_ht);
|
||||
rhashtable_destroy(&acl->acl_rule_entry_ht);
|
||||
|
||||
kfree(acl);
|
||||
}
|
||||
|
@ -1,114 +1,130 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
|
||||
/* Copyright (c) 2020 Marvell International Ltd. All rights reserved. */
|
||||
/* Copyright (c) 2020-2021 Marvell International Ltd. All rights reserved. */
|
||||
|
||||
#ifndef _PRESTERA_ACL_H_
|
||||
#define _PRESTERA_ACL_H_
|
||||
|
||||
enum prestera_acl_rule_match_entry_type {
|
||||
PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_ETH_TYPE = 1,
|
||||
PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_ETH_DMAC,
|
||||
PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_ETH_SMAC,
|
||||
PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_IP_PROTO,
|
||||
PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_PORT,
|
||||
PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_IP_SRC,
|
||||
PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_IP_DST,
|
||||
PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_L4_PORT_SRC,
|
||||
PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_L4_PORT_DST,
|
||||
PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_L4_PORT_RANGE_SRC,
|
||||
PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_L4_PORT_RANGE_DST,
|
||||
PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_VLAN_ID,
|
||||
PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_VLAN_TPID,
|
||||
PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_ICMP_TYPE,
|
||||
PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_ICMP_CODE
|
||||
#include <linux/types.h>
|
||||
#include "prestera_counter.h"
|
||||
|
||||
#define PRESTERA_ACL_KEYMASK_PCL_ID 0x3FF
|
||||
#define PRESTERA_ACL_KEYMASK_PCL_ID_USER \
|
||||
(PRESTERA_ACL_KEYMASK_PCL_ID & 0x00FF)
|
||||
|
||||
#define rule_match_set_n(match_p, type, val_p, size) \
|
||||
memcpy(&(match_p)[PRESTERA_ACL_RULE_MATCH_TYPE_##type], \
|
||||
val_p, size)
|
||||
#define rule_match_set(match_p, type, val) \
|
||||
memcpy(&(match_p)[PRESTERA_ACL_RULE_MATCH_TYPE_##type], \
|
||||
&(val), sizeof(val))
|
||||
|
||||
enum prestera_acl_match_type {
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_PCL_ID,
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_ETH_TYPE,
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_ETH_DMAC_0,
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_ETH_DMAC_1,
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_ETH_SMAC_0,
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_ETH_SMAC_1,
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_IP_PROTO,
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_SYS_PORT,
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_SYS_DEV,
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_IP_SRC,
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_IP_DST,
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_L4_PORT_SRC,
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_L4_PORT_DST,
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_L4_PORT_RANGE_SRC,
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_L4_PORT_RANGE_DST,
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_VLAN_ID,
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_VLAN_TPID,
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_ICMP_TYPE,
|
||||
PRESTERA_ACL_RULE_MATCH_TYPE_ICMP_CODE,
|
||||
|
||||
__PRESTERA_ACL_RULE_MATCH_TYPE_MAX
|
||||
};
|
||||
|
||||
enum prestera_acl_rule_action {
|
||||
PRESTERA_ACL_RULE_ACTION_ACCEPT,
|
||||
PRESTERA_ACL_RULE_ACTION_DROP,
|
||||
PRESTERA_ACL_RULE_ACTION_TRAP
|
||||
PRESTERA_ACL_RULE_ACTION_ACCEPT = 0,
|
||||
PRESTERA_ACL_RULE_ACTION_DROP = 1,
|
||||
PRESTERA_ACL_RULE_ACTION_TRAP = 2,
|
||||
PRESTERA_ACL_RULE_ACTION_COUNT = 7,
|
||||
|
||||
PRESTERA_ACL_RULE_ACTION_MAX
|
||||
};
|
||||
|
||||
struct prestera_switch;
|
||||
struct prestera_port;
|
||||
struct prestera_acl_rule;
|
||||
struct prestera_acl_ruleset;
|
||||
|
||||
struct prestera_flow_block_binding {
|
||||
struct list_head list;
|
||||
struct prestera_port *port;
|
||||
int span_id;
|
||||
enum {
|
||||
PRESTERA_ACL_IFACE_TYPE_PORT,
|
||||
PRESTERA_ACL_IFACE_TYPE_INDEX
|
||||
};
|
||||
|
||||
struct prestera_flow_block {
|
||||
struct list_head binding_list;
|
||||
struct prestera_switch *sw;
|
||||
struct net *net;
|
||||
struct prestera_acl_ruleset *ruleset;
|
||||
struct flow_block_cb *block_cb;
|
||||
struct prestera_acl_match {
|
||||
__be32 key[__PRESTERA_ACL_RULE_MATCH_TYPE_MAX];
|
||||
__be32 mask[__PRESTERA_ACL_RULE_MATCH_TYPE_MAX];
|
||||
};
|
||||
|
||||
struct prestera_acl_rule_action_entry {
|
||||
struct list_head list;
|
||||
struct prestera_acl_action_count {
|
||||
u32 id;
|
||||
};
|
||||
|
||||
struct prestera_acl_rule_entry_key {
|
||||
u32 prio;
|
||||
struct prestera_acl_match match;
|
||||
};
|
||||
|
||||
struct prestera_acl_hw_action_info {
|
||||
enum prestera_acl_rule_action id;
|
||||
union {
|
||||
struct prestera_acl_action_count count;
|
||||
};
|
||||
};
|
||||
|
||||
struct prestera_acl_rule_match_entry {
|
||||
struct list_head list;
|
||||
enum prestera_acl_rule_match_entry_type type;
|
||||
union {
|
||||
/* This struct (arg) used only to be passed as parameter for
|
||||
* acl_rule_entry_create. Must be flat. Can contain object keys, which will be
|
||||
* resolved to object links, before saving to acl_rule_entry struct
|
||||
*/
|
||||
struct prestera_acl_rule_entry_arg {
|
||||
u32 vtcam_id;
|
||||
struct {
|
||||
struct {
|
||||
u8 key;
|
||||
u8 mask;
|
||||
} u8;
|
||||
u8 valid:1;
|
||||
} accept, drop, trap;
|
||||
struct {
|
||||
u16 key;
|
||||
u16 mask;
|
||||
} u16;
|
||||
struct {
|
||||
u32 key;
|
||||
u32 mask;
|
||||
} u32;
|
||||
struct {
|
||||
u64 key;
|
||||
u64 mask;
|
||||
} u64;
|
||||
struct {
|
||||
u8 key[ETH_ALEN];
|
||||
u8 mask[ETH_ALEN];
|
||||
} mac;
|
||||
} keymask;
|
||||
u8 valid:1;
|
||||
u32 client;
|
||||
} count;
|
||||
};
|
||||
};
|
||||
|
||||
struct prestera_acl_rule {
|
||||
struct rhash_head ht_node; /* Member of acl HT */
|
||||
struct list_head list;
|
||||
struct prestera_acl_ruleset *ruleset;
|
||||
unsigned long cookie;
|
||||
u32 priority;
|
||||
struct prestera_acl_rule_entry_key re_key;
|
||||
struct prestera_acl_rule_entry_arg re_arg;
|
||||
struct prestera_acl_rule_entry *re;
|
||||
};
|
||||
|
||||
struct prestera_acl_iface {
|
||||
union {
|
||||
struct prestera_port *port;
|
||||
u32 index;
|
||||
};
|
||||
u8 type;
|
||||
};
|
||||
|
||||
struct prestera_acl;
|
||||
struct prestera_switch;
|
||||
struct prestera_flow_block;
|
||||
|
||||
int prestera_acl_init(struct prestera_switch *sw);
|
||||
void prestera_acl_fini(struct prestera_switch *sw);
|
||||
struct prestera_flow_block *
|
||||
prestera_acl_block_create(struct prestera_switch *sw, struct net *net);
|
||||
void prestera_acl_block_destroy(struct prestera_flow_block *block);
|
||||
struct net *prestera_acl_block_net(struct prestera_flow_block *block);
|
||||
struct prestera_switch *prestera_acl_block_sw(struct prestera_flow_block *block);
|
||||
int prestera_acl_block_bind(struct prestera_flow_block *block,
|
||||
struct prestera_port *port);
|
||||
int prestera_acl_block_unbind(struct prestera_flow_block *block,
|
||||
struct prestera_port *port);
|
||||
struct prestera_acl_ruleset *
|
||||
prestera_acl_block_ruleset_get(struct prestera_flow_block *block);
|
||||
|
||||
struct prestera_acl_rule *
|
||||
prestera_acl_rule_create(struct prestera_flow_block *block,
|
||||
prestera_acl_rule_create(struct prestera_acl_ruleset *ruleset,
|
||||
unsigned long cookie);
|
||||
u32 prestera_acl_rule_priority_get(struct prestera_acl_rule *rule);
|
||||
void prestera_acl_rule_priority_set(struct prestera_acl_rule *rule,
|
||||
u32 priority);
|
||||
u16 prestera_acl_rule_ruleset_id_get(const struct prestera_acl_rule *rule);
|
||||
struct list_head *
|
||||
prestera_acl_rule_action_list_get(struct prestera_acl_rule *rule);
|
||||
u8 prestera_acl_rule_action_len(struct prestera_acl_rule *rule);
|
||||
u8 prestera_acl_rule_match_len(struct prestera_acl_rule *rule);
|
||||
int prestera_acl_rule_action_add(struct prestera_acl_rule *rule,
|
||||
struct prestera_acl_rule_action_entry *entry);
|
||||
struct list_head *
|
||||
prestera_acl_rule_match_list_get(struct prestera_acl_rule *rule);
|
||||
int prestera_acl_rule_match_add(struct prestera_acl_rule *rule,
|
||||
struct prestera_acl_rule_match_entry *entry);
|
||||
void prestera_acl_rule_destroy(struct prestera_acl_rule *rule);
|
||||
struct prestera_acl_rule *
|
||||
prestera_acl_rule_lookup(struct prestera_acl_ruleset *ruleset,
|
||||
@ -117,8 +133,37 @@ int prestera_acl_rule_add(struct prestera_switch *sw,
|
||||
struct prestera_acl_rule *rule);
|
||||
void prestera_acl_rule_del(struct prestera_switch *sw,
|
||||
struct prestera_acl_rule *rule);
|
||||
int prestera_acl_rule_get_stats(struct prestera_switch *sw,
|
||||
int prestera_acl_rule_get_stats(struct prestera_acl *acl,
|
||||
struct prestera_acl_rule *rule,
|
||||
u64 *packets, u64 *bytes, u64 *last_use);
|
||||
struct prestera_acl_rule_entry *
|
||||
prestera_acl_rule_entry_find(struct prestera_acl *acl,
|
||||
struct prestera_acl_rule_entry_key *key);
|
||||
void prestera_acl_rule_entry_destroy(struct prestera_acl *acl,
|
||||
struct prestera_acl_rule_entry *e);
|
||||
struct prestera_acl_rule_entry *
|
||||
prestera_acl_rule_entry_create(struct prestera_acl *acl,
|
||||
struct prestera_acl_rule_entry_key *key,
|
||||
struct prestera_acl_rule_entry_arg *arg);
|
||||
struct prestera_acl_ruleset *
|
||||
prestera_acl_ruleset_get(struct prestera_acl *acl,
|
||||
struct prestera_flow_block *block);
|
||||
struct prestera_acl_ruleset *
|
||||
prestera_acl_ruleset_lookup(struct prestera_acl *acl,
|
||||
struct prestera_flow_block *block);
|
||||
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);
|
||||
int prestera_acl_ruleset_bind(struct prestera_acl_ruleset *ruleset,
|
||||
struct prestera_port *port);
|
||||
int prestera_acl_ruleset_unbind(struct prestera_acl_ruleset *ruleset,
|
||||
struct prestera_port *port);
|
||||
void
|
||||
prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule,
|
||||
u16 pcl_id);
|
||||
|
||||
int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup,
|
||||
void *keymask, u32 *vtcam_id);
|
||||
int prestera_acl_vtcam_id_put(struct prestera_acl *acl, u32 vtcam_id);
|
||||
|
||||
#endif /* _PRESTERA_ACL_H_ */
|
||||
|
475
drivers/net/ethernet/marvell/prestera/prestera_counter.c
Normal file
475
drivers/net/ethernet/marvell/prestera/prestera_counter.c
Normal file
@ -0,0 +1,475 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
|
||||
/* Copyright (c) 2021 Marvell International Ltd. All rights reserved */
|
||||
|
||||
#include "prestera.h"
|
||||
#include "prestera_hw.h"
|
||||
#include "prestera_acl.h"
|
||||
#include "prestera_counter.h"
|
||||
|
||||
#define COUNTER_POLL_TIME (msecs_to_jiffies(1000))
|
||||
#define COUNTER_RESCHED_TIME (msecs_to_jiffies(50))
|
||||
#define COUNTER_BULK_SIZE (256)
|
||||
|
||||
struct prestera_counter {
|
||||
struct prestera_switch *sw;
|
||||
struct delayed_work stats_dw;
|
||||
struct mutex mtx; /* protect block_list */
|
||||
struct prestera_counter_block **block_list;
|
||||
u32 total_read;
|
||||
u32 block_list_len;
|
||||
u32 curr_idx;
|
||||
bool is_fetching;
|
||||
};
|
||||
|
||||
struct prestera_counter_block {
|
||||
struct list_head list;
|
||||
u32 id;
|
||||
u32 offset;
|
||||
u32 num_counters;
|
||||
u32 client;
|
||||
struct idr counter_idr;
|
||||
refcount_t refcnt;
|
||||
struct mutex mtx; /* protect stats and counter_idr */
|
||||
struct prestera_counter_stats *stats;
|
||||
u8 *counter_flag;
|
||||
bool is_updating;
|
||||
bool full;
|
||||
};
|
||||
|
||||
enum {
|
||||
COUNTER_FLAG_READY = 0,
|
||||
COUNTER_FLAG_INVALID = 1
|
||||
};
|
||||
|
||||
static bool
|
||||
prestera_counter_is_ready(struct prestera_counter_block *block, u32 id)
|
||||
{
|
||||
return block->counter_flag[id - block->offset] == COUNTER_FLAG_READY;
|
||||
}
|
||||
|
||||
static void prestera_counter_lock(struct prestera_counter *counter)
|
||||
{
|
||||
mutex_lock(&counter->mtx);
|
||||
}
|
||||
|
||||
static void prestera_counter_unlock(struct prestera_counter *counter)
|
||||
{
|
||||
mutex_unlock(&counter->mtx);
|
||||
}
|
||||
|
||||
static void prestera_counter_block_lock(struct prestera_counter_block *block)
|
||||
{
|
||||
mutex_lock(&block->mtx);
|
||||
}
|
||||
|
||||
static void prestera_counter_block_unlock(struct prestera_counter_block *block)
|
||||
{
|
||||
mutex_unlock(&block->mtx);
|
||||
}
|
||||
|
||||
static bool prestera_counter_block_incref(struct prestera_counter_block *block)
|
||||
{
|
||||
return refcount_inc_not_zero(&block->refcnt);
|
||||
}
|
||||
|
||||
static bool prestera_counter_block_decref(struct prestera_counter_block *block)
|
||||
{
|
||||
return refcount_dec_and_test(&block->refcnt);
|
||||
}
|
||||
|
||||
/* must be called with prestera_counter_block_lock() */
|
||||
static void prestera_counter_stats_clear(struct prestera_counter_block *block,
|
||||
u32 counter_id)
|
||||
{
|
||||
memset(&block->stats[counter_id - block->offset], 0,
|
||||
sizeof(*block->stats));
|
||||
}
|
||||
|
||||
static struct prestera_counter_block *
|
||||
prestera_counter_block_lookup_not_full(struct prestera_counter *counter,
|
||||
u32 client)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
prestera_counter_lock(counter);
|
||||
for (i = 0; i < counter->block_list_len; i++) {
|
||||
if (counter->block_list[i] &&
|
||||
counter->block_list[i]->client == client &&
|
||||
!counter->block_list[i]->full &&
|
||||
prestera_counter_block_incref(counter->block_list[i])) {
|
||||
prestera_counter_unlock(counter);
|
||||
return counter->block_list[i];
|
||||
}
|
||||
}
|
||||
prestera_counter_unlock(counter);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int prestera_counter_block_list_add(struct prestera_counter *counter,
|
||||
struct prestera_counter_block *block)
|
||||
{
|
||||
struct prestera_counter_block **arr;
|
||||
u32 i;
|
||||
|
||||
prestera_counter_lock(counter);
|
||||
|
||||
for (i = 0; i < counter->block_list_len; i++) {
|
||||
if (counter->block_list[i])
|
||||
continue;
|
||||
|
||||
counter->block_list[i] = block;
|
||||
prestera_counter_unlock(counter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
arr = krealloc(counter->block_list, (counter->block_list_len + 1) *
|
||||
sizeof(*counter->block_list), GFP_KERNEL);
|
||||
if (!arr) {
|
||||
prestera_counter_unlock(counter);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
counter->block_list = arr;
|
||||
counter->block_list[counter->block_list_len] = block;
|
||||
counter->block_list_len++;
|
||||
prestera_counter_unlock(counter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct prestera_counter_block *
|
||||
prestera_counter_block_get(struct prestera_counter *counter, u32 client)
|
||||
{
|
||||
struct prestera_counter_block *block;
|
||||
int err;
|
||||
|
||||
block = prestera_counter_block_lookup_not_full(counter, client);
|
||||
if (block)
|
||||
return block;
|
||||
|
||||
block = kzalloc(sizeof(*block), GFP_KERNEL);
|
||||
if (!block)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
err = prestera_hw_counter_block_get(counter->sw, client,
|
||||
&block->id, &block->offset,
|
||||
&block->num_counters);
|
||||
if (err)
|
||||
goto err_block;
|
||||
|
||||
block->stats = kcalloc(block->num_counters,
|
||||
sizeof(*block->stats), GFP_KERNEL);
|
||||
if (!block->stats) {
|
||||
err = -ENOMEM;
|
||||
goto err_stats;
|
||||
}
|
||||
|
||||
block->counter_flag = kcalloc(block->num_counters,
|
||||
sizeof(*block->counter_flag),
|
||||
GFP_KERNEL);
|
||||
if (!block->counter_flag) {
|
||||
err = -ENOMEM;
|
||||
goto err_flag;
|
||||
}
|
||||
|
||||
block->client = client;
|
||||
mutex_init(&block->mtx);
|
||||
refcount_set(&block->refcnt, 1);
|
||||
idr_init_base(&block->counter_idr, block->offset);
|
||||
|
||||
err = prestera_counter_block_list_add(counter, block);
|
||||
if (err)
|
||||
goto err_list_add;
|
||||
|
||||
return block;
|
||||
|
||||
err_list_add:
|
||||
idr_destroy(&block->counter_idr);
|
||||
mutex_destroy(&block->mtx);
|
||||
kfree(block->counter_flag);
|
||||
err_flag:
|
||||
kfree(block->stats);
|
||||
err_stats:
|
||||
prestera_hw_counter_block_release(counter->sw, block->id);
|
||||
err_block:
|
||||
kfree(block);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static void prestera_counter_block_put(struct prestera_counter *counter,
|
||||
struct prestera_counter_block *block)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
if (!prestera_counter_block_decref(block))
|
||||
return;
|
||||
|
||||
prestera_counter_lock(counter);
|
||||
for (i = 0; i < counter->block_list_len; i++) {
|
||||
if (counter->block_list[i] &&
|
||||
counter->block_list[i]->id == block->id) {
|
||||
counter->block_list[i] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
prestera_counter_unlock(counter);
|
||||
|
||||
WARN_ON(!idr_is_empty(&block->counter_idr));
|
||||
|
||||
prestera_hw_counter_block_release(counter->sw, block->id);
|
||||
idr_destroy(&block->counter_idr);
|
||||
mutex_destroy(&block->mtx);
|
||||
kfree(block->stats);
|
||||
kfree(block);
|
||||
}
|
||||
|
||||
static int prestera_counter_get_vacant(struct prestera_counter_block *block,
|
||||
u32 *id)
|
||||
{
|
||||
int free_id;
|
||||
|
||||
if (block->full)
|
||||
return -ENOSPC;
|
||||
|
||||
prestera_counter_block_lock(block);
|
||||
free_id = idr_alloc_cyclic(&block->counter_idr, NULL, block->offset,
|
||||
block->offset + block->num_counters,
|
||||
GFP_KERNEL);
|
||||
if (free_id < 0) {
|
||||
if (free_id == -ENOSPC)
|
||||
block->full = true;
|
||||
|
||||
prestera_counter_block_unlock(block);
|
||||
return free_id;
|
||||
}
|
||||
*id = free_id;
|
||||
prestera_counter_block_unlock(block);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prestera_counter_get(struct prestera_counter *counter, u32 client,
|
||||
struct prestera_counter_block **bl, u32 *counter_id)
|
||||
{
|
||||
struct prestera_counter_block *block;
|
||||
int err;
|
||||
u32 id;
|
||||
|
||||
get_next_block:
|
||||
block = prestera_counter_block_get(counter, client);
|
||||
if (IS_ERR(block))
|
||||
return PTR_ERR(block);
|
||||
|
||||
err = prestera_counter_get_vacant(block, &id);
|
||||
if (err) {
|
||||
prestera_counter_block_put(counter, block);
|
||||
|
||||
if (err == -ENOSPC)
|
||||
goto get_next_block;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
prestera_counter_block_lock(block);
|
||||
if (block->is_updating)
|
||||
block->counter_flag[id - block->offset] = COUNTER_FLAG_INVALID;
|
||||
prestera_counter_block_unlock(block);
|
||||
|
||||
*counter_id = id;
|
||||
*bl = block;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void prestera_counter_put(struct prestera_counter *counter,
|
||||
struct prestera_counter_block *block, u32 counter_id)
|
||||
{
|
||||
if (!block)
|
||||
return;
|
||||
|
||||
prestera_counter_block_lock(block);
|
||||
idr_remove(&block->counter_idr, counter_id);
|
||||
block->full = false;
|
||||
prestera_counter_stats_clear(block, counter_id);
|
||||
prestera_counter_block_unlock(block);
|
||||
|
||||
prestera_hw_counter_clear(counter->sw, block->id, counter_id);
|
||||
prestera_counter_block_put(counter, block);
|
||||
}
|
||||
|
||||
static u32 prestera_counter_block_idx_next(struct prestera_counter *counter,
|
||||
u32 curr_idx)
|
||||
{
|
||||
u32 idx, i, start = curr_idx + 1;
|
||||
|
||||
prestera_counter_lock(counter);
|
||||
for (i = 0; i < counter->block_list_len; i++) {
|
||||
idx = (start + i) % counter->block_list_len;
|
||||
if (!counter->block_list[idx])
|
||||
continue;
|
||||
|
||||
prestera_counter_unlock(counter);
|
||||
return idx;
|
||||
}
|
||||
prestera_counter_unlock(counter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct prestera_counter_block *
|
||||
prestera_counter_block_get_by_idx(struct prestera_counter *counter, u32 idx)
|
||||
{
|
||||
if (idx >= counter->block_list_len)
|
||||
return NULL;
|
||||
|
||||
prestera_counter_lock(counter);
|
||||
|
||||
if (!counter->block_list[idx] ||
|
||||
!prestera_counter_block_incref(counter->block_list[idx])) {
|
||||
prestera_counter_unlock(counter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prestera_counter_unlock(counter);
|
||||
return counter->block_list[idx];
|
||||
}
|
||||
|
||||
static void prestera_counter_stats_work(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *dl_work =
|
||||
container_of(work, struct delayed_work, work);
|
||||
struct prestera_counter *counter =
|
||||
container_of(dl_work, struct prestera_counter, stats_dw);
|
||||
struct prestera_counter_block *block;
|
||||
u32 resched_time = COUNTER_POLL_TIME;
|
||||
u32 count = COUNTER_BULK_SIZE;
|
||||
bool done = false;
|
||||
int err;
|
||||
u32 i;
|
||||
|
||||
block = prestera_counter_block_get_by_idx(counter, counter->curr_idx);
|
||||
if (!block) {
|
||||
if (counter->is_fetching)
|
||||
goto abort;
|
||||
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (!counter->is_fetching) {
|
||||
err = prestera_hw_counter_trigger(counter->sw, block->id);
|
||||
if (err)
|
||||
goto abort;
|
||||
|
||||
prestera_counter_block_lock(block);
|
||||
block->is_updating = true;
|
||||
prestera_counter_block_unlock(block);
|
||||
|
||||
counter->is_fetching = true;
|
||||
counter->total_read = 0;
|
||||
resched_time = COUNTER_RESCHED_TIME;
|
||||
goto resched;
|
||||
}
|
||||
|
||||
prestera_counter_block_lock(block);
|
||||
err = prestera_hw_counters_get(counter->sw, counter->total_read,
|
||||
&count, &done,
|
||||
&block->stats[counter->total_read]);
|
||||
prestera_counter_block_unlock(block);
|
||||
if (err)
|
||||
goto abort;
|
||||
|
||||
counter->total_read += count;
|
||||
if (!done || counter->total_read < block->num_counters) {
|
||||
resched_time = COUNTER_RESCHED_TIME;
|
||||
goto resched;
|
||||
}
|
||||
|
||||
for (i = 0; i < block->num_counters; i++) {
|
||||
if (block->counter_flag[i] == COUNTER_FLAG_INVALID) {
|
||||
prestera_counter_block_lock(block);
|
||||
block->counter_flag[i] = COUNTER_FLAG_READY;
|
||||
memset(&block->stats[i], 0, sizeof(*block->stats));
|
||||
prestera_counter_block_unlock(block);
|
||||
}
|
||||
}
|
||||
|
||||
prestera_counter_block_lock(block);
|
||||
block->is_updating = false;
|
||||
prestera_counter_block_unlock(block);
|
||||
|
||||
goto next;
|
||||
abort:
|
||||
prestera_hw_counter_abort(counter->sw);
|
||||
next:
|
||||
counter->is_fetching = false;
|
||||
counter->curr_idx =
|
||||
prestera_counter_block_idx_next(counter, counter->curr_idx);
|
||||
resched:
|
||||
if (block)
|
||||
prestera_counter_block_put(counter, block);
|
||||
|
||||
schedule_delayed_work(&counter->stats_dw, resched_time);
|
||||
}
|
||||
|
||||
/* Can be executed without rtnl_lock().
|
||||
* So pay attention when something changing.
|
||||
*/
|
||||
int prestera_counter_stats_get(struct prestera_counter *counter,
|
||||
struct prestera_counter_block *block,
|
||||
u32 counter_id, u64 *packets, u64 *bytes)
|
||||
{
|
||||
if (!block || !prestera_counter_is_ready(block, counter_id)) {
|
||||
*packets = 0;
|
||||
*bytes = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
prestera_counter_block_lock(block);
|
||||
*packets = block->stats[counter_id - block->offset].packets;
|
||||
*bytes = block->stats[counter_id - block->offset].bytes;
|
||||
|
||||
prestera_counter_stats_clear(block, counter_id);
|
||||
prestera_counter_block_unlock(block);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prestera_counter_init(struct prestera_switch *sw)
|
||||
{
|
||||
struct prestera_counter *counter;
|
||||
|
||||
counter = kzalloc(sizeof(*counter), GFP_KERNEL);
|
||||
if (!counter)
|
||||
return -ENOMEM;
|
||||
|
||||
counter->block_list = kzalloc(sizeof(*counter->block_list), GFP_KERNEL);
|
||||
if (!counter->block_list) {
|
||||
kfree(counter);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_init(&counter->mtx);
|
||||
counter->block_list_len = 1;
|
||||
counter->sw = sw;
|
||||
sw->counter = counter;
|
||||
|
||||
INIT_DELAYED_WORK(&counter->stats_dw, prestera_counter_stats_work);
|
||||
schedule_delayed_work(&counter->stats_dw, COUNTER_POLL_TIME);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void prestera_counter_fini(struct prestera_switch *sw)
|
||||
{
|
||||
struct prestera_counter *counter = sw->counter;
|
||||
u32 i;
|
||||
|
||||
cancel_delayed_work_sync(&counter->stats_dw);
|
||||
|
||||
for (i = 0; i < counter->block_list_len; i++)
|
||||
WARN_ON(counter->block_list[i]);
|
||||
|
||||
mutex_destroy(&counter->mtx);
|
||||
kfree(counter->block_list);
|
||||
kfree(counter);
|
||||
}
|
30
drivers/net/ethernet/marvell/prestera/prestera_counter.h
Normal file
30
drivers/net/ethernet/marvell/prestera/prestera_counter.h
Normal file
@ -0,0 +1,30 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
|
||||
/* Copyright (c) 2021 Marvell International Ltd. All rights reserved. */
|
||||
|
||||
#ifndef _PRESTERA_COUNTER_H_
|
||||
#define _PRESTERA_COUNTER_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct prestera_counter_stats {
|
||||
u64 packets;
|
||||
u64 bytes;
|
||||
};
|
||||
|
||||
struct prestera_switch;
|
||||
struct prestera_counter;
|
||||
struct prestera_counter_block;
|
||||
|
||||
int prestera_counter_init(struct prestera_switch *sw);
|
||||
void prestera_counter_fini(struct prestera_switch *sw);
|
||||
|
||||
int prestera_counter_get(struct prestera_counter *counter, u32 client,
|
||||
struct prestera_counter_block **block,
|
||||
u32 *counter_id);
|
||||
void prestera_counter_put(struct prestera_counter *counter,
|
||||
struct prestera_counter_block *block, u32 counter_id);
|
||||
int prestera_counter_stats_get(struct prestera_counter *counter,
|
||||
struct prestera_counter_block *block,
|
||||
u32 counter_id, u64 *packets, u64 *bytes);
|
||||
|
||||
#endif /* _PRESTERA_COUNTER_H_ */
|
@ -60,11 +60,100 @@ static int prestera_flow_block_cb(enum tc_setup_type type,
|
||||
}
|
||||
}
|
||||
|
||||
static void prestera_flow_block_destroy(void *cb_priv)
|
||||
{
|
||||
struct prestera_flow_block *block = cb_priv;
|
||||
|
||||
WARN_ON(!list_empty(&block->binding_list));
|
||||
|
||||
kfree(block);
|
||||
}
|
||||
|
||||
static struct prestera_flow_block *
|
||||
prestera_flow_block_create(struct prestera_switch *sw, struct net *net)
|
||||
{
|
||||
struct prestera_flow_block *block;
|
||||
|
||||
block = kzalloc(sizeof(*block), GFP_KERNEL);
|
||||
if (!block)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&block->binding_list);
|
||||
block->net = net;
|
||||
block->sw = sw;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
static void prestera_flow_block_release(void *cb_priv)
|
||||
{
|
||||
struct prestera_flow_block *block = cb_priv;
|
||||
|
||||
prestera_acl_block_destroy(block);
|
||||
prestera_flow_block_destroy(block);
|
||||
}
|
||||
|
||||
static bool
|
||||
prestera_flow_block_is_bound(const struct prestera_flow_block *block)
|
||||
{
|
||||
return block->ruleset_zero;
|
||||
}
|
||||
|
||||
static struct prestera_flow_block_binding *
|
||||
prestera_flow_block_lookup(struct prestera_flow_block *block,
|
||||
struct prestera_port *port)
|
||||
{
|
||||
struct prestera_flow_block_binding *binding;
|
||||
|
||||
list_for_each_entry(binding, &block->binding_list, list)
|
||||
if (binding->port == port)
|
||||
return binding;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int prestera_flow_block_bind(struct prestera_flow_block *block,
|
||||
struct prestera_port *port)
|
||||
{
|
||||
struct prestera_flow_block_binding *binding;
|
||||
int err;
|
||||
|
||||
binding = kzalloc(sizeof(*binding), GFP_KERNEL);
|
||||
if (!binding)
|
||||
return -ENOMEM;
|
||||
|
||||
binding->span_id = PRESTERA_SPAN_INVALID_ID;
|
||||
binding->port = port;
|
||||
|
||||
if (prestera_flow_block_is_bound(block)) {
|
||||
err = prestera_acl_ruleset_bind(block->ruleset_zero, port);
|
||||
if (err)
|
||||
goto err_ruleset_bind;
|
||||
}
|
||||
|
||||
list_add(&binding->list, &block->binding_list);
|
||||
return 0;
|
||||
|
||||
err_ruleset_bind:
|
||||
kfree(binding);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int prestera_flow_block_unbind(struct prestera_flow_block *block,
|
||||
struct prestera_port *port)
|
||||
{
|
||||
struct prestera_flow_block_binding *binding;
|
||||
|
||||
binding = prestera_flow_block_lookup(block, port);
|
||||
if (!binding)
|
||||
return -ENOENT;
|
||||
|
||||
list_del(&binding->list);
|
||||
|
||||
if (prestera_flow_block_is_bound(block))
|
||||
prestera_acl_ruleset_unbind(block->ruleset_zero, port);
|
||||
|
||||
kfree(binding);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct prestera_flow_block *
|
||||
@ -78,7 +167,7 @@ prestera_flow_block_get(struct prestera_switch *sw,
|
||||
block_cb = flow_block_cb_lookup(f->block,
|
||||
prestera_flow_block_cb, sw);
|
||||
if (!block_cb) {
|
||||
block = prestera_acl_block_create(sw, f->net);
|
||||
block = prestera_flow_block_create(sw, f->net);
|
||||
if (!block)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
@ -86,7 +175,7 @@ prestera_flow_block_get(struct prestera_switch *sw,
|
||||
sw, block,
|
||||
prestera_flow_block_release);
|
||||
if (IS_ERR(block_cb)) {
|
||||
prestera_acl_block_destroy(block);
|
||||
prestera_flow_block_destroy(block);
|
||||
return ERR_CAST(block_cb);
|
||||
}
|
||||
|
||||
@ -110,7 +199,7 @@ static void prestera_flow_block_put(struct prestera_flow_block *block)
|
||||
return;
|
||||
|
||||
flow_block_cb_free(block_cb);
|
||||
prestera_acl_block_destroy(block);
|
||||
prestera_flow_block_destroy(block);
|
||||
}
|
||||
|
||||
static int prestera_setup_flow_block_bind(struct prestera_port *port,
|
||||
@ -128,7 +217,7 @@ static int prestera_setup_flow_block_bind(struct prestera_port *port,
|
||||
|
||||
block_cb = block->block_cb;
|
||||
|
||||
err = prestera_acl_block_bind(block, port);
|
||||
err = prestera_flow_block_bind(block, port);
|
||||
if (err)
|
||||
goto err_block_bind;
|
||||
|
||||
@ -162,7 +251,7 @@ static void prestera_setup_flow_block_unbind(struct prestera_port *port,
|
||||
|
||||
prestera_span_destroy(block);
|
||||
|
||||
err = prestera_acl_block_unbind(block, port);
|
||||
err = prestera_flow_block_unbind(block, port);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
|
@ -7,6 +7,22 @@
|
||||
#include <net/flow_offload.h>
|
||||
|
||||
struct prestera_port;
|
||||
struct prestera_switch;
|
||||
|
||||
struct prestera_flow_block_binding {
|
||||
struct list_head list;
|
||||
struct prestera_port *port;
|
||||
int span_id;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
int prestera_flow_block_setup(struct prestera_port *port,
|
||||
struct flow_block_offload *f);
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "prestera.h"
|
||||
#include "prestera_acl.h"
|
||||
#include "prestera_flow.h"
|
||||
#include "prestera_flower.h"
|
||||
|
||||
static int prestera_flower_parse_actions(struct prestera_flow_block *block,
|
||||
@ -10,35 +11,38 @@ static int prestera_flower_parse_actions(struct prestera_flow_block *block,
|
||||
struct flow_action *flow_action,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct prestera_acl_rule_action_entry a_entry;
|
||||
const struct flow_action_entry *act;
|
||||
int err, i;
|
||||
int i;
|
||||
|
||||
/* whole struct (rule->re_arg) must be initialized with 0 */
|
||||
if (!flow_action_has_entries(flow_action))
|
||||
return 0;
|
||||
|
||||
flow_action_for_each(i, act, flow_action) {
|
||||
memset(&a_entry, 0, sizeof(a_entry));
|
||||
|
||||
switch (act->id) {
|
||||
case FLOW_ACTION_ACCEPT:
|
||||
a_entry.id = PRESTERA_ACL_RULE_ACTION_ACCEPT;
|
||||
if (rule->re_arg.accept.valid)
|
||||
return -EEXIST;
|
||||
|
||||
rule->re_arg.accept.valid = 1;
|
||||
break;
|
||||
case FLOW_ACTION_DROP:
|
||||
a_entry.id = PRESTERA_ACL_RULE_ACTION_DROP;
|
||||
if (rule->re_arg.drop.valid)
|
||||
return -EEXIST;
|
||||
|
||||
rule->re_arg.drop.valid = 1;
|
||||
break;
|
||||
case FLOW_ACTION_TRAP:
|
||||
a_entry.id = PRESTERA_ACL_RULE_ACTION_TRAP;
|
||||
if (rule->re_arg.trap.valid)
|
||||
return -EEXIST;
|
||||
|
||||
rule->re_arg.trap.valid = 1;
|
||||
break;
|
||||
default:
|
||||
NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
|
||||
pr_err("Unsupported action\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
err = prestera_acl_rule_action_add(rule, &a_entry);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -47,12 +51,12 @@ static int prestera_flower_parse_actions(struct prestera_flow_block *block,
|
||||
static int prestera_flower_parse_meta(struct prestera_acl_rule *rule,
|
||||
struct flow_cls_offload *f,
|
||||
struct prestera_flow_block *block)
|
||||
{
|
||||
struct flow_rule *f_rule = flow_cls_offload_flow_rule(f);
|
||||
struct prestera_acl_rule_match_entry m_entry = {0};
|
||||
{ struct flow_rule *f_rule = flow_cls_offload_flow_rule(f);
|
||||
struct prestera_acl_match *r_match = &rule->re_key.match;
|
||||
struct prestera_port *port;
|
||||
struct net_device *ingress_dev;
|
||||
struct flow_match_meta match;
|
||||
struct prestera_port *port;
|
||||
__be16 key, mask;
|
||||
|
||||
flow_rule_match_meta(f_rule, &match);
|
||||
if (match.mask->ingress_ifindex != 0xFFFFFFFF) {
|
||||
@ -61,7 +65,7 @@ static int prestera_flower_parse_meta(struct prestera_acl_rule *rule,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ingress_dev = __dev_get_by_index(prestera_acl_block_net(block),
|
||||
ingress_dev = __dev_get_by_index(block->net,
|
||||
match.key->ingress_ifindex);
|
||||
if (!ingress_dev) {
|
||||
NL_SET_ERR_MSG_MOD(f->common.extack,
|
||||
@ -76,22 +80,28 @@ static int prestera_flower_parse_meta(struct prestera_acl_rule *rule,
|
||||
}
|
||||
port = netdev_priv(ingress_dev);
|
||||
|
||||
m_entry.type = PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_PORT;
|
||||
m_entry.keymask.u64.key = port->hw_id | ((u64)port->dev_id << 32);
|
||||
m_entry.keymask.u64.mask = ~(u64)0;
|
||||
mask = htons(0x1FFF);
|
||||
key = htons(port->hw_id);
|
||||
rule_match_set(r_match->key, SYS_PORT, key);
|
||||
rule_match_set(r_match->mask, SYS_PORT, mask);
|
||||
|
||||
mask = htons(0x1FF);
|
||||
key = htons(port->dev_id);
|
||||
rule_match_set(r_match->key, SYS_DEV, key);
|
||||
rule_match_set(r_match->mask, SYS_DEV, mask);
|
||||
|
||||
return 0;
|
||||
|
||||
return prestera_acl_rule_match_add(rule, &m_entry);
|
||||
}
|
||||
|
||||
static int prestera_flower_parse(struct prestera_flow_block *block,
|
||||
struct prestera_acl_rule *rule,
|
||||
struct flow_cls_offload *f)
|
||||
{
|
||||
struct flow_rule *f_rule = flow_cls_offload_flow_rule(f);
|
||||
{ struct flow_rule *f_rule = flow_cls_offload_flow_rule(f);
|
||||
struct flow_dissector *dissector = f_rule->match.dissector;
|
||||
struct prestera_acl_rule_match_entry m_entry;
|
||||
u16 n_proto_mask = 0;
|
||||
u16 n_proto_key = 0;
|
||||
struct prestera_acl_match *r_match = &rule->re_key.match;
|
||||
__be16 n_proto_mask = 0;
|
||||
__be16 n_proto_key = 0;
|
||||
u16 addr_type = 0;
|
||||
u8 ip_proto = 0;
|
||||
int err;
|
||||
@ -129,32 +139,19 @@ static int prestera_flower_parse(struct prestera_flow_block *block,
|
||||
struct flow_match_basic match;
|
||||
|
||||
flow_rule_match_basic(f_rule, &match);
|
||||
n_proto_key = ntohs(match.key->n_proto);
|
||||
n_proto_mask = ntohs(match.mask->n_proto);
|
||||
n_proto_key = match.key->n_proto;
|
||||
n_proto_mask = match.mask->n_proto;
|
||||
|
||||
if (n_proto_key == ETH_P_ALL) {
|
||||
if (ntohs(match.key->n_proto) == ETH_P_ALL) {
|
||||
n_proto_key = 0;
|
||||
n_proto_mask = 0;
|
||||
}
|
||||
|
||||
/* add eth type key,mask */
|
||||
memset(&m_entry, 0, sizeof(m_entry));
|
||||
m_entry.type = PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_ETH_TYPE;
|
||||
m_entry.keymask.u16.key = n_proto_key;
|
||||
m_entry.keymask.u16.mask = n_proto_mask;
|
||||
err = prestera_acl_rule_match_add(rule, &m_entry);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* add ip proto key,mask */
|
||||
memset(&m_entry, 0, sizeof(m_entry));
|
||||
m_entry.type = PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_IP_PROTO;
|
||||
m_entry.keymask.u8.key = match.key->ip_proto;
|
||||
m_entry.keymask.u8.mask = match.mask->ip_proto;
|
||||
err = prestera_acl_rule_match_add(rule, &m_entry);
|
||||
if (err)
|
||||
return err;
|
||||
rule_match_set(r_match->key, ETH_TYPE, n_proto_key);
|
||||
rule_match_set(r_match->mask, ETH_TYPE, n_proto_mask);
|
||||
|
||||
rule_match_set(r_match->key, IP_PROTO, match.key->ip_proto);
|
||||
rule_match_set(r_match->mask, IP_PROTO, match.mask->ip_proto);
|
||||
ip_proto = match.key->ip_proto;
|
||||
}
|
||||
|
||||
@ -163,27 +160,27 @@ static int prestera_flower_parse(struct prestera_flow_block *block,
|
||||
|
||||
flow_rule_match_eth_addrs(f_rule, &match);
|
||||
|
||||
/* add ethernet dst key,mask */
|
||||
memset(&m_entry, 0, sizeof(m_entry));
|
||||
m_entry.type = PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_ETH_DMAC;
|
||||
memcpy(&m_entry.keymask.mac.key,
|
||||
&match.key->dst, sizeof(match.key->dst));
|
||||
memcpy(&m_entry.keymask.mac.mask,
|
||||
&match.mask->dst, sizeof(match.mask->dst));
|
||||
err = prestera_acl_rule_match_add(rule, &m_entry);
|
||||
if (err)
|
||||
return err;
|
||||
/* DA key, mask */
|
||||
rule_match_set_n(r_match->key,
|
||||
ETH_DMAC_0, &match.key->dst[0], 4);
|
||||
rule_match_set_n(r_match->key,
|
||||
ETH_DMAC_1, &match.key->dst[4], 2);
|
||||
|
||||
/* add ethernet src key,mask */
|
||||
memset(&m_entry, 0, sizeof(m_entry));
|
||||
m_entry.type = PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_ETH_SMAC;
|
||||
memcpy(&m_entry.keymask.mac.key,
|
||||
&match.key->src, sizeof(match.key->src));
|
||||
memcpy(&m_entry.keymask.mac.mask,
|
||||
&match.mask->src, sizeof(match.mask->src));
|
||||
err = prestera_acl_rule_match_add(rule, &m_entry);
|
||||
if (err)
|
||||
return err;
|
||||
rule_match_set_n(r_match->mask,
|
||||
ETH_DMAC_0, &match.mask->dst[0], 4);
|
||||
rule_match_set_n(r_match->mask,
|
||||
ETH_DMAC_1, &match.mask->dst[4], 2);
|
||||
|
||||
/* SA key, mask */
|
||||
rule_match_set_n(r_match->key,
|
||||
ETH_SMAC_0, &match.key->src[0], 4);
|
||||
rule_match_set_n(r_match->key,
|
||||
ETH_SMAC_1, &match.key->src[4], 2);
|
||||
|
||||
rule_match_set_n(r_match->mask,
|
||||
ETH_SMAC_0, &match.mask->src[0], 4);
|
||||
rule_match_set_n(r_match->mask,
|
||||
ETH_SMAC_1, &match.mask->src[4], 2);
|
||||
}
|
||||
|
||||
if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
|
||||
@ -191,25 +188,11 @@ static int prestera_flower_parse(struct prestera_flow_block *block,
|
||||
|
||||
flow_rule_match_ipv4_addrs(f_rule, &match);
|
||||
|
||||
memset(&m_entry, 0, sizeof(m_entry));
|
||||
m_entry.type = PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_IP_SRC;
|
||||
memcpy(&m_entry.keymask.u32.key,
|
||||
&match.key->src, sizeof(match.key->src));
|
||||
memcpy(&m_entry.keymask.u32.mask,
|
||||
&match.mask->src, sizeof(match.mask->src));
|
||||
err = prestera_acl_rule_match_add(rule, &m_entry);
|
||||
if (err)
|
||||
return err;
|
||||
rule_match_set(r_match->key, IP_SRC, match.key->src);
|
||||
rule_match_set(r_match->mask, IP_SRC, match.mask->src);
|
||||
|
||||
memset(&m_entry, 0, sizeof(m_entry));
|
||||
m_entry.type = PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_IP_DST;
|
||||
memcpy(&m_entry.keymask.u32.key,
|
||||
&match.key->dst, sizeof(match.key->dst));
|
||||
memcpy(&m_entry.keymask.u32.mask,
|
||||
&match.mask->dst, sizeof(match.mask->dst));
|
||||
err = prestera_acl_rule_match_add(rule, &m_entry);
|
||||
if (err)
|
||||
return err;
|
||||
rule_match_set(r_match->key, IP_DST, match.key->dst);
|
||||
rule_match_set(r_match->mask, IP_DST, match.mask->dst);
|
||||
}
|
||||
|
||||
if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_PORTS)) {
|
||||
@ -224,21 +207,11 @@ static int prestera_flower_parse(struct prestera_flow_block *block,
|
||||
|
||||
flow_rule_match_ports(f_rule, &match);
|
||||
|
||||
memset(&m_entry, 0, sizeof(m_entry));
|
||||
m_entry.type = PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_L4_PORT_SRC;
|
||||
m_entry.keymask.u16.key = ntohs(match.key->src);
|
||||
m_entry.keymask.u16.mask = ntohs(match.mask->src);
|
||||
err = prestera_acl_rule_match_add(rule, &m_entry);
|
||||
if (err)
|
||||
return err;
|
||||
rule_match_set(r_match->key, L4_PORT_SRC, match.key->src);
|
||||
rule_match_set(r_match->mask, L4_PORT_SRC, match.mask->src);
|
||||
|
||||
memset(&m_entry, 0, sizeof(m_entry));
|
||||
m_entry.type = PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_L4_PORT_DST;
|
||||
m_entry.keymask.u16.key = ntohs(match.key->dst);
|
||||
m_entry.keymask.u16.mask = ntohs(match.mask->dst);
|
||||
err = prestera_acl_rule_match_add(rule, &m_entry);
|
||||
if (err)
|
||||
return err;
|
||||
rule_match_set(r_match->key, L4_PORT_DST, match.key->dst);
|
||||
rule_match_set(r_match->mask, L4_PORT_DST, match.mask->dst);
|
||||
}
|
||||
|
||||
if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_VLAN)) {
|
||||
@ -247,22 +220,15 @@ static int prestera_flower_parse(struct prestera_flow_block *block,
|
||||
flow_rule_match_vlan(f_rule, &match);
|
||||
|
||||
if (match.mask->vlan_id != 0) {
|
||||
memset(&m_entry, 0, sizeof(m_entry));
|
||||
m_entry.type = PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_VLAN_ID;
|
||||
m_entry.keymask.u16.key = match.key->vlan_id;
|
||||
m_entry.keymask.u16.mask = match.mask->vlan_id;
|
||||
err = prestera_acl_rule_match_add(rule, &m_entry);
|
||||
if (err)
|
||||
return err;
|
||||
__be16 key = cpu_to_be16(match.key->vlan_id);
|
||||
__be16 mask = cpu_to_be16(match.mask->vlan_id);
|
||||
|
||||
rule_match_set(r_match->key, VLAN_ID, key);
|
||||
rule_match_set(r_match->mask, VLAN_ID, mask);
|
||||
}
|
||||
|
||||
memset(&m_entry, 0, sizeof(m_entry));
|
||||
m_entry.type = PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_VLAN_TPID;
|
||||
m_entry.keymask.u16.key = ntohs(match.key->vlan_tpid);
|
||||
m_entry.keymask.u16.mask = ntohs(match.mask->vlan_tpid);
|
||||
err = prestera_acl_rule_match_add(rule, &m_entry);
|
||||
if (err)
|
||||
return err;
|
||||
rule_match_set(r_match->key, VLAN_TPID, match.key->vlan_tpid);
|
||||
rule_match_set(r_match->mask, VLAN_TPID, match.mask->vlan_tpid);
|
||||
}
|
||||
|
||||
if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_ICMP)) {
|
||||
@ -270,90 +236,109 @@ static int prestera_flower_parse(struct prestera_flow_block *block,
|
||||
|
||||
flow_rule_match_icmp(f_rule, &match);
|
||||
|
||||
memset(&m_entry, 0, sizeof(m_entry));
|
||||
m_entry.type = PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_ICMP_TYPE;
|
||||
m_entry.keymask.u8.key = match.key->type;
|
||||
m_entry.keymask.u8.mask = match.mask->type;
|
||||
err = prestera_acl_rule_match_add(rule, &m_entry);
|
||||
if (err)
|
||||
return err;
|
||||
rule_match_set(r_match->key, ICMP_TYPE, match.key->type);
|
||||
rule_match_set(r_match->mask, ICMP_TYPE, match.mask->type);
|
||||
|
||||
memset(&m_entry, 0, sizeof(m_entry));
|
||||
m_entry.type = PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_ICMP_CODE;
|
||||
m_entry.keymask.u8.key = match.key->code;
|
||||
m_entry.keymask.u8.mask = match.mask->code;
|
||||
err = prestera_acl_rule_match_add(rule, &m_entry);
|
||||
if (err)
|
||||
return err;
|
||||
rule_match_set(r_match->key, ICMP_CODE, match.key->code);
|
||||
rule_match_set(r_match->mask, ICMP_CODE, match.mask->code);
|
||||
}
|
||||
|
||||
return prestera_flower_parse_actions(block, rule,
|
||||
&f->rule->action,
|
||||
return prestera_flower_parse_actions(block, rule, &f->rule->action,
|
||||
f->common.extack);
|
||||
}
|
||||
|
||||
int prestera_flower_replace(struct prestera_flow_block *block,
|
||||
struct flow_cls_offload *f)
|
||||
{
|
||||
struct prestera_switch *sw = prestera_acl_block_sw(block);
|
||||
struct prestera_acl_ruleset *ruleset;
|
||||
struct prestera_acl *acl = block->sw->acl;
|
||||
struct prestera_acl_rule *rule;
|
||||
int err;
|
||||
|
||||
rule = prestera_acl_rule_create(block, f->cookie);
|
||||
if (IS_ERR(rule))
|
||||
return PTR_ERR(rule);
|
||||
ruleset = prestera_acl_ruleset_get(acl, block);
|
||||
if (IS_ERR(ruleset))
|
||||
return PTR_ERR(ruleset);
|
||||
|
||||
/* increments the ruleset reference */
|
||||
rule = prestera_acl_rule_create(ruleset, f->cookie);
|
||||
if (IS_ERR(rule)) {
|
||||
err = PTR_ERR(rule);
|
||||
goto err_rule_create;
|
||||
}
|
||||
|
||||
err = prestera_flower_parse(block, rule, f);
|
||||
if (err)
|
||||
goto err_flower_parse;
|
||||
goto err_rule_add;
|
||||
|
||||
err = prestera_acl_rule_add(sw, rule);
|
||||
if (!prestera_acl_ruleset_is_offload(ruleset)) {
|
||||
err = prestera_acl_ruleset_offload(ruleset);
|
||||
if (err)
|
||||
goto err_ruleset_offload;
|
||||
}
|
||||
|
||||
err = prestera_acl_rule_add(block->sw, rule);
|
||||
if (err)
|
||||
goto err_rule_add;
|
||||
|
||||
prestera_acl_ruleset_put(ruleset);
|
||||
return 0;
|
||||
|
||||
err_ruleset_offload:
|
||||
err_rule_add:
|
||||
err_flower_parse:
|
||||
prestera_acl_rule_destroy(rule);
|
||||
err_rule_create:
|
||||
prestera_acl_ruleset_put(ruleset);
|
||||
return err;
|
||||
}
|
||||
|
||||
void prestera_flower_destroy(struct prestera_flow_block *block,
|
||||
struct flow_cls_offload *f)
|
||||
{
|
||||
struct prestera_acl_ruleset *ruleset;
|
||||
struct prestera_acl_rule *rule;
|
||||
struct prestera_switch *sw;
|
||||
|
||||
rule = prestera_acl_rule_lookup(prestera_acl_block_ruleset_get(block),
|
||||
f->cookie);
|
||||
ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block);
|
||||
if (IS_ERR(ruleset))
|
||||
return;
|
||||
|
||||
rule = prestera_acl_rule_lookup(ruleset, f->cookie);
|
||||
if (rule) {
|
||||
sw = prestera_acl_block_sw(block);
|
||||
prestera_acl_rule_del(sw, rule);
|
||||
prestera_acl_rule_del(block->sw, rule);
|
||||
prestera_acl_rule_destroy(rule);
|
||||
}
|
||||
prestera_acl_ruleset_put(ruleset);
|
||||
|
||||
}
|
||||
|
||||
int prestera_flower_stats(struct prestera_flow_block *block,
|
||||
struct flow_cls_offload *f)
|
||||
{
|
||||
struct prestera_switch *sw = prestera_acl_block_sw(block);
|
||||
struct prestera_acl_ruleset *ruleset;
|
||||
struct prestera_acl_rule *rule;
|
||||
u64 packets;
|
||||
u64 lastuse;
|
||||
u64 bytes;
|
||||
int err;
|
||||
|
||||
rule = prestera_acl_rule_lookup(prestera_acl_block_ruleset_get(block),
|
||||
f->cookie);
|
||||
if (!rule)
|
||||
return -EINVAL;
|
||||
ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block);
|
||||
if (IS_ERR(ruleset))
|
||||
return PTR_ERR(ruleset);
|
||||
|
||||
err = prestera_acl_rule_get_stats(sw, rule, &packets, &bytes, &lastuse);
|
||||
rule = prestera_acl_rule_lookup(ruleset, f->cookie);
|
||||
if (!rule) {
|
||||
err = -EINVAL;
|
||||
goto err_rule_get_stats;
|
||||
}
|
||||
|
||||
err = prestera_acl_rule_get_stats(block->sw->acl, rule, &packets,
|
||||
&bytes, &lastuse);
|
||||
if (err)
|
||||
return err;
|
||||
goto err_rule_get_stats;
|
||||
|
||||
flow_stats_update(&f->stats, bytes, packets, 0, lastuse,
|
||||
FLOW_ACTION_HW_STATS_IMMEDIATE);
|
||||
return 0;
|
||||
FLOW_ACTION_HW_STATS_DELAYED);
|
||||
|
||||
err_rule_get_stats:
|
||||
prestera_acl_ruleset_put(ruleset);
|
||||
return err;
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
|
||||
/* Copyright (c) 2020 Marvell International Ltd. All rights reserved. */
|
||||
/* Copyright (c) 2020-2021 Marvell International Ltd. All rights reserved. */
|
||||
|
||||
#ifndef _PRESTERA_FLOWER_H_
|
||||
#define _PRESTERA_FLOWER_H_
|
||||
|
||||
#include <net/pkt_cls.h>
|
||||
|
||||
struct prestera_switch;
|
||||
struct prestera_flow_block;
|
||||
|
||||
int prestera_flower_replace(struct prestera_flow_block *block,
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "prestera.h"
|
||||
#include "prestera_hw.h"
|
||||
#include "prestera_acl.h"
|
||||
#include "prestera_counter.h"
|
||||
|
||||
#define PRESTERA_SWITCH_INIT_TIMEOUT_MS (30 * 1000)
|
||||
|
||||
@ -38,13 +39,19 @@ enum prestera_cmd_type_t {
|
||||
PRESTERA_CMD_TYPE_BRIDGE_PORT_ADD = 0x402,
|
||||
PRESTERA_CMD_TYPE_BRIDGE_PORT_DELETE = 0x403,
|
||||
|
||||
PRESTERA_CMD_TYPE_ACL_RULE_ADD = 0x500,
|
||||
PRESTERA_CMD_TYPE_ACL_RULE_DELETE = 0x501,
|
||||
PRESTERA_CMD_TYPE_ACL_RULE_STATS_GET = 0x510,
|
||||
PRESTERA_CMD_TYPE_ACL_RULESET_CREATE = 0x520,
|
||||
PRESTERA_CMD_TYPE_ACL_RULESET_DELETE = 0x521,
|
||||
PRESTERA_CMD_TYPE_ACL_PORT_BIND = 0x530,
|
||||
PRESTERA_CMD_TYPE_ACL_PORT_UNBIND = 0x531,
|
||||
PRESTERA_CMD_TYPE_COUNTER_GET = 0x510,
|
||||
PRESTERA_CMD_TYPE_COUNTER_ABORT = 0x511,
|
||||
PRESTERA_CMD_TYPE_COUNTER_TRIGGER = 0x512,
|
||||
PRESTERA_CMD_TYPE_COUNTER_BLOCK_GET = 0x513,
|
||||
PRESTERA_CMD_TYPE_COUNTER_BLOCK_RELEASE = 0x514,
|
||||
PRESTERA_CMD_TYPE_COUNTER_CLEAR = 0x515,
|
||||
|
||||
PRESTERA_CMD_TYPE_VTCAM_CREATE = 0x540,
|
||||
PRESTERA_CMD_TYPE_VTCAM_DESTROY = 0x541,
|
||||
PRESTERA_CMD_TYPE_VTCAM_RULE_ADD = 0x550,
|
||||
PRESTERA_CMD_TYPE_VTCAM_RULE_DELETE = 0x551,
|
||||
PRESTERA_CMD_TYPE_VTCAM_IFACE_BIND = 0x560,
|
||||
PRESTERA_CMD_TYPE_VTCAM_IFACE_UNBIND = 0x561,
|
||||
|
||||
PRESTERA_CMD_TYPE_RXTX_INIT = 0x800,
|
||||
|
||||
@ -359,76 +366,84 @@ struct prestera_msg_bridge_resp {
|
||||
u8 pad[2];
|
||||
};
|
||||
|
||||
struct prestera_msg_acl_action {
|
||||
__le32 id;
|
||||
__le32 reserved[5];
|
||||
struct prestera_msg_vtcam_create_req {
|
||||
struct prestera_msg_cmd cmd;
|
||||
__le32 keymask[__PRESTERA_ACL_RULE_MATCH_TYPE_MAX];
|
||||
u8 direction;
|
||||
u8 lookup;
|
||||
u8 pad[2];
|
||||
};
|
||||
|
||||
struct prestera_msg_acl_match {
|
||||
__le32 type;
|
||||
struct prestera_msg_vtcam_destroy_req {
|
||||
struct prestera_msg_cmd cmd;
|
||||
__le32 vtcam_id;
|
||||
};
|
||||
|
||||
struct prestera_msg_vtcam_rule_add_req {
|
||||
struct prestera_msg_cmd cmd;
|
||||
__le32 key[__PRESTERA_ACL_RULE_MATCH_TYPE_MAX];
|
||||
__le32 keymask[__PRESTERA_ACL_RULE_MATCH_TYPE_MAX];
|
||||
__le32 vtcam_id;
|
||||
__le32 prio;
|
||||
__le32 n_act;
|
||||
};
|
||||
|
||||
struct prestera_msg_vtcam_rule_del_req {
|
||||
struct prestera_msg_cmd cmd;
|
||||
__le32 vtcam_id;
|
||||
__le32 id;
|
||||
};
|
||||
|
||||
struct prestera_msg_vtcam_bind_req {
|
||||
struct prestera_msg_cmd cmd;
|
||||
union {
|
||||
struct {
|
||||
__le32 hw_id;
|
||||
__le32 dev_id;
|
||||
} port;
|
||||
__le32 index;
|
||||
};
|
||||
__le32 vtcam_id;
|
||||
__le16 pcl_id;
|
||||
__le16 type;
|
||||
};
|
||||
|
||||
struct prestera_msg_vtcam_resp {
|
||||
struct prestera_msg_ret ret;
|
||||
__le32 vtcam_id;
|
||||
__le32 rule_id;
|
||||
};
|
||||
|
||||
struct prestera_msg_acl_action {
|
||||
__le32 id;
|
||||
__le32 __reserved;
|
||||
union {
|
||||
struct {
|
||||
u8 key;
|
||||
u8 mask;
|
||||
} u8;
|
||||
struct {
|
||||
__le16 key;
|
||||
__le16 mask;
|
||||
} u16;
|
||||
struct {
|
||||
__le32 key;
|
||||
__le32 mask;
|
||||
} u32;
|
||||
struct {
|
||||
__le64 key;
|
||||
__le64 mask;
|
||||
} u64;
|
||||
struct {
|
||||
u8 key[ETH_ALEN];
|
||||
u8 mask[ETH_ALEN];
|
||||
} mac;
|
||||
} keymask;
|
||||
__le32 id;
|
||||
} count;
|
||||
__le32 reserved[6];
|
||||
};
|
||||
};
|
||||
|
||||
struct prestera_msg_acl_rule_req {
|
||||
struct prestera_msg_counter_req {
|
||||
struct prestera_msg_cmd cmd;
|
||||
__le32 id;
|
||||
__le32 priority;
|
||||
__le16 ruleset_id;
|
||||
u8 n_actions;
|
||||
u8 n_matches;
|
||||
__le32 client;
|
||||
__le32 block_id;
|
||||
__le32 num_counters;
|
||||
};
|
||||
|
||||
struct prestera_msg_acl_rule_resp {
|
||||
struct prestera_msg_ret ret;
|
||||
__le32 id;
|
||||
};
|
||||
|
||||
struct prestera_msg_acl_rule_stats_resp {
|
||||
struct prestera_msg_ret ret;
|
||||
struct prestera_msg_counter_stats {
|
||||
__le64 packets;
|
||||
__le64 bytes;
|
||||
};
|
||||
|
||||
struct prestera_msg_acl_ruleset_bind_req {
|
||||
struct prestera_msg_cmd cmd;
|
||||
__le32 port;
|
||||
__le32 dev;
|
||||
__le16 ruleset_id;
|
||||
u8 pad[2];
|
||||
};
|
||||
|
||||
struct prestera_msg_acl_ruleset_req {
|
||||
struct prestera_msg_cmd cmd;
|
||||
__le16 id;
|
||||
u8 pad[2];
|
||||
};
|
||||
|
||||
struct prestera_msg_acl_ruleset_resp {
|
||||
struct prestera_msg_counter_resp {
|
||||
struct prestera_msg_ret ret;
|
||||
__le16 id;
|
||||
u8 pad[2];
|
||||
__le32 block_id;
|
||||
__le32 offset;
|
||||
__le32 num_counters;
|
||||
__le32 done;
|
||||
struct prestera_msg_counter_stats stats[0];
|
||||
};
|
||||
|
||||
struct prestera_msg_span_req {
|
||||
@ -521,14 +536,19 @@ static void prestera_hw_build_tests(void)
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_vlan_req) != 16);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_fdb_req) != 28);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_bridge_req) != 16);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_acl_rule_req) != 16);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_acl_ruleset_bind_req) != 16);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_acl_ruleset_req) != 8);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_span_req) != 16);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_stp_req) != 16);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_rxtx_req) != 8);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_lag_req) != 16);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_cpu_code_counter_req) != 8);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_vtcam_create_req) != 84);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_vtcam_destroy_req) != 8);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_vtcam_rule_add_req) != 168);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_vtcam_rule_del_req) != 12);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_vtcam_bind_req) != 20);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_acl_action) != 32);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_counter_req) != 16);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_counter_stats) != 16);
|
||||
|
||||
/* check responses */
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_common_resp) != 8);
|
||||
@ -537,11 +557,10 @@ static void prestera_hw_build_tests(void)
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_port_stats_resp) != 248);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_port_info_resp) != 20);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_bridge_resp) != 12);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_acl_rule_resp) != 12);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_acl_rule_stats_resp) != 24);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_acl_ruleset_resp) != 12);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_span_resp) != 12);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_rxtx_resp) != 12);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_vtcam_resp) != 16);
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_counter_resp) != 24);
|
||||
|
||||
/* check events */
|
||||
BUILD_BUG_ON(sizeof(struct prestera_msg_event_port) != 20);
|
||||
@ -1044,225 +1063,162 @@ static void prestera_hw_remote_fc_to_eth(u8 fc, bool *pause, bool *asym_pause)
|
||||
}
|
||||
}
|
||||
|
||||
int prestera_hw_acl_ruleset_create(struct prestera_switch *sw, u16 *ruleset_id)
|
||||
int prestera_hw_vtcam_create(struct prestera_switch *sw,
|
||||
u8 lookup, const u32 *keymask, u32 *vtcam_id,
|
||||
enum prestera_hw_vtcam_direction_t dir)
|
||||
{
|
||||
struct prestera_msg_acl_ruleset_resp resp;
|
||||
struct prestera_msg_acl_ruleset_req req;
|
||||
int err;
|
||||
struct prestera_msg_vtcam_resp resp;
|
||||
struct prestera_msg_vtcam_create_req req = {
|
||||
.lookup = lookup,
|
||||
.direction = dir,
|
||||
};
|
||||
|
||||
err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_ACL_RULESET_CREATE,
|
||||
if (keymask)
|
||||
memcpy(req.keymask, keymask, sizeof(req.keymask));
|
||||
else
|
||||
memset(req.keymask, 0, sizeof(req.keymask));
|
||||
|
||||
err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_VTCAM_CREATE,
|
||||
&req.cmd, sizeof(req), &resp.ret, sizeof(resp));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*ruleset_id = __le16_to_cpu(resp.id);
|
||||
|
||||
*vtcam_id = __le32_to_cpu(resp.vtcam_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prestera_hw_acl_ruleset_del(struct prestera_switch *sw, u16 ruleset_id)
|
||||
int prestera_hw_vtcam_destroy(struct prestera_switch *sw, u32 vtcam_id)
|
||||
{
|
||||
struct prestera_msg_acl_ruleset_req req = {
|
||||
.id = __cpu_to_le16(ruleset_id),
|
||||
struct prestera_msg_vtcam_destroy_req req = {
|
||||
.vtcam_id = __cpu_to_le32(vtcam_id),
|
||||
};
|
||||
|
||||
return prestera_cmd(sw, PRESTERA_CMD_TYPE_ACL_RULESET_DELETE,
|
||||
return prestera_cmd(sw, PRESTERA_CMD_TYPE_VTCAM_DESTROY,
|
||||
&req.cmd, sizeof(req));
|
||||
}
|
||||
|
||||
static int prestera_hw_acl_actions_put(struct prestera_msg_acl_action *action,
|
||||
struct prestera_acl_rule *rule)
|
||||
static int
|
||||
prestera_acl_rule_add_put_action(struct prestera_msg_acl_action *action,
|
||||
struct prestera_acl_hw_action_info *info)
|
||||
{
|
||||
struct list_head *a_list = prestera_acl_rule_action_list_get(rule);
|
||||
struct prestera_acl_rule_action_entry *a_entry;
|
||||
int i = 0;
|
||||
action->id = __cpu_to_le32(info->id);
|
||||
|
||||
list_for_each_entry(a_entry, a_list, list) {
|
||||
action[i].id = __cpu_to_le32(a_entry->id);
|
||||
|
||||
switch (a_entry->id) {
|
||||
case PRESTERA_ACL_RULE_ACTION_ACCEPT:
|
||||
case PRESTERA_ACL_RULE_ACTION_DROP:
|
||||
case PRESTERA_ACL_RULE_ACTION_TRAP:
|
||||
/* just rule action id, no specific data */
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
i++;
|
||||
switch (info->id) {
|
||||
case PRESTERA_ACL_RULE_ACTION_ACCEPT:
|
||||
case PRESTERA_ACL_RULE_ACTION_DROP:
|
||||
case PRESTERA_ACL_RULE_ACTION_TRAP:
|
||||
/* just rule action id, no specific data */
|
||||
break;
|
||||
case PRESTERA_ACL_RULE_ACTION_COUNT:
|
||||
action->count.id = __cpu_to_le32(info->count.id);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prestera_hw_acl_matches_put(struct prestera_msg_acl_match *match,
|
||||
struct prestera_acl_rule *rule)
|
||||
int prestera_hw_vtcam_rule_add(struct prestera_switch *sw,
|
||||
u32 vtcam_id, u32 prio, void *key, void *keymask,
|
||||
struct prestera_acl_hw_action_info *act,
|
||||
u8 n_act, u32 *rule_id)
|
||||
{
|
||||
struct list_head *m_list = prestera_acl_rule_match_list_get(rule);
|
||||
struct prestera_acl_rule_match_entry *m_entry;
|
||||
int i = 0;
|
||||
|
||||
list_for_each_entry(m_entry, m_list, list) {
|
||||
match[i].type = __cpu_to_le32(m_entry->type);
|
||||
|
||||
switch (m_entry->type) {
|
||||
case PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_ETH_TYPE:
|
||||
case PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_L4_PORT_SRC:
|
||||
case PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_L4_PORT_DST:
|
||||
case PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_VLAN_ID:
|
||||
case PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_VLAN_TPID:
|
||||
match[i].keymask.u16.key =
|
||||
__cpu_to_le16(m_entry->keymask.u16.key);
|
||||
match[i].keymask.u16.mask =
|
||||
__cpu_to_le16(m_entry->keymask.u16.mask);
|
||||
break;
|
||||
case PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_ICMP_TYPE:
|
||||
case PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_ICMP_CODE:
|
||||
case PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_IP_PROTO:
|
||||
match[i].keymask.u8.key = m_entry->keymask.u8.key;
|
||||
match[i].keymask.u8.mask = m_entry->keymask.u8.mask;
|
||||
break;
|
||||
case PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_ETH_SMAC:
|
||||
case PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_ETH_DMAC:
|
||||
memcpy(match[i].keymask.mac.key,
|
||||
m_entry->keymask.mac.key,
|
||||
sizeof(match[i].keymask.mac.key));
|
||||
memcpy(match[i].keymask.mac.mask,
|
||||
m_entry->keymask.mac.mask,
|
||||
sizeof(match[i].keymask.mac.mask));
|
||||
break;
|
||||
case PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_IP_SRC:
|
||||
case PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_IP_DST:
|
||||
case PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_L4_PORT_RANGE_SRC:
|
||||
case PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_L4_PORT_RANGE_DST:
|
||||
match[i].keymask.u32.key =
|
||||
__cpu_to_le32(m_entry->keymask.u32.key);
|
||||
match[i].keymask.u32.mask =
|
||||
__cpu_to_le32(m_entry->keymask.u32.mask);
|
||||
break;
|
||||
case PRESTERA_ACL_RULE_MATCH_ENTRY_TYPE_PORT:
|
||||
match[i].keymask.u64.key =
|
||||
__cpu_to_le64(m_entry->keymask.u64.key);
|
||||
match[i].keymask.u64.mask =
|
||||
__cpu_to_le64(m_entry->keymask.u64.mask);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prestera_hw_acl_rule_add(struct prestera_switch *sw,
|
||||
struct prestera_acl_rule *rule,
|
||||
u32 *rule_id)
|
||||
{
|
||||
struct prestera_msg_acl_action *actions;
|
||||
struct prestera_msg_acl_match *matches;
|
||||
struct prestera_msg_acl_rule_resp resp;
|
||||
struct prestera_msg_acl_rule_req *req;
|
||||
u8 n_actions;
|
||||
u8 n_matches;
|
||||
struct prestera_msg_acl_action *actions_msg;
|
||||
struct prestera_msg_vtcam_rule_add_req *req;
|
||||
struct prestera_msg_vtcam_resp resp;
|
||||
void *buff;
|
||||
u32 size;
|
||||
int err;
|
||||
u8 i;
|
||||
|
||||
n_actions = prestera_acl_rule_action_len(rule);
|
||||
n_matches = prestera_acl_rule_match_len(rule);
|
||||
|
||||
size = sizeof(*req) + sizeof(*actions) * n_actions +
|
||||
sizeof(*matches) * n_matches;
|
||||
size = sizeof(*req) + sizeof(*actions_msg) * n_act;
|
||||
|
||||
buff = kzalloc(size, GFP_KERNEL);
|
||||
if (!buff)
|
||||
return -ENOMEM;
|
||||
|
||||
req = buff;
|
||||
actions = buff + sizeof(*req);
|
||||
matches = buff + sizeof(*req) + sizeof(*actions) * n_actions;
|
||||
|
||||
/* put acl actions into the message */
|
||||
err = prestera_hw_acl_actions_put(actions, rule);
|
||||
if (err)
|
||||
goto free_buff;
|
||||
req->n_act = __cpu_to_le32(n_act);
|
||||
actions_msg = buff + sizeof(*req);
|
||||
|
||||
/* put acl matches into the message */
|
||||
err = prestera_hw_acl_matches_put(matches, rule);
|
||||
if (err)
|
||||
goto free_buff;
|
||||
memcpy(req->key, key, sizeof(req->key));
|
||||
memcpy(req->keymask, keymask, sizeof(req->keymask));
|
||||
|
||||
req->ruleset_id = __cpu_to_le16(prestera_acl_rule_ruleset_id_get(rule));
|
||||
req->priority = __cpu_to_le32(prestera_acl_rule_priority_get(rule));
|
||||
req->n_actions = prestera_acl_rule_action_len(rule);
|
||||
req->n_matches = prestera_acl_rule_match_len(rule);
|
||||
/* put acl actions into the message */
|
||||
for (i = 0; i < n_act; i++) {
|
||||
err = prestera_acl_rule_add_put_action(&actions_msg[i],
|
||||
&act[i]);
|
||||
if (err)
|
||||
goto free_buff;
|
||||
}
|
||||
|
||||
err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_ACL_RULE_ADD,
|
||||
req->vtcam_id = __cpu_to_le32(vtcam_id);
|
||||
req->prio = __cpu_to_le32(prio);
|
||||
|
||||
err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_VTCAM_RULE_ADD,
|
||||
&req->cmd, size, &resp.ret, sizeof(resp));
|
||||
if (err)
|
||||
goto free_buff;
|
||||
|
||||
*rule_id = __le32_to_cpu(resp.id);
|
||||
*rule_id = __le32_to_cpu(resp.rule_id);
|
||||
free_buff:
|
||||
kfree(buff);
|
||||
return err;
|
||||
}
|
||||
|
||||
int prestera_hw_acl_rule_del(struct prestera_switch *sw, u32 rule_id)
|
||||
int prestera_hw_vtcam_rule_del(struct prestera_switch *sw,
|
||||
u32 vtcam_id, u32 rule_id)
|
||||
{
|
||||
struct prestera_msg_acl_rule_req req = {
|
||||
struct prestera_msg_vtcam_rule_del_req req = {
|
||||
.vtcam_id = __cpu_to_le32(vtcam_id),
|
||||
.id = __cpu_to_le32(rule_id)
|
||||
};
|
||||
|
||||
return prestera_cmd(sw, PRESTERA_CMD_TYPE_ACL_RULE_DELETE,
|
||||
return prestera_cmd(sw, PRESTERA_CMD_TYPE_VTCAM_RULE_DELETE,
|
||||
&req.cmd, sizeof(req));
|
||||
}
|
||||
|
||||
int prestera_hw_acl_rule_stats_get(struct prestera_switch *sw, u32 rule_id,
|
||||
u64 *packets, u64 *bytes)
|
||||
int prestera_hw_vtcam_iface_bind(struct prestera_switch *sw,
|
||||
struct prestera_acl_iface *iface,
|
||||
u32 vtcam_id, u16 pcl_id)
|
||||
{
|
||||
struct prestera_msg_acl_rule_stats_resp resp;
|
||||
struct prestera_msg_acl_rule_req req = {
|
||||
.id = __cpu_to_le32(rule_id)
|
||||
};
|
||||
int err;
|
||||
|
||||
err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_ACL_RULE_STATS_GET,
|
||||
&req.cmd, sizeof(req), &resp.ret, sizeof(resp));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*packets = __le64_to_cpu(resp.packets);
|
||||
*bytes = __le64_to_cpu(resp.bytes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prestera_hw_acl_port_bind(const struct prestera_port *port, u16 ruleset_id)
|
||||
{
|
||||
struct prestera_msg_acl_ruleset_bind_req req = {
|
||||
.port = __cpu_to_le32(port->hw_id),
|
||||
.dev = __cpu_to_le32(port->dev_id),
|
||||
.ruleset_id = __cpu_to_le16(ruleset_id),
|
||||
struct prestera_msg_vtcam_bind_req req = {
|
||||
.vtcam_id = __cpu_to_le32(vtcam_id),
|
||||
.type = __cpu_to_le16(iface->type),
|
||||
.pcl_id = __cpu_to_le16(pcl_id)
|
||||
};
|
||||
|
||||
return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_ACL_PORT_BIND,
|
||||
if (iface->type == PRESTERA_ACL_IFACE_TYPE_PORT) {
|
||||
req.port.dev_id = __cpu_to_le32(iface->port->dev_id);
|
||||
req.port.hw_id = __cpu_to_le32(iface->port->hw_id);
|
||||
} else {
|
||||
req.index = __cpu_to_le32(iface->index);
|
||||
}
|
||||
|
||||
return prestera_cmd(sw, PRESTERA_CMD_TYPE_VTCAM_IFACE_BIND,
|
||||
&req.cmd, sizeof(req));
|
||||
}
|
||||
|
||||
int prestera_hw_acl_port_unbind(const struct prestera_port *port,
|
||||
u16 ruleset_id)
|
||||
int prestera_hw_vtcam_iface_unbind(struct prestera_switch *sw,
|
||||
struct prestera_acl_iface *iface,
|
||||
u32 vtcam_id)
|
||||
{
|
||||
struct prestera_msg_acl_ruleset_bind_req req = {
|
||||
.port = __cpu_to_le32(port->hw_id),
|
||||
.dev = __cpu_to_le32(port->dev_id),
|
||||
.ruleset_id = __cpu_to_le16(ruleset_id),
|
||||
struct prestera_msg_vtcam_bind_req req = {
|
||||
.vtcam_id = __cpu_to_le32(vtcam_id),
|
||||
.type = __cpu_to_le16(iface->type)
|
||||
};
|
||||
|
||||
return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_ACL_PORT_UNBIND,
|
||||
if (iface->type == PRESTERA_ACL_IFACE_TYPE_PORT) {
|
||||
req.port.dev_id = __cpu_to_le32(iface->port->dev_id);
|
||||
req.port.hw_id = __cpu_to_le32(iface->port->hw_id);
|
||||
} else {
|
||||
req.index = __cpu_to_le32(iface->index);
|
||||
}
|
||||
|
||||
return prestera_cmd(sw, PRESTERA_CMD_TYPE_VTCAM_IFACE_UNBIND,
|
||||
&req.cmd, sizeof(req));
|
||||
}
|
||||
|
||||
@ -1916,3 +1872,100 @@ void prestera_hw_event_handler_unregister(struct prestera_switch *sw,
|
||||
list_del_rcu(&eh->list);
|
||||
kfree_rcu(eh, rcu);
|
||||
}
|
||||
|
||||
int prestera_hw_counter_trigger(struct prestera_switch *sw, u32 block_id)
|
||||
{
|
||||
struct prestera_msg_counter_req req = {
|
||||
.block_id = __cpu_to_le32(block_id)
|
||||
};
|
||||
|
||||
return prestera_cmd(sw, PRESTERA_CMD_TYPE_COUNTER_TRIGGER,
|
||||
&req.cmd, sizeof(req));
|
||||
}
|
||||
|
||||
int prestera_hw_counter_abort(struct prestera_switch *sw)
|
||||
{
|
||||
struct prestera_msg_counter_req req;
|
||||
|
||||
return prestera_cmd(sw, PRESTERA_CMD_TYPE_COUNTER_ABORT,
|
||||
&req.cmd, sizeof(req));
|
||||
}
|
||||
|
||||
int prestera_hw_counters_get(struct prestera_switch *sw, u32 idx,
|
||||
u32 *len, bool *done,
|
||||
struct prestera_counter_stats *stats)
|
||||
{
|
||||
struct prestera_msg_counter_resp *resp;
|
||||
struct prestera_msg_counter_req req = {
|
||||
.block_id = __cpu_to_le32(idx),
|
||||
.num_counters = __cpu_to_le32(*len),
|
||||
};
|
||||
size_t size = sizeof(*resp) + sizeof(*resp->stats) * (*len);
|
||||
int err, i;
|
||||
|
||||
resp = kmalloc(size, GFP_KERNEL);
|
||||
if (!resp)
|
||||
return -ENOMEM;
|
||||
|
||||
err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_COUNTER_GET,
|
||||
&req.cmd, sizeof(req), &resp->ret, size);
|
||||
if (err)
|
||||
goto free_buff;
|
||||
|
||||
for (i = 0; i < __le32_to_cpu(resp->num_counters); i++) {
|
||||
stats[i].packets += __le64_to_cpu(resp->stats[i].packets);
|
||||
stats[i].bytes += __le64_to_cpu(resp->stats[i].bytes);
|
||||
}
|
||||
|
||||
*len = __le32_to_cpu(resp->num_counters);
|
||||
*done = __le32_to_cpu(resp->done);
|
||||
|
||||
free_buff:
|
||||
kfree(resp);
|
||||
return err;
|
||||
}
|
||||
|
||||
int prestera_hw_counter_block_get(struct prestera_switch *sw,
|
||||
u32 client, u32 *block_id, u32 *offset,
|
||||
u32 *num_counters)
|
||||
{
|
||||
struct prestera_msg_counter_resp resp;
|
||||
struct prestera_msg_counter_req req = {
|
||||
.client = __cpu_to_le32(client)
|
||||
};
|
||||
int err;
|
||||
|
||||
err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_COUNTER_BLOCK_GET,
|
||||
&req.cmd, sizeof(req), &resp.ret, sizeof(resp));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*block_id = __le32_to_cpu(resp.block_id);
|
||||
*offset = __le32_to_cpu(resp.offset);
|
||||
*num_counters = __le32_to_cpu(resp.num_counters);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prestera_hw_counter_block_release(struct prestera_switch *sw,
|
||||
u32 block_id)
|
||||
{
|
||||
struct prestera_msg_counter_req req = {
|
||||
.block_id = __cpu_to_le32(block_id)
|
||||
};
|
||||
|
||||
return prestera_cmd(sw, PRESTERA_CMD_TYPE_COUNTER_BLOCK_RELEASE,
|
||||
&req.cmd, sizeof(req));
|
||||
}
|
||||
|
||||
int prestera_hw_counter_clear(struct prestera_switch *sw, u32 block_id,
|
||||
u32 counter_id)
|
||||
{
|
||||
struct prestera_msg_counter_req req = {
|
||||
.block_id = __cpu_to_le32(block_id),
|
||||
.num_counters = __cpu_to_le32(counter_id)
|
||||
};
|
||||
|
||||
return prestera_cmd(sw, PRESTERA_CMD_TYPE_COUNTER_CLEAR,
|
||||
&req.cmd, sizeof(req));
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#define _PRESTERA_HW_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include "prestera_acl.h"
|
||||
|
||||
enum prestera_accept_frm_type {
|
||||
PRESTERA_ACCEPT_FRAME_TYPE_TAGGED,
|
||||
@ -111,18 +112,31 @@ enum prestera_hw_cpu_code_cnt_t {
|
||||
PRESTERA_HW_CPU_CODE_CNT_TYPE_TRAP = 1,
|
||||
};
|
||||
|
||||
enum prestera_hw_vtcam_direction_t {
|
||||
PRESTERA_HW_VTCAM_DIR_INGRESS = 0,
|
||||
PRESTERA_HW_VTCAM_DIR_EGRESS = 1,
|
||||
};
|
||||
|
||||
enum {
|
||||
PRESTERA_HW_COUNTER_CLIENT_LOOKUP_0 = 0,
|
||||
PRESTERA_HW_COUNTER_CLIENT_LOOKUP_1 = 1,
|
||||
PRESTERA_HW_COUNTER_CLIENT_LOOKUP_2 = 2,
|
||||
};
|
||||
|
||||
struct prestera_switch;
|
||||
struct prestera_port;
|
||||
struct prestera_port_stats;
|
||||
struct prestera_port_caps;
|
||||
enum prestera_event_type;
|
||||
struct prestera_event;
|
||||
struct prestera_acl_rule;
|
||||
|
||||
typedef void (*prestera_event_cb_t)
|
||||
(struct prestera_switch *sw, struct prestera_event *evt, void *arg);
|
||||
|
||||
struct prestera_rxtx_params;
|
||||
struct prestera_acl_hw_action_info;
|
||||
struct prestera_acl_iface;
|
||||
struct prestera_counter_stats;
|
||||
|
||||
/* Switch API */
|
||||
int prestera_hw_switch_init(struct prestera_switch *sw);
|
||||
@ -186,21 +200,37 @@ int prestera_hw_bridge_delete(struct prestera_switch *sw, u16 bridge_id);
|
||||
int prestera_hw_bridge_port_add(struct prestera_port *port, u16 bridge_id);
|
||||
int prestera_hw_bridge_port_delete(struct prestera_port *port, u16 bridge_id);
|
||||
|
||||
/* ACL API */
|
||||
int prestera_hw_acl_ruleset_create(struct prestera_switch *sw,
|
||||
u16 *ruleset_id);
|
||||
int prestera_hw_acl_ruleset_del(struct prestera_switch *sw,
|
||||
u16 ruleset_id);
|
||||
int prestera_hw_acl_rule_add(struct prestera_switch *sw,
|
||||
struct prestera_acl_rule *rule,
|
||||
u32 *rule_id);
|
||||
int prestera_hw_acl_rule_del(struct prestera_switch *sw, u32 rule_id);
|
||||
int prestera_hw_acl_rule_stats_get(struct prestera_switch *sw,
|
||||
u32 rule_id, u64 *packets, u64 *bytes);
|
||||
int prestera_hw_acl_port_bind(const struct prestera_port *port,
|
||||
u16 ruleset_id);
|
||||
int prestera_hw_acl_port_unbind(const struct prestera_port *port,
|
||||
u16 ruleset_id);
|
||||
/* vTCAM API */
|
||||
int prestera_hw_vtcam_create(struct prestera_switch *sw,
|
||||
u8 lookup, const u32 *keymask, u32 *vtcam_id,
|
||||
enum prestera_hw_vtcam_direction_t direction);
|
||||
int prestera_hw_vtcam_rule_add(struct prestera_switch *sw, u32 vtcam_id,
|
||||
u32 prio, void *key, void *keymask,
|
||||
struct prestera_acl_hw_action_info *act,
|
||||
u8 n_act, u32 *rule_id);
|
||||
int prestera_hw_vtcam_rule_del(struct prestera_switch *sw,
|
||||
u32 vtcam_id, u32 rule_id);
|
||||
int prestera_hw_vtcam_destroy(struct prestera_switch *sw, u32 vtcam_id);
|
||||
int prestera_hw_vtcam_iface_bind(struct prestera_switch *sw,
|
||||
struct prestera_acl_iface *iface,
|
||||
u32 vtcam_id, u16 pcl_id);
|
||||
int prestera_hw_vtcam_iface_unbind(struct prestera_switch *sw,
|
||||
struct prestera_acl_iface *iface,
|
||||
u32 vtcam_id);
|
||||
|
||||
/* Counter API */
|
||||
int prestera_hw_counter_trigger(struct prestera_switch *sw, u32 block_id);
|
||||
int prestera_hw_counter_abort(struct prestera_switch *sw);
|
||||
int prestera_hw_counters_get(struct prestera_switch *sw, u32 idx,
|
||||
u32 *len, bool *done,
|
||||
struct prestera_counter_stats *stats);
|
||||
int prestera_hw_counter_block_get(struct prestera_switch *sw,
|
||||
u32 client, u32 *block_id, u32 *offset,
|
||||
u32 *num_counters);
|
||||
int prestera_hw_counter_block_release(struct prestera_switch *sw,
|
||||
u32 block_id);
|
||||
int prestera_hw_counter_clear(struct prestera_switch *sw, u32 block_id,
|
||||
u32 counter_id);
|
||||
|
||||
/* SPAN API */
|
||||
int prestera_hw_span_get(const struct prestera_port *port, u8 *span_id);
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "prestera_rxtx.h"
|
||||
#include "prestera_devlink.h"
|
||||
#include "prestera_ethtool.h"
|
||||
#include "prestera_counter.h"
|
||||
#include "prestera_switchdev.h"
|
||||
|
||||
#define PRESTERA_MTU_DEFAULT 1536
|
||||
@ -904,6 +905,10 @@ static int prestera_switch_init(struct prestera_switch *sw)
|
||||
if (err)
|
||||
goto err_handlers_register;
|
||||
|
||||
err = prestera_counter_init(sw);
|
||||
if (err)
|
||||
goto err_counter_init;
|
||||
|
||||
err = prestera_acl_init(sw);
|
||||
if (err)
|
||||
goto err_acl_init;
|
||||
@ -936,6 +941,8 @@ err_dl_register:
|
||||
err_span_init:
|
||||
prestera_acl_fini(sw);
|
||||
err_acl_init:
|
||||
prestera_counter_fini(sw);
|
||||
err_counter_init:
|
||||
prestera_event_handlers_unregister(sw);
|
||||
err_handlers_register:
|
||||
prestera_rxtx_switch_fini(sw);
|
||||
@ -956,6 +963,7 @@ static void prestera_switch_fini(struct prestera_switch *sw)
|
||||
prestera_devlink_traps_unregister(sw);
|
||||
prestera_span_fini(sw);
|
||||
prestera_acl_fini(sw);
|
||||
prestera_counter_fini(sw);
|
||||
prestera_event_handlers_unregister(sw);
|
||||
prestera_rxtx_switch_fini(sw);
|
||||
prestera_switchdev_fini(sw);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "prestera.h"
|
||||
#include "prestera_hw.h"
|
||||
#include "prestera_acl.h"
|
||||
#include "prestera_flow.h"
|
||||
#include "prestera_span.h"
|
||||
|
||||
struct prestera_span_entry {
|
||||
|
Loading…
x
Reference in New Issue
Block a user