From: Xiaoliang Yang <xiaoliang.yan...@nxp.com>

VCAP IS1 is a VCAP module which can filter on the most common L2/L3/L4
Ethernet keys, and modify the results of the basic QoS classification
and VLAN classification based on those flow keys.

There are 3 VCAP IS1 lookups, mapped over chains 10000, 11000 and 12000.
Currently the driver is hardcoded to use IS1_ACTION_TYPE_NORMAL half
keys.

Signed-off-by: Xiaoliang Yang <xiaoliang.yan...@nxp.com>
Signed-off-by: Vladimir Oltean <vladimir.olt...@nxp.com>
---
Changes in v2:
Stopped modifying VCAP_S1_CFG for lookup 0, which made no sense.
Checks for the key types that can be offloaded to IS1.

 drivers/net/dsa/ocelot/felix_vsc9959.c    |  1 +
 drivers/net/ethernet/mscc/ocelot.c        |  3 +
 drivers/net/ethernet/mscc/ocelot_flower.c | 86 ++++++++++++++++++++++
 drivers/net/ethernet/mscc/ocelot_vcap.c   | 88 +++++++++++++++++++++++
 drivers/net/ethernet/mscc/ocelot_vcap.h   | 17 +++++
 5 files changed, 195 insertions(+)

diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c 
b/drivers/net/dsa/ocelot/felix_vsc9959.c
index 01d0e698b77a..c02780710971 100644
--- a/drivers/net/dsa/ocelot/felix_vsc9959.c
+++ b/drivers/net/dsa/ocelot/felix_vsc9959.c
@@ -711,6 +711,7 @@ static const struct vcap_field vsc9959_vcap_is1_actions[] = 
{
        [VCAP_IS1_ACT_PAG_OVERRIDE_MASK]        = { 13,  8},
        [VCAP_IS1_ACT_PAG_VAL]                  = { 21,  8},
        [VCAP_IS1_ACT_RSV]                      = { 29,  9},
+       /* The fields below are incorrectly shifted by 2 in the manual */
        [VCAP_IS1_ACT_VID_REPLACE_ENA]          = { 38,  1},
        [VCAP_IS1_ACT_VID_ADD_VAL]              = { 39, 12},
        [VCAP_IS1_ACT_FID_SEL]                  = { 51,  2},
diff --git a/drivers/net/ethernet/mscc/ocelot.c 
b/drivers/net/ethernet/mscc/ocelot.c
index 2eba6b5385d1..b9cc4aaaafd7 100644
--- a/drivers/net/ethernet/mscc/ocelot.c
+++ b/drivers/net/ethernet/mscc/ocelot.c
@@ -108,6 +108,9 @@ static void ocelot_vcap_enable(struct ocelot *ocelot, int 
port)
        ocelot_write_gix(ocelot, ANA_PORT_VCAP_S2_CFG_S2_ENA |
                         ANA_PORT_VCAP_S2_CFG_S2_IP6_CFG(0xa),
                         ANA_PORT_VCAP_S2_CFG, port);
+
+       ocelot_write_gix(ocelot, ANA_PORT_VCAP_CFG_S1_ENA,
+                        ANA_PORT_VCAP_CFG, port);
 }
 
 static inline u32 ocelot_vlant_read_vlanaccess(struct ocelot *ocelot)
diff --git a/drivers/net/ethernet/mscc/ocelot_flower.c 
b/drivers/net/ethernet/mscc/ocelot_flower.c
index c0cb89c1967d..45b44444f0a7 100644
--- a/drivers/net/ethernet/mscc/ocelot_flower.c
+++ b/drivers/net/ethernet/mscc/ocelot_flower.c
@@ -57,6 +57,17 @@ static int ocelot_chain_to_lookup(int chain)
        return (chain / VCAP_LOOKUP) % 10;
 }
 
