mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-29 13:53:33 +00:00
sfc: offload left-hand side rules for conntrack
Handle the (comparatively) simple case of a -trk rule on an efx netdev (i.e. not a tunnel decap rule) with ct and goto chain actions. Reviewed-by: Pieter Jansen van Vuuren <pieter.jansen-van-vuuren@amd.com> Reviewed-by: Simon Horman <horms@kernel.org> Signed-off-by: Edward Cree <ecree.xilinx@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
1dfc29be4d
commit
01ad088fb0
6 changed files with 599 additions and 0 deletions
|
@ -26,6 +26,8 @@
|
|||
/* Lowest bit numbers and widths */
|
||||
#define EFX_DUMMY_FIELD_LBN 0
|
||||
#define EFX_DUMMY_FIELD_WIDTH 0
|
||||
#define EFX_BYTE_0_LBN 0
|
||||
#define EFX_BYTE_0_WIDTH 8
|
||||
#define EFX_WORD_0_LBN 0
|
||||
#define EFX_WORD_0_WIDTH 16
|
||||
#define EFX_WORD_1_LBN 16
|
||||
|
|
|
@ -727,6 +727,90 @@ int efx_mae_match_check_caps(struct efx_nic *efx,
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Checks for match fields not supported in LHS Outer Rules */
|
||||
#define UNSUPPORTED(_field) ({ \
|
||||
enum mask_type typ = classify_mask((const u8 *)&mask->_field, \
|
||||
sizeof(mask->_field)); \
|
||||
\
|
||||
if (typ != MASK_ZEROES) { \
|
||||
NL_SET_ERR_MSG_MOD(extack, "Unsupported match field " #_field);\
|
||||
rc = -EOPNOTSUPP; \
|
||||
} \
|
||||
rc; \
|
||||
})
|
||||
#define UNSUPPORTED_BIT(_field) ({ \
|
||||
if (mask->_field) { \
|
||||
NL_SET_ERR_MSG_MOD(extack, "Unsupported match field " #_field);\
|
||||
rc = -EOPNOTSUPP; \
|
||||
} \
|
||||
rc; \
|
||||
})
|
||||
|
||||
/* LHS rules are (normally) inserted in the Outer Rule table, which means
|
||||
* they use ENC_ fields in hardware to match regular (not enc_) fields from
|
||||
* &struct efx_tc_match_fields.
|
||||
*/
|
||||
int efx_mae_match_check_caps_lhs(struct efx_nic *efx,
|
||||
const struct efx_tc_match_fields *mask,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
const u8 *supported_fields = efx->tc->caps->outer_rule_fields;
|
||||
__be32 ingress_port = cpu_to_be32(mask->ingress_port);
|
||||
enum mask_type ingress_port_mask_type;
|
||||
int rc;
|
||||
|
||||
/* Check for _PREFIX assumes big-endian, so we need to convert */
|
||||
ingress_port_mask_type = classify_mask((const u8 *)&ingress_port,
|
||||
sizeof(ingress_port));
|
||||
rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_INGRESS_PORT],
|
||||
ingress_port_mask_type);
|
||||
if (rc) {
|
||||
NL_SET_ERR_MSG_FMT_MOD(extack, "No support for %s mask in field %s\n",
|
||||
mask_type_name(ingress_port_mask_type),
|
||||
"ingress_port");
|
||||
return rc;
|
||||
}
|
||||
if (CHECK(ENC_ETHER_TYPE, eth_proto) ||
|
||||
CHECK(ENC_VLAN0_TCI, vlan_tci[0]) ||
|
||||
CHECK(ENC_VLAN0_PROTO, vlan_proto[0]) ||
|
||||
CHECK(ENC_VLAN1_TCI, vlan_tci[1]) ||
|
||||
CHECK(ENC_VLAN1_PROTO, vlan_proto[1]) ||
|
||||
CHECK(ENC_ETH_SADDR, eth_saddr) ||
|
||||
CHECK(ENC_ETH_DADDR, eth_daddr) ||
|
||||
CHECK(ENC_IP_PROTO, ip_proto) ||
|
||||
CHECK(ENC_IP_TOS, ip_tos) ||
|
||||
CHECK(ENC_IP_TTL, ip_ttl) ||
|
||||
CHECK_BIT(ENC_IP_FRAG, ip_frag) ||
|
||||
UNSUPPORTED_BIT(ip_firstfrag) ||
|
||||
CHECK(ENC_SRC_IP4, src_ip) ||
|
||||
CHECK(ENC_DST_IP4, dst_ip) ||
|
||||
#ifdef CONFIG_IPV6
|
||||
CHECK(ENC_SRC_IP6, src_ip6) ||
|
||||
CHECK(ENC_DST_IP6, dst_ip6) ||
|
||||
#endif
|
||||
CHECK(ENC_L4_SPORT, l4_sport) ||
|
||||
CHECK(ENC_L4_DPORT, l4_dport) ||
|
||||
UNSUPPORTED(tcp_flags) ||
|
||||
CHECK_BIT(TCP_SYN_FIN_RST, tcp_syn_fin_rst))
|
||||
return rc;
|
||||
if (efx_tc_match_is_encap(mask)) {
|
||||
/* can't happen; disallowed for local rules, translated
|
||||
* for foreign rules.
|
||||
*/
|
||||
NL_SET_ERR_MSG_MOD(extack, "Unexpected encap match in LHS rule");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (UNSUPPORTED(enc_keyid) ||
|
||||
/* Can't filter on conntrack in LHS rules */
|
||||
UNSUPPORTED_BIT(ct_state_trk) ||
|
||||
UNSUPPORTED_BIT(ct_state_est) ||
|
||||
UNSUPPORTED(ct_mark) ||
|
||||
UNSUPPORTED(recirc_id))
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
#undef UNSUPPORTED
|
||||
#undef CHECK_BIT
|
||||
#undef CHECK
|
||||
|
||||
|
@ -1409,6 +1493,209 @@ int efx_mae_unregister_encap_match(struct efx_nic *efx,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int efx_mae_populate_lhs_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit),
|
||||
const struct efx_tc_match *match)
|
||||
{
|
||||
if (match->mask.ingress_port) {
|
||||
if (~match->mask.ingress_port)
|
||||
return -EOPNOTSUPP;
|
||||
MCDI_STRUCT_SET_DWORD(match_crit,
|
||||
MAE_ENC_FIELD_PAIRS_INGRESS_MPORT_SELECTOR,
|
||||
match->value.ingress_port);
|
||||
}
|
||||
MCDI_STRUCT_SET_DWORD(match_crit, MAE_ENC_FIELD_PAIRS_INGRESS_MPORT_SELECTOR_MASK,
|
||||
match->mask.ingress_port);
|
||||
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_ETHER_TYPE_BE,
|
||||
match->value.eth_proto);
|
||||
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_ETHER_TYPE_BE_MASK,
|
||||
match->mask.eth_proto);
|
||||
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_VLAN0_TCI_BE,
|
||||
match->value.vlan_tci[0]);
|
||||
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_VLAN0_TCI_BE_MASK,
|
||||
match->mask.vlan_tci[0]);
|
||||
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_VLAN0_PROTO_BE,
|
||||
match->value.vlan_proto[0]);
|
||||
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_VLAN0_PROTO_BE_MASK,
|
||||
match->mask.vlan_proto[0]);
|
||||
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_VLAN1_TCI_BE,
|
||||
match->value.vlan_tci[1]);
|
||||
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_VLAN1_TCI_BE_MASK,
|
||||
match->mask.vlan_tci[1]);
|
||||
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_VLAN1_PROTO_BE,
|
||||
match->value.vlan_proto[1]);
|
||||
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_VLAN1_PROTO_BE_MASK,
|
||||
match->mask.vlan_proto[1]);
|
||||
memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_ETH_SADDR_BE),
|
||||
match->value.eth_saddr, ETH_ALEN);
|
||||
memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_ETH_SADDR_BE_MASK),
|
||||
match->mask.eth_saddr, ETH_ALEN);
|
||||
memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_ETH_DADDR_BE),
|
||||
match->value.eth_daddr, ETH_ALEN);
|
||||
memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_ETH_DADDR_BE_MASK),
|
||||
match->mask.eth_daddr, ETH_ALEN);
|
||||
MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_PROTO,
|
||||
match->value.ip_proto);
|
||||
MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_PROTO_MASK,
|
||||
match->mask.ip_proto);
|
||||
MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_TOS,
|
||||
match->value.ip_tos);
|
||||
MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_TOS_MASK,
|
||||
match->mask.ip_tos);
|
||||
MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_TTL,
|
||||
match->value.ip_ttl);
|
||||
MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_TTL_MASK,
|
||||
match->mask.ip_ttl);
|
||||
MCDI_STRUCT_POPULATE_BYTE_1(match_crit,
|
||||
MAE_ENC_FIELD_PAIRS_ENC_VLAN_FLAGS,
|
||||
MAE_ENC_FIELD_PAIRS_ENC_IP_FRAG,
|
||||
match->value.ip_frag);
|
||||
MCDI_STRUCT_POPULATE_BYTE_1(match_crit,
|
||||
MAE_ENC_FIELD_PAIRS_ENC_VLAN_FLAGS_MASK,
|
||||
MAE_ENC_FIELD_PAIRS_ENC_IP_FRAG_MASK,
|
||||
match->mask.ip_frag);
|
||||
MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_SRC_IP4_BE,
|
||||
match->value.src_ip);
|
||||
MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_SRC_IP4_BE_MASK,
|
||||
match->mask.src_ip);
|
||||
MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_DST_IP4_BE,
|
||||
match->value.dst_ip);
|
||||
MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_DST_IP4_BE_MASK,
|
||||
match->mask.dst_ip);
|
||||
#ifdef CONFIG_IPV6
|
||||
memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_SRC_IP6_BE),
|
||||
&match->value.src_ip6, sizeof(struct in6_addr));
|
||||
memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_SRC_IP6_BE_MASK),
|
||||
&match->mask.src_ip6, sizeof(struct in6_addr));
|
||||
memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_DST_IP6_BE),
|
||||
&match->value.dst_ip6, sizeof(struct in6_addr));
|
||||
memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_DST_IP6_BE_MASK),
|
||||
&match->mask.dst_ip6, sizeof(struct in6_addr));
|
||||
#endif
|
||||
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_SPORT_BE,
|
||||
match->value.l4_sport);
|
||||
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_SPORT_BE_MASK,
|
||||
match->mask.l4_sport);
|
||||
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE,
|
||||
match->value.l4_dport);
|
||||
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE_MASK,
|
||||
match->mask.l4_dport);
|
||||
/* No enc-keys in LHS rules. Caps check should have caught this; any
|
||||
* enc-keys from an fLHS should have been translated to regular keys
|
||||
* and any EM should be a pseudo (we're an OR so can't have a direct
|
||||
* EM with another OR).
|
||||
*/
|
||||
if (WARN_ON_ONCE(match->encap && !match->encap->type))
|
||||
return -EOPNOTSUPP;
|
||||
if (WARN_ON_ONCE(match->mask.enc_src_ip))
|
||||
return -EOPNOTSUPP;
|
||||
if (WARN_ON_ONCE(match->mask.enc_dst_ip))
|
||||
return -EOPNOTSUPP;
|
||||
#ifdef CONFIG_IPV6
|
||||
if (WARN_ON_ONCE(!ipv6_addr_any(&match->mask.enc_src_ip6)))
|
||||
return -EOPNOTSUPP;
|
||||
if (WARN_ON_ONCE(!ipv6_addr_any(&match->mask.enc_dst_ip6)))
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
if (WARN_ON_ONCE(match->mask.enc_ip_tos))
|
||||
return -EOPNOTSUPP;
|
||||
if (WARN_ON_ONCE(match->mask.enc_ip_ttl))
|
||||
return -EOPNOTSUPP;
|
||||
if (WARN_ON_ONCE(match->mask.enc_sport))
|
||||
return -EOPNOTSUPP;
|
||||
if (WARN_ON_ONCE(match->mask.enc_dport))
|
||||
return -EOPNOTSUPP;
|
||||
if (WARN_ON_ONCE(match->mask.enc_keyid))
|
||||
return -EOPNOTSUPP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int efx_mae_insert_lhs_outer_rule(struct efx_nic *efx,
|
||||
struct efx_tc_lhs_rule *rule, u32 prio)
|
||||
{
|
||||
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);
|
||||
const struct efx_tc_lhs_action *act;
|
||||
size_t outlen;
|
||||
int rc;
|
||||
|
||||
MCDI_SET_DWORD(inbuf, MAE_OUTER_RULE_INSERT_IN_PRIO, prio);
|
||||
/* match */
|
||||
match_crit = _MCDI_DWORD(inbuf, MAE_OUTER_RULE_INSERT_IN_FIELD_MATCH_CRITERIA);
|
||||
rc = efx_mae_populate_lhs_match_criteria(match_crit, &rule->match);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* action */
|
||||
act = &rule->lhs_act;
|
||||
MCDI_SET_DWORD(inbuf, MAE_OUTER_RULE_INSERT_IN_ENCAP_TYPE,
|
||||
MAE_MCDI_ENCAP_TYPE_NONE);
|
||||
/* We always inhibit CT lookup on TCP_INTERESTING_FLAGS, since the
|
||||
* SW path needs to process the packet to update the conntrack tables
|
||||
* on connection establishment (SYN) or termination (FIN, RST).
|
||||
*/
|
||||
MCDI_POPULATE_DWORD_6(inbuf, MAE_OUTER_RULE_INSERT_IN_LOOKUP_CONTROL,
|
||||
MAE_OUTER_RULE_INSERT_IN_DO_CT, !!act->zone,
|
||||
MAE_OUTER_RULE_INSERT_IN_CT_TCP_FLAGS_INHIBIT, 1,
|
||||
MAE_OUTER_RULE_INSERT_IN_CT_DOMAIN,
|
||||
act->zone ? act->zone->zone : 0,
|
||||
MAE_OUTER_RULE_INSERT_IN_CT_VNI_MODE,
|
||||
MAE_CT_VNI_MODE_ZERO,
|
||||
MAE_OUTER_RULE_INSERT_IN_DO_COUNT, !!act->count,
|
||||
MAE_OUTER_RULE_INSERT_IN_RECIRC_ID,
|
||||
act->rid ? act->rid->fw_id : 0);
|
||||
if (act->count)
|
||||
MCDI_SET_DWORD(inbuf, MAE_OUTER_RULE_INSERT_IN_COUNTER_ID,
|
||||
act->count->cnt->fw_id);
|
||||
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;
|
||||
rule->fw_id = MCDI_DWORD(outbuf, MAE_OUTER_RULE_INSERT_OUT_OR_ID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int efx_mae_insert_lhs_rule(struct efx_nic *efx, struct efx_tc_lhs_rule *rule,
|
||||
u32 prio)
|
||||
{
|
||||
return efx_mae_insert_lhs_outer_rule(efx, rule, prio);
|
||||
}
|
||||
|
||||
static int efx_mae_remove_lhs_outer_rule(struct efx_nic *efx,
|
||||
struct efx_tc_lhs_rule *rule)
|
||||
{
|
||||
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, rule->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) != rule->fw_id))
|
||||
return -EIO;
|
||||
/* We're probably about to free @rule, but let's just make sure its
|
||||
* fw_id is blatted so that it won't look valid if it leaks out.
|
||||
*/
|
||||
rule->fw_id = MC_CMD_MAE_OUTER_RULE_INSERT_OUT_OUTER_RULE_ID_NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int efx_mae_remove_lhs_rule(struct efx_nic *efx, struct efx_tc_lhs_rule *rule)
|
||||
{
|
||||
return efx_mae_remove_lhs_outer_rule(efx, rule);
|
||||
}
|
||||
|
||||
/* Populating is done by taking each byte of @value in turn and storing
|
||||
* it in the appropriate bits of @row. @value must be big-endian; we
|
||||
* convert it to little-endianness as we go.
|
||||
|
|
|
@ -84,6 +84,9 @@ 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_match_check_caps_lhs(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,
|
||||
u8 ip_tos_mask, __be16 udp_sport_mask,
|
||||
struct netlink_ext_ack *extack);
|
||||
|
@ -112,6 +115,9 @@ 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_lhs_rule(struct efx_nic *efx, struct efx_tc_lhs_rule *rule,
|
||||
u32 prio);
|
||||
int efx_mae_remove_lhs_rule(struct efx_nic *efx, struct efx_tc_lhs_rule *rule);
|
||||
struct efx_tc_ct_entry; /* see tc_conntrack.h */
|
||||
int efx_mae_insert_ct(struct efx_nic *efx, struct efx_tc_ct_entry *conn);
|
||||
int efx_mae_remove_ct(struct efx_nic *efx, struct efx_tc_ct_entry *conn);
|
||||
|
|
|
@ -218,6 +218,12 @@ void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev);
|
|||
BUILD_BUG_ON(_field ## _LEN != 1); \
|
||||
*(u8 *)MCDI_STRUCT_PTR(_buf, _field) = _value; \
|
||||
} while (0)
|
||||
#define MCDI_STRUCT_POPULATE_BYTE_1(_buf, _field, _name, _value) do { \
|
||||
efx_dword_t _temp; \
|
||||
EFX_POPULATE_DWORD_1(_temp, _name, _value); \
|
||||
MCDI_STRUCT_SET_BYTE(_buf, _field, \
|
||||
EFX_DWORD_FIELD(_temp, EFX_BYTE_0)); \
|
||||
} while (0)
|
||||
#define MCDI_BYTE(_buf, _field) \
|
||||
((void)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 1), \
|
||||
*MCDI_PTR(_buf, _field))
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <net/pkt_cls.h>
|
||||
#include <net/vxlan.h>
|
||||
#include <net/geneve.h>
|
||||
#include <net/tc_act/tc_ct.h>
|
||||
#include "tc.h"
|
||||
#include "tc_bindings.h"
|
||||
#include "tc_encap_actions.h"
|
||||
|
@ -97,6 +98,12 @@ static const struct rhashtable_params efx_tc_match_action_ht_params = {
|
|||
.head_offset = offsetof(struct efx_tc_flow_rule, linkage),
|
||||
};
|
||||
|
||||
static const struct rhashtable_params efx_tc_lhs_rule_ht_params = {
|
||||
.key_len = sizeof(unsigned long),
|
||||
.key_offset = offsetof(struct efx_tc_lhs_rule, cookie),
|
||||
.head_offset = offsetof(struct efx_tc_lhs_rule, linkage),
|
||||
};
|
||||
|
||||
static const struct rhashtable_params efx_tc_recirc_ht_params = {
|
||||
.key_len = offsetof(struct efx_tc_recirc_id, linkage),
|
||||
.key_offset = 0,
|
||||
|
@ -736,6 +743,163 @@ static bool efx_tc_flower_action_order_ok(const struct efx_tc_action_set *act,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DOC: TC conntrack sequences
|
||||
*
|
||||
* The MAE hardware can handle at most two rounds of action rule matching,
|
||||
* consequently we support conntrack through the notion of a "left-hand side
|
||||
* rule". This is a rule which typically contains only the actions "ct" and
|
||||
* "goto chain N", and corresponds to one or more "right-hand side rules" in
|
||||
* chain N, which typically match on +trk+est, and may perform ct(nat) actions.
|
||||
* RHS rules go in the Action Rule table as normal but with a nonzero recirc_id
|
||||
* (the hardware equivalent of chain_index), while LHS rules may go in either
|
||||
* the Action Rule or the Outer Rule table, the latter being preferred for
|
||||
* performance reasons, and set both DO_CT and a recirc_id in their response.
|
||||
*
|
||||
* Besides the RHS rules, there are often also similar rules matching on
|
||||
* +trk+new which perform the ct(commit) action. These are not offloaded.
|
||||
*/
|
||||
|
||||
static bool efx_tc_rule_is_lhs_rule(struct flow_rule *fr,
|
||||
struct efx_tc_match *match)
|
||||
{
|
||||
const struct flow_action_entry *fa;
|
||||
int i;
|
||||
|
||||
flow_action_for_each(i, fa, &fr->action) {
|
||||
switch (fa->id) {
|
||||
case FLOW_ACTION_GOTO:
|
||||
return true;
|
||||
case FLOW_ACTION_CT:
|
||||
/* If rule is -trk, or doesn't mention trk at all, then
|
||||
* a CT action implies a conntrack lookup (hence it's an
|
||||
* LHS rule). If rule is +trk, then a CT action could
|
||||
* just be ct(nat) or even ct(commit) (though the latter
|
||||
* can't be offloaded).
|
||||
*/
|
||||
if (!match->mask.ct_state_trk || !match->value.ct_state_trk)
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int efx_tc_flower_handle_lhs_actions(struct efx_nic *efx,
|
||||
struct flow_cls_offload *tc,
|
||||
struct flow_rule *fr,
|
||||
struct net_device *net_dev,
|
||||
struct efx_tc_lhs_rule *rule)
|
||||
|
||||
{
|
||||
struct netlink_ext_ack *extack = tc->common.extack;
|
||||
struct efx_tc_lhs_action *act = &rule->lhs_act;
|
||||
const struct flow_action_entry *fa;
|
||||
bool pipe = true;
|
||||
int i;
|
||||
|
||||
flow_action_for_each(i, fa, &fr->action) {
|
||||
struct efx_tc_ct_zone *ct_zone;
|
||||
struct efx_tc_recirc_id *rid;
|
||||
|
||||
if (!pipe) {
|
||||
/* more actions after a non-pipe action */
|
||||
NL_SET_ERR_MSG_MOD(extack, "Action follows non-pipe action");
|
||||
return -EINVAL;
|
||||
}
|
||||
switch (fa->id) {
|
||||
case FLOW_ACTION_GOTO:
|
||||
if (!fa->chain_index) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Can't goto chain 0, no looping in hw");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
rid = efx_tc_get_recirc_id(efx, fa->chain_index,
|
||||
net_dev);
|
||||
if (IS_ERR(rid)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Failed to allocate a hardware recirculation ID for this chain_index");
|
||||
return PTR_ERR(rid);
|
||||
}
|
||||
act->rid = rid;
|
||||
if (fa->hw_stats) {
|
||||
struct efx_tc_counter_index *cnt;
|
||||
|
||||
if (!(fa->hw_stats & FLOW_ACTION_HW_STATS_DELAYED)) {
|
||||
NL_SET_ERR_MSG_FMT_MOD(extack,
|
||||
"hw_stats_type %u not supported (only 'delayed')",
|
||||
fa->hw_stats);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
cnt = efx_tc_flower_get_counter_index(efx, tc->cookie,
|
||||
EFX_TC_COUNTER_TYPE_OR);
|
||||
if (IS_ERR(cnt)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Failed to obtain a counter");
|
||||
return PTR_ERR(cnt);
|
||||
}
|
||||
WARN_ON(act->count); /* can't happen */
|
||||
act->count = cnt;
|
||||
}
|
||||
pipe = false;
|
||||
break;
|
||||
case FLOW_ACTION_CT:
|
||||
if (act->zone) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Can't offload multiple ct actions");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (fa->ct.action & (TCA_CT_ACT_COMMIT |
|
||||
TCA_CT_ACT_FORCE)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Can't offload ct commit/force");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (fa->ct.action & TCA_CT_ACT_CLEAR) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Can't clear ct in LHS rule");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (fa->ct.action & (TCA_CT_ACT_NAT |
|
||||
TCA_CT_ACT_NAT_SRC |
|
||||
TCA_CT_ACT_NAT_DST)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Can't perform NAT in LHS rule - packet isn't conntracked yet");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (fa->ct.action) {
|
||||
NL_SET_ERR_MSG_FMT_MOD(extack, "Unhandled ct.action %u for LHS rule\n",
|
||||
fa->ct.action);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
ct_zone = efx_tc_ct_register_zone(efx, fa->ct.zone,
|
||||
fa->ct.flow_table);
|
||||
if (IS_ERR(ct_zone)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Failed to register for CT updates");
|
||||
return PTR_ERR(ct_zone);
|
||||
}
|
||||
act->zone = ct_zone;
|
||||
break;
|
||||
default:
|
||||
NL_SET_ERR_MSG_FMT_MOD(extack, "Unhandled action %u for LHS rule\n",
|
||||
fa->id);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
if (pipe) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Missing goto chain in LHS rule");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void efx_tc_flower_release_lhs_actions(struct efx_nic *efx,
|
||||
struct efx_tc_lhs_action *act)
|
||||
{
|
||||
if (act->rid)
|
||||
efx_tc_put_recirc_id(efx, act->rid);
|
||||
if (act->zone)
|
||||
efx_tc_ct_unregister_zone(efx, act->zone);
|
||||
if (act->count)
|
||||
efx_tc_flower_put_counter_index(efx, act->count);
|
||||
}
|
||||
|
||||
static int efx_tc_flower_replace_foreign(struct efx_nic *efx,
|
||||
struct net_device *net_dev,
|
||||
struct flow_cls_offload *tc)
|
||||
|
@ -1050,6 +1214,78 @@ static int efx_tc_flower_replace_foreign(struct efx_nic *efx,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int efx_tc_flower_replace_lhs(struct efx_nic *efx,
|
||||
struct flow_cls_offload *tc,
|
||||
struct flow_rule *fr,
|
||||
struct efx_tc_match *match,
|
||||
struct efx_rep *efv,
|
||||
struct net_device *net_dev)
|
||||
{
|
||||
struct netlink_ext_ack *extack = tc->common.extack;
|
||||
struct efx_tc_lhs_rule *rule, *old;
|
||||
int rc;
|
||||
|
||||
if (tc->common.chain_index) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "LHS rule only allowed in chain 0");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (match->mask.ct_state_trk && match->value.ct_state_trk) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "LHS rule can never match +trk");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
/* LHS rules are always -trk, so we don't need to match on that */
|
||||
match->mask.ct_state_trk = 0;
|
||||
match->value.ct_state_trk = 0;
|
||||
|
||||
rc = efx_mae_match_check_caps_lhs(efx, &match->mask, extack);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rule = kzalloc(sizeof(*rule), GFP_USER);
|
||||
if (!rule)
|
||||
return -ENOMEM;
|
||||
rule->cookie = tc->cookie;
|
||||
old = rhashtable_lookup_get_insert_fast(&efx->tc->lhs_rule_ht,
|
||||
&rule->linkage,
|
||||
efx_tc_lhs_rule_ht_params);
|
||||
if (old) {
|
||||
netif_dbg(efx, drv, efx->net_dev,
|
||||
"Already offloaded rule (cookie %lx)\n", tc->cookie);
|
||||
rc = -EEXIST;
|
||||
NL_SET_ERR_MSG_MOD(extack, "Rule already offloaded");
|
||||
goto release;
|
||||
}
|
||||
|
||||
/* Parse actions */
|
||||
/* See note in efx_tc_flower_replace() regarding passed net_dev
|
||||
* (used for efx_tc_get_recirc_id()).
|
||||
*/
|
||||
rc = efx_tc_flower_handle_lhs_actions(efx, tc, fr, efx->net_dev, rule);
|
||||
if (rc)
|
||||
goto release;
|
||||
|
||||
rule->match = *match;
|
||||
|
||||
rc = efx_mae_insert_lhs_rule(efx, rule, EFX_TC_PRIO_TC);
|
||||
if (rc) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Failed to insert rule in hw");
|
||||
goto release;
|
||||
}
|
||||
netif_dbg(efx, drv, efx->net_dev,
|
||||
"Successfully parsed lhs rule (cookie %lx)\n",
|
||||
tc->cookie);
|
||||
return 0;
|
||||
|
||||
release:
|
||||
efx_tc_flower_release_lhs_actions(efx, &rule->lhs_act);
|
||||
if (!old)
|
||||
rhashtable_remove_fast(&efx->tc->lhs_rule_ht, &rule->linkage,
|
||||
efx_tc_lhs_rule_ht_params);
|
||||
kfree(rule);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int efx_tc_flower_replace(struct efx_nic *efx,
|
||||
struct net_device *net_dev,
|
||||
struct flow_cls_offload *tc,
|
||||
|
@ -1105,6 +1341,10 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (efx_tc_rule_is_lhs_rule(fr, &match))
|
||||
return efx_tc_flower_replace_lhs(efx, tc, fr, &match, efv,
|
||||
net_dev);
|
||||
|
||||
/* chain_index 0 is always recirc_id 0 (and does not appear in recirc_ht).
|
||||
* Conveniently, match.rid == NULL and match.value.recirc_id == 0 owing
|
||||
* to the initial memset(), so we don't need to do anything in that case.
|
||||
|
@ -1512,8 +1752,26 @@ static int efx_tc_flower_destroy(struct efx_nic *efx,
|
|||
struct flow_cls_offload *tc)
|
||||
{
|
||||
struct netlink_ext_ack *extack = tc->common.extack;
|
||||
struct efx_tc_lhs_rule *lhs_rule;
|
||||
struct efx_tc_flow_rule *rule;
|
||||
|
||||
lhs_rule = rhashtable_lookup_fast(&efx->tc->lhs_rule_ht, &tc->cookie,
|
||||
efx_tc_lhs_rule_ht_params);
|
||||
if (lhs_rule) {
|
||||
/* Remove it from HW */
|
||||
efx_mae_remove_lhs_rule(efx, lhs_rule);
|
||||
/* Delete it from SW */
|
||||
efx_tc_flower_release_lhs_actions(efx, &lhs_rule->lhs_act);
|
||||
rhashtable_remove_fast(&efx->tc->lhs_rule_ht, &lhs_rule->linkage,
|
||||
efx_tc_lhs_rule_ht_params);
|
||||
if (lhs_rule->match.encap)
|
||||
efx_tc_flower_release_encap_match(efx, lhs_rule->match.encap);
|
||||
netif_dbg(efx, drv, efx->net_dev, "Removed (lhs) filter %lx\n",
|
||||
lhs_rule->cookie);
|
||||
kfree(lhs_rule);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rule = rhashtable_lookup_fast(&efx->tc->match_action_ht, &tc->cookie,
|
||||
efx_tc_match_action_ht_params);
|
||||
if (!rule) {
|
||||
|
@ -1880,6 +2138,24 @@ static void efx_tc_recirc_free(void *ptr, void *arg)
|
|||
kfree(rid);
|
||||
}
|
||||
|
||||
static void efx_tc_lhs_free(void *ptr, void *arg)
|
||||
{
|
||||
struct efx_tc_lhs_rule *rule = ptr;
|
||||
struct efx_nic *efx = arg;
|
||||
|
||||
netif_err(efx, drv, efx->net_dev,
|
||||
"tc lhs_rule %lx still present at teardown, removing\n",
|
||||
rule->cookie);
|
||||
|
||||
if (rule->lhs_act.zone)
|
||||
efx_tc_ct_unregister_zone(efx, rule->lhs_act.zone);
|
||||
if (rule->lhs_act.count)
|
||||
efx_tc_flower_put_counter_index(efx, rule->lhs_act.count);
|
||||
efx_mae_remove_lhs_rule(efx, rule);
|
||||
|
||||
kfree(rule);
|
||||
}
|
||||
|
||||
static void efx_tc_flow_free(void *ptr, void *arg)
|
||||
{
|
||||
struct efx_tc_flow_rule *rule = ptr;
|
||||
|
@ -1926,6 +2202,9 @@ int efx_init_struct_tc(struct efx_nic *efx)
|
|||
rc = rhashtable_init(&efx->tc->match_action_ht, &efx_tc_match_action_ht_params);
|
||||
if (rc < 0)
|
||||
goto fail_match_action_ht;
|
||||
rc = rhashtable_init(&efx->tc->lhs_rule_ht, &efx_tc_lhs_rule_ht_params);
|
||||
if (rc < 0)
|
||||
goto fail_lhs_rule_ht;
|
||||
rc = efx_tc_init_conntrack(efx);
|
||||
if (rc < 0)
|
||||
goto fail_conntrack;
|
||||
|
@ -1948,6 +2227,8 @@ int efx_init_struct_tc(struct efx_nic *efx)
|
|||
fail_recirc_ht:
|
||||
efx_tc_destroy_conntrack(efx);
|
||||
fail_conntrack:
|
||||
rhashtable_destroy(&efx->tc->lhs_rule_ht);
|
||||
fail_lhs_rule_ht:
|
||||
rhashtable_destroy(&efx->tc->match_action_ht);
|
||||
fail_match_action_ht:
|
||||
rhashtable_destroy(&efx->tc->encap_match_ht);
|
||||
|
@ -1978,6 +2259,7 @@ void efx_fini_struct_tc(struct efx_nic *efx)
|
|||
MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL);
|
||||
EFX_WARN_ON_PARANOID(efx->tc->facts.reps.fw_id !=
|
||||
MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL);
|
||||
rhashtable_free_and_destroy(&efx->tc->lhs_rule_ht, efx_tc_lhs_free, efx);
|
||||
rhashtable_free_and_destroy(&efx->tc->match_action_ht, efx_tc_flow_free,
|
||||
efx);
|
||||
rhashtable_free_and_destroy(&efx->tc->encap_match_ht,
|
||||
|
|
|
@ -140,6 +140,12 @@ struct efx_tc_action_set_list {
|
|||
u32 fw_id;
|
||||
};
|
||||
|
||||
struct efx_tc_lhs_action {
|
||||
struct efx_tc_recirc_id *rid;
|
||||
struct efx_tc_ct_zone *zone;
|
||||
struct efx_tc_counter_index *count;
|
||||
};
|
||||
|
||||
struct efx_tc_flow_rule {
|
||||
unsigned long cookie;
|
||||
struct rhash_head linkage;
|
||||
|
@ -149,6 +155,14 @@ struct efx_tc_flow_rule {
|
|||
u32 fw_id;
|
||||
};
|
||||
|
||||
struct efx_tc_lhs_rule {
|
||||
unsigned long cookie;
|
||||
struct efx_tc_match match;
|
||||
struct efx_tc_lhs_action lhs_act;
|
||||
struct rhash_head linkage;
|
||||
u32 fw_id;
|
||||
};
|
||||
|
||||
enum efx_tc_rule_prios {
|
||||
EFX_TC_PRIO_TC, /* Rule inserted by TC */
|
||||
EFX_TC_PRIO_DFLT, /* Default switch rule; one of efx_tc_default_rules */
|
||||
|
@ -208,6 +222,7 @@ struct efx_tc_table_ct { /* TABLE_ID_CONNTRACK_TABLE */
|
|||
* @encap_ht: Hashtable of TC encap actions
|
||||
* @encap_match_ht: Hashtable of TC encap matches
|
||||
* @match_action_ht: Hashtable of TC match-action rules
|
||||
* @lhs_rule_ht: Hashtable of TC left-hand (act ct & goto chain) rules
|
||||
* @ct_zone_ht: Hashtable of TC conntrack flowtable bindings
|
||||
* @ct_ht: Hashtable of TC conntrack flow entries
|
||||
* @neigh_ht: Hashtable of neighbour watches (&struct efx_neigh_binder)
|
||||
|
@ -244,6 +259,7 @@ struct efx_tc_state {
|
|||
struct rhashtable encap_ht;
|
||||
struct rhashtable encap_match_ht;
|
||||
struct rhashtable match_action_ht;
|
||||
struct rhashtable lhs_rule_ht;
|
||||
struct rhashtable ct_zone_ht;
|
||||
struct rhashtable ct_ht;
|
||||
struct rhashtable neigh_ht;
|
||||
|
|
Loading…
Reference in a new issue