sfc: add notion of match on enc keys to MAE machinery
authorEdward Cree <ecree.xilinx@gmail.com>
Mon, 27 Mar 2023 10:36:04 +0000 (11:36 +0100)
committerDavid S. Miller <davem@davemloft.net>
Wed, 29 Mar 2023 08:06:08 +0000 (09:06 +0100)
Extend the MAE caps check to validate that the hardware supports these
 outer-header matches where used by the driver.
Extend efx_mae_populate_match_criteria() to fill in the outer rule ID
 and VNI match fields.
Nothing yet populates these match fields, nor creates outer rules.

Signed-off-by: Edward Cree <ecree.xilinx@gmail.com>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/sfc/mae.c
drivers/net/ethernet/sfc/mae.h
drivers/net/ethernet/sfc/tc.h

index c53d354c1fb23d0465416fbd815eb4ca89790088..2290a63908c5fcd8adb58d9844e7eceefbe6ef0d 100644 (file)
@@ -254,13 +254,23 @@ static int efx_mae_get_rule_fields(struct efx_nic *efx, u32 cmd,
        size_t outlen;
        int rc, i;
 
+       /* AR and OR caps MCDIs have identical layout, so we are using the
+        * same code for both.
+        */
+       BUILD_BUG_ON(MC_CMD_MAE_GET_AR_CAPS_OUT_LEN(MAE_NUM_FIELDS) <
+                    MC_CMD_MAE_GET_OR_CAPS_OUT_LEN(MAE_NUM_FIELDS));
        BUILD_BUG_ON(MC_CMD_MAE_GET_AR_CAPS_IN_LEN);
+       BUILD_BUG_ON(MC_CMD_MAE_GET_OR_CAPS_IN_LEN);
 
        rc = efx_mcdi_rpc(efx, cmd, NULL, 0, outbuf, sizeof(outbuf), &outlen);
        if (rc)
                return rc;
+       BUILD_BUG_ON(MC_CMD_MAE_GET_AR_CAPS_OUT_COUNT_OFST !=
+                    MC_CMD_MAE_GET_OR_CAPS_OUT_COUNT_OFST);
        count = MCDI_DWORD(outbuf, MAE_GET_AR_CAPS_OUT_COUNT);
        memset(field_support, MAE_FIELD_UNSUPPORTED, MAE_NUM_FIELDS);
+       BUILD_BUG_ON(MC_CMD_MAE_GET_AR_CAPS_OUT_FIELD_FLAGS_OFST !=
+                    MC_CMD_MAE_GET_OR_CAPS_OUT_FIELD_FLAGS_OFST);
        caps = _MCDI_DWORD(outbuf, MAE_GET_AR_CAPS_OUT_FIELD_FLAGS);
        /* We're only interested in the support status enum, not any other
         * flags, so just extract that from each entry.
@@ -278,8 +288,12 @@ int efx_mae_get_caps(struct efx_nic *efx, struct mae_caps *caps)
        rc = efx_mae_get_basic_caps(efx, caps);
        if (rc)
                return rc;
-       return efx_mae_get_rule_fields(efx, MC_CMD_MAE_GET_AR_CAPS,
-                                      caps->action_rule_fields);
+       rc = efx_mae_get_rule_fields(efx, MC_CMD_MAE_GET_AR_CAPS,
+                                    caps->action_rule_fields);
+       if (rc)
+               return rc;
+       return efx_mae_get_rule_fields(efx, MC_CMD_MAE_GET_OR_CAPS,
+                                      caps->outer_rule_fields);
 }
 
 /* Bit twiddling:
@@ -432,11 +446,67 @@ int efx_mae_match_check_caps(struct efx_nic *efx,
            CHECK_BIT(IP_FIRST_FRAG, ip_firstfrag) ||
            CHECK(RECIRC_ID, recirc_id))
                return rc;
+       /* Matches on outer fields are done in a separate hardware table,
+        * the Outer Rule table.  Thus the Action Rule merely does an
+        * exact match on Outer Rule ID if any outer field matches are
+        * present.  The exception is the VNI/VSID (enc_keyid), which is
+        * available to the Action Rule match iff the Outer Rule matched
+        * (and thus identified the encap protocol to use to extract it).
+        */
+       if (efx_tc_match_is_encap(mask)) {
+               rc = efx_mae_match_check_cap_typ(
+                               supported_fields[MAE_FIELD_OUTER_RULE_ID],
+                               MASK_ONES);
+               if (rc) {
+                       NL_SET_ERR_MSG_MOD(extack, "No support for encap rule ID matches");
+                       return rc;
+               }
+               if (CHECK(ENC_VNET_ID, enc_keyid))
+                       return rc;
+       } else if (mask->enc_keyid) {
+               NL_SET_ERR_MSG_MOD(extack, "Match on enc_keyid requires other encap fields");
+               return -EINVAL;
+       }
        return 0;
 }
 #undef CHECK_BIT
 #undef CHECK
 