+/* Caller must ensure this is a valid IS2 chain first,
+ * by calling ocelot_chain_to_block.
+ */
+static int ocelot_chain_to_pag(int chain)
+{
+       int lookup = ocelot_chain_to_lookup(chain);
+
+       /* calculate PAG value as chain index relative to the first PAG */
+       return chain - VCAP_IS2_CHAIN(lookup, 0);
+}
+
 static bool ocelot_is_goto_target_valid(int goto_target, int chain,
                                        bool ingress)
 {
@@ -209,8 +220,69 @@ static int ocelot_flower_parse_action(struct 
flow_cls_offload *f, bool ingress,
                        filter->action.pol.burst = a->police.burst;
                        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
                        break;
+               case FLOW_ACTION_VLAN_POP:
+                       if (filter->block_id != VCAP_IS1) {
+                               NL_SET_ERR_MSG_MOD(extack,
+                                                  "VLAN pop action can only be 
offloaded to VCAP IS1");
+                               return -EOPNOTSUPP;
+                       }
+                       if (filter->goto_target != -1) {
+                               NL_SET_ERR_MSG_MOD(extack,
+                                                  "Last action must be GOTO");
+                               return -EOPNOTSUPP;
+                       }
+                       filter->action.vlan_pop_cnt_ena = true;
+                       filter->action.vlan_pop_cnt++;
+                       if (filter->action.vlan_pop_cnt > 2) {
+                               NL_SET_ERR_MSG_MOD(extack,
+                                                  "Cannot pop more than 2 VLAN 
headers");
+                               return -EOPNOTSUPP;
+                       }
+                       filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
+                       break;
+               case FLOW_ACTION_VLAN_MANGLE:
+                       if (filter->block_id != VCAP_IS1) {
+                               NL_SET_ERR_MSG_MOD(extack,
+                                                  "VLAN modify action can only 
be offloaded to VCAP IS1");
+                               return -EOPNOTSUPP;
+                       }
+                       if (filter->goto_target != -1) {
+                               NL_SET_ERR_MSG_MOD(extack,
+                                                  "Last action must be GOTO");
+                               return -EOPNOTSUPP;
+                       }
+                       filter->action.vid_replace_ena = true;
+                       filter->action.pcp_dei_ena = true;
+                       filter->action.vid = a->vlan.vid;
+                       filter->action.pcp = a->vlan.prio;
+                       filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
+                       break;
+               case FLOW_ACTION_PRIORITY:
+                       if (filter->block_id != VCAP_IS1) {
+                               NL_SET_ERR_MSG_MOD(extack,
+                                                  "Priority action can only be 
offloaded to VCAP IS1");
+                               return -EOPNOTSUPP;
+                       }
+                       if (filter->goto_target != -1) {
+                               NL_SET_ERR_MSG_MOD(extack,
+                                                  "Last action must be GOTO");
+                               return -EOPNOTSUPP;
+                       }
+                       filter->action.qos_ena = true;
+                       filter->action.qos_val = a->priority;
+                       filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
+                       break;
                case FLOW_ACTION_GOTO:
                        filter->goto_target = a->chain_index;
+
+                       if (filter->block_id == VCAP_IS1 &&
+                           ocelot_chain_to_lookup(chain) == 2) {
+                               int pag = 
ocelot_chain_to_pag(filter->goto_target);
+
+                               filter->action.pag_override_mask = 0xff;
+                               filter->action.pag_val = pag;
+                               filter->type = OCELOT_VCAP_FILTER_PAG;
+                       }
                        break;
                default:
                        NL_SET_ERR_MSG_MOD(extack, "Cannot offload action");
@@ -242,6 +314,7 @@ static int ocelot_flower_parse_key(struct flow_cls_offload 
*f, bool ingress,
 {
        struct flow_rule *rule = flow_cls_offload_flow_rule(f);
        struct flow_dissector *dissector = rule->match.dissector;
+       struct netlink_ext_ack *extack = f->common.extack;
        u16 proto = ntohs(f->common.protocol);
        bool match_protocol = true;
 
@@ -265,6 +338,13 @@ static int ocelot_flower_parse_key(struct flow_cls_offload 
*f, bool ingress,
        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
                struct flow_match_eth_addrs match;
 
+               if (filter->block_id == VCAP_IS1 &&
+                   !is_zero_ether_addr(match.mask->dst)) {
+                       NL_SET_ERR_MSG_MOD(extack,
+                                          "Key type S1_NORMAL cannot match on 
destination MAC");
+                       return -EOPNOTSUPP;
+               }
+
                /* The hw support mac matches only for MAC_ETYPE key,
                 * therefore if other matches(port, tcp flags, etc) are added
                 * then just bail out
@@ -318,6 +398,12 @@ static int ocelot_flower_parse_key(struct flow_cls_offload 
*f, bool ingress,
                struct flow_match_ipv4_addrs match;
                u8 *tmp;
 
+               if (filter->block_id == VCAP_IS1 && *(u32 *)&match.mask->dst) {
+                       NL_SET_ERR_MSG_MOD(extack,
+                                          "Key type S1_NORMAL cannot match on 
destination IP");
+                       return -EOPNOTSUPP;
+               }
+
                flow_rule_match_ipv4_addrs(rule, &match);
                tmp = &filter->key.ipv4.sip.value.addr[0];
                memcpy(tmp, &match.key->src, 4);
diff --git a/drivers/net/ethernet/mscc/ocelot_vcap.c 
b/drivers/net/ethernet/mscc/ocelot_vcap.c
index 79ac3a5ba986..be9a179364ef 100644
--- a/drivers/net/ethernet/mscc/ocelot_vcap.c
+++ b/drivers/net/ethernet/mscc/ocelot_vcap.c
@@ -623,6 +623,91 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
        vcap_row_cmd(ocelot, vcap, row, VCAP_CMD_WRITE, VCAP_SEL_ALL);
 }
 
+static void is1_action_set(struct ocelot *ocelot, struct vcap_data *data,
+                          const struct ocelot_vcap_filter *filter)
+{
+       const struct vcap_props *vcap = &ocelot->vcap[VCAP_IS1];
+       const struct ocelot_vcap_action *a = &filter->action;
+
+       vcap_action_set(vcap, data, VCAP_IS1_ACT_VID_REPLACE_ENA,
+                       a->vid_replace_ena);
+       vcap_action_set(vcap, data, VCAP_IS1_ACT_VID_ADD_VAL, a->vid);
+       vcap_action_set(vcap, data, VCAP_IS1_ACT_VLAN_POP_CNT_ENA,
+                       a->vlan_pop_cnt_ena);
+       vcap_action_set(vcap, data, VCAP_IS1_ACT_VLAN_POP_CNT,
+                       a->vlan_pop_cnt);
+       vcap_action_set(vcap, data, VCAP_IS1_ACT_PCP_DEI_ENA, a->pcp_dei_ena);
+       vcap_action_set(vcap, data, VCAP_IS1_ACT_PCP_VAL, a->pcp);
+       vcap_action_set(vcap, data, VCAP_IS1_ACT_DEI_VAL, a->dei);
+       vcap_action_set(vcap, data, VCAP_IS1_ACT_QOS_ENA, a->qos_ena);
+       vcap_action_set(vcap, data, VCAP_IS1_ACT_QOS_VAL, a->qos_val);
+       vcap_action_set(vcap, data, VCAP_IS1_ACT_PAG_OVERRIDE_MASK,
+                       a->pag_override_mask);
+       vcap_action_set(vcap, data, VCAP_IS1_ACT_PAG_VAL, a->pag_val);
+}
+
+static void is1_entry_set(struct ocelot *ocelot, int ix,
+                         struct ocelot_vcap_filter *filter)
+{
+       const struct vcap_props *vcap = &ocelot->vcap[VCAP_IS1];
+       struct ocelot_vcap_key_vlan *tag = &filter->vlan;
+       struct ocelot_vcap_u64 payload;
+       struct vcap_data data;
+       int row = ix / 2;
+       u32 type;
+
+       memset(&payload, 0, sizeof(payload));
+       memset(&data, 0, sizeof(data));
+
+       /* Read row */
+       vcap_row_cmd(ocelot, vcap, row, VCAP_CMD_READ, VCAP_SEL_ALL);
+       vcap_cache2entry(ocelot, vcap, &data);
+       vcap_cache2action(ocelot, vcap, &data);
+
+       data.tg_sw = VCAP_TG_HALF;
+       data.type = IS1_ACTION_TYPE_NORMAL;
+       vcap_data_offset_get(vcap, &data, ix);
+       data.tg = (data.tg & ~data.tg_mask);
+       if (filter->prio != 0)
+               data.tg |= data.tg_value;
+
+       vcap_key_set(vcap, &data, VCAP_IS1_HK_IGR_PORT_MASK, 0,
+                    ~filter->ingress_port_mask);
+       vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_L2_MC, filter->dmac_mc);
+       vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_L2_BC, filter->dmac_bc);
+       vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_VLAN_TAGGED, tag->tagged);
+       vcap_key_set(vcap, &data, VCAP_IS1_HK_VID,
+                    tag->vid.value, tag->vid.mask);
+       vcap_key_set(vcap, &data, VCAP_IS1_HK_PCP,
+                    tag->pcp.value[0], tag->pcp.mask[0]);
+       type = IS1_TYPE_S1_NORMAL;
+
+       switch (filter->key_type) {
+       case OCELOT_VCAP_KEY_ETYPE: {
+               struct ocelot_vcap_key_etype *etype = &filter->key.etype;
+
+               vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_L2_SMAC,
+                                  etype->smac.value, etype->smac.mask);
+               vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_ETYPE,
+                                  etype->etype.value, etype->etype.mask);
+               break;
+       }
+       default:
+               break;
+       }
+       vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_TYPE,
+                        type ? OCELOT_VCAP_BIT_1 : OCELOT_VCAP_BIT_0);
+
+       is1_action_set(ocelot, &data, filter);
+       vcap_data_set(data.counter, data.counter_offset,
+                     vcap->counter_width, filter->stats.pkts);
+
+       /* Write row */
+       vcap_entry2cache(ocelot, vcap, &data);
+       vcap_action2cache(ocelot, vcap, &data);
+       vcap_row_cmd(ocelot, vcap, row, VCAP_CMD_WRITE, VCAP_SEL_ALL);
+}
+
 static void vcap_entry_get(struct ocelot *ocelot, int ix,
                           struct ocelot_vcap_filter *filter)
 {
@@ -646,6 +731,8 @@ static void vcap_entry_get(struct ocelot *ocelot, int ix,
 static void vcap_entry_set(struct ocelot *ocelot, int ix,
                           struct ocelot_vcap_filter *filter)
 {
+       if (filter->block_id == VCAP_IS1)
+               return is1_entry_set(ocelot, ix, filter);
        if (filter->block_id == VCAP_IS2)
                return is2_entry_set(ocelot, ix, filter);
 }
@@ -1005,6 +1092,7 @@ int ocelot_vcap_init(struct ocelot *ocelot)
 {
        int i;
 
+       ocelot_vcap_init_one(ocelot, &ocelot->vcap[VCAP_IS1]);
        ocelot_vcap_init_one(ocelot, &ocelot->vcap[VCAP_IS2]);
 
        /* Create a policer that will drop the frames for the cpu.
diff --git a/drivers/net/ethernet/mscc/ocelot_vcap.h 
b/drivers/net/ethernet/mscc/ocelot_vcap.h
index bd876b49f0fa..665b4c3aa200 100644
--- a/drivers/net/ethernet/mscc/ocelot_vcap.h
+++ b/drivers/net/ethernet/mscc/ocelot_vcap.h
@@ -160,6 +160,7 @@ struct ocelot_vcap_key_ipv4 {
 struct ocelot_vcap_key_ipv6 {
        struct ocelot_vcap_u8 proto; /* IPv6 protocol */
        struct ocelot_vcap_u128 sip; /* IPv6 source (byte 0-7 ignored) */
+       struct ocelot_vcap_u128 dip; /* IPv6 destination (byte 0-7 ignored) */
        enum ocelot_vcap_bit ttl;  /* TTL zero */
        struct ocelot_vcap_u8 ds;
        struct ocelot_vcap_u48 data; /* Not UDP/TCP: IP data */
@@ -185,6 +186,21 @@ enum ocelot_mask_mode {
 
 struct ocelot_vcap_action {
        union {
+               /* VCAP IS1 */
+               struct {
+                       bool vid_replace_ena;
+                       u16 vid;
+                       bool vlan_pop_cnt_ena;
+                       int vlan_pop_cnt;
+                       bool pcp_dei_ena;
+                       u8 pcp;
+                       u8 dei;
+                       bool qos_ena;
+                       u8 qos_val;
+                       u8 pag_override_mask;
+                       u8 pag_val;
+               };
+
                /* VCAP IS2 */
                struct {
                        bool cpu_copy_ena;
@@ -217,6 +233,7 @@ struct ocelot_vcap_filter {
        int block_id;
        int goto_target;
        int lookup;
+       u8 pag;
        u16 prio;
        u32 id;
 
-- 
2.25.1

Reply via email to