]> git.baikalelectronics.ru Git - kernel.git/commitdiff
net: mscc: ocelot: offload ingress skbedit and vlan actions to VCAP IS1
authorXiaoliang Yang <xiaoliang.yang_1@nxp.com>
Fri, 2 Oct 2020 12:02:23 +0000 (15:02 +0300)
committerDavid S. Miller <davem@davemloft.net>
Fri, 2 Oct 2020 22:40:30 +0000 (15:40 -0700)
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.

Note that the VLAN_MANGLE has been omitted for now. In hardware, the
VCAP_IS1_ACT_VID_REPLACE_ENA field replaces the classified VLAN
(metadata associated with the frame) and not the VLAN from the header
itself. There are currently some issues which need to be addressed when
operating in standalone, or in bridge with vlan_filtering=0 modes,
because in those cases the switch ports have VLAN awareness disabled,
and changing the classified VLAN to anything other than the pvid causes
the packets to be dropped. Another issue is that on egress, we expect
port tagging to push the classified VLAN, but port tagging is disabled
in the modes mentioned above, so although the classified VLAN is
replaced, it is not visible in the packet transmitted by the switch.

Signed-off-by: Xiaoliang Yang <xiaoliang.yang_1@nxp.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/dsa/ocelot/felix_vsc9959.c
drivers/net/ethernet/mscc/ocelot.c
drivers/net/ethernet/mscc/ocelot_flower.c
drivers/net/ethernet/mscc/ocelot_vcap.c
drivers/net/ethernet/mscc/ocelot_vcap.h

index bfbf811b060fd3ddd8630dc81f0e8b02e576b353..875ea60c0df0867d648c31d42a8a7d0c1017ecfb 100644 (file)
@@ -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},
index 974821b9cdc4d51f3705e4847b36651a4d65fd43..ba47359c26c750a22f75b39d688f89d051059211 100644 (file)
@@ -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)
index c0cb89c1967d6d28635642682a088988294c1281..b8a588e65929ac644f948355cb6e4c5ea5b6dc07 100644 (file)
@@ -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,52 @@ 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_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 +297,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 +321,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 +381,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);
index f6b232ab19b1af1c0575720d8947a367fca63d5a..be3293e7c8928fedeb6ac90468cdb3694019c4e7 100644 (file)
@@ -640,6 +640,140 @@ 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;
+       }
+       case OCELOT_VCAP_KEY_IPV4: {
+               struct ocelot_vcap_key_ipv4 *ipv4 = &filter->key.ipv4;
+               struct ocelot_vcap_udp_tcp *sport = &ipv4->sport;
+               struct ocelot_vcap_udp_tcp *dport = &ipv4->dport;
+               enum ocelot_vcap_bit tcp_udp = OCELOT_VCAP_BIT_0;
+               struct ocelot_vcap_u8 proto = ipv4->proto;
+               struct ocelot_vcap_ipv4 sip = ipv4->sip;
+               u32 val, msk;
+
+               vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_IP_SNAP,
+                                OCELOT_VCAP_BIT_1);
+               vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_IP4,
+                                OCELOT_VCAP_BIT_1);
+               vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_ETYPE_LEN,
+                                OCELOT_VCAP_BIT_1);
+               vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_L3_IP4_SIP,
+                                  sip.value.addr, sip.mask.addr);
+
+               val = proto.value[0];
+               msk = proto.mask[0];
+
+               if ((val == NEXTHDR_TCP || val == NEXTHDR_UDP) && msk == 0xff)
+                       tcp_udp = OCELOT_VCAP_BIT_1;
+               vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_TCP_UDP, tcp_udp);
+
+               if (tcp_udp) {
+                       enum ocelot_vcap_bit tcp = OCELOT_VCAP_BIT_0;
+
+                       if (val == NEXTHDR_TCP)
+                               tcp = OCELOT_VCAP_BIT_1;
+
+                       vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_TCP, tcp);
+                       vcap_key_l4_port_set(vcap, &data, VCAP_IS1_HK_L4_SPORT,
+                                            sport);
+                       /* Overloaded field */
+                       vcap_key_l4_port_set(vcap, &data, VCAP_IS1_HK_ETYPE,
+                                            dport);
+               } else {
+                       /* IPv4 "other" frame */
+                       struct ocelot_vcap_u16 etype = {0};
+
+                       /* Overloaded field */
+                       etype.value[0] = proto.value[0];
+                       etype.mask[0] = proto.mask[0];
+
+                       vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_ETYPE,
+                                          etype.value, etype.mask);
+               }
+       }
+       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)
 {
@@ -663,6 +797,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);
 }
index a8e03dbf1083896183cf73f7ae91b08f019ea0e2..a71bb44476481e84628b708f1065b55031810e2b 100644 (file)
@@ -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;