@@ -27,6 +27,7 @@
#include "sfc_debug.h"
#include "sfc_log.h"
#include "sfc_filter.h"
+#include "sfc_flow_tunnel.h"
#include "sfc_sriov.h"
#include "sfc_mae.h"
#include "sfc_dp.h"
@@ -258,6 +259,8 @@ struct sfc_adapter {
struct sfc_intr intr;
struct sfc_port port;
struct sfc_sw_xstats sw_xstats;
+ /* Registry of tunnel offload contexts */
+ struct sfc_flow_tunnel flow_tunnels[SFC_FT_MAX_NTUNNELS];
struct sfc_filter filter;
struct sfc_mae mae;
@@ -2547,15 +2547,46 @@ sfc_flow_parse_rte_to_mae(struct rte_eth_dev *dev,
struct sfc_flow_spec_mae *spec_mae = &spec->mae;
int rc;
+ /*
+ * If the flow is meant to be a JUMP rule in tunnel offload,
+ * preparse its actions and save its properties in spec_mae.
+ */
+ rc = sfc_flow_tunnel_detect_jump_rule(sa, actions, spec_mae, error);
+ if (rc != 0)
+ goto fail;
+
rc = sfc_mae_rule_parse_pattern(sa, pattern, spec_mae, error);
if (rc != 0)
- return rc;
+ goto fail;
+
+ if (spec_mae->ft_rule_type == SFC_FT_RULE_JUMP) {
+ /*
+ * This flow is represented solely by the outer rule.
+ * It is supposed to mark and count matching packets.
+ */
+ goto skip_action_rule;
+ }
rc = sfc_mae_rule_parse_actions(sa, actions, spec_mae, error);
if (rc != 0)
- return rc;
+ goto fail;
+
+skip_action_rule:
+ if (spec_mae->ft != NULL) {
+ if (spec_mae->ft_rule_type == SFC_FT_RULE_JUMP)
+ spec_mae->ft->jump_rule_is_set = B_TRUE;
+
+ ++(spec_mae->ft->refcnt);
+ }
return 0;
+
+fail:
+ /* Reset these values to avoid confusing sfc_mae_flow_cleanup(). */
+ spec_mae->ft_rule_type = SFC_FT_RULE_NONE;
+ spec_mae->ft = NULL;
+
+ return rc;
}
static int
@@ -63,8 +63,20 @@ struct sfc_flow_spec_filter {
struct sfc_flow_rss rss_conf;
};
+/* Indicates the role of a given flow in tunnel offload */
+enum sfc_flow_tunnel_rule_type {
+ /* The flow has nothing to do with tunnel offload */
+ SFC_FT_RULE_NONE = 0,
+ /* The flow represents a JUMP rule */
+ SFC_FT_RULE_JUMP,
+};
+
/* MAE-specific flow specification */
struct sfc_flow_spec_mae {
+ /* FLow Tunnel (FT) rule type (or NONE) */
+ enum sfc_flow_tunnel_rule_type ft_rule_type;
+ /* Flow Tunnel (FT) context (or NULL) */
+ struct sfc_flow_tunnel *ft;
/* Desired priority level */
unsigned int priority;
/* Outer rule registry entry */
@@ -7,6 +7,7 @@
#include <stdint.h>
#include "sfc.h"
+#include "sfc_flow.h"
#include "sfc_dp_rx.h"
#include "sfc_flow_tunnel.h"
#include "sfc_mae.h"
@@ -27,3 +28,118 @@ sfc_flow_tunnel_is_active(struct sfc_adapter *sa)
return ((sa->negotiated_rx_meta & RTE_ETH_RX_META_TUNNEL_ID) != 0);
}
+
+struct sfc_flow_tunnel *
+sfc_flow_tunnel_pick(struct sfc_adapter *sa, uint32_t ft_mark)
+{
+ uint32_t tunnel_mark = SFC_FT_GET_TUNNEL_MARK(ft_mark);
+
+ SFC_ASSERT(sfc_adapter_is_locked(sa));
+
+ if (tunnel_mark != SFC_FT_TUNNEL_MARK_INVALID) {
+ sfc_ft_id_t ft_id = SFC_FT_TUNNEL_MARK_TO_ID(tunnel_mark);
+ struct sfc_flow_tunnel *ft = &sa->flow_tunnels[ft_id];
+
+ ft->id = ft_id;
+
+ return ft;
+ }
+
+ return NULL;
+}
+
+int
+sfc_flow_tunnel_detect_jump_rule(struct sfc_adapter *sa,
+ const struct rte_flow_action *actions,
+ struct sfc_flow_spec_mae *spec,
+ struct rte_flow_error *error)
+{
+ const struct rte_flow_action_mark *action_mark = NULL;
+ const struct rte_flow_action_jump *action_jump = NULL;
+ struct sfc_flow_tunnel *ft;
+ uint32_t ft_mark = 0;
+ int rc = 0;
+
+ SFC_ASSERT(sfc_adapter_is_locked(sa));
+
+ if (!sfc_flow_tunnel_is_active(sa)) {
+ /* Tunnel-related actions (if any) will be turned down later. */
+ return 0;
+ }
+
+ if (actions == NULL) {
+ rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ACTION_NUM, NULL,
+ "NULL actions");
+ return -rte_errno;
+ }
+
+ for (; actions->type != RTE_FLOW_ACTION_TYPE_END; ++actions) {
+ if (actions->type == RTE_FLOW_ACTION_TYPE_VOID)
+ continue;
+
+ if (actions->conf == NULL) {
+ rc = EINVAL;
+ continue;
+ }
+
+ switch (actions->type) {
+ case RTE_FLOW_ACTION_TYPE_MARK:
+ if (action_mark == NULL) {
+ action_mark = actions->conf;
+ ft_mark = action_mark->id;
+ } else {
+ rc = EINVAL;
+ }
+ break;
+ case RTE_FLOW_ACTION_TYPE_JUMP:
+ if (action_jump == NULL) {
+ action_jump = actions->conf;
+ if (action_jump->group != 0)
+ rc = EINVAL;
+ } else {
+ rc = EINVAL;
+ }
+ break;
+ default:
+ rc = ENOTSUP;
+ break;
+ }
+ }
+
+ ft = sfc_flow_tunnel_pick(sa, ft_mark);
+ if (ft != NULL && action_jump != 0) {
+ sfc_dbg(sa, "tunnel offload: JUMP: detected");
+
+ if (rc != 0) {
+ /* The loop above might have spotted wrong actions. */
+ sfc_err(sa, "tunnel offload: JUMP: invalid actions: %s",
+ strerror(rc));
+ goto fail;
+ }
+
+ if (ft->refcnt == 0) {
+ sfc_err(sa, "tunnel offload: JUMP: tunnel=%u does not exist",
+ ft->id);
+ rc = ENOENT;
+ goto fail;
+ }
+
+ if (ft->jump_rule_is_set) {
+ sfc_err(sa, "tunnel offload: JUMP: already exists in tunnel=%u",
+ ft->id);
+ rc = EEXIST;
+ goto fail;
+ }
+
+ spec->ft_rule_type = SFC_FT_RULE_JUMP;
+ spec->ft = ft;
+ }
+
+ return 0;
+
+fail:
+ return rte_flow_error_set(error, rc,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "tunnel offload: JUMP: preparsing failed");
+}
@@ -10,6 +10,8 @@
#include <stdbool.h>
#include <stdint.h>
+#include "efx.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -26,12 +28,41 @@ typedef uint8_t sfc_ft_id_t;
#define SFC_FT_USER_MARK_MASK \
RTE_LEN2MASK(SFC_FT_USER_MARK_BITS, uint32_t)
+#define SFC_FT_GET_TUNNEL_MARK(_mark) \
+ ((_mark) >> SFC_FT_USER_MARK_BITS)
+
+#define SFC_FT_TUNNEL_MARK_INVALID (0)
+
+#define SFC_FT_TUNNEL_MARK_TO_ID(_tunnel_mark) \
+ ((_tunnel_mark) - 1)
+
+#define SFC_FT_ID_TO_TUNNEL_MARK(_id) \
+ ((_id) + 1)
+
+#define SFC_FT_MAX_NTUNNELS \
+ (RTE_LEN2MASK(SFC_FT_TUNNEL_MARK_BITS, uint8_t) - 1)
+
+struct sfc_flow_tunnel {
+ bool jump_rule_is_set;
+ efx_tunnel_protocol_t encap_type;
+ unsigned int refcnt;
+ sfc_ft_id_t id;
+};
+
struct sfc_adapter;
bool sfc_flow_tunnel_is_supported(struct sfc_adapter *sa);
bool sfc_flow_tunnel_is_active(struct sfc_adapter *sa);
+struct sfc_flow_tunnel *sfc_flow_tunnel_pick(struct sfc_adapter *sa,
+ uint32_t ft_mark);
+
+int sfc_flow_tunnel_detect_jump_rule(struct sfc_adapter *sa,
+ const struct rte_flow_action *actions,
+ struct sfc_flow_spec_mae *spec,
+ struct rte_flow_error *error);
+
#ifdef __cplusplus
}
#endif
@@ -269,6 +269,9 @@ sfc_mae_outer_rule_enable(struct sfc_adapter *sa,
}
}
+ if (match_spec_action == NULL)
+ goto skip_action_rule;
+
rc = efx_mae_match_spec_outer_rule_id_set(match_spec_action,
&fw_rsrc->rule_id);
if (rc != 0) {
@@ -283,6 +286,7 @@ sfc_mae_outer_rule_enable(struct sfc_adapter *sa,
return rc;
}
+skip_action_rule:
if (fw_rsrc->refcnt == 0) {
sfc_dbg(sa, "enabled outer_rule=%p: OR_ID=0x%08x",
rule, fw_rsrc->rule_id.id);
@@ -806,6 +810,14 @@ sfc_mae_flow_cleanup(struct sfc_adapter *sa,
spec_mae = &spec->mae;
+ if (spec_mae->ft != NULL) {
+ if (spec_mae->ft_rule_type == SFC_FT_RULE_JUMP)
+ spec_mae->ft->jump_rule_is_set = B_FALSE;
+
+ SFC_ASSERT(spec_mae->ft->refcnt != 0);
+ --(spec_mae->ft->refcnt);
+ }
+
SFC_ASSERT(spec_mae->rule_id.id == EFX_MAE_RSRC_ID_INVALID);
if (spec_mae->outer_rule != NULL)
@@ -2146,6 +2158,16 @@ sfc_mae_rule_process_outer(struct sfc_adapter *sa,
ctx->match_spec_outer = NULL;
no_or_id:
+ switch (ctx->ft_rule_type) {
+ case SFC_FT_RULE_NONE:
+ break;
+ case SFC_FT_RULE_JUMP:
+ /* No action rule */
+ return 0;
+ default:
+ SFC_ASSERT(B_FALSE);
+ }
+
/*
* In MAE, lookup sequence comprises outer parse, outer rule lookup,
* inner parse (when some outer rule is hit) and action rule lookup.
@@ -2183,6 +2205,7 @@ sfc_mae_rule_encap_parse_init(struct sfc_adapter *sa,
struct rte_flow_error *error)
{
struct sfc_mae *mae = &sa->mae;
+ uint8_t recirc_id = 0;
int rc;
if (pattern == NULL) {
@@ -2222,34 +2245,71 @@ sfc_mae_rule_encap_parse_init(struct sfc_adapter *sa,
break;
}
- if (pattern->type == RTE_FLOW_ITEM_TYPE_END)
- return 0;
+ switch (ctx->ft_rule_type) {
+ case SFC_FT_RULE_NONE:
+ if (pattern->type == RTE_FLOW_ITEM_TYPE_END)
+ return 0;
+ break;
+ case SFC_FT_RULE_JUMP:
+ if (pattern->type != RTE_FLOW_ITEM_TYPE_END) {
+ return rte_flow_error_set(error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ITEM,
+ pattern, "tunnel offload: JUMP: invalid item");
+ }
+ ctx->encap_type = ctx->ft->encap_type;
+ break;
+ default:
+ SFC_ASSERT(B_FALSE);
+ break;
+ }
if ((mae->encap_types_supported & (1U << ctx->encap_type)) == 0) {
return rte_flow_error_set(error, ENOTSUP,
- RTE_FLOW_ERROR_TYPE_ITEM,
- pattern, "Unsupported tunnel item");
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "OR: unsupported tunnel type");
}
- if (ctx->priority >= mae->nb_outer_rule_prios_max) {
- return rte_flow_error_set(error, ENOTSUP,
- RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
- NULL, "Unsupported priority level");
- }
+ switch (ctx->ft_rule_type) {
+ case SFC_FT_RULE_JUMP:
+ recirc_id = SFC_FT_ID_TO_TUNNEL_MARK(ctx->ft->id);
+ /* FALLTHROUGH */
+ case SFC_FT_RULE_NONE:
+ if (ctx->priority >= mae->nb_outer_rule_prios_max) {
+ return rte_flow_error_set(error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
+ NULL, "OR: unsupported priority level");
+ }
- rc = efx_mae_match_spec_init(sa->nic, EFX_MAE_RULE_OUTER, ctx->priority,
- &ctx->match_spec_outer);
- if (rc != 0) {
- return rte_flow_error_set(error, rc,
- RTE_FLOW_ERROR_TYPE_ITEM, pattern,
- "Failed to initialise outer rule match specification");
- }
+ rc = efx_mae_match_spec_init(sa->nic,
+ EFX_MAE_RULE_OUTER, ctx->priority,
+ &ctx->match_spec_outer);
+ if (rc != 0) {
+ return rte_flow_error_set(error, rc,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "OR: failed to initialise the match specification");
+ }
+
+ /*
+ * Outermost items comprise a match
+ * specification of type OUTER.
+ */
+ ctx->match_spec = ctx->match_spec_outer;
- /* Outermost items comprise a match specification of type OUTER. */
- ctx->match_spec = ctx->match_spec_outer;
+ /* Outermost items use "ENC" EFX MAE field IDs. */
+ ctx->field_ids_remap = field_ids_remap_to_encap;
- /* Outermost items use "ENC" EFX MAE field IDs. */
- ctx->field_ids_remap = field_ids_remap_to_encap;
+ rc = efx_mae_outer_rule_recirc_id_set(ctx->match_spec,
+ recirc_id);
+ if (rc != 0) {
+ return rte_flow_error_set(error, rc,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "OR: failed to initialise RECIRC_ID");
+ }
+ break;
+ default:
+ SFC_ASSERT(B_FALSE);
+ break;
+ }
return 0;
}
@@ -2276,17 +2336,29 @@ sfc_mae_rule_parse_pattern(struct sfc_adapter *sa,
int rc;
memset(&ctx_mae, 0, sizeof(ctx_mae));
+ ctx_mae.ft_rule_type = spec->ft_rule_type;
ctx_mae.priority = spec->priority;
+ ctx_mae.ft = spec->ft;
ctx_mae.sa = sa;
- rc = efx_mae_match_spec_init(sa->nic, EFX_MAE_RULE_ACTION,
- spec->priority,
- &ctx_mae.match_spec_action);
- if (rc != 0) {
- rc = rte_flow_error_set(error, rc,
- RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
- "Failed to initialise action rule match specification");
- goto fail_init_match_spec_action;
+ switch (ctx_mae.ft_rule_type) {
+ case SFC_FT_RULE_JUMP:
+ /* No action rule */
+ break;
+ case SFC_FT_RULE_NONE:
+ rc = efx_mae_match_spec_init(sa->nic, EFX_MAE_RULE_ACTION,
+ spec->priority,
+ &ctx_mae.match_spec_action);
+ if (rc != 0) {
+ rc = rte_flow_error_set(error, rc,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "AR: failed to initialise the match specification");
+ goto fail_init_match_spec_action;
+ }
+ break;
+ default:
+ SFC_ASSERT(B_FALSE);
+ break;
}
/*
@@ -2320,7 +2392,8 @@ sfc_mae_rule_parse_pattern(struct sfc_adapter *sa,
if (rc != 0)
goto fail_process_outer;
- if (!efx_mae_match_spec_is_valid(sa->nic, ctx_mae.match_spec_action)) {
+ if (ctx_mae.match_spec_action != NULL &&
+ !efx_mae_match_spec_is_valid(sa->nic, ctx_mae.match_spec_action)) {
rc = rte_flow_error_set(error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ITEM, NULL,
"Inconsistent pattern");
@@ -2338,7 +2411,8 @@ sfc_mae_rule_parse_pattern(struct sfc_adapter *sa,
sfc_mae_rule_encap_parse_fini(sa, &ctx_mae);
fail_encap_parse_init:
- efx_mae_match_spec_fini(sa->nic, ctx_mae.match_spec_action);
+ if (ctx_mae.match_spec_action != NULL)
+ efx_mae_match_spec_fini(sa->nic, ctx_mae.match_spec_action);
fail_init_match_spec_action:
return rc;
@@ -3254,6 +3328,9 @@ sfc_mae_action_rule_class_verify(struct sfc_adapter *sa,
{
const struct rte_flow *entry;
+ if (spec->match_spec == NULL)
+ return 0;
+
TAILQ_FOREACH_REVERSE(entry, &sa->flow_list, sfc_flow_list, entries) {
const struct sfc_flow_spec *entry_spec = &entry->spec;
const struct sfc_flow_spec_mae *es_mae = &entry_spec->mae;
@@ -3325,11 +3402,10 @@ sfc_mae_flow_insert(struct sfc_adapter *sa,
struct sfc_flow_spec_mae *spec_mae = &spec->mae;
struct sfc_mae_outer_rule *outer_rule = spec_mae->outer_rule;
struct sfc_mae_action_set *action_set = spec_mae->action_set;
- struct sfc_mae_fw_rsrc *fw_rsrc = &action_set->fw_rsrc;
+ struct sfc_mae_fw_rsrc *fw_rsrc;
int rc;
SFC_ASSERT(spec_mae->rule_id.id == EFX_MAE_RSRC_ID_INVALID);
- SFC_ASSERT(action_set != NULL);
if (outer_rule != NULL) {
rc = sfc_mae_outer_rule_enable(sa, outer_rule,
@@ -3338,6 +3414,11 @@ sfc_mae_flow_insert(struct sfc_adapter *sa,
goto fail_outer_rule_enable;
}
+ if (action_set == NULL) {
+ sfc_dbg(sa, "enabled flow=%p (no AR)", flow);
+ return 0;
+ }
+
rc = sfc_mae_action_set_enable(sa, action_set);
if (rc != 0)
goto fail_action_set_enable;
@@ -3351,6 +3432,8 @@ sfc_mae_flow_insert(struct sfc_adapter *sa,
}
}
+ fw_rsrc = &action_set->fw_rsrc;
+
rc = efx_mae_action_rule_insert(sa->nic, spec_mae->match_spec,
NULL, &fw_rsrc->aset_id,
&spec_mae->rule_id);
@@ -3384,8 +3467,12 @@ sfc_mae_flow_remove(struct sfc_adapter *sa,
struct sfc_mae_outer_rule *outer_rule = spec_mae->outer_rule;
int rc;
+ if (action_set == NULL) {
+ sfc_dbg(sa, "disabled flow=%p (no AR)", flow);
+ goto skip_action_rule;
+ }
+
SFC_ASSERT(spec_mae->rule_id.id != EFX_MAE_RSRC_ID_INVALID);
- SFC_ASSERT(action_set != NULL);
rc = efx_mae_action_rule_remove(sa->nic, &spec_mae->rule_id);
if (rc != 0) {
@@ -3398,6 +3485,7 @@ sfc_mae_flow_remove(struct sfc_adapter *sa,
sfc_mae_action_set_disable(sa, action_set);
+skip_action_rule:
if (outer_rule != NULL)
sfc_mae_outer_rule_disable(sa, outer_rule);
@@ -3416,7 +3504,7 @@ sfc_mae_query_counter(struct sfc_adapter *sa,
unsigned int i;
int rc;
- if (action_set->n_counters == 0) {
+ if (action_set == NULL || action_set->n_counters == 0) {
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION, action,
"Queried flow rule does not have count actions");
@@ -285,9 +285,11 @@ struct sfc_mae_parse_ctx {
size_t tunnel_def_mask_size;
const void *tunnel_def_mask;
bool match_mport_set;
+ enum sfc_flow_tunnel_rule_type ft_rule_type;
struct sfc_mae_pattern_data pattern_data;
efx_tunnel_protocol_t encap_type;
unsigned int priority;
+ struct sfc_flow_tunnel *ft;
};
int sfc_mae_attach(struct sfc_adapter *sa);