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.yan...@nxp.com>
---
 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

Reply via email to