Skip to content

Commit

Permalink
Merge branch 'sfc-tc-decap-support'
Browse files Browse the repository at this point in the history
Edward Cree says:

====================
sfc: support TC decap rules

This series adds support for offloading tunnel decapsulation TC rules to
 ef100 NICs, allowing matching encapsulated packets to be decapsulated in
 hardware and redirected to VFs.
For now an encap match must be on precisely the following fields:
 ethertype (IPv4 or IPv6), source IP, destination IP, ipproto UDP,
 UDP destination port.  This simplifies checking for overlaps in the
 driver; the hardware supports a wider range of match fields which
 future driver work may expose.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Mar 29, 2023
2 parents 37018b5 + 17654d8 commit be435af
Show file tree
Hide file tree
Showing 4 changed files with 857 additions and 18 deletions.
227 changes: 223 additions & 4 deletions drivers/net/ethernet/sfc/mae.c
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ static int efx_mae_get_basic_caps(struct efx_nic *efx, struct mae_caps *caps)
if (outlen < sizeof(outbuf))
return -EIO;
caps->match_field_count = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_MATCH_FIELD_COUNT);
caps->encap_types = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_ENCAP_TYPES_SUPPORTED);
caps->action_prios = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_ACTION_PRIOS);
return 0;
}
Expand All @@ -254,13 +255,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.
Expand All @@ -278,8 +289,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:
Expand Down Expand Up @@ -432,11 +447,86 @@ 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_check_encap_type_supported(struct efx_nic *efx, enum efx_encap_type typ)
{
unsigned int bit;

switch (typ & EFX_ENCAP_TYPES_MASK) {
case EFX_ENCAP_TYPE_VXLAN:
bit = MC_CMD_MAE_GET_CAPS_OUT_ENCAP_TYPE_VXLAN_LBN;
break;
case EFX_ENCAP_TYPE_GENEVE:
bit = MC_CMD_MAE_GET_CAPS_OUT_ENCAP_TYPE_GENEVE_LBN;
break;
default:
return -EOPNOTSUPP;
}
if (efx->tc->caps->encap_types & BIT(bit))
return 0;
return -EOPNOTSUPP;
}

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));
Expand Down Expand Up @@ -488,6 +578,20 @@ int efx_mae_free_counter(struct efx_nic *efx, struct efx_tc_counter *cnt)
return 0;
}

static int efx_mae_encap_type_to_mae_type(enum efx_encap_type type)
{
switch (type & EFX_ENCAP_TYPES_MASK) {
case EFX_ENCAP_TYPE_NONE:
return MAE_MCDI_ENCAP_TYPE_NONE;
case EFX_ENCAP_TYPE_VXLAN:
return MAE_MCDI_ENCAP_TYPE_VXLAN;
case EFX_ENCAP_TYPE_GENEVE:
return MAE_MCDI_ENCAP_TYPE_GENEVE;
default:
return -EOPNOTSUPP;
}
}

int efx_mae_lookup_mport(struct efx_nic *efx, u32 vf_idx, u32 *id)
{
struct ef100_nic_data *nic_data = efx->nic_data;
Expand Down Expand Up @@ -682,9 +786,10 @@ int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act)
size_t outlen;
int rc;

MCDI_POPULATE_DWORD_2(inbuf, MAE_ACTION_SET_ALLOC_IN_FLAGS,
MCDI_POPULATE_DWORD_3(inbuf, MAE_ACTION_SET_ALLOC_IN_FLAGS,
MAE_ACTION_SET_ALLOC_IN_VLAN_PUSH, act->vlan_push,
MAE_ACTION_SET_ALLOC_IN_VLAN_POP, act->vlan_pop);
MAE_ACTION_SET_ALLOC_IN_VLAN_POP, act->vlan_pop,
MAE_ACTION_SET_ALLOC_IN_DECAP, act->decap);

MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID,
MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
Expand Down Expand Up @@ -845,6 +950,97 @@ int efx_mae_free_action_set_list(struct efx_nic *efx,
return 0;
}

int efx_mae_register_encap_match(struct efx_nic *efx,
struct efx_tc_encap_match *encap)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_OUTER_RULE_INSERT_IN_LEN(MAE_ENC_FIELD_PAIRS_LEN));
MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_OUTER_RULE_INSERT_OUT_LEN);
MCDI_DECLARE_STRUCT_PTR(match_crit);
size_t outlen;
int rc;

