net: microchip: sparx5: Add TC vlan action support for the ES0 VCAP
authorSteen Hegelund <steen.hegelund@microchip.com>
Tue, 14 Feb 2023 10:40:49 +0000 (11:40 +0100)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 16 Feb 2023 07:59:49 +0000 (08:59 +0100)
This provides these 3 actions for rule in the ES0 VCAP:

- action vlan pop
- action vlan modify id X priority Y
- action vlan push id X priority Y protocol Z

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
drivers/net/ethernet/microchip/sparx5/sparx5_tc.h
drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c
drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c
drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h
drivers/net/ethernet/microchip/vcap/vcap_tc.c
drivers/net/ethernet/microchip/vcap/vcap_tc.h

index 01273db..7ef470b 100644 (file)
@@ -29,6 +29,72 @@ enum SPX5_FORWARDING_SEL {
        SPX5_FWSEL_DISCARD,
 };
 
+/* Controls tag A (outer tagging) */
+enum SPX5_OUTER_TAG_SEL {
+       SPX5_OTAG_PORT,
+       SPX5_OTAG_TAG_A,
+       SPX5_OTAG_FORCED_PORT,
+       SPX5_OTAG_UNTAG,
+};
+
+/* Selects TPID for ES0 tag A */
+enum SPX5_TPID_A_SEL {
+       SPX5_TPID_A_8100,
+       SPX5_TPID_A_88A8,
+       SPX5_TPID_A_CUST1,
+       SPX5_TPID_A_CUST2,
+       SPX5_TPID_A_CUST3,
+       SPX5_TPID_A_CLASSIFIED,
+};
+
+/* Selects VID for ES0 tag A */
+enum SPX5_VID_A_SEL {
+       SPX5_VID_A_CLASSIFIED,
+       SPX5_VID_A_VAL,
+       SPX5_VID_A_IFH,
+       SPX5_VID_A_RESERVED,
+};
+
+/* Select PCP source for ES0 tag A */
+enum SPX5_PCP_A_SEL {
+       SPX5_PCP_A_CLASSIFIED,
+       SPX5_PCP_A_VAL,
+       SPX5_PCP_A_RESERVED,
+       SPX5_PCP_A_POPPED,
+       SPX5_PCP_A_MAPPED_0,
+       SPX5_PCP_A_MAPPED_1,
+       SPX5_PCP_A_MAPPED_2,
+       SPX5_PCP_A_MAPPED_3,
+};
+
+/* Select DEI source for ES0 tag A */
+enum SPX5_DEI_A_SEL {
+       SPX5_DEI_A_CLASSIFIED,
+       SPX5_DEI_A_VAL,
+       SPX5_DEI_A_REW,
+       SPX5_DEI_A_POPPED,
+       SPX5_DEI_A_MAPPED_0,
+       SPX5_DEI_A_MAPPED_1,
+       SPX5_DEI_A_MAPPED_2,
+       SPX5_DEI_A_MAPPED_3,
+};
+
+/* Controls tag B (inner tagging) */
+enum SPX5_INNER_TAG_SEL {
+       SPX5_ITAG_NO_PUSH,
+       SPX5_ITAG_PUSH_B_TAG,
+};
+
+/* Selects TPID for ES0 tag B. */
+enum SPX5_TPID_B_SEL {
+       SPX5_TPID_B_8100,
+       SPX5_TPID_B_88A8,
+       SPX5_TPID_B_CUST1,
+       SPX5_TPID_B_CUST2,
+       SPX5_TPID_B_CUST3,
+       SPX5_TPID_B_CLASSIFIED,
+};
+
 int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type,
                         void *type_data);
 
index 67b49ad..b36819a 100644 (file)
@@ -29,6 +29,31 @@ struct sparx5_multiple_rules {
 };
 
 static int