+#define CHECK(_mcdi)   ({                                                     \
+       rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ ## _mcdi],\
+                                        MASK_ONES);                           \
+       if (rc)                                                                \
+               NL_SET_ERR_MSG_FMT_MOD(extack,                                 \
+                                      "No support for field %s", #_mcdi);     \
+       rc;                                                                    \
+})
+/* Checks that the fields needed for encap-rule matches are supported by the
+ * MAE.  All the fields are exact-match.
+ */
+int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6,
+                                  struct netlink_ext_ack *extack)
+{
+       u8 *supported_fields = efx->tc->caps->outer_rule_fields;
+       int rc;
+
+       if (CHECK(ENC_ETHER_TYPE))
+               return rc;
+       if (ipv6) {
+               if (CHECK(ENC_SRC_IP6) ||
+                   CHECK(ENC_DST_IP6))
+                       return rc;
+       } else {
+               if (CHECK(ENC_SRC_IP4) ||
+                   CHECK(ENC_DST_IP4))
+                       return rc;
+       }
+       if (CHECK(ENC_L4_DPORT) ||
+           CHECK(ENC_IP_PROTO))
+               return rc;
+       return 0;
+}
+#undef CHECK
+
 int efx_mae_allocate_counter(struct efx_nic *efx, struct efx_tc_counter *cnt)
 {
        MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTER_ALLOC_OUT_LEN(1));
@@ -941,6 +1011,29 @@ static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit),
                                match->value.tcp_flags);
        MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_TCP_FLAGS_BE_MASK,
                                match->mask.tcp_flags);
+       /* enc-keys are handled indirectly, through encap_match ID */
+       if (match->encap) {
+               MCDI_STRUCT_SET_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_OUTER_RULE_ID,
+                                     match->encap->fw_id);
+               MCDI_STRUCT_SET_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_OUTER_RULE_ID_MASK,
+                                     U32_MAX);
+               /* enc_keyid (VNI/VSID) is not part of the encap_match */
+               MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ENC_VNET_ID_BE,
+                                        match->value.enc_keyid);
+               MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ENC_VNET_ID_BE_MASK,
+                                        match->mask.enc_keyid);
+       } else if (WARN_ON_ONCE(match->mask.enc_src_ip) ||
+                  WARN_ON_ONCE(match->mask.enc_dst_ip) ||
+                  WARN_ON_ONCE(!ipv6_addr_any(&match->mask.enc_src_ip6)) ||
+                  WARN_ON_ONCE(!ipv6_addr_any(&match->mask.enc_dst_ip6)) ||
+                  WARN_ON_ONCE(match->mask.enc_ip_tos) ||
+                  WARN_ON_ONCE(match->mask.enc_ip_ttl) ||
+                  WARN_ON_ONCE(match->mask.enc_sport) ||
+                  WARN_ON_ONCE(match->mask.enc_dport) ||
+                  WARN_ON_ONCE(match->mask.enc_keyid)) {
+               /* No enc-keys should appear in a rule without an encap_match */
+               return -EOPNOTSUPP;
+       }
        return 0;
 }
 
index bec293a067337fd530fc1e5ee80728779c4fae9e..2ccbc62d79b98163aad93f2d3b4f5e58aa464297 100644 (file)
@@ -72,6 +72,7 @@ struct mae_caps {
        u32 match_field_count;
        u32 action_prios;
        u8 action_rule_fields[MAE_NUM_FIELDS];
+       u8 outer_rule_fields[MAE_NUM_FIELDS];
 };
 
 int efx_mae_get_caps(struct efx_nic *efx, struct mae_caps *caps);
@@ -79,6 +80,8 @@ int efx_mae_get_caps(struct efx_nic *efx, struct mae_caps *caps);
 int efx_mae_match_check_caps(struct efx_nic *efx,
                             const struct efx_tc_match_fields *mask,
                             struct netlink_ext_ack *extack);
+int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6,
+                                  struct netlink_ext_ack *extack);
 
 int efx_mae_allocate_counter(struct efx_nic *efx, struct efx_tc_counter *cnt);
 int efx_mae_free_counter(struct efx_nic *efx, struct efx_tc_counter *cnt);
index 542853f60c2a5fd159c9b9cdef209539cdb227d3..c1485679507cc185dc1b4829f5e324d1569adf5c 100644 (file)
@@ -48,11 +48,35 @@ struct efx_tc_match_fields {
        /* L4 */
        __be16 l4_sport, l4_dport; /* Ports (UDP, TCP) */
        __be16 tcp_flags;
+       /* Encap.  The following are *outer* fields.  Note that there are no
+        * outer eth (L2) fields; this is because TC doesn't have them.
+        */
+       __be32 enc_src_ip, enc_dst_ip;
+       struct in6_addr enc_src_ip6, enc_dst_ip6;
+       u8 enc_ip_tos, enc_ip_ttl;
+       __be16 enc_sport, enc_dport;
+       __be32 enc_keyid; /* e.g. VNI, VSID */
+};
+
+static inline bool efx_tc_match_is_encap(const struct efx_tc_match_fields *mask)
+{
+       return mask->enc_src_ip || mask->enc_dst_ip ||
+              !ipv6_addr_any(&mask->enc_src_ip6) ||
+              !ipv6_addr_any(&mask->enc_dst_ip6) || mask->enc_ip_tos ||
+              mask->enc_ip_ttl || mask->enc_sport || mask->enc_dport;
+}
+
+struct efx_tc_encap_match {
+       __be32 src_ip, dst_ip;
+       struct in6_addr src_ip6, dst_ip6;
+       __be16 udp_dport;
+       u32 fw_id; /* index of this entry in firmware encap match table */
 };
 
 struct efx_tc_match {
        struct efx_tc_match_fields value;
        struct efx_tc_match_fields mask;
+       struct efx_tc_encap_match *encap;
 };
 
 struct efx_tc_action_set_list {