Skip to content

Commit ba79e9a

Browse files
committed
Merge branch 'sfc-decap'
Edward Cree says: ==================== sfc: more flexible encap matches on TC decap rules This series extends the TC offload support on EF100 to support optionally matching on the IP ToS and UDP source port of the outer header in rules performing tunnel decapsulation. Both of these fields allow masked matches if the underlying hardware supports it (current EF100 hardware supports masking on ToS, but only exact-match on source port). Given that the source port is typically populated from a hash of inner header entropy, it's not clear whether filtering on it is useful, but since we can support it we may as well expose the capability. ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents d3616dc + b6583d5 commit ba79e9a

File tree

4 files changed

+197
-64
lines changed

4 files changed

+197
-64
lines changed

drivers/net/ethernet/sfc/mae.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,12 +482,14 @@ int efx_mae_match_check_caps(struct efx_nic *efx,
482482
rc; \
483483
})
484484
/* Checks that the fields needed for encap-rule matches are supported by the
485-
* MAE. All the fields are exact-match.
485+
* MAE. All the fields are exact-match, except possibly ENC_IP_TOS.
486486
*/
487487
int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6,
488+
u8 ip_tos_mask, __be16 udp_sport_mask,
488489
struct netlink_ext_ack *extack)
489490
{
490491
u8 *supported_fields = efx->tc->caps->outer_rule_fields;
492+
enum mask_type typ;
491493
int rc;
492494

493495
if (CHECK(ENC_ETHER_TYPE))
@@ -504,6 +506,22 @@ int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6,
504506
if (CHECK(ENC_L4_DPORT) ||
505507
CHECK(ENC_IP_PROTO))
506508
return rc;
509+
typ = classify_mask((const u8 *)&udp_sport_mask, sizeof(udp_sport_mask));
510+
rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ENC_L4_SPORT],
511+
typ);
512+
if (rc) {
513+
NL_SET_ERR_MSG_FMT_MOD(extack, "No support for %s mask in field %s",
514+
mask_type_name(typ), "enc_src_port");
515+
return rc;
516+
}
517+
typ = classify_mask(&ip_tos_mask, sizeof(ip_tos_mask));
518+
rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ENC_IP_TOS],
519+
typ);
520+
if (rc) {
521+
NL_SET_ERR_MSG_FMT_MOD(extack, "No support for %s mask in field %s",
522+
mask_type_name(typ), "enc_ip_tos");
523+
return rc;
524+
}
507525
return 0;
508526
}
509527
#undef CHECK
@@ -1001,8 +1019,16 @@ int efx_mae_register_encap_match(struct efx_nic *efx,
10011019
encap->udp_dport);
10021020
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE_MASK,
10031021
~(__be16)0);
1022+
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE,
1023+
encap->udp_sport);
1024+
MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE_MASK,
1025+
encap->udp_sport_mask);
10041026
MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_PROTO, IPPROTO_UDP);
10051027
MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_PROTO_MASK, ~0);
1028+
MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_TOS,
1029+
encap->ip_tos);
1030+
MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_TOS_MASK,
1031+
encap->ip_tos_mask);
10061032
rc = efx_mcdi_rpc(efx, MC_CMD_MAE_OUTER_RULE_INSERT, inbuf,
10071033
sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
10081034
if (rc)

drivers/net/ethernet/sfc/mae.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ int efx_mae_match_check_caps(struct efx_nic *efx,
8282
const struct efx_tc_match_fields *mask,
8383
struct netlink_ext_ack *extack);
8484
int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6,
85+
u8 ip_tos_mask, __be16 udp_sport_mask,
8586
struct netlink_ext_ack *extack);
8687
int efx_mae_check_encap_type_supported(struct efx_nic *efx,
8788
enum efx_encap_type typ);

drivers/net/ethernet/sfc/tc.c

