[PATCH v2 net-next 03/10] net: mscc: ocelot: allocated rules to different hardware VCAP TCAMs by chain index

From: Xiaoliang Yang
Date: Tue Jun 02 2020 - 01:25:02 EST


There are three hardware TCAMs for ocelot chips: IS1, IS2 and ES0. Each
one supports different actions. The hardware flow order is: IS1->IS2->ES0.

This patch add three blocks to store rules according to chain index.
chain 0 is offloaded to IS1, chain 1 is offloaded to IS2, and egress chain
0 is offloaded to ES0.

Using action goto chain to express flow order as follows:
tc filter add dev swp0 chain 0 parent ffff: flower skip_sw \
action goto chain 1

Signed-off-by: Xiaoliang Yang <xiaoliang.yang_1@xxxxxxx>
---
drivers/net/ethernet/mscc/ocelot_ace.c | 51 +++++++++++++++--------
drivers/net/ethernet/mscc/ocelot_ace.h | 7 ++--
drivers/net/ethernet/mscc/ocelot_flower.c | 46 +++++++++++++++++---
include/soc/mscc/ocelot.h | 2 +-
include/soc/mscc/ocelot_vcap.h | 4 +-
5 files changed, 81 insertions(+), 29 deletions(-)

diff --git a/drivers/net/ethernet/mscc/ocelot_ace.c b/drivers/net/ethernet/mscc/ocelot_ace.c
index 748c618db7d8..b76593b40097 100644
--- a/drivers/net/ethernet/mscc/ocelot_ace.c
+++ b/drivers/net/ethernet/mscc/ocelot_ace.c
@@ -341,6 +341,8 @@ static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data,
vcap_action_set(vcap, data, VCAP_IS2_ACT_CPU_QU_NUM, 0);
vcap_action_set(vcap, data, VCAP_IS2_ACT_CPU_COPY_ENA, 0);
break;
+ default:
+ break;
}
}

@@ -644,9 +646,9 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
}