+sparx5_tc_flower_es0_tpid(struct vcap_tc_flower_parse_usage *st)
+{
+       int err = 0;
+
+       switch (st->tpid) {
+       case ETH_P_8021Q:
+               err = vcap_rule_add_key_u32(st->vrule,
+                                           VCAP_KF_8021Q_TPID,
+                                           SPX5_TPID_SEL_8100, ~0);
+               break;
+       case ETH_P_8021AD:
+               err = vcap_rule_add_key_u32(st->vrule,
+                                           VCAP_KF_8021Q_TPID,
+                                           SPX5_TPID_SEL_88A8, ~0);
+               break;
+       default:
+               NL_SET_ERR_MSG_MOD(st->fco->common.extack,
+                                  "Invalid vlan proto");
+               err = -EINVAL;
+               break;
+       }
+       return err;
+}
+
+static int
 sparx5_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st)
 {
        struct flow_match_basic mt;
@@ -168,13 +193,21 @@ sparx5_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st)
 {
        enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS;
        enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS;
+       int err;
 
        if (st->admin->vtype == VCAP_TYPE_IS0) {
                vid_key = VCAP_KF_8021Q_VID0;
                pcp_key = VCAP_KF_8021Q_PCP0;
        }
 
-       return vcap_tc_flower_handler_vlan_usage(st, vid_key, pcp_key);
+       err = vcap_tc_flower_handler_vlan_usage(st, vid_key, pcp_key);
+       if (err)
+               return err;
+
+       if (st->admin->vtype == VCAP_TYPE_ES0 && st->tpid)
+               err = sparx5_tc_flower_es0_tpid(st);
+
+       return err;
 }
 
 static int (*sparx5_tc_flower_usage_handlers[])(struct vcap_tc_flower_parse_usage *st) = {
@@ -191,38 +224,28 @@ static int (*sparx5_tc_flower_usage_handlers[])(struct vcap_tc_flower_parse_usag
        [FLOW_DISSECTOR_KEY_IP] = vcap_tc_flower_handler_ip_usage,
 };
 
-static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco,
+static int sparx5_tc_use_dissectors(struct vcap_tc_flower_parse_usage *st,
                                    struct vcap_admin *admin,
-                                   struct vcap_rule *vrule,
-                                   u16 *l3_proto)
+                                   struct vcap_rule *vrule)
 {
-       struct vcap_tc_flower_parse_usage state = {
-               .fco = fco,
-               .vrule = vrule,
-               .l3_proto = ETH_P_ALL,
-               .admin = admin,
-       };
        int idx, err = 0;
 
-       state.frule = flow_cls_offload_flow_rule(fco);
        for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) {
-               if (!flow_rule_match_key(state.frule, idx))
+               if (!flow_rule_match_key(st->frule, idx))
                        continue;
                if (!sparx5_tc_flower_usage_handlers[idx])
                        continue;
-               err = sparx5_tc_flower_usage_handlers[idx](&state);
+               err = sparx5_tc_flower_usage_handlers[idx](st);
                if (err)
                        return err;
        }
 
-       if (state.frule->match.dissector->used_keys ^ state.used_keys) {
-               NL_SET_ERR_MSG_MOD(fco->common.extack,
+       if (st->frule->match.dissector->used_keys ^ st->used_keys) {
+               NL_SET_ERR_MSG_MOD(st->fco->common.extack,
                                   "Unsupported match item");
                return -ENOENT;
        }
 
-       if (l3_proto)
-               *l3_proto = state.l3_proto;
        return err;
 }
 
@@ -281,6 +304,27 @@ static int sparx5_tc_flower_action_check(struct vcap_control *vctrl,
                return -EOPNOTSUPP;
        }
 
+       if (action_mask & BIT(FLOW_ACTION_VLAN_PUSH) &&
+           action_mask & BIT(FLOW_ACTION_VLAN_POP)) {
+               NL_SET_ERR_MSG_MOD(fco->common.extack,
+                                  "Cannot combine vlan push and pop action");
+               return -EOPNOTSUPP;
+       }
+
+       if (action_mask & BIT(FLOW_ACTION_VLAN_PUSH) &&
+           action_mask & BIT(FLOW_ACTION_VLAN_MANGLE)) {
+               NL_SET_ERR_MSG_MOD(fco->common.extack,
+                                  "Cannot combine vlan push and modify action");
+               return -EOPNOTSUPP;
+       }
+
+       if (action_mask & BIT(FLOW_ACTION_VLAN_POP) &&
+           action_mask & BIT(FLOW_ACTION_VLAN_MANGLE)) {
+               NL_SET_ERR_MSG_MOD(fco->common.extack,
+                                  "Cannot combine vlan pop and modify action");
+               return -EOPNOTSUPP;
+       }
+
        return 0;
 }
 
