[PATCH net-next V3 09/15] net/mlx5e: psp: Add an rx_decap steering table
From: Tariq Toukan
Date: Thu Sep 03 2026 - 05:04:31 EST
From: Cosmin Ratiu <cratiu@xxxxxxxxxx>
Introduce an additional steering table for PSP transport mode
decapsulation, containing:
- one rule per supported PSP version which does:
- transport mode decap (removes UDP+PSP headers and PSP trailer)
- recomputes iph->tot_len
- recomputes IP checksum
- reparses packet headers
- copy SPI into reg_b (which ends up as cqe.ft_metadata)
- set a decap marker and the PSP version in the flow_tag, so the RX
handler can make sense of the packet
- default drop rule for unsupported PSP versions (per PSP spec).
Packets are forwarded to the previously added rx table, where:
- one rule forwards UDP traffic to the UDP default destination.
- default rule forwards traffic to the TTC table.
The reason is to avoid steering loops. If packets were to be injected
into the TTC directly after rx_decap, it may be possible to create a
steering loop with RX packets of the form IP|UDP|PSP|UDP|PSP...
The rx flow table guarantees that packets go through PSP steering at
most once.
The steering mode is saved in a new field 'fs.decap_enabled'.
Updating the mode is done through accel_psp_fs_rx_reconfigure(), which
creates the decap steering table if needed and possible. It then uses an
atomic rule update to redirect traffic to the new table.
This is now invoked with decap_wanted == false.
The intention is for failures creating the new table to not block
feature reconfig/PSP toggling. Decap table creation and traffic
redirection failures therefore result in a warning that PSP HW GRO will
not work.
Nothing happens on older HW/FW without the ability to decapsulate PSP
transport.
An upcoming patch will add dynamic reconfiguration of PSP steering based
on HW GRO.
Signed-off-by: Cosmin Ratiu <cratiu@xxxxxxxxxx>
Reviewed-by: Dragos Tatulea <dtatulea@xxxxxxxxxx>
Signed-off-by: Tariq Toukan <tariqt@xxxxxxxxxx>
---
.../net/ethernet/mellanox/mlx5/core/en/fs.h | 1 +
.../mellanox/mlx5/core/en_accel/psp.c | 249 +++++++++++++++++-
2 files changed, 248 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h
index 4973fb473ff0..a802f80d90be 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h
@@ -98,6 +98,7 @@ enum {
#if defined(CONFIG_MLX5_EN_PSP)
MLX5E_ACCEL_FS_PSP_FT_LEVEL = MLX5E_INNER_TTC_FT_LEVEL + 1,
MLX5E_ACCEL_FS_PSP_ERR_FT_LEVEL,
+ MLX5E_ACCEL_FS_PSP_DECAP_FT_LEVEL,
MLX5E_ACCEL_FS_PSP_RX_FT_LEVEL,
#endif
};
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c
index 20d7f14fb4ac..84133cbf8689 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c
@@ -23,6 +23,13 @@ enum accel_psp_syndrome {
PSP_BAD_TRAILER,
};
+static const u8 psp_supported_versions[] = {
+ PSP_VERSION_HDR0_AES_GCM_128,
+ PSP_VERSION_HDR0_AES_GCM_256,
+};
+
+#define MLX5E_PSP_NUM_SUPPORTED_VERSIONS ARRAY_SIZE(psp_supported_versions)
+
struct mlx5e_psp_tx_table {
struct mlx5_flow_namespace *ns;
struct mlx5_flow_table *ft;
@@ -43,6 +50,7 @@ struct mlx5e_psp_rx_decrypt_table {
struct mlx5_flow_table *ft;
struct mlx5_flow_group *miss_group;
struct mlx5_flow_handle *miss_rule;
+ struct mlx5_modify_hdr *modify_hdr;
struct mlx5_flow_handle *rule;
};
@@ -53,6 +61,15 @@ struct mlx5e_psp_rx_table {
struct mlx5_flow_handle *udp_rules[ACCEL_FS_PSP_NUM_TYPES];
};
+struct mlx5e_psp_rx_decap_table {
+ struct mlx5_flow_table *ft;
+ struct mlx5_flow_group *drop_group;
+ struct mlx5_modify_hdr *modify_hdr;
+ struct mlx5_pkt_reformat *reformat;
+ struct mlx5_flow_handle *rule[MLX5E_PSP_NUM_SUPPORTED_VERSIONS];
+ struct mlx5_flow_handle *unsupported_rule;
+};
+
struct mlx5e_psp_fs {
struct mlx5_core_dev *mdev;
struct mlx5_fc *tx_counter;
@@ -64,9 +81,14 @@ struct mlx5e_psp_fs {
struct mlx5_fc *rx_auth_fail_counter;
struct mlx5_fc *rx_err_counter;
struct mlx5_fc *rx_bad_counter;
+ /* When set, steering is configured to decapsulate PSP (remove UDP+PSP
+ * headers and PSP trailer) and hand off the SPI in cqe.ft_metadata.
+ */
+ bool decap_enabled;
struct mlx5e_psp_rx_decrypt_table decrypt[ACCEL_FS_PSP_NUM_TYPES];
struct mlx5e_psp_rx_check_table check;
+ struct mlx5e_psp_rx_decap_table decap;
struct mlx5e_psp_rx_table rx;
};
@@ -111,6 +133,15 @@ static void accel_psp_fs_del_flow_rule(struct mlx5_flow_handle **rule)
}
}
+static void accel_psp_fs_dealloc_modify_hdr(struct mlx5_core_dev *dev,
+ struct mlx5_modify_hdr **modhdr)
+{
+ if (*modhdr) {
+ mlx5_modify_header_dealloc(dev, *modhdr);
+ *modhdr = NULL;
+ }
+}
+
static int accel_psp_fs_create_miss_group(struct mlx5_flow_table *ft,
struct mlx5_flow_group **group)
{
@@ -403,11 +434,164 @@ int accel_psp_fs_rx_check_ft_create(struct mlx5e_psp_fs *fs,
return err;
}
+static
+void accel_psp_fs_rx_decap_ft_destroy(struct mlx5e_psp_fs *fs,
+ struct mlx5e_psp_rx_decap_table *decap)
+{
+ int i;
+
+ accel_psp_fs_del_flow_rule(&decap->unsupported_rule);
+ for (i = 0; i < MLX5E_PSP_NUM_SUPPORTED_VERSIONS; i++)
+ accel_psp_fs_del_flow_rule(&decap->rule[i]);
+ if (decap->reformat) {
+ mlx5_packet_reformat_dealloc(fs->mdev, decap->reformat);
+ decap->reformat = NULL;
+ }
+ accel_psp_fs_dealloc_modify_hdr(fs->mdev, &decap->modify_hdr);
+ accel_psp_fs_destroy_flow_group(&decap->drop_group);
+ accel_psp_fs_destroy_ft(&decap->ft);
+ fs->decap_enabled = false;
+}
+
+static void setup_fte_psp_version(struct mlx5_flow_spec *spec, u8 version)
+{
+ void *misc_params_6;
+
+ memset(spec, 0, sizeof(*spec));
+ spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_6;
+ misc_params_6 = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
+ misc_parameters_6);
+ MLX5_SET_TO_ONES(fte_match_set_misc6, misc_params_6, psp_version);
+ misc_params_6 = MLX5_ADDR_OF(fte_match_param, spec->match_value,
+ misc_parameters_6);
+ MLX5_SET(fte_match_set_misc6, misc_params_6, psp_version, version);
+}
+
+static
+int accel_psp_fs_rx_decap_ft_create(struct mlx5e_psp_fs *fs,
+ struct mlx5e_psp_rx_decap_table *decap)
+{
+ u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {};
+ struct mlx5_pkt_reformat_params reformat_params = {};
+ struct mlx5_flow_table_attr ft_attr = {};
+ struct mlx5_flow_destination dest = {};
+ struct mlx5_core_dev *mdev = fs->mdev;
+ struct mlx5_pkt_reformat *reformat;
+ struct mlx5_modify_hdr *modify_hdr;
+ struct mlx5_flow_handle *rule;
+ struct mlx5_flow_spec *spec;
+ int i, err = 0;
+
+ spec = kvzalloc_obj(*spec);
+ if (!spec)
+ return -ENOMEM;
+
+ /* Create FT */
+ ft_attr.max_fte = 1 + MLX5E_PSP_NUM_SUPPORTED_VERSIONS;
+ ft_attr.level = MLX5E_ACCEL_FS_PSP_DECAP_FT_LEVEL;
+ ft_attr.prio = MLX5E_NIC_PRIO;
+ ft_attr.autogroup.num_reserved_entries = 1;
+ err = accel_psp_fs_create_ft(fs, &ft_attr, &decap->ft);
+ if (err) {
+ mlx5_core_err(mdev, "fail to create psp decap rx ft err=%d\n",
+ err);
+ goto out_spec;
+ }
+
+ /* Create drop group */
+ err = accel_psp_fs_create_miss_group(decap->ft, &decap->drop_group);
+ if (err) {
+ mlx5_core_err(mdev,
+ "fail to create psp decap rx drop_group err=%d\n",
+ err);
+ goto out_err;
+ }
+
+ /* Add default drop rule */
+ err = accel_psp_add_drop_rule(decap->ft, NULL, fs->rx_bad_counter,
+ &decap->unsupported_rule);
+ if (err) {
+ mlx5_core_err(mdev,
+ "fail to create psp decap unsupported versions drop rule err=%d\n",
+ err);
+ goto out_err;
+ }
+
+ /* modify_hdr: copy SPI from REG_C_0 to REG_B */
+ MLX5_SET(copy_action_in, action, action_type, MLX5_ACTION_TYPE_COPY);
+ MLX5_SET(copy_action_in, action, src_field,
+ MLX5_ACTION_IN_FIELD_METADATA_REG_C_0);
+ MLX5_SET(copy_action_in, action, src_offset, 0);
+ MLX5_SET(copy_action_in, action, length, 0); /* 0 = 32 bits */
+ MLX5_SET(copy_action_in, action, dst_field,
+ MLX5_ACTION_IN_FIELD_METADATA_REG_B);
+ MLX5_SET(copy_action_in, action, dst_offset, 0);
+
+ modify_hdr = mlx5_modify_header_alloc(mdev, MLX5_FLOW_NAMESPACE_KERNEL,
+ 1, action);
+ if (IS_ERR(modify_hdr)) {
+ err = PTR_ERR(modify_hdr);
+ goto out_err;
+ }
+ decap->modify_hdr = modify_hdr;
+
+ /* pkt_reformat: decap PSP transport */
+ reformat_params.type = MLX5_REFORMAT_TYPE_REMOVE_PSP_TRANSPORT;
+ reformat = mlx5_packet_reformat_alloc(mdev, &reformat_params,
+ MLX5_FLOW_NAMESPACE_KERNEL);
+ if (IS_ERR(reformat)) {
+ err = PTR_ERR(reformat);
+ goto out_err;
+ }
+ decap->reformat = reformat;
+
+ for (i = 0; i < MLX5E_PSP_NUM_SUPPORTED_VERSIONS; i++) {
+ u8 version = psp_supported_versions[i];
+ struct mlx5_flow_act flow_act = {};
+
+ /* match(version) => decap, copy SPI, fwd to rx FT */
+ setup_fte_psp_version(spec, version);
+
+ /*
+ * Override the flow tag set in the decrypt table with
+ * the decap PSP marker and version.
+ */
+ spec->flow_context.flags = FLOW_CONTEXT_HAS_TAG;
+ spec->flow_context.flow_tag =
+ FIELD_PREP(MLX5E_ACCEL_FLOW_TAG_PROTO_MASK,
+ MLX5E_ACCEL_FLOW_TAG_PROTO_PSP_DECAP) |
+ FIELD_PREP(MLX5E_ACCEL_FLOW_TAG_PSP_VER_MASK, version);
+
+ flow_act.action = MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT |
+ MLX5_FLOW_CONTEXT_ACTION_MOD_HDR |
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+ flow_act.pkt_reformat = reformat;
+ flow_act.modify_hdr = modify_hdr;
+ dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
+ dest.ft = fs->rx.ft;
+
+ rule = mlx5_add_flow_rules(decap->ft, spec, &flow_act, &dest, 1);
+ if (IS_ERR(rule)) {
+ err = PTR_ERR(rule);
+ goto out_err;
+ }
+ decap->rule[i] = rule;
+ }
+ goto out_spec;
+
+out_err:
+ accel_psp_fs_rx_decap_ft_destroy(fs, decap);
+out_spec:
+ kvfree(spec);
+ return err;
+}
+
static void
accel_psp_fs_rx_decrypt_ft_destroy(struct mlx5e_psp_fs *fs,
struct mlx5e_psp_rx_decrypt_table *decrypt)
{
accel_psp_fs_del_flow_rule(&decrypt->rule);
+ accel_psp_fs_dealloc_modify_hdr(fs->mdev, &decrypt->modify_hdr);
accel_psp_fs_del_flow_rule(&decrypt->miss_rule);
accel_psp_fs_destroy_flow_group(&decrypt->miss_group);
accel_psp_fs_destroy_ft(&decrypt->ft);
@@ -427,10 +611,12 @@ accel_psp_fs_rx_decrypt_ft_create(struct mlx5e_psp_fs *fs,
struct mlx5e_psp_rx_decrypt_table *decrypt,
struct mlx5_flow_destination *default_dest)
{
+ u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {};
struct mlx5_flow_table_attr ft_attr = {};
struct mlx5_flow_destination dest = {};
struct mlx5_core_dev *mdev = fs->mdev;
MLX5_DECLARE_FLOW_ACT(flow_act);
+ struct mlx5_modify_hdr *modhdr;
struct mlx5_flow_handle *rule;
struct mlx5_flow_spec *spec;
int err = 0;
@@ -474,6 +660,24 @@ accel_psp_fs_rx_decrypt_ft_create(struct mlx5e_psp_fs *fs,
}
decrypt->miss_rule = rule;
+ /* Create modify_hdr to copy SPI to REG_C_0 */
+ MLX5_SET(copy_action_in, action, action_type, MLX5_ACTION_TYPE_COPY);
+ MLX5_SET(copy_action_in, action, src_field,
+ MLX5_ACTION_IN_FIELD_PSP_HEADER_1);
+ MLX5_SET(copy_action_in, action, src_offset, 0);
+ MLX5_SET(copy_action_in, action, length, 0); /* 0 = 32 bits */
+ MLX5_SET(copy_action_in, action, dst_field,
+ MLX5_ACTION_IN_FIELD_METADATA_REG_C_0);
+ MLX5_SET(copy_action_in, action, dst_offset, 0);
+
+ modhdr = mlx5_modify_header_alloc(mdev, MLX5_FLOW_NAMESPACE_KERNEL, 1,
+ action);
+ if (IS_ERR(modhdr)) {
+ err = PTR_ERR(modhdr);
+ goto out_err;
+ }
+ decrypt->modify_hdr = modhdr;
+
/* Add PSP RX decrypt rule */
setup_fte_udp_psp(spec, PSP_DEFAULT_UDP_PORT);
@@ -484,8 +688,10 @@ accel_psp_fs_rx_decrypt_ft_create(struct mlx5e_psp_fs *fs,
MLX5E_ACCEL_FLOW_TAG_PROTO_PSP);
flow_act.crypto.type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_PSP;
- flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
- MLX5_FLOW_CONTEXT_ACTION_CRYPTO_DECRYPT;
+ flow_act.action = MLX5_FLOW_CONTEXT_ACTION_CRYPTO_DECRYPT |
+ MLX5_FLOW_CONTEXT_ACTION_MOD_HDR |
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+ flow_act.modify_hdr = modhdr;
dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
dest.ft = fs->check.ft;
rule = mlx5_add_flow_rules(decrypt->ft, spec, &flow_act, &dest, 1);
@@ -506,6 +712,42 @@ accel_psp_fs_rx_decrypt_ft_create(struct mlx5e_psp_fs *fs,
return err;
}
+static void accel_psp_fs_rx_reconfigure(struct mlx5e_psp_fs *fs,
+ bool decap_wanted)
+{
+ bool decap_supported =
+ MLX5_CAP_FLOWTABLE(fs->mdev,
+ flow_table_properties_nic_receive.reformat_del_psp_transport);
+ bool decap_enable = decap_wanted && decap_supported;
+ struct mlx5_flow_destination dest = {};
+ int err;
+
+ if (decap_enable == fs->decap_enabled)
+ return;
+
+ /* Create the decap table if needed. */
+ if (decap_enable && !fs->decap.ft) {
+ err = accel_psp_fs_rx_decap_ft_create(fs, &fs->decap);
+ if (err)
+ goto out_err;
+ }
+
+ /* Redirect traffic to the correct table. */
+ dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
+ dest.ft = decap_enable ? fs->decap.ft : fs->rx.ft;
+ err = mlx5_modify_rule_destination(fs->check.rule, &dest, NULL);
+ if (err)
+ goto out_err;
+
+ fs->decap_enabled = decap_enable;
+ return;
+
+out_err:
+ mlx5_core_warn(fs->mdev,
+ "Failed to create/modify PSP decapsulation rules (err %d), HW GRO for PSP unavailable\n",
+ err);
+}
+
static void accel_psp_fs_rx_destroy(struct mlx5e_psp_fs *fs)
{
struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(fs->fs, false);
@@ -518,6 +760,7 @@ static void accel_psp_fs_rx_destroy(struct mlx5e_psp_fs *fs)
accel_psp_fs_rx_decrypt_ft_destroy(fs, &fs->decrypt[i]);
}
accel_psp_fs_rx_check_ft_destroy(&fs->check);
+ accel_psp_fs_rx_decap_ft_destroy(fs, &fs->decap);
accel_psp_fs_rx_ft_destroy(&fs->rx);
if (tc_blocked)
mlx5e_accel_unblock_tc_offload(fs->mdev);
@@ -565,6 +808,8 @@ static int accel_psp_fs_rx_create(struct mlx5e_psp_fs *fs,
mlx5_ttc_fwd_dest(ttc, fs_psp2tt(i), &dest);
}
+ accel_psp_fs_rx_reconfigure(fs, false);
+
return 0;
err_decrypt_ft:
--
2.44.0