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:/Allan
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