static void vcap_entry_get(struct ocelot *ocelot, struct ocelot_ace_rule *rule,
- int ix)
+ int ix, int block_id)
{
- const struct vcap_props *vcap = &ocelot->vcap[VCAP_IS2];
+ const struct vcap_props *vcap = &ocelot->vcap[block_id];
struct vcap_data data;
int row, count;
u32 cnt;
@@ -663,6 +665,19 @@ static void vcap_entry_get(struct ocelot *ocelot, struct ocelot_ace_rule *rule,
rule->stats.pkts = cnt;
}

+static void vcap_entry_set(struct ocelot *ocelot, int ix,
+ struct ocelot_ace_rule *ace,
+ int block_id)
+{
+ switch (block_id) {
+ case VCAP_IS2:
+ is2_entry_set(ocelot, ix, ace);
+ break;
+ default:
+ break;
+ }
+}
+
static void ocelot_ace_rule_add(struct ocelot *ocelot,
struct ocelot_acl_block *block,
struct ocelot_ace_rule *rule)
@@ -790,7 +805,7 @@ static bool ocelot_ace_is_problematic_non_mac_etype(struct ocelot_ace_rule *ace)
static bool ocelot_exclusive_mac_etype_ace_rules(struct ocelot *ocelot,
struct ocelot_ace_rule *ace)
{
- struct ocelot_acl_block *block = &ocelot->acl_block;
+ struct ocelot_acl_block *block = &ocelot->acl_block[VCAP_IS2];
struct ocelot_ace_rule *tmp;
unsigned long port;
int i;
@@ -824,15 +839,16 @@ static bool ocelot_exclusive_mac_etype_ace_rules(struct ocelot *ocelot,
return true;
}

-int ocelot_ace_rule_offload_add(struct ocelot *ocelot,
+int ocelot_ace_rule_offload_add(struct ocelot *ocelot, int block_id,
struct ocelot_ace_rule *rule,
struct netlink_ext_ack *extack)
{
- struct ocelot_acl_block *block = &ocelot->acl_block;
+ struct ocelot_acl_block *block = &ocelot->acl_block[block_id];
struct ocelot_ace_rule *ace;
int i, index;

- if (!ocelot_exclusive_mac_etype_ace_rules(ocelot, rule)) {
+ if (block_id == VCAP_IS2 &&
+ !ocelot_exclusive_mac_etype_ace_rules(ocelot, rule)) {
NL_SET_ERR_MSG_MOD(extack,
"Cannot mix MAC_ETYPE with non-MAC_ETYPE rules");
return -EBUSY;
@@ -847,11 +863,11 @@ int ocelot_ace_rule_offload_add(struct ocelot *ocelot,
/* Move down the rules to make place for the new rule */
for (i = block->count - 1; i > index; i--) {
ace = ocelot_ace_rule_get_rule_index(block, i);
- is2_entry_set(ocelot, i, ace);
+ vcap_entry_set(ocelot, i, ace, block_id);
}

/* Now insert the new rule */
- is2_entry_set(ocelot, index, rule);
+ vcap_entry_set(ocelot, index, rule, block_id);
return 0;
}

@@ -902,10 +918,10 @@ static void ocelot_ace_rule_del(struct ocelot *ocelot,
block->count--;
}

-int ocelot_ace_rule_offload_del(struct ocelot *ocelot,
+int ocelot_ace_rule_offload_del(struct ocelot *ocelot, int block_id,
struct ocelot_ace_rule *rule)
{
- struct ocelot_acl_block *block = &ocelot->acl_block;
+ struct ocelot_acl_block *block = &ocelot->acl_block[block_id];
struct ocelot_ace_rule del_ace;
struct ocelot_ace_rule *ace;
int i, index;
@@ -921,29 +937,29 @@ int ocelot_ace_rule_offload_del(struct ocelot *ocelot,
/* Move up all the blocks over the deleted rule */
for (i = index; i < block->count; i++) {
ace = ocelot_ace_rule_get_rule_index(block, i);
- is2_entry_set(ocelot, i, ace);
+ vcap_entry_set(ocelot, i, ace, block_id);
}

/* Now delete the last rule, because it is duplicated */
- is2_entry_set(ocelot, block->count, &del_ace);
+ vcap_entry_set(ocelot, block->count, &del_ace, block_id);

return 0;
}

-int ocelot_ace_rule_stats_update(struct ocelot *ocelot,
+int ocelot_ace_rule_stats_update(struct ocelot *ocelot, int block_id,
struct ocelot_ace_rule *rule)
{
- struct ocelot_acl_block *block = &ocelot->acl_block;
+ struct ocelot_acl_block *block = &ocelot->acl_block[block_id];
struct ocelot_ace_rule *tmp;
int index;

index = ocelot_ace_rule_get_index_id(block, rule);
- vcap_entry_get(ocelot, rule, index);
+ vcap_entry_get(ocelot, rule, index, block_id);

/* After we get the result we need to clear the counters */
tmp = ocelot_ace_rule_get_rule_index(block, index);
tmp->stats.pkts = 0;
- is2_entry_set(ocelot, index, tmp);
+ vcap_entry_set(ocelot, index, tmp, block_id);

return 0;
}
@@ -968,7 +984,7 @@ static void vcap_init(struct ocelot *ocelot, const struct vcap_props *vcap)

int ocelot_ace_init(struct ocelot *ocelot)
{
- struct ocelot_acl_block *block = &ocelot->acl_block;
+ struct ocelot_acl_block *block;

vcap_init(ocelot, &ocelot->vcap[VCAP_IS2]);

@@ -987,6 +1003,7 @@ int ocelot_ace_init(struct ocelot *ocelot)
ocelot_write_gix(ocelot, 0x3fffff, ANA_POL_CIR_STATE,
OCELOT_POLICER_DISCARD);

+ block = &ocelot->acl_block[VCAP_IS2];
block->pol_lpr = OCELOT_POLICER_DISCARD - 1;
INIT_LIST_HEAD(&block->rules);

diff --git a/drivers/net/ethernet/mscc/ocelot_ace.h b/drivers/net/ethernet/mscc/ocelot_ace.h
index 099e177f2617..a9fd99401a65 100644
--- a/drivers/net/ethernet/mscc/ocelot_ace.h
+++ b/drivers/net/ethernet/mscc/ocelot_ace.h
@@ -175,6 +175,7 @@ struct ocelot_ace_frame_ipv6 {
};

enum ocelot_ace_action {
+ OCELOT_ACL_ACTION_NULL,
OCELOT_ACL_ACTION_DROP,
OCELOT_ACL_ACTION_TRAP,
OCELOT_ACL_ACTION_POLICE,
@@ -214,12 +215,12 @@ struct ocelot_ace_rule {
u32 pol_ix;
};

-int ocelot_ace_rule_offload_add(struct ocelot *ocelot,
+int ocelot_ace_rule_offload_add(struct ocelot *ocelot, int block_id,
struct ocelot_ace_rule *rule,
struct netlink_ext_ack *extack);
-int ocelot_ace_rule_offload_del(struct ocelot *ocelot,
+int ocelot_ace_rule_offload_del(struct ocelot *ocelot, int block_id,
struct ocelot_ace_rule *rule);
-int ocelot_ace_rule_stats_update(struct ocelot *ocelot,
+int ocelot_ace_rule_stats_update(struct ocelot *ocelot, int block_id,
struct ocelot_ace_rule *rule);

int ocelot_ace_init(struct ocelot *ocelot);
diff --git a/drivers/net/ethernet/mscc/ocelot_flower.c b/drivers/net/ethernet/mscc/ocelot_flower.c
index 891925f73cbc..a1f7b6b28170 100644
--- a/drivers/net/ethernet/mscc/ocelot_flower.c
+++ b/drivers/net/ethernet/mscc/ocelot_flower.c
@@ -9,13 +9,26 @@

#include "ocelot_ace.h"

+static int ocelot_block_id_get(int chain, bool ingress)
+{
+ /* Select TCAM blocks by using chain index. Rules in chain 0 are
+ * implemented on IS1, chain 1 are implemented on IS2, and egress
+ * chain corresponds to ES0 block.
+ */
+ if (ingress)
+ return chain ? VCAP_IS2 : VCAP_IS1;
+ else
+ return VCAP_ES0;
+}
+
static int ocelot_flower_parse_action(struct flow_cls_offload *f,
struct ocelot_ace_rule *ace)
{
+ struct netlink_ext_ack *extack = f->common.extack;
const struct flow_action_entry *a;
+ int i, allowed_chain = 0;
s64 burst;
u64 rate;
- int i;

if (!flow_offload_has_one_action(&f->rule->action))
return -EOPNOTSUPP;
@@ -28,9 +41,11 @@ static int ocelot_flower_parse_action(struct flow_cls_offload *f,
switch (a->id) {
case FLOW_ACTION_DROP:
ace->action = OCELOT_ACL_ACTION_DROP;
+ allowed_chain = 1;
break;
case FLOW_ACTION_TRAP:
ace->action = OCELOT_ACL_ACTION_TRAP;
+ allowed_chain = 1;
break;
case FLOW_ACTION_POLICE:
ace->action = OCELOT_ACL_ACTION_POLICE;
@@ -38,10 +53,23 @@ static int ocelot_flower_parse_action(struct flow_cls_offload *f,
ace->pol.rate = div_u64(rate, 1000) * 8;
burst = rate * PSCHED_NS2TICKS(a->police.burst);
ace->pol.burst = div_u64(burst, PSCHED_TICKS_PER_SEC);
+ allowed_chain = 1;
+ break;
+ case FLOW_ACTION_GOTO:
+ if (a->chain_index != f->common.chain_index + 1) {
+ NL_SET_ERR_MSG_MOD(extack, "HW only support goto next chain\n");
+ return -EOPNOTSUPP;
+ }
+ ace->action = OCELOT_ACL_ACTION_NULL;
+ allowed_chain = f->common.chain_index;
break;
default:
return -EOPNOTSUPP;
}
+ if (f->common.chain_index != allowed_chain) {
+ NL_SET_ERR_MSG_MOD(extack, "Action is not supported on this chain\n");
+ return -EOPNOTSUPP;
+ }
}

return 0;
@@ -205,7 +233,7 @@ int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
struct flow_cls_offload *f, bool ingress)
{
struct ocelot_ace_rule *ace;
- int ret;
+ int ret, block_id;

ace = ocelot_ace_rule_create(ocelot, port, f);
if (!ace)
@@ -216,8 +244,10 @@ int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
kfree(ace);
return ret;
}
+ block_id = ocelot_block_id_get(f->common.chain_index, ingress);

- return ocelot_ace_rule_offload_add(ocelot, ace, f->common.extack);
+ return ocelot_ace_rule_offload_add(ocelot, block_id, ace,
+ f->common.extack);
}
EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace);

@@ -225,11 +255,13 @@ int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,
struct flow_cls_offload *f, bool ingress)
{
struct ocelot_ace_rule ace;
+ int block_id;

ace.prio = f->common.prio;
ace.id = f->cookie;
+ block_id = ocelot_block_id_get(f->common.chain_index, ingress);

- return ocelot_ace_rule_offload_del(ocelot, &ace);
+ return ocelot_ace_rule_offload_del(ocelot, block_id, &ace);
}
EXPORT_SYMBOL_GPL(ocelot_cls_flower_destroy);

@@ -237,11 +269,13 @@ int ocelot_cls_flower_stats(struct ocelot *ocelot, int port,
struct flow_cls_offload *f, bool ingress)
{
struct ocelot_ace_rule ace;
- int ret;
+ int ret, block_id;

ace.prio = f->common.prio;
ace.id = f->cookie;
- ret = ocelot_ace_rule_stats_update(ocelot, &ace);
+ block_id = ocelot_block_id_get(f->common.chain_index, ingress);
+
+ ret = ocelot_ace_rule_stats_update(ocelot, block_id, &ace);
if (ret)
return ret;

diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index 91357b1c8f31..4b2320bdc036 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -540,7 +540,7 @@ struct ocelot {

struct list_head multicast;

- struct ocelot_acl_block acl_block;
+ struct ocelot_acl_block acl_block[3];

const struct vcap_props *vcap;

diff --git a/include/soc/mscc/ocelot_vcap.h b/include/soc/mscc/ocelot_vcap.h
index 26d9384b3657..495847a40490 100644
--- a/include/soc/mscc/ocelot_vcap.h
+++ b/include/soc/mscc/ocelot_vcap.h
@@ -14,9 +14,9 @@
*/

enum {
- /* VCAP_IS1, */
+ VCAP_IS1,
VCAP_IS2,
- /* VCAP_ES0, */
+ VCAP_ES0,
};

struct vcap_props {
--
2.17.1