Skip to content

Commit

Permalink
net: prestera: acl: add multi-chain support offload
Browse files Browse the repository at this point in the history
Add support of rule offloading added to the non-zero index chain,
which was previously forbidden. Also, goto action is offloaded
allowing to jump for processing of desired chain.

Note that only implicit chain 0 is bound to the device port(s) for
processing. The rest of chains have to be jumped by actions.

Signed-off-by: Volodymyr Mytnyk <vmytnyk@marvell.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Volodymyr Mytnyk authored and David S. Miller committed Feb 14, 2022
1 parent e81f1e0 commit fa5d824
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 39 deletions.
124 changes: 107 additions & 17 deletions drivers/net/ethernet/marvell/prestera/prestera_acl.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct prestera_acl {

struct prestera_acl_ruleset_ht_key {
struct prestera_flow_block *block;
u32 chain_index;
};

struct prestera_acl_rule_entry {
Expand All @@ -33,6 +34,10 @@ struct prestera_acl_rule_entry {
struct {
u8 valid:1;
} accept, drop, trap;
struct {
struct prestera_acl_action_jump i;
u8 valid:1;
} jump;
struct {
u32 id;
struct prestera_counter_block *block;
Expand All @@ -49,6 +54,7 @@ struct prestera_acl_ruleset {
refcount_t refcount;
void *keymask;
u32 vtcam_id;
u32 index;
u16 pcl_id;
bool offload;
};
Expand Down Expand Up @@ -83,20 +89,45 @@ static const struct rhashtable_params __prestera_acl_rule_entry_ht_params = {
.automatic_shrinking = true,
};

int prestera_acl_chain_to_client(u32 chain_index, u32 *client)
{
u32 client_map[] = {
PRESTERA_HW_COUNTER_CLIENT_LOOKUP_0,
PRESTERA_HW_COUNTER_CLIENT_LOOKUP_1,
PRESTERA_HW_COUNTER_CLIENT_LOOKUP_2
};

if (chain_index > ARRAY_SIZE(client_map))
return -EINVAL;

*client = client_map[chain_index];
return 0;
}

static bool prestera_acl_chain_is_supported(u32 chain_index)
{
return (chain_index & ~PRESTERA_ACL_CHAIN_MASK) == 0;
}

static struct prestera_acl_ruleset *
prestera_acl_ruleset_create(struct prestera_acl *acl,
struct prestera_flow_block *block)
struct prestera_flow_block *block,
u32 chain_index)
{
struct prestera_acl_ruleset *ruleset;
u32 uid = 0;
int err;

if (!prestera_acl_chain_is_supported(chain_index))
return ERR_PTR(-EINVAL);

ruleset = kzalloc(sizeof(*ruleset), GFP_KERNEL);
if (!ruleset)
return ERR_PTR(-ENOMEM);

ruleset->acl = acl;
ruleset->ht_key.block = block;
ruleset->ht_key.chain_index = chain_index;
refcount_set(&ruleset->refcount, 1);

err = rhashtable_init(&ruleset->rule_ht, &prestera_acl_rule_ht_params);
Expand All @@ -108,7 +139,9 @@ prestera_acl_ruleset_create(struct prestera_acl *acl,
goto err_ruleset_create;

/* make pcl-id based on uid */
ruleset->pcl_id = (u8)uid;
ruleset->pcl_id = PRESTERA_ACL_PCL_ID_MAKE((u8)uid, chain_index);
ruleset->index = uid;

err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node,
prestera_acl_ruleset_ht_params);
if (err)
Expand All @@ -133,59 +166,91 @@ void prestera_acl_ruleset_keymask_set(struct prestera_acl_ruleset *ruleset,

int prestera_acl_ruleset_offload(struct prestera_acl_ruleset *ruleset)
{
struct prestera_acl_iface iface;
u32 vtcam_id;
int err;

if (ruleset->offload)
return -EEXIST;

err = prestera_acl_vtcam_id_get(ruleset->acl, 0,
err = prestera_acl_vtcam_id_get(ruleset->acl,
ruleset->ht_key.chain_index,
ruleset->keymask, &vtcam_id);
if (err)
return err;
goto err_vtcam_create;

if (ruleset->ht_key.chain_index) {
/* for chain > 0, bind iface index to pcl-id to be able
* to jump from any other ruleset to this one using the index.
*/
iface.index = ruleset->index;
iface.type = PRESTERA_ACL_IFACE_TYPE_INDEX;
err = prestera_hw_vtcam_iface_bind(ruleset->acl->sw, &iface,
vtcam_id, ruleset->pcl_id);
if (err)
goto err_ruleset_bind;
}

ruleset->vtcam_id = vtcam_id;
ruleset->offload = true;
return 0;

err_ruleset_bind:
prestera_acl_vtcam_id_put(ruleset->acl, ruleset->vtcam_id);
err_vtcam_create:
return err;
}

static void prestera_acl_ruleset_destroy(struct prestera_acl_ruleset *ruleset)
{
struct prestera_acl *acl = ruleset->acl;
u8 uid = ruleset->pcl_id & PRESTERA_ACL_KEYMASK_PCL_ID_USER;
int err;

rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node,
prestera_acl_ruleset_ht_params);

if (ruleset->offload)
if (ruleset->offload) {
if (ruleset->ht_key.chain_index) {
struct prestera_acl_iface iface = {
.type = PRESTERA_ACL_IFACE_TYPE_INDEX,
.index = ruleset->index
};
err = prestera_hw_vtcam_iface_unbind(acl->sw, &iface,
ruleset->vtcam_id);
WARN_ON(err);
}
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);
}

static struct prestera_acl_ruleset *
__prestera_acl_ruleset_lookup(struct prestera_acl *acl,
struct prestera_flow_block *block)
struct prestera_flow_block *block,
u32 chain_index)
{
struct prestera_acl_ruleset_ht_key ht_key;

memset(&ht_key, 0, sizeof(ht_key));
ht_key.block = block;
ht_key.chain_index = chain_index;
return rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key,
prestera_acl_ruleset_ht_params);
}

struct prestera_acl_ruleset *
prestera_acl_ruleset_lookup(struct prestera_acl *acl,
struct prestera_flow_block *block)
struct prestera_flow_block *block,
u32 chain_index)
{
struct prestera_acl_ruleset *ruleset;

ruleset = __prestera_acl_ruleset_lookup(acl, block);
ruleset = __prestera_acl_ruleset_lookup(acl, block, chain_index);
if (!ruleset)
return ERR_PTR(-ENOENT);

Expand All @@ -195,17 +260,18 @@ prestera_acl_ruleset_lookup(struct prestera_acl *acl,

struct prestera_acl_ruleset *
prestera_acl_ruleset_get(struct prestera_acl *acl,
struct prestera_flow_block *block)
struct prestera_flow_block *block,
u32 chain_index)
{
struct prestera_acl_ruleset *ruleset;

ruleset = __prestera_acl_ruleset_lookup(acl, block);
ruleset = __prestera_acl_ruleset_lookup(acl, block, chain_index);
if (ruleset) {
refcount_inc(&ruleset->refcount);
return ruleset;
}

return prestera_acl_ruleset_create(acl, block);
return prestera_acl_ruleset_create(acl, block, chain_index);
}

void prestera_acl_ruleset_put(struct prestera_acl_ruleset *ruleset)
Expand Down Expand Up @@ -293,14 +359,19 @@ prestera_acl_rule_lookup(struct prestera_acl_ruleset *ruleset,
prestera_acl_rule_ht_params);
}

u32 prestera_acl_ruleset_index_get(const struct prestera_acl_ruleset *ruleset)
{
return ruleset->index;
}

bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset)
{
return ruleset->offload;
}

struct prestera_acl_rule *
prestera_acl_rule_create(struct prestera_acl_ruleset *ruleset,
unsigned long cookie)
unsigned long cookie, u32 chain_index)
{
struct prestera_acl_rule *rule;

Expand All @@ -310,6 +381,7 @@ prestera_acl_rule_create(struct prestera_acl_ruleset *ruleset,

rule->ruleset = ruleset;
rule->cookie = cookie;
rule->chain_index = chain_index;

refcount_inc(&ruleset->refcount);

Expand All @@ -324,6 +396,10 @@ void prestera_acl_rule_priority_set(struct prestera_acl_rule *rule,

void prestera_acl_rule_destroy(struct prestera_acl_rule *rule)
{
if (rule->jump_ruleset)
/* release ruleset kept by jump action */
prestera_acl_ruleset_put(rule->jump_ruleset);

prestera_acl_ruleset_put(rule->ruleset);
kfree(rule);
}
Expand All @@ -347,7 +423,10 @@ int prestera_acl_rule_add(struct prestera_switch *sw,

/* setup counter */
rule->re_arg.count.valid = true;
rule->re_arg.count.client = PRESTERA_HW_COUNTER_CLIENT_LOOKUP_0;
err = prestera_acl_chain_to_client(ruleset->ht_key.chain_index,
&rule->re_arg.count.client);
if (err)
goto err_rule_add;

rule->re = prestera_acl_rule_entry_find(sw->acl, &rule->re_key);
err = WARN_ON(rule->re) ? -EEXIST : 0;
Expand All @@ -360,8 +439,10 @@ int prestera_acl_rule_add(struct prestera_switch *sw,
if (err)
goto err_rule_add;

/* bind the block (all ports) to chain index 0 */
if (!ruleset->rule_count) {
/* bind the block (all ports) to chain index 0, rest of
* the chains are bound to goto action
*/
if (!ruleset->ht_key.chain_index && !ruleset->rule_count) {
err = prestera_acl_ruleset_block_bind(ruleset, block);
if (err)
goto err_acl_block_bind;
Expand Down Expand Up @@ -395,7 +476,7 @@ void prestera_acl_rule_del(struct prestera_switch *sw,
prestera_acl_rule_entry_destroy(sw->acl, rule->re);

/* unbind block (all ports) */
if (!ruleset->rule_count)
if (!ruleset->ht_key.chain_index && !ruleset->rule_count)
prestera_acl_ruleset_block_unbind(ruleset, block);
}

Expand Down Expand Up @@ -459,6 +540,12 @@ static int __prestera_acl_rule_entry2hw_add(struct prestera_switch *sw,
act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_TRAP;
act_num++;
}
/* jump */
if (e->jump.valid) {
act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_JUMP;
act_hw[act_num].jump = e->jump.i;
act_num++;
}
/* counter */
if (e->counter.block) {
act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_COUNT;
Expand Down Expand Up @@ -505,6 +592,9 @@ __prestera_acl_rule_entry_act_construct(struct prestera_switch *sw,
e->drop.valid = arg->drop.valid;
/* trap */
e->trap.valid = arg->trap.valid;
/* jump */
e->jump.valid = arg->jump.valid;
e->jump.i = arg->jump.i;
/* counter */
if (arg->count.valid) {
int err;
Expand Down
Loading

0 comments on commit fa5d824

Please sign in to comment.