Lines changed: 142 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -132,23 +132,6 @@ static void efx_tc_free_action_set_list(struct efx_nic *efx,
132132
/* Don't kfree, as acts is embedded inside a struct efx_tc_flow_rule */
133133
}
134134

135-
static void efx_tc_flow_free(void *ptr, void *arg)
136-
{
137-
struct efx_tc_flow_rule *rule = ptr;
138-
struct efx_nic *efx = arg;
139-
140-
netif_err(efx, drv, efx->net_dev,
141-
"tc rule %lx still present at teardown, removing\n",
142-
rule->cookie);
143-
144-
efx_mae_delete_rule(efx, rule->fw_id);
145-
146-
/* Release entries in subsidiary tables */
147-
efx_tc_free_action_set_list(efx, &rule->acts, true);
148-
149-
kfree(rule);
150-
}
151-
152135
/* Boilerplate for the simple 'copy a field' cases */
153136
#define _MAP_KEY_AND_MASK(_name, _type, _tcget, _tcfield, _field) \
154137
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_##_name)) { \
@@ -219,6 +202,7 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx,
219202
BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) |
220203
BIT(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) |
221204
BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) |
205+
BIT(FLOW_DISSECTOR_KEY_ENC_IP) |
222206
BIT(FLOW_DISSECTOR_KEY_ENC_PORTS) |
223207
BIT(FLOW_DISSECTOR_KEY_ENC_CONTROL) |
224208
BIT(FLOW_DISSECTOR_KEY_TCP) |
@@ -363,20 +347,48 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx,
363347
return 0;
364348
}
365349

