| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/04 11:00 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"KMSANReasoning": "",
"NeedsKMSAN": false,
"Reasoning": "The patch touches only the vendor-specific Mellanox mlx5 driver (drivers/net/ethernet/mellanox/mlx5/ and include/linux/mlx5/) and selftests (tools/testing/selftests/). Mellanox ConnectX hardware is not emulated in standard QEMU/GCE virtualization environments, making the driver code structurally unreachable, and selftests do not affect the kernel image.",
"WorthFuzzing": false
} |
| 1/1 | 2026/09/04 11:00 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 9a02a46aa67dc99c7122f097a34eb60ca2f981f5\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Sep 4 11:00:09 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h\nindex 4973fb473ff02..a802f80d90bea 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h\n@@ -98,6 +98,7 @@ enum {\n #if defined(CONFIG_MLX5_EN_PSP)\n \tMLX5E_ACCEL_FS_PSP_FT_LEVEL = MLX5E_INNER_TTC_FT_LEVEL + 1,\n \tMLX5E_ACCEL_FS_PSP_ERR_FT_LEVEL,\n+\tMLX5E_ACCEL_FS_PSP_DECAP_FT_LEVEL,\n \tMLX5E_ACCEL_FS_PSP_RX_FT_LEVEL,\n #endif\n };\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h\nindex 3f212e46fc2f9..98709d48b8015 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h\n@@ -218,6 +218,28 @@ static inline void mlx5e_accel_tx_finish(struct mlx5e_txqsq *sq,\n #endif\n }\n \n+static inline int mlx5e_accel_block_tc_offload(struct mlx5_core_dev *mdev)\n+{\n+\tint ret = 0;\n+\n+\tmutex_lock(\u0026mdev-\u003eoffload_block.lock);\n+\tif (mdev-\u003eoffload_block.num_accel)\n+\t\tret = -EBUSY;\n+\telse\n+\t\tmdev-\u003eoffload_block.num_tc++;\n+\tmutex_unlock(\u0026mdev-\u003eoffload_block.lock);\n+\n+\treturn ret;\n+}\n+\n+static inline void mlx5e_accel_unblock_tc_offload(struct mlx5_core_dev *mdev)\n+{\n+\tmutex_lock(\u0026mdev-\u003eoffload_block.lock);\n+\tif (!WARN_ON_ONCE(!mdev-\u003eoffload_block.num_tc))\n+\t\tmdev-\u003eoffload_block.num_tc--;\n+\tmutex_unlock(\u0026mdev-\u003eoffload_block.lock);\n+}\n+\n static inline int mlx5e_accel_init_rx(struct mlx5e_priv *priv)\n {\n \treturn mlx5e_ktls_init_rx(priv);\n@@ -229,6 +251,11 @@ static inline void mlx5e_accel_cleanup_rx(struct mlx5e_priv *priv)\n \tmlx5_accel_psp_fs_cleanup_rx_tables(priv);\n }\n \n+static inline void mlx5e_accel_update_rx(struct mlx5e_priv *priv)\n+{\n+\tmlx5e_psp_update_rx(priv);\n+}\n+\n static inline int mlx5e_accel_init_tx(struct mlx5e_priv *priv)\n {\n \treturn mlx5e_ktls_init_tx(priv);\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/flow_tag.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/flow_tag.h\nnew file mode 100644\nindex 0000000000000..cd10a74e2b27a\n--- /dev/null\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/flow_tag.h\n@@ -0,0 +1,49 @@\n+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */\n+/* Copyright (c) 2026, NVIDIA CORPORATION \u0026 AFFILIATES. All rights reserved. */\n+\n+#ifndef __MLX5E_FLOW_TAG_H__\n+#define __MLX5E_FLOW_TAG_H__\n+\n+#include \u003clinux/bitfield.h\u003e\n+#include \u003clinux/bits.h\u003e\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/types.h\u003e\n+#include \u003clinux/mlx5/device.h\u003e\n+\n+/* Unified accel flow_tag layout in CQE sop_drop_qpn [23:0]:\n+ *\n+ * [23:21] = protocol ID (3 bits):\n+ * 0 = none (default)\n+ * 1 = IPsec\n+ * 2 = MACsec\n+ * 3 = PSP (HW decrypted, PSP header present)\n+ * 4 = PSP decap (HW decrypted \u0026 decapsulated)\n+ * 5-7 = reserved\n+ * [20:17] = PSP version (4 bits, valid when proto == PSP decap)\n+ * 16 = reserved\n+ * [15:0] = used by other subsystems (e.g. TC).\n+ */\n+#define MLX5E_ACCEL_FLOW_TAG_MASK\t\tGENMASK(23, 0)\n+#define MLX5E_ACCEL_FLOW_TAG_PROTO_MASK\t\tGENMASK(23, 21)\n+#define MLX5E_ACCEL_FLOW_TAG_PSP_VER_MASK\tGENMASK(20, 17)\n+\n+enum {\n+\tMLX5E_ACCEL_FLOW_TAG_PROTO_NONE\t\t= 0,\n+\tMLX5E_ACCEL_FLOW_TAG_PROTO_IPSEC\t= 1,\n+\tMLX5E_ACCEL_FLOW_TAG_PROTO_MACSEC\t= 2,\n+\tMLX5E_ACCEL_FLOW_TAG_PROTO_PSP\t\t= 3,\n+\tMLX5E_ACCEL_FLOW_TAG_PROTO_PSP_DECAP\t= 4,\n+};\n+\n+static inline u32 mlx5e_accel_flow_tag(struct mlx5_cqe64 *cqe)\n+{\n+\treturn be32_to_cpu(cqe-\u003esop_drop_qpn) \u0026 MLX5E_ACCEL_FLOW_TAG_MASK;\n+}\n+\n+static inline u32 mlx5e_accel_flow_tag_proto(struct mlx5_cqe64 *cqe)\n+{\n+\treturn FIELD_GET(MLX5E_ACCEL_FLOW_TAG_PROTO_MASK,\n+\t\t\t mlx5e_accel_flow_tag(cqe));\n+}\n+\n+#endif /* __MLX5E_FLOW_TAG_H__ */\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c\nindex 329608c59313b..5565c385258c3 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c\n@@ -4,6 +4,8 @@\n #include \u003clinux/netdevice.h\u003e\n #include \"en.h\"\n #include \"en/fs.h\"\n+#include \"en_accel/en_accel.h\"\n+#include \"en_accel/flow_tag.h\"\n #include \"eswitch.h\"\n #include \"ipsec.h\"\n #include \"fs_core.h\"\n@@ -189,7 +191,7 @@ static void ipsec_rx_rule_add_match_obj(struct mlx5e_ipsec_sa_entry *sa_entry,\n \t\t\t\t misc_parameters_2.metadata_reg_c_2);\n \t\tMLX5_SET(fte_match_param, spec-\u003ematch_value,\n \t\t\t misc_parameters_2.metadata_reg_c_2,\n-\t\t\t sa_entry-\u003eipsec_obj_id | BIT(31));\n+\t\t\t sa_entry-\u003eipsec_obj_id);\n \n \t\tspec-\u003ematch_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_2;\n \t}\n@@ -2065,7 +2067,7 @@ static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry)\n \tif (!attrs-\u003edrop) {\n \t\tif (rx != ipsec-\u003erx_esw)\n \t\t\terr = setup_modify_header(ipsec, attrs-\u003etype,\n-\t\t\t\t\t\t sa_entry-\u003eipsec_obj_id | BIT(31),\n+\t\t\t\t\t\t sa_entry-\u003eipsec_obj_id,\n \t\t\t\t\t\t XFRM_DEV_OFFLOAD_IN, \u0026flow_act);\n \t\telse\n \t\t\terr = mlx5_esw_ipsec_rx_setup_modify_header(sa_entry, \u0026flow_act);\n@@ -2098,6 +2100,12 @@ static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry)\n \t\tflow_act.action |= MLX5_FLOW_CONTEXT_ACTION_DROP;\n \telse\n \t\tflow_act.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;\n+\tif (!attrs-\u003edrop \u0026\u0026 rx != ipsec-\u003erx_esw) {\n+\t\tspec-\u003eflow_context.flags |= FLOW_CONTEXT_HAS_TAG;\n+\t\tspec-\u003eflow_context.flow_tag =\n+\t\t\tFIELD_PREP(MLX5E_ACCEL_FLOW_TAG_PROTO_MASK,\n+\t\t\t\t MLX5E_ACCEL_FLOW_TAG_PROTO_IPSEC);\n+\t}\n \tdest[0].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;\n \tdest[0].ft = rx-\u003eft.status;\n \tdest[1].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;\n@@ -2574,53 +2582,19 @@ void mlx5e_accel_ipsec_fs_read_stats(struct mlx5e_priv *priv, void *ipsec_stats)\n \t}\n }\n \n-#ifdef CONFIG_MLX5_ESWITCH\n-static int mlx5e_ipsec_block_tc_offload(struct mlx5_core_dev *mdev)\n-{\n-\tstruct mlx5_eswitch *esw = mdev-\u003epriv.eswitch;\n-\tint err = 0;\n-\n-\tif (esw) {\n-\t\terr = mlx5_esw_lock(esw);\n-\t\tif (err)\n-\t\t\treturn err;\n-\t}\n-\n-\tif (mdev-\u003enum_block_ipsec) {\n-\t\terr = -EBUSY;\n-\t\tgoto unlock;\n-\t}\n-\n-\tmdev-\u003enum_block_tc++;\n-\n-unlock:\n-\tif (esw)\n-\t\tmlx5_esw_unlock(esw);\n-\n-\treturn err;\n-}\n-#else\n-static int mlx5e_ipsec_block_tc_offload(struct mlx5_core_dev *mdev)\n-{\n-\tif (mdev-\u003enum_block_ipsec)\n-\t\treturn -EBUSY;\n-\n-\tmdev-\u003enum_block_tc++;\n-\treturn 0;\n-}\n-#endif\n-\n-static void mlx5e_ipsec_unblock_tc_offload(struct mlx5_core_dev *mdev)\n+static bool accel_ipsec_should_block_tc(struct mlx5e_ipsec_sa_entry *sa_entry)\n {\n-\tmdev-\u003enum_block_tc--;\n+\treturn sa_entry-\u003eattrs.type == XFRM_DEV_OFFLOAD_PACKET ||\n+\t\tsa_entry-\u003eattrs.dir == XFRM_DEV_OFFLOAD_IN;\n }\n \n int mlx5e_accel_ipsec_fs_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry)\n {\n+\tbool block_tc = accel_ipsec_should_block_tc(sa_entry);\n \tint err;\n \n-\tif (sa_entry-\u003eattrs.type == XFRM_DEV_OFFLOAD_PACKET) {\n-\t\terr = mlx5e_ipsec_block_tc_offload(sa_entry-\u003eipsec-\u003emdev);\n+\tif (block_tc) {\n+\t\terr = mlx5e_accel_block_tc_offload(sa_entry-\u003eipsec-\u003emdev);\n \t\tif (err)\n \t\t\treturn err;\n \t}\n@@ -2636,8 +2610,8 @@ int mlx5e_accel_ipsec_fs_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry)\n \treturn 0;\n \n err_out:\n-\tif (sa_entry-\u003eattrs.type == XFRM_DEV_OFFLOAD_PACKET)\n-\t\tmlx5e_ipsec_unblock_tc_offload(sa_entry-\u003eipsec-\u003emdev);\n+\tif (block_tc)\n+\t\tmlx5e_accel_unblock_tc_offload(sa_entry-\u003eipsec-\u003emdev);\n \treturn err;\n }\n \n@@ -2651,8 +2625,8 @@ void mlx5e_accel_ipsec_fs_del_rule(struct mlx5e_ipsec_sa_entry *sa_entry)\n \tif (ipsec_rule-\u003epkt_reformat)\n \t\tmlx5_packet_reformat_dealloc(mdev, ipsec_rule-\u003epkt_reformat);\n \n-\tif (sa_entry-\u003eattrs.type == XFRM_DEV_OFFLOAD_PACKET)\n-\t\tmlx5e_ipsec_unblock_tc_offload(mdev);\n+\tif (accel_ipsec_should_block_tc(sa_entry))\n+\t\tmlx5e_accel_unblock_tc_offload(mdev);\n \n \tif (sa_entry-\u003eattrs.dir == XFRM_DEV_OFFLOAD_OUT) {\n \t\ttx_ft_put(sa_entry-\u003eipsec, sa_entry-\u003eattrs.type);\n@@ -2686,7 +2660,7 @@ int mlx5e_accel_ipsec_fs_add_pol(struct mlx5e_ipsec_pol_entry *pol_entry)\n {\n \tint err;\n \n-\terr = mlx5e_ipsec_block_tc_offload(pol_entry-\u003eipsec-\u003emdev);\n+\terr = mlx5e_accel_block_tc_offload(pol_entry-\u003eipsec-\u003emdev);\n \tif (err)\n \t\treturn err;\n \n@@ -2701,7 +2675,7 @@ int mlx5e_accel_ipsec_fs_add_pol(struct mlx5e_ipsec_pol_entry *pol_entry)\n \treturn 0;\n \n err_out:\n-\tmlx5e_ipsec_unblock_tc_offload(pol_entry-\u003eipsec-\u003emdev);\n+\tmlx5e_accel_unblock_tc_offload(pol_entry-\u003eipsec-\u003emdev);\n \treturn err;\n }\n \n@@ -2712,7 +2686,7 @@ void mlx5e_accel_ipsec_fs_del_pol(struct mlx5e_ipsec_pol_entry *pol_entry)\n \n \tmlx5_del_flow_rules(ipsec_rule-\u003erule);\n \n-\tmlx5e_ipsec_unblock_tc_offload(pol_entry-\u003eipsec-\u003emdev);\n+\tmlx5e_accel_unblock_tc_offload(pol_entry-\u003eipsec-\u003emdev);\n \n \tif (pol_entry-\u003eattrs.dir == XFRM_DEV_OFFLOAD_IN) {\n \t\trx_ft_put_policy(pol_entry-\u003eipsec,\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h\nindex 45b0d19e735cb..57aabd5e61ece 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h\n@@ -38,10 +38,9 @@\n #include \u003cnet/xfrm.h\u003e\n #include \"en.h\"\n #include \"en/txrx.h\"\n+#include \"en_accel/flow_tag.h\"\n \n-/* Bit31: IPsec marker, Bit30: reserved, Bit29-24: IPsec syndrome, Bit23-0: IPsec obj id */\n-#define MLX5_IPSEC_METADATA_MARKER(metadata) ((((metadata) \u003e\u003e 30) \u0026 0x3) == 0x2)\n-#define MLX5_IPSEC_METADATA_SYNDROM(metadata) (((metadata) \u003e\u003e 24) \u0026 GENMASK(5, 0))\n+/* IPsec obj id in ft_metadata bits[23:0] */\n #define MLX5_IPSEC_METADATA_HANDLE(metadata) ((metadata) \u0026 GENMASK(23, 0))\n \n struct mlx5e_accel_tx_ipsec_state {\n@@ -74,7 +73,8 @@ static inline unsigned int mlx5e_ipsec_tx_ids_len(struct mlx5e_accel_tx_ipsec_st\n \n static inline bool mlx5_ipsec_is_rx_flow(struct mlx5_cqe64 *cqe)\n {\n-\treturn MLX5_IPSEC_METADATA_MARKER(be32_to_cpu(cqe-\u003eft_metadata));\n+\treturn mlx5e_accel_flow_tag_proto(cqe) ==\n+\t\tMLX5E_ACCEL_FLOW_TAG_PROTO_IPSEC;\n }\n \n static inline bool mlx5e_ipsec_eseg_meta(struct mlx5_wqe_eth_seg *eseg)\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c\nindex daff53ba7d09f..aa7a28954861b 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c\n@@ -10,6 +10,7 @@\n #include \"en.h\"\n #include \"lib/aso.h\"\n #include \"lib/crypto.h\"\n+#include \"en_accel/en_accel.h\"\n #include \"en_accel/macsec.h\"\n \n #define MLX5_MACSEC_EPN_SCOPE_MID 0x80000000L\n@@ -324,6 +325,8 @@ static void mlx5e_macsec_cleanup_sa_fs(struct mlx5e_macsec *macsec,\n \tmlx5_macsec_fs_del_rule(macsec-\u003emdev-\u003emacsec_fs, sa-\u003emacsec_rule, action, netdev,\n \t\t\t\tfs_id);\n \tsa-\u003emacsec_rule = NULL;\n+\tif (!is_tx)\n+\t\tmlx5e_accel_unblock_tc_offload(macsec-\u003emdev);\n }\n \n static void mlx5e_macsec_cleanup_sa(struct mlx5e_macsec *macsec,\n@@ -343,6 +346,7 @@ static int mlx5e_macsec_init_sa_fs(struct macsec_context *ctx,\n \tconst struct macsec_tx_sc *tx_sc = \u0026ctx-\u003esecy-\u003etx_sc;\n \tstruct mlx5_macsec_rule_attrs rule_attrs;\n \tunion mlx5_macsec_rule *macsec_rule;\n+\tint err = 0;\n \n \tif (is_tx \u0026\u0026 tx_sc-\u003eencoding_sa != sa-\u003eassoc_num)\n \t\treturn 0;\n@@ -353,13 +357,26 @@ static int mlx5e_macsec_init_sa_fs(struct macsec_context *ctx,\n \trule_attrs.action = (is_tx) ? MLX5_ACCEL_MACSEC_ACTION_ENCRYPT :\n \t\t\t\t MLX5_ACCEL_MACSEC_ACTION_DECRYPT;\n \n+\tif (!is_tx) {\n+\t\terr = mlx5e_accel_block_tc_offload(priv-\u003emdev);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n+\n \tmacsec_rule = mlx5_macsec_fs_add_rule(macsec_fs, ctx, \u0026rule_attrs, fs_id);\n-\tif (!macsec_rule)\n-\t\treturn -ENOMEM;\n+\tif (!macsec_rule) {\n+\t\terr = -ENOMEM;\n+\t\tgoto out_unblock_tc;\n+\t}\n \n \tsa-\u003emacsec_rule = macsec_rule;\n \n \treturn 0;\n+\n+out_unblock_tc:\n+\tif (!is_tx)\n+\t\tmlx5e_accel_unblock_tc_offload(priv-\u003emdev);\n+\treturn err;\n }\n \n static int mlx5e_macsec_init_sa(struct macsec_context *ctx,\n@@ -735,12 +752,12 @@ static int mlx5e_macsec_add_rxsc(struct macsec_context *ctx)\n \t * a non-NULL md_dst with uninitialised contents.\n \t */\n \terr = xa_alloc(\u0026macsec-\u003esc_xarray, \u0026sc_xarray_element-\u003efs_id, sc_xarray_element,\n-\t\t XA_LIMIT(1, MLX5_MACEC_RX_FS_ID_MAX), GFP_KERNEL);\n+\t\t XA_LIMIT(1, MLX5_MACSEC_RX_FS_ID_MAX), GFP_KERNEL);\n \tif (err) {\n \t\tif (err == -EBUSY)\n \t\t\tnetdev_err(ctx-\u003enetdev,\n \t\t\t\t \"MACsec offload: unable to create entry for RX SC (%d Rx SCs already allocated)\\n\",\n-\t\t\t\t MLX5_MACEC_RX_FS_ID_MAX);\n+\t\t\t\t MLX5_MACSEC_RX_FS_ID_MAX);\n \t\tgoto destroy_md_dst;\n \t}\n \n@@ -1137,7 +1154,12 @@ static int macsec_upd_secy_hw_address(struct macsec_context *ctx,\n \tstruct mlx5e_macsec_sa *rx_sa;\n \tstruct list_head *list;\n \tint i, err = 0;\n+\tbool block_tc;\n \n+\t/* Best-effort TC block across the operation, to prevent a mistimed TC\n+\t * filter add from preventing the 2nd loop from happening.\n+\t */\n+\tblock_tc = mlx5e_accel_block_tc_offload(priv-\u003emdev) == 0;\n \n \tlist = \u0026macsec_device-\u003emacsec_rx_sc_list_head;\n \tlist_for_each_entry_safe(rx_sc, tmp, list, rx_sc_list_element) {\n@@ -1168,6 +1190,8 @@ static int macsec_upd_secy_hw_address(struct macsec_context *ctx,\n \n \tmemcpy(macsec_device-\u003edev_addr, dev-\u003edev_addr, dev-\u003eaddr_len);\n out:\n+\tif (block_tc)\n+\t\tmlx5e_accel_unblock_tc_offload(priv-\u003emdev);\n \treturn err;\n }\n \n@@ -1700,7 +1724,7 @@ void mlx5e_macsec_offload_handle_rx_skb(struct net_device *netdev,\n \tif (!macsec)\n \t\treturn;\n \n-\tfs_id = MLX5_MACSEC_RX_METADAT_HANDLE(macsec_meta_data);\n+\tfs_id = MLX5_MACSEC_RX_METADATA_HANDLE(macsec_meta_data);\n \n \trcu_read_lock();\n \tsc_xarray_element = xa_load(\u0026macsec-\u003esc_xarray, fs_id);\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.h\nindex 27df72e23106d..2066cb573f68e 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.h\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.h\n@@ -9,6 +9,7 @@\n #include \u003clinux/mlx5/driver.h\u003e\n #include \u003cnet/macsec.h\u003e\n #include \u003cnet/dst_metadata.h\u003e\n+#include \"en_accel/flow_tag.h\"\n #include \"lib/macsec_fs.h\"\n \n struct mlx5e_priv;\n@@ -31,7 +32,8 @@ static inline bool mlx5e_macsec_skb_is_offload(struct sk_buff *skb)\n \n static inline bool mlx5e_macsec_is_rx_flow(struct mlx5_cqe64 *cqe)\n {\n-\treturn MLX5_MACSEC_METADATA_MARKER(be32_to_cpu(cqe-\u003eft_metadata));\n+\treturn mlx5e_accel_flow_tag_proto(cqe) ==\n+\t\tMLX5E_ACCEL_FLOW_TAG_PROTO_MACSEC;\n }\n \n void mlx5e_macsec_offload_handle_rx_skb(struct net_device *netdev, struct sk_buff *skb,\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c\nindex 73b232379263a..c40d2ddf6af27 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c\n@@ -6,7 +6,9 @@\n #include \"mlx5_core.h\"\n #include \"psp.h\"\n #include \"lib/crypto.h\"\n+#include \"en_accel/en_accel.h\"\n #include \"en_accel/psp.h\"\n+#include \"en_accel/psp_rxtx.h\"\n #include \"fs_core.h\"\n \n enum accel_fs_psp_type {\n@@ -21,6 +23,13 @@ enum accel_psp_syndrome {\n \tPSP_BAD_TRAILER,\n };\n \n+static const u8 psp_supported_versions[] = {\n+\tPSP_VERSION_HDR0_AES_GCM_128,\n+\tPSP_VERSION_HDR0_AES_GCM_256,\n+};\n+\n+#define MLX5E_PSP_NUM_SUPPORTED_VERSIONS ARRAY_SIZE(psp_supported_versions)\n+\n struct mlx5e_psp_tx_table {\n \tstruct mlx5_flow_namespace *ns;\n \tstruct mlx5_flow_table *ft;\n@@ -41,7 +50,7 @@ struct mlx5e_psp_rx_decrypt_table {\n \tstruct mlx5_flow_table *ft;\n \tstruct mlx5_flow_group *miss_group;\n \tstruct mlx5_flow_handle *miss_rule;\n-\tstruct mlx5_modify_hdr *rx_modify_hdr;\n+\tstruct mlx5_modify_hdr *modify_hdr;\n \tstruct mlx5_flow_handle *rule;\n };\n \n@@ -52,6 +61,15 @@ struct mlx5e_psp_rx_table {\n \tstruct mlx5_flow_handle *udp_rules[ACCEL_FS_PSP_NUM_TYPES];\n };\n \n+struct mlx5e_psp_rx_decap_table {\n+\tstruct mlx5_flow_table *ft;\n+\tstruct mlx5_flow_group *drop_group;\n+\tstruct mlx5_modify_hdr *modify_hdr;\n+\tstruct mlx5_pkt_reformat *reformat;\n+\tstruct mlx5_flow_handle *rule[MLX5E_PSP_NUM_SUPPORTED_VERSIONS];\n+\tstruct mlx5_flow_handle *unsupported_rule;\n+};\n+\n struct mlx5e_psp_fs {\n \tstruct mlx5_core_dev *mdev;\n \tstruct mlx5_fc *tx_counter;\n@@ -63,12 +81,23 @@ struct mlx5e_psp_fs {\n \tstruct mlx5_fc *rx_auth_fail_counter;\n \tstruct mlx5_fc *rx_err_counter;\n \tstruct mlx5_fc *rx_bad_counter;\n+\t/* When set, steering is configured to decapsulate PSP (remove UDP+PSP\n+\t * headers and PSP trailer) and hand off the SPI in cqe.ft_metadata.\n+\t */\n+\tbool decap_enabled;\n \n \tstruct mlx5e_psp_rx_decrypt_table decrypt[ACCEL_FS_PSP_NUM_TYPES];\n \tstruct mlx5e_psp_rx_check_table check;\n+\tstruct mlx5e_psp_rx_decap_table decap;\n \tstruct mlx5e_psp_rx_table rx;\n };\n \n+static bool shampo_enabled(struct mlx5e_priv *priv)\n+{\n+\treturn priv-\u003echannels.params.packet_merge.type ==\n+\t\tMLX5E_PACKET_MERGE_SHAMPO;\n+}\n+\n /* PSP RX flow steering */\n static enum mlx5_traffic_types fs_psp2tt(enum accel_fs_psp_type i)\n {\n@@ -110,6 +139,15 @@ static void accel_psp_fs_del_flow_rule(struct mlx5_flow_handle **rule)\n \t}\n }\n \n+static void accel_psp_fs_dealloc_modify_hdr(struct mlx5_core_dev *dev,\n+\t\t\t\t\t struct mlx5_modify_hdr **modhdr)\n+{\n+\tif (*modhdr) {\n+\t\tmlx5_modify_header_dealloc(dev, *modhdr);\n+\t\t*modhdr = NULL;\n+\t}\n+}\n+\n static int accel_psp_fs_create_miss_group(struct mlx5_flow_table *ft,\n \t\t\t\t\t struct mlx5_flow_group **group)\n {\n@@ -402,15 +440,164 @@ int accel_psp_fs_rx_check_ft_create(struct mlx5e_psp_fs *fs,\n \treturn err;\n }\n \n+static\n+void accel_psp_fs_rx_decap_ft_destroy(struct mlx5e_psp_fs *fs,\n+\t\t\t\t struct mlx5e_psp_rx_decap_table *decap)\n+{\n+\tint i;\n+\n+\taccel_psp_fs_del_flow_rule(\u0026decap-\u003eunsupported_rule);\n+\tfor (i = 0; i \u003c MLX5E_PSP_NUM_SUPPORTED_VERSIONS; i++)\n+\t\taccel_psp_fs_del_flow_rule(\u0026decap-\u003erule[i]);\n+\tif (decap-\u003ereformat) {\n+\t\tmlx5_packet_reformat_dealloc(fs-\u003emdev, decap-\u003ereformat);\n+\t\tdecap-\u003ereformat = NULL;\n+\t}\n+\taccel_psp_fs_dealloc_modify_hdr(fs-\u003emdev, \u0026decap-\u003emodify_hdr);\n+\taccel_psp_fs_destroy_flow_group(\u0026decap-\u003edrop_group);\n+\taccel_psp_fs_destroy_ft(\u0026decap-\u003eft);\n+\tfs-\u003edecap_enabled = false;\n+}\n+\n+static void setup_fte_psp_version(struct mlx5_flow_spec *spec, u8 version)\n+{\n+\tvoid *misc_params_6;\n+\n+\tmemset(spec, 0, sizeof(*spec));\n+\tspec-\u003ematch_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_6;\n+\tmisc_params_6 = MLX5_ADDR_OF(fte_match_param, spec-\u003ematch_criteria,\n+\t\t\t\t misc_parameters_6);\n+\tMLX5_SET_TO_ONES(fte_match_set_misc6, misc_params_6, psp_version);\n+\tmisc_params_6 = MLX5_ADDR_OF(fte_match_param, spec-\u003ematch_value,\n+\t\t\t\t misc_parameters_6);\n+\tMLX5_SET(fte_match_set_misc6, misc_params_6, psp_version, version);\n+}\n+\n+static\n+int accel_psp_fs_rx_decap_ft_create(struct mlx5e_psp_fs *fs,\n+\t\t\t\t struct mlx5e_psp_rx_decap_table *decap)\n+{\n+\tu8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {};\n+\tstruct mlx5_pkt_reformat_params reformat_params = {};\n+\tstruct mlx5_flow_table_attr ft_attr = {};\n+\tstruct mlx5_flow_destination dest = {};\n+\tstruct mlx5_core_dev *mdev = fs-\u003emdev;\n+\tstruct mlx5_pkt_reformat *reformat;\n+\tstruct mlx5_modify_hdr *modify_hdr;\n+\tstruct mlx5_flow_handle *rule;\n+\tstruct mlx5_flow_spec *spec;\n+\tint i, err = 0;\n+\n+\tspec = kvzalloc_obj(*spec);\n+\tif (!spec)\n+\t\treturn -ENOMEM;\n+\n+\t/* Create FT */\n+\tft_attr.max_fte = 1 + MLX5E_PSP_NUM_SUPPORTED_VERSIONS;\n+\tft_attr.level = MLX5E_ACCEL_FS_PSP_DECAP_FT_LEVEL;\n+\tft_attr.prio = MLX5E_NIC_PRIO;\n+\tft_attr.autogroup.num_reserved_entries = 1;\n+\terr = accel_psp_fs_create_ft(fs, \u0026ft_attr, \u0026decap-\u003eft);\n+\tif (err) {\n+\t\tmlx5_core_err(mdev, \"fail to create psp decap rx ft err=%d\\n\",\n+\t\t\t err);\n+\t\tgoto out_spec;\n+\t}\n+\n+\t/* Create drop group */\n+\terr = accel_psp_fs_create_miss_group(decap-\u003eft, \u0026decap-\u003edrop_group);\n+\tif (err) {\n+\t\tmlx5_core_err(mdev,\n+\t\t\t \"fail to create psp decap rx drop_group err=%d\\n\",\n+\t\t\t err);\n+\t\tgoto out_err;\n+\t}\n+\n+\t/* Add default drop rule */\n+\terr = accel_psp_add_drop_rule(decap-\u003eft, NULL, fs-\u003erx_bad_counter,\n+\t\t\t\t \u0026decap-\u003eunsupported_rule);\n+\tif (err) {\n+\t\tmlx5_core_err(mdev,\n+\t\t\t \"fail to create psp decap unsupported versions drop rule err=%d\\n\",\n+\t\t\t err);\n+\t\tgoto out_err;\n+\t}\n+\n+\t/* modify_hdr: copy SPI from REG_C_0 to REG_B */\n+\tMLX5_SET(copy_action_in, action, action_type, MLX5_ACTION_TYPE_COPY);\n+\tMLX5_SET(copy_action_in, action, src_field,\n+\t\t MLX5_ACTION_IN_FIELD_METADATA_REG_C_0);\n+\tMLX5_SET(copy_action_in, action, src_offset, 0);\n+\tMLX5_SET(copy_action_in, action, length, 0); /* 0 = 32 bits */\n+\tMLX5_SET(copy_action_in, action, dst_field,\n+\t\t MLX5_ACTION_IN_FIELD_METADATA_REG_B);\n+\tMLX5_SET(copy_action_in, action, dst_offset, 0);\n+\n+\tmodify_hdr = mlx5_modify_header_alloc(mdev, MLX5_FLOW_NAMESPACE_KERNEL,\n+\t\t\t\t\t 1, action);\n+\tif (IS_ERR(modify_hdr)) {\n+\t\terr = PTR_ERR(modify_hdr);\n+\t\tgoto out_err;\n+\t}\n+\tdecap-\u003emodify_hdr = modify_hdr;\n+\n+\t/* pkt_reformat: decap PSP transport */\n+\treformat_params.type = MLX5_REFORMAT_TYPE_REMOVE_PSP_TRANSPORT;\n+\treformat = mlx5_packet_reformat_alloc(mdev, \u0026reformat_params,\n+\t\t\t\t\t MLX5_FLOW_NAMESPACE_KERNEL);\n+\tif (IS_ERR(reformat)) {\n+\t\terr = PTR_ERR(reformat);\n+\t\tgoto out_err;\n+\t}\n+\tdecap-\u003ereformat = reformat;\n+\n+\tfor (i = 0; i \u003c MLX5E_PSP_NUM_SUPPORTED_VERSIONS; i++) {\n+\t\tu8 version = psp_supported_versions[i];\n+\t\tstruct mlx5_flow_act flow_act = {};\n+\n+\t\t/* match(version) =\u003e decap, copy SPI, fwd to rx FT */\n+\t\tsetup_fte_psp_version(spec, version);\n+\n+\t\t/*\n+\t\t * Override the flow tag set in the decrypt table with\n+\t\t * the decap PSP marker and version.\n+\t\t */\n+\t\tspec-\u003eflow_context.flags = FLOW_CONTEXT_HAS_TAG;\n+\t\tspec-\u003eflow_context.flow_tag =\n+\t\t\tFIELD_PREP(MLX5E_ACCEL_FLOW_TAG_PROTO_MASK,\n+\t\t\t\t MLX5E_ACCEL_FLOW_TAG_PROTO_PSP_DECAP) |\n+\t\t\tFIELD_PREP(MLX5E_ACCEL_FLOW_TAG_PSP_VER_MASK, version);\n+\n+\t\tflow_act.action = MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT |\n+\t\t\tMLX5_FLOW_CONTEXT_ACTION_MOD_HDR |\n+\t\t\tMLX5_FLOW_CONTEXT_ACTION_FWD_DEST;\n+\t\tflow_act.pkt_reformat = reformat;\n+\t\tflow_act.modify_hdr = modify_hdr;\n+\t\tdest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;\n+\t\tdest.ft = fs-\u003erx.ft;\n+\n+\t\trule = mlx5_add_flow_rules(decap-\u003eft, spec, \u0026flow_act, \u0026dest, 1);\n+\t\tif (IS_ERR(rule)) {\n+\t\t\terr = PTR_ERR(rule);\n+\t\t\tgoto out_err;\n+\t\t}\n+\t\tdecap-\u003erule[i] = rule;\n+\t}\n+\tgoto out_spec;\n+\n+out_err:\n+\taccel_psp_fs_rx_decap_ft_destroy(fs, decap);\n+out_spec:\n+\tkvfree(spec);\n+\treturn err;\n+}\n+\n static void\n accel_psp_fs_rx_decrypt_ft_destroy(struct mlx5e_psp_fs *fs,\n \t\t\t\t struct mlx5e_psp_rx_decrypt_table *decrypt)\n {\n \taccel_psp_fs_del_flow_rule(\u0026decrypt-\u003erule);\n-\tif (decrypt-\u003erx_modify_hdr) {\n-\t\tmlx5_modify_header_dealloc(fs-\u003emdev, decrypt-\u003erx_modify_hdr);\n-\t\tdecrypt-\u003erx_modify_hdr = NULL;\n-\t}\n+\taccel_psp_fs_dealloc_modify_hdr(fs-\u003emdev, \u0026decrypt-\u003emodify_hdr);\n \taccel_psp_fs_del_flow_rule(\u0026decrypt-\u003emiss_rule);\n \taccel_psp_fs_destroy_flow_group(\u0026decrypt-\u003emiss_group);\n \taccel_psp_fs_destroy_ft(\u0026decrypt-\u003eft);\n@@ -431,11 +618,11 @@ accel_psp_fs_rx_decrypt_ft_create(struct mlx5e_psp_fs *fs,\n \t\t\t\t struct mlx5_flow_destination *default_dest)\n {\n \tu8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {};\n-\tstruct mlx5_modify_hdr *modify_hdr = NULL;\n \tstruct mlx5_flow_table_attr ft_attr = {};\n \tstruct mlx5_flow_destination dest = {};\n \tstruct mlx5_core_dev *mdev = fs-\u003emdev;\n \tMLX5_DECLARE_FLOW_ACT(flow_act);\n+\tstruct mlx5_modify_hdr *modhdr;\n \tstruct mlx5_flow_handle *rule;\n \tstruct mlx5_flow_spec *spec;\n \tint err = 0;\n@@ -479,30 +666,38 @@ accel_psp_fs_rx_decrypt_ft_create(struct mlx5e_psp_fs *fs,\n \t}\n \tdecrypt-\u003emiss_rule = rule;\n \n-\t/* Add PSP RX decrypt rule */\n-\tsetup_fte_udp_psp(spec, PSP_DEFAULT_UDP_PORT);\n-\tflow_act.crypto.type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_PSP;\n-\t/* Set bit[31, 30] PSP marker */\n-#define MLX5E_PSP_MARKER_BIT (BIT(30) | BIT(31))\n-\tMLX5_SET(set_action_in, action, action_type, MLX5_ACTION_TYPE_SET);\n-\tMLX5_SET(set_action_in, action, field, MLX5_ACTION_IN_FIELD_METADATA_REG_B);\n-\tMLX5_SET(set_action_in, action, data, MLX5E_PSP_MARKER_BIT);\n-\tMLX5_SET(set_action_in, action, offset, 0);\n-\tMLX5_SET(set_action_in, action, length, 32);\n-\n-\tmodify_hdr = mlx5_modify_header_alloc(mdev, MLX5_FLOW_NAMESPACE_KERNEL, 1, action);\n-\tif (IS_ERR(modify_hdr)) {\n-\t\terr = PTR_ERR(modify_hdr);\n-\t\tmlx5_core_err(mdev, \"fail to alloc psp set modify_header_id err=%d\\n\", err);\n-\t\tmodify_hdr = NULL;\n+\t/* Create modify_hdr to copy SPI to REG_C_0 */\n+\tMLX5_SET(copy_action_in, action, action_type, MLX5_ACTION_TYPE_COPY);\n+\tMLX5_SET(copy_action_in, action, src_field,\n+\t\t MLX5_ACTION_IN_FIELD_PSP_HEADER_1);\n+\tMLX5_SET(copy_action_in, action, src_offset, 0);\n+\tMLX5_SET(copy_action_in, action, length, 0); /* 0 = 32 bits */\n+\tMLX5_SET(copy_action_in, action, dst_field,\n+\t\t MLX5_ACTION_IN_FIELD_METADATA_REG_C_0);\n+\tMLX5_SET(copy_action_in, action, dst_offset, 0);\n+\n+\tmodhdr = mlx5_modify_header_alloc(mdev, MLX5_FLOW_NAMESPACE_KERNEL, 1,\n+\t\t\t\t\t action);\n+\tif (IS_ERR(modhdr)) {\n+\t\terr = PTR_ERR(modhdr);\n \t\tgoto out_err;\n \t}\n-\tdecrypt-\u003erx_modify_hdr = modify_hdr;\n+\tdecrypt-\u003emodify_hdr = modhdr;\n \n-\tflow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |\n-\t\t\t MLX5_FLOW_CONTEXT_ACTION_CRYPTO_DECRYPT |\n-\t\t\t MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;\n-\tflow_act.modify_hdr = modify_hdr;\n+\t/* Add PSP RX decrypt rule */\n+\tsetup_fte_udp_psp(spec, PSP_DEFAULT_UDP_PORT);\n+\n+\t/* Set PSP marker via flow_tag */\n+\tspec-\u003eflow_context.flags = FLOW_CONTEXT_HAS_TAG;\n+\tspec-\u003eflow_context.flow_tag =\n+\t\tFIELD_PREP(MLX5E_ACCEL_FLOW_TAG_PROTO_MASK,\n+\t\t\t MLX5E_ACCEL_FLOW_TAG_PROTO_PSP);\n+\n+\tflow_act.crypto.type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_PSP;\n+\tflow_act.action = MLX5_FLOW_CONTEXT_ACTION_CRYPTO_DECRYPT |\n+\t\tMLX5_FLOW_CONTEXT_ACTION_MOD_HDR |\n+\t\tMLX5_FLOW_CONTEXT_ACTION_FWD_DEST;\n+\tflow_act.modify_hdr = modhdr;\n \tdest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;\n \tdest.ft = fs-\u003echeck.ft;\n \trule = mlx5_add_flow_rules(decrypt-\u003eft, spec, \u0026flow_act, \u0026dest, 1);\n@@ -523,9 +718,46 @@ accel_psp_fs_rx_decrypt_ft_create(struct mlx5e_psp_fs *fs,\n \treturn err;\n }\n \n+static void accel_psp_fs_rx_reconfigure(struct mlx5e_psp_fs *fs,\n+\t\t\t\t\tbool decap_wanted)\n+{\n+\tbool decap_supported =\n+\t\tMLX5_CAP_FLOWTABLE(fs-\u003emdev,\n+\t\t\t\t flow_table_properties_nic_receive.reformat_del_psp_transport);\n+\tbool decap_enable = decap_wanted \u0026\u0026 decap_supported;\n+\tstruct mlx5_flow_destination dest = {};\n+\tint err;\n+\n+\tif (decap_enable == fs-\u003edecap_enabled)\n+\t\treturn;\n+\n+\t/* Create the decap table if needed. */\n+\tif (decap_enable \u0026\u0026 !fs-\u003edecap.ft) {\n+\t\terr = accel_psp_fs_rx_decap_ft_create(fs, \u0026fs-\u003edecap);\n+\t\tif (err)\n+\t\t\tgoto out_err;\n+\t}\n+\n+\t/* Redirect traffic to the correct table. */\n+\tdest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;\n+\tdest.ft = decap_enable ? fs-\u003edecap.ft : fs-\u003erx.ft;\n+\terr = mlx5_modify_rule_destination(fs-\u003echeck.rule, \u0026dest, NULL);\n+\tif (err)\n+\t\tgoto out_err;\n+\n+\tfs-\u003edecap_enabled = decap_enable;\n+\treturn;\n+\n+out_err:\n+\tmlx5_core_warn(fs-\u003emdev,\n+\t\t \"Failed to create/modify PSP decapsulation rules (err %d), HW GRO for PSP unavailable\\n\",\n+\t\t err);\n+}\n+\n static void accel_psp_fs_rx_destroy(struct mlx5e_psp_fs *fs)\n {\n \tstruct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(fs-\u003efs, false);\n+\tbool tc_blocked = fs-\u003erx.ft;\n \tint i;\n \n \t/* disconnect */\n@@ -534,19 +766,29 @@ static void accel_psp_fs_rx_destroy(struct mlx5e_psp_fs *fs)\n \t\taccel_psp_fs_rx_decrypt_ft_destroy(fs, \u0026fs-\u003edecrypt[i]);\n \t}\n \taccel_psp_fs_rx_check_ft_destroy(\u0026fs-\u003echeck);\n+\taccel_psp_fs_rx_decap_ft_destroy(fs, \u0026fs-\u003edecap);\n \taccel_psp_fs_rx_ft_destroy(\u0026fs-\u003erx);\n+\tif (tc_blocked)\n+\t\tmlx5e_accel_unblock_tc_offload(fs-\u003emdev);\n }\n \n static int accel_psp_fs_rx_create(struct mlx5e_psp_fs *fs,\n+\t\t\t\t bool decap_enable,\n \t\t\t\t struct netlink_ext_ack *extack)\n {\n \tstruct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(fs-\u003efs, false);\n \tint i, err;\n \n+\terr = mlx5e_accel_block_tc_offload(fs-\u003emdev);\n+\tif (err) {\n+\t\tNL_SET_ERR_MSG(extack, \"TC offload active, cannot enable PSP\");\n+\t\treturn err;\n+\t}\n+\n \terr = accel_psp_fs_rx_ft_create(fs, \u0026fs-\u003erx);\n \tif (err) {\n \t\tNL_SET_ERR_MSG(extack, \"Failed creating RX steering table\");\n-\t\treturn err;\n+\t\tgoto err_unblock_tc;\n \t}\n \n \terr = accel_psp_fs_rx_check_ft_create(fs, \u0026fs-\u003echeck);\n@@ -573,6 +815,8 @@ static int accel_psp_fs_rx_create(struct mlx5e_psp_fs *fs,\n \t\tmlx5_ttc_fwd_dest(ttc, fs_psp2tt(i), \u0026dest);\n \t}\n \n+\taccel_psp_fs_rx_reconfigure(fs, decap_enable);\n+\n \treturn 0;\n \n err_decrypt_ft:\n@@ -583,6 +827,8 @@ static int accel_psp_fs_rx_create(struct mlx5e_psp_fs *fs,\n \taccel_psp_fs_rx_check_ft_destroy(\u0026fs-\u003echeck);\n err_ft:\n \taccel_psp_fs_rx_ft_destroy(\u0026fs-\u003erx);\n+err_unblock_tc:\n+\tmlx5e_accel_unblock_tc_offload(fs-\u003emdev);\n \treturn err;\n }\n \n@@ -839,7 +1085,8 @@ static int accel_psp_fs_create(struct mlx5e_priv *priv,\n {\n \tint err;\n \n-\terr = accel_psp_fs_rx_create(priv-\u003epsp-\u003efs, extack);\n+\terr = accel_psp_fs_rx_create(priv-\u003epsp-\u003efs, shampo_enabled(priv),\n+\t\t\t\t extack);\n \tif (err)\n \t\treturn err;\n \n@@ -1116,3 +1363,14 @@ void mlx5e_psp_cleanup(struct mlx5e_priv *priv)\n \tpriv-\u003epsp = NULL;\n \tkfree(psp);\n }\n+\n+void mlx5e_psp_update_rx(struct mlx5e_priv *priv)\n+{\n+\tstruct mlx5e_psp *psp = priv-\u003epsp;\n+\n+\tnetdev_assert_locked(priv-\u003enetdev);\n+\tif (!psp || !psp-\u003efs-\u003echeck.ft)\n+\t\treturn;\n+\n+\taccel_psp_fs_rx_reconfigure(psp-\u003efs, shampo_enabled(priv));\n+}\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.h\nindex 3f441e7dd55ac..43571a039ea61 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.h\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.h\n@@ -49,6 +49,7 @@ int mlx5e_psp_register(struct mlx5e_priv *priv);\n void mlx5e_psp_unregister(struct mlx5e_priv *priv);\n int mlx5e_psp_init(struct mlx5e_priv *priv);\n void mlx5e_psp_cleanup(struct mlx5e_priv *priv);\n+void mlx5e_psp_update_rx(struct mlx5e_priv *priv);\n #else\n static inline void mlx5_accel_psp_fs_cleanup_rx_tables(struct mlx5e_priv *priv) { }\n static inline void mlx5_accel_psp_fs_cleanup_tx_tables(struct mlx5e_priv *priv) { }\n@@ -61,5 +62,6 @@ static inline int mlx5e_psp_register(struct mlx5e_priv *priv) { return 0; }\n static inline void mlx5e_psp_unregister(struct mlx5e_priv *priv) { }\n static inline int mlx5e_psp_init(struct mlx5e_priv *priv) { return 0; }\n static inline void mlx5e_psp_cleanup(struct mlx5e_priv *priv) { }\n+static inline void mlx5e_psp_update_rx(struct mlx5e_priv *priv) { }\n #endif /* CONFIG_MLX5_EN_PSP */\n #endif /* __MLX5E_ACCEL_PSP_H__ */\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c\nindex 348fd7a962615..2beffee14278d 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c\n@@ -118,10 +118,25 @@ bool mlx5e_psp_offload_handle_rx_skb(struct net_device *netdev, struct sk_buff *\n {\n \tstruct mlx5e_priv *priv = netdev_priv(netdev);\n \tu16 dev_id = priv-\u003epsp-\u003epsd-\u003eid;\n-\tbool strip_icv = true;\n-\tu8 generation = 0;\n+\tstruct psp_skb_ext *pse;\n+\n+\tif (mlx5e_psp_is_decap(cqe)) {\n+\t\t/* UDP + PSP headers and PSP trailer removed by HW.\n+\t\t * Construct the PSP extension from CQE metadata.\n+\t\t */\n+\t\tpse = skb_ext_add(skb, SKB_EXT_PSP);\n+\t\tif (unlikely(!pse))\n+\t\t\tgoto drop;\n+\n+\t\tpse-\u003espi = mlx5e_psp_get_spi(cqe);\n+\t\tpse-\u003eversion = mlx5e_psp_get_version(cqe);\n+\t\tpse-\u003edev_id = dev_id;\n+\t\tpse-\u003egeneration = 0;\n+\t\tskb-\u003edecrypted = 1;\n+\t\treturn false;\n+\t}\n \n-\tif (psp_dev_rcv(skb, dev_id, generation, strip_icv))\n+\tif (psp_dev_rcv(skb, dev_id, 0, true))\n \t\tgoto drop;\n \n \tskb-\u003edecrypted = 1;\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.h\nindex 2b080c39cc37e..32a8330027c0b 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.h\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.h\n@@ -9,10 +9,7 @@\n #include \u003cnet/psp.h\u003e\n #include \"en.h\"\n #include \"en/txrx.h\"\n-\n-/* Bit30: PSP marker, Bit22-0: PSP obj id */\n-#define MLX5_PSP_METADATA_MARKER(metadata) ((((metadata) \u003e\u003e 30) \u0026 0x3) == 0x3)\n-#define MLX5_PSP_METADATA_HANDLE(metadata) ((metadata) \u0026 GENMASK(22, 0))\n+#include \"en_accel/flow_tag.h\"\n \n struct mlx5e_accel_tx_psp_state {\n \tu32 tailen;\n@@ -82,7 +79,29 @@ static inline unsigned int mlx5e_psp_tx_ids_len(struct mlx5e_accel_tx_psp_state\n \n static inline bool mlx5e_psp_is_rx_flow(struct mlx5_cqe64 *cqe)\n {\n-\treturn MLX5_PSP_METADATA_MARKER(be32_to_cpu(cqe-\u003eft_metadata));\n+\tu32 proto = mlx5e_accel_flow_tag_proto(cqe);\n+\n+\treturn proto == MLX5E_ACCEL_FLOW_TAG_PROTO_PSP ||\n+\t\tproto == MLX5E_ACCEL_FLOW_TAG_PROTO_PSP_DECAP;\n+\n+}\n+\n+static inline bool mlx5e_psp_is_decap(struct mlx5_cqe64 *cqe)\n+{\n+\tu32 proto = mlx5e_accel_flow_tag_proto(cqe);\n+\n+\treturn proto == MLX5E_ACCEL_FLOW_TAG_PROTO_PSP_DECAP;\n+}\n+\n+static inline u8 mlx5e_psp_get_version(struct mlx5_cqe64 *cqe)\n+{\n+\treturn FIELD_GET(MLX5E_ACCEL_FLOW_TAG_PSP_VER_MASK,\n+\t\t\t mlx5e_accel_flow_tag(cqe));\n+}\n+\n+static inline __be32 mlx5e_psp_get_spi(struct mlx5_cqe64 *cqe)\n+{\n+\treturn cqe-\u003eft_metadata;\n }\n \n bool mlx5e_psp_offload_handle_rx_skb(struct net_device *netdev, struct sk_buff *skb,\n@@ -110,6 +129,21 @@ static inline bool mlx5e_psp_is_rx_flow(struct mlx5_cqe64 *cqe)\n \treturn false;\n }\n \n+static inline bool mlx5e_psp_is_decap(struct mlx5_cqe64 *cqe)\n+{\n+\treturn false;\n+}\n+\n+static inline u8 mlx5e_psp_get_version(struct mlx5_cqe64 *cqe)\n+{\n+\treturn 0;\n+}\n+\n+static inline __be32 mlx5e_psp_get_spi(struct mlx5_cqe64 *cqe)\n+{\n+\treturn 0;\n+}\n+\n static inline bool mlx5e_psp_offload_handle_rx_skb(struct net_device *netdev,\n \t\t\t\t\t\t struct sk_buff *skb,\n \t\t\t\t\t\t struct mlx5_cqe64 *cqe)\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c\nindex fc110a7d16e8d..b5449d849838d 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c\n@@ -6288,7 +6288,15 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv)\n \n static int mlx5e_update_nic_rx(struct mlx5e_priv *priv)\n {\n-\treturn mlx5e_refresh_tirs(priv-\u003emdev, false, false);\n+\tint err;\n+\n+\terr = mlx5e_refresh_tirs(priv-\u003emdev, false, false);\n+\tif (err)\n+\t\treturn err;\n+\n+\tmlx5e_accel_update_rx(priv);\n+\n+\treturn 0;\n }\n \n static const struct mlx5e_profile mlx5e_nic_profile = {\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c\nindex 206cf9db34668..90571cb53e320 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c\n@@ -2221,6 +2221,33 @@ static bool mlx5e_hw_gro_skb_has_enough_space(struct sk_buff *skb,\n \t\treturn page_size * nr_frags + data_bcnt \u003c= GRO_LEGACY_MAX_SIZE;\n }\n \n+static bool mlx5e_hw_gro_psp_match(struct sk_buff *skb, struct mlx5_cqe64 *cqe)\n+{\n+#ifdef CONFIG_MLX5_EN_PSP\n+\tstruct psp_skb_ext *pse = skb_ext_find(skb, SKB_EXT_PSP);\n+\tbool is_psp = mlx5e_psp_is_rx_flow(cqe);\n+\n+\tif (likely(!is_psp \u0026\u0026 !pse))\n+\t\treturn true;\n+\n+\t/* No match on PSP status change (no crypto -\u003e crypto or vice-versa). */\n+\tif (unlikely(is_psp != !!pse))\n+\t\treturn false;\n+\n+\t/* SPI and version are only available in CQE metadata for decap flows.\n+\t * Non-decap PSP cannot be matched here, force a flush.\n+\t */\n+\tif (unlikely(!mlx5e_psp_is_decap(cqe)))\n+\t\treturn false;\n+\n+\t/* No match on security parameters change. */\n+\treturn pse-\u003espi == mlx5e_psp_get_spi(cqe) \u0026\u0026\n+\t\tpse-\u003eversion == mlx5e_psp_get_version(cqe);\n+#else\n+\treturn true;\n+#endif\n+}\n+\n static void mlx5e_handle_rx_cqe_mpwrq_shampo(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)\n {\n \tu16 data_bcnt\t\t= mpwrq_get_cqe_byte_cnt(cqe) - cqe-\u003eshampo.header_size;\n@@ -2263,8 +2290,9 @@ static void mlx5e_handle_rx_cqe_mpwrq_shampo(struct mlx5e_rq *rq, struct mlx5_cq\n \t}\n \n \tif (*skb \u0026\u0026\n-\t !(match \u0026\u0026 mlx5e_hw_gro_skb_has_enough_space(*skb, data_bcnt,\n-\t\t\t\t\t\t\t page_size))) {\n+\t !(match \u0026\u0026\n+\t mlx5e_hw_gro_skb_has_enough_space(*skb, data_bcnt, page_size) \u0026\u0026\n+\t mlx5e_hw_gro_psp_match(*skb, cqe))) {\n \t\tmatch = false;\n \t\tmlx5e_shampo_flush_skb(rq, cqe, match);\n \t}\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c\nindex b290beb4369ad..05c703795df4a 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c\n@@ -4813,14 +4813,14 @@ static bool is_flow_rule_duplicate_allowed(struct net_device *dev,\n \treturn netif_is_lag_port(dev) \u0026\u0026 rpriv \u0026\u0026 rpriv-\u003erep-\u003evport != MLX5_VPORT_UPLINK;\n }\n \n-/* As IPsec and TC order is not aligned between software and hardware-offload,\n- * either IPsec offload or TC offload, not both, is allowed for a specific interface.\n+/* TC offload and accel protocols can overwrite each other's flow_tag with\n+ * steering rules and they cannot simultaneously operate on the same interface.\n+ * Additionally, as IPsec and TC order is not aligned between software and\n+ * hardware-offload, only one is allowed for a specific interface.\n */\n-static bool is_tc_ipsec_order_check_needed(struct net_device *filter, struct mlx5e_priv *priv)\n+static bool is_tc_accel_check_needed(struct net_device *filter,\n+\t\t\t\t struct mlx5e_priv *priv)\n {\n-\tif (!IS_ENABLED(CONFIG_MLX5_EN_IPSEC))\n-\t\treturn false;\n-\n \tif (filter != priv-\u003enetdev)\n \t\treturn false;\n \n@@ -4830,27 +4830,35 @@ static bool is_tc_ipsec_order_check_needed(struct net_device *filter, struct mlx\n \treturn true;\n }\n \n-static int mlx5e_tc_block_ipsec_offload(struct net_device *filter, struct mlx5e_priv *priv)\n+static int mlx5e_tc_block_accel_offload(struct net_device *filter,\n+\t\t\t\t\tstruct mlx5e_priv *priv)\n {\n \tstruct mlx5_core_dev *mdev = priv-\u003emdev;\n+\tint ret = 0;\n \n-\tif (!is_tc_ipsec_order_check_needed(filter, priv))\n+\tif (!is_tc_accel_check_needed(filter, priv))\n \t\treturn 0;\n \n-\tif (mdev-\u003enum_block_tc)\n-\t\treturn -EBUSY;\n-\n-\tmdev-\u003enum_block_ipsec++;\n+\tmutex_lock(\u0026mdev-\u003eoffload_block.lock);\n+\tif (mdev-\u003eoffload_block.num_tc)\n+\t\tret = -EBUSY;\n+\telse\n+\t\tmdev-\u003eoffload_block.num_accel++;\n+\tmutex_unlock(\u0026mdev-\u003eoffload_block.lock);\n \n-\treturn 0;\n+\treturn ret;\n }\n \n-static void mlx5e_tc_unblock_ipsec_offload(struct net_device *filter, struct mlx5e_priv *priv)\n+static void mlx5e_tc_unblock_accel_offload(struct net_device *filter,\n+\t\t\t\t\t struct mlx5e_priv *priv)\n {\n-\tif (!is_tc_ipsec_order_check_needed(filter, priv))\n+\tif (!is_tc_accel_check_needed(filter, priv))\n \t\treturn;\n \n-\tpriv-\u003emdev-\u003enum_block_ipsec--;\n+\tmutex_lock(\u0026priv-\u003emdev-\u003eoffload_block.lock);\n+\tif (!WARN_ON_ONCE(!priv-\u003emdev-\u003eoffload_block.num_accel))\n+\t\tpriv-\u003emdev-\u003eoffload_block.num_accel--;\n+\tmutex_unlock(\u0026priv-\u003emdev-\u003eoffload_block.lock);\n }\n \n int mlx5e_configure_flower(struct net_device *dev, struct mlx5e_priv *priv,\n@@ -4865,7 +4873,7 @@ int mlx5e_configure_flower(struct net_device *dev, struct mlx5e_priv *priv,\n \tif (!mlx5_esw_hold(priv-\u003emdev))\n \t\treturn -EBUSY;\n \n-\terr = mlx5e_tc_block_ipsec_offload(dev, priv);\n+\terr = mlx5e_tc_block_accel_offload(dev, priv);\n \tif (err)\n \t\tgoto esw_release;\n \n@@ -4914,7 +4922,7 @@ int mlx5e_configure_flower(struct net_device *dev, struct mlx5e_priv *priv,\n err_free:\n \tmlx5e_flow_put(priv, flow);\n out:\n-\tmlx5e_tc_unblock_ipsec_offload(dev, priv);\n+\tmlx5e_tc_unblock_accel_offload(dev, priv);\n \tmlx5_esw_put(priv-\u003emdev);\n esw_release:\n \tmlx5_esw_release(priv-\u003emdev);\n@@ -4957,7 +4965,7 @@ int mlx5e_delete_flower(struct net_device *dev, struct mlx5e_priv *priv,\n \ttrace_mlx5e_delete_flower(f);\n \tmlx5e_flow_put(priv, flow);\n \n-\tmlx5e_tc_unblock_ipsec_offload(dev, priv);\n+\tmlx5e_tc_unblock_accel_offload(dev, priv);\n \tmlx5_esw_put(priv-\u003emdev);\n \treturn 0;\n \ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h\nindex e1b8cb78369f1..53b6043e33d22 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h\n@@ -36,6 +36,7 @@\n #include \u003cnet/pkt_cls.h\u003e\n #include \"en.h\"\n #include \"eswitch.h\"\n+#include \"en_accel/flow_tag.h\"\n #include \"en/tc_ct.h\"\n #include \"en/tc_tun.h\"\n #include \"en/tc/int_port.h\"\n@@ -370,11 +371,13 @@ struct mlx5e_tc_table *mlx5e_tc_table_alloc(void);\n void mlx5e_tc_table_free(struct mlx5e_tc_table *tc);\n static inline bool mlx5e_cqe_regb_chain(struct mlx5_cqe64 *cqe)\n {\n-\tu32 chain, reg_b;\n+\tu32 flow_tag, reg_b, chain;\n \n+\tflow_tag = mlx5e_accel_flow_tag_proto(cqe);\n \treg_b = be32_to_cpu(cqe-\u003eft_metadata);\n \n-\tif (reg_b \u003e\u003e (MLX5E_TC_TABLE_CHAIN_TAG_BITS + ESW_ZONE_ID_BITS))\n+\tif (flow_tag != MLX5E_ACCEL_FLOW_TAG_PROTO_NONE ||\n+\t (reg_b \u003e\u003e (MLX5E_TC_TABLE_CHAIN_TAG_BITS + ESW_ZONE_ID_BITS)))\n \t\treturn false;\n \n \tchain = reg_b \u0026 MLX5E_TC_TABLE_CHAIN_TAG_MASK;\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/macsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/macsec_fs.c\nindex efc5167493c12..4ca3a6ded2841 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/macsec_fs.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/macsec_fs.c\n@@ -8,6 +8,7 @@\n #include \u003clinux/mlx5/macsec.h\u003e\n #include \"fs_core.h\"\n #include \"lib/macsec_fs.h\"\n+#include \"en_accel/flow_tag.h\"\n #include \"mlx5_core.h\"\n \n /* MACsec TX flow steering */\n@@ -45,9 +46,6 @@\n #define MLX5_SECTAG_HEADER_SIZE_WITHOUT_SCI 0x8\n #define MLX5_SECTAG_HEADER_SIZE_WITH_SCI (MLX5_SECTAG_HEADER_SIZE_WITHOUT_SCI + MACSEC_SCI_LEN)\n \n-/* MACsec fs_id handling for steering */\n-#define macsec_fs_set_rx_fs_id(fs_id) ((fs_id) | BIT(30))\n-\n struct mlx5_sectag_header {\n \t__be16 ethertype;\n \tu8 tci_an;\n@@ -1757,11 +1755,10 @@ macsec_fs_rx_add_rule(struct mlx5_macsec_fs *macsec_fs,\n \trx_tables = \u0026rx_fs-\u003etables;\n \tft_crypto = \u0026rx_tables-\u003eft_crypto;\n \n-\t/* Set bit[31 - 30] macsec marker - 0x01 */\n \t/* Set bit[15-0] fs id */\n \tMLX5_SET(set_action_in, action, action_type, MLX5_ACTION_TYPE_SET);\n \tMLX5_SET(set_action_in, action, field, MLX5_ACTION_IN_FIELD_METADATA_REG_B);\n-\tMLX5_SET(set_action_in, action, data, macsec_fs_set_rx_fs_id(fs_id));\n+\tMLX5_SET(set_action_in, action, data, fs_id);\n \tMLX5_SET(set_action_in, action, offset, 0);\n \tMLX5_SET(set_action_in, action, length, 32);\n \n@@ -1778,6 +1775,11 @@ macsec_fs_rx_add_rule(struct mlx5_macsec_fs *macsec_fs,\n \t/* Rx crypto table with SCI rule */\n \tmacsec_fs_rx_setup_fte(spec, \u0026flow_act, attrs, true);\n \n+\tspec-\u003eflow_context.flags |= FLOW_CONTEXT_HAS_TAG;\n+\tspec-\u003eflow_context.flow_tag =\n+\t\tFIELD_PREP(MLX5E_ACCEL_FLOW_TAG_PROTO_MASK,\n+\t\t\t MLX5E_ACCEL_FLOW_TAG_PROTO_MACSEC);\n+\n \tflow_act.modify_hdr = modify_hdr;\n \tflow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |\n \t\t\t MLX5_FLOW_CONTEXT_ACTION_CRYPTO_DECRYPT |\n@@ -1803,6 +1805,11 @@ macsec_fs_rx_add_rule(struct mlx5_macsec_fs *macsec_fs,\n \n \t\tmacsec_fs_rx_setup_fte(spec, \u0026flow_act, attrs, false);\n \n+\t\tspec-\u003eflow_context.flags |= FLOW_CONTEXT_HAS_TAG;\n+\t\tspec-\u003eflow_context.flow_tag =\n+\t\t\tFIELD_PREP(MLX5E_ACCEL_FLOW_TAG_PROTO_MASK,\n+\t\t\t\t MLX5E_ACCEL_FLOW_TAG_PROTO_MACSEC);\n+\n \t\tflow_act.modify_hdr = modify_hdr;\n \t\tflow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |\n \t\t\t\t MLX5_FLOW_CONTEXT_ACTION_CRYPTO_DECRYPT |\n@@ -2160,8 +2167,8 @@ static int mlx5_macsec_fs_add_roce_rule_rx(struct mlx5_macsec_fs *macsec_fs, u32\n \n \tspec-\u003ematch_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_2;\n \tMLX5_SET_TO_ONES(fte_match_param, spec-\u003ematch_criteria, misc_parameters_2.metadata_reg_c_5);\n-\tMLX5_SET(fte_match_param, spec-\u003ematch_value, misc_parameters_2.metadata_reg_c_5,\n-\t\t macsec_fs_set_rx_fs_id(fs_id));\n+\tMLX5_SET(fte_match_param, spec-\u003ematch_value,\n+\t\t misc_parameters_2.metadata_reg_c_5, fs_id);\n \tflow_act.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW;\n \tnew_rule = mlx5_add_flow_rules(rx_fs-\u003eroce.ft_macsec_op_check, spec, \u0026flow_act,\n \t\t\t\t NULL, 0);\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/macsec_fs.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/macsec_fs.h\nindex 15acaff436417..b8b8b412d5c3e 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/macsec_fs.h\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/macsec_fs.h\n@@ -6,11 +6,10 @@\n \n #ifdef CONFIG_MLX5_MACSEC\n \n-/* Bit31 - 30: MACsec marker, Bit15-0: MACsec id */\n-#define MLX5_MACEC_RX_FS_ID_MAX USHRT_MAX /* Must be power of two */\n-#define MLX5_MACSEC_RX_FS_ID_MASK MLX5_MACEC_RX_FS_ID_MAX\n-#define MLX5_MACSEC_METADATA_MARKER(metadata) ((((metadata) \u003e\u003e 30) \u0026 0x3) == 0x1)\n-#define MLX5_MACSEC_RX_METADAT_HANDLE(metadata) ((metadata) \u0026 MLX5_MACSEC_RX_FS_ID_MASK)\n+/* MACsec fs_id in ft_metadata bits[15:0] */\n+#define MLX5_MACSEC_RX_FS_ID_MAX USHRT_MAX /* Must be power of two */\n+#define MLX5_MACSEC_RX_FS_ID_MASK MLX5_MACSEC_RX_FS_ID_MAX\n+#define MLX5_MACSEC_RX_METADATA_HANDLE(metadata) ((metadata) \u0026 MLX5_MACSEC_RX_FS_ID_MASK)\n \n /* MACsec TX flow steering */\n #define MLX5_ETH_WQE_FT_META_MACSEC_MASK \\\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c\nindex 5f28d906c35b6..46b34c80c4585 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c\n@@ -1810,6 +1810,7 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)\n \tlockdep_register_key(\u0026dev-\u003elock_key);\n \tmutex_init(\u0026dev-\u003eintf_state_mutex);\n \tlockdep_set_class(\u0026dev-\u003eintf_state_mutex, \u0026dev-\u003elock_key);\n+\tmutex_init(\u0026dev-\u003eoffload_block.lock);\n \tmutex_init(\u0026dev-\u003emlx5e_res.uplink_netdev_lock);\n \tmutex_init(\u0026dev-\u003ewc_state_lock);\n \n@@ -1901,6 +1902,7 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)\n \tmutex_destroy(\u0026priv-\u003ealloc_mutex);\n \tmutex_destroy(\u0026priv-\u003ebfregs.wc_head.lock);\n \tmutex_destroy(\u0026priv-\u003ebfregs.reg_head.lock);\n+\tmutex_destroy(\u0026dev-\u003eoffload_block.lock);\n \tmutex_destroy(\u0026dev-\u003eintf_state_mutex);\n \tlockdep_unregister_key(\u0026dev-\u003elock_key);\n \treturn err;\n@@ -1928,6 +1930,7 @@ void mlx5_mdev_uninit(struct mlx5_core_dev *dev)\n \tmutex_destroy(\u0026priv-\u003ebfregs.reg_head.lock);\n \tmutex_destroy(\u0026dev-\u003ewc_state_lock);\n \tmutex_destroy(\u0026dev-\u003emlx5e_res.uplink_netdev_lock);\n+\tmutex_destroy(\u0026dev-\u003eoffload_block.lock);\n \tmutex_destroy(\u0026dev-\u003eintf_state_mutex);\n \tlockdep_unregister_key(\u0026dev-\u003elock_key);\n }\ndiff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h\nindex 83d0a83bbfbca..3bdb0c5a87ac0 100644\n--- a/include/linux/mlx5/driver.h\n+++ b/include/linux/mlx5/driver.h\n@@ -788,8 +788,11 @@ struct mlx5_core_dev {\n \tu32 vsc_addr;\n \tstruct mlx5_hv_vhca\t*hv_vhca;\n \tstruct mlx5_hwmon\t*hwmon;\n-\tu64\t\t\tnum_block_tc;\n-\tu64\t\t\tnum_block_ipsec;\n+\tstruct {\n+\t\tstruct mutex lock;\n+\t\tu64 num_tc;\n+\t\tu64 num_accel;\n+\t} offload_block;\n #ifdef CONFIG_MLX5_MACSEC\n \tstruct mlx5_macsec_fs *macsec_fs;\n \t/* MACsec notifier chain to sync MACsec core and IB database */\ndiff --git a/tools/testing/selftests/drivers/net/Makefile b/tools/testing/selftests/drivers/net/Makefile\nindex d5bf4cb638a8c..be77a15eaf38e 100644\n--- a/tools/testing/selftests/drivers/net/Makefile\n+++ b/tools/testing/selftests/drivers/net/Makefile\n@@ -27,6 +27,11 @@ TEST_PROGS := \\\n \txdp.py \\\n # end of TEST_PROGS\n \n+TEST_FILES := \\\n+\tgro_lib.py \\\n+\tpsp_lib.py \\\n+# end of TEST_FILES\n+\n # YNL files, must be before \"include ..lib.mk\"\n YNL_GEN_FILES := psp_responder\n TEST_GEN_FILES += $(YNL_GEN_FILES)\ndiff --git a/tools/testing/selftests/drivers/net/gro.py b/tools/testing/selftests/drivers/net/gro.py\nindex 6ab8c97880d1c..7111095bb4797 100755\n--- a/tools/testing/selftests/drivers/net/gro.py\n+++ b/tools/testing/selftests/drivers/net/gro.py\n@@ -37,90 +37,20 @@ Test cases:\n \"\"\"\n \n import glob\n-import os\n import re\n from lib.py import ksft_run, ksft_exit, ksft_pr\n-from lib.py import NetDrvEpEnv, KsftFailEx, KsftXfailEx\n+from lib.py import NetDrvEpEnv, KsftXfailEx\n from lib.py import NetdevFamily, EthtoolFamily\n-from lib.py import bkg, cmd, defer, ethtool, ip\n+from lib.py import defer, ethtool\n from lib.py import ksft_variants, KsftNamedVariant\n+from gro_lib import run_gro_bin, run_with_retries, set_ethtool_feat\n+from gro_lib import set_mtu_restore, setup_hw_gro, write_defer_restore\n \n \n # gro.c uses hardcoded DPORT=8000\n GRO_DPORT = 8000\n \n \n-def _resolve_dmac(cfg, ipver):\n- \"\"\"\n- Find the destination MAC address remote host should use to send packets\n- towards the local host. It may be a router / gateway address.\n- \"\"\"\n-\n- attr = \"dmac\" + ipver\n- # Cache the response across test cases\n- if hasattr(cfg, attr):\n- return getattr(cfg, attr)\n-\n- route = ip(f\"-{ipver} route get {cfg.addr_v[ipver]}\",\n- json=True, host=cfg.remote)[0]\n- gw = route.get(\"gateway\")\n- # Local L2 segment, address directly\n- if not gw:\n- setattr(cfg, attr, cfg.dev['address'])\n- return getattr(cfg, attr)\n-\n- # ping to make sure neighbor is resolved,\n- # bind to an interface, for v6 the GW is likely link local\n- cmd(f\"ping -c1 -W0 -I{cfg.remote_ifname} {gw}\", host=cfg.remote)\n-\n- neigh = ip(f\"neigh get {gw} dev {cfg.remote_ifname}\",\n- json=True, host=cfg.remote)[0]\n- setattr(cfg, attr, neigh['lladdr'])\n- return getattr(cfg, attr)\n-\n-\n-def _write_defer_restore(cfg, path, val, defer_undo=False):\n- with open(path, \"r\", encoding=\"utf-8\") as fp:\n- orig_val = fp.read().strip()\n- if str(val) == orig_val:\n- return\n- with open(path, \"w\", encoding=\"utf-8\") as fp:\n- fp.write(val)\n- if defer_undo:\n- defer(_write_defer_restore, cfg, path, orig_val)\n-\n-\n-def _set_mtu_restore(dev, mtu, host):\n- if dev['mtu'] \u003c mtu:\n- ip(f\"link set dev {dev['ifname']} mtu {mtu}\", host=host)\n- defer(ip, f\"link set dev {dev['ifname']} mtu {dev['mtu']}\", host=host)\n-\n-\n-def _set_ethtool_feat(dev, current, feats, host=None):\n- s2n = {True: \"on\", False: \"off\"}\n-\n- new = [\"-K\", dev]\n- old = [\"-K\", dev]\n- no_change = True\n- for name, state in feats.items():\n- new += [name, s2n[state]]\n- old += [name, s2n[current[name][\"active\"]]]\n-\n- if current[name][\"active\"] != state:\n- no_change = False\n- if current[name][\"fixed\"]:\n- raise KsftXfailEx(f\"Device does not support {name}\")\n- if no_change:\n- return\n-\n- eth_cmd = ethtool(\" \".join(new), host=host)\n- defer(ethtool, \" \".join(old), host=host)\n-\n- # If ethtool printed something kernel must have modified some features\n- if eth_cmd.stdout:\n- ksft_pr(eth_cmd)\n-\n-\n def _get_queue_stats(cfg, queue_id):\n \"\"\"Get stats for a specific Rx queue.\"\"\"\n cfg.wait_hw_stats_settle()\n@@ -182,47 +112,6 @@ def _setup_queue_count(cfg, num_queues):\n ethtool(f\"-L {cfg.ifname} combined {num_queues}\")\n \n \n-def _run_gro_bin(cfg, test_name, protocol=None, num_flows=None,\n- order_check=False, verbose=False, fail=False):\n- \"\"\"Run gro binary with given test and return the process result.\"\"\"\n- if not hasattr(cfg, \"bin_remote\"):\n- cfg.bin_local = cfg.net_lib_dir / \"gro\"\n- cfg.bin_remote = cfg.remote.deploy(cfg.bin_local)\n-\n- if protocol is None:\n- ipver = cfg.addr_ipver\n- protocol = f\"ipv{ipver}\"\n- else:\n- ipver = \"6\" if protocol[-1] == \"6\" else \"4\"\n-\n- dmac = _resolve_dmac(cfg, ipver)\n-\n- base_args = [\n- f\"--{protocol}\",\n- f\"--dmac {dmac}\",\n- f\"--smac {cfg.remote_dev['address']}\",\n- f\"--daddr {cfg.addr_v[ipver]}\",\n- f\"--saddr {cfg.remote_addr_v[ipver]}\",\n- f\"--test {test_name}\",\n- ]\n- if num_flows:\n- base_args.append(f\"--num-flows {num_flows}\")\n- if order_check:\n- base_args.append(\"--order-check\")\n- if verbose:\n- base_args.append(\"--verbose\")\n-\n- args = \" \".join(base_args)\n-\n- rx_cmd = f\"{cfg.bin_local} {args} --rx --iface {cfg.ifname}\"\n- tx_cmd = f\"{cfg.bin_remote} {args} --iface {cfg.remote_ifname}\"\n-\n- with bkg(rx_cmd, ksft_ready=True, exit_wait=True, fail=fail) as rx_proc:\n- cmd(tx_cmd, host=cfg.remote)\n-\n- return rx_proc\n-\n-\n def _setup(cfg, mode, test_name):\n \"\"\" Setup hardware loopback mode for GRO testing. \"\"\"\n \n@@ -237,60 +126,39 @@ def _setup(cfg, mode, test_name):\n \n # \"large_*\" tests need at least 4k MTU\n if test_name.startswith(\"large_\"):\n- _set_mtu_restore(cfg.dev, 4096, None)\n- _set_mtu_restore(cfg.remote_dev, 4096, cfg.remote)\n+ set_mtu_restore(cfg.dev, 4096, None)\n+ set_mtu_restore(cfg.remote_dev, 4096, cfg.remote)\n \n if mode == \"sw\":\n flush_path = f\"/sys/class/net/{cfg.ifname}/gro_flush_timeout\"\n irq_path = f\"/sys/class/net/{cfg.ifname}/napi_defer_hard_irqs\"\n \n- _write_defer_restore(cfg, flush_path, \"200000\", defer_undo=True)\n- _write_defer_restore(cfg, irq_path, \"10\", defer_undo=True)\n+ write_defer_restore(cfg, flush_path, \"200000\", defer_undo=True)\n+ write_defer_restore(cfg, irq_path, \"10\", defer_undo=True)\n \n- _set_ethtool_feat(cfg.ifname, cfg.feat,\n- {\"generic-receive-offload\": True,\n- \"rx-gro-hw\": False,\n- \"large-receive-offload\": False})\n+ set_ethtool_feat(cfg.ifname, cfg.feat,\n+ {\"generic-receive-offload\": True,\n+ \"rx-gro-hw\": False,\n+ \"large-receive-offload\": False})\n elif mode == \"hw\":\n- _set_ethtool_feat(cfg.ifname, cfg.feat,\n- {\"generic-receive-offload\": False,\n- \"rx-gro-hw\": True,\n- \"large-receive-offload\": False})\n-\n- # Some NICs treat HW GRO as a GRO sub-feature so disabling GRO\n- # will also clear HW GRO. Use a hack of installing XDP generic\n- # to skip SW GRO, even when enabled.\n- feat = ethtool(f\"-k {cfg.ifname}\", json=True)[0]\n- if not feat[\"rx-gro-hw\"][\"active\"]:\n- ksft_pr(\"Driver clears HW GRO and SW GRO is cleared, using generic XDP workaround\")\n- prog = cfg.net_lib_dir / \"xdp_dummy.bpf.o\"\n- ip(f\"link set dev {cfg.ifname} xdpgeneric obj {prog} sec xdp\")\n- defer(ip, f\"link set dev {cfg.ifname} xdpgeneric off\")\n-\n- # Attaching XDP may change features, fetch the latest state\n- feat = ethtool(f\"-k {cfg.ifname}\", json=True)[0]\n-\n- _set_ethtool_feat(cfg.ifname, feat,\n- {\"generic-receive-offload\": True,\n- \"rx-gro-hw\": True,\n- \"large-receive-offload\": False})\n+ setup_hw_gro(cfg)\n elif mode == \"lro\":\n # netdevsim advertises LRO for feature inheritance testing with\n # bonding/team tests but it doesn't actually perform the offload\n cfg.require_nsim(nsim_test=False)\n \n- _set_ethtool_feat(cfg.ifname, cfg.feat,\n- {\"generic-receive-offload\": False,\n- \"rx-gro-hw\": False,\n- \"large-receive-offload\": True})\n+ set_ethtool_feat(cfg.ifname, cfg.feat,\n+ {\"generic-receive-offload\": False,\n+ \"rx-gro-hw\": False,\n+ \"large-receive-offload\": True})\n \n try:\n # Disable TSO for local tests\n cfg.require_nsim() # will raise KsftXfailEx if not running on nsim\n \n- _set_ethtool_feat(cfg.remote_ifname, cfg.remote_feat,\n- {\"tcp-segmentation-offload\": False},\n- host=cfg.remote)\n+ set_ethtool_feat(cfg.remote_ifname, cfg.remote_feat,\n+ {\"tcp-segmentation-offload\": False},\n+ host=cfg.remote)\n except KsftXfailEx:\n pass\n \n@@ -356,31 +224,7 @@ def test(cfg, mode, protocol, test_name):\n \n _setup(cfg, mode, test_name)\n \n- # Each test is run 6 times to deflake, because given the receive timing,\n- # not all packets that should coalesce will be considered in the same flow\n- # on every try.\n- max_retries = 6\n- for attempt in range(max_retries):\n- fail_now = attempt \u003e= max_retries - 1\n- rx_proc = _run_gro_bin(cfg, test_name, protocol=protocol,\n- verbose=True, fail=fail_now)\n-\n- if rx_proc.ret == 0:\n- return\n-\n- ksft_pr(rx_proc)\n-\n- # ret==42 means the receiver detected over-coalescing.\n- # This is unambiguous proof of a bug, retries can only cause\n- # false negatives.\n- if rx_proc.ret == 42:\n- raise KsftFailEx(f\"GRO over-coalesced in {protocol}/{test_name}\")\n-\n- if test_name.startswith(\"large_\") and os.environ.get(\"KSFT_MACHINE_SLOW\"):\n- ksft_pr(f\"Ignoring {protocol}/{test_name} failure due to slow environment\")\n- return\n-\n- ksft_pr(f\"Attempt {attempt + 1}/{max_retries} failed, retrying...\")\n+ run_with_retries(cfg, test_name, protocol=protocol, verbose=True)\n \n \n def _capacity_variants():\n@@ -420,7 +264,7 @@ def test_gro_capacity(cfg, mode, setup_func):\n if queue_id is not None:\n stats_before = _get_queue_stats(cfg, queue_id)\n \n- rx_proc = _run_gro_bin(cfg, \"capacity\", num_flows=num_flows)\n+ rx_proc = run_gro_bin(cfg, \"capacity\", num_flows=num_flows)\n output = rx_proc.stdout\n \n if queue_id is not None:\ndiff --git a/tools/testing/selftests/drivers/net/gro_lib.py b/tools/testing/selftests/drivers/net/gro_lib.py\nnew file mode 100644\nindex 0000000000000..519986028980c\n--- /dev/null\n+++ b/tools/testing/selftests/drivers/net/gro_lib.py\n@@ -0,0 +1,204 @@\n+# SPDX-License-Identifier: GPL-2.0\n+\n+\"\"\"Shared helpers for the GRO selftests.\"\"\"\n+\n+import os\n+\n+from lib.py import ksft_pr\n+from lib.py import KsftFailEx, KsftXfailEx\n+from lib.py import bkg, cmd, defer, ethtool, ip\n+\n+\n+# gro.c exits with this code when it detects over-coalescing\n+EXIT_OVER_COALESCE = 42\n+\n+\n+def resolve_dmac(cfg, ipver):\n+ \"\"\"\n+ Finds the destination MAC address remote host should use to send packets\n+ towards the local host. It may be a router / gateway address.\n+ \"\"\"\n+\n+ attr = \"dmac\" + ipver\n+ # Cache the response across test cases\n+ if hasattr(cfg, attr):\n+ return getattr(cfg, attr)\n+\n+ route = ip(f\"-{ipver} route get {cfg.addr_v[ipver]}\",\n+ json=True, host=cfg.remote)[0]\n+ gw = route.get(\"gateway\")\n+ # Local L2 segment, address directly\n+ if not gw:\n+ setattr(cfg, attr, cfg.dev['address'])\n+ return getattr(cfg, attr)\n+\n+ # ping to make sure neighbor is resolved,\n+ # bind to an interface, for v6 the GW is likely link local\n+ cmd(f\"ping -c1 -W0 -I{cfg.remote_ifname} {gw}\", host=cfg.remote)\n+\n+ neigh = ip(f\"neigh get {gw} dev {cfg.remote_ifname}\",\n+ json=True, host=cfg.remote)[0]\n+ setattr(cfg, attr, neigh['lladdr'])\n+ return getattr(cfg, attr)\n+\n+\n+def write_defer_restore(cfg, path, val, defer_undo=False):\n+ \"\"\"Writes val to a sysfs file, optionally restoring it on test exit.\"\"\"\n+ with open(path, \"r\", encoding=\"utf-8\") as fp:\n+ orig_val = fp.read().strip()\n+ if str(val) == orig_val:\n+ return\n+ with open(path, \"w\", encoding=\"utf-8\") as fp:\n+ fp.write(val)\n+ if defer_undo:\n+ defer(write_defer_restore, cfg, path, orig_val)\n+\n+\n+def set_mtu_restore(dev, mtu, host):\n+ \"\"\"Raises a device's MTU to at least mtu, restoring it on test exit.\"\"\"\n+ if dev['mtu'] \u003c mtu:\n+ ip(f\"link set dev {dev['ifname']} mtu {mtu}\", host=host)\n+ defer(ip, f\"link set dev {dev['ifname']} mtu {dev['mtu']}\", host=host)\n+\n+\n+def set_ethtool_feat(dev, current, feats, host=None):\n+ \"\"\"Sets ethtool features, restoring them on test exit.\n+\n+ current is the feature state as reported by \"ethtool -k\", xfail if a\n+ feature which needs changing is fixed.\n+ \"\"\"\n+ s2n = {True: \"on\", False: \"off\"}\n+\n+ new = [\"-K\", dev]\n+ old = [\"-K\", dev]\n+ no_change = True\n+ for name, state in feats.items():\n+ new += [name, s2n[state]]\n+ old += [name, s2n[current[name][\"active\"]]]\n+\n+ if current[name][\"active\"] != state:\n+ no_change = False\n+ if current[name][\"fixed\"]:\n+ raise KsftXfailEx(f\"Device does not support {name}\")\n+ if no_change:\n+ return\n+\n+ eth_cmd = ethtool(\" \".join(new), host=host)\n+ defer(ethtool, \" \".join(old), host=host)\n+\n+ # If ethtool printed something kernel must have modified some features\n+ if eth_cmd.stdout:\n+ ksft_pr(eth_cmd)\n+\n+\n+def setup_hw_gro(cfg):\n+ \"\"\"Turns on HW GRO and make sure SW GRO stays out of the way.\n+\n+ Expects cfg.feat to hold the local device's \"ethtool -k\" state.\n+ \"\"\"\n+ set_ethtool_feat(cfg.ifname, cfg.feat,\n+ {\"generic-receive-offload\": False,\n+ \"rx-gro-hw\": True,\n+ \"large-receive-offload\": False})\n+\n+ # Some NICs treat HW GRO as a GRO sub-feature so disabling GRO\n+ # will also clear HW GRO. Use a hack of installing XDP generic\n+ # to skip SW GRO, even when enabled.\n+ feat = ethtool(f\"-k {cfg.ifname}\", json=True)[0]\n+ if not feat[\"rx-gro-hw\"][\"active\"]:\n+ ksft_pr(\"Driver clears HW GRO and SW GRO is cleared, using generic XDP workaround\")\n+ prog = cfg.net_lib_dir / \"xdp_dummy.bpf.o\"\n+ ip(f\"link set dev {cfg.ifname} xdpgeneric obj {prog} sec xdp\")\n+ defer(ip, f\"link set dev {cfg.ifname} xdpgeneric off\")\n+\n+ # Attaching XDP may change features, fetch the latest state\n+ feat = ethtool(f\"-k {cfg.ifname}\", json=True)[0]\n+\n+ set_ethtool_feat(cfg.ifname, feat,\n+ {\"generic-receive-offload\": True,\n+ \"rx-gro-hw\": True,\n+ \"large-receive-offload\": False})\n+\n+\n+# pylint: disable=too-many-arguments,too-many-positional-arguments\n+# pylint: disable=too-many-locals\n+def run_gro_bin(cfg, test_name, protocol=None, num_flows=None,\n+ order_check=False, verbose=False, fail=False,\n+ common_args=None):\n+ \"\"\"Runs gro binary with given test and return the process result.\n+\n+ common_args is a list of extra arguments passed to both ends.\n+ \"\"\"\n+ if not hasattr(cfg, \"bin_remote\"):\n+ cfg.bin_local = cfg.net_lib_dir / \"gro\"\n+ cfg.bin_remote = cfg.remote.deploy(cfg.bin_local)\n+\n+ if protocol is None:\n+ ipver = cfg.addr_ipver\n+ protocol = f\"ipv{ipver}\"\n+ else:\n+ ipver = \"6\" if protocol[-1] == \"6\" else \"4\"\n+\n+ dmac = resolve_dmac(cfg, ipver)\n+\n+ base_args = [\n+ f\"--{protocol}\",\n+ f\"--dmac {dmac}\",\n+ f\"--smac {cfg.remote_dev['address']}\",\n+ f\"--daddr {cfg.addr_v[ipver]}\",\n+ f\"--saddr {cfg.remote_addr_v[ipver]}\",\n+ f\"--test {test_name}\",\n+ ]\n+ if num_flows:\n+ base_args.append(f\"--num-flows {num_flows}\")\n+ if order_check:\n+ base_args.append(\"--order-check\")\n+ if verbose:\n+ base_args.append(\"--verbose\")\n+ if common_args:\n+ base_args += common_args\n+\n+ args = \" \".join(base_args)\n+\n+ rx_cmd = f\"{cfg.bin_local} {args} --rx --iface {cfg.ifname}\"\n+ tx_cmd = f\"{cfg.bin_remote} {args} --iface {cfg.remote_ifname}\"\n+\n+ with bkg(rx_cmd, ksft_ready=True, exit_wait=True, fail=fail) as rx_proc:\n+ cmd(tx_cmd, host=cfg.remote)\n+\n+ return rx_proc\n+\n+\n+def run_with_retries(cfg, test_name, protocol=None, **kwargs):\n+ \"\"\"Runs a single gro test case, retrying to deflake it.\n+\n+ Each test is run 6 times, because given the receive timing, not all\n+ packets that should coalesce will be considered in the same flow on\n+ every try. Over-coalescing is a hard failure, retries can only\n+ cause false negatives there.\n+\n+ Extra keyword arguments (num_flows, order_check, verbose,\n+ common_args) are forwarded to run_gro_bin().\n+ \"\"\"\n+ max_retries = 6\n+ for attempt in range(max_retries):\n+ fail_now = attempt \u003e= max_retries - 1\n+ rx_proc = run_gro_bin(cfg, test_name, protocol=protocol,\n+ fail=fail_now, **kwargs)\n+\n+ if rx_proc.ret == 0:\n+ return\n+\n+ ksft_pr(rx_proc)\n+\n+ # ret==42 means the receiver detected over-coalescing.\n+ # This is unambiguous proof of a bug, retries can only cause\n+ # false negatives.\n+ if rx_proc.ret == EXIT_OVER_COALESCE:\n+ raise KsftFailEx(f\"GRO over-coalesced in {protocol}/{test_name}\")\n+\n+ if test_name.startswith(\"large_\") and os.environ.get(\"KSFT_MACHINE_SLOW\"):\n+ ksft_pr(f\"Ignoring {protocol}/{test_name} failure due to slow environment\")\n+ return\n+\n+ ksft_pr(f\"Attempt {attempt + 1}/{max_retries} failed, retrying...\")\ndiff --git a/tools/testing/selftests/drivers/net/hw/Makefile b/tools/testing/selftests/drivers/net/hw/Makefile\nindex 78bb0169350b2..0a7851846708a 100644\n--- a/tools/testing/selftests/drivers/net/hw/Makefile\n+++ b/tools/testing/selftests/drivers/net/hw/Makefile\n@@ -13,6 +13,20 @@ else\n $(warning excluding iouring tests, liburing not installed or too old)\n endif\n \n+# psp_gro.py uses the gro binary in PSP mode, which requires OpenSSL.\n+PKG_CONFIG ?= pkg-config\n+HAVE_OPENSSL := $(shell echo 'int main(void) { return 0; }' | \\\n+\t\t $(CC) -x c - -include openssl/evp.h \\\n+\t\t $(shell $(PKG_CONFIG) --cflags --libs libcrypto 2\u003e/dev/null \\\n+\t\t\t || echo -lcrypto) \\\n+\t\t -o /dev/null \u003e/dev/null 2\u003e\u00261 \u0026\u0026 echo 1)\n+\n+ifeq ($(HAVE_OPENSSL),1)\n+COND_PROGS += psp_gro.py\n+else\n+$(warning excluding PSP GRO tests, libcrypto not installed)\n+endif\n+\n TEST_GEN_FILES := \\\n \t$(COND_GEN_FILES) \\\n # end of TEST_GEN_FILES\n@@ -40,6 +54,7 @@ TEST_PROGS = \\\n \tnk_qlease.py \\\n \tntuple.py \\\n \tpp_alloc_fail.py \\\n+\t$(COND_PROGS) \\\n \trss_api.py \\\n \trss_ctx.py \\\n \trss_drv.py \\\n@@ -64,6 +79,8 @@ TEST_FILES := \\\n \n TEST_INCLUDES := \\\n \t$(wildcard lib/py/*.py ../lib/py/*.py) \\\n+\t../gro_lib.py \\\n+\t../psp_lib.py \\\n \t../../../net/lib.sh \\\n \t../../../net/forwarding/ipip_lib.sh \\\n \t../../../net/forwarding/lib.sh \\\ndiff --git a/tools/testing/selftests/drivers/net/hw/psp_gro.py b/tools/testing/selftests/drivers/net/hw/psp_gro.py\nnew file mode 100755\nindex 0000000000000..9af80a022a17a\n--- /dev/null\n+++ b/tools/testing/selftests/drivers/net/hw/psp_gro.py\n@@ -0,0 +1,172 @@\n+#!/usr/bin/env python3\n+# SPDX-License-Identifier: GPL-2.0\n+\n+\"\"\"\n+PSP conformance tests built on the GRO test binary.\n+\n+This reuses the gro binary in --psp mode:\n+The sender crafts encapsulated \u0026 sw-encrypted PSP packets with the receiver PSP\n+rx-assoc, and the receiver device decrypts and decapsulates the packets before\n+an AF_PACKET tap gets to analyze them.\n+\n+All tests from gro.py which could run with PSP are included.\n+\"\"\"\n+\n+import socket\n+\n+from lib.py import ksft_run, ksft_exit\n+from lib.py import ksft_variants, KsftNamedVariant\n+from lib.py import KsftSkipEx\n+from lib.py import NetDrvEpEnv, PSPFamily\n+from lib.py import defer, ethtool\n+\n+# The helpers live next to their other users, gro.py and psp.py. Importing\n+# lib.py above puts the selftests root on sys.path, which is what makes these\n+# two resolve, so they have to stay after it - pylint wants the opposite.\n+# pylint: disable=wrong-import-order,import-error\n+from drivers.net.gro_lib import run_with_retries, set_mtu_restore, setup_hw_gro\n+from drivers.net.psp_lib import init_psp_dev, require_version\n+# pylint: enable=wrong-import-order,import-error\n+\n+\n+# Tests for both IP versions, a subset of gro.py. Missing:\n+# - tcp_csum: PSP packets are validated by HW (with the ICV) and marked with\n+# CHECKSUM_UNNECESSARY.\n+_COMMON = [\"data_same\", \"data_lrg_sml\", \"data_sml_lrg\", \"data_lrg_1byte\",\n+ \"data_burst\",\n+ \"ack\",\n+ \"flags_psh\", \"flags_syn\", \"flags_rst\", \"flags_urg\", \"flags_cwr\",\n+ \"tcp_seq\", \"tcp_ts\", \"tcp_opt\",\n+ \"ip_ecn\", \"ip_tos\",\n+ \"large_max\", \"large_rem\",\n+]\n+\n+# Tests specific to IPv4, a subset of gro.py. Missing:\n+# - ip_csum: psp_dev_rcv() recomputes the IP checksum.\n+# - ip_frag4: PSP is incompatible with IP fragmentation.\n+_V4 = [\"ip_ttl\", \"ip_opt\",\n+ \"ip_id_df1_inc\", \"ip_id_df1_fixed\",\n+ \"ip_id_df0_inc\", \"ip_id_df0_fixed\",\n+ \"ip_id_df1_inc_fixed\", \"ip_id_df1_fixed_inc\",\n+]\n+\n+# No IPv6-only tests:\n+# - ip_frag6: PSP doesn't support fragmentation\n+# - ip_v6ext_same, ip_v6ext_diff: PSP doesn't support IPv6 ext headers.\n+\n+\n+def _psp_variants():\n+ for ver in range(4):\n+ for proto in [\"ipv4\", \"ipv6\"]:\n+ for test_name in _COMMON + (_V4 if proto == \"ipv4\" else []):\n+ yield KsftNamedVariant(f\"v{ver}_{proto}_{test_name}\",\n+ ver, proto, test_name)\n+\n+\n+def _ip_variants():\n+ \"\"\"IPv4/IPv6.\"\"\"\n+ for proto in (\"ipv4\", \"ipv6\"):\n+ yield KsftNamedVariant(proto, proto)\n+\n+\n+def _psp_assoc(cfg, version=0):\n+ # This socket receives no traffic, exists solely to own the rx assoc.\n+ s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)\n+ defer(s.close)\n+ return cfg.pspnl.rx_assoc({\"version\": version, \"dev-id\": cfg.psp_dev_id,\n+ \"sock-fd\": s.fileno()})['rx-key']\n+\n+\n+def _setup(cfg, version, test_name):\n+ \"\"\"Enables PSP and HW GRO on the device under test.\"\"\"\n+ init_psp_dev(cfg)\n+ require_version(cfg, version)\n+\n+ if not hasattr(cfg, \"feat\"):\n+ cfg.feat = ethtool(f\"-k {cfg.ifname}\", json=True)[0]\n+\n+ setup_hw_gro(cfg)\n+\n+ # \"large_*\" tests need at least 4k MTU\n+ if test_name.startswith(\"large_\"):\n+ set_mtu_restore(cfg.dev, 4096, None)\n+ set_mtu_restore(cfg.remote_dev, 4096, cfg.remote)\n+\n+\n+def _psp_args(cfg, versions):\n+ \"\"\"Produces PSP associations as gro binary --psp-assoc arguments.\"\"\"\n+ keys = [_psp_assoc(cfg, ver) for ver in versions]\n+ return [f\"--psp-assoc {ver},{key['spi']:x},{key['key'].hex()}\"\n+ for ver, key in zip(versions, keys)]\n+\n+\n+def _run(cfg, test_name, protocol, versions):\n+ \"\"\"Sets up, associates and runs one gro test case under PSP.\"\"\"\n+ ipver = protocol[-1]\n+ cfg.require_ipver(ipver)\n+\n+ _setup(cfg, max(versions), test_name)\n+\n+ run_with_retries(cfg, test_name, protocol=protocol, verbose=True,\n+ common_args=_psp_args(cfg, versions))\n+\n+\n+@ksft_variants(_psp_variants())\n+def test_psp_gro(cfg, version, protocol, test_name):\n+ \"\"\"Runs one gro conformance case with PSP encapsulation.\"\"\"\n+ _run(cfg, test_name, protocol, [version])\n+\n+\n+# PSP-specific GRO tests\n+\n+@ksft_variants(_ip_variants())\n+def test_psp_spi_diff(cfg, protocol):\n+ \"\"\"Frames from two different SPIs must not coalesce.\"\"\"\n+ _run(cfg, \"psp_spi_diff\", protocol, [0, 0])\n+\n+\n+@ksft_variants(_ip_variants())\n+def test_psp_ver_diff(cfg, protocol):\n+ \"\"\"Frames from two different PSP versions must not coalesce.\"\"\"\n+ init_psp_dev(cfg)\n+ if len(cfg.psp_info['psp-versions-cap']) \u003c 2:\n+ raise KsftSkipEx(\"Device supports a single PSP version\")\n+\n+ _run(cfg, \"psp_ver_diff\", protocol, [0, 1])\n+\n+\n+@ksft_variants(_ip_variants())\n+def test_psp_mixed(cfg, protocol):\n+ \"\"\"A PSP frame must not coalesce with a clear text one.\"\"\"\n+ _run(cfg, \"psp_mixed\", protocol, [0])\n+\n+\n+@ksft_variants(_ip_variants())\n+def test_psp_after_reconfig(cfg, protocol):\n+ \"\"\"Verifies that decap still works after PSP off + on.\"\"\"\n+ cfg.require_ipver(protocol[-1])\n+\n+ _setup(cfg, 0, \"data_same\")\n+\n+ cap = cfg.psp_info['psp-versions-cap']\n+ cfg.pspnl.dev_set({'id': cfg.psp_dev_id, 'psp-versions-ena': []})\n+ cfg.pspnl.dev_set({'id': cfg.psp_dev_id, 'psp-versions-ena': cap})\n+\n+ run_with_retries(cfg, \"data_same\", protocol=protocol, verbose=True,\n+ common_args=_psp_args(cfg, [0]))\n+\n+\n+def main() -\u003e None:\n+ \"\"\" Ksft boiler plate main \"\"\"\n+\n+ with NetDrvEpEnv(__file__, nsim_test=False) as cfg:\n+ cfg.pspnl = PSPFamily()\n+\n+ ksft_run(cases=[test_psp_gro, test_psp_spi_diff, test_psp_ver_diff,\n+ test_psp_mixed, test_psp_after_reconfig],\n+ args=(cfg, ))\n+ ksft_exit()\n+\n+\n+if __name__ == \"__main__\":\n+ main()\ndiff --git a/tools/testing/selftests/drivers/net/psp.py b/tools/testing/selftests/drivers/net/psp.py\nindex 315648a770d00..47983fb8c7c22 100755\n--- a/tools/testing/selftests/drivers/net/psp.py\n+++ b/tools/testing/selftests/drivers/net/psp.py\n@@ -11,6 +11,8 @@ import struct\n import termios\n import time\n \n+from psp_lib import init_psp_dev, require_version\n+\n from lib.py import defer\n from lib.py import ksft_run, ksft_exit, ksft_pr\n from lib.py import ksft_true, ksft_eq, ksft_ne, ksft_gt, ksft_raises\n@@ -122,34 +124,13 @@ def _get_stat(cfg, key):\n # Test case boiler plate\n #\n \n-def _init_psp_dev(cfg, use_psp_ifindex=False):\n- if not hasattr(cfg, 'psp_dev_id'):\n- # Figure out which local device we are testing against\n- # For NetDrvContEnv: use psp_ifindex instead of ifindex\n- target_ifindex = cfg.psp_ifindex if use_psp_ifindex else cfg.ifindex\n- for dev in cfg.pspnl.dev_get({}, dump=True):\n- if dev['ifindex'] == target_ifindex:\n- cfg.psp_info = dev\n- cfg.psp_dev_id = cfg.psp_info['id']\n- break\n- else:\n- raise KsftSkipEx(\"No PSP devices found\")\n-\n- # Enable PSP if necessary\n- cap = cfg.psp_info['psp-versions-cap']\n- ena = cfg.psp_info['psp-versions-ena']\n- if cap != ena:\n- cfg.pspnl.dev_set({'id': cfg.psp_dev_id, 'psp-versions-ena': cap})\n- defer(cfg.pspnl.dev_set, {'id': cfg.psp_dev_id,\n- 'psp-versions-ena': ena })\n-\n #\n # Test cases\n #\n \n def dev_list_devices(cfg):\n \"\"\" Dump all devices \"\"\"\n- _init_psp_dev(cfg)\n+ init_psp_dev(cfg)\n \n devices = cfg.pspnl.dev_get({}, dump=True)\n \n@@ -161,7 +142,7 @@ def dev_list_devices(cfg):\n \n def dev_get_device(cfg):\n \"\"\" Get the device we intend to use \"\"\"\n- _init_psp_dev(cfg)\n+ init_psp_dev(cfg)\n \n dev = cfg.pspnl.dev_get({'id': cfg.psp_dev_id})\n ksft_eq(dev['id'], cfg.psp_dev_id)\n@@ -180,7 +161,7 @@ def dev_get_device_bad(cfg):\n \n def dev_rotate(cfg):\n \"\"\" Test key rotation \"\"\"\n- _init_psp_dev(cfg)\n+ init_psp_dev(cfg)\n \n prev_rotations = _get_stat(cfg, 'key-rotations')\n \n@@ -195,7 +176,7 @@ def dev_rotate(cfg):\n \n def dev_rotate_spi(cfg):\n \"\"\" Test key rotation and SPI check \"\"\"\n- _init_psp_dev(cfg)\n+ init_psp_dev(cfg)\n \n top_a = top_b = 0\n with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:\n@@ -217,7 +198,7 @@ def dev_rotate_spi(cfg):\n \n def assoc_basic(cfg):\n \"\"\" Test creating associations \"\"\"\n- _init_psp_dev(cfg)\n+ init_psp_dev(cfg)\n \n with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:\n assoc = cfg.pspnl.rx_assoc({\"version\": 0,\n@@ -237,7 +218,7 @@ def assoc_basic(cfg):\n \n def assoc_bad_dev(cfg):\n \"\"\" Test creating associations with bad device ID \"\"\"\n- _init_psp_dev(cfg)\n+ init_psp_dev(cfg)\n \n with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:\n with ksft_raises(NlError) as cm:\n@@ -249,7 +230,7 @@ def assoc_bad_dev(cfg):\n \n def assoc_sk_only_conn(cfg):\n \"\"\" Test creating associations based on socket \"\"\"\n- _init_psp_dev(cfg)\n+ init_psp_dev(cfg)\n \n with _make_clr_conn(cfg) as s:\n assoc = cfg.pspnl.rx_assoc({\"version\": 0,\n@@ -263,7 +244,7 @@ def assoc_sk_only_conn(cfg):\n \n def assoc_sk_only_mismatch(cfg):\n \"\"\" Test creating associations based on socket (dev mismatch) \"\"\"\n- _init_psp_dev(cfg)\n+ init_psp_dev(cfg)\n \n with _make_clr_conn(cfg) as s:\n with ksft_raises(NlError) as cm:\n@@ -278,7 +259,7 @@ def assoc_sk_only_mismatch(cfg):\n \n def assoc_sk_only_mismatch_tx(cfg):\n \"\"\" Test creating associations based on socket (dev mismatch) \"\"\"\n- _init_psp_dev(cfg)\n+ init_psp_dev(cfg)\n \n with _make_clr_conn(cfg) as s:\n with ksft_raises(NlError) as cm:\n@@ -296,7 +277,7 @@ def assoc_sk_only_mismatch_tx(cfg):\n \n def assoc_sk_only_unconn(cfg):\n \"\"\" Test creating associations based on socket (unconnected, should fail) \"\"\"\n- _init_psp_dev(cfg)\n+ init_psp_dev(cfg)\n \n with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:\n with ksft_raises(NlError) as cm:\n@@ -309,7 +290,7 @@ def assoc_sk_only_unconn(cfg):\n \n def assoc_version_mismatch(cfg):\n \"\"\" Test creating associations where Rx and Tx PSP versions do not match \"\"\"\n- _init_psp_dev(cfg)\n+ init_psp_dev(cfg)\n \n versions = list(cfg.psp_info['psp-versions-cap'])\n if len(versions) \u003c 2:\n@@ -335,7 +316,7 @@ def assoc_version_mismatch(cfg):\n \n def assoc_twice(cfg):\n \"\"\" Test reusing Tx assoc for two sockets \"\"\"\n- _init_psp_dev(cfg)\n+ init_psp_dev(cfg)\n \n def rx_assoc_check(s):\n assoc = cfg.pspnl.rx_assoc({\"version\": 0,\n@@ -369,19 +350,9 @@ def assoc_twice(cfg):\n \n def _data_basic_send(cfg, version, ipver):\n \"\"\" Test basic data send \"\"\"\n- _init_psp_dev(cfg)\n-\n- # Version 0 is required by spec, don't let it skip\n- if version:\n- name = cfg.pspnl.consts[\"version\"].entries_by_val[version].name\n- if name not in cfg.psp_info['psp-versions-cap']:\n- with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:\n- with ksft_raises(NlError) as cm:\n- cfg.pspnl.rx_assoc({\"version\": version,\n- \"dev-id\": cfg.psp_dev_id,\n- \"sock-fd\": s.fileno()})\n- ksft_eq(cm.exception.nl_msg.error, -errno.EOPNOTSUPP)\n- raise KsftSkipEx(\"PSP version not supported\", name)\n+ init_psp_dev(cfg)\n+\n+ require_version(cfg, version)\n \n s = _make_psp_conn(cfg, version, ipver)\n \n@@ -418,7 +389,7 @@ def __bad_xfer_do(cfg, s, tx, version='hdr0-aes-gcm-128'):\n \n def data_send_bad_key(cfg):\n \"\"\" Test send data with bad key \"\"\"\n- _init_psp_dev(cfg)\n+ init_psp_dev(cfg)\n \n s = _make_psp_conn(cfg)\n \n@@ -433,7 +404,7 @@ def data_send_bad_key(cfg):\n \n def data_send_disconnect(cfg):\n \"\"\" Test socket close after sending data \"\"\"\n- _init_psp_dev(cfg)\n+ init_psp_dev(cfg)\n \n with _make_psp_conn(cfg) as s:\n assoc = cfg.pspnl.rx_assoc({\"version\": 0,\n@@ -451,7 +422,7 @@ def data_send_disconnect(cfg):\n \n \n def _data_mss_adjust(cfg, ipver):\n- _init_psp_dev(cfg)\n+ init_psp_dev(cfg)\n \n # First figure out what the MSS would be without any adjustments\n s = _make_clr_conn(cfg, ipver)\n@@ -491,7 +462,7 @@ def _data_mss_adjust(cfg, ipver):\n \n def data_stale_key(cfg):\n \"\"\" Test send on a double-rotated key \"\"\"\n- _init_psp_dev(cfg)\n+ init_psp_dev(cfg)\n \n prev_stale = _get_stat(cfg, 'stale-events')\n s = _make_psp_conn(cfg)\n@@ -766,7 +737,7 @@ def _psp_dev_get_check_netkit_psp_assoc(cfg):\n \n def _dev_assoc_no_nsid(cfg):\n \"\"\" Test dev-assoc and dev-disassoc without nsid attribute \"\"\"\n- _init_psp_dev(cfg, True)\n+ init_psp_dev(cfg, True)\n \n # Associate without nsid - should look up ifindex in caller's netns\n cfg.pspnl.dev_assoc({'id': cfg.psp_dev_id,\n@@ -800,7 +771,7 @@ def _psp_dev_assoc_cleanup_on_netkit_del(cfg):\n Creates a disposable netkit pair for this test to avoid destroying\n the shared environment.\n \"\"\"\n- _init_psp_dev(cfg, True)\n+ init_psp_dev(cfg, True)\n defer(delattr, cfg, 'psp_dev_id')\n defer(delattr, cfg, 'psp_info')\n \n@@ -877,7 +848,7 @@ def _try_disassoc(cfg, psp_dev_id, ifindex, nsid=None):\n \n def _assoc_nk_guest(cfg):\n \"\"\"Associate nk_guest with PSP device and register cleanup via defer().\"\"\"\n- _init_psp_dev(cfg, True)\n+ init_psp_dev(cfg, True)\n \n cfg.pspnl.dev_assoc({'id': cfg.psp_dev_id,\n 'ifindex': cfg.nk_guest_ifindex,\ndiff --git a/tools/testing/selftests/drivers/net/psp_lib.py b/tools/testing/selftests/drivers/net/psp_lib.py\nnew file mode 100644\nindex 0000000000000..d450db81cfc2a\n--- /dev/null\n+++ b/tools/testing/selftests/drivers/net/psp_lib.py\n@@ -0,0 +1,57 @@\n+# SPDX-License-Identifier: GPL-2.0\n+\n+\"\"\"Shared helpers for the PSP selftests.\"\"\"\n+\n+import errno\n+import socket\n+\n+from lib.py import defer\n+from lib.py import ksft_eq, ksft_raises\n+from lib.py import KsftSkipEx\n+from lib.py import NlError\n+\n+\n+def require_version(cfg, version):\n+ \"\"\"Skip unless the device supports a PSP version.\n+\n+ Version 0 is required by the spec, so it never skips. Before\n+ skipping, check that the kernel rejects the unsupported version\n+ properly - this is the only coverage that path gets.\n+ \"\"\"\n+ if not version:\n+ return\n+\n+ name = cfg.pspnl.consts[\"version\"].entries_by_val[version].name\n+ if name in cfg.psp_info['psp-versions-cap']:\n+ return\n+\n+ with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:\n+ with ksft_raises(NlError) as cm:\n+ cfg.pspnl.rx_assoc({\"version\": version,\n+ \"dev-id\": cfg.psp_dev_id,\n+ \"sock-fd\": s.fileno()})\n+ ksft_eq(cm.exception.nl_msg.error, -errno.EOPNOTSUPP)\n+ raise KsftSkipEx(\"PSP version not supported\", name)\n+\n+\n+def init_psp_dev(cfg, use_psp_ifindex=False):\n+ \"\"\"Find the PSP device under test and enable all supported versions.\"\"\"\n+ if not hasattr(cfg, 'psp_dev_id'):\n+ # Figure out which local device we are testing against\n+ # For NetDrvContEnv: use psp_ifindex instead of ifindex\n+ target_ifindex = cfg.psp_ifindex if use_psp_ifindex else cfg.ifindex\n+ for dev in cfg.pspnl.dev_get({}, dump=True):\n+ if dev['ifindex'] == target_ifindex:\n+ cfg.psp_info = dev\n+ cfg.psp_dev_id = cfg.psp_info['id']\n+ break\n+ else:\n+ raise KsftSkipEx(\"No PSP devices found\")\n+\n+ # Enable PSP if necessary\n+ cap = cfg.psp_info['psp-versions-cap']\n+ ena = cfg.psp_info['psp-versions-ena']\n+ if cap != ena:\n+ cfg.pspnl.dev_set({'id': cfg.psp_dev_id, 'psp-versions-ena': cap})\n+ defer(cfg.pspnl.dev_set, {'id': cfg.psp_dev_id,\n+ 'psp-versions-ena': ena})\ndiff --git a/tools/testing/selftests/net/lib/Makefile b/tools/testing/selftests/net/lib/Makefile\nindex ff83603397d06..ce67e8d3e4f6c 100644\n--- a/tools/testing/selftests/net/lib/Makefile\n+++ b/tools/testing/selftests/net/lib/Makefile\n@@ -22,4 +22,20 @@ TEST_INCLUDES := $(wildcard py/*.py sh/*.sh)\n \n include ../../lib.mk\n \n+# gro optionally needs OpenSSL for PSP.\n+PKG_CONFIG ?= pkg-config\n+LIBCRYPTO_CFLAGS := $(shell $(PKG_CONFIG) --cflags libcrypto 2\u003e/dev/null)\n+LIBCRYPTO_LIBS := $(shell $(PKG_CONFIG) --libs libcrypto 2\u003e/dev/null || \\\n+\t\t echo -lcrypto)\n+HAVE_OPENSSL := $(shell echo 'int main(void) { return 0; }' | \\\n+\t\t $(CC) -x c - -include openssl/evp.h $(LIBCRYPTO_CFLAGS) \\\n+\t\t $(LIBCRYPTO_LIBS) -o /dev/null \u003e/dev/null 2\u003e\u00261 \u0026\u0026 echo 1)\n+\n+ifeq ($(HAVE_OPENSSL),1)\n+$(OUTPUT)/gro: CFLAGS += -DHAVE_OPENSSL $(LIBCRYPTO_CFLAGS)\n+$(OUTPUT)/gro: LDLIBS += $(LIBCRYPTO_LIBS)\n+else\n+$(warning libcrypto not found, gro will be built without PSP support)\n+endif\n+\n include ../bpf.mk\ndiff --git a/tools/testing/selftests/net/lib/gro.c b/tools/testing/selftests/net/lib/gro.c\nindex 7a333155de1ab..25f54eed601f5 100644\n--- a/tools/testing/selftests/net/lib/gro.c\n+++ b/tools/testing/selftests/net/lib/gro.c\n@@ -64,6 +64,7 @@\n #define _GNU_SOURCE\n \n #include \u003carpa/inet.h\u003e\n+#include \u003cendian.h\u003e\n #include \u003cerrno.h\u003e\n #include \u003cerror.h\u003e\n #include \u003cgetopt.h\u003e\n@@ -79,14 +80,22 @@\n #include \u003cnetinet/ip.h\u003e\n #include \u003cnetinet/ip6.h\u003e\n #include \u003cnetinet/tcp.h\u003e\n+#include \u003cnetinet/udp.h\u003e\n #include \u003cstdbool.h\u003e\n #include \u003cstddef.h\u003e\n #include \u003cstdio.h\u003e\n #include \u003cstdarg.h\u003e\n+#include \u003cstdlib.h\u003e\n #include \u003cstring.h\u003e\n #include \u003ctime.h\u003e\n #include \u003cunistd.h\u003e\n \n+#include \u003clinux/psp.h\u003e\n+\n+#ifdef HAVE_OPENSSL\n+#include \u003copenssl/evp.h\u003e\n+#endif\n+\n #include \"kselftest.h\"\n #include \"ksft.h\"\n \n@@ -110,6 +119,38 @@\n \n #define EXIT_OVER_COALESCE\t42\n \n+/* PSP transport mode encapsulation, as built by the sender:\n+ *\n+ *\t[eth][IP][UDP dport=1000][PSP hdr][encrypted L4][ICV]\n+ *\n+ * Can't use the kernel-only include/net/psp/types.h header, so copy the\n+ * encoding here.\n+ */\n+struct psphdr {\n+\tuint8_t\t\tnexthdr;\n+\tuint8_t\t\thdrlen;\n+\tuint8_t\t\tcrypt_offset;\n+\tuint8_t\t\tverfl;\n+\tuint32_t\tspi;\t\t/* big endian */\n+\tuint64_t\tiv;\t\t/* big endian */\n+} __packed;\n+\n+#define PSP_UDP_PORT\t\t1000\n+#define PSP_UDP_LEN\t\tsizeof(struct udphdr)\n+#define PSP_HDR_LEN\t\tsizeof(struct psphdr)\n+#define PSP_ICV_LEN\t\t16\n+/* total frame growth: the inserted headers plus the trailing ICV */\n+#define PSP_ENCAP_LEN\t\t(PSP_UDP_LEN + PSP_HDR_LEN + PSP_ICV_LEN)\n+#define PSP_HDRLEN_NOOPT\t1\t/* (1 + 1) * 8 == PSP_HDR_LEN */\n+#define PSP_VERFL_ONE\t\t0x01\n+#define PSP_VERFL_VER_SHIFT\t2\n+#define PSP_MAX_ASSOC\t\t2\n+#define PSP_KEY_LEN_MAX\t\t32\n+\n+enum {\n+\tOPT_PSP_ASSOC = 256,\n+};\n+\n #define ipv6_optlen(p) (((p)-\u003ehdrlen+1) \u003c\u003c 3) /* calculate IPv6 extension header len */\n #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))\n \n@@ -143,6 +184,9 @@ static bool pppoe;\n static uint64_t txtime_ns;\n static int num_flows = 4;\n static bool order_check;\n+static bool psp_enabled;\n+static int psp_idx;\n+static int psp_num_assoc;\n \n #define CAPACITY_PAYLOAD_LEN 200\n \n@@ -158,7 +202,8 @@ static int max_payload(void)\n \n static int calc_mss(void)\n {\n-\treturn ASSUMED_MTU - (total_hdr_len - ETH_HLEN);\n+\treturn ASSUMED_MTU - (total_hdr_len - ETH_HLEN) -\n+\t\t(psp_enabled ? PSP_ENCAP_LEN : 0);\n }\n \n static int num_large_pkt(void)\n@@ -384,6 +429,215 @@ static void fill_transportlayer(void *buf, int seq_offset, int ack_offset,\n \ttcph-\u003echeck = tcp_checksum(tcph, payload_len);\n }\n \n+#ifdef HAVE_OPENSSL\n+\n+struct psp_assoc {\n+\tuint32_t\tspi;\n+\tuint8_t\t\tkey[PSP_KEY_LEN_MAX];\n+\tint\t\tkeylen;\n+\tint\t\tversion;\n+\tEVP_CIPHER_CTX\t*ctx;\n+};\n+\n+static struct psp_assoc psp_assocs[PSP_MAX_ASSOC];\n+static uint64_t psp_next_iv = 1;\n+\n+static char psp_scratch[L2_HLEN_MAX + IP_MAXPACKET + PSP_ENCAP_LEN];\n+\n+static int psp_key_len(int version)\n+{\n+\tswitch (version) {\n+\tcase PSP_VERSION_HDR0_AES_GCM_128:\n+\tcase PSP_VERSION_HDR0_AES_GMAC_128:\n+\t\treturn 16;\n+\tcase PSP_VERSION_HDR0_AES_GCM_256:\n+\tcase PSP_VERSION_HDR0_AES_GMAC_256:\n+\t\treturn 32;\n+\tdefault:\n+\t\terror(1, 0, \"psp: unknown version %d\", version);\n+\t\treturn 0;\n+\t}\n+}\n+\n+static bool psp_version_is_gmac(int version)\n+{\n+\treturn version == PSP_VERSION_HDR0_AES_GMAC_128 ||\n+\t version == PSP_VERSION_HDR0_AES_GMAC_256;\n+}\n+\n+/* Parses one --psp-assoc \"version,spi,key\".\n+ * The SPI and key (both hex) is a PSP RX assoc from the device under test.\n+ */\n+static void psp_parse_assoc(char *arg)\n+{\n+\tchar *ver_s, *spi_s, *key_s, *end;\n+\tconst EVP_CIPHER *cipher;\n+\tstruct psp_assoc *assoc;\n+\tunsigned long val;\n+\tsize_t len, i;\n+\n+\tif (psp_num_assoc == PSP_MAX_ASSOC)\n+\t\terror(1, 0, \"psp: at most %d associations\", PSP_MAX_ASSOC);\n+\tassoc = \u0026psp_assocs[psp_num_assoc];\n+\n+\tver_s = strtok(arg, \",\");\n+\tspi_s = strtok(NULL, \",\");\n+\tkey_s = strtok(NULL, \",\");\n+\tif (!ver_s || !spi_s || !key_s || strtok(NULL, \",\"))\n+\t\terror(1, 0, \"psp: --psp-assoc wants version,spi,key\");\n+\n+\terrno = 0;\n+\tval = strtoul(ver_s, \u0026end, 10);\n+\tif (errno || end == ver_s || *end ||\n+\t val \u003e PSP_VERSION_HDR0_AES_GMAC_256)\n+\t\terror(1, 0, \"psp: bad version '%s'\", ver_s);\n+\tassoc-\u003eversion = val;\n+\n+\terrno = 0;\n+\tval = strtoul(spi_s, \u0026end, 16);\n+\tif (errno || end == spi_s || *end || val \u003e 0xffffffffUL)\n+\t\terror(1, 0, \"psp: bad SPI '%s'\", spi_s);\n+\tassoc-\u003espi = val;\n+\n+\tlen = strlen(key_s);\n+\tif (len != (size_t)psp_key_len(assoc-\u003eversion) * 2 ||\n+\t strspn(key_s, \"0123456789abcdefABCDEF\") != len)\n+\t\terror(1, 0, \"psp: version %d needs a %d byte key, got '%s'\",\n+\t\t assoc-\u003eversion, psp_key_len(assoc-\u003eversion), key_s);\n+\n+\tassoc-\u003ekeylen = len / 2;\n+\tfor (i = 0; i \u003c len; i += 2) {\n+\t\tunsigned int byte;\n+\n+\t\tif (sscanf(key_s + i, \"%2x\", \u0026byte) != 1)\n+\t\t\terror(1, 0, \"psp: bad key '%s'\", key_s);\n+\t\tassoc-\u003ekey[i / 2] = byte;\n+\t}\n+\n+\tcipher = assoc-\u003ekeylen == 16 ? EVP_aes_128_gcm() : EVP_aes_256_gcm();\n+\tassoc-\u003ectx = EVP_CIPHER_CTX_new();\n+\tif (!assoc-\u003ectx)\n+\t\terror(1, 0, \"psp: EVP_CIPHER_CTX_new\");\n+\tif (EVP_EncryptInit_ex(assoc-\u003ectx, cipher, NULL, assoc-\u003ekey,\n+\t\t\t NULL) != 1)\n+\t\terror(1, 0, \"psp: EVP_EncryptInit_ex key\");\n+\tpsp_num_assoc++;\n+\tpsp_enabled = true;\n+}\n+\n+static void psp_crypt(struct psp_assoc *assoc, const uint8_t *nonce,\n+\t\t const uint8_t *aad, int aad_len,\n+\t\t uint8_t *data, int data_len, uint8_t *icv)\n+{\n+\tuint8_t *crypt_data = psp_version_is_gmac(assoc-\u003eversion) ? NULL : data;\n+\tEVP_CIPHER_CTX *ctx = assoc-\u003ectx;\n+\tint outl;\n+\n+\tif (EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, nonce) != 1)\n+\t\terror(1, 0, \"psp: EVP_EncryptInit_ex nonce\");\n+\tif (EVP_EncryptUpdate(ctx, NULL, \u0026outl, aad, aad_len) != 1)\n+\t\terror(1, 0, \"psp: EVP_EncryptUpdate aad\");\n+\tif (EVP_EncryptUpdate(ctx, crypt_data, \u0026outl, data, data_len) != 1)\n+\t\terror(1, 0, \"psp: EVP_EncryptUpdate data\");\n+\tif (EVP_EncryptFinal_ex(ctx, icv, \u0026outl) != 1)\n+\t\terror(1, 0, \"psp: EVP_EncryptFinal_ex\");\n+\tif (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, PSP_ICV_LEN,\n+\t\t\t\ticv) != 1)\n+\t\terror(1, 0, \"psp: EVP_CTRL_GCM_GET_TAG\");\n+}\n+\n+/* Encapsulates \u0026 encrypts @pkt with PSP transport mode into psp_scratch.\n+ * Returns the scratch buffer and updates *@lenp.\n+ */\n+static char *psp_encapsulate(const char *pkt, int *lenp)\n+{\n+\tstruct psp_assoc *assoc = \u0026psp_assocs[psp_idx];\n+\tint ip_hlen, l4_off, l4_len, aad_len;\n+\tstruct psphdr *psph;\n+\tstruct udphdr *udph;\n+\t__be16 sport, dport;\n+\tuint8_t *data, *icv;\n+\tint len = *lenp;\n+\tuint8_t nexthdr;\n+\n+\tif (len + PSP_ENCAP_LEN \u003e (int)sizeof(psp_scratch))\n+\t\terror(1, 0, \"psp: %d byte frame does not fit\", len);\n+\n+\tmemcpy(psp_scratch, pkt, len);\n+\n+\tif (proto == PF_INET) {\n+\t\tstruct iphdr *iph = (struct iphdr *)(psp_scratch + ETH_HLEN);\n+\n+\t\tip_hlen = iph-\u003eihl * 4;\n+\t\tnexthdr = iph-\u003eprotocol;\n+\t} else {\n+\t\tstruct ipv6hdr *ip6h =\n+\t\t\t(struct ipv6hdr *)(psp_scratch + ETH_HLEN);\n+\n+\t\tip_hlen = sizeof(*ip6h);\n+\t\tnexthdr = ip6h-\u003enexthdr;\n+\t}\n+\n+\tl4_off = ETH_HLEN + ip_hlen;\n+\tl4_len = len - l4_off;\n+\tif (l4_len \u003c 4)\n+\t\terror(1, 0, \"psp: no room for an L4 header\");\n+\n+\tsport = *(__be16 *)(psp_scratch + l4_off);\n+\tdport = *(__be16 *)(psp_scratch + l4_off + sizeof(sport));\n+\n+\tmemmove(psp_scratch + l4_off + PSP_UDP_LEN + PSP_HDR_LEN,\n+\t\tpsp_scratch + l4_off, l4_len);\n+\n+\tudph = (struct udphdr *)(psp_scratch + l4_off);\n+\tudph-\u003esource = sport ^ dport;\n+\tudph-\u003edest = htons(PSP_UDP_PORT);\n+\tudph-\u003elen = htons(PSP_UDP_LEN + PSP_HDR_LEN + l4_len + PSP_ICV_LEN);\n+\tudph-\u003echeck = 0;\n+\n+\tpsph = (struct psphdr *)(udph + 1);\n+\tpsph-\u003enexthdr = nexthdr;\n+\tpsph-\u003ehdrlen = PSP_HDRLEN_NOOPT;\n+\tpsph-\u003ecrypt_offset = 0; /* Matches the kernel. */\n+\tpsph-\u003everfl = (assoc-\u003eversion \u003c\u003c PSP_VERFL_VER_SHIFT) | PSP_VERFL_ONE;\n+\tpsph-\u003espi = htonl(assoc-\u003espi);\n+\tpsph-\u003eiv = htobe64(psp_next_iv++);\n+\n+\tdata = (uint8_t *)psph + PSP_HDR_LEN;\n+\ticv = (uint8_t *)psph + PSP_HDR_LEN + l4_len;\n+\taad_len = PSP_HDR_LEN;\n+\n+\t/* The GCM nonce is wire (spi, iv), which are contiguous. */\n+\tpsp_crypt(assoc,\n+\t\t (const uint8_t *)psph + offsetof(struct psphdr, spi),\n+\t\t (const uint8_t *)psph, aad_len, data, l4_len, icv);\n+\n+\tif (proto == PF_INET) {\n+\t\tstruct iphdr *iph = (struct iphdr *)(psp_scratch + ETH_HLEN);\n+\n+\t\tiph-\u003etot_len = htons(ntohs(iph-\u003etot_len) + PSP_ENCAP_LEN);\n+\t\tiph-\u003eprotocol = IPPROTO_UDP;\n+\t\tiph-\u003echeck = 0;\n+\t\tiph-\u003echeck = checksum_fold(iph, ip_hlen, 0);\n+\t} else {\n+\t\tstruct ipv6hdr *ip6h =\n+\t\t\t(struct ipv6hdr *)(psp_scratch + ETH_HLEN);\n+\n+\t\tip6h-\u003epayload_len =\n+\t\t\thtons(ntohs(ip6h-\u003epayload_len) + PSP_ENCAP_LEN);\n+\t\tip6h-\u003enexthdr = IPPROTO_UDP;\n+\t}\n+\n+\t*lenp = len + PSP_ENCAP_LEN;\n+\treturn psp_scratch;\n+}\n+#else\n+static void psp_parse_assoc(char *arg)\n+{\n+\terror(1, 0, \"gro was built without OpenSSL, PSP is not available\");\n+}\n+#endif /* HAVE_OPENSSL */\n+\n static void write_packet(int fd, char *buf, int len, struct sockaddr_ll *daddr)\n {\n \tchar control[CMSG_SPACE(sizeof(uint64_t))];\n@@ -392,6 +646,11 @@ static void write_packet(int fd, char *buf, int len, struct sockaddr_ll *daddr)\n \tstruct cmsghdr *cm;\n \tint ret = -1;\n \n+#ifdef HAVE_OPENSSL\n+\tif (psp_enabled)\n+\t\tbuf = psp_encapsulate(buf, \u0026len);\n+#endif\n+\n \tiov.iov_base = buf;\n \tiov.iov_len = len;\n \n@@ -572,6 +831,10 @@ static void send_large(int fd, struct sockaddr_ll *daddr, int remainder)\n \tconst int mss = calc_mss();\n \tint i;\n \n+\tif (num_pkt \u003e MAX_LARGE_PKT_CNT)\n+\t\terror(1, 0, \"too many large packets: %d \u003e %zu\", num_pkt,\n+\t\t (size_t)MAX_LARGE_PKT_CNT);\n+\n \tfor (i = 0; i \u003c num_pkt; i++)\n \t\tcreate_packet(pkts[i], i * mss, 0, mss, 0);\n \tcreate_packet(last, num_pkt * mss, 0, remainder, 0);\n@@ -1111,6 +1374,39 @@ static void send_changed_pppoe_sid(int fd, struct sockaddr_ll *daddr)\n \twrite_packet(fd, buf, pkt_size, daddr);\n }\n \n+/* PSP packets don't coalesce across SPIs or versions. */\n+static void send_psp_assoc_switch(int fd, struct sockaddr_ll *daddr)\n+{\n+\tstatic char buf[MAX_HDR_LEN + PAYLOAD_LEN];\n+\tint pkt_size = total_hdr_len + PAYLOAD_LEN;\n+\n+\tif (psp_num_assoc \u003c 2)\n+\t\terror(1, 0, \"%s needs two PSP associations\", testname);\n+\n+\tcreate_packet(buf, 0, 0, PAYLOAD_LEN, 0);\n+\twrite_packet(fd, buf, pkt_size, daddr);\n+\n+\tcreate_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);\n+\tpsp_idx = 1;\n+\twrite_packet(fd, buf, pkt_size, daddr);\n+\tpsp_idx = 0;\n+}\n+\n+/* A PSP packet and a cleartext packet of the same flow don't coalesce. */\n+static void send_psp_mixed(int fd, struct sockaddr_ll *daddr)\n+{\n+\tstatic char buf[MAX_HDR_LEN + PAYLOAD_LEN];\n+\tint pkt_size = total_hdr_len + PAYLOAD_LEN;\n+\n+\tcreate_packet(buf, 0, 0, PAYLOAD_LEN, 0);\n+\twrite_packet(fd, buf, pkt_size, daddr);\n+\n+\tcreate_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);\n+\tpsp_enabled = false;\n+\twrite_packet(fd, buf, pkt_size, daddr);\n+\tpsp_enabled = true;\n+}\n+\n static void bind_packetsocket(int fd)\n {\n \tstruct sockaddr_ll daddr = {};\n@@ -1559,6 +1855,16 @@ static void gro_sender(void)\n \t\tusleep(fin_delay_us);\n \t\twrite_packet(txfd, fin_pkt, total_hdr_len, \u0026daddr);\n \n+\t/* PSP sub-tests */\n+\t} else if (strcmp(testname, \"psp_spi_diff\") == 0 ||\n+\t\t strcmp(testname, \"psp_ver_diff\") == 0) {\n+\t\tsend_psp_assoc_switch(txfd, \u0026daddr);\n+\t\tusleep(fin_delay_us);\n+\t\twrite_packet(txfd, fin_pkt, total_hdr_len, \u0026daddr);\n+\t} else if (strcmp(testname, \"psp_mixed\") == 0) {\n+\t\tsend_psp_mixed(txfd, \u0026daddr);\n+\t\tusleep(fin_delay_us);\n+\t\twrite_packet(txfd, fin_pkt, total_hdr_len, \u0026daddr);\n \t} else {\n \t\terror(1, 0, \"Unknown testcase: %s\", testname);\n \t}\n@@ -1782,6 +2088,22 @@ static void gro_receiver(void)\n \t\tprintf(\"different PPPoE session ID doesn't coalesce: \");\n \t\tcheck_recv_pkts(rxfd, correct_payload, 2);\n \n+\t/* PSP sub-tests */\n+\t} else if (strcmp(testname, \"psp_spi_diff\") == 0) {\n+\t\tcorrect_payload[0] = PAYLOAD_LEN;\n+\t\tcorrect_payload[1] = PAYLOAD_LEN;\n+\t\tprintf(\"different PSP SPI doesn't coalesce: \");\n+\t\tcheck_recv_pkts(rxfd, correct_payload, 2);\n+\t} else if (strcmp(testname, \"psp_ver_diff\") == 0) {\n+\t\tcorrect_payload[0] = PAYLOAD_LEN;\n+\t\tcorrect_payload[1] = PAYLOAD_LEN;\n+\t\tprintf(\"different PSP version doesn't coalesce: \");\n+\t\tcheck_recv_pkts(rxfd, correct_payload, 2);\n+\t} else if (strcmp(testname, \"psp_mixed\") == 0) {\n+\t\tcorrect_payload[0] = PAYLOAD_LEN;\n+\t\tcorrect_payload[1] = PAYLOAD_LEN;\n+\t\tprintf(\"PSP and cleartext don't coalesce: \");\n+\t\tcheck_recv_pkts(rxfd, correct_payload, 2);\n \t} else {\n \t\terror(1, 0, \"Test case error: unknown testname %s\", testname);\n \t}\n@@ -1802,6 +2124,7 @@ static void parse_args(int argc, char **argv)\n \t\t{ \"ip6ip6\", no_argument, NULL, 'E' },\n \t\t{ \"pppoev4\", no_argument, NULL, 'p' },\n \t\t{ \"pppoev6\", no_argument, NULL, 'P' },\n+\t\t{ \"psp-assoc\", required_argument, NULL, OPT_PSP_ASSOC },\n \t\t{ \"num-flows\", required_argument, NULL, 'n' },\n \t\t{ \"rx\", no_argument, NULL, 'r' },\n \t\t{ \"saddr\", required_argument, NULL, 's' },\n@@ -1813,8 +2136,12 @@ static void parse_args(int argc, char **argv)\n \t};\n \tint c;\n \n-\twhile ((c = getopt_long(argc, argv, \"46d:D:eEi:n:pPrs:S:t:ov\", opts, NULL)) != -1) {\n+\twhile ((c = getopt_long(argc, argv, \"46d:D:eEi:n:pPrs:S:t:ov\",\n+\t\t\t opts, NULL)) != -1) {\n \t\tswitch (c) {\n+\t\tcase OPT_PSP_ASSOC:\n+\t\t\tpsp_parse_assoc(optarg);\n+\t\t\tbreak;\n \t\tcase '4':\n \t\t\tproto = PF_INET;\n \t\t\tethhdr_proto = htons(ETH_P_IP);\n@@ -1904,6 +2231,12 @@ int main(int argc, char **argv)\n \t\terror(1, 0, \"Protocol family is not ipv4 or ipv6\");\n \t}\n \n+\tif (psp_enabled \u0026\u0026 (ipip || ip6ip6 || pppoe))\n+\t\terror(1, 0, \"--psp-assoc doesn't support ipip, ip6ip6 or PPPoE\");\n+\n+\tif (!strncmp(testname, \"psp_\", 4) \u0026\u0026 !psp_enabled)\n+\t\terror(1, 0, \"test %s requires --psp-assoc\", testname);\n+\n \tread_MAC(src_mac, smac);\n \tread_MAC(dst_mac, dmac);\n \n"
} |
| 2/1 | 2026/09/04 11:00 | agent | patch-evaluator |
0mModel:core Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch touches only the vendor-specific Mellanox mlx5 driver (drivers/net/ethernet/mellanox/mlx5/ and include/linux/mlx5/) and selftests (tools/testing/selftests/). Mellanox ConnectX hardware is not emulated in standard QEMU/GCE virtualization environments, making the driver code structurally unreachable, and selftests do not affect the kernel image.",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 9a02a46aa67dc99c7122f097a34eb60ca2f981f5
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Sep 4 11:00:09 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h
index 4973fb473ff02..a802f80d90bea 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/en_accel.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h
index 3f212e46fc2f9..98709d48b8015 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h
@@ -218,6 +218,28 @@ static inline void mlx5e_accel_tx_finish(struct mlx5e_txqsq *sq,
#endif
}
+static inline int mlx5e_accel_block_tc_offload(struct mlx5_core_dev *mdev)
+{
+ int ret = 0;
+
+ mutex_lock(&mdev->offload_block.lock);
+ if (mdev->offload_block.num_accel)
+ ret = -EBUSY;
+ else
+ mdev->offload_block.num_tc++;
+ mutex_unlock(&mdev->offload_block.lock);
+
+ return ret;
+}
+
+static inline void mlx5e_accel_unblock_tc_offload(struct mlx5_core_dev *mdev)
+{
+ mutex_lock(&mdev->offload_block.lock);
+ if (!WARN_ON_ONCE(!mdev->offload_block.num_tc))
+ mdev->offload_block.num_tc--;
+ mutex_unlock(&mdev->offload_block.lock);
+}
+
static inline int mlx5e_accel_init_rx(struct mlx5e_priv *priv)
{
return mlx5e_ktls_init_rx(priv);
@@ -229,6 +251,11 @@ static inline void mlx5e_accel_cleanup_rx(struct mlx5e_priv *priv)
mlx5_accel_psp_fs_cleanup_rx_tables(priv);
}
+static inline void mlx5e_accel_update_rx(struct mlx5e_priv *priv)
+{
+ mlx5e_psp_update_rx(priv);
+}
+
static inline int mlx5e_accel_init_tx(struct mlx5e_priv *priv)
{
return mlx5e_ktls_init_tx(priv);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/flow_tag.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/flow_tag.h
new file mode 100644
index 0000000000000..cd10a74e2b27a
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/flow_tag.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
+
+#ifndef __MLX5E_FLOW_TAG_H__
+#define __MLX5E_FLOW_TAG_H__
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/mlx5/device.h>
+
+/* Unified accel flow_tag layout in CQE sop_drop_qpn [23:0]:
+ *
+ * [23:21] = protocol ID (3 bits):
+ * 0 = none (default)
+ * 1 = IPsec
+ * 2 = MACsec
+ * 3 = PSP (HW decrypted, PSP header present)
+ * 4 = PSP decap (HW decrypted & decapsulated)
+ * 5-7 = reserved
+ * [20:17] = PSP version (4 bits, valid when proto == PSP decap)
+ * 16 = reserved
+ * [15:0] = used by other subsystems (e.g. TC).
+ */
+#define MLX5E_ACCEL_FLOW_TAG_MASK GENMASK(23, 0)
+#define MLX5E_ACCEL_FLOW_TAG_PROTO_MASK GENMASK(23, 21)
+#define MLX5E_ACCEL_FLOW_TAG_PSP_VER_MASK GENMASK(20, 17)
+
+enum {
+ MLX5E_ACCEL_FLOW_TAG_PROTO_NONE = 0,
+ MLX5E_ACCEL_FLOW_TAG_PROTO_IPSEC = 1,
+ MLX5E_ACCEL_FLOW_TAG_PROTO_MACSEC = 2,
+ MLX5E_ACCEL_FLOW_TAG_PROTO_PSP = 3,
+ MLX5E_ACCEL_FLOW_TAG_PROTO_PSP_DECAP = 4,
+};
+
+static inline u32 mlx5e_accel_flow_tag(struct mlx5_cqe64 *cqe)
+{
+ return be32_to_cpu(cqe->sop_drop_qpn) & MLX5E_ACCEL_FLOW_TAG_MASK;
+}
+
+static inline u32 mlx5e_accel_flow_tag_proto(struct mlx5_cqe64 *cqe)
+{
+ return FIELD_GET(MLX5E_ACCEL_FLOW_TAG_PROTO_MASK,
+ mlx5e_accel_flow_tag(cqe));
+}
+
+#endif /* __MLX5E_FLOW_TAG_H__ */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c
index 329608c59313b..5565c385258c3 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c
@@ -4,6 +4,8 @@
#include <linux/netdevice.h>
#include "en.h"
#include "en/fs.h"
+#include "en_accel/en_accel.h"
+#include "en_accel/flow_tag.h"
#include "eswitch.h"
#include "ipsec.h"
#include "fs_core.h"
@@ -189,7 +191,7 @@ static void ipsec_rx_rule_add_match_obj(struct mlx5e_ipsec_sa_entry *sa_entry,
misc_parameters_2.metadata_reg_c_2);
MLX5_SET(fte_match_param, spec->match_value,
misc_parameters_2.metadata_reg_c_2,
- sa_entry->ipsec_obj_id | BIT(31));
+ sa_entry->ipsec_obj_id);
spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_2;
}
@@ -2065,7 +2067,7 @@ static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry)
if (!attrs->drop) {
if (rx != ipsec->rx_esw)
err = setup_modify_header(ipsec, attrs->type,
- sa_entry->ipsec_obj_id | BIT(31),
+ sa_entry->ipsec_obj_id,
XFRM_DEV_OFFLOAD_IN, &flow_act);
else
err = mlx5_esw_ipsec_rx_setup_modify_header(sa_entry, &flow_act);
@@ -2098,6 +2100,12 @@ static int rx_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry)
flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_DROP;
else
flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+ if (!attrs->drop && rx != ipsec->rx_esw) {
+ 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_IPSEC);
+ }
dest[0].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
dest[0].ft = rx->ft.status;
dest[1].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
@@ -2574,53 +2582,19 @@ void mlx5e_accel_ipsec_fs_read_stats(struct mlx5e_priv *priv, void *ipsec_stats)
}
}
-#ifdef CONFIG_MLX5_ESWITCH
-static int mlx5e_ipsec_block_tc_offload(struct mlx5_core_dev *mdev)
-{
- struct mlx5_eswitch *esw = mdev->priv.eswitch;
- int err = 0;
-
- if (esw) {
- err = mlx5_esw_lock(esw);
- if (err)
- return err;
- }
-
- if (mdev->num_block_ipsec) {
- err = -EBUSY;
- goto unlock;
- }
-
- mdev->num_block_tc++;
-
-unlock:
- if (esw)
- mlx5_esw_unlock(esw);
-
- return err;
-}
-#else
-static int mlx5e_ipsec_block_tc_offload(struct mlx5_core_dev *mdev)
-{
- if (mdev->num_block_ipsec)
- return -EBUSY;
-
- mdev->num_block_tc++;
- return 0;
-}
-#endif
-
-static void mlx5e_ipsec_unblock_tc_offload(struct mlx5_core_dev *mdev)
+static bool accel_ipsec_should_block_tc(struct mlx5e_ipsec_sa_entry *sa_entry)
{
- mdev->num_block_tc--;
+ return sa_entry->attrs.type == XFRM_DEV_OFFLOAD_PACKET ||
+ sa_entry->attrs.dir == XFRM_DEV_OFFLOAD_IN;
}
int mlx5e_accel_ipsec_fs_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry)
{
+ bool block_tc = accel_ipsec_should_block_tc(sa_entry);
int err;
- if (sa_entry->attrs.type == XFRM_DEV_OFFLOAD_PACKET) {
- err = mlx5e_ipsec_block_tc_offload(sa_entry->ipsec->mdev);
+ if (block_tc) {
+ err = mlx5e_accel_block_tc_offload(sa_entry->ipsec->mdev);
if (err)
return err;
}
@@ -2636,8 +2610,8 @@ int mlx5e_accel_ipsec_fs_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry)
return 0;
err_out:
- if (sa_entry->attrs.type == XFRM_DEV_OFFLOAD_PACKET)
- mlx5e_ipsec_unblock_tc_offload(sa_entry->ipsec->mdev);
+ if (block_tc)
+ mlx5e_accel_unblock_tc_offload(sa_entry->ipsec->mdev);
return err;
}
@@ -2651,8 +2625,8 @@ void mlx5e_accel_ipsec_fs_del_rule(struct mlx5e_ipsec_sa_entry *sa_entry)
if (ipsec_rule->pkt_reformat)
mlx5_packet_reformat_dealloc(mdev, ipsec_rule->pkt_reformat);
- if (sa_entry->attrs.type == XFRM_DEV_OFFLOAD_PACKET)
- mlx5e_ipsec_unblock_tc_offload(mdev);
+ if (accel_ipsec_should_block_tc(sa_entry))
+ mlx5e_accel_unblock_tc_offload(mdev);
if (sa_entry->attrs.dir == XFRM_DEV_OFFLOAD_OUT) {
tx_ft_put(sa_entry->ipsec, sa_entry->attrs.type);
@@ -2686,7 +2660,7 @@ int mlx5e_accel_ipsec_fs_add_pol(struct mlx5e_ipsec_pol_entry *pol_entry)
{
int err;
- err = mlx5e_ipsec_block_tc_offload(pol_entry->ipsec->mdev);
+ err = mlx5e_accel_block_tc_offload(pol_entry->ipsec->mdev);
if (err)
return err;
@@ -2701,7 +2675,7 @@ int mlx5e_accel_ipsec_fs_add_pol(struct mlx5e_ipsec_pol_entry *pol_entry)
return 0;
err_out:
- mlx5e_ipsec_unblock_tc_offload(pol_entry->ipsec->mdev);
+ mlx5e_accel_unblock_tc_offload(pol_entry->ipsec->mdev);
return err;
}
@@ -2712,7 +2686,7 @@ void mlx5e_accel_ipsec_fs_del_pol(struct mlx5e_ipsec_pol_entry *pol_entry)
mlx5_del_flow_rules(ipsec_rule->rule);
- mlx5e_ipsec_unblock_tc_offload(pol_entry->ipsec->mdev);
+ mlx5e_accel_unblock_tc_offload(pol_entry->ipsec->mdev);
if (pol_entry->attrs.dir == XFRM_DEV_OFFLOAD_IN) {
rx_ft_put_policy(pol_entry->ipsec,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h
index 45b0d19e735cb..57aabd5e61ece 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h
@@ -38,10 +38,9 @@
#include <net/xfrm.h>
#include "en.h"
#include "en/txrx.h"
+#include "en_accel/flow_tag.h"
-/* Bit31: IPsec marker, Bit30: reserved, Bit29-24: IPsec syndrome, Bit23-0: IPsec obj id */
-#define MLX5_IPSEC_METADATA_MARKER(metadata) ((((metadata) >> 30) & 0x3) == 0x2)
-#define MLX5_IPSEC_METADATA_SYNDROM(metadata) (((metadata) >> 24) & GENMASK(5, 0))
+/* IPsec obj id in ft_metadata bits[23:0] */
#define MLX5_IPSEC_METADATA_HANDLE(metadata) ((metadata) & GENMASK(23, 0))
struct mlx5e_accel_tx_ipsec_state {
@@ -74,7 +73,8 @@ static inline unsigned int mlx5e_ipsec_tx_ids_len(struct mlx5e_accel_tx_ipsec_st
static inline bool mlx5_ipsec_is_rx_flow(struct mlx5_cqe64 *cqe)
{
- return MLX5_IPSEC_METADATA_MARKER(be32_to_cpu(cqe->ft_metadata));
+ return mlx5e_accel_flow_tag_proto(cqe) ==
+ MLX5E_ACCEL_FLOW_TAG_PROTO_IPSEC;
}
static inline bool mlx5e_ipsec_eseg_meta(struct mlx5_wqe_eth_seg *eseg)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c
index daff53ba7d09f..aa7a28954861b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c
@@ -10,6 +10,7 @@
#include "en.h"
#include "lib/aso.h"
#include "lib/crypto.h"
+#include "en_accel/en_accel.h"
#include "en_accel/macsec.h"
#define MLX5_MACSEC_EPN_SCOPE_MID 0x80000000L
@@ -324,6 +325,8 @@ static void mlx5e_macsec_cleanup_sa_fs(struct mlx5e_macsec *macsec,
mlx5_macsec_fs_del_rule(macsec->mdev->macsec_fs, sa->macsec_rule, action, netdev,
fs_id);
sa->macsec_rule = NULL;
+ if (!is_tx)
+ mlx5e_accel_unblock_tc_offload(macsec->mdev);
}
static void mlx5e_macsec_cleanup_sa(struct mlx5e_macsec *macsec,
@@ -343,6 +346,7 @@ static int mlx5e_macsec_init_sa_fs(struct macsec_context *ctx,
const struct macsec_tx_sc *tx_sc = &ctx->secy->tx_sc;
struct mlx5_macsec_rule_attrs rule_attrs;
union mlx5_macsec_rule *macsec_rule;
+ int err = 0;
if (is_tx && tx_sc->encoding_sa != sa->assoc_num)
return 0;
@@ -353,13 +357,26 @@ static int mlx5e_macsec_init_sa_fs(struct macsec_context *ctx,
rule_attrs.action = (is_tx) ? MLX5_ACCEL_MACSEC_ACTION_ENCRYPT :
MLX5_ACCEL_MACSEC_ACTION_DECRYPT;
+ if (!is_tx) {
+ err = mlx5e_accel_block_tc_offload(priv->mdev);
+ if (err)
+ return err;
+ }
+
macsec_rule = mlx5_macsec_fs_add_rule(macsec_fs, ctx, &rule_attrs, fs_id);
- if (!macsec_rule)
- return -ENOMEM;
+ if (!macsec_rule) {
+ err = -ENOMEM;
+ goto out_unblock_tc;
+ }
sa->macsec_rule = macsec_rule;
return 0;
+
+out_unblock_tc:
+ if (!is_tx)
+ mlx5e_accel_unblock_tc_offload(priv->mdev);
+ return err;
}
static int mlx5e_macsec_init_sa(struct macsec_context *ctx,
@@ -735,12 +752,12 @@ static int mlx5e_macsec_add_rxsc(struct macsec_context *ctx)
* a non-NULL md_dst with uninitialised contents.
*/
err = xa_alloc(&macsec->sc_xarray, &sc_xarray_element->fs_id, sc_xarray_element,
- XA_LIMIT(1, MLX5_MACEC_RX_FS_ID_MAX), GFP_KERNEL);
+ XA_LIMIT(1, MLX5_MACSEC_RX_FS_ID_MAX), GFP_KERNEL);
if (err) {
if (err == -EBUSY)
netdev_err(ctx->netdev,
"MACsec offload: unable to create entry for RX SC (%d Rx SCs already allocated)\n",
- MLX5_MACEC_RX_FS_ID_MAX);
+ MLX5_MACSEC_RX_FS_ID_MAX);
goto destroy_md_dst;
}
@@ -1137,7 +1154,12 @@ static int macsec_upd_secy_hw_address(struct macsec_context *ctx,
struct mlx5e_macsec_sa *rx_sa;
struct list_head *list;
int i, err = 0;
+ bool block_tc;
+ /* Best-effort TC block across the operation, to prevent a mistimed TC
+ * filter add from preventing the 2nd loop from happening.
+ */
+ block_tc = mlx5e_accel_block_tc_offload(priv->mdev) == 0;
list = &macsec_device->macsec_rx_sc_list_head;
list_for_each_entry_safe(rx_sc, tmp, list, rx_sc_list_element) {
@@ -1168,6 +1190,8 @@ static int macsec_upd_secy_hw_address(struct macsec_context *ctx,
memcpy(macsec_device->dev_addr, dev->dev_addr, dev->addr_len);
out:
+ if (block_tc)
+ mlx5e_accel_unblock_tc_offload(priv->mdev);
return err;
}
@@ -1700,7 +1724,7 @@ void mlx5e_macsec_offload_handle_rx_skb(struct net_device *netdev,
if (!macsec)
return;
- fs_id = MLX5_MACSEC_RX_METADAT_HANDLE(macsec_meta_data);
+ fs_id = MLX5_MACSEC_RX_METADATA_HANDLE(macsec_meta_data);
rcu_read_lock();
sc_xarray_element = xa_load(&macsec->sc_xarray, fs_id);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.h
index 27df72e23106d..2066cb573f68e 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.h
@@ -9,6 +9,7 @@
#include <linux/mlx5/driver.h>
#include <net/macsec.h>
#include <net/dst_metadata.h>
+#include "en_accel/flow_tag.h"
#include "lib/macsec_fs.h"
struct mlx5e_priv;
@@ -31,7 +32,8 @@ static inline bool mlx5e_macsec_skb_is_offload(struct sk_buff *skb)
static inline bool mlx5e_macsec_is_rx_flow(struct mlx5_cqe64 *cqe)
{
- return MLX5_MACSEC_METADATA_MARKER(be32_to_cpu(cqe->ft_metadata));
+ return mlx5e_accel_flow_tag_proto(cqe) ==
+ MLX5E_ACCEL_FLOW_TAG_PROTO_MACSEC;
}
void mlx5e_macsec_offload_handle_rx_skb(struct net_device *netdev, struct sk_buff *skb,
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 73b232379263a..c40d2ddf6af27 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c
@@ -6,7 +6,9 @@
#include "mlx5_core.h"
#include "psp.h"
#include "lib/crypto.h"
+#include "en_accel/en_accel.h"
#include "en_accel/psp.h"
+#include "en_accel/psp_rxtx.h"
#include "fs_core.h"
enum accel_fs_psp_type {
@@ -21,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;
@@ -41,7 +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 *rx_modify_hdr;
+ struct mlx5_modify_hdr *modify_hdr;
struct mlx5_flow_handle *rule;
};
@@ -52,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;
@@ -63,12 +81,23 @@ 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;
};
+static bool shampo_enabled(struct mlx5e_priv *priv)
+{
+ return priv->channels.params.packet_merge.type ==
+ MLX5E_PACKET_MERGE_SHAMPO;
+}
+
/* PSP RX flow steering */
static enum mlx5_traffic_types fs_psp2tt(enum accel_fs_psp_type i)
{
@@ -110,6 +139,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)
{
@@ -402,15 +440,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);
- if (decrypt->rx_modify_hdr) {
- mlx5_modify_header_dealloc(fs->mdev, decrypt->rx_modify_hdr);
- decrypt->rx_modify_hdr = NULL;
- }
+ 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);
@@ -431,11 +618,11 @@ accel_psp_fs_rx_decrypt_ft_create(struct mlx5e_psp_fs *fs,
struct mlx5_flow_destination *default_dest)
{
u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {};
- struct mlx5_modify_hdr *modify_hdr = NULL;
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;
@@ -479,30 +666,38 @@ accel_psp_fs_rx_decrypt_ft_create(struct mlx5e_psp_fs *fs,
}
decrypt->miss_rule = rule;
- /* Add PSP RX decrypt rule */
- setup_fte_udp_psp(spec, PSP_DEFAULT_UDP_PORT);
- flow_act.crypto.type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_PSP;
- /* Set bit[31, 30] PSP marker */
-#define MLX5E_PSP_MARKER_BIT (BIT(30) | BIT(31))
- MLX5_SET(set_action_in, action, action_type, MLX5_ACTION_TYPE_SET);
- MLX5_SET(set_action_in, action, field, MLX5_ACTION_IN_FIELD_METADATA_REG_B);
- MLX5_SET(set_action_in, action, data, MLX5E_PSP_MARKER_BIT);
- MLX5_SET(set_action_in, action, offset, 0);
- MLX5_SET(set_action_in, action, length, 32);
-
- modify_hdr = mlx5_modify_header_alloc(mdev, MLX5_FLOW_NAMESPACE_KERNEL, 1, action);
- if (IS_ERR(modify_hdr)) {
- err = PTR_ERR(modify_hdr);
- mlx5_core_err(mdev, "fail to alloc psp set modify_header_id err=%d\n", err);
- modify_hdr = NULL;
+ /* 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->rx_modify_hdr = modify_hdr;
+ decrypt->modify_hdr = modhdr;
- flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
- MLX5_FLOW_CONTEXT_ACTION_CRYPTO_DECRYPT |
- MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
- flow_act.modify_hdr = modify_hdr;
+ /* Add PSP RX decrypt rule */
+ setup_fte_udp_psp(spec, PSP_DEFAULT_UDP_PORT);
+
+ /* Set PSP marker via flow_tag */
+ 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);
+
+ flow_act.crypto.type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_PSP;
+ 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);
@@ -523,9 +718,46 @@ 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);
+ bool tc_blocked = fs->rx.ft;
int i;
/* disconnect */
@@ -534,19 +766,29 @@ 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);
}
static int accel_psp_fs_rx_create(struct mlx5e_psp_fs *fs,
+ bool decap_enable,
struct netlink_ext_ack *extack)
{
struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(fs->fs, false);
int i, err;
+ err = mlx5e_accel_block_tc_offload(fs->mdev);
+ if (err) {
+ NL_SET_ERR_MSG(extack, "TC offload active, cannot enable PSP");
+ return err;
+ }
+
err = accel_psp_fs_rx_ft_create(fs, &fs->rx);
if (err) {
NL_SET_ERR_MSG(extack, "Failed creating RX steering table");
- return err;
+ goto err_unblock_tc;
}
err = accel_psp_fs_rx_check_ft_create(fs, &fs->check);
@@ -573,6 +815,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, decap_enable);
+
return 0;
err_decrypt_ft:
@@ -583,6 +827,8 @@ static int accel_psp_fs_rx_create(struct mlx5e_psp_fs *fs,
accel_psp_fs_rx_check_ft_destroy(&fs->check);
err_ft:
accel_psp_fs_rx_ft_destroy(&fs->rx);
+err_unblock_tc:
+ mlx5e_accel_unblock_tc_offload(fs->mdev);
return err;
}
@@ -839,7 +1085,8 @@ static int accel_psp_fs_create(struct mlx5e_priv *priv,
{
int err;
- err = accel_psp_fs_rx_create(priv->psp->fs, extack);
+ err = accel_psp_fs_rx_create(priv->psp->fs, shampo_enabled(priv),
+ extack);
if (err)
return err;
@@ -1116,3 +1363,14 @@ void mlx5e_psp_cleanup(struct mlx5e_priv *priv)
priv->psp = NULL;
kfree(psp);
}
+
+void mlx5e_psp_update_rx(struct mlx5e_priv *priv)
+{
+ struct mlx5e_psp *psp = priv->psp;
+
+ netdev_assert_locked(priv->netdev);
+ if (!psp || !psp->fs->check.ft)
+ return;
+
+ accel_psp_fs_rx_reconfigure(psp->fs, shampo_enabled(priv));
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.h
index 3f441e7dd55ac..43571a039ea61 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.h
@@ -49,6 +49,7 @@ int mlx5e_psp_register(struct mlx5e_priv *priv);
void mlx5e_psp_unregister(struct mlx5e_priv *priv);
int mlx5e_psp_init(struct mlx5e_priv *priv);
void mlx5e_psp_cleanup(struct mlx5e_priv *priv);
+void mlx5e_psp_update_rx(struct mlx5e_priv *priv);
#else
static inline void mlx5_accel_psp_fs_cleanup_rx_tables(struct mlx5e_priv *priv) { }
static inline void mlx5_accel_psp_fs_cleanup_tx_tables(struct mlx5e_priv *priv) { }
@@ -61,5 +62,6 @@ static inline int mlx5e_psp_register(struct mlx5e_priv *priv) { return 0; }
static inline void mlx5e_psp_unregister(struct mlx5e_priv *priv) { }
static inline int mlx5e_psp_init(struct mlx5e_priv *priv) { return 0; }
static inline void mlx5e_psp_cleanup(struct mlx5e_priv *priv) { }
+static inline void mlx5e_psp_update_rx(struct mlx5e_priv *priv) { }
#endif /* CONFIG_MLX5_EN_PSP */
#endif /* __MLX5E_ACCEL_PSP_H__ */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c
index 348fd7a962615..2beffee14278d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c
@@ -118,10 +118,25 @@ bool mlx5e_psp_offload_handle_rx_skb(struct net_device *netdev, struct sk_buff *
{
struct mlx5e_priv *priv = netdev_priv(netdev);
u16 dev_id = priv->psp->psd->id;
- bool strip_icv = true;
- u8 generation = 0;
+ struct psp_skb_ext *pse;
+
+ if (mlx5e_psp_is_decap(cqe)) {
+ /* UDP + PSP headers and PSP trailer removed by HW.
+ * Construct the PSP extension from CQE metadata.
+ */
+ pse = skb_ext_add(skb, SKB_EXT_PSP);
+ if (unlikely(!pse))
+ goto drop;
+
+ pse->spi = mlx5e_psp_get_spi(cqe);
+ pse->version = mlx5e_psp_get_version(cqe);
+ pse->dev_id = dev_id;
+ pse->generation = 0;
+ skb->decrypted = 1;
+ return false;
+ }
- if (psp_dev_rcv(skb, dev_id, generation, strip_icv))
+ if (psp_dev_rcv(skb, dev_id, 0, true))
goto drop;
skb->decrypted = 1;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.h
index 2b080c39cc37e..32a8330027c0b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.h
@@ -9,10 +9,7 @@
#include <net/psp.h>
#include "en.h"
#include "en/txrx.h"
-
-/* Bit30: PSP marker, Bit22-0: PSP obj id */
-#define MLX5_PSP_METADATA_MARKER(metadata) ((((metadata) >> 30) & 0x3) == 0x3)
-#define MLX5_PSP_METADATA_HANDLE(metadata) ((metadata) & GENMASK(22, 0))
+#include "en_accel/flow_tag.h"
struct mlx5e_accel_tx_psp_state {
u32 tailen;
@@ -82,7 +79,29 @@ static inline unsigned int mlx5e_psp_tx_ids_len(struct mlx5e_accel_tx_psp_state
static inline bool mlx5e_psp_is_rx_flow(struct mlx5_cqe64 *cqe)
{
- return MLX5_PSP_METADATA_MARKER(be32_to_cpu(cqe->ft_metadata));
+ u32 proto = mlx5e_accel_flow_tag_proto(cqe);
+
+ return proto == MLX5E_ACCEL_FLOW_TAG_PROTO_PSP ||
+ proto == MLX5E_ACCEL_FLOW_TAG_PROTO_PSP_DECAP;
+
+}
+
+static inline bool mlx5e_psp_is_decap(struct mlx5_cqe64 *cqe)
+{
+ u32 proto = mlx5e_accel_flow_tag_proto(cqe);
+
+ return proto == MLX5E_ACCEL_FLOW_TAG_PROTO_PSP_DECAP;
+}
+
+static inline u8 mlx5e_psp_get_version(struct mlx5_cqe64 *cqe)
+{
+ return FIELD_GET(MLX5E_ACCEL_FLOW_TAG_PSP_VER_MASK,
+ mlx5e_accel_flow_tag(cqe));
+}
+
+static inline __be32 mlx5e_psp_get_spi(struct mlx5_cqe64 *cqe)
+{
+ return cqe->ft_metadata;
}
bool mlx5e_psp_offload_handle_rx_skb(struct net_device *netdev, struct sk_buff *skb,
@@ -110,6 +129,21 @@ static inline bool mlx5e_psp_is_rx_flow(struct mlx5_cqe64 *cqe)
return false;
}
+static inline bool mlx5e_psp_is_decap(struct mlx5_cqe64 *cqe)
+{
+ return false;
+}
+
+static inline u8 mlx5e_psp_get_version(struct mlx5_cqe64 *cqe)
+{
+ return 0;
+}
+
+static inline __be32 mlx5e_psp_get_spi(struct mlx5_cqe64 *cqe)
+{
+ return 0;
+}
+
static inline bool mlx5e_psp_offload_handle_rx_skb(struct net_device *netdev,
struct sk_buff *skb,
struct mlx5_cqe64 *cqe)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index fc110a7d16e8d..b5449d849838d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -6288,7 +6288,15 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv)
static int mlx5e_update_nic_rx(struct mlx5e_priv *priv)
{
- return mlx5e_refresh_tirs(priv->mdev, false, false);
+ int err;
+
+ err = mlx5e_refresh_tirs(priv->mdev, false, false);
+ if (err)
+ return err;
+
+ mlx5e_accel_update_rx(priv);
+
+ return 0;
}
static const struct mlx5e_profile mlx5e_nic_profile = {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index 206cf9db34668..90571cb53e320 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -2221,6 +2221,33 @@ static bool mlx5e_hw_gro_skb_has_enough_space(struct sk_buff *skb,
return page_size * nr_frags + data_bcnt <= GRO_LEGACY_MAX_SIZE;
}
+static bool mlx5e_hw_gro_psp_match(struct sk_buff *skb, struct mlx5_cqe64 *cqe)
+{
+#ifdef CONFIG_MLX5_EN_PSP
+ struct psp_skb_ext *pse = skb_ext_find(skb, SKB_EXT_PSP);
+ bool is_psp = mlx5e_psp_is_rx_flow(cqe);
+
+ if (likely(!is_psp && !pse))
+ return true;
+
+ /* No match on PSP status change (no crypto -> crypto or vice-versa). */
+ if (unlikely(is_psp != !!pse))
+ return false;
+
+ /* SPI and version are only available in CQE metadata for decap flows.
+ * Non-decap PSP cannot be matched here, force a flush.
+ */
+ if (unlikely(!mlx5e_psp_is_decap(cqe)))
+ return false;
+
+ /* No match on security parameters change. */
+ return pse->spi == mlx5e_psp_get_spi(cqe) &&
+ pse->version == mlx5e_psp_get_version(cqe);
+#else
+ return true;
+#endif
+}
+
static void mlx5e_handle_rx_cqe_mpwrq_shampo(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
{
u16 data_bcnt = mpwrq_get_cqe_byte_cnt(cqe) - cqe->shampo.header_size;
@@ -2263,8 +2290,9 @@ static void mlx5e_handle_rx_cqe_mpwrq_shampo(struct mlx5e_rq *rq, struct mlx5_cq
}
if (*skb &&
- !(match && mlx5e_hw_gro_skb_has_enough_space(*skb, data_bcnt,
- page_size))) {
+ !(match &&
+ mlx5e_hw_gro_skb_has_enough_space(*skb, data_bcnt, page_size) &&
+ mlx5e_hw_gro_psp_match(*skb, cqe))) {
match = false;
mlx5e_shampo_flush_skb(rq, cqe, match);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
index b290beb4369ad..05c703795df4a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
@@ -4813,14 +4813,14 @@ static bool is_flow_rule_duplicate_allowed(struct net_device *dev,
return netif_is_lag_port(dev) && rpriv && rpriv->rep->vport != MLX5_VPORT_UPLINK;
}
-/* As IPsec and TC order is not aligned between software and hardware-offload,
- * either IPsec offload or TC offload, not both, is allowed for a specific interface.
+/* TC offload and accel protocols can overwrite each other's flow_tag with
+ * steering rules and they cannot simultaneously operate on the same interface.
+ * Additionally, as IPsec and TC order is not aligned between software and
+ * hardware-offload, only one is allowed for a specific interface.
*/
-static bool is_tc_ipsec_order_check_needed(struct net_device *filter, struct mlx5e_priv *priv)
+static bool is_tc_accel_check_needed(struct net_device *filter,
+ struct mlx5e_priv *priv)
{
- if (!IS_ENABLED(CONFIG_MLX5_EN_IPSEC))
- return false;
-
if (filter != priv->netdev)
return false;
@@ -4830,27 +4830,35 @@ static bool is_tc_ipsec_order_check_needed(struct net_device *filter, struct mlx
return true;
}
-static int mlx5e_tc_block_ipsec_offload(struct net_device *filter, struct mlx5e_priv *priv)
+static int mlx5e_tc_block_accel_offload(struct net_device *filter,
+ struct mlx5e_priv *priv)
{
struct mlx5_core_dev *mdev = priv->mdev;
+ int ret = 0;
- if (!is_tc_ipsec_order_check_needed(filter, priv))
+ if (!is_tc_accel_check_needed(filter, priv))
return 0;
- if (mdev->num_block_tc)
- return -EBUSY;
-
- mdev->num_block_ipsec++;
+ mutex_lock(&mdev->offload_block.lock);
+ if (mdev->offload_block.num_tc)
+ ret = -EBUSY;
+ else
+ mdev->offload_block.num_accel++;
+ mutex_unlock(&mdev->offload_block.lock);
- return 0;
+ return ret;
}
-static void mlx5e_tc_unblock_ipsec_offload(struct net_device *filter, struct mlx5e_priv *priv)
+static void mlx5e_tc_unblock_accel_offload(struct net_device *filter,
+ struct mlx5e_priv *priv)
{
- if (!is_tc_ipsec_order_check_needed(filter, priv))
+ if (!is_tc_accel_check_needed(filter, priv))
return;
- priv->mdev->num_block_ipsec--;
+ mutex_lock(&priv->mdev->offload_block.lock);
+ if (!WARN_ON_ONCE(!priv->mdev->offload_block.num_accel))
+ priv->mdev->offload_block.num_accel--;
+ mutex_unlock(&priv->mdev->offload_block.lock);
}
int mlx5e_configure_flower(struct net_device *dev, struct mlx5e_priv *priv,
@@ -4865,7 +4873,7 @@ int mlx5e_configure_flower(struct net_device *dev, struct mlx5e_priv *priv,
if (!mlx5_esw_hold(priv->mdev))
return -EBUSY;
- err = mlx5e_tc_block_ipsec_offload(dev, priv);
+ err = mlx5e_tc_block_accel_offload(dev, priv);
if (err)
goto esw_release;
@@ -4914,7 +4922,7 @@ int mlx5e_configure_flower(struct net_device *dev, struct mlx5e_priv *priv,
err_free:
mlx5e_flow_put(priv, flow);
out:
- mlx5e_tc_unblock_ipsec_offload(dev, priv);
+ mlx5e_tc_unblock_accel_offload(dev, priv);
mlx5_esw_put(priv->mdev);
esw_release:
mlx5_esw_release(priv->mdev);
@@ -4957,7 +4965,7 @@ int mlx5e_delete_flower(struct net_device *dev, struct mlx5e_priv *priv,
trace_mlx5e_delete_flower(f);
mlx5e_flow_put(priv, flow);
- mlx5e_tc_unblock_ipsec_offload(dev, priv);
+ mlx5e_tc_unblock_accel_offload(dev, priv);
mlx5_esw_put(priv->mdev);
return 0;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h
index e1b8cb78369f1..53b6043e33d22 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h
@@ -36,6 +36,7 @@
#include <net/pkt_cls.h>
#include "en.h"
#include "eswitch.h"
+#include "en_accel/flow_tag.h"
#include "en/tc_ct.h"
#include "en/tc_tun.h"
#include "en/tc/int_port.h"
@@ -370,11 +371,13 @@ struct mlx5e_tc_table *mlx5e_tc_table_alloc(void);
void mlx5e_tc_table_free(struct mlx5e_tc_table *tc);
static inline bool mlx5e_cqe_regb_chain(struct mlx5_cqe64 *cqe)
{
- u32 chain, reg_b;
+ u32 flow_tag, reg_b, chain;
+ flow_tag = mlx5e_accel_flow_tag_proto(cqe);
reg_b = be32_to_cpu(cqe->ft_metadata);
- if (reg_b >> (MLX5E_TC_TABLE_CHAIN_TAG_BITS + ESW_ZONE_ID_BITS))
+ if (flow_tag != MLX5E_ACCEL_FLOW_TAG_PROTO_NONE ||
+ (reg_b >> (MLX5E_TC_TABLE_CHAIN_TAG_BITS + ESW_ZONE_ID_BITS)))
return false;
chain = reg_b & MLX5E_TC_TABLE_CHAIN_TAG_MASK;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/macsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/macsec_fs.c
index efc5167493c12..4ca3a6ded2841 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/macsec_fs.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/macsec_fs.c
@@ -8,6 +8,7 @@
#include <linux/mlx5/macsec.h>
#include "fs_core.h"
#include "lib/macsec_fs.h"
+#include "en_accel/flow_tag.h"
#include "mlx5_core.h"
/* MACsec TX flow steering */
@@ -45,9 +46,6 @@
#define MLX5_SECTAG_HEADER_SIZE_WITHOUT_SCI 0x8
#define MLX5_SECTAG_HEADER_SIZE_WITH_SCI (MLX5_SECTAG_HEADER_SIZE_WITHOUT_SCI + MACSEC_SCI_LEN)
-/* MACsec fs_id handling for steering */
-#define macsec_fs_set_rx_fs_id(fs_id) ((fs_id) | BIT(30))
-
struct mlx5_sectag_header {
__be16 ethertype;
u8 tci_an;
@@ -1757,11 +1755,10 @@ macsec_fs_rx_add_rule(struct mlx5_macsec_fs *macsec_fs,
rx_tables = &rx_fs->tables;
ft_crypto = &rx_tables->ft_crypto;
- /* Set bit[31 - 30] macsec marker - 0x01 */
/* Set bit[15-0] fs id */
MLX5_SET(set_action_in, action, action_type, MLX5_ACTION_TYPE_SET);
MLX5_SET(set_action_in, action, field, MLX5_ACTION_IN_FIELD_METADATA_REG_B);
- MLX5_SET(set_action_in, action, data, macsec_fs_set_rx_fs_id(fs_id));
+ MLX5_SET(set_action_in, action, data, fs_id);
MLX5_SET(set_action_in, action, offset, 0);
MLX5_SET(set_action_in, action, length, 32);
@@ -1778,6 +1775,11 @@ macsec_fs_rx_add_rule(struct mlx5_macsec_fs *macsec_fs,
/* Rx crypto table with SCI rule */
macsec_fs_rx_setup_fte(spec, &flow_act, attrs, true);
+ 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_MACSEC);
+
flow_act.modify_hdr = modify_hdr;
flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
MLX5_FLOW_CONTEXT_ACTION_CRYPTO_DECRYPT |
@@ -1803,6 +1805,11 @@ macsec_fs_rx_add_rule(struct mlx5_macsec_fs *macsec_fs,
macsec_fs_rx_setup_fte(spec, &flow_act, attrs, false);
+ 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_MACSEC);
+
flow_act.modify_hdr = modify_hdr;
flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
MLX5_FLOW_CONTEXT_ACTION_CRYPTO_DECRYPT |
@@ -2160,8 +2167,8 @@ static int mlx5_macsec_fs_add_roce_rule_rx(struct mlx5_macsec_fs *macsec_fs, u32
spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_2;
MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, misc_parameters_2.metadata_reg_c_5);
- MLX5_SET(fte_match_param, spec->match_value, misc_parameters_2.metadata_reg_c_5,
- macsec_fs_set_rx_fs_id(fs_id));
+ MLX5_SET(fte_match_param, spec->match_value,
+ misc_parameters_2.metadata_reg_c_5, fs_id);
flow_act.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW;
new_rule = mlx5_add_flow_rules(rx_fs->roce.ft_macsec_op_check, spec, &flow_act,
NULL, 0);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/macsec_fs.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/macsec_fs.h
index 15acaff436417..b8b8b412d5c3e 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/macsec_fs.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/macsec_fs.h
@@ -6,11 +6,10 @@
#ifdef CONFIG_MLX5_MACSEC
-/* Bit31 - 30: MACsec marker, Bit15-0: MACsec id */
-#define MLX5_MACEC_RX_FS_ID_MAX USHRT_MAX /* Must be power of two */
-#define MLX5_MACSEC_RX_FS_ID_MASK MLX5_MACEC_RX_FS_ID_MAX
-#define MLX5_MACSEC_METADATA_MARKER(metadata) ((((metadata) >> 30) & 0x3) == 0x1)
-#define MLX5_MACSEC_RX_METADAT_HANDLE(metadata) ((metadata) & MLX5_MACSEC_RX_FS_ID_MASK)
+/* MACsec fs_id in ft_metadata bits[15:0] */
+#define MLX5_MACSEC_RX_FS_ID_MAX USHRT_MAX /* Must be power of two */
+#define MLX5_MACSEC_RX_FS_ID_MASK MLX5_MACSEC_RX_FS_ID_MAX
+#define MLX5_MACSEC_RX_METADATA_HANDLE(metadata) ((metadata) & MLX5_MACSEC_RX_FS_ID_MASK)
/* MACsec TX flow steering */
#define MLX5_ETH_WQE_FT_META_MACSEC_MASK \
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 5f28d906c35b6..46b34c80c4585 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -1810,6 +1810,7 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)
lockdep_register_key(&dev->lock_key);
mutex_init(&dev->intf_state_mutex);
lockdep_set_class(&dev->intf_state_mutex, &dev->lock_key);
+ mutex_init(&dev->offload_block.lock);
mutex_init(&dev->mlx5e_res.uplink_netdev_lock);
mutex_init(&dev->wc_state_lock);
@@ -1901,6 +1902,7 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)
mutex_destroy(&priv->alloc_mutex);
mutex_destroy(&priv->bfregs.wc_head.lock);
mutex_destroy(&priv->bfregs.reg_head.lock);
+ mutex_destroy(&dev->offload_block.lock);
mutex_destroy(&dev->intf_state_mutex);
lockdep_unregister_key(&dev->lock_key);
return err;
@@ -1928,6 +1930,7 @@ void mlx5_mdev_uninit(struct mlx5_core_dev *dev)
mutex_destroy(&priv->bfregs.reg_head.lock);
mutex_destroy(&dev->wc_state_lock);
mutex_destroy(&dev->mlx5e_res.uplink_netdev_lock);
+ mutex_destroy(&dev->offload_block.lock);
mutex_destroy(&dev->intf_state_mutex);
lockdep_unregister_key(&dev->lock_key);
}
diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h
index 83d0a83bbfbca..3bdb0c5a87ac0 100644
--- a/include/linux/mlx5/driver.h
+++ b/include/linux/mlx5/driver.h
@@ -788,8 +788,11 @@ struct mlx5_core_dev {
u32 vsc_addr;
struct mlx5_hv_vhca *hv_vhca;
struct mlx5_hwmon *hwmon;
- u64 num_block_tc;
- u64 num_block_ipsec;
+ struct {
+ struct mutex lock;
+ u64 num_tc;
+ u64 num_accel;
+ } offload_block;
#ifdef CONFIG_MLX5_MACSEC
struct mlx5_macsec_fs *macsec_fs;
/* MACsec notifier chain to sync MACsec core and IB database */
diff --git a/tools/testing/selftests/drivers/net/Makefile b/tools/testing/selftests/drivers/net/Makefile
index d5bf4cb638a8c..be77a15eaf38e 100644
--- a/tools/testing/selftests/drivers/net/Makefile
+++ b/tools/testing/selftests/drivers/net/Makefile
@@ -27,6 +27,11 @@ TEST_PROGS := \
xdp.py \
# end of TEST_PROGS
+TEST_FILES := \
+ gro_lib.py \
+ psp_lib.py \
+# end of TEST_FILES
+
# YNL files, must be before "include ..lib.mk"
YNL_GEN_FILES := psp_responder
TEST_GEN_FILES += $(YNL_GEN_FILES)
diff --git a/tools/testing/selftests/drivers/net/gro.py b/tools/testing/selftests/drivers/net/gro.py
index 6ab8c97880d1c..7111095bb4797 100755
--- a/tools/testing/selftests/drivers/net/gro.py
+++ b/tools/testing/selftests/drivers/net/gro.py
@@ -37,90 +37,20 @@ Test cases:
"""
import glob
-import os
import re
from lib.py import ksft_run, ksft_exit, ksft_pr
-from lib.py import NetDrvEpEnv, KsftFailEx, KsftXfailEx
+from lib.py import NetDrvEpEnv, KsftXfailEx
from lib.py import NetdevFamily, EthtoolFamily
-from lib.py import bkg, cmd, defer, ethtool, ip
+from lib.py import defer, ethtool
from lib.py import ksft_variants, KsftNamedVariant
+from gro_lib import run_gro_bin, run_with_retries, set_ethtool_feat
+from gro_lib import set_mtu_restore, setup_hw_gro, write_defer_restore
# gro.c uses hardcoded DPORT=8000
GRO_DPORT = 8000
-def _resolve_dmac(cfg, ipver):
- """
- Find the destination MAC address remote host should use to send packets
- towards the local host. It may be a router / gateway address.
- """
-
- attr = "dmac" + ipver
- # Cache the response across test cases
- if hasattr(cfg, attr):
- return getattr(cfg, attr)
-
- route = ip(f"-{ipver} route get {cfg.addr_v[ipver]}",
- json=True, host=cfg.remote)[0]
- gw = route.get("gateway")
- # Local L2 segment, address directly
- if not gw:
- setattr(cfg, attr, cfg.dev['address'])
- return getattr(cfg, attr)
-
- # ping to make sure neighbor is resolved,
- # bind to an interface, for v6 the GW is likely link local
- cmd(f"ping -c1 -W0 -I{cfg.remote_ifname} {gw}", host=cfg.remote)
-
- neigh = ip(f"neigh get {gw} dev {cfg.remote_ifname}",
- json=True, host=cfg.remote)[0]
- setattr(cfg, attr, neigh['lladdr'])
- return getattr(cfg, attr)
-
-
-def _write_defer_restore(cfg, path, val, defer_undo=False):
- with open(path, "r", encoding="utf-8") as fp:
- orig_val = fp.read().strip()
- if str(val) == orig_val:
- return
- with open(path, "w", encoding="utf-8") as fp:
- fp.write(val)
- if defer_undo:
- defer(_write_defer_restore, cfg, path, orig_val)
-
-
-def _set_mtu_restore(dev, mtu, host):
- if dev['mtu'] < mtu:
- ip(f"link set dev {dev['ifname']} mtu {mtu}", host=host)
- defer(ip, f"link set dev {dev['ifname']} mtu {dev['mtu']}", host=host)
-
-
-def _set_ethtool_feat(dev, current, feats, host=None):
- s2n = {True: "on", False: "off"}
-
- new = ["-K", dev]
- old = ["-K", dev]
- no_change = True
- for name, state in feats.items():
- new += [name, s2n[state]]
- old += [name, s2n[current[name]["active"]]]
-
- if current[name]["active"] != state:
- no_change = False
- if current[name]["fixed"]:
- raise KsftXfailEx(f"Device does not support {name}")
- if no_change:
- return
-
- eth_cmd = ethtool(" ".join(new), host=host)
- defer(ethtool, " ".join(old), host=host)
-
- # If ethtool printed something kernel must have modified some features
- if eth_cmd.stdout:
- ksft_pr(eth_cmd)
-
-
def _get_queue_stats(cfg, queue_id):
"""Get stats for a specific Rx queue."""
cfg.wait_hw_stats_settle()
@@ -182,47 +112,6 @@ def _setup_queue_count(cfg, num_queues):
ethtool(f"-L {cfg.ifname} combined {num_queues}")
-def _run_gro_bin(cfg, test_name, protocol=None, num_flows=None,
- order_check=False, verbose=False, fail=False):
- """Run gro binary with given test and return the process result."""
- if not hasattr(cfg, "bin_remote"):
- cfg.bin_local = cfg.net_lib_dir / "gro"
- cfg.bin_remote = cfg.remote.deploy(cfg.bin_local)
-
- if protocol is None:
- ipver = cfg.addr_ipver
- protocol = f"ipv{ipver}"
- else:
- ipver = "6" if protocol[-1] == "6" else "4"
-
- dmac = _resolve_dmac(cfg, ipver)
-
- base_args = [
- f"--{protocol}",
- f"--dmac {dmac}",
- f"--smac {cfg.remote_dev['address']}",
- f"--daddr {cfg.addr_v[ipver]}",
- f"--saddr {cfg.remote_addr_v[ipver]}",
- f"--test {test_name}",
- ]
- if num_flows:
- base_args.append(f"--num-flows {num_flows}")
- if order_check:
- base_args.append("--order-check")
- if verbose:
- base_args.append("--verbose")
-
- args = " ".join(base_args)
-
- rx_cmd = f"{cfg.bin_local} {args} --rx --iface {cfg.ifname}"
- tx_cmd = f"{cfg.bin_remote} {args} --iface {cfg.remote_ifname}"
-
- with bkg(rx_cmd, ksft_ready=True, exit_wait=True, fail=fail) as rx_proc:
- cmd(tx_cmd, host=cfg.remote)
-
- return rx_proc
-
-
def _setup(cfg, mode, test_name):
""" Setup hardware loopback mode for GRO testing. """
@@ -237,60 +126,39 @@ def _setup(cfg, mode, test_name):
# "large_*" tests need at least 4k MTU
if test_name.startswith("large_"):
- _set_mtu_restore(cfg.dev, 4096, None)
- _set_mtu_restore(cfg.remote_dev, 4096, cfg.remote)
+ set_mtu_restore(cfg.dev, 4096, None)
+ set_mtu_restore(cfg.remote_dev, 4096, cfg.remote)
if mode == "sw":
flush_path = f"/sys/class/net/{cfg.ifname}/gro_flush_timeout"
irq_path = f"/sys/class/net/{cfg.ifname}/napi_defer_hard_irqs"
- _write_defer_restore(cfg, flush_path, "200000", defer_undo=True)
- _write_defer_restore(cfg, irq_path, "10", defer_undo=True)
+ write_defer_restore(cfg, flush_path, "200000", defer_undo=True)
+ write_defer_restore(cfg, irq_path, "10", defer_undo=True)
- _set_ethtool_feat(cfg.ifname, cfg.feat,
- {"generic-receive-offload": True,
- "rx-gro-hw": False,
- "large-receive-offload": False})
+ set_ethtool_feat(cfg.ifname, cfg.feat,
+ {"generic-receive-offload": True,
+ "rx-gro-hw": False,
+ "large-receive-offload": False})
elif mode == "hw":
- _set_ethtool_feat(cfg.ifname, cfg.feat,
- {"generic-receive-offload": False,
- "rx-gro-hw": True,
- "large-receive-offload": False})
-
- # Some NICs treat HW GRO as a GRO sub-feature so disabling GRO
- # will also clear HW GRO. Use a hack of installing XDP generic
- # to skip SW GRO, even when enabled.
- feat = ethtool(f"-k {cfg.ifname}", json=True)[0]
- if not feat["rx-gro-hw"]["active"]:
- ksft_pr("Driver clears HW GRO and SW GRO is cleared, using generic XDP workaround")
- prog = cfg.net_lib_dir / "xdp_dummy.bpf.o"
- ip(f"link set dev {cfg.ifname} xdpgeneric obj {prog} sec xdp")
- defer(ip, f"link set dev {cfg.ifname} xdpgeneric off")
-
- # Attaching XDP may change features, fetch the latest state
- feat = ethtool(f"-k {cfg.ifname}", json=True)[0]
-
- _set_ethtool_feat(cfg.ifname, feat,
- {"generic-receive-offload": True,
- "rx-gro-hw": True,
- "large-receive-offload": False})
+ setup_hw_gro(cfg)
elif mode == "lro":
# netdevsim advertises LRO for feature inheritance testing with
# bonding/team tests but it doesn't actually perform the offload
cfg.require_nsim(nsim_test=False)
- _set_ethtool_feat(cfg.ifname, cfg.feat,
- {"generic-receive-offload": False,
- "rx-gro-hw": False,
- "large-receive-offload": True})
+ set_ethtool_feat(cfg.ifname, cfg.feat,
+ {"generic-receive-offload": False,
+ "rx-gro-hw": False,
+ "large-receive-offload": True})
try:
# Disable TSO for local tests
cfg.require_nsim() # will raise KsftXfailEx if not running on nsim
- _set_ethtool_feat(cfg.remote_ifname, cfg.remote_feat,
- {"tcp-segmentation-offload": False},
- host=cfg.remote)
+ set_ethtool_feat(cfg.remote_ifname, cfg.remote_feat,
+ {"tcp-segmentation-offload": False},
+ host=cfg.remote)
except KsftXfailEx:
pass
@@ -356,31 +224,7 @@ def test(cfg, mode, protocol, test_name):
_setup(cfg, mode, test_name)
- # Each test is run 6 times to deflake, because given the receive timing,
- # not all packets that should coalesce will be considered in the same flow
- # on every try.
- max_retries = 6
- for attempt in range(max_retries):
- fail_now = attempt >= max_retries - 1
- rx_proc = _run_gro_bin(cfg, test_name, protocol=protocol,
- verbose=True, fail=fail_now)
-
- if rx_proc.ret == 0:
- return
-
- ksft_pr(rx_proc)
-
- # ret==42 means the receiver detected over-coalescing.
- # This is unambiguous proof of a bug, retries can only cause
- # false negatives.
- if rx_proc.ret == 42:
- raise KsftFailEx(f"GRO over-coalesced in {protocol}/{test_name}")
-
- if test_name.startswith("large_") and os.environ.get("KSFT_MACHINE_SLOW"):
- ksft_pr(f"Ignoring {protocol}/{test_name} failure due to slow environment")
- return
-
- ksft_pr(f"Attempt {attempt + 1}/{max_retries} failed, retrying...")
+ run_with_retries(cfg, test_name, protocol=protocol, verbose=True)
def _capacity_variants():
@@ -420,7 +264,7 @@ def test_gro_capacity(cfg, mode, setup_func):
if queue_id is not None:
stats_before = _get_queue_stats(cfg, queue_id)
- rx_proc = _run_gro_bin(cfg, "capacity", num_flows=num_flows)
+ rx_proc = run_gro_bin(cfg, "capacity", num_flows=num_flows)
output = rx_proc.stdout
if queue_id is not None:
diff --git a/tools/testing/selftests/drivers/net/gro_lib.py b/tools/testing/selftests/drivers/net/gro_lib.py
new file mode 100644
index 0000000000000..519986028980c
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/gro_lib.py
@@ -0,0 +1,204 @@
+# SPDX-License-Identifier: GPL-2.0
+
+"""Shared helpers for the GRO selftests."""
+
+import os
+
+from lib.py import ksft_pr
+from lib.py import KsftFailEx, KsftXfailEx
+from lib.py import bkg, cmd, defer, ethtool, ip
+
+
+# gro.c exits with this code when it detects over-coalescing
+EXIT_OVER_COALESCE = 42
+
+
+def resolve_dmac(cfg, ipver):
+ """
+ Finds the destination MAC address remote host should use to send packets
+ towards the local host. It may be a router / gateway address.
+ """
+
+ attr = "dmac" + ipver
+ # Cache the response across test cases
+ if hasattr(cfg, attr):
+ return getattr(cfg, attr)
+
+ route = ip(f"-{ipver} route get {cfg.addr_v[ipver]}",
+ json=True, host=cfg.remote)[0]
+ gw = route.get("gateway")
+ # Local L2 segment, address directly
+ if not gw:
+ setattr(cfg, attr, cfg.dev['address'])
+ return getattr(cfg, attr)
+
+ # ping to make sure neighbor is resolved,
+ # bind to an interface, for v6 the GW is likely link local
+ cmd(f"ping -c1 -W0 -I{cfg.remote_ifname} {gw}", host=cfg.remote)
+
+ neigh = ip(f"neigh get {gw} dev {cfg.remote_ifname}",
+ json=True, host=cfg.remote)[0]
+ setattr(cfg, attr, neigh['lladdr'])
+ return getattr(cfg, attr)
+
+
+def write_defer_restore(cfg, path, val, defer_undo=False):
+ """Writes val to a sysfs file, optionally restoring it on test exit."""
+ with open(path, "r", encoding="utf-8") as fp:
+ orig_val = fp.read().strip()
+ if str(val) == orig_val:
+ return
+ with open(path, "w", encoding="utf-8") as fp:
+ fp.write(val)
+ if defer_undo:
+ defer(write_defer_restore, cfg, path, orig_val)
+
+
+def set_mtu_restore(dev, mtu, host):
+ """Raises a device's MTU to at least mtu, restoring it on test exit."""
+ if dev['mtu'] < mtu:
+ ip(f"link set dev {dev['ifname']} mtu {mtu}", host=host)
+ defer(ip, f"link set dev {dev['ifname']} mtu {dev['mtu']}", host=host)
+
+
+def set_ethtool_feat(dev, current, feats, host=None):
+ """Sets ethtool features, restoring them on test exit.
+
+ current is the feature state as reported by "ethtool -k", xfail if a
+ feature which needs changing is fixed.
+ """
+ s2n = {True: "on", False: "off"}
+
+ new = ["-K", dev]
+ old = ["-K", dev]
+ no_change = True
+ for name, state in feats.items():
+ new += [name, s2n[state]]
+ old += [name, s2n[current[name]["active"]]]
+
+ if current[name]["active"] != state:
+ no_change = False
+ if current[name]["fixed"]:
+ raise KsftXfailEx(f"Device does not support {name}")
+ if no_change:
+ return
+
+ eth_cmd = ethtool(" ".join(new), host=host)
+ defer(ethtool, " ".join(old), host=host)
+
+ # If ethtool printed something kernel must have modified some features
+ if eth_cmd.stdout:
+ ksft_pr(eth_cmd)
+
+
+def setup_hw_gro(cfg):
+ """Turns on HW GRO and make sure SW GRO stays out of the way.
+
+ Expects cfg.feat to hold the local device's "ethtool -k" state.
+ """
+ set_ethtool_feat(cfg.ifname, cfg.feat,
+ {"generic-receive-offload": False,
+ "rx-gro-hw": True,
+ "large-receive-offload": False})
+
+ # Some NICs treat HW GRO as a GRO sub-feature so disabling GRO
+ # will also clear HW GRO. Use a hack of installing XDP generic
+ # to skip SW GRO, even when enabled.
+ feat = ethtool(f"-k {cfg.ifname}", json=True)[0]
+ if not feat["rx-gro-hw"]["active"]:
+ ksft_pr("Driver clears HW GRO and SW GRO is cleared, using generic XDP workaround")
+ prog = cfg.net_lib_dir / "xdp_dummy.bpf.o"
+ ip(f"link set dev {cfg.ifname} xdpgeneric obj {prog} sec xdp")
+ defer(ip, f"link set dev {cfg.ifname} xdpgeneric off")
+
+ # Attaching XDP may change features, fetch the latest state
+ feat = ethtool(f"-k {cfg.ifname}", json=True)[0]
+
+ set_ethtool_feat(cfg.ifname, feat,
+ {"generic-receive-offload": True,
+ "rx-gro-hw": True,
+ "large-receive-offload": False})
+
+
+# pylint: disable=too-many-arguments,too-many-positional-arguments
+# pylint: disable=too-many-locals
+def run_gro_bin(cfg, test_name, protocol=None, num_flows=None,
+ order_check=False, verbose=False, fail=False,
+ common_args=None):
+ """Runs gro binary with given test and return the process result.
+
+ common_args is a list of extra arguments passed to both ends.
+ """
+ if not hasattr(cfg, "bin_remote"):
+ cfg.bin_local = cfg.net_lib_dir / "gro"
+ cfg.bin_remote = cfg.remote.deploy(cfg.bin_local)
+
+ if protocol is None:
+ ipver = cfg.addr_ipver
+ protocol = f"ipv{ipver}"
+ else:
+ ipver = "6" if protocol[-1] == "6" else "4"
+
+ dmac = resolve_dmac(cfg, ipver)
+
+ base_args = [
+ f"--{protocol}",
+ f"--dmac {dmac}",
+ f"--smac {cfg.remote_dev['address']}",
+ f"--daddr {cfg.addr_v[ipver]}",
+ f"--saddr {cfg.remote_addr_v[ipver]}",
+ f"--test {test_name}",
+ ]
+ if num_flows:
+ base_args.append(f"--num-flows {num_flows}")
+ if order_check:
+ base_args.append("--order-check")
+ if verbose:
+ base_args.append("--verbose")
+ if common_args:
+ base_args += common_args
+
+ args = " ".join(base_args)
+
+ rx_cmd = f"{cfg.bin_local} {args} --rx --iface {cfg.ifname}"
+ tx_cmd = f"{cfg.bin_remote} {args} --iface {cfg.remote_ifname}"
+
+ with bkg(rx_cmd, ksft_ready=True, exit_wait=True, fail=fail) as rx_proc:
+ cmd(tx_cmd, host=cfg.remote)
+
+ return rx_proc
+
+
+def run_with_retries(cfg, test_name, protocol=None, **kwargs):
+ """Runs a single gro test case, retrying to deflake it.
+
+ Each test is run 6 times, because given the receive timing, not all
+ packets that should coalesce will be considered in the same flow on
+ every try. Over-coalescing is a hard failure, retries can only
+ cause false negatives there.
+
+ Extra keyword arguments (num_flows, order_check, verbose,
+ common_args) are forwarded to run_gro_bin().
+ """
+ max_retries = 6
+ for attempt in range(max_retries):
+ fail_now = attempt >= max_retries - 1
+ rx_proc = run_gro_bin(cfg, test_name, protocol=protocol,
+ fail=fail_now, **kwargs)
+
+ if rx_proc.ret == 0:
+ return
+
+ ksft_pr(rx_proc)
+
+ # ret==42 means the receiver detected over-coalescing.
+ # This is unambiguous proof of a bug, retries can only cause
+ # false negatives.
+ if rx_proc.ret == EXIT_OVER_COALESCE:
+ raise KsftFailEx(f"GRO over-coalesced in {protocol}/{test_name}")
+
+ if test_name.startswith("large_") and os.environ.get("KSFT_MACHINE_SLOW"):
+ ksft_pr(f"Ignoring {protocol}/{test_name} failure due to slow environment")
+ return
+
+ ksft_pr(f"Attempt {attempt + 1}/{max_retries} failed, retrying...")
diff --git a/tools/testing/selftests/drivers/net/hw/Makefile b/tools/testing/selftests/drivers/net/hw/Makefile
index 78bb0169350b2..0a7851846708a 100644
--- a/tools/testing/selftests/drivers/net/hw/Makefile
+++ b/tools/testing/selftests/drivers/net/hw/Makefile
@@ -13,6 +13,20 @@ else
$(warning excluding iouring tests, liburing not installed or too old)
endif
+# psp_gro.py uses the gro binary in PSP mode, which requires OpenSSL.
+PKG_CONFIG ?= pkg-config
+HAVE_OPENSSL := $(shell echo 'int main(void) { return 0; }' | \
+ $(CC) -x c - -include openssl/evp.h \
+ $(shell $(PKG_CONFIG) --cflags --libs libcrypto 2>/dev/null \
+ || echo -lcrypto) \
+ -o /dev/null >/dev/null 2>&1 && echo 1)
+
+ifeq ($(HAVE_OPENSSL),1)
+COND_PROGS += psp_gro.py
+else
+$(warning excluding PSP GRO tests, libcrypto not installed)
+endif
+
TEST_GEN_FILES := \
$(COND_GEN_FILES) \
# end of TEST_GEN_FILES
@@ -40,6 +54,7 @@ TEST_PROGS = \
nk_qlease.py \
ntuple.py \
pp_alloc_fail.py \
+ $(COND_PROGS) \
rss_api.py \
rss_ctx.py \
rss_drv.py \
@@ -64,6 +79,8 @@ TEST_FILES := \
TEST_INCLUDES := \
$(wildcard lib/py/*.py ../lib/py/*.py) \
+ ../gro_lib.py \
+ ../psp_lib.py \
../../../net/lib.sh \
../../../net/forwarding/ipip_lib.sh \
../../../net/forwarding/lib.sh \
diff --git a/tools/testing/selftests/drivers/net/hw/psp_gro.py b/tools/testing/selftests/drivers/net/hw/psp_gro.py
new file mode 100755
index 0000000000000..9af80a022a17a
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/hw/psp_gro.py
@@ -0,0 +1,172 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+"""
+PSP conformance tests built on the GRO test binary.
+
+This reuses the gro binary in --psp mode:
+The sender crafts encapsulated & sw-encrypted PSP packets with the receiver PSP
+rx-assoc, and the receiver device decrypts and decapsulates the packets before
+an AF_PACKET tap gets to analyze them.
+
+All tests from gro.py which could run with PSP are included.
+"""
+
+import socket
+
+from lib.py import ksft_run, ksft_exit
+from lib.py import ksft_variants, KsftNamedVariant
+from lib.py import KsftSkipEx
+from lib.py import NetDrvEpEnv, PSPFamily
+from lib.py import defer, ethtool
+
+# The helpers live next to their other users, gro.py and psp.py. Importing
+# lib.py above puts the selftests root on sys.path, which is what makes these
+# two resolve, so they have to stay after it - pylint wants the opposite.
+# pylint: disable=wrong-import-order,import-error
+from drivers.net.gro_lib import run_with_retries, set_mtu_restore, setup_hw_gro
+from drivers.net.psp_lib import init_psp_dev, require_version
+# pylint: enable=wrong-import-order,import-error
+
+
+# Tests for both IP versions, a subset of gro.py. Missing:
+# - tcp_csum: PSP packets are validated by HW (with the ICV) and marked with
+# CHECKSUM_UNNECESSARY.
+_COMMON = ["data_same", "data_lrg_sml", "data_sml_lrg", "data_lrg_1byte",
+ "data_burst",
+ "ack",
+ "flags_psh", "flags_syn", "flags_rst", "flags_urg", "flags_cwr",
+ "tcp_seq", "tcp_ts", "tcp_opt",
+ "ip_ecn", "ip_tos",
+ "large_max", "large_rem",
+]
+
+# Tests specific to IPv4, a subset of gro.py. Missing:
+# - ip_csum: psp_dev_rcv() recomputes the IP checksum.
+# - ip_frag4: PSP is incompatible with IP fragmentation.
+_V4 = ["ip_ttl", "ip_opt",
+ "ip_id_df1_inc", "ip_id_df1_fixed",
+ "ip_id_df0_inc", "ip_id_df0_fixed",
+ "ip_id_df1_inc_fixed", "ip_id_df1_fixed_inc",
+]
+
+# No IPv6-only tests:
+# - ip_frag6: PSP doesn't support fragmentation
+# - ip_v6ext_same, ip_v6ext_diff: PSP doesn't support IPv6 ext headers.
+
+
+def _psp_variants():
+ for ver in range(4):
+ for proto in ["ipv4", "ipv6"]:
+ for test_name in _COMMON + (_V4 if proto == "ipv4" else []):
+ yield KsftNamedVariant(f"v{ver}_{proto}_{test_name}",
+ ver, proto, test_name)
+
+
+def _ip_variants():
+ """IPv4/IPv6."""
+ for proto in ("ipv4", "ipv6"):
+ yield KsftNamedVariant(proto, proto)
+
+
+def _psp_assoc(cfg, version=0):
+ # This socket receives no traffic, exists solely to own the rx assoc.
+ s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
+ defer(s.close)
+ return cfg.pspnl.rx_assoc({"version": version, "dev-id": cfg.psp_dev_id,
+ "sock-fd": s.fileno()})['rx-key']
+
+
+def _setup(cfg, version, test_name):
+ """Enables PSP and HW GRO on the device under test."""
+ init_psp_dev(cfg)
+ require_version(cfg, version)
+
+ if not hasattr(cfg, "feat"):
+ cfg.feat = ethtool(f"-k {cfg.ifname}", json=True)[0]
+
+ setup_hw_gro(cfg)
+
+ # "large_*" tests need at least 4k MTU
+ if test_name.startswith("large_"):
+ set_mtu_restore(cfg.dev, 4096, None)
+ set_mtu_restore(cfg.remote_dev, 4096, cfg.remote)
+
+
+def _psp_args(cfg, versions):
+ """Produces PSP associations as gro binary --psp-assoc arguments."""
+ keys = [_psp_assoc(cfg, ver) for ver in versions]
+ return [f"--psp-assoc {ver},{key['spi']:x},{key['key'].hex()}"
+ for ver, key in zip(versions, keys)]
+
+
+def _run(cfg, test_name, protocol, versions):
+ """Sets up, associates and runs one gro test case under PSP."""
+ ipver = protocol[-1]
+ cfg.require_ipver(ipver)
+
+ _setup(cfg, max(versions), test_name)
+
+ run_with_retries(cfg, test_name, protocol=protocol, verbose=True,
+ common_args=_psp_args(cfg, versions))
+
+
+@ksft_variants(_psp_variants())
+def test_psp_gro(cfg, version, protocol, test_name):
+ """Runs one gro conformance case with PSP encapsulation."""
+ _run(cfg, test_name, protocol, [version])
+
+
+# PSP-specific GRO tests
+
+@ksft_variants(_ip_variants())
+def test_psp_spi_diff(cfg, protocol):
+ """Frames from two different SPIs must not coalesce."""
+ _run(cfg, "psp_spi_diff", protocol, [0, 0])
+
+
+@ksft_variants(_ip_variants())
+def test_psp_ver_diff(cfg, protocol):
+ """Frames from two different PSP versions must not coalesce."""
+ init_psp_dev(cfg)
+ if len(cfg.psp_info['psp-versions-cap']) < 2:
+ raise KsftSkipEx("Device supports a single PSP version")
+
+ _run(cfg, "psp_ver_diff", protocol, [0, 1])
+
+
+@ksft_variants(_ip_variants())
+def test_psp_mixed(cfg, protocol):
+ """A PSP frame must not coalesce with a clear text one."""
+ _run(cfg, "psp_mixed", protocol, [0])
+
+
+@ksft_variants(_ip_variants())
+def test_psp_after_reconfig(cfg, protocol):
+ """Verifies that decap still works after PSP off + on."""
+ cfg.require_ipver(protocol[-1])
+
+ _setup(cfg, 0, "data_same")
+
+ cap = cfg.psp_info['psp-versions-cap']
+ cfg.pspnl.dev_set({'id': cfg.psp_dev_id, 'psp-versions-ena': []})
+ cfg.pspnl.dev_set({'id': cfg.psp_dev_id, 'psp-versions-ena': cap})
+
+ run_with_retries(cfg, "data_same", protocol=protocol, verbose=True,
+ common_args=_psp_args(cfg, [0]))
+
+
+def main() -> None:
+ """ Ksft boiler plate main """
+
+ with NetDrvEpEnv(__file__, nsim_test=False) as cfg:
+ cfg.pspnl = PSPFamily()
+
+ ksft_run(cases=[test_psp_gro, test_psp_spi_diff, test_psp_ver_diff,
+ test_psp_mixed, test_psp_after_reconfig],
+ args=(cfg, ))
+ ksft_exit()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/testing/selftests/drivers/net/psp.py b/tools/testing/selftests/drivers/net/psp.py
index 315648a770d00..47983fb8c7c22 100755
--- a/tools/testing/selftests/drivers/net/psp.py
+++ b/tools/testing/selftests/drivers/net/psp.py
@@ -11,6 +11,8 @@ import struct
import termios
import time
+from psp_lib import init_psp_dev, require_version
+
from lib.py import defer
from lib.py import ksft_run, ksft_exit, ksft_pr
from lib.py import ksft_true, ksft_eq, ksft_ne, ksft_gt, ksft_raises
@@ -122,34 +124,13 @@ def _get_stat(cfg, key):
# Test case boiler plate
#
-def _init_psp_dev(cfg, use_psp_ifindex=False):
- if not hasattr(cfg, 'psp_dev_id'):
- # Figure out which local device we are testing against
- # For NetDrvContEnv: use psp_ifindex instead of ifindex
- target_ifindex = cfg.psp_ifindex if use_psp_ifindex else cfg.ifindex
- for dev in cfg.pspnl.dev_get({}, dump=True):
- if dev['ifindex'] == target_ifindex:
- cfg.psp_info = dev
- cfg.psp_dev_id = cfg.psp_info['id']
- break
- else:
- raise KsftSkipEx("No PSP devices found")
-
- # Enable PSP if necessary
- cap = cfg.psp_info['psp-versions-cap']
- ena = cfg.psp_info['psp-versions-ena']
- if cap != ena:
- cfg.pspnl.dev_set({'id': cfg.psp_dev_id, 'psp-versions-ena': cap})
- defer(cfg.pspnl.dev_set, {'id': cfg.psp_dev_id,
- 'psp-versions-ena': ena })
-
#
# Test cases
#
def dev_list_devices(cfg):
""" Dump all devices """
- _init_psp_dev(cfg)
+ init_psp_dev(cfg)
devices = cfg.pspnl.dev_get({}, dump=True)
@@ -161,7 +142,7 @@ def dev_list_devices(cfg):
def dev_get_device(cfg):
""" Get the device we intend to use """
- _init_psp_dev(cfg)
+ init_psp_dev(cfg)
dev = cfg.pspnl.dev_get({'id': cfg.psp_dev_id})
ksft_eq(dev['id'], cfg.psp_dev_id)
@@ -180,7 +161,7 @@ def dev_get_device_bad(cfg):
def dev_rotate(cfg):
""" Test key rotation """
- _init_psp_dev(cfg)
+ init_psp_dev(cfg)
prev_rotations = _get_stat(cfg, 'key-rotations')
@@ -195,7 +176,7 @@ def dev_rotate(cfg):
def dev_rotate_spi(cfg):
""" Test key rotation and SPI check """
- _init_psp_dev(cfg)
+ init_psp_dev(cfg)
top_a = top_b = 0
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
@@ -217,7 +198,7 @@ def dev_rotate_spi(cfg):
def assoc_basic(cfg):
""" Test creating associations """
- _init_psp_dev(cfg)
+ init_psp_dev(cfg)
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
assoc = cfg.pspnl.rx_assoc({"version": 0,
@@ -237,7 +218,7 @@ def assoc_basic(cfg):
def assoc_bad_dev(cfg):
""" Test creating associations with bad device ID """
- _init_psp_dev(cfg)
+ init_psp_dev(cfg)
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
with ksft_raises(NlError) as cm:
@@ -249,7 +230,7 @@ def assoc_bad_dev(cfg):
def assoc_sk_only_conn(cfg):
""" Test creating associations based on socket """
- _init_psp_dev(cfg)
+ init_psp_dev(cfg)
with _make_clr_conn(cfg) as s:
assoc = cfg.pspnl.rx_assoc({"version": 0,
@@ -263,7 +244,7 @@ def assoc_sk_only_conn(cfg):
def assoc_sk_only_mismatch(cfg):
""" Test creating associations based on socket (dev mismatch) """
- _init_psp_dev(cfg)
+ init_psp_dev(cfg)
with _make_clr_conn(cfg) as s:
with ksft_raises(NlError) as cm:
@@ -278,7 +259,7 @@ def assoc_sk_only_mismatch(cfg):
def assoc_sk_only_mismatch_tx(cfg):
""" Test creating associations based on socket (dev mismatch) """
- _init_psp_dev(cfg)
+ init_psp_dev(cfg)
with _make_clr_conn(cfg) as s:
with ksft_raises(NlError) as cm:
@@ -296,7 +277,7 @@ def assoc_sk_only_mismatch_tx(cfg):
def assoc_sk_only_unconn(cfg):
""" Test creating associations based on socket (unconnected, should fail) """
- _init_psp_dev(cfg)
+ init_psp_dev(cfg)
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
with ksft_raises(NlError) as cm:
@@ -309,7 +290,7 @@ def assoc_sk_only_unconn(cfg):
def assoc_version_mismatch(cfg):
""" Test creating associations where Rx and Tx PSP versions do not match """
- _init_psp_dev(cfg)
+ init_psp_dev(cfg)
versions = list(cfg.psp_info['psp-versions-cap'])
if len(versions) < 2:
@@ -335,7 +316,7 @@ def assoc_version_mismatch(cfg):
def assoc_twice(cfg):
""" Test reusing Tx assoc for two sockets """
- _init_psp_dev(cfg)
+ init_psp_dev(cfg)
def rx_assoc_check(s):
assoc = cfg.pspnl.rx_assoc({"version": 0,
@@ -369,19 +350,9 @@ def assoc_twice(cfg):
def _data_basic_send(cfg, version, ipver):
""" Test basic data send """
- _init_psp_dev(cfg)
-
- # Version 0 is required by spec, don't let it skip
- if version:
- name = cfg.pspnl.consts["version"].entries_by_val[version].name
- if name not in cfg.psp_info['psp-versions-cap']:
- with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
- with ksft_raises(NlError) as cm:
- cfg.pspnl.rx_assoc({"version": version,
- "dev-id": cfg.psp_dev_id,
- "sock-fd": s.fileno()})
- ksft_eq(cm.exception.nl_msg.error, -errno.EOPNOTSUPP)
- raise KsftSkipEx("PSP version not supported", name)
+ init_psp_dev(cfg)
+
+ require_version(cfg, version)
s = _make_psp_conn(cfg, version, ipver)
@@ -418,7 +389,7 @@ def __bad_xfer_do(cfg, s, tx, version='hdr0-aes-gcm-128'):
def data_send_bad_key(cfg):
""" Test send data with bad key """
- _init_psp_dev(cfg)
+ init_psp_dev(cfg)
s = _make_psp_conn(cfg)
@@ -433,7 +404,7 @@ def data_send_bad_key(cfg):
def data_send_disconnect(cfg):
""" Test socket close after sending data """
- _init_psp_dev(cfg)
+ init_psp_dev(cfg)
with _make_psp_conn(cfg) as s:
assoc = cfg.pspnl.rx_assoc({"version": 0,
@@ -451,7 +422,7 @@ def data_send_disconnect(cfg):
def _data_mss_adjust(cfg, ipver):
- _init_psp_dev(cfg)
+ init_psp_dev(cfg)
# First figure out what the MSS would be without any adjustments
s = _make_clr_conn(cfg, ipver)
@@ -491,7 +462,7 @@ def _data_mss_adjust(cfg, ipver):
def data_stale_key(cfg):
""" Test send on a double-rotated key """
- _init_psp_dev(cfg)
+ init_psp_dev(cfg)
prev_stale = _get_stat(cfg, 'stale-events')
s = _make_psp_conn(cfg)
@@ -766,7 +737,7 @@ def _psp_dev_get_check_netkit_psp_assoc(cfg):
def _dev_assoc_no_nsid(cfg):
""" Test dev-assoc and dev-disassoc without nsid attribute """
- _init_psp_dev(cfg, True)
+ init_psp_dev(cfg, True)
# Associate without nsid - should look up ifindex in caller's netns
cfg.pspnl.dev_assoc({'id': cfg.psp_dev_id,
@@ -800,7 +771,7 @@ def _psp_dev_assoc_cleanup_on_netkit_del(cfg):
Creates a disposable netkit pair for this test to avoid destroying
the shared environment.
"""
- _init_psp_dev(cfg, True)
+ init_psp_dev(cfg, True)
defer(delattr, cfg, 'psp_dev_id')
defer(delattr, cfg, 'psp_info')
@@ -877,7 +848,7 @@ def _try_disassoc(cfg, psp_dev_id, ifindex, nsid=None):
def _assoc_nk_guest(cfg):
"""Associate nk_guest with PSP device and register cleanup via defer()."""
- _init_psp_dev(cfg, True)
+ init_psp_dev(cfg, True)
cfg.pspnl.dev_assoc({'id': cfg.psp_dev_id,
'ifindex': cfg.nk_guest_ifindex,
diff --git a/tools/testing/selftests/drivers/net/psp_lib.py b/tools/testing/selftests/drivers/net/psp_lib.py
new file mode 100644
index 0000000000000..d450db81cfc2a
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/psp_lib.py
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: GPL-2.0
+
+"""Shared helpers for the PSP selftests."""
+
+import errno
+import socket
+
+from lib.py import defer
+from lib.py import ksft_eq, ksft_raises
+from lib.py import KsftSkipEx
+from lib.py import NlError
+
+
+def require_version(cfg, version):
+ """Skip unless the device supports a PSP version.
+
+ Version 0 is required by the spec, so it never skips. Before
+ skipping, check that the kernel rejects the unsupported version
+ properly - this is the only coverage that path gets.
+ """
+ if not version:
+ return
+
+ name = cfg.pspnl.consts["version"].entries_by_val[version].name
+ if name in cfg.psp_info['psp-versions-cap']:
+ return
+
+ with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
+ with ksft_raises(NlError) as cm:
+ cfg.pspnl.rx_assoc({"version": version,
+ "dev-id": cfg.psp_dev_id,
+ "sock-fd": s.fileno()})
+ ksft_eq(cm.exception.nl_msg.error, -errno.EOPNOTSUPP)
+ raise KsftSkipEx("PSP version not supported", name)
+
+
+def init_psp_dev(cfg, use_psp_ifindex=False):
+ """Find the PSP device under test and enable all supported versions."""
+ if not hasattr(cfg, 'psp_dev_id'):
+ # Figure out which local device we are testing against
+ # For NetDrvContEnv: use psp_ifindex instead of ifindex
+ target_ifindex = cfg.psp_ifindex if use_psp_ifindex else cfg.ifindex
+ for dev in cfg.pspnl.dev_get({}, dump=True):
+ if dev['ifindex'] == target_ifindex:
+ cfg.psp_info = dev
+ cfg.psp_dev_id = cfg.psp_info['id']
+ break
+ else:
+ raise KsftSkipEx("No PSP devices found")
+
+ # Enable PSP if necessary
+ cap = cfg.psp_info['psp-versions-cap']
+ ena = cfg.psp_info['psp-versions-ena']
+ if cap != ena:
+ cfg.pspnl.dev_set({'id': cfg.psp_dev_id, 'psp-versions-ena': cap})
+ defer(cfg.pspnl.dev_set, {'id': cfg.psp_dev_id,
+ 'psp-versions-ena': ena})
diff --git a/tools/testing/selftests/net/lib/Makefile b/tools/testing/selftests/net/lib/Makefile
index ff83603397d06..ce67e8d3e4f6c 100644
--- a/tools/testing/selftests/net/lib/Makefile
+++ b/tools/testing/selftests/net/lib/Makefile
@@ -22,4 +22,20 @@ TEST_INCLUDES := $(wildcard py/*.py sh/*.sh)
include ../../lib.mk
+# gro optionally needs OpenSSL for PSP.
+PKG_CONFIG ?= pkg-config
+LIBCRYPTO_CFLAGS := $(shell $(PKG_CONFIG) --cflags libcrypto 2>/dev/null)
+LIBCRYPTO_LIBS := $(shell $(PKG_CONFIG) --libs libcrypto 2>/dev/null || \
+ echo -lcrypto)
+HAVE_OPENSSL := $(shell echo 'int main(void) { return 0; }' | \
+ $(CC) -x c - -include openssl/evp.h $(LIBCRYPTO_CFLAGS) \
+ $(LIBCRYPTO_LIBS) -o /dev/null >/dev/null 2>&1 && echo 1)
+
+ifeq ($(HAVE_OPENSSL),1)
+$(OUTPUT)/gro: CFLAGS += -DHAVE_OPENSSL $(LIBCRYPTO_CFLAGS)
+$(OUTPUT)/gro: LDLIBS += $(LIBCRYPTO_LIBS)
+else
+$(warning libcrypto not found, gro will be built without PSP support)
+endif
+
include ../bpf.mk
diff --git a/tools/testing/selftests/net/lib/gro.c b/tools/testing/selftests/net/lib/gro.c
index 7a333155de1ab..25f54eed601f5 100644
--- a/tools/testing/selftests/net/lib/gro.c
+++ b/tools/testing/selftests/net/lib/gro.c
@@ -64,6 +64,7 @@
#define _GNU_SOURCE
#include <arpa/inet.h>
+#include <endian.h>
#include <errno.h>
#include <error.h>
#include <getopt.h>
@@ -79,14 +80,22 @@
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
+#include <netinet/udp.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdarg.h>
+#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
+#include <linux/psp.h>
+
+#ifdef HAVE_OPENSSL
+#include <openssl/evp.h>
+#endif
+
#include "kselftest.h"
#include "ksft.h"
@@ -110,6 +119,38 @@
#define EXIT_OVER_COALESCE 42
+/* PSP transport mode encapsulation, as built by the sender:
+ *
+ * [eth][IP][UDP dport=1000][PSP hdr][encrypted L4][ICV]
+ *
+ * Can't use the kernel-only include/net/psp/types.h header, so copy the
+ * encoding here.
+ */
+struct psphdr {
+ uint8_t nexthdr;
+ uint8_t hdrlen;
+ uint8_t crypt_offset;
+ uint8_t verfl;
+ uint32_t spi; /* big endian */
+ uint64_t iv; /* big endian */
+} __packed;
+
+#define PSP_UDP_PORT 1000
+#define PSP_UDP_LEN sizeof(struct udphdr)
+#define PSP_HDR_LEN sizeof(struct psphdr)
+#define PSP_ICV_LEN 16
+/* total frame growth: the inserted headers plus the trailing ICV */
+#define PSP_ENCAP_LEN (PSP_UDP_LEN + PSP_HDR_LEN + PSP_ICV_LEN)
+#define PSP_HDRLEN_NOOPT 1 /* (1 + 1) * 8 == PSP_HDR_LEN */
+#define PSP_VERFL_ONE 0x01
+#define PSP_VERFL_VER_SHIFT 2
+#define PSP_MAX_ASSOC 2
+#define PSP_KEY_LEN_MAX 32
+
+enum {
+ OPT_PSP_ASSOC = 256,
+};
+
#define ipv6_optlen(p) (((p)->hdrlen+1) << 3) /* calculate IPv6 extension header len */
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
@@ -143,6 +184,9 @@ static bool pppoe;
static uint64_t txtime_ns;
static int num_flows = 4;
static bool order_check;
+static bool psp_enabled;
+static int psp_idx;
+static int psp_num_assoc;
#define CAPACITY_PAYLOAD_LEN 200
@@ -158,7 +202,8 @@ static int max_payload(void)
static int calc_mss(void)
{
- return ASSUMED_MTU - (total_hdr_len - ETH_HLEN);
+ return ASSUMED_MTU - (total_hdr_len - ETH_HLEN) -
+ (psp_enabled ? PSP_ENCAP_LEN : 0);
}
static int num_large_pkt(void)
@@ -384,6 +429,215 @@ static void fill_transportlayer(void *buf, int seq_offset, int ack_offset,
tcph->check = tcp_checksum(tcph, payload_len);
}
+#ifdef HAVE_OPENSSL
+
+struct psp_assoc {
+ uint32_t spi;
+ uint8_t key[PSP_KEY_LEN_MAX];
+ int keylen;
+ int version;
+ EVP_CIPHER_CTX *ctx;
+};
+
+static struct psp_assoc psp_assocs[PSP_MAX_ASSOC];
+static uint64_t psp_next_iv = 1;
+
+static char psp_scratch[L2_HLEN_MAX + IP_MAXPACKET + PSP_ENCAP_LEN];
+
+static int psp_key_len(int version)
+{
+ switch (version) {
+ case PSP_VERSION_HDR0_AES_GCM_128:
+ case PSP_VERSION_HDR0_AES_GMAC_128:
+ return 16;
+ case PSP_VERSION_HDR0_AES_GCM_256:
+ case PSP_VERSION_HDR0_AES_GMAC_256:
+ return 32;
+ default:
+ error(1, 0, "psp: unknown version %d", version);
+ return 0;
+ }
+}
+
+static bool psp_version_is_gmac(int version)
+{
+ return version == PSP_VERSION_HDR0_AES_GMAC_128 ||
+ version == PSP_VERSION_HDR0_AES_GMAC_256;
+}
+
+/* Parses one --psp-assoc "version,spi,key".
+ * The SPI and key (both hex) is a PSP RX assoc from the device under test.
+ */
+static void psp_parse_assoc(char *arg)
+{
+ char *ver_s, *spi_s, *key_s, *end;
+ const EVP_CIPHER *cipher;
+ struct psp_assoc *assoc;
+ unsigned long val;
+ size_t len, i;
+
+ if (psp_num_assoc == PSP_MAX_ASSOC)
+ error(1, 0, "psp: at most %d associations", PSP_MAX_ASSOC);
+ assoc = &psp_assocs[psp_num_assoc];
+
+ ver_s = strtok(arg, ",");
+ spi_s = strtok(NULL, ",");
+ key_s = strtok(NULL, ",");
+ if (!ver_s || !spi_s || !key_s || strtok(NULL, ","))
+ error(1, 0, "psp: --psp-assoc wants version,spi,key");
+
+ errno = 0;
+ val = strtoul(ver_s, &end, 10);
+ if (errno || end == ver_s || *end ||
+ val > PSP_VERSION_HDR0_AES_GMAC_256)
+ error(1, 0, "psp: bad version '%s'", ver_s);
+ assoc->version = val;
+
+ errno = 0;
+ val = strtoul(spi_s, &end, 16);
+ if (errno || end == spi_s || *end || val > 0xffffffffUL)
+ error(1, 0, "psp: bad SPI '%s'", spi_s);
+ assoc->spi = val;
+
+ len = strlen(key_s);
+ if (len != (size_t)psp_key_len(assoc->version) * 2 ||
+ strspn(key_s, "0123456789abcdefABCDEF") != len)
+ error(1, 0, "psp: version %d needs a %d byte key, got '%s'",
+ assoc->version, psp_key_len(assoc->version), key_s);
+
+ assoc->keylen = len / 2;
+ for (i = 0; i < len; i += 2) {
+ unsigned int byte;
+
+ if (sscanf(key_s + i, "%2x", &byte) != 1)
+ error(1, 0, "psp: bad key '%s'", key_s);
+ assoc->key[i / 2] = byte;
+ }
+
+ cipher = assoc->keylen == 16 ? EVP_aes_128_gcm() : EVP_aes_256_gcm();
+ assoc->ctx = EVP_CIPHER_CTX_new();
+ if (!assoc->ctx)
+ error(1, 0, "psp: EVP_CIPHER_CTX_new");
+ if (EVP_EncryptInit_ex(assoc->ctx, cipher, NULL, assoc->key,
+ NULL) != 1)
+ error(1, 0, "psp: EVP_EncryptInit_ex key");
+ psp_num_assoc++;
+ psp_enabled = true;
+}
+
+static void psp_crypt(struct psp_assoc *assoc, const uint8_t *nonce,
+ const uint8_t *aad, int aad_len,
+ uint8_t *data, int data_len, uint8_t *icv)
+{
+ uint8_t *crypt_data = psp_version_is_gmac(assoc->version) ? NULL : data;
+ EVP_CIPHER_CTX *ctx = assoc->ctx;
+ int outl;
+
+ if (EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, nonce) != 1)
+ error(1, 0, "psp: EVP_EncryptInit_ex nonce");
+ if (EVP_EncryptUpdate(ctx, NULL, &outl, aad, aad_len) != 1)
+ error(1, 0, "psp: EVP_EncryptUpdate aad");
+ if (EVP_EncryptUpdate(ctx, crypt_data, &outl, data, data_len) != 1)
+ error(1, 0, "psp: EVP_EncryptUpdate data");
+ if (EVP_EncryptFinal_ex(ctx, icv, &outl) != 1)
+ error(1, 0, "psp: EVP_EncryptFinal_ex");
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, PSP_ICV_LEN,
+ icv) != 1)
+ error(1, 0, "psp: EVP_CTRL_GCM_GET_TAG");
+}
+
+/* Encapsulates & encrypts @pkt with PSP transport mode into psp_scratch.
+ * Returns the scratch buffer and updates *@lenp.
+ */
+static char *psp_encapsulate(const char *pkt, int *lenp)
+{
+ struct psp_assoc *assoc = &psp_assocs[psp_idx];
+ int ip_hlen, l4_off, l4_len, aad_len;
+ struct psphdr *psph;
+ struct udphdr *udph;
+ __be16 sport, dport;
+ uint8_t *data, *icv;
+ int len = *lenp;
+ uint8_t nexthdr;
+
+ if (len + PSP_ENCAP_LEN > (int)sizeof(psp_scratch))
+ error(1, 0, "psp: %d byte frame does not fit", len);
+
+ memcpy(psp_scratch, pkt, len);
+
+ if (proto == PF_INET) {
+ struct iphdr *iph = (struct iphdr *)(psp_scratch + ETH_HLEN);
+
+ ip_hlen = iph->ihl * 4;
+ nexthdr = iph->protocol;
+ } else {
+ struct ipv6hdr *ip6h =
+ (struct ipv6hdr *)(psp_scratch + ETH_HLEN);
+
+ ip_hlen = sizeof(*ip6h);
+ nexthdr = ip6h->nexthdr;
+ }
+
+ l4_off = ETH_HLEN + ip_hlen;
+ l4_len = len - l4_off;
+ if (l4_len < 4)
+ error(1, 0, "psp: no room for an L4 header");
+
+ sport = *(__be16 *)(psp_scratch + l4_off);
+ dport = *(__be16 *)(psp_scratch + l4_off + sizeof(sport));
+
+ memmove(psp_scratch + l4_off + PSP_UDP_LEN + PSP_HDR_LEN,
+ psp_scratch + l4_off, l4_len);
+
+ udph = (struct udphdr *)(psp_scratch + l4_off);
+ udph->source = sport ^ dport;
+ udph->dest = htons(PSP_UDP_PORT);
+ udph->len = htons(PSP_UDP_LEN + PSP_HDR_LEN + l4_len + PSP_ICV_LEN);
+ udph->check = 0;
+
+ psph = (struct psphdr *)(udph + 1);
+ psph->nexthdr = nexthdr;
+ psph->hdrlen = PSP_HDRLEN_NOOPT;
+ psph->crypt_offset = 0; /* Matches the kernel. */
+ psph->verfl = (assoc->version << PSP_VERFL_VER_SHIFT) | PSP_VERFL_ONE;
+ psph->spi = htonl(assoc->spi);
+ psph->iv = htobe64(psp_next_iv++);
+
+ data = (uint8_t *)psph + PSP_HDR_LEN;
+ icv = (uint8_t *)psph + PSP_HDR_LEN + l4_len;
+ aad_len = PSP_HDR_LEN;
+
+ /* The GCM nonce is wire (spi, iv), which are contiguous. */
+ psp_crypt(assoc,
+ (const uint8_t *)psph + offsetof(struct psphdr, spi),
+ (const uint8_t *)psph, aad_len, data, l4_len, icv);
+
+ if (proto == PF_INET) {
+ struct iphdr *iph = (struct iphdr *)(psp_scratch + ETH_HLEN);
+
+ iph->tot_len = htons(ntohs(iph->tot_len) + PSP_ENCAP_LEN);
+ iph->protocol = IPPROTO_UDP;
+ iph->check = 0;
+ iph->check = checksum_fold(iph, ip_hlen, 0);
+ } else {
+ struct ipv6hdr *ip6h =
+ (struct ipv6hdr *)(psp_scratch + ETH_HLEN);
+
+ ip6h->payload_len =
+ htons(ntohs(ip6h->payload_len) + PSP_ENCAP_LEN);
+ ip6h->nexthdr = IPPROTO_UDP;
+ }
+
+ *lenp = len + PSP_ENCAP_LEN;
+ return psp_scratch;
+}
+#else
+static void psp_parse_assoc(char *arg)
+{
+ error(1, 0, "gro was built without OpenSSL, PSP is not available");
+}
+#endif /* HAVE_OPENSSL */
+
static void write_packet(int fd, char *buf, int len, struct sockaddr_ll *daddr)
{
char control[CMSG_SPACE(sizeof(uint64_t))];
@@ -392,6 +646,11 @@ static void write_packet(int fd, char *buf, int len, struct sockaddr_ll *daddr)
struct cmsghdr *cm;
int ret = -1;
+#ifdef HAVE_OPENSSL
+ if (psp_enabled)
+ buf = psp_encapsulate(buf, &len);
+#endif
+
iov.iov_base = buf;
iov.iov_len = len;
@@ -572,6 +831,10 @@ static void send_large(int fd, struct sockaddr_ll *daddr, int remainder)
const int mss = calc_mss();
int i;
+ if (num_pkt > MAX_LARGE_PKT_CNT)
+ error(1, 0, "too many large packets: %d > %zu", num_pkt,
+ (size_t)MAX_LARGE_PKT_CNT);
+
for (i = 0; i < num_pkt; i++)
create_packet(pkts[i], i * mss, 0, mss, 0);
create_packet(last, num_pkt * mss, 0, remainder, 0);
@@ -1111,6 +1374,39 @@ static void send_changed_pppoe_sid(int fd, struct sockaddr_ll *daddr)
write_packet(fd, buf, pkt_size, daddr);
}
+/* PSP packets don't coalesce across SPIs or versions. */
+static void send_psp_assoc_switch(int fd, struct sockaddr_ll *daddr)
+{
+ static char buf[MAX_HDR_LEN + PAYLOAD_LEN];
+ int pkt_size = total_hdr_len + PAYLOAD_LEN;
+
+ if (psp_num_assoc < 2)
+ error(1, 0, "%s needs two PSP associations", testname);
+
+ create_packet(buf, 0, 0, PAYLOAD_LEN, 0);
+ write_packet(fd, buf, pkt_size, daddr);
+
+ create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);
+ psp_idx = 1;
+ write_packet(fd, buf, pkt_size, daddr);
+ psp_idx = 0;
+}
+
+/* A PSP packet and a cleartext packet of the same flow don't coalesce. */
+static void send_psp_mixed(int fd, struct sockaddr_ll *daddr)
+{
+ static char buf[MAX_HDR_LEN + PAYLOAD_LEN];
+ int pkt_size = total_hdr_len + PAYLOAD_LEN;
+
+ create_packet(buf, 0, 0, PAYLOAD_LEN, 0);
+ write_packet(fd, buf, pkt_size, daddr);
+
+ create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);
+ psp_enabled = false;
+ write_packet(fd, buf, pkt_size, daddr);
+ psp_enabled = true;
+}
+
static void bind_packetsocket(int fd)
{
struct sockaddr_ll daddr = {};
@@ -1559,6 +1855,16 @@ static void gro_sender(void)
usleep(fin_delay_us);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ /* PSP sub-tests */
+ } else if (strcmp(testname, "psp_spi_diff") == 0 ||
+ strcmp(testname, "psp_ver_diff") == 0) {
+ send_psp_assoc_switch(txfd, &daddr);
+ usleep(fin_delay_us);
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ } else if (strcmp(testname, "psp_mixed") == 0) {
+ send_psp_mixed(txfd, &daddr);
+ usleep(fin_delay_us);
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
} else {
error(1, 0, "Unknown testcase: %s", testname);
}
@@ -1782,6 +2088,22 @@ static void gro_receiver(void)
printf("different PPPoE session ID doesn't coalesce: ");
check_recv_pkts(rxfd, correct_payload, 2);
+ /* PSP sub-tests */
+ } else if (strcmp(testname, "psp_spi_diff") == 0) {
+ correct_payload[0] = PAYLOAD_LEN;
+ correct_payload[1] = PAYLOAD_LEN;
+ printf("different PSP SPI doesn't coalesce: ");
+ check_recv_pkts(rxfd, correct_payload, 2);
+ } else if (strcmp(testname, "psp_ver_diff") == 0) {
+ correct_payload[0] = PAYLOAD_LEN;
+ correct_payload[1] = PAYLOAD_LEN;
+ printf("different PSP version doesn't coalesce: ");
+ check_recv_pkts(rxfd, correct_payload, 2);
+ } else if (strcmp(testname, "psp_mixed") == 0) {
+ correct_payload[0] = PAYLOAD_LEN;
+ correct_payload[1] = PAYLOAD_LEN;
+ printf("PSP and cleartext don't coalesce: ");
+ check_recv_pkts(rxfd, correct_payload, 2);
} else {
error(1, 0, "Test case error: unknown testname %s", testname);
}
@@ -1802,6 +2124,7 @@ static void parse_args(int argc, char **argv)
{ "ip6ip6", no_argument, NULL, 'E' },
{ "pppoev4", no_argument, NULL, 'p' },
{ "pppoev6", no_argument, NULL, 'P' },
+ { "psp-assoc", required_argument, NULL, OPT_PSP_ASSOC },
{ "num-flows", required_argument, NULL, 'n' },
{ "rx", no_argument, NULL, 'r' },
{ "saddr", required_argument, NULL, 's' },
@@ -1813,8 +2136,12 @@ static void parse_args(int argc, char **argv)
};
int c;
- while ((c = getopt_long(argc, argv, "46d:D:eEi:n:pPrs:S:t:ov", opts, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "46d:D:eEi:n:pPrs:S:t:ov",
+ opts, NULL)) != -1) {
switch (c) {
+ case OPT_PSP_ASSOC:
+ psp_parse_assoc(optarg);
+ break;
case '4':
proto = PF_INET;
ethhdr_proto = htons(ETH_P_IP);
@@ -1904,6 +2231,12 @@ int main(int argc, char **argv)
error(1, 0, "Protocol family is not ipv4 or ipv6");
}
+ if (psp_enabled && (ipip || ip6ip6 || pppoe))
+ error(1, 0, "--psp-assoc doesn't support ipip, ip6ip6 or PPPoE");
+
+ if (!strncmp(testname, "psp_", 4) && !psp_enabled)
+ error(1, 0, "test %s requires --psp-assoc", testname);
+
read_MAC(src_mac, smac);
read_MAC(dst_mac, dmac);
|
| 3/2 | 2026/09/04 11:00 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 43101 output: 121 thoughts: 1527 Thoughts: **Analysis of the Patch Series for Fuzzing Suitability** Okay, let's break this down. First, I need to understand the scope of these changes. I'll start by reviewing the modified files. Looks like we're dealing with a patch series that touches: * `drivers/net/ethernet/mellanox/mlx5/`: This is the Mellanox ConnectX NIC driver space. * `include/linux/mlx5/driver.h`: Relevant Mellanox header files. * `tools/testing/selftests/...`: Self-tests. So, it's Mellanox NIC driver code and user-space tests. Now, the triage guidelines are crucial here. I need to see if this patch series falls under any of the "negative criteria" - those that would make it *not* worth fuzzing. I'm checking Section 2: "WHEN TO RETURN WorthFuzzing=false". Two things jump out immediately: 1. "Non-kernel and non-executable changes: User-space directories, self-tests... that do not affect the compiled kernel image..." The self-tests directory is a clear match for this. 2. "Structurally unreachable hardware: Vendor-specific PCIe switches, SmartNICs... requiring physical ASIC/PCIe cards not emulated in standard QEMU..." Now, is `mlx5` structurally unreachable? This is key. My expertise tells me `mlx5` is the Mellanox ConnectX NIC driver. It's designed for *physical* Mellanox hardware. Does QEMU emulate this? No, it doesn't. This confirms that Mellanox ConnectX NICs, are structurally unreachable in standard virtualized environments like QEMU and GCE, without physical Mellanox hardware. Let's double-check the diff. The C code changes are all confined to `drivers/net/ethernet/mellanox/mlx5` and header files. There's nothing outside that. The rest are selftests. So, `mlx5` is a vendor-specific NIC driver requiring real hardware; it's not emulated. `tools/testing/selftests/` are user-space tests. Therefore, the negative criteria are met. WorthFuzzing *must* be `false`. Now, I need to format the results for `set-results`: * WorthFuzzing: false * FocusSymbols: \[ ] * EnableConfigs: \[ ] * Reasoning: Vendor-specific Mellanox mlx5 hardware driver (ConnectX) not emulated in standard QEMU/GCE environments, and user-space selftests under tools/. I'm ready to invoke `set-results`. |
| 4/2 | 2026/09/04 11:00 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch touches only the vendor-specific Mellanox mlx5 driver (drivers/net/ethernet/mellanox/mlx5/ and include/linux/mlx5/) and selftests (tools/testing/selftests/). Mellanox ConnectX hardware is not emulated in standard QEMU/GCE virtualization environments, making the driver code structurally unreachable, and selftests do not affect the kernel image.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch touches only the vendor-specific Mellanox mlx5 driver (drivers/net/ethernet/mellanox/mlx5/ and include/linux/mlx5/) and selftests (tools/testing/selftests/). Mellanox ConnectX hardware is not emulated in standard QEMU/GCE virtualization environments, making the driver code structurally unreachable, and selftests do not affect the kernel image.",
"WorthFuzzing": false
} |
| 5/1 | 2026/09/04 11:00 | action | If |
0mArgs:{
"WorthFuzzing": false
}Results: null |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|