@@ -801,6 +845,157 @@ static int sparx5_tc_action_trap(struct vcap_admin *admin,
        return err;
 }
 
+static int sparx5_tc_action_vlan_pop(struct vcap_admin *admin,
+                                    struct vcap_rule *vrule,
+                                    struct flow_cls_offload *fco,
+                                    u16 tpid)
+{
+       int err = 0;
+
+       switch (admin->vtype) {
+       case VCAP_TYPE_ES0:
+               break;
+       default:
+               NL_SET_ERR_MSG_MOD(fco->common.extack,
+                                  "VLAN pop action not supported in this VCAP");
+               return -EOPNOTSUPP;
+       }
+
+       switch (tpid) {
+       case ETH_P_8021Q:
+       case ETH_P_8021AD:
+               err = vcap_rule_add_action_u32(vrule,
+                                              VCAP_AF_PUSH_OUTER_TAG,
+                                              SPX5_OTAG_UNTAG);
+               break;
+       default:
+               NL_SET_ERR_MSG_MOD(fco->common.extack,
+                                  "Invalid vlan proto");
+               err = -EINVAL;
+       }
+       return err;
+}
+
+static int sparx5_tc_action_vlan_modify(struct vcap_admin *admin,
+                                       struct vcap_rule *vrule,
+                                       struct flow_cls_offload *fco,
+                                       struct flow_action_entry *act,
+                                       u16 tpid)
+{
+       int err = 0;
+
+       switch (admin->vtype) {
+       case VCAP_TYPE_ES0:
+               err = vcap_rule_add_action_u32(vrule,
+                                              VCAP_AF_PUSH_OUTER_TAG,
+                                              SPX5_OTAG_TAG_A);
+               if (err)
+                       return err;
+               break;
+       default:
+               NL_SET_ERR_MSG_MOD(fco->common.extack,
+                                  "VLAN modify action not supported in this VCAP");
+               return -EOPNOTSUPP;
+       }
+
+       switch (tpid) {
+       case ETH_P_8021Q:
+               err = vcap_rule_add_action_u32(vrule,
+                                              VCAP_AF_TAG_A_TPID_SEL,
+                                              SPX5_TPID_A_8100);
+               break;
+       case ETH_P_8021AD:
+               err = vcap_rule_add_action_u32(vrule,
+                                              VCAP_AF_TAG_A_TPID_SEL,
+                                              SPX5_TPID_A_88A8);
+               break;
+       default:
+               NL_SET_ERR_MSG_MOD(fco->common.extack,
+                                  "Invalid vlan proto");
+               err = -EINVAL;
+       }
+       if (err)
+               return err;
+
+       err = vcap_rule_add_action_u32(vrule,
+                                      VCAP_AF_TAG_A_VID_SEL,
+                                      SPX5_VID_A_VAL);
+       if (err)
+               return err;
+
+       err = vcap_rule_add_action_u32(vrule,
+                                      VCAP_AF_VID_A_VAL,
+                                      act->vlan.vid);
+       if (err)
+               return err;
+
+       err = vcap_rule_add_action_u32(vrule,
+                                      VCAP_AF_TAG_A_PCP_SEL,
+                                      SPX5_PCP_A_VAL);
+       if (err)
+               return err;
+
+       err = vcap_rule_add_action_u32(vrule,
+                                      VCAP_AF_PCP_A_VAL,
+                                      act->vlan.prio);
+       if (err)
+               return err;
+
+       return vcap_rule_add_action_u32(vrule,
+                                       VCAP_AF_TAG_A_DEI_SEL,
+                                       SPX5_DEI_A_CLASSIFIED);
+}
+
+static int sparx5_tc_action_vlan_push(struct vcap_admin *admin,
+                                     struct vcap_rule *vrule,
+                                     struct flow_cls_offload *fco,
+                                     struct flow_action_entry *act,
+                                     u16 tpid)
+{
+       u16 act_tpid = be16_to_cpu(act->vlan.proto);
+       int err = 0;
+
+       switch (admin->vtype) {
+       case VCAP_TYPE_ES0:
+               break;
+       default:
+               NL_SET_ERR_MSG_MOD(fco->common.extack,
+                                  "VLAN push action not supported in this VCAP");
+               return -EOPNOTSUPP;
+       }
+
+       if (tpid == ETH_P_8021AD) {
+               NL_SET_ERR_MSG_MOD(fco->common.extack,
+                                  "Cannot push on double tagged frames");
+               return -EOPNOTSUPP;
+       }
+
+       err = sparx5_tc_action_vlan_modify(admin, vrule, fco, act, act_tpid);
+       if (err)
+               return err;
+
+       switch (act_tpid) {
+       case ETH_P_8021Q:
+               break;
+       case ETH_P_8021AD:
+               /* Push classified tag as inner tag */
+               err = vcap_rule_add_action_u32(vrule,
+                                              VCAP_AF_PUSH_INNER_TAG,
+                                              SPX5_ITAG_PUSH_B_TAG);
+               if (err)
+                       break;
+               err = vcap_rule_add_action_u32(vrule,
+                                              VCAP_AF_TAG_B_TPID_SEL,
+                                              SPX5_TPID_B_CLASSIFIED);
+               break;
+       default:
+               NL_SET_ERR_MSG_MOD(fco->common.extack,
+                                  "Invalid vlan proto");
+               err = -EINVAL;
+       }
+       return err;
+}
+
 static int sparx5_tc_flower_replace(struct net_device *ndev,
                                    struct flow_cls_offload *fco,
                                    struct vcap_admin *admin,
@@ -809,6 +1004,11 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
        struct sparx5_psfp_sf sf = { .max_sdu = SPX5_PSFP_SF_MAX_SDU };
        struct netlink_ext_ack *extack = fco->common.extack;
        int err, idx, tc_sg_idx = -1, tc_pol_idx = -1;
+       struct vcap_tc_flower_parse_usage state = {
+               .fco = fco,
+               .l3_proto = ETH_P_ALL,
+               .admin = admin,
+       };
        struct sparx5_port *port = netdev_priv(ndev);
        struct sparx5_multiple_rules multi = {};
        struct sparx5 *sparx5 = port->sparx5;
@@ -818,7 +1018,6 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
        struct vcap_control *vctrl;
        struct flow_rule *frule;
        struct vcap_rule *vrule;
-       u16 l3_proto;
 
        vctrl = port->sparx5->vcap_ctrl;
 
@@ -833,8 +1032,9 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
 
        vrule->cookie = fco->cookie;
 
-       l3_proto = ETH_P_ALL;
-       err = sparx5_tc_use_dissectors(fco, admin, vrule, &l3_proto);
+       state.vrule = vrule;
+       state.frule = flow_cls_offload_flow_rule(fco);
+       err = sparx5_tc_use_dissectors(&state, admin, vrule);
        if (err)
                goto out;
 
@@ -888,6 +1088,24 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
                                                fco->common.chain_index,
                                                act->chain_index);
                        break;