350+
static void efx_tc_flower_release_encap_match(struct efx_nic *efx,
351+
struct efx_tc_encap_match *encap)
352+
{
353+
int rc;
354+
355+
if (!refcount_dec_and_test(&encap->ref))
356+
return; /* still in use */
357+
358+
if (encap->type == EFX_TC_EM_DIRECT) {
359+
rc = efx_mae_unregister_encap_match(efx, encap);
360+
if (rc)
361+
/* Display message but carry on and remove entry from our
362+
* SW tables, because there's not much we can do about it.
363+
*/
364+
netif_err(efx, drv, efx->net_dev,
365+
"Failed to release encap match %#x, rc %d\n",
366+
encap->fw_id, rc);
367+
}
368+
rhashtable_remove_fast(&efx->tc->encap_match_ht, &encap->linkage,
369+
efx_tc_encap_match_ht_params);
370+
if (encap->pseudo)
371+
efx_tc_flower_release_encap_match(efx, encap->pseudo);
372+
kfree(encap);
373+
}
374+
366375
static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
367376
struct efx_tc_match *match,
368377
enum efx_encap_type type,
378+
enum efx_tc_em_pseudo_type em_type,
379+
u8 child_ip_tos_mask,
380+
__be16 child_udp_sport_mask,
369381
struct netlink_ext_ack *extack)
370382
{
371-
struct efx_tc_encap_match *encap, *old;
383+
struct efx_tc_encap_match *encap, *old, *pseudo = NULL;
372384
bool ipv6 = false;
373385
int rc;
374386

375387
/* We require that the socket-defining fields (IP addrs and UDP dest
376-
* port) are present and exact-match. Other fields are currently not
377-
* allowed. This meets what OVS will ask for, and means that we don't
378-
* need to handle difficult checks for overlapping matches as could
379-
* come up if we allowed masks or varying sets of match fields.
388+
* port) are present and exact-match. Other fields may only be used
389+
* if the field-set (and any masks) are the same for all encap
390+
* matches on the same <sip,dip,dport> tuple; this is enforced by
391+
* pseudo encap matches.
380392
*/
381393
if (match->mask.enc_dst_ip | match->mask.enc_src_ip) {
382394
if (!IS_ALL_ONES(match->mask.enc_dst_ip)) {
@@ -414,29 +426,42 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
414426
NL_SET_ERR_MSG_MOD(extack, "Egress encap match is not exact on dst UDP port");
415427
return -EOPNOTSUPP;
416428
}
417-
if (match->mask.enc_sport) {
418-
NL_SET_ERR_MSG_MOD(extack, "Egress encap match on src UDP port not supported");
419-
return -EOPNOTSUPP;
420-
}
421-
if (match->mask.enc_ip_tos) {
422-
NL_SET_ERR_MSG_MOD(extack, "Egress encap match on IP ToS not supported");
423-
return -EOPNOTSUPP;
429+
if (match->mask.enc_sport || match->mask.enc_ip_tos) {
430+
struct efx_tc_match pmatch = *match;
431+
432+
if (em_type == EFX_TC_EM_PSEUDO_MASK) { /* can't happen */
433+
NL_SET_ERR_MSG_MOD(extack, "Bad recursion in egress encap match handler");
434+
return -EOPNOTSUPP;
435+
}
436+
pmatch.value.enc_ip_tos = 0;
437+
pmatch.mask.enc_ip_tos = 0;
438+
pmatch.value.enc_sport = 0;
439+
pmatch.mask.enc_sport = 0;
440+
rc = efx_tc_flower_record_encap_match(efx, &pmatch, type,
441+
EFX_TC_EM_PSEUDO_MASK,
442+
match->mask.enc_ip_tos,
443+
match->mask.enc_sport,
444+
extack);
445+
if (rc)
446+
return rc;
447+
pseudo = pmatch.encap;
424448
}
425449
if (match->mask.enc_ip_ttl) {
426450
NL_SET_ERR_MSG_MOD(extack, "Egress encap match on IP TTL not supported");
427-
return -EOPNOTSUPP;
451+
rc = -EOPNOTSUPP;
452+
goto fail_pseudo;
428453
}
429454

430-
rc = efx_mae_check_encap_match_caps(efx, ipv6, extack);
431-
if (rc) {
432-
NL_SET_ERR_MSG_FMT_MOD(extack, "MAE hw reports no support for IPv%d encap matches",
433-
ipv6 ? 6 : 4);
434-
return -EOPNOTSUPP;
435-
}
455+
rc = efx_mae_check_encap_match_caps(efx, ipv6, match->mask.enc_ip_tos,
456+
match->mask.enc_sport, extack);
457+
if (rc)
458+
goto fail_pseudo;
436459

437460
encap = kzalloc(sizeof(*encap), GFP_USER);
438-
if (!encap)
439-
return -ENOMEM;
461+
if (!encap) {
462+
rc = -ENOMEM;
463+
goto fail_pseudo;
464+
}
440465
encap->src_ip = match->value.enc_src_ip;
441466
encap->dst_ip = match->value.enc_dst_ip;
442467
#ifdef CONFIG_IPV6
@@ -445,12 +470,66 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
445470
#endif
446471
encap->udp_dport = match->value.enc_dport;
447472
encap->tun_type = type;
473+
encap->ip_tos = match->value.enc_ip_tos;
474+
encap->ip_tos_mask = match->mask.enc_ip_tos;
475+
encap->child_ip_tos_mask = child_ip_tos_mask;
476+
encap->udp_sport = match->value.enc_sport;
477+
encap->udp_sport_mask = match->mask.enc_sport;
478+
encap->child_udp_sport_mask = child_udp_sport_mask;
479+
encap->type = em_type;
480+
encap->pseudo = pseudo;
448481
old = rhashtable_lookup_get_insert_fast(&efx->tc->encap_match_ht,
449482
&encap->linkage,
450483
efx_tc_encap_match_ht_params);
451484
if (old) {
452485
/* don't need our new entry */
453486
kfree(encap);
487+
if (pseudo) /* don't need our new pseudo either */
488+
efx_tc_flower_release_encap_match(efx, pseudo);
489+
/* check old and new em_types are compatible */
490+
switch (old->type) {
491+
case EFX_TC_EM_DIRECT:
492+
/* old EM is in hardware, so mustn't overlap with a
493+
* pseudo, but may be shared with another direct EM
494+
*/
495+
if (em_type == EFX_TC_EM_DIRECT)
496+
break;
497+
NL_SET_ERR_MSG_MOD(extack, "Pseudo encap match conflicts with existing direct entry");
498+
return -EEXIST;
499+
case EFX_TC_EM_PSEUDO_MASK:
500+
/* old EM is protecting a ToS- or src port-qualified
501+
* filter, so may only be shared with another pseudo
502+
* for the same ToS and src port masks.
503+
*/
504+
if (em_type != EFX_TC_EM_PSEUDO_MASK) {
505+
NL_SET_ERR_MSG_FMT_MOD(extack,
506+
"%s encap match conflicts with existing pseudo(MASK) entry",
507+
encap->type ? "Pseudo" : "Direct");
508+
return -EEXIST;
509+
}
510+
if (child_ip_tos_mask != old->child_ip_tos_mask) {
511+
NL_SET_ERR_MSG_FMT_MOD(extack,
512+
"Pseudo encap match for TOS mask %#04x conflicts with existing pseudo(MASK) entry for TOS mask %#04x",
513+
child_ip_tos_mask,
514+
old->child_ip_tos_mask);
515+
return -EEXIST;
516+
}
517+
if (child_udp_sport_mask != old->child_udp_sport_mask) {
518+
NL_SET_ERR_MSG_FMT_MOD(extack,
519+
"Pseudo encap match for UDP src port mask %#x conflicts with existing pseudo(MASK) entry for mask %#x",
520+
child_udp_sport_mask,
521+
old->child_udp_sport_mask);
522+
return -EEXIST;
523+
}
524+
break;
525+
default: /* Unrecognised pseudo-type. Just say no */
526+
NL_SET_ERR_MSG_FMT_MOD(extack,
527+
"%s encap match conflicts with existing pseudo(%d) entry",
528+
encap->type ? "Pseudo" : "Direct",
529+
old->type);
530+
return -EEXIST;
531+
}
532+
/* check old and new tun_types are compatible */
454533
if (old->tun_type != type) {
455534
NL_SET_ERR_MSG_FMT_MOD(extack,
456535
"Egress encap match with conflicting tun_type %u != %u",
@@ -462,10 +541,12 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
462541
/* existing entry found */
463542
encap = old;
464543
} else {
465-
rc = efx_mae_register_encap_match(efx, encap);
466-
if (rc) {
467-
NL_SET_ERR_MSG_MOD(extack, "Failed to record egress encap match in HW");
468-
goto fail;
544+
if (em_type == EFX_TC_EM_DIRECT) {
545+
rc = efx_mae_register_encap_match(efx, encap);
546+
if (rc) {
547+
NL_SET_ERR_MSG_MOD(extack, "Failed to record egress encap match in HW");
548+
goto fail;
549+
}
469550
}
470551
refcount_set(&encap->ref, 1);
471552
}
@@ -475,30 +556,12 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
475556
rhashtable_remove_fast(&efx->tc->encap_match_ht, &encap->linkage,
476557
efx_tc_encap_match_ht_params);
477558
kfree(encap);
559+
fail_pseudo:
560+
if (pseudo)
561+
efx_tc_flower_release_encap_match(efx, pseudo);
478562
return rc;
479563
}
480564

481-
static void efx_tc_flower_release_encap_match(struct efx_nic *efx,
482-
struct efx_tc_encap_match *encap)
483-
{
484-
int rc;
485-
486-
if (!refcount_dec_and_test(&encap->ref))
487-
return; /* still in use */
488-
489-
rc = efx_mae_unregister_encap_match(efx, encap);
490-
if (rc)
491-
/* Display message but carry on and remove entry from our
492-
* SW tables, because there's not much we can do about it.
493-
*/
494-
netif_err(efx, drv, efx->net_dev,
495-
"Failed to release encap match %#x, rc %d\n",
496-
encap->fw_id, rc);
497-
rhashtable_remove_fast(&efx->tc->encap_match_ht, &encap->linkage,
498-
efx_tc_encap_match_ht_params);
499-
kfree(encap);
500-
}
501-
502565
static void efx_tc_delete_rule(struct efx_nic *efx, struct efx_tc_flow_rule *rule)
503566
{
504567
efx_mae_delete_rule(efx, rule->fw_id);
@@ -652,6 +715,7 @@ static int efx_tc_flower_replace_foreign(struct efx_nic *efx,
652715
}
653716

654717
rc = efx_tc_flower_record_encap_match(efx, &match, type,
718+
EFX_TC_EM_DIRECT, 0, 0,
655719
extack);
656720
if (rc)
657721
goto release;
@@ -1454,6 +1518,21 @@ static void efx_tc_encap_match_free(void *ptr, void *__unused)
14541518
kfree(encap);
14551519
}
14561520

1521+
static void efx_tc_flow_free(void *ptr, void *arg)
1522+
{
1523+
struct efx_tc_flow_rule *rule = ptr;
1524+
struct efx_nic *efx = arg;
1525+
1526+
netif_err(efx, drv, efx->net_dev,
1527+
"tc rule %lx still present at teardown, removing\n",
1528+
rule->cookie);
1529+
1530+
/* Also releases entries in subsidiary tables */
1531+
efx_tc_delete_rule(efx, rule);
1532+
1533+
kfree(rule);
1534+
}
1535+
14571536
int efx_init_struct_tc(struct efx_nic *efx)
14581537
{
14591538
int rc;

drivers/net/ethernet/sfc/tc.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,41 @@ static inline bool efx_tc_match_is_encap(const struct efx_tc_match_fields *mask)
7474
mask->enc_ip_ttl || mask->enc_sport || mask->enc_dport;
7575
}
7676

77+
/**
78+
* enum efx_tc_em_pseudo_type - &struct efx_tc_encap_match pseudo type
79+
*
80+
* These are used to classify "pseudo" encap matches, which don't refer
81+
* to an entry in hardware but rather indicate that a section of the
82+
* match space is in use by another Outer Rule.
83+
*
84+
* @EFX_TC_EM_DIRECT: real HW entry in Outer Rule table; not a pseudo.
85+
* Hardware index in &struct efx_tc_encap_match.fw_id is valid.
86+
* @EFX_TC_EM_PSEUDO_MASK: registered by an encap match which includes a
87+
* match on an optional field (currently ip_tos and/or udp_sport),
88+
* to prevent an overlapping encap match _without_ optional fields.
89+
* The pseudo encap match may be referenced again by an encap match
90+
* with different values for these fields, but all masks must match the
91+
* first (stored in our child_* fields).
92+
*/
93+
enum efx_tc_em_pseudo_type {
94+
EFX_TC_EM_DIRECT,
95+
EFX_TC_EM_PSEUDO_MASK,
96+
};
97+
7798
struct efx_tc_encap_match {
7899
__be32 src_ip, dst_ip;
79100
struct in6_addr src_ip6, dst_ip6;
80101
__be16 udp_dport;
102+
__be16 udp_sport, udp_sport_mask;
103+
u8 ip_tos, ip_tos_mask;
81104
struct rhash_head linkage;
82105
enum efx_encap_type tun_type;
106+
u8 child_ip_tos_mask;
107+
__be16 child_udp_sport_mask;
83108
refcount_t ref;
109+
enum efx_tc_em_pseudo_type type;
84110
u32 fw_id; /* index of this entry in firmware encap match table */
111+
struct efx_tc_encap_match *pseudo; /* Referenced pseudo EM if needed */
85112
};
86113

87114
struct efx_tc_match {

0 commit comments

Comments
 (0)