rc = efx_mae_encap_type_to_mae_type(encap->tun_type);
if (rc < 0)
return rc;
match_crit = _MCDI_DWORD(inbuf, MAE_OUTER_RULE_INSERT_IN_FIELD_MATCH_CRITERIA);
/* The struct contains IP src and dst, and udp dport.
* So we actually need to filter on IP src and dst, L4 dport, and
* ipproto == udp.
*/
MCDI_SET_DWORD(inbuf, MAE_OUTER_RULE_INSERT_IN_ENCAP_TYPE, rc);
#ifdef CONFIG_IPV6
if (encap->src_ip | encap->dst_ip) {
#endif
MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_SRC_IP4_BE,
encap->src_ip);
MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_SRC_IP4_BE_MASK,
~(__be32)0);
MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_DST_IP4_BE,
encap->dst_ip);
MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_DST_IP4_BE_MASK,
~(__be32)0);
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_ETHER_TYPE_BE,
htons(ETH_P_IP));
#ifdef CONFIG_IPV6
} else {
memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_SRC_IP6_BE),
&encap->src_ip6, sizeof(encap->src_ip6));
memset(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_SRC_IP6_BE_MASK),
0xff, sizeof(encap->src_ip6));
memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_DST_IP6_BE),
&encap->dst_ip6, sizeof(encap->dst_ip6));
memset(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_DST_IP6_BE_MASK),
0xff, sizeof(encap->dst_ip6));
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_ETHER_TYPE_BE,
htons(ETH_P_IPV6));
}
#endif
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_ETHER_TYPE_BE_MASK,
~(__be16)0);
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE,
encap->udp_dport);
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE_MASK,
~(__be16)0);
MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_PROTO, IPPROTO_UDP);
MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_PROTO_MASK, ~0);
rc = efx_mcdi_rpc(efx, MC_CMD_MAE_OUTER_RULE_INSERT, inbuf,
sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
if (rc)
return rc;
if (outlen < sizeof(outbuf))
return -EIO;
encap->fw_id = MCDI_DWORD(outbuf, MAE_OUTER_RULE_INSERT_OUT_OR_ID);
return 0;
}

int efx_mae_unregister_encap_match(struct efx_nic *efx,
struct efx_tc_encap_match *encap)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_OUTER_RULE_REMOVE_OUT_LEN(1));
MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_OUTER_RULE_REMOVE_IN_LEN(1));
size_t outlen;
int rc;

MCDI_SET_DWORD(inbuf, MAE_OUTER_RULE_REMOVE_IN_OR_ID, encap->fw_id);
rc = efx_mcdi_rpc(efx, MC_CMD_MAE_OUTER_RULE_REMOVE, inbuf,
sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
if (rc)
return rc;
if (outlen < sizeof(outbuf))
return -EIO;
/* FW freed a different ID than we asked for, should also never happen.
* Warn because it means we've now got a different idea to the FW of
* what encap_mds exist, which could cause mayhem later.
*/
if (WARN_ON(MCDI_DWORD(outbuf, MAE_OUTER_RULE_REMOVE_OUT_REMOVED_OR_ID) != encap->fw_id))
return -EIO;
/* We're probably about to free @encap, but let's just make sure its
* fw_id is blatted so that it won't look valid if it leaks out.
*/
encap->fw_id = MC_CMD_MAE_OUTER_RULE_INSERT_OUT_OUTER_RULE_ID_NULL;
return 0;
}

static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit),
const struct efx_tc_match *match)
{
Expand Down Expand Up @@ -941,6 +1137,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;
}

Expand Down
11 changes: 11 additions & 0 deletions drivers/net/ethernet/sfc/mae.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,21 @@ void efx_mae_counters_grant_credits(struct work_struct *work);

struct mae_caps {
u32 match_field_count;
u32 encap_types;
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);

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_check_encap_type_supported(struct efx_nic *efx,
enum efx_encap_type typ);

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);
Expand All @@ -91,6 +97,11 @@ int efx_mae_alloc_action_set_list(struct efx_nic *efx,
int efx_mae_free_action_set_list(struct efx_nic *efx,
struct efx_tc_action_set_list *acts);

int efx_mae_register_encap_match(struct efx_nic *efx,
struct efx_tc_encap_match *encap);
int efx_mae_unregister_encap_match(struct efx_nic *efx,
struct efx_tc_encap_match *encap);

int efx_mae_insert_rule(struct efx_nic *efx, const struct efx_tc_match *match,
u32 prio, u32 acts_id, u32 *id);
int efx_mae_delete_rule(struct efx_nic *efx, u32 id);
Expand Down
Loading

0 comments on commit be435af

Please sign in to comment.