+               case FLOW_ACTION_VLAN_POP:
+                       err = sparx5_tc_action_vlan_pop(admin, vrule, fco,
+                                                       state.tpid);
+                       if (err)
+                               goto out;
+                       break;
+               case FLOW_ACTION_VLAN_PUSH:
+                       err = sparx5_tc_action_vlan_push(admin, vrule, fco,
+                                                        act, state.tpid);
+                       if (err)
+                               goto out;
+                       break;
+               case FLOW_ACTION_VLAN_MANGLE:
+                       err = sparx5_tc_action_vlan_modify(admin, vrule, fco,
+                                                          act, state.tpid);
+                       if (err)
+                               goto out;
+                       break;
                default:
                        NL_SET_ERR_MSG_MOD(fco->common.extack,
                                           "Unsupported TC action");
@@ -904,8 +1122,8 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
                        goto out;
        }
 
-       err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin, l3_proto,
-                                              &multi);
+       err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin,
+                                              state.l3_proto, &multi);
        if (err) {
                NL_SET_ERR_MSG_MOD(fco->common.extack,
                                   "No matching port keyset for filter protocol and keys");
@@ -913,7 +1131,7 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
        }
 
        /* provide the l3 protocol to guide the keyset selection */
-       err = vcap_val_rule(vrule, l3_proto);
+       err = vcap_val_rule(vrule, state.l3_proto);
        if (err) {
                vcap_set_tc_exterr(fco, vrule);
                goto out;
@@ -923,7 +1141,7 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
                NL_SET_ERR_MSG_MOD(fco->common.extack,
                                   "Could not add the filter");
 
-       if (l3_proto == ETH_P_ALL)
+       if (state.l3_proto == ETH_P_ALL)
                err = sparx5_tc_add_remaining_rules(vctrl, fco, vrule, admin,
                                                    &multi);
 
index 2086406..d0d4e03 100644 (file)
@@ -852,6 +852,9 @@ static void sparx5_vcap_es0_add_default_fields(struct net_device *ndev,
        struct sparx5_port *port = netdev_priv(ndev);
 
        vcap_rule_add_key_u32(rule, VCAP_KF_IF_EGR_PORT_NO, port->portno, ~0);
+       /* Match untagged frames if there was no VLAN key */
+       vcap_rule_add_key_u32(rule, VCAP_KF_8021Q_TPID, SPX5_TPID_SEL_UNTAGGED,
+                             ~0);
 }
 
 static void sparx5_vcap_es2_add_default_fields(struct net_device *ndev,
index 4b0ad1a..3260ab5 100644 (file)
@@ -176,6 +176,18 @@ enum vcap_es2_port_sel_arp {
        VCAP_ES2_PS_ARP_ARP,
 };
 
+/* Selects TPID for ES0 matching */
+enum SPX5_TPID_SEL {
+       SPX5_TPID_SEL_UNTAGGED,
+       SPX5_TPID_SEL_8100,
+       SPX5_TPID_SEL_UNUSED_0,
+       SPX5_TPID_SEL_UNUSED_1,
+       SPX5_TPID_SEL_88A8,
+       SPX5_TPID_SEL_TPIDCFG_1,
+       SPX5_TPID_SEL_TPIDCFG_2,
+       SPX5_TPID_SEL_TPIDCFG_3,
+};
+
 /* Get the port keyset for the vcap lookup */
 int sparx5_vcap_get_port_keyset(struct net_device *ndev,
                                struct vcap_admin *admin,
index 09a994a..09abe79 100644 (file)
@@ -235,6 +235,9 @@ int vcap_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st,
                        goto out;
        }
 
+       if (mt.mask->vlan_tpid)
+               st->tpid = be16_to_cpu(mt.key->vlan_tpid);
+
        st->used_keys |= BIT(FLOW_DISSECTOR_KEY_VLAN);
 
        return 0;
index 5c55ccb..071f892 100644 (file)
@@ -13,6 +13,7 @@ struct vcap_tc_flower_parse_usage {
        struct vcap_admin *admin;
        u16 l3_proto;
        u8 l4_proto;
+       u16 tpid;
        unsigned int used_keys;
 };