mlx5-updates-2024-08-29

HW-Managed Flow Steering in mlx5 driver
 
 Yevgeny Kliteynik says:
 =======================
 
 1. Overview
 -----------
 
 ConnectX devices support packet matching, modification, and redirection.
 This functionality is referred as Flow Steering.
 To configure a steering rule, the rule is written to the device-owned
 memory. This memory is accessed and cached by the device when processing
 a packet.
 
 The first implementation of Flow Steering was done in FW, and it is
 referred in the mlx5 driver as Device-Managed Flow Steering (DMFS).
 Later we introduced SW-managed Flow Steering (SWS or SMFS), where the
 driver is writing directly to the device's configuration memory (ICM)
 through RC QP using RDMA operations (RDMA-read and RDAM-write), thus
 achieving higher rates of rule insertion/deletion.
 
 Now we introduce a new flow steering implementation: HW-Managed Flow
 Steering (HWS or HMFS).
 
 In this new approach, the driver is configuring steering rules directly
 to the HW using the WQs with a special new type of WQE. This way we can
 reach higher rule insertion/deletion rate with much lower CPU utilization
 compared to SWS.
 
 The key benefits of HWS as opposed to SWS:
 + HW manages the steering decision tree
    - HW calculates CRC for each entry
    - HW handles tree hash collisions
    - HW & FW manage objects refcount
 + HW keeps cache coherency:
    - HW provides tree access locking and synchronization
    - HW provides notification on completion
 + Insertion rate isn’t affected by background traffic
    - Dedicated HW components that handle insertion
 
 2. Performance
 --------------
 
 Measuring Connection Tracking with simple IPv4 flows w/o NAT, we
 are able to get ~5 times more flows offloaded per second using HWS.
 
 3. Configuration
 ----------------
 
 The enablement of HWS mode in eswitch manager is done using the same
 devlink param that is already used for switching between FW-managed
 steering and SW-managed steering modes:
 
   # devlink dev param set pci/<PCI_ID> name flow_steering_mode cmod runtime value hmfs
 
 4. Upstream Submission
 ----------------------
 
 HWS support consists of 3 main components:
 + Steering:
    - The lower layer that exposes HWS API to upper layers and implements
      all the management of flow steering building blocks
 + FS-Core
    - Implementation of fs_hws layer to enable fs_core to use HWS instead
      of FW or SW steering
    - Create HW steering action pools to utilize the ability of HWS to
      share steering actions among different rules
    - Add support for configuring HWS mode through devlink command,
      similar to configuring SWS mode
 + Connection Tracking
    - Implementation of CT support for HW steering
    - Hooks up the CT ops for the new steering mode and uses the HWS API
      to implement connection tracking.
 
 Because of the large number of patches, we need to perform the submission
 in several separate patch series. This series is the first submission that
 lays the ground work for the next submissions, where an actual user of HWS
 will be added.
 
 5. Patches in this series
 -------------------------
 
 This patch series contains implementation of the first bullet from above.
 The patches are:
 
 [patch 01/15] net/mlx5: Added missing mlx5_ifc definition for HW Steering
 [patch 02/15] net/mlx5: Added missing definitions in preparation for HW Steering
 [patch 03/15] net/mlx5: HWS, added actions handling
 [patch 04/15] net/mlx5: HWS, added tables handling
 [patch 05/15] net/mlx5: HWS, added rules handling
 [patch 06/15] net/mlx5: HWS, added definers handling
 [patch 07/15] net/mlx5: HWS, added matchers functionality
 [patch 08/15] net/mlx5: HWS, added FW commands handling
 [patch 09/15] net/mlx5: HWS, added modify header pattern and args handling
 [patch 10/15] net/mlx5: HWS, added vport handling
 [patch 11/15] net/mlx5: HWS, added memory management handling
 [patch 12/15] net/mlx5: HWS, added backward-compatible API handling
 [patch 13/15] net/mlx5: HWS, added debug dump and internal headers
 [patch 14/15] net/mlx5: HWS, added send engine and context handling
 [patch 15/15] net/mlx5: HWS, added API and enabled HWS support
 
 =======================
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEEGhZs6bAKwk/OTgTpSD+KveBX+j4FAmbfOf4ACgkQSD+KveBX
 +j7hWgf/UzlKp8uyqb+7MpMWP6EgT8WUwWdpDfAr1jubIFz7e+VGaA/7QCThe89u
 alcgYvIDQCGrpB/0qXY+kaKPvqOej2wPCiLU7K2JB5y20pZ/RATlFuFBZjsMzufJ
 7NxzZgTPUDz+8OWK0mm0LxEJRJYoJ69gAnR0jvLGx9uSjv/f9lNICvWBaI58hkzb
 HJa6sJNBiFj4EnkipxWCP0GQ4dddMkgCIVYb91FtlBA4SGZtmPS35NqQJKtGnKF3
 ZhZuaTeRdw8bFDJnhbu0ur9cs4EUorZE5QBWhoHYN0zFZF4JmqCCC1HvCS7LEKaU
 PgtREk20H2jPIRFwQuX05D6M4zSizg==
 =AsB3
 -----END PGP SIGNATURE-----

Merge tag 'mlx5-updates-2024-09-02' of git://git.kernel.org/pub/scm/linux/kernel/git/saeed/linux

Saeed Mahameed says:

====================
mlx5-updates-2024-08-29

HW-Managed Flow Steering in mlx5 driver

Yevgeny Kliteynik says:
=======================

1. Overview
-----------

ConnectX devices support packet matching, modification, and redirection.
This functionality is referred as Flow Steering.
To configure a steering rule, the rule is written to the device-owned
memory. This memory is accessed and cached by the device when processing
a packet.

The first implementation of Flow Steering was done in FW, and it is
referred in the mlx5 driver as Device-Managed Flow Steering (DMFS).
Later we introduced SW-managed Flow Steering (SWS or SMFS), where the
driver is writing directly to the device's configuration memory (ICM)
through RC QP using RDMA operations (RDMA-read and RDAM-write), thus
achieving higher rates of rule insertion/deletion.

Now we introduce a new flow steering implementation: HW-Managed Flow
Steering (HWS or HMFS).

In this new approach, the driver is configuring steering rules directly
to the HW using the WQs with a special new type of WQE. This way we can
reach higher rule insertion/deletion rate with much lower CPU utilization
compared to SWS.

The key benefits of HWS as opposed to SWS:
+ HW manages the steering decision tree
   - HW calculates CRC for each entry
   - HW handles tree hash collisions
   - HW & FW manage objects refcount
+ HW keeps cache coherency:
   - HW provides tree access locking and synchronization
   - HW provides notification on completion
+ Insertion rate isn’t affected by background traffic
   - Dedicated HW components that handle insertion

2. Performance
--------------

Measuring Connection Tracking with simple IPv4 flows w/o NAT, we
are able to get ~5 times more flows offloaded per second using HWS.

3. Configuration
----------------

The enablement of HWS mode in eswitch manager is done using the same
devlink param that is already used for switching between FW-managed
steering and SW-managed steering modes:

  # devlink dev param set pci/<PCI_ID> name flow_steering_mode cmod runtime value hmfs

4. Upstream Submission
----------------------

HWS support consists of 3 main components:
+ Steering:
   - The lower layer that exposes HWS API to upper layers and implements
     all the management of flow steering building blocks
+ FS-Core
   - Implementation of fs_hws layer to enable fs_core to use HWS instead
     of FW or SW steering
   - Create HW steering action pools to utilize the ability of HWS to
     share steering actions among different rules
   - Add support for configuring HWS mode through devlink command,
     similar to configuring SWS mode
+ Connection Tracking
   - Implementation of CT support for HW steering
   - Hooks up the CT ops for the new steering mode and uses the HWS API
     to implement connection tracking.

Because of the large number of patches, we need to perform the submission
in several separate patch series. This series is the first submission that
lays the ground work for the next submissions, where an actual user of HWS
will be added.

5. Patches in this series
-------------------------

This patch series contains implementation of the first bullet from above.

=======================

* tag 'mlx5-updates-2024-09-02' of git://git.kernel.org/pub/scm/linux/kernel/git/saeed/linux:
  net/mlx5: HWS, added API and enabled HWS support
  net/mlx5: HWS, added send engine and context handling
  net/mlx5: HWS, added debug dump and internal headers
  net/mlx5: HWS, added backward-compatible API handling
  net/mlx5: HWS, added memory management handling
  net/mlx5: HWS, added vport handling
  net/mlx5: HWS, added modify header pattern and args handling
  net/mlx5: HWS, added FW commands handling
  net/mlx5: HWS, added matchers functionality
  net/mlx5: HWS, added definers handling
  net/mlx5: HWS, added rules handling
  net/mlx5: HWS, added tables handling
  net/mlx5: HWS, added actions handling
  net/mlx5: Added missing definitions in preparation for HW Steering
  net/mlx5: Added missing mlx5_ifc definition for HW Steering
====================

Link: https://patch.msgid.link/20240909181250.41596-1-saeed@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2024-09-10 20:01:15 -07:00
commit 474bb1aa45
41 changed files with 17285 additions and 36 deletions

View File

@ -130,6 +130,9 @@ Enabling the driver and kconfig options
| Build support for software-managed steering in the NIC.
**CONFIG_MLX5_HW_STEERING=(y/n)**
| Build support for hardware-managed steering in the NIC.
**CONFIG_MLX5_TC_CT=(y/n)**

View File

@ -172,6 +172,16 @@ config MLX5_SW_STEERING
help
Build support for software-managed steering in the NIC.
config MLX5_HW_STEERING
bool "Mellanox Technologies hardware-managed steering"
depends on MLX5_CORE_EN && MLX5_ESWITCH
default y
help
Build support for Hardware-Managed Flow Steering (HMFS) in the NIC.
HMFS is a new approach to managing steering rules where STEs are
written to ICM by HW (as opposed to SW in software-managed steering),
which allows higher rate of rule insertion.
config MLX5_SF
bool "Mellanox Technologies subfunction device support using auxiliary device"
depends on MLX5_CORE && MLX5_CORE_EN

View File

@ -119,6 +119,27 @@ mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o
steering/dr_action.o steering/fs_dr.o \
steering/dr_definer.o steering/dr_ptrn.o \
steering/dr_arg.o steering/dr_dbg.o lib/smfs.o
#
# HW Steering
#
mlx5_core-$(CONFIG_MLX5_HW_STEERING) += steering/hws/mlx5hws_cmd.o \
steering/hws/mlx5hws_context.o \
steering/hws/mlx5hws_pat_arg.o \
steering/hws/mlx5hws_buddy.o \
steering/hws/mlx5hws_pool.o \
steering/hws/mlx5hws_table.o \
steering/hws/mlx5hws_action.o \
steering/hws/mlx5hws_rule.o \
steering/hws/mlx5hws_matcher.o \
steering/hws/mlx5hws_send.o \
steering/hws/mlx5hws_definer.o \
steering/hws/mlx5hws_bwc.o \
steering/hws/mlx5hws_debug.o \
steering/hws/mlx5hws_vport.o \
steering/hws/mlx5hws_bwc_complex.o
#
# SF device
#

View File

@ -110,7 +110,9 @@ enum fs_flow_table_type {
FS_FT_RDMA_RX = 0X7,
FS_FT_RDMA_TX = 0X8,
FS_FT_PORT_SEL = 0X9,
FS_FT_MAX_TYPE = FS_FT_PORT_SEL,
FS_FT_FDB_RX = 0xa,
FS_FT_FDB_TX = 0xb,
FS_FT_MAX_TYPE = FS_FT_FDB_TX,
};
enum fs_flow_table_op_mod {
@ -368,7 +370,9 @@ struct mlx5_flow_root_namespace *find_root(struct fs_node *node);
(type == FS_FT_RDMA_RX) ? MLX5_CAP_FLOWTABLE_RDMA_RX(mdev, cap) : \
(type == FS_FT_RDMA_TX) ? MLX5_CAP_FLOWTABLE_RDMA_TX(mdev, cap) : \
(type == FS_FT_PORT_SEL) ? MLX5_CAP_FLOWTABLE_PORT_SELECTION(mdev, cap) : \
(BUILD_BUG_ON_ZERO(FS_FT_PORT_SEL != FS_FT_MAX_TYPE))\
(type == FS_FT_FDB_RX) ? MLX5_CAP_ESW_FLOWTABLE_FDB(mdev, cap) : \
(type == FS_FT_FDB_TX) ? MLX5_CAP_ESW_FLOWTABLE_FDB(mdev, cap) : \
(BUILD_BUG_ON_ZERO(FS_FT_FDB_TX != FS_FT_MAX_TYPE))\
)
#endif

View File

@ -251,9 +251,9 @@ int mlx5dr_cmd_query_flow_table(struct mlx5_core_dev *dev,
output->level = MLX5_GET(query_flow_table_out, out, flow_table_context.level);
output->sw_owner_icm_root_1 = MLX5_GET64(query_flow_table_out, out,
flow_table_context.sw_owner_icm_root_1);
flow_table_context.sws.sw_owner_icm_root_1);
output->sw_owner_icm_root_0 = MLX5_GET64(query_flow_table_out, out,
flow_table_context.sw_owner_icm_root_0);
flow_table_context.sws.sw_owner_icm_root_0);
return 0;
}
@ -480,15 +480,15 @@ int mlx5dr_cmd_create_flow_table(struct mlx5_core_dev *mdev,
*/
if (attr->table_type == MLX5_FLOW_TABLE_TYPE_NIC_RX) {
MLX5_SET64(flow_table_context, ft_mdev,
sw_owner_icm_root_0, attr->icm_addr_rx);
sws.sw_owner_icm_root_0, attr->icm_addr_rx);
} else if (attr->table_type == MLX5_FLOW_TABLE_TYPE_NIC_TX) {
MLX5_SET64(flow_table_context, ft_mdev,
sw_owner_icm_root_0, attr->icm_addr_tx);
sws.sw_owner_icm_root_0, attr->icm_addr_tx);
} else if (attr->table_type == MLX5_FLOW_TABLE_TYPE_FDB) {
MLX5_SET64(flow_table_context, ft_mdev,
sw_owner_icm_root_0, attr->icm_addr_rx);
sws.sw_owner_icm_root_0, attr->icm_addr_rx);
MLX5_SET64(flow_table_context, ft_mdev,
sw_owner_icm_root_1, attr->icm_addr_tx);
sws.sw_owner_icm_root_1, attr->icm_addr_tx);
}
}

View File

@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
subdir-ccflags-y += -I$(src)/..

View File

@ -0,0 +1,954 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#ifndef MLX5HWS_H_
#define MLX5HWS_H_
struct mlx5hws_context;
struct mlx5hws_table;
struct mlx5hws_matcher;
struct mlx5hws_rule;
enum mlx5hws_table_type {
MLX5HWS_TABLE_TYPE_FDB,
MLX5HWS_TABLE_TYPE_MAX,
};
enum mlx5hws_matcher_resource_mode {
/* Allocate resources based on number of rules with minimal failure probability */
MLX5HWS_MATCHER_RESOURCE_MODE_RULE,
/* Allocate fixed size hash table based on given column and rows */
MLX5HWS_MATCHER_RESOURCE_MODE_HTABLE,
};
enum mlx5hws_action_type {
MLX5HWS_ACTION_TYP_LAST,
MLX5HWS_ACTION_TYP_REFORMAT_TNL_L2_TO_L2,
MLX5HWS_ACTION_TYP_REFORMAT_L2_TO_TNL_L2,
MLX5HWS_ACTION_TYP_REFORMAT_TNL_L3_TO_L2,
MLX5HWS_ACTION_TYP_REFORMAT_L2_TO_TNL_L3,
MLX5HWS_ACTION_TYP_DROP,
MLX5HWS_ACTION_TYP_MISS,
MLX5HWS_ACTION_TYP_TBL,
MLX5HWS_ACTION_TYP_CTR,
MLX5HWS_ACTION_TYP_TAG,
MLX5HWS_ACTION_TYP_MODIFY_HDR,
MLX5HWS_ACTION_TYP_VPORT,
MLX5HWS_ACTION_TYP_POP_VLAN,
MLX5HWS_ACTION_TYP_PUSH_VLAN,
MLX5HWS_ACTION_TYP_ASO_METER,
MLX5HWS_ACTION_TYP_INSERT_HEADER,
MLX5HWS_ACTION_TYP_REMOVE_HEADER,
MLX5HWS_ACTION_TYP_RANGE,
MLX5HWS_ACTION_TYP_SAMPLER,
MLX5HWS_ACTION_TYP_DEST_ARRAY,
MLX5HWS_ACTION_TYP_MAX,
};
enum mlx5hws_action_flags {
MLX5HWS_ACTION_FLAG_HWS_FDB = 1 << 0,
/* Shared action can be used over a few threads, since the
* data is written only once at the creation of the action.
*/
MLX5HWS_ACTION_FLAG_SHARED = 1 << 1,
};
enum mlx5hws_action_aso_meter_color {
MLX5HWS_ACTION_ASO_METER_COLOR_RED = 0x0,
MLX5HWS_ACTION_ASO_METER_COLOR_YELLOW = 0x1,
MLX5HWS_ACTION_ASO_METER_COLOR_GREEN = 0x2,
MLX5HWS_ACTION_ASO_METER_COLOR_UNDEFINED = 0x3,
};
enum mlx5hws_send_queue_actions {
/* Start executing all pending queued rules */
MLX5HWS_SEND_QUEUE_ACTION_DRAIN_ASYNC = 1 << 0,
/* Start executing all pending queued rules wait till completion */
MLX5HWS_SEND_QUEUE_ACTION_DRAIN_SYNC = 1 << 1,
};
struct mlx5hws_context_attr {
u16 queues;
u16 queue_size;
bool bwc; /* add support for backward compatible API*/
};
struct mlx5hws_table_attr {
enum mlx5hws_table_type type;
u32 level;
};
enum mlx5hws_matcher_flow_src {
MLX5HWS_MATCHER_FLOW_SRC_ANY = 0x0,
MLX5HWS_MATCHER_FLOW_SRC_WIRE = 0x1,
MLX5HWS_MATCHER_FLOW_SRC_VPORT = 0x2,
};
enum mlx5hws_matcher_insert_mode {
MLX5HWS_MATCHER_INSERT_BY_HASH = 0x0,
MLX5HWS_MATCHER_INSERT_BY_INDEX = 0x1,
};
enum mlx5hws_matcher_distribute_mode {
MLX5HWS_MATCHER_DISTRIBUTE_BY_HASH = 0x0,
MLX5HWS_MATCHER_DISTRIBUTE_BY_LINEAR = 0x1,
};
struct mlx5hws_matcher_attr {
/* Processing priority inside table */
u32 priority;
/* Provide all rules with unique rule_idx in num_log range to reduce locking */
bool optimize_using_rule_idx;
/* Resource mode and corresponding size */
enum mlx5hws_matcher_resource_mode mode;
/* Optimize insertion in case packet origin is the same for all rules */
enum mlx5hws_matcher_flow_src optimize_flow_src;
/* Define the insertion and distribution modes for this matcher */
enum mlx5hws_matcher_insert_mode insert_mode;
enum mlx5hws_matcher_distribute_mode distribute_mode;
/* Define whether the created matcher supports resizing into a bigger matcher */
bool resizable;
union {
struct {
u8 sz_row_log;
u8 sz_col_log;
} table;
struct {
u8 num_log;
} rule;
};
/* Optional AT attach configuration - Max number of additional AT */
u8 max_num_of_at_attach;
};
struct mlx5hws_rule_attr {
void *user_data;
/* Valid if matcher optimize_using_rule_idx is set or
* if matcher is configured to insert rules by index.
*/
u32 rule_idx;
u32 flow_source;
u16 queue_id;
u32 burst:1;
};
/* In actions that take offset, the offset is unique, pointing to a single
* resource and the user should not reuse the same index because data changing
* is not atomic.
*/
struct mlx5hws_rule_action {
struct mlx5hws_action *action;
union {
struct {
u32 value;
} tag;
struct {
u32 offset;
} counter;
struct {
u32 offset;
u8 *data;
} modify_header;
struct {
u32 offset;
u8 hdr_idx;
u8 *data;
} reformat;
struct {
__be32 vlan_hdr;
} push_vlan;
struct {
u32 offset;
enum mlx5hws_action_aso_meter_color init_color;
} aso_meter;
};
};
struct mlx5hws_action_reformat_header {
size_t sz;
void *data;
};
struct mlx5hws_action_insert_header {
struct mlx5hws_action_reformat_header hdr;
/* PRM start anchor to which header will be inserted */
u8 anchor;
/* Header insertion offset in bytes, from the start
* anchor to the location where new header will be inserted.
*/
u8 offset;
/* Indicates this header insertion adds encapsulation header to the packet,
* requiring device to update offloaded fields (for example IPv4 total length).
*/
bool encap;
};
struct mlx5hws_action_remove_header_attr {
/* PRM start anchor from which header will be removed */
u8 anchor;
/* Header remove offset in bytes, from the start
* anchor to the location where remove header starts.
*/
u8 offset;
/* Indicates the removed header size in bytes */
size_t size;
};
struct mlx5hws_action_mh_pattern {
/* Byte size of modify actions provided by "data" */
size_t sz;
/* PRM format modify actions pattern */
__be64 *data;
};
struct mlx5hws_action_dest_attr {
/* Required destination action to forward the packet */
struct mlx5hws_action *dest;
/* Optional reformat action */
struct mlx5hws_action *reformat;
};
/* Check whether HWS is supported
*
* @param[in] mdev
* The device to check.
* @return true if supported, false otherwise.
*/
static inline bool mlx5hws_is_supported(struct mlx5_core_dev *mdev)
{
u8 ignore_flow_level_rtc_valid;
u8 wqe_based_flow_table_update;
wqe_based_flow_table_update =
MLX5_CAP_GEN(mdev, wqe_based_flow_table_update_cap);
ignore_flow_level_rtc_valid =
MLX5_CAP_FLOWTABLE(mdev,
flow_table_properties_nic_receive.ignore_flow_level_rtc_valid);
return wqe_based_flow_table_update && ignore_flow_level_rtc_valid;
}
/* Open a context used for direct rule insertion using hardware steering.
* Each context can contain multiple tables of different types.
*
* @param[in] mdev
* The device to be used for HWS.
* @param[in] attr
* Attributes used for context open.
* @return pointer to mlx5hws_context on success NULL otherwise.
*/
struct mlx5hws_context *
mlx5hws_context_open(struct mlx5_core_dev *mdev,
struct mlx5hws_context_attr *attr);
/* Close a context used for direct hardware steering.
*
* @param[in] ctx
* mlx5hws context to close.
* @return zero on success non zero otherwise.
*/
int mlx5hws_context_close(struct mlx5hws_context *ctx);
/* Set a peer context, each context can have multiple contexts as peers.
*
* @param[in] ctx
* The context in which the peer_ctx will be peered to it.
* @param[in] peer_ctx
* The peer context.
* @param[in] peer_vhca_id
* The peer context vhca id.
*/
void mlx5hws_context_set_peer(struct mlx5hws_context *ctx,
struct mlx5hws_context *peer_ctx,
u16 peer_vhca_id);
/* Create a new direct rule table. Each table can contain multiple matchers.
*
* @param[in] ctx
* The context in which the new table will be opened.
* @param[in] attr
* Attributes used for table creation.
* @return pointer to mlx5hws_table on success NULL otherwise.
*/
struct mlx5hws_table *
mlx5hws_table_create(struct mlx5hws_context *ctx,
struct mlx5hws_table_attr *attr);
/* Destroy direct rule table.
*
* @param[in] tbl
* Table to destroy.
* @return zero on success non zero otherwise.
*/
int mlx5hws_table_destroy(struct mlx5hws_table *tbl);
/* Get ID of the flow table.
*
* @param[in] tbl
* Table to get ID of.
* @return ID of the table.
*/
u32 mlx5hws_table_get_id(struct mlx5hws_table *tbl);
/* Set default miss table for mlx5hws_table by using another mlx5hws_table
* Traffic which all table matchers miss will be forwarded to miss table.
*
* @param[in] tbl
* Source table
* @param[in] miss_tbl
* Target (miss) table, or NULL to remove current miss table
* @return zero on success non zero otherwise.
*/
int mlx5hws_table_set_default_miss(struct mlx5hws_table *tbl,
struct mlx5hws_table *miss_tbl);
/* Create new match template based on items mask, the match template
* will be used for matcher creation.
*
* @param[in] ctx
* The context in which the new template will be created.
* @param[in] match_param
* Describe the mask based on PRM match parameters
* @param[in] match_param_sz
* Size of match param buffer
* @param[in] match_criteria_enable
* Bitmap for each sub-set in match_criteria buffer
* @return pointer to mlx5hws_match_template on success NULL otherwise
*/
struct mlx5hws_match_template *
mlx5hws_match_template_create(struct mlx5hws_context *ctx,
u32 *match_param,
u32 match_param_sz,
u8 match_criteria_enable);
/* Destroy match template.
*
* @param[in] mt
* Match template to destroy.
* @return zero on success non zero otherwise.
*/
int mlx5hws_match_template_destroy(struct mlx5hws_match_template *mt);
/* Create new action template based on action_type array, the action template
* will be used for matcher creation.
*
* @param[in] action_type
* An array of actions based on the order of actions which will be provided
* with rule_actions to mlx5hws_rule_create. The last action is marked
* using MLX5HWS_ACTION_TYP_LAST.
* @return pointer to mlx5hws_action_template on success NULL otherwise
*/
struct mlx5hws_action_template *
mlx5hws_action_template_create(enum mlx5hws_action_type action_type[]);
/* Destroy action template.
*
* @param[in] at
* Action template to destroy.
* @return zero on success non zero otherwise.
*/
int mlx5hws_action_template_destroy(struct mlx5hws_action_template *at);
/* Create a new direct rule matcher. Each matcher can contain multiple rules.
* Matchers on the table will be processed by priority. Matching fields and
* mask are described by the match template. In some cases multiple match
* templates can be used on the same matcher.
*
* @param[in] table
* The table in which the new matcher will be opened.
* @param[in] mt
* Array of match templates to be used on matcher.
* @param[in] num_of_mt
* Number of match templates in mt array.
* @param[in] at
* Array of action templates to be used on matcher.
* @param[in] num_of_at
* Number of action templates in mt array.
* @param[in] attr
* Attributes used for matcher creation.
* @return pointer to mlx5hws_matcher on success NULL otherwise.
*/
struct mlx5hws_matcher *
mlx5hws_matcher_create(struct mlx5hws_table *table,
struct mlx5hws_match_template *mt[],
u8 num_of_mt,
struct mlx5hws_action_template *at[],
u8 num_of_at,
struct mlx5hws_matcher_attr *attr);
/* Destroy direct rule matcher.
*
* @param[in] matcher
* Matcher to destroy.
* @return zero on success non zero otherwise.
*/
int mlx5hws_matcher_destroy(struct mlx5hws_matcher *matcher);
/* Attach new action template to direct rule matcher.
*
* @param[in] matcher
* Matcher to attach at to.
* @param[in] at
* Action template to be attached to the matcher.
* @return zero on success non zero otherwise.
*/
int mlx5hws_matcher_attach_at(struct mlx5hws_matcher *matcher,
struct mlx5hws_action_template *at);
/* Link two matchers and enable moving rules from src matcher to dst matcher.
* Both matchers must be in the same table type, must be created with 'resizable'
* property, and should have the same characteristics (e.g. same mt, same at).
*
* It is the user's responsibility to make sure that the dst matcher
* was allocated with the appropriate size.
*
* Once the function is completed, the user is:
* - allowed to move rules from src into dst matcher
* - no longer allowed to insert rules to the src matcher
*
* The user is always allowed to insert rules to the dst matcher and
* to delete rules from any matcher.
*
* @param[in] src_matcher
* source matcher for moving rules from
* @param[in] dst_matcher
* destination matcher for moving rules to
* @return zero on successful move, non zero otherwise.
*/
int mlx5hws_matcher_resize_set_target(struct mlx5hws_matcher *src_matcher,
struct mlx5hws_matcher *dst_matcher);
/* Enqueue moving rule operation: moving rule from src matcher to a dst matcher
*
* @param[in] src_matcher
* matcher that the rule belongs to
* @param[in] rule
* the rule to move
* @param[in] attr
* rule attributes
* @return zero on success, non zero otherwise.
*/
int mlx5hws_matcher_resize_rule_move(struct mlx5hws_matcher *src_matcher,
struct mlx5hws_rule *rule,
struct mlx5hws_rule_attr *attr);
/* Enqueue create rule operation.
*
* @param[in] matcher
* The matcher in which the new rule will be created.
* @param[in] mt_idx
* Match template index to create the match with.
* @param[in] match_param
* The match parameter PRM buffer used for the value matching.
* @param[in] rule_actions
* Rule action to be executed on match.
* @param[in] at_idx
* Action template index to apply the actions with.
* @param[in] num_of_actions
* Number of rule actions.
* @param[in] attr
* Rule creation attributes.
* @param[in, out] rule_handle
* A valid rule handle. The handle doesn't require any initialization.
* @return zero on successful enqueue non zero otherwise.
*/
int mlx5hws_rule_create(struct mlx5hws_matcher *matcher,
u8 mt_idx,
u32 *match_param,
u8 at_idx,
struct mlx5hws_rule_action rule_actions[],
struct mlx5hws_rule_attr *attr,
struct mlx5hws_rule *rule_handle);
/* Enqueue destroy rule operation.
*
* @param[in] rule
* The rule destruction to enqueue.
* @param[in] attr
* Rule destruction attributes.
* @return zero on successful enqueue non zero otherwise.
*/
int mlx5hws_rule_destroy(struct mlx5hws_rule *rule,
struct mlx5hws_rule_attr *attr);
/* Enqueue update actions on an existing rule.
*
* @param[in, out] rule_handle
* A valid rule handle to update.
* @param[in] at_idx
* Action template index to update the actions with.
* @param[in] rule_actions
* Rule action to be executed on match.
* @param[in] attr
* Rule update attributes.
* @return zero on successful enqueue non zero otherwise.
*/
int mlx5hws_rule_action_update(struct mlx5hws_rule *rule,
u8 at_idx,
struct mlx5hws_rule_action rule_actions[],
struct mlx5hws_rule_attr *attr);
/* Get action type.
*
* @param[in] action
* The action to get the type of.
* @return action type.
*/
enum mlx5hws_action_type
mlx5hws_action_get_type(struct mlx5hws_action *action);
/* Create direct rule drop action.
*
* @param[in] ctx
* The context in which the new action will be created.
* @param[in] flags
* Action creation flags. (enum mlx5hws_action_flags)
* @return pointer to mlx5hws_action on success NULL otherwise.
*/
struct mlx5hws_action *
mlx5hws_action_create_dest_drop(struct mlx5hws_context *ctx,
u32 flags);
/* Create direct rule default miss action.
* Defaults are RX: Drop TX: Wire.
*
* @param[in] ctx
* The context in which the new action will be created.
* @param[in] flags
* Action creation flags. (enum mlx5hws_action_flags)
* @return pointer to mlx5hws_action on success NULL otherwise.
*/
struct mlx5hws_action *
mlx5hws_action_create_default_miss(struct mlx5hws_context *ctx,
u32 flags);
/* Create direct rule goto table action.
*
* @param[in] ctx
* The context in which the new action will be created.
* @param[in] tbl
* Destination table.
* @param[in] flags
* Action creation flags. (enum mlx5hws_action_flags)
* @return pointer to mlx5hws_action on success NULL otherwise.
*/
struct mlx5hws_action *
mlx5hws_action_create_dest_table(struct mlx5hws_context *ctx,
struct mlx5hws_table *tbl,
u32 flags);
/* Create direct rule goto table number action.
*
* @param[in] ctx
* The context in which the new action will be created.
* @param[in] tbl_num
* Destination table number.
* @param[in] flags
* Action creation flags. (enum mlx5hws_action_flags)
* @return pointer to mlx5hws_action on success NULL otherwise.
*/
struct mlx5hws_action *
mlx5hws_action_create_dest_table_num(struct mlx5hws_context *ctx,
u32 table_num, u32 flags);
/* Create direct rule range match action.
*
* @param[in] ctx
* The context in which the new action will be created.
* @param[in] field
* Field to comapare the value.
* @param[in] hit_ft
* Flow table to go to on hit.
* @param[in] miss_ft
* Flow table to go to on miss.
* @param[in] min
* Minimal value of the field to be considered as hit.
* @param[in] max
* Maximal value of the field to be considered as hit.
* @param[in] flags
* Action creation flags. (enum mlx5hws_action_flags)
* @return pointer to mlx5hws_action on success NULL otherwise.
*/
struct mlx5hws_action *
mlx5hws_action_create_dest_match_range(struct mlx5hws_context *ctx,
u32 field,
struct mlx5_flow_table *hit_ft,
struct mlx5_flow_table *miss_ft,
u32 min, u32 max, u32 flags);
/* Create direct rule flow sampler action.
*
* @param[in] ctx
* The context in which the new action will be created.
* @param[in] sampler_id
* Flow sampler object ID.
* @param[in] flags
* Action creation flags. (enum mlx5hws_action_flags)
* @return pointer to mlx5hws_action on success NULL otherwise.
*/
struct mlx5hws_action *
mlx5hws_action_create_flow_sampler(struct mlx5hws_context *ctx,
u32 sampler_id, u32 flags);
/* Create direct rule goto vport action.
*
* @param[in] ctx
* The context in which the new action will be created.
* @param[in] vport_num
* Destination vport number.
* @param[in] vhca_id_valid
* Tells if the vhca_id parameter is valid.
* @param[in] vhca_id
* VHCA ID of the destination vport.
* @param[in] flags
* Action creation flags. (enum mlx5hws_action_flags)
* @return pointer to mlx5hws_action on success NULL otherwise.
*/
struct mlx5hws_action *
mlx5hws_action_create_dest_vport(struct mlx5hws_context *ctx,
u16 vport_num,
bool vhca_id_valid,
u16 vhca_id,
u32 flags);
/* Create direct rule TAG action.
*
* @param[in] ctx
* The context in which the new action will be created.
* @param[in] flags
* Action creation flags. (enum mlx5hws_action_flags)
* @return pointer to mlx5hws_action on success NULL otherwise.
*/
struct mlx5hws_action *
mlx5hws_action_create_tag(struct mlx5hws_context *ctx,
u32 flags);
/* Create direct rule counter action.
*
* @param[in] ctx
* The context in which the new action will be created.
* @param[in] obj_id
* Direct rule counter object ID.
* @param[in] flags
* Action creation flags. (enum mlx5hws_action_flags)
* @return pointer to mlx5hws_action on success NULL otherwise.
*/
struct mlx5hws_action *
mlx5hws_action_create_counter(struct mlx5hws_context *ctx,
u32 obj_id,
u32 flags);
/* Create direct rule reformat action.
*
* @param[in] ctx
* The context in which the new action will be created.
* @param[in] reformat_type
* Type of reformat prefixed with MLX5HWS_ACTION_TYP_REFORMAT.
* @param[in] num_of_hdrs
* Number of provided headers in "hdrs" array.
* @param[in] hdrs
* Headers array containing header information.
* @param[in] log_bulk_size
* Number of unique values used with this reformat.
* @param[in] flags
* Action creation flags. (enum mlx5hws_action_flags)
* @return pointer to mlx5hws_action on success NULL otherwise.
*/
struct mlx5hws_action *
mlx5hws_action_create_reformat(struct mlx5hws_context *ctx,
enum mlx5hws_action_type reformat_type,
u8 num_of_hdrs,
struct mlx5hws_action_reformat_header *hdrs,
u32 log_bulk_size,
u32 flags);
/* Create direct rule modify header action.
*
* @param[in] ctx
* The context in which the new action will be created.
* @param[in] num_of_patterns
* Number of provided patterns in "patterns" array.
* @param[in] patterns
* Patterns array containing pattern information.
* @param[in] log_bulk_size
* Number of unique values used with this pattern.
* @param[in] flags
* Action creation flags. (enum mlx5hws_action_flags)
* @return pointer to mlx5hws_action on success NULL otherwise.
*/
struct mlx5hws_action *
mlx5hws_action_create_modify_header(struct mlx5hws_context *ctx,
u8 num_of_patterns,
struct mlx5hws_action_mh_pattern *patterns,
u32 log_bulk_size,
u32 flags);
/* Create direct rule ASO flow meter action.
*
* @param[in] ctx
* The context in which the new action will be created.
* @param[in] obj_id
* ASO object ID.
* @param[in] return_reg_c
* Copy the ASO object value into this reg_c, after a packet hits a rule with this ASO object.
* @param[in] flags
* Action creation flags. (enum mlx5hws_action_flags)
* @return pointer to mlx5hws_action on success NULL otherwise.
*/
struct mlx5hws_action *
mlx5hws_action_create_aso_meter(struct mlx5hws_context *ctx,
u32 obj_id,
u8 return_reg_c,
u32 flags);
/* Create direct rule pop vlan action.
* @param[in] ctx
* The context in which the new action will be created.
* @param[in] flags
* Action creation flags. (enum mlx5hws_action_flags)
* @return pointer to mlx5hws_action on success NULL otherwise.
*/
struct mlx5hws_action *
mlx5hws_action_create_pop_vlan(struct mlx5hws_context *ctx, u32 flags);
/* Create direct rule push vlan action.
* @param[in] ctx
* The context in which the new action will be created.
* @param[in] flags
* Action creation flags. (enum mlx5hws_action_flags)
* @return pointer to mlx5hws_action on success NULL otherwise.
*/
struct mlx5hws_action *
mlx5hws_action_create_push_vlan(struct mlx5hws_context *ctx, u32 flags);
/* Create a dest array action, this action can duplicate packets and forward to
* multiple destinations in the destination list.
* @param[in] ctx
* The context in which the new action will be created.
* @param[in] num_dest
* The number of dests attributes.
* @param[in] dests
* The destination array. Each contains a destination action and can have
* additional actions.
* @param[in] ignore_flow_level
* Boolean that says whether to turn on 'ignore_flow_level' for this dest.
* @param[in] flow_source
* Source port of the traffic for this actions.
* @param[in] flags
* Action creation flags. (enum mlx5hws_action_flags)
* @return pointer to mlx5hws_action on success NULL otherwise.
*/
struct mlx5hws_action *
mlx5hws_action_create_dest_array(struct mlx5hws_context *ctx,
size_t num_dest,
struct mlx5hws_action_dest_attr *dests,
bool ignore_flow_level,
u32 flow_source,
u32 flags);
/* Create insert header action.
*
* @param[in] ctx
* The context in which the new action will be created.
* @param[in] num_of_hdrs
* Number of provided headers in "hdrs" array.
* @param[in] hdrs
* Headers array containing header information.
* @param[in] log_bulk_size
* Number of unique values used with this insert header.
* @param[in] flags
* Action creation flags. (enum mlx5hws_action_flags)
* @return pointer to mlx5hws_action on success NULL otherwise.
*/
struct mlx5hws_action *
mlx5hws_action_create_insert_header(struct mlx5hws_context *ctx,
u8 num_of_hdrs,
struct mlx5hws_action_insert_header *hdrs,
u32 log_bulk_size,
u32 flags);
/* Create remove header action.
*
* @param[in] ctx
* The context in which the new action will be created.
* @param[in] attr
* attributes: specifies the remove header type, PRM start anchor and
* the PRM end anchor or the PRM start anchor and remove size in bytes.
* @param[in] flags
* Action creation flags. (enum mlx5hws_action_flags)
* @return pointer to mlx5hws_action on success NULL otherwise.
*/
struct mlx5hws_action *
mlx5hws_action_create_remove_header(struct mlx5hws_context *ctx,
struct mlx5hws_action_remove_header_attr *attr,
u32 flags);
/* Create direct rule LAST action.
*
* @param[in] ctx
* The context in which the new action will be created.
* @param[in] flags
* Action creation flags. (enum mlx5hws_action_flags)
* @return pointer to mlx5hws_action on success NULL otherwise.
*/
struct mlx5hws_action *
mlx5hws_action_create_last(struct mlx5hws_context *ctx, u32 flags);
/* Destroy direct rule action.
*
* @param[in] action
* The action to destroy.
* @return zero on success non zero otherwise.
*/
int mlx5hws_action_destroy(struct mlx5hws_action *action);
enum mlx5hws_flow_op_status {
MLX5HWS_FLOW_OP_SUCCESS,
MLX5HWS_FLOW_OP_ERROR,
};
struct mlx5hws_flow_op_result {
enum mlx5hws_flow_op_status status;
void *user_data;
};
/* Poll queue for rule creation and deletions completions.
*
* @param[in] ctx
* The context to which the queue belong to.
* @param[in] queue_id
* The id of the queue to poll.
* @param[in, out] res
* Completion array.
* @param[in] res_nb
* Maximum number of results to return.
* @return negative number on failure, the number of completions otherwise.
*/
int mlx5hws_send_queue_poll(struct mlx5hws_context *ctx,
u16 queue_id,
struct mlx5hws_flow_op_result res[],
u32 res_nb);
/* Perform an action on the queue
*
* @param[in] ctx
* The context to which the queue belong to.
* @param[in] queue_id
* The id of the queue to perform the action on.
* @param[in] actions
* Actions to perform on the queue. (enum mlx5hws_send_queue_actions)
* @return zero on success non zero otherwise.
*/
int mlx5hws_send_queue_action(struct mlx5hws_context *ctx,
u16 queue_id,
u32 actions);
/* Dump HWS info
*
* @param[in] ctx
* The context which to dump the info from.
* @return zero on success non zero otherwise.
*/
int mlx5hws_debug_dump(struct mlx5hws_context *ctx);
struct mlx5hws_bwc_matcher;
struct mlx5hws_bwc_rule;
struct mlx5hws_match_parameters {
size_t match_sz;
u32 *match_buf; /* Device spec format */
};
/* Create a new BWC direct rule matcher.
* This function does the following:
* - creates match template based on flow items
* - creates an empty action template
* - creates a usual mlx5hws_matcher with these mt and at, setting
* its size to minimal
* Notes:
* - table->ctx must have BWC support
* - complex rules are not supported
*
* @param[in] table
* The table in which the new matcher will be opened
* @param[in] priority
* Priority for this BWC matcher
* @param[in] match_criteria_enable
* Bitmask that defines matching criteria
* @param[in] mask
* Match parameters
* @return pointer to mlx5hws_bwc_matcher on success or NULL otherwise.
*/
struct mlx5hws_bwc_matcher *
mlx5hws_bwc_matcher_create(struct mlx5hws_table *table,
u32 priority,
u8 match_criteria_enable,
struct mlx5hws_match_parameters *mask);
/* Destroy BWC direct rule matcher.
*
* @param[in] bwc_matcher
* Matcher to destroy
* @return zero on success, non zero otherwise
*/
int mlx5hws_bwc_matcher_destroy(struct mlx5hws_bwc_matcher *bwc_matcher);
/* Create a new BWC rule.
* Unlike the usual rule creation function, this one is blocking: when the
* function returns, the rule is written to its place (no need to poll).
* This function does the following:
* - finds matching action template based on the provided rule_actions, or
* creates new action template if matching action template doesn't exist
* - updates corresponding BWC matcher stats
* - if needed, the function performs rehash:
* - creates a new matcher based on mt, at, new_sz
* - moves all the existing matcher rules to the new matcher
* - removes the old matcher
* - inserts new rule
* - polls till completion is received
* Notes:
* - matcher->tbl->ctx must have BWC support
* - separate BWC ctx queues are used
*
* @param[in] bwc_matcher
* The BWC matcher in which the new rule will be created.
* @param[in] params
* Match perameters
* @param[in] flow_source
* Flow source for this rule
* @param[in] rule_actions
* Rule action to be executed on match
* @return valid BWC rule handle on success, NULL otherwise
*/
struct mlx5hws_bwc_rule *
mlx5hws_bwc_rule_create(struct mlx5hws_bwc_matcher *bwc_matcher,
struct mlx5hws_match_parameters *params,
u32 flow_source,
struct mlx5hws_rule_action rule_actions[]);
/* Destroy BWC direct rule.
*
* @param[in] bwc_rule
* Rule to destroy
* @return zero on success, non zero otherwise
*/
int mlx5hws_bwc_rule_destroy(struct mlx5hws_bwc_rule *bwc_rule);
/* Update actions on an existing BWC rule.
*
* @param[in] bwc_rule
* Rule to update
* @param[in] rule_actions
* Rule action to update with
* @return zero on successful update, non zero otherwise.
*/
int mlx5hws_bwc_rule_action_update(struct mlx5hws_bwc_rule *bwc_rule,
struct mlx5hws_rule_action rule_actions[]);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,307 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#ifndef MLX5HWS_ACTION_H_
#define MLX5HWS_ACTION_H_
/* Max number of STEs needed for a rule (including match) */
#define MLX5HWS_ACTION_MAX_STE 20
/* Max number of internal subactions of ipv6_ext */
#define MLX5HWS_ACTION_IPV6_EXT_MAX_SA 4
enum mlx5hws_action_stc_idx {
MLX5HWS_ACTION_STC_IDX_CTRL = 0,
MLX5HWS_ACTION_STC_IDX_HIT = 1,
MLX5HWS_ACTION_STC_IDX_DW5 = 2,
MLX5HWS_ACTION_STC_IDX_DW6 = 3,
MLX5HWS_ACTION_STC_IDX_DW7 = 4,
MLX5HWS_ACTION_STC_IDX_MAX = 5,
/* STC Jumvo STE combo: CTR, Hit */
MLX5HWS_ACTION_STC_IDX_LAST_JUMBO_STE = 1,
/* STC combo1: CTR, SINGLE, DOUBLE, Hit */
MLX5HWS_ACTION_STC_IDX_LAST_COMBO1 = 3,
/* STC combo2: CTR, 3 x SINGLE, Hit */
MLX5HWS_ACTION_STC_IDX_LAST_COMBO2 = 4,
/* STC combo2: CTR, TRIPLE, Hit */
MLX5HWS_ACTION_STC_IDX_LAST_COMBO3 = 2,
};
enum mlx5hws_action_offset {
MLX5HWS_ACTION_OFFSET_DW0 = 0,
MLX5HWS_ACTION_OFFSET_DW5 = 5,
MLX5HWS_ACTION_OFFSET_DW6 = 6,
MLX5HWS_ACTION_OFFSET_DW7 = 7,
MLX5HWS_ACTION_OFFSET_HIT = 3,
MLX5HWS_ACTION_OFFSET_HIT_LSB = 4,
};
enum {
MLX5HWS_ACTION_DOUBLE_SIZE = 8,
MLX5HWS_ACTION_INLINE_DATA_SIZE = 4,
MLX5HWS_ACTION_HDR_LEN_L2_MACS = 12,
MLX5HWS_ACTION_HDR_LEN_L2_VLAN = 4,
MLX5HWS_ACTION_HDR_LEN_L2_ETHER = 2,
MLX5HWS_ACTION_HDR_LEN_L2 = (MLX5HWS_ACTION_HDR_LEN_L2_MACS +
MLX5HWS_ACTION_HDR_LEN_L2_ETHER),
MLX5HWS_ACTION_HDR_LEN_L2_W_VLAN = (MLX5HWS_ACTION_HDR_LEN_L2 +
MLX5HWS_ACTION_HDR_LEN_L2_VLAN),
MLX5HWS_ACTION_REFORMAT_DATA_SIZE = 64,
DECAP_L3_NUM_ACTIONS_W_NO_VLAN = 6,
DECAP_L3_NUM_ACTIONS_W_VLAN = 7,
};
enum mlx5hws_action_setter_flag {
ASF_SINGLE1 = 1 << 0,
ASF_SINGLE2 = 1 << 1,
ASF_SINGLE3 = 1 << 2,
ASF_DOUBLE = ASF_SINGLE2 | ASF_SINGLE3,
ASF_TRIPLE = ASF_SINGLE1 | ASF_DOUBLE,
ASF_INSERT = 1 << 3,
ASF_REMOVE = 1 << 4,
ASF_MODIFY = 1 << 5,
ASF_CTR = 1 << 6,
ASF_HIT = 1 << 7,
};
struct mlx5hws_action_default_stc {
struct mlx5hws_pool_chunk nop_ctr;
struct mlx5hws_pool_chunk nop_dw5;
struct mlx5hws_pool_chunk nop_dw6;
struct mlx5hws_pool_chunk nop_dw7;
struct mlx5hws_pool_chunk default_hit;
u32 refcount;
};
struct mlx5hws_action_shared_stc {
struct mlx5hws_pool_chunk stc_chunk;
u32 refcount;
};
struct mlx5hws_actions_apply_data {
struct mlx5hws_send_engine *queue;
struct mlx5hws_rule_action *rule_action;
__be32 *wqe_data;
struct mlx5hws_wqe_gta_ctrl_seg *wqe_ctrl;
u32 jump_to_action_stc;
struct mlx5hws_context_common_res *common_res;
enum mlx5hws_table_type tbl_type;
u32 next_direct_idx;
u8 require_dep;
};
struct mlx5hws_actions_wqe_setter;
typedef void (*mlx5hws_action_setter_fp)(struct mlx5hws_actions_apply_data *apply,
struct mlx5hws_actions_wqe_setter *setter);
struct mlx5hws_actions_wqe_setter {
mlx5hws_action_setter_fp set_single;
mlx5hws_action_setter_fp set_double;
mlx5hws_action_setter_fp set_triple;
mlx5hws_action_setter_fp set_hit;
mlx5hws_action_setter_fp set_ctr;
u8 idx_single;
u8 idx_double;
u8 idx_triple;
u8 idx_ctr;
u8 idx_hit;
u8 stage_idx;
u8 flags;
};
struct mlx5hws_action_template {
struct mlx5hws_actions_wqe_setter setters[MLX5HWS_ACTION_MAX_STE];
enum mlx5hws_action_type *action_type_arr;
u8 num_of_action_stes;
u8 num_actions;
u8 only_term;
};
struct mlx5hws_action {
u8 type;
u8 flags;
struct mlx5hws_context *ctx;
union {
struct {
struct mlx5hws_pool_chunk stc[MLX5HWS_TABLE_TYPE_MAX];
union {
struct {
u32 pat_id;
u32 arg_id;
__be64 single_action;
u32 nope_locations;
u8 num_of_patterns;
u8 single_action_type;
u8 num_of_actions;
u8 max_num_of_actions;
u8 require_reparse;
} modify_header;
struct {
u32 arg_id;
u32 header_size;
u16 max_hdr_sz;
u8 num_of_hdrs;
u8 anchor;
u8 e_anchor;
u8 offset;
bool encap;
u8 require_reparse;
} reformat;
struct {
u32 obj_id;
u8 return_reg_id;
} aso;
struct {
u16 vport_num;
u16 esw_owner_vhca_id;
bool esw_owner_vhca_id_valid;
} vport;
struct {
u32 obj_id;
} dest_obj;
struct {
struct mlx5hws_cmd_forward_tbl *fw_island;
size_t num_dest;
struct mlx5hws_cmd_set_fte_dest *dest_list;
} dest_array;
struct {
u8 type;
u8 start_anchor;
u8 end_anchor;
u8 num_of_words;
bool decap;
} insert_hdr;
struct {
/* PRM start anchor from which header will be removed */
u8 anchor;
/* Header remove offset in bytes, from the start
* anchor to the location where remove header starts.
*/
u8 offset;
/* Indicates the removed header size in bytes */
size_t size;
} remove_header;
struct {
struct mlx5hws_matcher_action_ste *table_ste;
struct mlx5hws_action *hit_ft_action;
struct mlx5hws_definer *definer;
} range;
};
};
struct ibv_flow_action *flow_action;
u32 obj_id;
struct ibv_qp *qp;
};
};
const char *mlx5hws_action_type_to_str(enum mlx5hws_action_type action_type);
int mlx5hws_action_get_default_stc(struct mlx5hws_context *ctx,
u8 tbl_type);
void mlx5hws_action_put_default_stc(struct mlx5hws_context *ctx,
u8 tbl_type);
void mlx5hws_action_prepare_decap_l3_data(u8 *src, u8 *dst,
u16 num_of_actions);
int mlx5hws_action_template_process(struct mlx5hws_action_template *at);
bool mlx5hws_action_check_combo(struct mlx5hws_context *ctx,
enum mlx5hws_action_type *user_actions,
enum mlx5hws_table_type table_type);
int mlx5hws_action_alloc_single_stc(struct mlx5hws_context *ctx,
struct mlx5hws_cmd_stc_modify_attr *stc_attr,
u32 table_type,
struct mlx5hws_pool_chunk *stc);
void mlx5hws_action_free_single_stc(struct mlx5hws_context *ctx,
u32 table_type,
struct mlx5hws_pool_chunk *stc);
static inline void
mlx5hws_action_setter_default_single(struct mlx5hws_actions_apply_data *apply,
struct mlx5hws_actions_wqe_setter *setter)
{
apply->wqe_data[MLX5HWS_ACTION_OFFSET_DW5] = 0;
apply->wqe_ctrl->stc_ix[MLX5HWS_ACTION_STC_IDX_DW5] =
htonl(apply->common_res->default_stc->nop_dw5.offset);
}
static inline void
mlx5hws_action_setter_default_double(struct mlx5hws_actions_apply_data *apply,
struct mlx5hws_actions_wqe_setter *setter)
{
apply->wqe_data[MLX5HWS_ACTION_OFFSET_DW6] = 0;
apply->wqe_data[MLX5HWS_ACTION_OFFSET_DW7] = 0;
apply->wqe_ctrl->stc_ix[MLX5HWS_ACTION_STC_IDX_DW6] =
htonl(apply->common_res->default_stc->nop_dw6.offset);
apply->wqe_ctrl->stc_ix[MLX5HWS_ACTION_STC_IDX_DW7] =
htonl(apply->common_res->default_stc->nop_dw7.offset);
}
static inline void
mlx5hws_action_setter_default_ctr(struct mlx5hws_actions_apply_data *apply,
struct mlx5hws_actions_wqe_setter *setter)
{
apply->wqe_data[MLX5HWS_ACTION_OFFSET_DW0] = 0;
apply->wqe_ctrl->stc_ix[MLX5HWS_ACTION_STC_IDX_CTRL] =
htonl(apply->common_res->default_stc->nop_ctr.offset);
}
static inline void
mlx5hws_action_apply_setter(struct mlx5hws_actions_apply_data *apply,
struct mlx5hws_actions_wqe_setter *setter,
bool is_jumbo)
{
u8 num_of_actions;
/* Set control counter */
if (setter->set_ctr)
setter->set_ctr(apply, setter);
else
mlx5hws_action_setter_default_ctr(apply, setter);
if (!is_jumbo) {
if (unlikely(setter->set_triple)) {
/* Set triple on match */
setter->set_triple(apply, setter);
num_of_actions = MLX5HWS_ACTION_STC_IDX_LAST_COMBO3;
} else {
/* Set single and double on match */
if (setter->set_single)
setter->set_single(apply, setter);
else
mlx5hws_action_setter_default_single(apply, setter);
if (setter->set_double)
setter->set_double(apply, setter);
else
mlx5hws_action_setter_default_double(apply, setter);
num_of_actions = setter->set_double ?
MLX5HWS_ACTION_STC_IDX_LAST_COMBO1 :
MLX5HWS_ACTION_STC_IDX_LAST_COMBO2;
}
} else {
apply->wqe_data[MLX5HWS_ACTION_OFFSET_DW5] = 0;
apply->wqe_data[MLX5HWS_ACTION_OFFSET_DW6] = 0;
apply->wqe_data[MLX5HWS_ACTION_OFFSET_DW7] = 0;
apply->wqe_ctrl->stc_ix[MLX5HWS_ACTION_STC_IDX_DW5] = 0;
apply->wqe_ctrl->stc_ix[MLX5HWS_ACTION_STC_IDX_DW6] = 0;
apply->wqe_ctrl->stc_ix[MLX5HWS_ACTION_STC_IDX_DW7] = 0;
num_of_actions = MLX5HWS_ACTION_STC_IDX_LAST_JUMBO_STE;
}
/* Set next/final hit action */
setter->set_hit(apply, setter);
/* Set number of actions */
apply->wqe_ctrl->stc_ix[MLX5HWS_ACTION_STC_IDX_CTRL] |=
htonl(num_of_actions << 29);
}
#endif /* MLX5HWS_ACTION_H_ */

View File

@ -0,0 +1,149 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#include "mlx5hws_internal.h"
#include "mlx5hws_buddy.h"
static int hws_buddy_init(struct mlx5hws_buddy_mem *buddy, u32 max_order)
{
int i, s, ret = 0;
buddy->max_order = max_order;
buddy->bitmap = kcalloc(buddy->max_order + 1,
sizeof(*buddy->bitmap),
GFP_KERNEL);
if (!buddy->bitmap)
return -ENOMEM;
buddy->num_free = kcalloc(buddy->max_order + 1,
sizeof(*buddy->num_free),
GFP_KERNEL);
if (!buddy->num_free) {
ret = -ENOMEM;
goto err_out_free_bits;
}
for (i = 0; i <= (int)buddy->max_order; ++i) {
s = 1 << (buddy->max_order - i);
buddy->bitmap[i] = bitmap_zalloc(s, GFP_KERNEL);
if (!buddy->bitmap[i]) {
ret = -ENOMEM;
goto err_out_free_num_free;
}
}
bitmap_set(buddy->bitmap[buddy->max_order], 0, 1);
buddy->num_free[buddy->max_order] = 1;
return 0;
err_out_free_num_free:
for (i = 0; i <= (int)buddy->max_order; ++i)
bitmap_free(buddy->bitmap[i]);
kfree(buddy->num_free);
err_out_free_bits:
kfree(buddy->bitmap);
return ret;
}
struct mlx5hws_buddy_mem *mlx5hws_buddy_create(u32 max_order)
{
struct mlx5hws_buddy_mem *buddy;
buddy = kzalloc(sizeof(*buddy), GFP_KERNEL);
if (!buddy)
return NULL;
if (hws_buddy_init(buddy, max_order))
goto free_buddy;
return buddy;
free_buddy:
kfree(buddy);
return NULL;
}
void mlx5hws_buddy_cleanup(struct mlx5hws_buddy_mem *buddy)
{
int i;
for (i = 0; i <= (int)buddy->max_order; ++i)
bitmap_free(buddy->bitmap[i]);
kfree(buddy->num_free);
kfree(buddy->bitmap);
}
static int hws_buddy_find_free_seg(struct mlx5hws_buddy_mem *buddy,
u32 start_order,
u32 *segment,
u32 *order)
{
unsigned int seg, order_iter, m;
for (order_iter = start_order;
order_iter <= buddy->max_order; ++order_iter) {
if (!buddy->num_free[order_iter])
continue;
m = 1 << (buddy->max_order - order_iter);
seg = find_first_bit(buddy->bitmap[order_iter], m);
if (WARN(seg >= m,
"ICM Buddy: failed finding free mem for order %d\n",
order_iter))
return -ENOMEM;
break;
}
if (order_iter > buddy->max_order)
return -ENOMEM;
*segment = seg;
*order = order_iter;
return 0;
}
int mlx5hws_buddy_alloc_mem(struct mlx5hws_buddy_mem *buddy, u32 order)
{
u32 seg, order_iter, err;
err = hws_buddy_find_free_seg(buddy, order, &seg, &order_iter);
if (err)
return err;
bitmap_clear(buddy->bitmap[order_iter], seg, 1);
--buddy->num_free[order_iter];
while (order_iter > order) {
--order_iter;
seg <<= 1;
bitmap_set(buddy->bitmap[order_iter], seg ^ 1, 1);
++buddy->num_free[order_iter];
}
seg <<= order;
return seg;
}
void mlx5hws_buddy_free_mem(struct mlx5hws_buddy_mem *buddy, u32 seg, u32 order)
{
seg >>= order;
while (test_bit(seg ^ 1, buddy->bitmap[order])) {
bitmap_clear(buddy->bitmap[order], seg ^ 1, 1);
--buddy->num_free[order];
seg >>= 1;
++order;
}
bitmap_set(buddy->bitmap[order], seg, 1);
++buddy->num_free[order];
}

View File

@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#ifndef MLX5HWS_BUDDY_H_
#define MLX5HWS_BUDDY_H_
struct mlx5hws_buddy_mem {
unsigned long **bitmap;
unsigned int *num_free;
u32 max_order;
};
struct mlx5hws_buddy_mem *mlx5hws_buddy_create(u32 max_order);
void mlx5hws_buddy_cleanup(struct mlx5hws_buddy_mem *buddy);
int mlx5hws_buddy_alloc_mem(struct mlx5hws_buddy_mem *buddy, u32 order);
void mlx5hws_buddy_free_mem(struct mlx5hws_buddy_mem *buddy, u32 seg, u32 order);
#endif /* MLX5HWS_BUDDY_H_ */

View File

@ -0,0 +1,997 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#include "mlx5hws_internal.h"
static u16 hws_bwc_gen_queue_idx(struct mlx5hws_context *ctx)
{
/* assign random queue */
return get_random_u8() % mlx5hws_bwc_queues(ctx);
}
static u16
hws_bwc_get_burst_th(struct mlx5hws_context *ctx, u16 queue_id)
{
return min(ctx->send_queue[queue_id].num_entries / 2,
MLX5HWS_BWC_MATCHER_REHASH_BURST_TH);
}
static struct mutex *
hws_bwc_get_queue_lock(struct mlx5hws_context *ctx, u16 idx)
{
return &ctx->bwc_send_queue_locks[idx];
}
static void hws_bwc_lock_all_queues(struct mlx5hws_context *ctx)
{
u16 bwc_queues = mlx5hws_bwc_queues(ctx);
struct mutex *queue_lock; /* Protect the queue */
int i;
for (i = 0; i < bwc_queues; i++) {
queue_lock = hws_bwc_get_queue_lock(ctx, i);
mutex_lock(queue_lock);
}
}
static void hws_bwc_unlock_all_queues(struct mlx5hws_context *ctx)
{
u16 bwc_queues = mlx5hws_bwc_queues(ctx);
struct mutex *queue_lock; /* Protect the queue */
int i = bwc_queues;
while (i--) {
queue_lock = hws_bwc_get_queue_lock(ctx, i);
mutex_unlock(queue_lock);
}
}
static void hws_bwc_matcher_init_attr(struct mlx5hws_matcher_attr *attr,
u32 priority,
u8 size_log)
{
memset(attr, 0, sizeof(*attr));
attr->priority = priority;
attr->optimize_using_rule_idx = 0;
attr->mode = MLX5HWS_MATCHER_RESOURCE_MODE_RULE;
attr->optimize_flow_src = MLX5HWS_MATCHER_FLOW_SRC_ANY;
attr->insert_mode = MLX5HWS_MATCHER_INSERT_BY_HASH;
attr->distribute_mode = MLX5HWS_MATCHER_DISTRIBUTE_BY_HASH;
attr->rule.num_log = size_log;
attr->resizable = true;
attr->max_num_of_at_attach = MLX5HWS_BWC_MATCHER_ATTACH_AT_NUM;
}
int mlx5hws_bwc_matcher_create_simple(struct mlx5hws_bwc_matcher *bwc_matcher,
struct mlx5hws_table *table,
u32 priority,
u8 match_criteria_enable,
struct mlx5hws_match_parameters *mask,
enum mlx5hws_action_type action_types[])
{
enum mlx5hws_action_type init_action_types[1] = { MLX5HWS_ACTION_TYP_LAST };
struct mlx5hws_context *ctx = table->ctx;
u16 bwc_queues = mlx5hws_bwc_queues(ctx);
struct mlx5hws_matcher_attr attr = {0};
int i;
bwc_matcher->rules = kcalloc(bwc_queues, sizeof(*bwc_matcher->rules), GFP_KERNEL);
if (!bwc_matcher->rules)
goto err;
for (i = 0; i < bwc_queues; i++)
INIT_LIST_HEAD(&bwc_matcher->rules[i]);
hws_bwc_matcher_init_attr(&attr,
priority,
MLX5HWS_BWC_MATCHER_INIT_SIZE_LOG);
bwc_matcher->priority = priority;
bwc_matcher->size_log = MLX5HWS_BWC_MATCHER_INIT_SIZE_LOG;
/* create dummy action template */
bwc_matcher->at[0] =
mlx5hws_action_template_create(action_types ?
action_types : init_action_types);
if (!bwc_matcher->at[0]) {
mlx5hws_err(table->ctx, "BWC matcher: failed creating action template\n");
goto free_bwc_matcher_rules;
}
bwc_matcher->num_of_at = 1;
bwc_matcher->mt = mlx5hws_match_template_create(ctx,
mask->match_buf,
mask->match_sz,
match_criteria_enable);
if (!bwc_matcher->mt) {
mlx5hws_err(table->ctx, "BWC matcher: failed creating match template\n");
goto free_at;
}
bwc_matcher->matcher = mlx5hws_matcher_create(table,
&bwc_matcher->mt, 1,
&bwc_matcher->at[0],
bwc_matcher->num_of_at,
&attr);
if (!bwc_matcher->matcher) {
mlx5hws_err(table->ctx, "BWC matcher: failed creating HWS matcher\n");
goto free_mt;
}
return 0;
free_mt:
mlx5hws_match_template_destroy(bwc_matcher->mt);
free_at:
mlx5hws_action_template_destroy(bwc_matcher->at[0]);
free_bwc_matcher_rules:
kfree(bwc_matcher->rules);
err:
return -EINVAL;
}
struct mlx5hws_bwc_matcher *
mlx5hws_bwc_matcher_create(struct mlx5hws_table *table,
u32 priority,
u8 match_criteria_enable,
struct mlx5hws_match_parameters *mask)
{
struct mlx5hws_bwc_matcher *bwc_matcher;
bool is_complex;
int ret;
if (!mlx5hws_context_bwc_supported(table->ctx)) {
mlx5hws_err(table->ctx,
"BWC matcher: context created w/o BWC API compatibility\n");
return NULL;
}
bwc_matcher = kzalloc(sizeof(*bwc_matcher), GFP_KERNEL);
if (!bwc_matcher)
return NULL;
/* Check if the required match params can be all matched
* in single STE, otherwise complex matcher is needed.
*/
is_complex = mlx5hws_bwc_match_params_is_complex(table->ctx, match_criteria_enable, mask);
if (is_complex)
ret = mlx5hws_bwc_matcher_create_complex(bwc_matcher,
table,
priority,
match_criteria_enable,
mask);
else
ret = mlx5hws_bwc_matcher_create_simple(bwc_matcher,
table,
priority,
match_criteria_enable,
mask,
NULL);
if (ret)
goto free_bwc_matcher;
return bwc_matcher;
free_bwc_matcher:
kfree(bwc_matcher);
return NULL;
}
int mlx5hws_bwc_matcher_destroy_simple(struct mlx5hws_bwc_matcher *bwc_matcher)
{
int i;
mlx5hws_matcher_destroy(bwc_matcher->matcher);
bwc_matcher->matcher = NULL;
for (i = 0; i < bwc_matcher->num_of_at; i++)
mlx5hws_action_template_destroy(bwc_matcher->at[i]);
mlx5hws_match_template_destroy(bwc_matcher->mt);
kfree(bwc_matcher->rules);
return 0;
}
int mlx5hws_bwc_matcher_destroy(struct mlx5hws_bwc_matcher *bwc_matcher)
{
if (bwc_matcher->num_of_rules)
mlx5hws_err(bwc_matcher->matcher->tbl->ctx,
"BWC matcher destroy: matcher still has %d rules\n",
bwc_matcher->num_of_rules);
mlx5hws_bwc_matcher_destroy_simple(bwc_matcher);
kfree(bwc_matcher);
return 0;
}
static int hws_bwc_queue_poll(struct mlx5hws_context *ctx,
u16 queue_id,
u32 *pending_rules,
bool drain)
{
struct mlx5hws_flow_op_result comp[MLX5HWS_BWC_MATCHER_REHASH_BURST_TH];
u16 burst_th = hws_bwc_get_burst_th(ctx, queue_id);
bool got_comp = *pending_rules >= burst_th;
bool queue_full;
int err = 0;
int ret;
int i;
/* Check if there are any completions at all */
if (!got_comp && !drain)
return 0;
queue_full = mlx5hws_send_engine_full(&ctx->send_queue[queue_id]);
while (queue_full || ((got_comp || drain) && *pending_rules)) {
ret = mlx5hws_send_queue_poll(ctx, queue_id, comp, burst_th);
if (unlikely(ret < 0)) {
mlx5hws_err(ctx, "BWC poll error: polling queue %d returned %d\n",
queue_id, ret);
return -EINVAL;
}
if (ret) {
(*pending_rules) -= ret;
for (i = 0; i < ret; i++) {
if (unlikely(comp[i].status != MLX5HWS_FLOW_OP_SUCCESS)) {
mlx5hws_err(ctx,
"BWC poll error: polling queue %d returned completion with error\n",
queue_id);
err = -EINVAL;
}
}
queue_full = false;
}
got_comp = !!ret;
}
return err;
}
void
mlx5hws_bwc_rule_fill_attr(struct mlx5hws_bwc_matcher *bwc_matcher,
u16 bwc_queue_idx,
u32 flow_source,
struct mlx5hws_rule_attr *rule_attr)
{
struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
/* no use of INSERT_BY_INDEX in bwc rule */
rule_attr->rule_idx = 0;
/* notify HW at each rule insertion/deletion */
rule_attr->burst = 0;
/* We don't need user data, but the API requires it to exist */
rule_attr->user_data = (void *)0xFACADE;
rule_attr->queue_id = mlx5hws_bwc_get_queue_id(ctx, bwc_queue_idx);
rule_attr->flow_source = flow_source;
}
struct mlx5hws_bwc_rule *
mlx5hws_bwc_rule_alloc(struct mlx5hws_bwc_matcher *bwc_matcher)
{
struct mlx5hws_bwc_rule *bwc_rule;
bwc_rule = kzalloc(sizeof(*bwc_rule), GFP_KERNEL);
if (unlikely(!bwc_rule))
goto out_err;
bwc_rule->rule = kzalloc(sizeof(*bwc_rule->rule), GFP_KERNEL);
if (unlikely(!bwc_rule->rule))
goto free_rule;
bwc_rule->bwc_matcher = bwc_matcher;
return bwc_rule;
free_rule:
kfree(bwc_rule);
out_err:
return NULL;
}
void mlx5hws_bwc_rule_free(struct mlx5hws_bwc_rule *bwc_rule)
{
if (likely(bwc_rule->rule))
kfree(bwc_rule->rule);
kfree(bwc_rule);
}
static void hws_bwc_rule_list_add(struct mlx5hws_bwc_rule *bwc_rule, u16 idx)
{
struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
bwc_matcher->num_of_rules++;
bwc_rule->bwc_queue_idx = idx;
list_add(&bwc_rule->list_node, &bwc_matcher->rules[idx]);
}
static void hws_bwc_rule_list_remove(struct mlx5hws_bwc_rule *bwc_rule)
{
struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
bwc_matcher->num_of_rules--;
list_del_init(&bwc_rule->list_node);
}
static int
hws_bwc_rule_destroy_hws_async(struct mlx5hws_bwc_rule *bwc_rule,
struct mlx5hws_rule_attr *attr)
{
return mlx5hws_rule_destroy(bwc_rule->rule, attr);
}
static int
hws_bwc_rule_destroy_hws_sync(struct mlx5hws_bwc_rule *bwc_rule,
struct mlx5hws_rule_attr *rule_attr)
{
struct mlx5hws_context *ctx = bwc_rule->bwc_matcher->matcher->tbl->ctx;
struct mlx5hws_flow_op_result completion;
int ret;
ret = hws_bwc_rule_destroy_hws_async(bwc_rule, rule_attr);
if (unlikely(ret))
return ret;
do {
ret = mlx5hws_send_queue_poll(ctx, rule_attr->queue_id, &completion, 1);
} while (ret != 1);
if (unlikely(completion.status != MLX5HWS_FLOW_OP_SUCCESS ||
(bwc_rule->rule->status != MLX5HWS_RULE_STATUS_DELETED &&
bwc_rule->rule->status != MLX5HWS_RULE_STATUS_DELETING))) {
mlx5hws_err(ctx, "Failed destroying BWC rule: completion %d, rule status %d\n",
completion.status, bwc_rule->rule->status);
return -EINVAL;
}
return 0;
}
int mlx5hws_bwc_rule_destroy_simple(struct mlx5hws_bwc_rule *bwc_rule)
{
struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
u16 idx = bwc_rule->bwc_queue_idx;
struct mlx5hws_rule_attr attr;
struct mutex *queue_lock; /* Protect the queue */
int ret;
mlx5hws_bwc_rule_fill_attr(bwc_matcher, idx, 0, &attr);
queue_lock = hws_bwc_get_queue_lock(ctx, idx);
mutex_lock(queue_lock);
ret = hws_bwc_rule_destroy_hws_sync(bwc_rule, &attr);
hws_bwc_rule_list_remove(bwc_rule);
mutex_unlock(queue_lock);
return ret;
}
int mlx5hws_bwc_rule_destroy(struct mlx5hws_bwc_rule *bwc_rule)
{
int ret;
ret = mlx5hws_bwc_rule_destroy_simple(bwc_rule);
mlx5hws_bwc_rule_free(bwc_rule);
return ret;
}
static int
hws_bwc_rule_create_async(struct mlx5hws_bwc_rule *bwc_rule,
u32 *match_param,
u8 at_idx,
struct mlx5hws_rule_action rule_actions[],
struct mlx5hws_rule_attr *rule_attr)
{
return mlx5hws_rule_create(bwc_rule->bwc_matcher->matcher,
0, /* only one match template supported */
match_param,
at_idx,
rule_actions,
rule_attr,
bwc_rule->rule);
}
static int
hws_bwc_rule_create_sync(struct mlx5hws_bwc_rule *bwc_rule,
u32 *match_param,
u8 at_idx,
struct mlx5hws_rule_action rule_actions[],
struct mlx5hws_rule_attr *rule_attr)
{
struct mlx5hws_context *ctx = bwc_rule->bwc_matcher->matcher->tbl->ctx;
u32 expected_completions = 1;
int ret;
ret = hws_bwc_rule_create_async(bwc_rule, match_param,
at_idx, rule_actions,
rule_attr);
if (unlikely(ret))
return ret;
ret = hws_bwc_queue_poll(ctx, rule_attr->queue_id, &expected_completions, true);
return ret;
}
static int
hws_bwc_rule_update_sync(struct mlx5hws_bwc_rule *bwc_rule,
u8 at_idx,
struct mlx5hws_rule_action rule_actions[],
struct mlx5hws_rule_attr *rule_attr)
{
struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
u32 expected_completions = 1;
int ret;
ret = mlx5hws_rule_action_update(bwc_rule->rule,
at_idx,
rule_actions,
rule_attr);
if (unlikely(ret))
return ret;
ret = hws_bwc_queue_poll(ctx, rule_attr->queue_id, &expected_completions, true);
if (unlikely(ret))
mlx5hws_err(ctx, "Failed updating BWC rule (%d)\n", ret);
return ret;
}
static bool
hws_bwc_matcher_size_maxed_out(struct mlx5hws_bwc_matcher *bwc_matcher)
{
struct mlx5hws_cmd_query_caps *caps = bwc_matcher->matcher->tbl->ctx->caps;
return bwc_matcher->size_log + MLX5HWS_MATCHER_ASSURED_MAIN_TBL_DEPTH >=
caps->ste_alloc_log_max - 1;
}
static bool
hws_bwc_matcher_rehash_size_needed(struct mlx5hws_bwc_matcher *bwc_matcher,
u32 num_of_rules)
{
if (unlikely(hws_bwc_matcher_size_maxed_out(bwc_matcher)))
return false;
if (unlikely((num_of_rules * 100 / MLX5HWS_BWC_MATCHER_REHASH_PERCENT_TH) >=
(1UL << bwc_matcher->size_log)))
return true;
return false;
}
static void
hws_bwc_rule_actions_to_action_types(struct mlx5hws_rule_action rule_actions[],
enum mlx5hws_action_type action_types[])
{
int i = 0;
for (i = 0;
rule_actions[i].action && (rule_actions[i].action->type != MLX5HWS_ACTION_TYP_LAST);
i++) {
action_types[i] = (enum mlx5hws_action_type)rule_actions[i].action->type;
}
action_types[i] = MLX5HWS_ACTION_TYP_LAST;
}
static int
hws_bwc_matcher_extend_at(struct mlx5hws_bwc_matcher *bwc_matcher,
struct mlx5hws_rule_action rule_actions[])
{
enum mlx5hws_action_type action_types[MLX5HWS_BWC_MAX_ACTS];
hws_bwc_rule_actions_to_action_types(rule_actions, action_types);
bwc_matcher->at[bwc_matcher->num_of_at] =
mlx5hws_action_template_create(action_types);
if (unlikely(!bwc_matcher->at[bwc_matcher->num_of_at]))
return -ENOMEM;
bwc_matcher->num_of_at++;
return 0;
}
static int
hws_bwc_matcher_extend_size(struct mlx5hws_bwc_matcher *bwc_matcher)
{
struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
struct mlx5hws_cmd_query_caps *caps = ctx->caps;
if (unlikely(hws_bwc_matcher_size_maxed_out(bwc_matcher))) {
mlx5hws_err(ctx, "Can't resize matcher: depth exceeds limit %d\n",
caps->rtc_log_depth_max);
return -ENOMEM;
}
bwc_matcher->size_log =
min(bwc_matcher->size_log + MLX5HWS_BWC_MATCHER_SIZE_LOG_STEP,
caps->ste_alloc_log_max - MLX5HWS_MATCHER_ASSURED_MAIN_TBL_DEPTH);
return 0;
}
static int
hws_bwc_matcher_find_at(struct mlx5hws_bwc_matcher *bwc_matcher,
struct mlx5hws_rule_action rule_actions[])
{
enum mlx5hws_action_type *action_type_arr;
int i, j;
/* start from index 1 - first action template is a dummy */
for (i = 1; i < bwc_matcher->num_of_at; i++) {
j = 0;
action_type_arr = bwc_matcher->at[i]->action_type_arr;
while (rule_actions[j].action &&
rule_actions[j].action->type != MLX5HWS_ACTION_TYP_LAST) {
if (action_type_arr[j] != rule_actions[j].action->type)
break;
j++;
}
if (action_type_arr[j] == MLX5HWS_ACTION_TYP_LAST &&
(!rule_actions[j].action ||
rule_actions[j].action->type == MLX5HWS_ACTION_TYP_LAST))
return i;
}
return -1;
}
static int hws_bwc_matcher_move_all_simple(struct mlx5hws_bwc_matcher *bwc_matcher)
{
struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
u16 bwc_queues = mlx5hws_bwc_queues(ctx);
struct mlx5hws_bwc_rule **bwc_rules;
struct mlx5hws_rule_attr rule_attr;
u32 *pending_rules;
int i, j, ret = 0;
bool all_done;
u16 burst_th;
mlx5hws_bwc_rule_fill_attr(bwc_matcher, 0, 0, &rule_attr);
pending_rules = kcalloc(bwc_queues, sizeof(*pending_rules), GFP_KERNEL);
if (!pending_rules)
return -ENOMEM;
bwc_rules = kcalloc(bwc_queues, sizeof(*bwc_rules), GFP_KERNEL);
if (!bwc_rules) {
ret = -ENOMEM;
goto free_pending_rules;
}
for (i = 0; i < bwc_queues; i++) {
if (list_empty(&bwc_matcher->rules[i]))
bwc_rules[i] = NULL;
else
bwc_rules[i] = list_first_entry(&bwc_matcher->rules[i],
struct mlx5hws_bwc_rule,
list_node);
}
do {
all_done = true;
for (i = 0; i < bwc_queues; i++) {
rule_attr.queue_id = mlx5hws_bwc_get_queue_id(ctx, i);
burst_th = hws_bwc_get_burst_th(ctx, rule_attr.queue_id);
for (j = 0; j < burst_th && bwc_rules[i]; j++) {
rule_attr.burst = !!((j + 1) % burst_th);
ret = mlx5hws_matcher_resize_rule_move(bwc_matcher->matcher,
bwc_rules[i]->rule,
&rule_attr);
if (unlikely(ret)) {
mlx5hws_err(ctx,
"Moving BWC rule failed during rehash (%d)\n",
ret);
goto free_bwc_rules;
}
all_done = false;
pending_rules[i]++;
bwc_rules[i] = list_is_last(&bwc_rules[i]->list_node,
&bwc_matcher->rules[i]) ?
NULL : list_next_entry(bwc_rules[i], list_node);
ret = hws_bwc_queue_poll(ctx, rule_attr.queue_id,
&pending_rules[i], false);
if (unlikely(ret))
goto free_bwc_rules;
}
}
} while (!all_done);
/* drain all the bwc queues */
for (i = 0; i < bwc_queues; i++) {
if (pending_rules[i]) {
u16 queue_id = mlx5hws_bwc_get_queue_id(ctx, i);
mlx5hws_send_engine_flush_queue(&ctx->send_queue[queue_id]);
ret = hws_bwc_queue_poll(ctx, queue_id,
&pending_rules[i], true);
if (unlikely(ret))
goto free_bwc_rules;
}
}
free_bwc_rules:
kfree(bwc_rules);
free_pending_rules:
kfree(pending_rules);
return ret;
}
static int hws_bwc_matcher_move_all(struct mlx5hws_bwc_matcher *bwc_matcher)
{
return hws_bwc_matcher_move_all_simple(bwc_matcher);
}
static int hws_bwc_matcher_move(struct mlx5hws_bwc_matcher *bwc_matcher)
{
struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
struct mlx5hws_matcher_attr matcher_attr = {0};
struct mlx5hws_matcher *old_matcher;
struct mlx5hws_matcher *new_matcher;
int ret;
hws_bwc_matcher_init_attr(&matcher_attr,
bwc_matcher->priority,
bwc_matcher->size_log);
old_matcher = bwc_matcher->matcher;
new_matcher = mlx5hws_matcher_create(old_matcher->tbl,
&bwc_matcher->mt, 1,
bwc_matcher->at,
bwc_matcher->num_of_at,
&matcher_attr);
if (!new_matcher) {
mlx5hws_err(ctx, "Rehash error: matcher creation failed\n");
return -ENOMEM;
}
ret = mlx5hws_matcher_resize_set_target(old_matcher, new_matcher);
if (ret) {
mlx5hws_err(ctx, "Rehash error: failed setting resize target\n");
return ret;
}
ret = hws_bwc_matcher_move_all(bwc_matcher);
if (ret) {
mlx5hws_err(ctx, "Rehash error: moving rules failed\n");
return -ENOMEM;
}
bwc_matcher->matcher = new_matcher;
mlx5hws_matcher_destroy(old_matcher);
return 0;
}
static int
hws_bwc_matcher_rehash_size(struct mlx5hws_bwc_matcher *bwc_matcher)
{
u32 num_of_rules;
int ret;
/* If the current matcher size is already at its max size, we can't
* do the rehash. Skip it and try adding the rule again - perhaps
* there was some change.
*/
if (hws_bwc_matcher_size_maxed_out(bwc_matcher))
return 0;
/* It is possible that other rule has already performed rehash.
* Need to check again if we really need rehash.
* If the reason for rehash was size, but not any more - skip rehash.
*/
num_of_rules = __atomic_load_n(&bwc_matcher->num_of_rules, __ATOMIC_RELAXED);
if (!hws_bwc_matcher_rehash_size_needed(bwc_matcher, num_of_rules))
return 0;
/* Now we're done all the checking - do the rehash:
* - extend match RTC size
* - create new matcher
* - move all the rules to the new matcher
* - destroy the old matcher
*/
ret = hws_bwc_matcher_extend_size(bwc_matcher);
if (ret)
return ret;
return hws_bwc_matcher_move(bwc_matcher);
}
static int
hws_bwc_matcher_rehash_at(struct mlx5hws_bwc_matcher *bwc_matcher)
{
/* Rehash by action template doesn't require any additional checking.
* The bwc_matcher already contains the new action template.
* Just do the usual rehash:
* - create new matcher
* - move all the rules to the new matcher
* - destroy the old matcher
*/
return hws_bwc_matcher_move(bwc_matcher);
}
int mlx5hws_bwc_rule_create_simple(struct mlx5hws_bwc_rule *bwc_rule,
u32 *match_param,
struct mlx5hws_rule_action rule_actions[],
u32 flow_source,
u16 bwc_queue_idx)
{
struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
struct mlx5hws_rule_attr rule_attr;
struct mutex *queue_lock; /* Protect the queue */
u32 num_of_rules;
int ret = 0;
int at_idx;
mlx5hws_bwc_rule_fill_attr(bwc_matcher, bwc_queue_idx, flow_source, &rule_attr);
queue_lock = hws_bwc_get_queue_lock(ctx, bwc_queue_idx);
mutex_lock(queue_lock);
/* check if rehash needed due to missing action template */
at_idx = hws_bwc_matcher_find_at(bwc_matcher, rule_actions);
if (unlikely(at_idx < 0)) {
/* we need to extend BWC matcher action templates array */
mutex_unlock(queue_lock);
hws_bwc_lock_all_queues(ctx);
ret = hws_bwc_matcher_extend_at(bwc_matcher, rule_actions);
if (unlikely(ret)) {
hws_bwc_unlock_all_queues(ctx);
return ret;
}
/* action templates array was extended, we need the last idx */
at_idx = bwc_matcher->num_of_at - 1;
ret = mlx5hws_matcher_attach_at(bwc_matcher->matcher,
bwc_matcher->at[at_idx]);
if (unlikely(ret)) {
/* Action template attach failed, possibly due to
* requiring more action STEs.
* Need to attempt creating new matcher with all
* the action templates, including the new one.
*/
ret = hws_bwc_matcher_rehash_at(bwc_matcher);
if (unlikely(ret)) {
mlx5hws_action_template_destroy(bwc_matcher->at[at_idx]);
bwc_matcher->at[at_idx] = NULL;
bwc_matcher->num_of_at--;
hws_bwc_unlock_all_queues(ctx);
mlx5hws_err(ctx,
"BWC rule insertion: rehash AT failed (%d)\n", ret);
return ret;
}
}
hws_bwc_unlock_all_queues(ctx);
mutex_lock(queue_lock);
}
/* check if number of rules require rehash */
num_of_rules = bwc_matcher->num_of_rules;
if (unlikely(hws_bwc_matcher_rehash_size_needed(bwc_matcher, num_of_rules))) {
mutex_unlock(queue_lock);
hws_bwc_lock_all_queues(ctx);
ret = hws_bwc_matcher_rehash_size(bwc_matcher);
hws_bwc_unlock_all_queues(ctx);
if (ret) {
mlx5hws_err(ctx, "BWC rule insertion: rehash size [%d -> %d] failed (%d)\n",
bwc_matcher->size_log - MLX5HWS_BWC_MATCHER_SIZE_LOG_STEP,
bwc_matcher->size_log,
ret);
return ret;
}
mutex_lock(queue_lock);
}
ret = hws_bwc_rule_create_sync(bwc_rule,
match_param,
at_idx,
rule_actions,
&rule_attr);
if (likely(!ret)) {
hws_bwc_rule_list_add(bwc_rule, bwc_queue_idx);
mutex_unlock(queue_lock);
return 0; /* rule inserted successfully */
}
/* At this point the rule wasn't added.
* It could be because there was collision, or some other problem.
* If we don't dive deeper than API, the only thing we know is that
* the status of completion is RTE_FLOW_OP_ERROR.
* Try rehash by size and insert rule again - last chance.
*/
mutex_unlock(queue_lock);
hws_bwc_lock_all_queues(ctx);
ret = hws_bwc_matcher_rehash_size(bwc_matcher);
hws_bwc_unlock_all_queues(ctx);
if (ret) {
mlx5hws_err(ctx, "BWC rule insertion: rehash failed (%d)\n", ret);
return ret;
}
/* Rehash done, but we still have that pesky rule to add */
mutex_lock(queue_lock);
ret = hws_bwc_rule_create_sync(bwc_rule,
match_param,
at_idx,
rule_actions,
&rule_attr);
if (unlikely(ret)) {
mutex_unlock(queue_lock);
mlx5hws_err(ctx, "BWC rule insertion failed (%d)\n", ret);
return ret;
}
hws_bwc_rule_list_add(bwc_rule, bwc_queue_idx);
mutex_unlock(queue_lock);
return 0;
}
struct mlx5hws_bwc_rule *
mlx5hws_bwc_rule_create(struct mlx5hws_bwc_matcher *bwc_matcher,
struct mlx5hws_match_parameters *params,
u32 flow_source,
struct mlx5hws_rule_action rule_actions[])
{
struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
struct mlx5hws_bwc_rule *bwc_rule;
u16 bwc_queue_idx;
int ret;
if (unlikely(!mlx5hws_context_bwc_supported(ctx))) {
mlx5hws_err(ctx, "BWC rule: Context created w/o BWC API compatibility\n");
return NULL;
}
bwc_rule = mlx5hws_bwc_rule_alloc(bwc_matcher);
if (unlikely(!bwc_rule))
return NULL;
bwc_queue_idx = hws_bwc_gen_queue_idx(ctx);
ret = mlx5hws_bwc_rule_create_simple(bwc_rule,
params->match_buf,
rule_actions,
flow_source,
bwc_queue_idx);
if (unlikely(ret)) {
mlx5hws_bwc_rule_free(bwc_rule);
return NULL;
}
return bwc_rule;
}
static int
hws_bwc_rule_action_update(struct mlx5hws_bwc_rule *bwc_rule,
struct mlx5hws_rule_action rule_actions[])
{
struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
struct mlx5hws_rule_attr rule_attr;
struct mutex *queue_lock; /* Protect the queue */
int at_idx, ret;
u16 idx;
idx = bwc_rule->bwc_queue_idx;
mlx5hws_bwc_rule_fill_attr(bwc_matcher, idx, 0, &rule_attr);
queue_lock = hws_bwc_get_queue_lock(ctx, idx);
mutex_lock(queue_lock);
/* check if rehash needed due to missing action template */
at_idx = hws_bwc_matcher_find_at(bwc_matcher, rule_actions);
if (unlikely(at_idx < 0)) {
/* we need to extend BWC matcher action templates array */
mutex_unlock(queue_lock);
hws_bwc_lock_all_queues(ctx);
/* check again - perhaps other thread already did extend_at */
at_idx = hws_bwc_matcher_find_at(bwc_matcher, rule_actions);
if (likely(at_idx < 0)) {
ret = hws_bwc_matcher_extend_at(bwc_matcher, rule_actions);
if (unlikely(ret)) {
hws_bwc_unlock_all_queues(ctx);
mlx5hws_err(ctx, "BWC rule update: failed extending AT (%d)", ret);
return -EINVAL;
}
/* action templates array was extended, we need the last idx */
at_idx = bwc_matcher->num_of_at - 1;
ret = mlx5hws_matcher_attach_at(bwc_matcher->matcher,
bwc_matcher->at[at_idx]);
if (unlikely(ret)) {
/* Action template attach failed, possibly due to
* requiring more action STEs.
* Need to attempt creating new matcher with all
* the action templates, including the new one.
*/
ret = hws_bwc_matcher_rehash_at(bwc_matcher);
if (unlikely(ret)) {
mlx5hws_action_template_destroy(bwc_matcher->at[at_idx]);
bwc_matcher->at[at_idx] = NULL;
bwc_matcher->num_of_at--;
hws_bwc_unlock_all_queues(ctx);
mlx5hws_err(ctx,
"BWC rule update: rehash AT failed (%d)\n",
ret);
return ret;
}
}
}
hws_bwc_unlock_all_queues(ctx);
mutex_lock(queue_lock);
}
ret = hws_bwc_rule_update_sync(bwc_rule,
at_idx,
rule_actions,
&rule_attr);
mutex_unlock(queue_lock);
if (unlikely(ret))
mlx5hws_err(ctx, "BWC rule: update failed (%d)\n", ret);
return ret;
}
int mlx5hws_bwc_rule_action_update(struct mlx5hws_bwc_rule *bwc_rule,
struct mlx5hws_rule_action rule_actions[])
{
struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
if (unlikely(!mlx5hws_context_bwc_supported(ctx))) {
mlx5hws_err(ctx, "BWC rule: Context created w/o BWC API compatibility\n");
return -EINVAL;
}
return hws_bwc_rule_action_update(bwc_rule, rule_actions);
}

View File

@ -0,0 +1,73 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#ifndef MLX5HWS_BWC_H_
#define MLX5HWS_BWC_H_
#define MLX5HWS_BWC_MATCHER_INIT_SIZE_LOG 1
#define MLX5HWS_BWC_MATCHER_SIZE_LOG_STEP 1
#define MLX5HWS_BWC_MATCHER_REHASH_PERCENT_TH 70
#define MLX5HWS_BWC_MATCHER_REHASH_BURST_TH 32
#define MLX5HWS_BWC_MATCHER_ATTACH_AT_NUM 255
#define MLX5HWS_BWC_MAX_ACTS 16
struct mlx5hws_bwc_matcher {
struct mlx5hws_matcher *matcher;
struct mlx5hws_match_template *mt;
struct mlx5hws_action_template *at[MLX5HWS_BWC_MATCHER_ATTACH_AT_NUM];
u8 num_of_at;
u16 priority;
u8 size_log;
u32 num_of_rules; /* atomically accessed */
struct list_head *rules;
};
struct mlx5hws_bwc_rule {
struct mlx5hws_bwc_matcher *bwc_matcher;
struct mlx5hws_rule *rule;
u16 bwc_queue_idx;
struct list_head list_node;
};
int
mlx5hws_bwc_matcher_create_simple(struct mlx5hws_bwc_matcher *bwc_matcher,
struct mlx5hws_table *table,
u32 priority,
u8 match_criteria_enable,
struct mlx5hws_match_parameters *mask,
enum mlx5hws_action_type action_types[]);
int mlx5hws_bwc_matcher_destroy_simple(struct mlx5hws_bwc_matcher *bwc_matcher);
struct mlx5hws_bwc_rule *mlx5hws_bwc_rule_alloc(struct mlx5hws_bwc_matcher *bwc_matcher);
void mlx5hws_bwc_rule_free(struct mlx5hws_bwc_rule *bwc_rule);
int mlx5hws_bwc_rule_create_simple(struct mlx5hws_bwc_rule *bwc_rule,
u32 *match_param,
struct mlx5hws_rule_action rule_actions[],
u32 flow_source,
u16 bwc_queue_idx);
int mlx5hws_bwc_rule_destroy_simple(struct mlx5hws_bwc_rule *bwc_rule);
void mlx5hws_bwc_rule_fill_attr(struct mlx5hws_bwc_matcher *bwc_matcher,
u16 bwc_queue_idx,
u32 flow_source,
struct mlx5hws_rule_attr *rule_attr);
static inline u16 mlx5hws_bwc_queues(struct mlx5hws_context *ctx)
{
/* Besides the control queue, half of the queues are
* reguler HWS queues, and the other half are BWC queues.
*/
return (ctx->queues - 1) / 2;
}
static inline u16 mlx5hws_bwc_get_queue_id(struct mlx5hws_context *ctx, u16 idx)
{
return idx + mlx5hws_bwc_queues(ctx);
}
#endif /* MLX5HWS_BWC_H_ */

View File

@ -0,0 +1,86 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#include "mlx5hws_internal.h"
bool mlx5hws_bwc_match_params_is_complex(struct mlx5hws_context *ctx,
u8 match_criteria_enable,
struct mlx5hws_match_parameters *mask)
{
struct mlx5hws_definer match_layout = {0};
struct mlx5hws_match_template *mt;
bool is_complex = false;
int ret;
if (!match_criteria_enable)
return false; /* empty matcher */
mt = mlx5hws_match_template_create(ctx,
mask->match_buf,
mask->match_sz,
match_criteria_enable);
if (!mt) {
mlx5hws_err(ctx, "BWC: failed creating match template\n");
return false;
}
ret = mlx5hws_definer_calc_layout(ctx, mt, &match_layout);
if (ret) {
/* The only case that we're interested in is E2BIG,
* which means that the match parameters need to be
* split into complex martcher.
* For all other cases (good or bad) - just return true
* and let the usual match creation path handle it,
* both for good and bad flows.
*/
if (ret == E2BIG) {
is_complex = true;
mlx5hws_dbg(ctx, "Matcher definer layout: need complex matcher\n");
} else {
mlx5hws_err(ctx, "Failed to calculate matcher definer layout\n");
}
}
mlx5hws_match_template_destroy(mt);
return is_complex;
}
int mlx5hws_bwc_matcher_create_complex(struct mlx5hws_bwc_matcher *bwc_matcher,
struct mlx5hws_table *table,
u32 priority,
u8 match_criteria_enable,
struct mlx5hws_match_parameters *mask)
{
mlx5hws_err(table->ctx, "Complex matcher is not supported yet\n");
return -EOPNOTSUPP;
}
void
mlx5hws_bwc_matcher_destroy_complex(struct mlx5hws_bwc_matcher *bwc_matcher)
{
/* nothing to do here */
}
int mlx5hws_bwc_rule_create_complex(struct mlx5hws_bwc_rule *bwc_rule,
struct mlx5hws_match_parameters *params,
u32 flow_source,
struct mlx5hws_rule_action rule_actions[],
u16 bwc_queue_idx)
{
mlx5hws_err(bwc_rule->bwc_matcher->matcher->tbl->ctx,
"Complex rule is not supported yet\n");
return -EOPNOTSUPP;
}
int mlx5hws_bwc_rule_destroy_complex(struct mlx5hws_bwc_rule *bwc_rule)
{
return 0;
}
int mlx5hws_bwc_matcher_move_all_complex(struct mlx5hws_bwc_matcher *bwc_matcher)
{
mlx5hws_err(bwc_matcher->matcher->tbl->ctx,
"Moving complex rule is not supported yet\n");
return -EOPNOTSUPP;
}

View File

@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#ifndef MLX5HWS_BWC_COMPLEX_H_
#define MLX5HWS_BWC_COMPLEX_H_
bool mlx5hws_bwc_match_params_is_complex(struct mlx5hws_context *ctx,
u8 match_criteria_enable,
struct mlx5hws_match_parameters *mask);
int mlx5hws_bwc_matcher_create_complex(struct mlx5hws_bwc_matcher *bwc_matcher,
struct mlx5hws_table *table,
u32 priority,
u8 match_criteria_enable,
struct mlx5hws_match_parameters *mask);
void mlx5hws_bwc_matcher_destroy_complex(struct mlx5hws_bwc_matcher *bwc_matcher);
int mlx5hws_bwc_matcher_move_all_complex(struct mlx5hws_bwc_matcher *bwc_matcher);
int mlx5hws_bwc_rule_create_complex(struct mlx5hws_bwc_rule *bwc_rule,
struct mlx5hws_match_parameters *params,
u32 flow_source,
struct mlx5hws_rule_action rule_actions[],
u16 bwc_queue_idx);
int mlx5hws_bwc_rule_destroy_complex(struct mlx5hws_bwc_rule *bwc_rule);
#endif /* MLX5HWS_BWC_COMPLEX_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,361 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#ifndef MLX5HWS_CMD_H_
#define MLX5HWS_CMD_H_
#define WIRE_PORT 0xFFFF
#define ACCESS_KEY_LEN 32
enum mlx5hws_cmd_ext_dest_flags {
MLX5HWS_CMD_EXT_DEST_REFORMAT = 1 << 0,
MLX5HWS_CMD_EXT_DEST_ESW_OWNER_VHCA_ID = 1 << 1,
};
struct mlx5hws_cmd_set_fte_dest {
u8 destination_type;
u32 destination_id;
enum mlx5hws_cmd_ext_dest_flags ext_flags;
u32 ext_reformat_id;
u16 esw_owner_vhca_id;
};
struct mlx5hws_cmd_set_fte_attr {
u32 action_flags;
bool ignore_flow_level;
u8 flow_source;
u8 extended_dest;
u8 encrypt_decrypt_type;
u32 encrypt_decrypt_obj_id;
u32 packet_reformat_id;
u32 dests_num;
struct mlx5hws_cmd_set_fte_dest *dests;
};
struct mlx5hws_cmd_ft_create_attr {
u8 type;
u8 level;
bool rtc_valid;
bool decap_en;
bool reformat_en;
};
struct mlx5hws_cmd_ft_modify_attr {
u8 type;
u32 rtc_id_0;
u32 rtc_id_1;
u32 table_miss_id;
u8 table_miss_action;
u64 modify_fs;
};
struct mlx5hws_cmd_ft_query_attr {
u8 type;
};
struct mlx5hws_cmd_fg_attr {
u32 table_id;
u32 table_type;
};
struct mlx5hws_cmd_forward_tbl {
u8 type;
u32 ft_id;
u32 fg_id;
u32 refcount;
};
struct mlx5hws_cmd_rtc_create_attr {
u32 pd;
u32 stc_base;
u32 ste_base;
u32 ste_offset;
u32 miss_ft_id;
bool fw_gen_wqe;
u8 update_index_mode;
u8 access_index_mode;
u8 num_hash_definer;
u8 log_depth;
u8 log_size;
u8 table_type;
u8 match_definer_0;
u8 match_definer_1;
u8 reparse_mode;
bool is_frst_jumbo;
bool is_scnd_range;
};
struct mlx5hws_cmd_alias_obj_create_attr {
u32 obj_id;
u16 vhca_id;
u16 obj_type;
u8 access_key[ACCESS_KEY_LEN];
};
struct mlx5hws_cmd_stc_create_attr {
u8 log_obj_range;
u8 table_type;
};
struct mlx5hws_cmd_stc_modify_attr {
u32 stc_offset;
u8 action_offset;
u8 reparse_mode;
enum mlx5_ifc_stc_action_type action_type;
union {
u32 id; /* TIRN, TAG, FT ID, STE ID, CRYPTO */
struct {
u8 decap;
u16 start_anchor;
u16 end_anchor;
} remove_header;
struct {
u32 arg_id;
u32 pattern_id;
} modify_header;
struct {
__be64 data;
} modify_action;
struct {
u32 arg_id;
u32 header_size;
u8 is_inline;
u8 encap;
u16 insert_anchor;
u16 insert_offset;
} insert_header;
struct {
u8 aso_type;
u32 devx_obj_id;
u8 return_reg_id;
} aso;
struct {
u16 vport_num;
u16 esw_owner_vhca_id;
u8 eswitch_owner_vhca_id_valid;
} vport;
struct {
struct mlx5hws_pool_chunk ste;
struct mlx5hws_pool *ste_pool;
u32 ste_obj_id; /* Internal */
u32 match_definer_id;
u8 log_hash_size;
bool ignore_tx;
} ste_table;
struct {
u16 start_anchor;
u16 num_of_words;
} remove_words;
struct {
u8 type;
u8 op;
u8 size;
} reformat_trailer;
u32 dest_table_id;
u32 dest_tir_num;
};
};
struct mlx5hws_cmd_ste_create_attr {
u8 log_obj_range;
u8 table_type;
};
struct mlx5hws_cmd_definer_create_attr {
u8 *dw_selector;
u8 *byte_selector;
u8 *match_mask;
};
struct mlx5hws_cmd_allow_other_vhca_access_attr {
u16 obj_type;
u32 obj_id;
u8 access_key[ACCESS_KEY_LEN];
};
struct mlx5hws_cmd_packet_reformat_create_attr {
u8 type;
size_t data_sz;
void *data;
u8 reformat_param_0;
};
struct mlx5hws_cmd_query_ft_caps {
u8 max_level;
u8 reparse;
u8 ignore_flow_level_rtc_valid;
};
struct mlx5hws_cmd_generate_wqe_attr {
u8 *wqe_ctrl;
u8 *gta_ctrl;
u8 *gta_data_0;
u8 *gta_data_1;
u32 pdn;
};
struct mlx5hws_cmd_query_caps {
u32 flex_protocols;
u8 wqe_based_update;
u8 rtc_reparse_mode;
u16 ste_format;
u8 rtc_index_mode;
u8 ste_alloc_log_max;
u8 ste_alloc_log_gran;
u8 stc_alloc_log_max;
u8 stc_alloc_log_gran;
u8 rtc_log_depth_max;
u8 format_select_gtpu_dw_0;
u8 format_select_gtpu_dw_1;
u8 flow_table_hash_type;
u8 format_select_gtpu_dw_2;
u8 format_select_gtpu_ext_dw_0;
u8 access_index_mode;
u32 linear_match_definer;
bool full_dw_jumbo_support;
bool rtc_hash_split_table;
bool rtc_linear_lookup_table;
u32 supp_type_gen_wqe;
u8 rtc_max_hash_def_gen_wqe;
u16 supp_ste_format_gen_wqe;
struct mlx5hws_cmd_query_ft_caps nic_ft;
struct mlx5hws_cmd_query_ft_caps fdb_ft;
bool eswitch_manager;
bool merged_eswitch;
u32 eswitch_manager_vport_number;
u8 log_header_modify_argument_granularity;
u8 log_header_modify_argument_max_alloc;
u8 sq_ts_format;
u8 fdb_tir_stc;
u64 definer_format_sup;
u32 trivial_match_definer;
u32 vhca_id;
u32 shared_vhca_id;
char fw_ver[64];
bool ipsec_offload;
bool is_ecpf;
u8 flex_parser_ok_bits_supp;
u8 flex_parser_id_geneve_tlv_option_0;
u8 flex_parser_id_mpls_over_gre;
u8 flex_parser_id_mpls_over_udp;
};
int mlx5hws_cmd_flow_table_create(struct mlx5_core_dev *mdev,
struct mlx5hws_cmd_ft_create_attr *ft_attr,
u32 *table_id);
int mlx5hws_cmd_flow_table_modify(struct mlx5_core_dev *mdev,
struct mlx5hws_cmd_ft_modify_attr *ft_attr,
u32 table_id);
int mlx5hws_cmd_flow_table_query(struct mlx5_core_dev *mdev,
u32 obj_id,
struct mlx5hws_cmd_ft_query_attr *ft_attr,
u64 *icm_addr_0, u64 *icm_addr_1);
int mlx5hws_cmd_flow_table_destroy(struct mlx5_core_dev *mdev,
u8 fw_ft_type, u32 table_id);
void mlx5hws_cmd_alias_flow_table_destroy(struct mlx5_core_dev *mdev,
u32 table_id);
int mlx5hws_cmd_rtc_create(struct mlx5_core_dev *mdev,
struct mlx5hws_cmd_rtc_create_attr *rtc_attr,
u32 *rtc_id);
void mlx5hws_cmd_rtc_destroy(struct mlx5_core_dev *mdev, u32 rtc_id);
int mlx5hws_cmd_stc_create(struct mlx5_core_dev *mdev,
struct mlx5hws_cmd_stc_create_attr *stc_attr,
u32 *stc_id);
int mlx5hws_cmd_stc_modify(struct mlx5_core_dev *mdev,
u32 stc_id,
struct mlx5hws_cmd_stc_modify_attr *stc_attr);
void mlx5hws_cmd_stc_destroy(struct mlx5_core_dev *mdev, u32 stc_id);
int mlx5hws_cmd_generate_wqe(struct mlx5_core_dev *mdev,
struct mlx5hws_cmd_generate_wqe_attr *attr,
struct mlx5_cqe64 *ret_cqe);
int mlx5hws_cmd_ste_create(struct mlx5_core_dev *mdev,
struct mlx5hws_cmd_ste_create_attr *ste_attr,
u32 *ste_id);
void mlx5hws_cmd_ste_destroy(struct mlx5_core_dev *mdev, u32 ste_id);
int mlx5hws_cmd_definer_create(struct mlx5_core_dev *mdev,
struct mlx5hws_cmd_definer_create_attr *def_attr,
u32 *definer_id);
void mlx5hws_cmd_definer_destroy(struct mlx5_core_dev *mdev,
u32 definer_id);
int mlx5hws_cmd_arg_create(struct mlx5_core_dev *mdev,
u16 log_obj_range,
u32 pd,
u32 *arg_id);
void mlx5hws_cmd_arg_destroy(struct mlx5_core_dev *mdev,
u32 arg_id);
int mlx5hws_cmd_header_modify_pattern_create(struct mlx5_core_dev *mdev,
u32 pattern_length,
u8 *actions,
u32 *ptrn_id);
void mlx5hws_cmd_header_modify_pattern_destroy(struct mlx5_core_dev *mdev,
u32 ptrn_id);
int mlx5hws_cmd_packet_reformat_create(struct mlx5_core_dev *mdev,
struct mlx5hws_cmd_packet_reformat_create_attr *attr,
u32 *reformat_id);
int mlx5hws_cmd_packet_reformat_destroy(struct mlx5_core_dev *mdev,
u32 reformat_id);
int mlx5hws_cmd_set_fte(struct mlx5_core_dev *mdev,
u32 table_type,
u32 table_id,
u32 group_id,
struct mlx5hws_cmd_set_fte_attr *fte_attr);
int mlx5hws_cmd_delete_fte(struct mlx5_core_dev *mdev,
u32 table_type, u32 table_id);
struct mlx5hws_cmd_forward_tbl *
mlx5hws_cmd_forward_tbl_create(struct mlx5_core_dev *mdev,
struct mlx5hws_cmd_ft_create_attr *ft_attr,
struct mlx5hws_cmd_set_fte_attr *fte_attr);
void mlx5hws_cmd_forward_tbl_destroy(struct mlx5_core_dev *mdev,
struct mlx5hws_cmd_forward_tbl *tbl);
int mlx5hws_cmd_alias_obj_create(struct mlx5_core_dev *mdev,
struct mlx5hws_cmd_alias_obj_create_attr *alias_attr,
u32 *obj_id);
int mlx5hws_cmd_alias_obj_destroy(struct mlx5_core_dev *mdev,
u16 obj_type,
u32 obj_id);
int mlx5hws_cmd_sq_modify_rdy(struct mlx5_core_dev *mdev, u32 sqn);
int mlx5hws_cmd_query_caps(struct mlx5_core_dev *mdev,
struct mlx5hws_cmd_query_caps *caps);
void mlx5hws_cmd_set_attr_connect_miss_tbl(struct mlx5hws_context *ctx,
u32 fw_ft_type,
enum mlx5hws_table_type type,
struct mlx5hws_cmd_ft_modify_attr *ft_attr);
int mlx5hws_cmd_allow_other_vhca_access(struct mlx5_core_dev *mdev,
struct mlx5hws_cmd_allow_other_vhca_access_attr *attr);
int mlx5hws_cmd_query_gvmi(struct mlx5_core_dev *mdev, bool other_function,
u16 vport_number, u16 *gvmi);
#endif /* MLX5HWS_CMD_H_ */

View File

@ -0,0 +1,260 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2024 NVIDIA CORPORATION. All rights reserved. */
#include "mlx5hws_internal.h"
bool mlx5hws_context_cap_dynamic_reparse(struct mlx5hws_context *ctx)
{
return IS_BIT_SET(ctx->caps->rtc_reparse_mode, MLX5_IFC_RTC_REPARSE_BY_STC);
}
u8 mlx5hws_context_get_reparse_mode(struct mlx5hws_context *ctx)
{
/* Prefer to use dynamic reparse, reparse only specific actions */
if (mlx5hws_context_cap_dynamic_reparse(ctx))
return MLX5_IFC_RTC_REPARSE_NEVER;
/* Otherwise use less efficient static */
return MLX5_IFC_RTC_REPARSE_ALWAYS;
}
static int hws_context_pools_init(struct mlx5hws_context *ctx)
{
struct mlx5hws_pool_attr pool_attr = {0};
u8 max_log_sz;
int ret;
int i;
ret = mlx5hws_pat_init_pattern_cache(&ctx->pattern_cache);
if (ret)
return ret;
ret = mlx5hws_definer_init_cache(&ctx->definer_cache);
if (ret)
goto uninit_pat_cache;
/* Create an STC pool per FT type */
pool_attr.pool_type = MLX5HWS_POOL_TYPE_STC;
pool_attr.flags = MLX5HWS_POOL_FLAGS_FOR_STC_POOL;
max_log_sz = min(MLX5HWS_POOL_STC_LOG_SZ, ctx->caps->stc_alloc_log_max);
pool_attr.alloc_log_sz = max(max_log_sz, ctx->caps->stc_alloc_log_gran);
for (i = 0; i < MLX5HWS_TABLE_TYPE_MAX; i++) {
pool_attr.table_type = i;
ctx->stc_pool[i] = mlx5hws_pool_create(ctx, &pool_attr);
if (!ctx->stc_pool[i]) {
mlx5hws_err(ctx, "Failed to allocate STC pool [%d]", i);
ret = -ENOMEM;
goto free_stc_pools;
}
}
return 0;
free_stc_pools:
for (i = 0; i < MLX5HWS_TABLE_TYPE_MAX; i++)
if (ctx->stc_pool[i])
mlx5hws_pool_destroy(ctx->stc_pool[i]);
mlx5hws_definer_uninit_cache(ctx->definer_cache);
uninit_pat_cache:
mlx5hws_pat_uninit_pattern_cache(ctx->pattern_cache);
return ret;
}
static void hws_context_pools_uninit(struct mlx5hws_context *ctx)
{
int i;
for (i = 0; i < MLX5HWS_TABLE_TYPE_MAX; i++) {
if (ctx->stc_pool[i])
mlx5hws_pool_destroy(ctx->stc_pool[i]);
}
mlx5hws_definer_uninit_cache(ctx->definer_cache);
mlx5hws_pat_uninit_pattern_cache(ctx->pattern_cache);
}
static int hws_context_init_pd(struct mlx5hws_context *ctx)
{
int ret = 0;
ret = mlx5_core_alloc_pd(ctx->mdev, &ctx->pd_num);
if (ret) {
mlx5hws_err(ctx, "Failed to allocate PD\n");
return ret;
}
ctx->flags |= MLX5HWS_CONTEXT_FLAG_PRIVATE_PD;
return 0;
}
static int hws_context_uninit_pd(struct mlx5hws_context *ctx)
{
if (ctx->flags & MLX5HWS_CONTEXT_FLAG_PRIVATE_PD)
mlx5_core_dealloc_pd(ctx->mdev, ctx->pd_num);
return 0;
}
static void hws_context_check_hws_supp(struct mlx5hws_context *ctx)
{
struct mlx5hws_cmd_query_caps *caps = ctx->caps;
/* HWS not supported on device / FW */
if (!caps->wqe_based_update) {
mlx5hws_err(ctx, "Required HWS WQE based insertion cap not supported\n");
return;
}
if (!caps->eswitch_manager) {
mlx5hws_err(ctx, "HWS is not supported for non eswitch manager port\n");
return;
}
/* Current solution requires all rules to set reparse bit */
if ((!caps->nic_ft.reparse ||
(!caps->fdb_ft.reparse && caps->eswitch_manager)) ||
!IS_BIT_SET(caps->rtc_reparse_mode, MLX5_IFC_RTC_REPARSE_ALWAYS)) {
mlx5hws_err(ctx, "Required HWS reparse cap not supported\n");
return;
}
/* FW/HW must support 8DW STE */
if (!IS_BIT_SET(caps->ste_format, MLX5_IFC_RTC_STE_FORMAT_8DW)) {
mlx5hws_err(ctx, "Required HWS STE format not supported\n");
return;
}
/* Adding rules by hash and by offset are requirements */
if (!IS_BIT_SET(caps->rtc_index_mode, MLX5_IFC_RTC_STE_UPDATE_MODE_BY_HASH) ||
!IS_BIT_SET(caps->rtc_index_mode, MLX5_IFC_RTC_STE_UPDATE_MODE_BY_OFFSET)) {
mlx5hws_err(ctx, "Required HWS RTC update mode not supported\n");
return;
}
/* Support for SELECT definer ID is required */
if (!IS_BIT_SET(caps->definer_format_sup, MLX5_IFC_DEFINER_FORMAT_ID_SELECT)) {
mlx5hws_err(ctx, "Required HWS Dynamic definer not supported\n");
return;
}
ctx->flags |= MLX5HWS_CONTEXT_FLAG_HWS_SUPPORT;
}
static int hws_context_init_hws(struct mlx5hws_context *ctx,
struct mlx5hws_context_attr *attr)
{
int ret;
hws_context_check_hws_supp(ctx);
if (!(ctx->flags & MLX5HWS_CONTEXT_FLAG_HWS_SUPPORT))
return 0;
ret = hws_context_init_pd(ctx);
if (ret)
return ret;
ret = hws_context_pools_init(ctx);
if (ret)
goto uninit_pd;
if (attr->bwc)
ctx->flags |= MLX5HWS_CONTEXT_FLAG_BWC_SUPPORT;
ret = mlx5hws_send_queues_open(ctx, attr->queues, attr->queue_size);
if (ret)
goto pools_uninit;
INIT_LIST_HEAD(&ctx->tbl_list);
return 0;
pools_uninit:
hws_context_pools_uninit(ctx);
uninit_pd:
hws_context_uninit_pd(ctx);
return ret;
}
static void hws_context_uninit_hws(struct mlx5hws_context *ctx)
{
if (!(ctx->flags & MLX5HWS_CONTEXT_FLAG_HWS_SUPPORT))
return;
mlx5hws_send_queues_close(ctx);
hws_context_pools_uninit(ctx);
hws_context_uninit_pd(ctx);
}
struct mlx5hws_context *mlx5hws_context_open(struct mlx5_core_dev *mdev,
struct mlx5hws_context_attr *attr)
{
struct mlx5hws_context *ctx;
int ret;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return NULL;
ctx->mdev = mdev;
mutex_init(&ctx->ctrl_lock);
xa_init(&ctx->peer_ctx_xa);
ctx->caps = kzalloc(sizeof(*ctx->caps), GFP_KERNEL);
if (!ctx->caps)
goto free_ctx;
ret = mlx5hws_cmd_query_caps(mdev, ctx->caps);
if (ret)
goto free_caps;
ret = mlx5hws_vport_init_vports(ctx);
if (ret)
goto free_caps;
ret = hws_context_init_hws(ctx, attr);
if (ret)
goto uninit_vports;
mlx5hws_debug_init_dump(ctx);
return ctx;
uninit_vports:
mlx5hws_vport_uninit_vports(ctx);
free_caps:
kfree(ctx->caps);
free_ctx:
xa_destroy(&ctx->peer_ctx_xa);
mutex_destroy(&ctx->ctrl_lock);
kfree(ctx);
return NULL;
}
int mlx5hws_context_close(struct mlx5hws_context *ctx)
{
mlx5hws_debug_uninit_dump(ctx);
hws_context_uninit_hws(ctx);
mlx5hws_vport_uninit_vports(ctx);
kfree(ctx->caps);
xa_destroy(&ctx->peer_ctx_xa);
mutex_destroy(&ctx->ctrl_lock);
kfree(ctx);
return 0;
}
void mlx5hws_context_set_peer(struct mlx5hws_context *ctx,
struct mlx5hws_context *peer_ctx,
u16 peer_vhca_id)
{
mutex_lock(&ctx->ctrl_lock);
if (xa_err(xa_store(&ctx->peer_ctx_xa, peer_vhca_id, peer_ctx, GFP_KERNEL)))
pr_warn("HWS: failed storing peer vhca ID in peer xarray\n");
mutex_unlock(&ctx->ctrl_lock);
}

View File

@ -0,0 +1,64 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#ifndef MLX5HWS_CONTEXT_H_
#define MLX5HWS_CONTEXT_H_
enum mlx5hws_context_flags {
MLX5HWS_CONTEXT_FLAG_HWS_SUPPORT = 1 << 0,
MLX5HWS_CONTEXT_FLAG_PRIVATE_PD = 1 << 1,
MLX5HWS_CONTEXT_FLAG_BWC_SUPPORT = 1 << 2,
};
enum mlx5hws_context_shared_stc_type {
MLX5HWS_CONTEXT_SHARED_STC_DECAP_L3 = 0,
MLX5HWS_CONTEXT_SHARED_STC_DOUBLE_POP = 1,
MLX5HWS_CONTEXT_SHARED_STC_MAX = 2,
};
struct mlx5hws_context_common_res {
struct mlx5hws_action_default_stc *default_stc;
struct mlx5hws_action_shared_stc *shared_stc[MLX5HWS_CONTEXT_SHARED_STC_MAX];
struct mlx5hws_cmd_forward_tbl *default_miss;
};
struct mlx5hws_context_debug_info {
struct dentry *steering_debugfs;
struct dentry *fdb_debugfs;
};
struct mlx5hws_context_vports {
u16 esw_manager_gvmi;
u16 uplink_gvmi;
struct xarray vport_gvmi_xa;
};
struct mlx5hws_context {
struct mlx5_core_dev *mdev;
struct mlx5hws_cmd_query_caps *caps;
u32 pd_num;
struct mlx5hws_pool *stc_pool[MLX5HWS_TABLE_TYPE_MAX];
struct mlx5hws_context_common_res common_res[MLX5HWS_TABLE_TYPE_MAX];
struct mlx5hws_pattern_cache *pattern_cache;
struct mlx5hws_definer_cache *definer_cache;
struct mutex ctrl_lock; /* control lock to protect the whole context */
enum mlx5hws_context_flags flags;
struct mlx5hws_send_engine *send_queue;
size_t queues;
struct mutex *bwc_send_queue_locks; /* protect BWC queues */
struct list_head tbl_list;
struct mlx5hws_context_debug_info debug_info;
struct xarray peer_ctx_xa;
struct mlx5hws_context_vports vports;
};
static inline bool mlx5hws_context_bwc_supported(struct mlx5hws_context *ctx)
{
return ctx->flags & MLX5HWS_CONTEXT_FLAG_BWC_SUPPORT;
}
bool mlx5hws_context_cap_dynamic_reparse(struct mlx5hws_context *ctx);
u8 mlx5hws_context_get_reparse_mode(struct mlx5hws_context *ctx);
#endif /* MLX5HWS_CONTEXT_H_ */

View File

@ -0,0 +1,480 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#include <linux/debugfs.h>
#include <linux/kernel.h>
#include <linux/seq_file.h>
#include <linux/version.h>
#include "mlx5hws_internal.h"
static int
hws_debug_dump_matcher_template_definer(struct seq_file *f,
void *parent_obj,
struct mlx5hws_definer *definer,
enum mlx5hws_debug_res_type type)
{
int i;
if (!definer)
return 0;
seq_printf(f, "%d,0x%llx,0x%llx,%d,%d,",
type,
HWS_PTR_TO_ID(definer),
HWS_PTR_TO_ID(parent_obj),
definer->obj_id,
definer->type);
for (i = 0; i < DW_SELECTORS; i++)
seq_printf(f, "0x%x%s", definer->dw_selector[i],
(i == DW_SELECTORS - 1) ? "," : "-");
for (i = 0; i < BYTE_SELECTORS; i++)
seq_printf(f, "0x%x%s", definer->byte_selector[i],
(i == BYTE_SELECTORS - 1) ? "," : "-");
for (i = 0; i < MLX5HWS_JUMBO_TAG_SZ; i++)
seq_printf(f, "%02x", definer->mask.jumbo[i]);
seq_puts(f, "\n");
return 0;
}
static int
hws_debug_dump_matcher_match_template(struct seq_file *f, struct mlx5hws_matcher *matcher)
{
enum mlx5hws_debug_res_type type;
int i, ret;
for (i = 0; i < matcher->num_of_mt; i++) {
struct mlx5hws_match_template *mt = &matcher->mt[i];
seq_printf(f, "%d,0x%llx,0x%llx,%d,%d,%d\n",
MLX5HWS_DEBUG_RES_TYPE_MATCHER_MATCH_TEMPLATE,
HWS_PTR_TO_ID(mt),
HWS_PTR_TO_ID(matcher),
mt->fc_sz,
0, 0);
type = MLX5HWS_DEBUG_RES_TYPE_MATCHER_TEMPLATE_MATCH_DEFINER;
ret = hws_debug_dump_matcher_template_definer(f, mt, mt->definer, type);
if (ret)
return ret;
}
return 0;
}
static int
hws_debug_dump_matcher_action_template(struct seq_file *f, struct mlx5hws_matcher *matcher)
{
enum mlx5hws_action_type action_type;
int i, j;
for (i = 0; i < matcher->num_of_at; i++) {
struct mlx5hws_action_template *at = &matcher->at[i];
seq_printf(f, "%d,0x%llx,0x%llx,%d,%d,%d",
MLX5HWS_DEBUG_RES_TYPE_MATCHER_ACTION_TEMPLATE,
HWS_PTR_TO_ID(at),
HWS_PTR_TO_ID(matcher),
at->only_term,
at->num_of_action_stes,
at->num_actions);
for (j = 0; j < at->num_actions; j++) {
action_type = at->action_type_arr[j];
seq_printf(f, ",%s", mlx5hws_action_type_to_str(action_type));
}
seq_puts(f, "\n");
}
return 0;
}
static int
hws_debug_dump_matcher_attr(struct seq_file *f, struct mlx5hws_matcher *matcher)
{
struct mlx5hws_matcher_attr *attr = &matcher->attr;
seq_printf(f, "%d,0x%llx,%d,%d,%d,%d,%d,%d,%d,%d\n",
MLX5HWS_DEBUG_RES_TYPE_MATCHER_ATTR,
HWS_PTR_TO_ID(matcher),
attr->priority,
attr->mode,
attr->table.sz_row_log,
attr->table.sz_col_log,
attr->optimize_using_rule_idx,
attr->optimize_flow_src,
attr->insert_mode,
attr->distribute_mode);
return 0;
}
static int hws_debug_dump_matcher(struct seq_file *f, struct mlx5hws_matcher *matcher)
{
enum mlx5hws_table_type tbl_type = matcher->tbl->type;
struct mlx5hws_cmd_ft_query_attr ft_attr = {0};
struct mlx5hws_pool_chunk *ste;
struct mlx5hws_pool *ste_pool;
u64 icm_addr_0 = 0;
u64 icm_addr_1 = 0;
u32 ste_0_id = -1;
u32 ste_1_id = -1;
int ret;
seq_printf(f, "%d,0x%llx,0x%llx,%d,%d,0x%llx",
MLX5HWS_DEBUG_RES_TYPE_MATCHER,
HWS_PTR_TO_ID(matcher),
HWS_PTR_TO_ID(matcher->tbl),
matcher->num_of_mt,
matcher->end_ft_id,
matcher->col_matcher ? HWS_PTR_TO_ID(matcher->col_matcher) : 0);
ste = &matcher->match_ste.ste;
ste_pool = matcher->match_ste.pool;
if (ste_pool) {
ste_0_id = mlx5hws_pool_chunk_get_base_id(ste_pool, ste);
if (tbl_type == MLX5HWS_TABLE_TYPE_FDB)
ste_1_id = mlx5hws_pool_chunk_get_base_mirror_id(ste_pool, ste);
}
seq_printf(f, ",%d,%d,%d,%d",
matcher->match_ste.rtc_0_id,
(int)ste_0_id,
matcher->match_ste.rtc_1_id,
(int)ste_1_id);
ste = &matcher->action_ste[0].ste;
ste_pool = matcher->action_ste[0].pool;
if (ste_pool) {
ste_0_id = mlx5hws_pool_chunk_get_base_id(ste_pool, ste);
if (tbl_type == MLX5HWS_TABLE_TYPE_FDB)
ste_1_id = mlx5hws_pool_chunk_get_base_mirror_id(ste_pool, ste);
else
ste_1_id = -1;
} else {
ste_0_id = -1;
ste_1_id = -1;
}
ft_attr.type = matcher->tbl->fw_ft_type;
ret = mlx5hws_cmd_flow_table_query(matcher->tbl->ctx->mdev,
matcher->end_ft_id,
&ft_attr,
&icm_addr_0,
&icm_addr_1);
if (ret)
return ret;
seq_printf(f, ",%d,%d,%d,%d,%d,0x%llx,0x%llx\n",
matcher->action_ste[0].rtc_0_id,
(int)ste_0_id,
matcher->action_ste[0].rtc_1_id,
(int)ste_1_id,
0,
mlx5hws_debug_icm_to_idx(icm_addr_0),
mlx5hws_debug_icm_to_idx(icm_addr_1));
ret = hws_debug_dump_matcher_attr(f, matcher);
if (ret)
return ret;
ret = hws_debug_dump_matcher_match_template(f, matcher);
if (ret)
return ret;
ret = hws_debug_dump_matcher_action_template(f, matcher);
if (ret)
return ret;
return 0;
}
static int hws_debug_dump_table(struct seq_file *f, struct mlx5hws_table *tbl)
{
struct mlx5hws_cmd_ft_query_attr ft_attr = {0};
struct mlx5hws_matcher *matcher;
u64 local_icm_addr_0 = 0;
u64 local_icm_addr_1 = 0;
u64 icm_addr_0 = 0;
u64 icm_addr_1 = 0;
int ret;
seq_printf(f, "%d,0x%llx,0x%llx,%d,%d,%d,%d,%d",
MLX5HWS_DEBUG_RES_TYPE_TABLE,
HWS_PTR_TO_ID(tbl),
HWS_PTR_TO_ID(tbl->ctx),
tbl->ft_id,
MLX5HWS_TABLE_TYPE_BASE + tbl->type,
tbl->fw_ft_type,
tbl->level,
0);
ft_attr.type = tbl->fw_ft_type;
ret = mlx5hws_cmd_flow_table_query(tbl->ctx->mdev,
tbl->ft_id,
&ft_attr,
&icm_addr_0,
&icm_addr_1);
if (ret)
return ret;
seq_printf(f, ",0x%llx,0x%llx,0x%llx,0x%llx,0x%llx\n",
mlx5hws_debug_icm_to_idx(icm_addr_0),
mlx5hws_debug_icm_to_idx(icm_addr_1),
mlx5hws_debug_icm_to_idx(local_icm_addr_0),
mlx5hws_debug_icm_to_idx(local_icm_addr_1),
HWS_PTR_TO_ID(tbl->default_miss.miss_tbl));
list_for_each_entry(matcher, &tbl->matchers_list, list_node) {
ret = hws_debug_dump_matcher(f, matcher);
if (ret)
return ret;
}
return 0;
}
static int
hws_debug_dump_context_send_engine(struct seq_file *f, struct mlx5hws_context *ctx)
{
struct mlx5hws_send_engine *send_queue;
struct mlx5hws_send_ring *send_ring;
struct mlx5hws_send_ring_cq *cq;
struct mlx5hws_send_ring_sq *sq;
int i;
for (i = 0; i < (int)ctx->queues; i++) {
send_queue = &ctx->send_queue[i];
seq_printf(f, "%d,0x%llx,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
MLX5HWS_DEBUG_RES_TYPE_CONTEXT_SEND_ENGINE,
HWS_PTR_TO_ID(ctx),
i,
send_queue->used_entries,
send_queue->num_entries,
1, /* one send ring per queue */
send_queue->num_entries,
send_queue->err,
send_queue->completed.ci,
send_queue->completed.pi,
send_queue->completed.mask);
send_ring = &send_queue->send_ring;
cq = &send_ring->send_cq;
sq = &send_ring->send_sq;
seq_printf(f, "%d,0x%llx,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
MLX5HWS_DEBUG_RES_TYPE_CONTEXT_SEND_RING,
HWS_PTR_TO_ID(ctx),
0, /* one send ring per send queue */
i,
cq->mcq.cqn,
0,
0,
0,
0,
0,
0,
cq->mcq.cqe_sz,
sq->sqn,
0,
0,
0);
}
return 0;
}
static int hws_debug_dump_context_caps(struct seq_file *f, struct mlx5hws_context *ctx)
{
struct mlx5hws_cmd_query_caps *caps = ctx->caps;
seq_printf(f, "%d,0x%llx,%s,%d,%d,%d,%d,",
MLX5HWS_DEBUG_RES_TYPE_CONTEXT_CAPS,
HWS_PTR_TO_ID(ctx),
caps->fw_ver,
caps->wqe_based_update,
caps->ste_format,
caps->ste_alloc_log_max,
caps->log_header_modify_argument_max_alloc);
seq_printf(f, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%s\n",
caps->flex_protocols,
caps->rtc_reparse_mode,
caps->rtc_index_mode,
caps->ste_alloc_log_gran,
caps->stc_alloc_log_max,
caps->stc_alloc_log_gran,
caps->rtc_log_depth_max,
caps->format_select_gtpu_dw_0,
caps->format_select_gtpu_dw_1,
caps->format_select_gtpu_dw_2,
caps->format_select_gtpu_ext_dw_0,
caps->nic_ft.max_level,
caps->nic_ft.reparse,
caps->fdb_ft.max_level,
caps->fdb_ft.reparse,
caps->log_header_modify_argument_granularity,
caps->linear_match_definer,
"regc_3");
return 0;
}
static int hws_debug_dump_context_attr(struct seq_file *f, struct mlx5hws_context *ctx)
{
seq_printf(f, "%u,0x%llx,%d,%zu,%d,%s,%d,%d\n",
MLX5HWS_DEBUG_RES_TYPE_CONTEXT_ATTR,
HWS_PTR_TO_ID(ctx),
ctx->pd_num,
ctx->queues,
ctx->send_queue->num_entries,
"None", /* no shared gvmi */
ctx->caps->vhca_id,
0xffff); /* no shared gvmi */
return 0;
}
static int hws_debug_dump_context_info(struct seq_file *f, struct mlx5hws_context *ctx)
{
struct mlx5_core_dev *dev = ctx->mdev;
int ret;
seq_printf(f, "%d,0x%llx,%d,%s,%s.KERNEL_%u_%u_%u\n",
MLX5HWS_DEBUG_RES_TYPE_CONTEXT,
HWS_PTR_TO_ID(ctx),
ctx->flags & MLX5HWS_CONTEXT_FLAG_HWS_SUPPORT,
pci_name(dev->pdev),
HWS_DEBUG_FORMAT_VERSION,
LINUX_VERSION_MAJOR,
LINUX_VERSION_PATCHLEVEL,
LINUX_VERSION_SUBLEVEL);
ret = hws_debug_dump_context_attr(f, ctx);
if (ret)
return ret;
ret = hws_debug_dump_context_caps(f, ctx);
if (ret)
return ret;
return 0;
}
static int hws_debug_dump_context_stc_resource(struct seq_file *f,
struct mlx5hws_context *ctx,
u32 tbl_type,
struct mlx5hws_pool_resource *resource)
{
seq_printf(f, "%d,0x%llx,%u,%u\n",
MLX5HWS_DEBUG_RES_TYPE_CONTEXT_STC,
HWS_PTR_TO_ID(ctx),
tbl_type,
resource->base_id);
return 0;
}
static int hws_debug_dump_context_stc(struct seq_file *f, struct mlx5hws_context *ctx)
{
struct mlx5hws_pool *stc_pool;
u32 table_type;
int ret;
int i;
for (i = 0; i < MLX5HWS_TABLE_TYPE_MAX; i++) {
stc_pool = ctx->stc_pool[i];
table_type = MLX5HWS_TABLE_TYPE_BASE + i;
if (!stc_pool)
continue;
if (stc_pool->resource[0]) {
ret = hws_debug_dump_context_stc_resource(f, ctx, table_type,
stc_pool->resource[0]);
if (ret)
return ret;
}
if (i == MLX5HWS_TABLE_TYPE_FDB && stc_pool->mirror_resource[0]) {
ret = hws_debug_dump_context_stc_resource(f, ctx, table_type,
stc_pool->mirror_resource[0]);
if (ret)
return ret;
}
}
return 0;
}
static int hws_debug_dump_context(struct seq_file *f, struct mlx5hws_context *ctx)
{
struct mlx5hws_table *tbl;
int ret;
ret = hws_debug_dump_context_info(f, ctx);
if (ret)
return ret;
ret = hws_debug_dump_context_send_engine(f, ctx);
if (ret)
return ret;
ret = hws_debug_dump_context_stc(f, ctx);
if (ret)
return ret;
list_for_each_entry(tbl, &ctx->tbl_list, tbl_list_node) {
ret = hws_debug_dump_table(f, tbl);
if (ret)
return ret;
}
return 0;
}
static int
hws_debug_dump(struct seq_file *f, struct mlx5hws_context *ctx)
{
int ret;
if (!f || !ctx)
return -EINVAL;
mutex_lock(&ctx->ctrl_lock);
ret = hws_debug_dump_context(f, ctx);
mutex_unlock(&ctx->ctrl_lock);
return ret;
}
static int hws_dump_show(struct seq_file *file, void *priv)
{
return hws_debug_dump(file, file->private);
}
DEFINE_SHOW_ATTRIBUTE(hws_dump);
void mlx5hws_debug_init_dump(struct mlx5hws_context *ctx)
{
struct mlx5_core_dev *dev = ctx->mdev;
char file_name[128];
ctx->debug_info.steering_debugfs =
debugfs_create_dir("steering", mlx5_debugfs_get_dev_root(dev));
ctx->debug_info.fdb_debugfs =
debugfs_create_dir("fdb", ctx->debug_info.steering_debugfs);
sprintf(file_name, "ctx_%p", ctx);
debugfs_create_file(file_name, 0444, ctx->debug_info.fdb_debugfs,
ctx, &hws_dump_fops);
}
void mlx5hws_debug_uninit_dump(struct mlx5hws_context *ctx)
{
debugfs_remove_recursive(ctx->debug_info.steering_debugfs);
}

View File

@ -0,0 +1,40 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#ifndef MLX5HWS_DEBUG_H_
#define MLX5HWS_DEBUG_H_
#define HWS_DEBUG_FORMAT_VERSION "1.0"
#define HWS_PTR_TO_ID(p) ((u64)(uintptr_t)(p) & 0xFFFFFFFFULL)
enum mlx5hws_debug_res_type {
MLX5HWS_DEBUG_RES_TYPE_CONTEXT = 4000,
MLX5HWS_DEBUG_RES_TYPE_CONTEXT_ATTR = 4001,
MLX5HWS_DEBUG_RES_TYPE_CONTEXT_CAPS = 4002,
MLX5HWS_DEBUG_RES_TYPE_CONTEXT_SEND_ENGINE = 4003,
MLX5HWS_DEBUG_RES_TYPE_CONTEXT_SEND_RING = 4004,
MLX5HWS_DEBUG_RES_TYPE_CONTEXT_STC = 4005,
MLX5HWS_DEBUG_RES_TYPE_TABLE = 4100,
MLX5HWS_DEBUG_RES_TYPE_MATCHER = 4200,
MLX5HWS_DEBUG_RES_TYPE_MATCHER_ATTR = 4201,
MLX5HWS_DEBUG_RES_TYPE_MATCHER_MATCH_TEMPLATE = 4202,
MLX5HWS_DEBUG_RES_TYPE_MATCHER_TEMPLATE_MATCH_DEFINER = 4203,
MLX5HWS_DEBUG_RES_TYPE_MATCHER_ACTION_TEMPLATE = 4204,
MLX5HWS_DEBUG_RES_TYPE_MATCHER_TEMPLATE_HASH_DEFINER = 4205,
MLX5HWS_DEBUG_RES_TYPE_MATCHER_TEMPLATE_RANGE_DEFINER = 4206,
MLX5HWS_DEBUG_RES_TYPE_MATCHER_TEMPLATE_COMPARE_MATCH_DEFINER = 4207,
};
static inline u64
mlx5hws_debug_icm_to_idx(u64 icm_addr)
{
return (icm_addr >> 6) & 0xffffffff;
}
void mlx5hws_debug_init_dump(struct mlx5hws_context *ctx);
void mlx5hws_debug_uninit_dump(struct mlx5hws_context *ctx);
#endif /* MLX5HWS_DEBUG_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,834 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#ifndef MLX5HWS_DEFINER_H_
#define MLX5HWS_DEFINER_H_
/* Max available selecotrs */
#define DW_SELECTORS 9
#define BYTE_SELECTORS 8
/* Selectors based on match TAG */
#define DW_SELECTORS_MATCH 6
#define DW_SELECTORS_LIMITED 3
/* Selectors based on range TAG */
#define DW_SELECTORS_RANGE 2
#define BYTE_SELECTORS_RANGE 8
#define HWS_NUM_OF_FLEX_PARSERS 8
enum mlx5hws_definer_fname {
MLX5HWS_DEFINER_FNAME_ETH_SMAC_47_16_O,
MLX5HWS_DEFINER_FNAME_ETH_SMAC_47_16_I,
MLX5HWS_DEFINER_FNAME_ETH_SMAC_15_0_O,
MLX5HWS_DEFINER_FNAME_ETH_SMAC_15_0_I,
MLX5HWS_DEFINER_FNAME_ETH_DMAC_47_16_O,
MLX5HWS_DEFINER_FNAME_ETH_DMAC_47_16_I,
MLX5HWS_DEFINER_FNAME_ETH_DMAC_15_0_O,
MLX5HWS_DEFINER_FNAME_ETH_DMAC_15_0_I,
MLX5HWS_DEFINER_FNAME_ETH_TYPE_O,
MLX5HWS_DEFINER_FNAME_ETH_TYPE_I,
MLX5HWS_DEFINER_FNAME_ETH_L3_TYPE_O,
MLX5HWS_DEFINER_FNAME_ETH_L3_TYPE_I,
MLX5HWS_DEFINER_FNAME_VLAN_TYPE_O,
MLX5HWS_DEFINER_FNAME_VLAN_TYPE_I,
MLX5HWS_DEFINER_FNAME_VLAN_FIRST_PRIO_O,
MLX5HWS_DEFINER_FNAME_VLAN_FIRST_PRIO_I,
MLX5HWS_DEFINER_FNAME_VLAN_CFI_O,
MLX5HWS_DEFINER_FNAME_VLAN_CFI_I,
MLX5HWS_DEFINER_FNAME_VLAN_ID_O,
MLX5HWS_DEFINER_FNAME_VLAN_ID_I,
MLX5HWS_DEFINER_FNAME_VLAN_SECOND_TYPE_O,
MLX5HWS_DEFINER_FNAME_VLAN_SECOND_TYPE_I,
MLX5HWS_DEFINER_FNAME_VLAN_SECOND_PRIO_O,
MLX5HWS_DEFINER_FNAME_VLAN_SECOND_PRIO_I,
MLX5HWS_DEFINER_FNAME_VLAN_SECOND_CFI_O,
MLX5HWS_DEFINER_FNAME_VLAN_SECOND_CFI_I,
MLX5HWS_DEFINER_FNAME_VLAN_SECOND_ID_O,
MLX5HWS_DEFINER_FNAME_VLAN_SECOND_ID_I,
MLX5HWS_DEFINER_FNAME_IPV4_IHL_O,
MLX5HWS_DEFINER_FNAME_IPV4_IHL_I,
MLX5HWS_DEFINER_FNAME_IP_DSCP_O,
MLX5HWS_DEFINER_FNAME_IP_DSCP_I,
MLX5HWS_DEFINER_FNAME_IP_ECN_O,
MLX5HWS_DEFINER_FNAME_IP_ECN_I,
MLX5HWS_DEFINER_FNAME_IP_TTL_O,
MLX5HWS_DEFINER_FNAME_IP_TTL_I,
MLX5HWS_DEFINER_FNAME_IPV4_DST_O,
MLX5HWS_DEFINER_FNAME_IPV4_DST_I,
MLX5HWS_DEFINER_FNAME_IPV4_SRC_O,
MLX5HWS_DEFINER_FNAME_IPV4_SRC_I,
MLX5HWS_DEFINER_FNAME_IP_VERSION_O,
MLX5HWS_DEFINER_FNAME_IP_VERSION_I,
MLX5HWS_DEFINER_FNAME_IP_FRAG_O,
MLX5HWS_DEFINER_FNAME_IP_FRAG_I,
MLX5HWS_DEFINER_FNAME_IP_LEN_O,
MLX5HWS_DEFINER_FNAME_IP_LEN_I,
MLX5HWS_DEFINER_FNAME_IP_TOS_O,
MLX5HWS_DEFINER_FNAME_IP_TOS_I,
MLX5HWS_DEFINER_FNAME_IPV6_FLOW_LABEL_O,
MLX5HWS_DEFINER_FNAME_IPV6_FLOW_LABEL_I,
MLX5HWS_DEFINER_FNAME_IPV6_DST_127_96_O,
MLX5HWS_DEFINER_FNAME_IPV6_DST_95_64_O,
MLX5HWS_DEFINER_FNAME_IPV6_DST_63_32_O,
MLX5HWS_DEFINER_FNAME_IPV6_DST_31_0_O,
MLX5HWS_DEFINER_FNAME_IPV6_DST_127_96_I,
MLX5HWS_DEFINER_FNAME_IPV6_DST_95_64_I,
MLX5HWS_DEFINER_FNAME_IPV6_DST_63_32_I,
MLX5HWS_DEFINER_FNAME_IPV6_DST_31_0_I,
MLX5HWS_DEFINER_FNAME_IPV6_SRC_127_96_O,
MLX5HWS_DEFINER_FNAME_IPV6_SRC_95_64_O,
MLX5HWS_DEFINER_FNAME_IPV6_SRC_63_32_O,
MLX5HWS_DEFINER_FNAME_IPV6_SRC_31_0_O,
MLX5HWS_DEFINER_FNAME_IPV6_SRC_127_96_I,
MLX5HWS_DEFINER_FNAME_IPV6_SRC_95_64_I,
MLX5HWS_DEFINER_FNAME_IPV6_SRC_63_32_I,
MLX5HWS_DEFINER_FNAME_IPV6_SRC_31_0_I,
MLX5HWS_DEFINER_FNAME_IP_PROTOCOL_O,
MLX5HWS_DEFINER_FNAME_IP_PROTOCOL_I,
MLX5HWS_DEFINER_FNAME_L4_SPORT_O,
MLX5HWS_DEFINER_FNAME_L4_SPORT_I,
MLX5HWS_DEFINER_FNAME_L4_DPORT_O,
MLX5HWS_DEFINER_FNAME_L4_DPORT_I,
MLX5HWS_DEFINER_FNAME_TCP_FLAGS_I,
MLX5HWS_DEFINER_FNAME_TCP_FLAGS_O,
MLX5HWS_DEFINER_FNAME_TCP_SEQ_NUM,
MLX5HWS_DEFINER_FNAME_TCP_ACK_NUM,
MLX5HWS_DEFINER_FNAME_GTP_TEID,
MLX5HWS_DEFINER_FNAME_GTP_MSG_TYPE,
MLX5HWS_DEFINER_FNAME_GTP_EXT_FLAG,
MLX5HWS_DEFINER_FNAME_GTP_NEXT_EXT_HDR,
MLX5HWS_DEFINER_FNAME_GTP_EXT_HDR_PDU,
MLX5HWS_DEFINER_FNAME_GTP_EXT_HDR_QFI,
MLX5HWS_DEFINER_FNAME_GTPU_DW0,
MLX5HWS_DEFINER_FNAME_GTPU_FIRST_EXT_DW0,
MLX5HWS_DEFINER_FNAME_GTPU_DW2,
MLX5HWS_DEFINER_FNAME_FLEX_PARSER_0,
MLX5HWS_DEFINER_FNAME_FLEX_PARSER_1,
MLX5HWS_DEFINER_FNAME_FLEX_PARSER_2,
MLX5HWS_DEFINER_FNAME_FLEX_PARSER_3,
MLX5HWS_DEFINER_FNAME_FLEX_PARSER_4,
MLX5HWS_DEFINER_FNAME_FLEX_PARSER_5,
MLX5HWS_DEFINER_FNAME_FLEX_PARSER_6,
MLX5HWS_DEFINER_FNAME_FLEX_PARSER_7,
MLX5HWS_DEFINER_FNAME_VPORT_REG_C_0,
MLX5HWS_DEFINER_FNAME_VXLAN_FLAGS,
MLX5HWS_DEFINER_FNAME_VXLAN_VNI,
MLX5HWS_DEFINER_FNAME_VXLAN_GPE_FLAGS,
MLX5HWS_DEFINER_FNAME_VXLAN_GPE_RSVD0,
MLX5HWS_DEFINER_FNAME_VXLAN_GPE_PROTO,
MLX5HWS_DEFINER_FNAME_VXLAN_GPE_VNI,
MLX5HWS_DEFINER_FNAME_VXLAN_GPE_RSVD1,
MLX5HWS_DEFINER_FNAME_GENEVE_OPT_LEN,
MLX5HWS_DEFINER_FNAME_GENEVE_OAM,
MLX5HWS_DEFINER_FNAME_GENEVE_PROTO,
MLX5HWS_DEFINER_FNAME_GENEVE_VNI,
MLX5HWS_DEFINER_FNAME_SOURCE_QP,
MLX5HWS_DEFINER_FNAME_SOURCE_GVMI,
MLX5HWS_DEFINER_FNAME_REG_0,
MLX5HWS_DEFINER_FNAME_REG_1,
MLX5HWS_DEFINER_FNAME_REG_2,
MLX5HWS_DEFINER_FNAME_REG_3,
MLX5HWS_DEFINER_FNAME_REG_4,
MLX5HWS_DEFINER_FNAME_REG_5,
MLX5HWS_DEFINER_FNAME_REG_6,
MLX5HWS_DEFINER_FNAME_REG_7,
MLX5HWS_DEFINER_FNAME_REG_8,
MLX5HWS_DEFINER_FNAME_REG_9,
MLX5HWS_DEFINER_FNAME_REG_10,
MLX5HWS_DEFINER_FNAME_REG_11,
MLX5HWS_DEFINER_FNAME_REG_A,
MLX5HWS_DEFINER_FNAME_REG_B,
MLX5HWS_DEFINER_FNAME_GRE_KEY_PRESENT,
MLX5HWS_DEFINER_FNAME_GRE_C,
MLX5HWS_DEFINER_FNAME_GRE_K,
MLX5HWS_DEFINER_FNAME_GRE_S,
MLX5HWS_DEFINER_FNAME_GRE_PROTOCOL,
MLX5HWS_DEFINER_FNAME_GRE_OPT_KEY,
MLX5HWS_DEFINER_FNAME_GRE_OPT_SEQ,
MLX5HWS_DEFINER_FNAME_GRE_OPT_CHECKSUM,
MLX5HWS_DEFINER_FNAME_INTEGRITY_O,
MLX5HWS_DEFINER_FNAME_INTEGRITY_I,
MLX5HWS_DEFINER_FNAME_ICMP_DW1,
MLX5HWS_DEFINER_FNAME_ICMP_DW2,
MLX5HWS_DEFINER_FNAME_ICMP_DW3,
MLX5HWS_DEFINER_FNAME_IPSEC_SPI,
MLX5HWS_DEFINER_FNAME_IPSEC_SEQUENCE_NUMBER,
MLX5HWS_DEFINER_FNAME_IPSEC_SYNDROME,
MLX5HWS_DEFINER_FNAME_MPLS0_O,
MLX5HWS_DEFINER_FNAME_MPLS1_O,
MLX5HWS_DEFINER_FNAME_MPLS2_O,
MLX5HWS_DEFINER_FNAME_MPLS3_O,
MLX5HWS_DEFINER_FNAME_MPLS4_O,
MLX5HWS_DEFINER_FNAME_MPLS0_I,
MLX5HWS_DEFINER_FNAME_MPLS1_I,
MLX5HWS_DEFINER_FNAME_MPLS2_I,
MLX5HWS_DEFINER_FNAME_MPLS3_I,
MLX5HWS_DEFINER_FNAME_MPLS4_I,
MLX5HWS_DEFINER_FNAME_FLEX_PARSER0_OK,
MLX5HWS_DEFINER_FNAME_FLEX_PARSER1_OK,
MLX5HWS_DEFINER_FNAME_FLEX_PARSER2_OK,
MLX5HWS_DEFINER_FNAME_FLEX_PARSER3_OK,
MLX5HWS_DEFINER_FNAME_FLEX_PARSER4_OK,
MLX5HWS_DEFINER_FNAME_FLEX_PARSER5_OK,
MLX5HWS_DEFINER_FNAME_FLEX_PARSER6_OK,
MLX5HWS_DEFINER_FNAME_FLEX_PARSER7_OK,
MLX5HWS_DEFINER_FNAME_OKS2_MPLS0_O,
MLX5HWS_DEFINER_FNAME_OKS2_MPLS1_O,
MLX5HWS_DEFINER_FNAME_OKS2_MPLS2_O,
MLX5HWS_DEFINER_FNAME_OKS2_MPLS3_O,
MLX5HWS_DEFINER_FNAME_OKS2_MPLS4_O,
MLX5HWS_DEFINER_FNAME_OKS2_MPLS0_I,
MLX5HWS_DEFINER_FNAME_OKS2_MPLS1_I,
MLX5HWS_DEFINER_FNAME_OKS2_MPLS2_I,
MLX5HWS_DEFINER_FNAME_OKS2_MPLS3_I,
MLX5HWS_DEFINER_FNAME_OKS2_MPLS4_I,
MLX5HWS_DEFINER_FNAME_GENEVE_OPT_OK_0,
MLX5HWS_DEFINER_FNAME_GENEVE_OPT_OK_1,
MLX5HWS_DEFINER_FNAME_GENEVE_OPT_OK_2,
MLX5HWS_DEFINER_FNAME_GENEVE_OPT_OK_3,
MLX5HWS_DEFINER_FNAME_GENEVE_OPT_OK_4,
MLX5HWS_DEFINER_FNAME_GENEVE_OPT_OK_5,
MLX5HWS_DEFINER_FNAME_GENEVE_OPT_OK_6,
MLX5HWS_DEFINER_FNAME_GENEVE_OPT_OK_7,
MLX5HWS_DEFINER_FNAME_GENEVE_OPT_DW_0,
MLX5HWS_DEFINER_FNAME_GENEVE_OPT_DW_1,
MLX5HWS_DEFINER_FNAME_GENEVE_OPT_DW_2,
MLX5HWS_DEFINER_FNAME_GENEVE_OPT_DW_3,
MLX5HWS_DEFINER_FNAME_GENEVE_OPT_DW_4,
MLX5HWS_DEFINER_FNAME_GENEVE_OPT_DW_5,
MLX5HWS_DEFINER_FNAME_GENEVE_OPT_DW_6,
MLX5HWS_DEFINER_FNAME_GENEVE_OPT_DW_7,
MLX5HWS_DEFINER_FNAME_IB_L4_OPCODE,
MLX5HWS_DEFINER_FNAME_IB_L4_QPN,
MLX5HWS_DEFINER_FNAME_IB_L4_A,
MLX5HWS_DEFINER_FNAME_RANDOM_NUM,
MLX5HWS_DEFINER_FNAME_PTYPE_L2_O,
MLX5HWS_DEFINER_FNAME_PTYPE_L2_I,
MLX5HWS_DEFINER_FNAME_PTYPE_L3_O,
MLX5HWS_DEFINER_FNAME_PTYPE_L3_I,
MLX5HWS_DEFINER_FNAME_PTYPE_L4_O,
MLX5HWS_DEFINER_FNAME_PTYPE_L4_I,
MLX5HWS_DEFINER_FNAME_PTYPE_L4_EXT_O,
MLX5HWS_DEFINER_FNAME_PTYPE_L4_EXT_I,
MLX5HWS_DEFINER_FNAME_PTYPE_FRAG_O,
MLX5HWS_DEFINER_FNAME_PTYPE_FRAG_I,
MLX5HWS_DEFINER_FNAME_TNL_HDR_0,
MLX5HWS_DEFINER_FNAME_TNL_HDR_1,
MLX5HWS_DEFINER_FNAME_TNL_HDR_2,
MLX5HWS_DEFINER_FNAME_TNL_HDR_3,
MLX5HWS_DEFINER_FNAME_MAX,
};
enum mlx5hws_definer_match_criteria {
MLX5HWS_DEFINER_MATCH_CRITERIA_EMPTY = 0,
MLX5HWS_DEFINER_MATCH_CRITERIA_OUTER = 1 << 0,
MLX5HWS_DEFINER_MATCH_CRITERIA_MISC = 1 << 1,
MLX5HWS_DEFINER_MATCH_CRITERIA_INNER = 1 << 2,
MLX5HWS_DEFINER_MATCH_CRITERIA_MISC2 = 1 << 3,
MLX5HWS_DEFINER_MATCH_CRITERIA_MISC3 = 1 << 4,
MLX5HWS_DEFINER_MATCH_CRITERIA_MISC4 = 1 << 5,
MLX5HWS_DEFINER_MATCH_CRITERIA_MISC5 = 1 << 6,
MLX5HWS_DEFINER_MATCH_CRITERIA_MISC6 = 1 << 7,
};
enum mlx5hws_definer_type {
MLX5HWS_DEFINER_TYPE_MATCH,
MLX5HWS_DEFINER_TYPE_JUMBO,
};
enum mlx5hws_definer_match_flag {
MLX5HWS_DEFINER_MATCH_FLAG_TNL_VXLAN_GPE = 1 << 0,
MLX5HWS_DEFINER_MATCH_FLAG_TNL_GENEVE = 1 << 1,
MLX5HWS_DEFINER_MATCH_FLAG_TNL_GTPU = 1 << 2,
MLX5HWS_DEFINER_MATCH_FLAG_TNL_GRE = 1 << 3,
MLX5HWS_DEFINER_MATCH_FLAG_TNL_VXLAN = 1 << 4,
MLX5HWS_DEFINER_MATCH_FLAG_TNL_HEADER_0_1 = 1 << 5,
MLX5HWS_DEFINER_MATCH_FLAG_TNL_GRE_OPT_KEY = 1 << 6,
MLX5HWS_DEFINER_MATCH_FLAG_TNL_HEADER_2 = 1 << 7,
MLX5HWS_DEFINER_MATCH_FLAG_TNL_MPLS_OVER_GRE = 1 << 8,
MLX5HWS_DEFINER_MATCH_FLAG_TNL_MPLS_OVER_UDP = 1 << 9,
MLX5HWS_DEFINER_MATCH_FLAG_ICMPV4 = 1 << 10,
MLX5HWS_DEFINER_MATCH_FLAG_ICMPV6 = 1 << 11,
MLX5HWS_DEFINER_MATCH_FLAG_TCP_O = 1 << 12,
MLX5HWS_DEFINER_MATCH_FLAG_TCP_I = 1 << 13,
};
struct mlx5hws_definer_fc {
struct mlx5hws_context *ctx;
/* Source */
u32 s_byte_off;
int s_bit_off;
u32 s_bit_mask;
/* Destination */
u32 byte_off;
int bit_off;
u32 bit_mask;
enum mlx5hws_definer_fname fname;
void (*tag_set)(struct mlx5hws_definer_fc *fc,
void *mach_param,
u8 *tag);
void (*tag_mask_set)(struct mlx5hws_definer_fc *fc,
void *mach_param,
u8 *tag);
};
struct mlx5_ifc_definer_hl_eth_l2_bits {
u8 dmac_47_16[0x20];
u8 dmac_15_0[0x10];
u8 l3_ethertype[0x10];
u8 reserved_at_40[0x1];
u8 sx_sniffer[0x1];
u8 functional_lb[0x1];
u8 ip_fragmented[0x1];
u8 qp_type[0x2];
u8 encap_type[0x2];
u8 port_number[0x2];
u8 l3_type[0x2];
u8 l4_type_bwc[0x2];
u8 first_vlan_qualifier[0x2];
u8 first_priority[0x3];
u8 first_cfi[0x1];
u8 first_vlan_id[0xc];
u8 l4_type[0x4];
u8 reserved_at_64[0x2];
u8 ipsec_layer[0x2];
u8 l2_type[0x2];
u8 force_lb[0x1];
u8 l2_ok[0x1];
u8 l3_ok[0x1];
u8 l4_ok[0x1];
u8 second_vlan_qualifier[0x2];
u8 second_priority[0x3];
u8 second_cfi[0x1];
u8 second_vlan_id[0xc];
};
struct mlx5_ifc_definer_hl_eth_l2_src_bits {
u8 smac_47_16[0x20];
u8 smac_15_0[0x10];
u8 loopback_syndrome[0x8];
u8 l3_type[0x2];
u8 l4_type_bwc[0x2];
u8 first_vlan_qualifier[0x2];
u8 ip_fragmented[0x1];
u8 functional_lb[0x1];
};
struct mlx5_ifc_definer_hl_ib_l2_bits {
u8 sx_sniffer[0x1];
u8 force_lb[0x1];
u8 functional_lb[0x1];
u8 reserved_at_3[0x3];
u8 port_number[0x2];
u8 sl[0x4];
u8 qp_type[0x2];
u8 lnh[0x2];
u8 dlid[0x10];
u8 vl[0x4];
u8 lrh_packet_length[0xc];
u8 slid[0x10];
};
struct mlx5_ifc_definer_hl_eth_l3_bits {
u8 ip_version[0x4];
u8 ihl[0x4];
union {
u8 tos[0x8];
struct {
u8 dscp[0x6];
u8 ecn[0x2];
};
};
u8 time_to_live_hop_limit[0x8];
u8 protocol_next_header[0x8];
u8 identification[0x10];
union {
u8 ipv4_frag[0x10];
struct {
u8 flags[0x3];
u8 fragment_offset[0xd];
};
};
u8 ipv4_total_length[0x10];
u8 checksum[0x10];
u8 reserved_at_60[0xc];
u8 flow_label[0x14];
u8 packet_length[0x10];
u8 ipv6_payload_length[0x10];
};
struct mlx5_ifc_definer_hl_eth_l4_bits {
u8 source_port[0x10];
u8 destination_port[0x10];
u8 data_offset[0x4];
u8 l4_ok[0x1];
u8 l3_ok[0x1];
u8 ip_fragmented[0x1];
u8 tcp_ns[0x1];
union {
u8 tcp_flags[0x8];
struct {
u8 tcp_cwr[0x1];
u8 tcp_ece[0x1];
u8 tcp_urg[0x1];
u8 tcp_ack[0x1];
u8 tcp_psh[0x1];
u8 tcp_rst[0x1];
u8 tcp_syn[0x1];
u8 tcp_fin[0x1];
};
};
u8 first_fragment[0x1];
u8 reserved_at_31[0xf];
};
struct mlx5_ifc_definer_hl_src_qp_gvmi_bits {
u8 loopback_syndrome[0x8];
u8 l3_type[0x2];
u8 l4_type_bwc[0x2];
u8 first_vlan_qualifier[0x2];
u8 reserved_at_e[0x1];
u8 functional_lb[0x1];
u8 source_gvmi[0x10];
u8 force_lb[0x1];
u8 ip_fragmented[0x1];
u8 source_is_requestor[0x1];
u8 reserved_at_23[0x5];
u8 source_qp[0x18];
};
struct mlx5_ifc_definer_hl_ib_l4_bits {
u8 opcode[0x8];
u8 qp[0x18];
u8 se[0x1];
u8 migreq[0x1];
u8 ackreq[0x1];
u8 fecn[0x1];
u8 becn[0x1];
u8 bth[0x1];
u8 deth[0x1];
u8 dcceth[0x1];
u8 reserved_at_28[0x2];
u8 pad_count[0x2];
u8 tver[0x4];
u8 p_key[0x10];
u8 reserved_at_40[0x8];
u8 deth_source_qp[0x18];
};
enum mlx5hws_integrity_ok1_bits {
MLX5HWS_DEFINER_OKS1_FIRST_L4_OK = 24,
MLX5HWS_DEFINER_OKS1_FIRST_L3_OK = 25,
MLX5HWS_DEFINER_OKS1_SECOND_L4_OK = 26,
MLX5HWS_DEFINER_OKS1_SECOND_L3_OK = 27,
MLX5HWS_DEFINER_OKS1_FIRST_L4_CSUM_OK = 28,
MLX5HWS_DEFINER_OKS1_FIRST_IPV4_CSUM_OK = 29,
MLX5HWS_DEFINER_OKS1_SECOND_L4_CSUM_OK = 30,
MLX5HWS_DEFINER_OKS1_SECOND_IPV4_CSUM_OK = 31,
};
struct mlx5_ifc_definer_hl_oks1_bits {
union {
u8 oks1_bits[0x20];
struct {
u8 second_ipv4_checksum_ok[0x1];
u8 second_l4_checksum_ok[0x1];
u8 first_ipv4_checksum_ok[0x1];
u8 first_l4_checksum_ok[0x1];
u8 second_l3_ok[0x1];
u8 second_l4_ok[0x1];
u8 first_l3_ok[0x1];
u8 first_l4_ok[0x1];
u8 flex_parser7_steering_ok[0x1];
u8 flex_parser6_steering_ok[0x1];
u8 flex_parser5_steering_ok[0x1];
u8 flex_parser4_steering_ok[0x1];
u8 flex_parser3_steering_ok[0x1];
u8 flex_parser2_steering_ok[0x1];
u8 flex_parser1_steering_ok[0x1];
u8 flex_parser0_steering_ok[0x1];
u8 second_ipv6_extension_header_vld[0x1];
u8 first_ipv6_extension_header_vld[0x1];
u8 l3_tunneling_ok[0x1];
u8 l2_tunneling_ok[0x1];
u8 second_tcp_ok[0x1];
u8 second_udp_ok[0x1];
u8 second_ipv4_ok[0x1];
u8 second_ipv6_ok[0x1];
u8 second_l2_ok[0x1];
u8 vxlan_ok[0x1];
u8 gre_ok[0x1];
u8 first_tcp_ok[0x1];
u8 first_udp_ok[0x1];
u8 first_ipv4_ok[0x1];
u8 first_ipv6_ok[0x1];
u8 first_l2_ok[0x1];
};
};
};
struct mlx5_ifc_definer_hl_oks2_bits {
u8 reserved_at_0[0xa];
u8 second_mpls_ok[0x1];
u8 second_mpls4_s_bit[0x1];
u8 second_mpls4_qualifier[0x1];
u8 second_mpls3_s_bit[0x1];
u8 second_mpls3_qualifier[0x1];
u8 second_mpls2_s_bit[0x1];
u8 second_mpls2_qualifier[0x1];
u8 second_mpls1_s_bit[0x1];
u8 second_mpls1_qualifier[0x1];
u8 second_mpls0_s_bit[0x1];
u8 second_mpls0_qualifier[0x1];
u8 first_mpls_ok[0x1];
u8 first_mpls4_s_bit[0x1];
u8 first_mpls4_qualifier[0x1];
u8 first_mpls3_s_bit[0x1];
u8 first_mpls3_qualifier[0x1];
u8 first_mpls2_s_bit[0x1];
u8 first_mpls2_qualifier[0x1];
u8 first_mpls1_s_bit[0x1];
u8 first_mpls1_qualifier[0x1];
u8 first_mpls0_s_bit[0x1];
u8 first_mpls0_qualifier[0x1];
};
struct mlx5_ifc_definer_hl_voq_bits {
u8 reserved_at_0[0x18];
u8 ecn_ok[0x1];
u8 congestion[0x1];
u8 profile[0x2];
u8 internal_prio[0x4];
};
struct mlx5_ifc_definer_hl_ipv4_src_dst_bits {
u8 source_address[0x20];
u8 destination_address[0x20];
};
struct mlx5_ifc_definer_hl_random_number_bits {
u8 random_number[0x10];
u8 reserved[0x10];
};
struct mlx5_ifc_definer_hl_ipv6_addr_bits {
u8 ipv6_address_127_96[0x20];
u8 ipv6_address_95_64[0x20];
u8 ipv6_address_63_32[0x20];
u8 ipv6_address_31_0[0x20];
};
struct mlx5_ifc_definer_tcp_icmp_header_bits {
union {
struct {
u8 icmp_dw1[0x20];
u8 icmp_dw2[0x20];
u8 icmp_dw3[0x20];
};
struct {
u8 tcp_seq[0x20];
u8 tcp_ack[0x20];
u8 tcp_win_urg[0x20];
};
};
};
struct mlx5_ifc_definer_hl_tunnel_header_bits {
u8 tunnel_header_0[0x20];
u8 tunnel_header_1[0x20];
u8 tunnel_header_2[0x20];
u8 tunnel_header_3[0x20];
};
struct mlx5_ifc_definer_hl_ipsec_bits {
u8 spi[0x20];
u8 sequence_number[0x20];
u8 reserved[0x10];
u8 ipsec_syndrome[0x8];
u8 next_header[0x8];
};
struct mlx5_ifc_definer_hl_metadata_bits {
u8 metadata_to_cqe[0x20];
u8 general_purpose[0x20];
u8 acomulated_hash[0x20];
};
struct mlx5_ifc_definer_hl_flex_parser_bits {
u8 flex_parser_7[0x20];
u8 flex_parser_6[0x20];
u8 flex_parser_5[0x20];
u8 flex_parser_4[0x20];
u8 flex_parser_3[0x20];
u8 flex_parser_2[0x20];
u8 flex_parser_1[0x20];
u8 flex_parser_0[0x20];
};
struct mlx5_ifc_definer_hl_registers_bits {
u8 register_c_10[0x20];
u8 register_c_11[0x20];
u8 register_c_8[0x20];
u8 register_c_9[0x20];
u8 register_c_6[0x20];
u8 register_c_7[0x20];
u8 register_c_4[0x20];
u8 register_c_5[0x20];
u8 register_c_2[0x20];
u8 register_c_3[0x20];
u8 register_c_0[0x20];
u8 register_c_1[0x20];
};
struct mlx5_ifc_definer_hl_mpls_bits {
u8 mpls0_label[0x20];
u8 mpls1_label[0x20];
u8 mpls2_label[0x20];
u8 mpls3_label[0x20];
u8 mpls4_label[0x20];
};
struct mlx5_ifc_definer_hl_bits {
struct mlx5_ifc_definer_hl_eth_l2_bits eth_l2_outer;
struct mlx5_ifc_definer_hl_eth_l2_bits eth_l2_inner;
struct mlx5_ifc_definer_hl_eth_l2_src_bits eth_l2_src_outer;
struct mlx5_ifc_definer_hl_eth_l2_src_bits eth_l2_src_inner;
struct mlx5_ifc_definer_hl_ib_l2_bits ib_l2;
struct mlx5_ifc_definer_hl_eth_l3_bits eth_l3_outer;
struct mlx5_ifc_definer_hl_eth_l3_bits eth_l3_inner;
struct mlx5_ifc_definer_hl_eth_l4_bits eth_l4_outer;
struct mlx5_ifc_definer_hl_eth_l4_bits eth_l4_inner;
struct mlx5_ifc_definer_hl_src_qp_gvmi_bits source_qp_gvmi;
struct mlx5_ifc_definer_hl_ib_l4_bits ib_l4;
struct mlx5_ifc_definer_hl_oks1_bits oks1;
struct mlx5_ifc_definer_hl_oks2_bits oks2;
struct mlx5_ifc_definer_hl_voq_bits voq;
u8 reserved_at_480[0x380];
struct mlx5_ifc_definer_hl_ipv4_src_dst_bits ipv4_src_dest_outer;
struct mlx5_ifc_definer_hl_ipv4_src_dst_bits ipv4_src_dest_inner;
struct mlx5_ifc_definer_hl_ipv6_addr_bits ipv6_dst_outer;
struct mlx5_ifc_definer_hl_ipv6_addr_bits ipv6_dst_inner;
struct mlx5_ifc_definer_hl_ipv6_addr_bits ipv6_src_outer;
struct mlx5_ifc_definer_hl_ipv6_addr_bits ipv6_src_inner;
u8 unsupported_dest_ib_l3[0x80];
u8 unsupported_source_ib_l3[0x80];
u8 unsupported_udp_misc_outer[0x20];
u8 unsupported_udp_misc_inner[0x20];
struct mlx5_ifc_definer_tcp_icmp_header_bits tcp_icmp;
struct mlx5_ifc_definer_hl_tunnel_header_bits tunnel_header;
struct mlx5_ifc_definer_hl_mpls_bits mpls_outer;
struct mlx5_ifc_definer_hl_mpls_bits mpls_inner;
u8 unsupported_config_headers_outer[0x80];
u8 unsupported_config_headers_inner[0x80];
struct mlx5_ifc_definer_hl_random_number_bits random_number;
struct mlx5_ifc_definer_hl_ipsec_bits ipsec;
struct mlx5_ifc_definer_hl_metadata_bits metadata;
u8 unsupported_utc_timestamp[0x40];
u8 unsupported_free_running_timestamp[0x40];
struct mlx5_ifc_definer_hl_flex_parser_bits flex_parser;
struct mlx5_ifc_definer_hl_registers_bits registers;
/* Reserved in case header layout on future HW */
u8 unsupported_reserved[0xd40];
};
enum mlx5hws_definer_gtp {
MLX5HWS_DEFINER_GTP_EXT_HDR_BIT = 0x04,
};
struct mlx5_ifc_header_gtp_bits {
u8 version[0x3];
u8 proto_type[0x1];
u8 reserved1[0x1];
union {
u8 msg_flags[0x3];
struct {
u8 ext_hdr_flag[0x1];
u8 seq_num_flag[0x1];
u8 pdu_flag[0x1];
};
};
u8 msg_type[0x8];
u8 msg_len[0x8];
u8 teid[0x20];
};
struct mlx5_ifc_header_opt_gtp_bits {
u8 seq_num[0x10];
u8 pdu_num[0x8];
u8 next_ext_hdr_type[0x8];
};
struct mlx5_ifc_header_gtp_psc_bits {
u8 len[0x8];
u8 pdu_type[0x4];
u8 flags[0x4];
u8 qfi[0x8];
u8 reserved2[0x8];
};
struct mlx5_ifc_header_ipv6_vtc_bits {
u8 version[0x4];
union {
u8 tos[0x8];
struct {
u8 dscp[0x6];
u8 ecn[0x2];
};
};
u8 flow_label[0x14];
};
struct mlx5_ifc_header_ipv6_routing_ext_bits {
u8 next_hdr[0x8];
u8 hdr_len[0x8];
u8 type[0x8];
u8 segments_left[0x8];
union {
u8 flags[0x20];
struct {
u8 last_entry[0x8];
u8 flag[0x8];
u8 tag[0x10];
};
};
};
struct mlx5_ifc_header_vxlan_bits {
u8 flags[0x8];
u8 reserved1[0x18];
u8 vni[0x18];
u8 reserved2[0x8];
};
struct mlx5_ifc_header_vxlan_gpe_bits {
u8 flags[0x8];
u8 rsvd0[0x10];
u8 protocol[0x8];
u8 vni[0x18];
u8 rsvd1[0x8];
};
struct mlx5_ifc_header_gre_bits {
union {
u8 c_rsvd0_ver[0x10];
struct {
u8 gre_c_present[0x1];
u8 reserved_at_1[0x1];
u8 gre_k_present[0x1];
u8 gre_s_present[0x1];
u8 reserved_at_4[0x9];
u8 version[0x3];
};
};
u8 gre_protocol[0x10];
u8 checksum[0x10];
u8 reserved_at_30[0x10];
};
struct mlx5_ifc_header_geneve_bits {
union {
u8 ver_opt_len_o_c_rsvd[0x10];
struct {
u8 version[0x2];
u8 opt_len[0x6];
u8 o_flag[0x1];
u8 c_flag[0x1];
u8 reserved_at_a[0x6];
};
};
u8 protocol_type[0x10];
u8 vni[0x18];
u8 reserved_at_38[0x8];
};
struct mlx5_ifc_header_geneve_opt_bits {
u8 class[0x10];
u8 type[0x8];
u8 reserved[0x3];
u8 len[0x5];
};
struct mlx5_ifc_header_icmp_bits {
union {
u8 icmp_dw1[0x20];
struct {
u8 type[0x8];
u8 code[0x8];
u8 cksum[0x10];
};
};
union {
u8 icmp_dw2[0x20];
struct {
u8 ident[0x10];
u8 seq_nb[0x10];
};
};
};
struct mlx5hws_definer {
enum mlx5hws_definer_type type;
u8 dw_selector[DW_SELECTORS];
u8 byte_selector[BYTE_SELECTORS];
struct mlx5hws_rule_match_tag mask;
u32 obj_id;
};
struct mlx5hws_definer_cache {
struct list_head list_head;
};
struct mlx5hws_definer_cache_item {
struct mlx5hws_definer definer;
u32 refcount;
struct list_head list_node;
};
static inline bool
mlx5hws_definer_is_jumbo(struct mlx5hws_definer *definer)
{
return (definer->type == MLX5HWS_DEFINER_TYPE_JUMBO);
}
void mlx5hws_definer_create_tag(u32 *match_param,
struct mlx5hws_definer_fc *fc,
u32 fc_sz,
u8 *tag);
int mlx5hws_definer_get_id(struct mlx5hws_definer *definer);
int mlx5hws_definer_mt_init(struct mlx5hws_context *ctx,
struct mlx5hws_match_template *mt);
void mlx5hws_definer_mt_uninit(struct mlx5hws_context *ctx,
struct mlx5hws_match_template *mt);
int mlx5hws_definer_init_cache(struct mlx5hws_definer_cache **cache);
void mlx5hws_definer_uninit_cache(struct mlx5hws_definer_cache *cache);
int mlx5hws_definer_compare(struct mlx5hws_definer *definer_a,
struct mlx5hws_definer *definer_b);
int mlx5hws_definer_get_obj(struct mlx5hws_context *ctx,
struct mlx5hws_definer *definer);
void mlx5hws_definer_free(struct mlx5hws_context *ctx,
struct mlx5hws_definer *definer);
int mlx5hws_definer_calc_layout(struct mlx5hws_context *ctx,
struct mlx5hws_match_template *mt,
struct mlx5hws_definer *match_definer);
struct mlx5hws_definer_fc *
mlx5hws_definer_conv_match_params_to_compressed_fc(struct mlx5hws_context *ctx,
u8 match_criteria_enable,
u32 *match_param,
int *fc_sz);
#endif /* MLX5HWS_DEFINER_H_ */

View File

@ -0,0 +1,59 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#ifndef MLX5HWS_INTERNAL_H_
#define MLX5HWS_INTERNAL_H_
#include <linux/mlx5/transobj.h>
#include <linux/mlx5/vport.h>
#include "fs_core.h"
#include "wq.h"
#include "lib/mlx5.h"
#include "mlx5hws_prm.h"
#include "mlx5hws.h"
#include "mlx5hws_pool.h"
#include "mlx5hws_vport.h"
#include "mlx5hws_context.h"
#include "mlx5hws_table.h"
#include "mlx5hws_send.h"
#include "mlx5hws_rule.h"
#include "mlx5hws_cmd.h"
#include "mlx5hws_action.h"
#include "mlx5hws_definer.h"
#include "mlx5hws_matcher.h"
#include "mlx5hws_debug.h"
#include "mlx5hws_pat_arg.h"
#include "mlx5hws_bwc.h"
#include "mlx5hws_bwc_complex.h"
#define W_SIZE 2
#define DW_SIZE 4
#define BITS_IN_BYTE 8
#define BITS_IN_DW (BITS_IN_BYTE * DW_SIZE)
#define IS_BIT_SET(_value, _bit) ((_value) & (1ULL << (_bit)))
#define mlx5hws_err(ctx, arg...) mlx5_core_err((ctx)->mdev, ##arg)
#define mlx5hws_info(ctx, arg...) mlx5_core_info((ctx)->mdev, ##arg)
#define mlx5hws_dbg(ctx, arg...) mlx5_core_dbg((ctx)->mdev, ##arg)
#define MLX5HWS_TABLE_TYPE_BASE 2
#define MLX5HWS_ACTION_STE_IDX_ANY 0
static inline bool is_mem_zero(const u8 *mem, size_t size)
{
if (unlikely(!size)) {
pr_warn("HWS: invalid buffer of size 0 in %s\n", __func__);
return true;
}
return (*mem == 0) && memcmp(mem, mem + 1, size - 1) == 0;
}
static inline unsigned long align(unsigned long val, unsigned long align)
{
return (val + align - 1) & ~(align - 1);
}
#endif /* MLX5HWS_INTERNAL_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,107 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#ifndef MLX5HWS_MATCHER_H_
#define MLX5HWS_MATCHER_H_
/* We calculated that concatenating a collision table to the main table with
* 3% of the main table rows will be enough resources for high insertion
* success probability.
*
* The calculation: log2(2^x * 3 / 100) = log2(2^x) + log2(3/100) = x - 5.05 ~ 5
*/
#define MLX5HWS_MATCHER_ASSURED_ROW_RATIO 5
/* Threshold to determine if amount of rules require a collision table */
#define MLX5HWS_MATCHER_ASSURED_RULES_TH 10
/* Required depth of an assured collision table */
#define MLX5HWS_MATCHER_ASSURED_COL_TBL_DEPTH 4
/* Required depth of the main large table */
#define MLX5HWS_MATCHER_ASSURED_MAIN_TBL_DEPTH 2
enum mlx5hws_matcher_offset {
MLX5HWS_MATCHER_OFFSET_TAG_DW1 = 12,
MLX5HWS_MATCHER_OFFSET_TAG_DW0 = 13,
};
enum mlx5hws_matcher_flags {
MLX5HWS_MATCHER_FLAGS_COLLISION = 1 << 2,
MLX5HWS_MATCHER_FLAGS_RESIZABLE = 1 << 3,
};
struct mlx5hws_match_template {
struct mlx5hws_definer *definer;
struct mlx5hws_definer_fc *fc;
u32 *match_param;
u8 match_criteria_enable;
u16 fc_sz;
};
struct mlx5hws_matcher_match_ste {
struct mlx5hws_pool_chunk ste;
u32 rtc_0_id;
u32 rtc_1_id;
struct mlx5hws_pool *pool;
};
struct mlx5hws_matcher_action_ste {
struct mlx5hws_pool_chunk ste;
struct mlx5hws_pool_chunk stc;
u32 rtc_0_id;
u32 rtc_1_id;
struct mlx5hws_pool *pool;
u8 max_stes;
};
struct mlx5hws_matcher_resize_data_node {
struct mlx5hws_pool_chunk stc;
u32 rtc_0_id;
u32 rtc_1_id;
struct mlx5hws_pool *pool;
};
struct mlx5hws_matcher_resize_data {
struct mlx5hws_matcher_resize_data_node action_ste[2];
u8 max_stes;
struct list_head list_node;
};
struct mlx5hws_matcher {
struct mlx5hws_table *tbl;
struct mlx5hws_matcher_attr attr;
struct mlx5hws_match_template *mt;
struct mlx5hws_action_template *at;
u8 num_of_at;
u8 num_of_mt;
/* enum mlx5hws_matcher_flags */
u8 flags;
u32 end_ft_id;
struct mlx5hws_matcher *col_matcher;
struct mlx5hws_matcher *resize_dst;
struct mlx5hws_matcher_match_ste match_ste;
struct mlx5hws_matcher_action_ste action_ste[2];
struct list_head list_node;
struct list_head resize_data;
};
static inline bool
mlx5hws_matcher_mt_is_jumbo(struct mlx5hws_match_template *mt)
{
return mlx5hws_definer_is_jumbo(mt->definer);
}
static inline bool mlx5hws_matcher_is_resizable(struct mlx5hws_matcher *matcher)
{
return !!(matcher->flags & MLX5HWS_MATCHER_FLAGS_RESIZABLE);
}
static inline bool mlx5hws_matcher_is_in_resize(struct mlx5hws_matcher *matcher)
{
return !!matcher->resize_dst;
}
static inline bool mlx5hws_matcher_is_insert_by_idx(struct mlx5hws_matcher *matcher)
{
return matcher->attr.insert_mode == MLX5HWS_MATCHER_INSERT_BY_INDEX;
}
#endif /* MLX5HWS_MATCHER_H_ */

View File

@ -0,0 +1,579 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#include "mlx5hws_internal.h"
enum mlx5hws_arg_chunk_size
mlx5hws_arg_data_size_to_arg_log_size(u16 data_size)
{
/* Return the roundup of log2(data_size) */
if (data_size <= MLX5HWS_ARG_DATA_SIZE)
return MLX5HWS_ARG_CHUNK_SIZE_1;
if (data_size <= MLX5HWS_ARG_DATA_SIZE * 2)
return MLX5HWS_ARG_CHUNK_SIZE_2;
if (data_size <= MLX5HWS_ARG_DATA_SIZE * 4)
return MLX5HWS_ARG_CHUNK_SIZE_3;
if (data_size <= MLX5HWS_ARG_DATA_SIZE * 8)
return MLX5HWS_ARG_CHUNK_SIZE_4;
return MLX5HWS_ARG_CHUNK_SIZE_MAX;
}
u32 mlx5hws_arg_data_size_to_arg_size(u16 data_size)
{
return BIT(mlx5hws_arg_data_size_to_arg_log_size(data_size));
}
enum mlx5hws_arg_chunk_size
mlx5hws_arg_get_arg_log_size(u16 num_of_actions)
{
return mlx5hws_arg_data_size_to_arg_log_size(num_of_actions *
MLX5HWS_MODIFY_ACTION_SIZE);
}
u32 mlx5hws_arg_get_arg_size(u16 num_of_actions)
{
return BIT(mlx5hws_arg_get_arg_log_size(num_of_actions));
}
bool mlx5hws_pat_require_reparse(__be64 *actions, u16 num_of_actions)
{
u16 i, field;
u8 action_id;
for (i = 0; i < num_of_actions; i++) {
action_id = MLX5_GET(set_action_in, &actions[i], action_type);
switch (action_id) {
case MLX5_MODIFICATION_TYPE_NOP:
field = MLX5_MODI_OUT_NONE;
break;
case MLX5_MODIFICATION_TYPE_SET:
case MLX5_MODIFICATION_TYPE_ADD:
field = MLX5_GET(set_action_in, &actions[i], field);
break;
case MLX5_MODIFICATION_TYPE_COPY:
case MLX5_MODIFICATION_TYPE_ADD_FIELD:
field = MLX5_GET(copy_action_in, &actions[i], dst_field);
break;
default:
/* Insert/Remove/Unknown actions require reparse */
return true;
}
/* Below fields can change packet structure require a reparse */
if (field == MLX5_MODI_OUT_ETHERTYPE ||
field == MLX5_MODI_OUT_IPV6_NEXT_HDR)
return true;
}
return false;
}
/* Cache and cache element handling */
int mlx5hws_pat_init_pattern_cache(struct mlx5hws_pattern_cache **cache)
{
struct mlx5hws_pattern_cache *new_cache;
new_cache = kzalloc(sizeof(*new_cache), GFP_KERNEL);
if (!new_cache)
return -ENOMEM;
INIT_LIST_HEAD(&new_cache->ptrn_list);
mutex_init(&new_cache->lock);
*cache = new_cache;
return 0;
}
void mlx5hws_pat_uninit_pattern_cache(struct mlx5hws_pattern_cache *cache)
{
mutex_destroy(&cache->lock);
kfree(cache);
}
static bool mlx5hws_pat_compare_pattern(int cur_num_of_actions,
__be64 cur_actions[],
int num_of_actions,
__be64 actions[])
{
int i;
if (cur_num_of_actions != num_of_actions)
return false;
for (i = 0; i < num_of_actions; i++) {
u8 action_id =
MLX5_GET(set_action_in, &actions[i], action_type);
if (action_id == MLX5_MODIFICATION_TYPE_COPY ||
action_id == MLX5_MODIFICATION_TYPE_ADD_FIELD) {
if (actions[i] != cur_actions[i])
return false;
} else {
/* Compare just the control, not the values */
if ((__force __be32)actions[i] !=
(__force __be32)cur_actions[i])
return false;
}
}
return true;
}
static struct mlx5hws_pattern_cache_item *
mlx5hws_pat_find_cached_pattern(struct mlx5hws_pattern_cache *cache,
u16 num_of_actions,
__be64 *actions)
{
struct mlx5hws_pattern_cache_item *cached_pat = NULL;
list_for_each_entry(cached_pat, &cache->ptrn_list, ptrn_list_node) {
if (mlx5hws_pat_compare_pattern(cached_pat->mh_data.num_of_actions,
(__be64 *)cached_pat->mh_data.data,
num_of_actions,
actions))
return cached_pat;
}
return NULL;
}
static struct mlx5hws_pattern_cache_item *
mlx5hws_pat_get_existing_cached_pattern(struct mlx5hws_pattern_cache *cache,
u16 num_of_actions,
__be64 *actions)
{
struct mlx5hws_pattern_cache_item *cached_pattern;
cached_pattern = mlx5hws_pat_find_cached_pattern(cache, num_of_actions, actions);
if (cached_pattern) {
/* LRU: move it to be first in the list */
list_del_init(&cached_pattern->ptrn_list_node);
list_add(&cached_pattern->ptrn_list_node, &cache->ptrn_list);
cached_pattern->refcount++;
}
return cached_pattern;
}
static struct mlx5hws_pattern_cache_item *
mlx5hws_pat_add_pattern_to_cache(struct mlx5hws_pattern_cache *cache,
u32 pattern_id,
u16 num_of_actions,
__be64 *actions)
{
struct mlx5hws_pattern_cache_item *cached_pattern;
cached_pattern = kzalloc(sizeof(*cached_pattern), GFP_KERNEL);
if (!cached_pattern)
return NULL;
cached_pattern->mh_data.num_of_actions = num_of_actions;
cached_pattern->mh_data.pattern_id = pattern_id;
cached_pattern->mh_data.data =
kmemdup(actions, num_of_actions * MLX5HWS_MODIFY_ACTION_SIZE, GFP_KERNEL);
if (!cached_pattern->mh_data.data)
goto free_cached_obj;
list_add(&cached_pattern->ptrn_list_node, &cache->ptrn_list);
cached_pattern->refcount = 1;
return cached_pattern;
free_cached_obj:
kfree(cached_pattern);
return NULL;
}
static struct mlx5hws_pattern_cache_item *
mlx5hws_pat_find_cached_pattern_by_id(struct mlx5hws_pattern_cache *cache,
u32 ptrn_id)
{
struct mlx5hws_pattern_cache_item *cached_pattern = NULL;
list_for_each_entry(cached_pattern, &cache->ptrn_list, ptrn_list_node) {
if (cached_pattern->mh_data.pattern_id == ptrn_id)
return cached_pattern;
}
return NULL;
}
static void
mlx5hws_pat_remove_pattern(struct mlx5hws_pattern_cache_item *cached_pattern)
{
list_del_init(&cached_pattern->ptrn_list_node);
kfree(cached_pattern->mh_data.data);
kfree(cached_pattern);
}
void mlx5hws_pat_put_pattern(struct mlx5hws_context *ctx, u32 ptrn_id)
{
struct mlx5hws_pattern_cache *cache = ctx->pattern_cache;
struct mlx5hws_pattern_cache_item *cached_pattern;
mutex_lock(&cache->lock);
cached_pattern = mlx5hws_pat_find_cached_pattern_by_id(cache, ptrn_id);
if (!cached_pattern) {
mlx5hws_err(ctx, "Failed to find cached pattern with provided ID\n");
pr_warn("HWS: pattern ID %d is not found\n", ptrn_id);
goto out;
}
if (--cached_pattern->refcount)
goto out;
mlx5hws_pat_remove_pattern(cached_pattern);
mlx5hws_cmd_header_modify_pattern_destroy(ctx->mdev, ptrn_id);
out:
mutex_unlock(&cache->lock);
}
int mlx5hws_pat_get_pattern(struct mlx5hws_context *ctx,
__be64 *pattern, size_t pattern_sz,
u32 *pattern_id)
{
u16 num_of_actions = pattern_sz / MLX5HWS_MODIFY_ACTION_SIZE;
struct mlx5hws_pattern_cache_item *cached_pattern;
u32 ptrn_id = 0;
int ret = 0;
mutex_lock(&ctx->pattern_cache->lock);
cached_pattern = mlx5hws_pat_get_existing_cached_pattern(ctx->pattern_cache,
num_of_actions,
pattern);
if (cached_pattern) {
*pattern_id = cached_pattern->mh_data.pattern_id;
goto out_unlock;
}
ret = mlx5hws_cmd_header_modify_pattern_create(ctx->mdev,
pattern_sz,
(u8 *)pattern,
&ptrn_id);
if (ret) {
mlx5hws_err(ctx, "Failed to create pattern FW object\n");
goto out_unlock;
}
cached_pattern = mlx5hws_pat_add_pattern_to_cache(ctx->pattern_cache,
ptrn_id,
num_of_actions,
pattern);
if (!cached_pattern) {
mlx5hws_err(ctx, "Failed to add pattern to cache\n");
ret = -EINVAL;
goto clean_pattern;
}
mutex_unlock(&ctx->pattern_cache->lock);
*pattern_id = ptrn_id;
return ret;
clean_pattern:
mlx5hws_cmd_header_modify_pattern_destroy(ctx->mdev, *pattern_id);
out_unlock:
mutex_unlock(&ctx->pattern_cache->lock);
return ret;
}
static void
mlx5d_arg_init_send_attr(struct mlx5hws_send_engine_post_attr *send_attr,
void *comp_data,
u32 arg_idx)
{
send_attr->opcode = MLX5HWS_WQE_OPCODE_TBL_ACCESS;
send_attr->opmod = MLX5HWS_WQE_GTA_OPMOD_MOD_ARG;
send_attr->len = MLX5HWS_WQE_SZ_GTA_CTRL + MLX5HWS_WQE_SZ_GTA_DATA;
send_attr->id = arg_idx;
send_attr->user_data = comp_data;
}
void mlx5hws_arg_decapl3_write(struct mlx5hws_send_engine *queue,
u32 arg_idx,
u8 *arg_data,
u16 num_of_actions)
{
struct mlx5hws_send_engine_post_attr send_attr = {0};
struct mlx5hws_wqe_gta_data_seg_arg *wqe_arg = NULL;
struct mlx5hws_wqe_gta_ctrl_seg *wqe_ctrl = NULL;
struct mlx5hws_send_engine_post_ctrl ctrl;
size_t wqe_len;
mlx5d_arg_init_send_attr(&send_attr, NULL, arg_idx);
ctrl = mlx5hws_send_engine_post_start(queue);
mlx5hws_send_engine_post_req_wqe(&ctrl, (void *)&wqe_ctrl, &wqe_len);
memset(wqe_ctrl, 0, wqe_len);
mlx5hws_send_engine_post_req_wqe(&ctrl, (void *)&wqe_arg, &wqe_len);
mlx5hws_action_prepare_decap_l3_data(arg_data, (u8 *)wqe_arg,
num_of_actions);
mlx5hws_send_engine_post_end(&ctrl, &send_attr);
}
void mlx5hws_arg_write(struct mlx5hws_send_engine *queue,
void *comp_data,
u32 arg_idx,
u8 *arg_data,
size_t data_size)
{
struct mlx5hws_send_engine_post_attr send_attr = {0};
struct mlx5hws_wqe_gta_data_seg_arg *wqe_arg;
struct mlx5hws_send_engine_post_ctrl ctrl;
struct mlx5hws_wqe_gta_ctrl_seg *wqe_ctrl;
int i, full_iter, leftover;
size_t wqe_len;
mlx5d_arg_init_send_attr(&send_attr, comp_data, arg_idx);
/* Each WQE can hold 64B of data, it might require multiple iteration */
full_iter = data_size / MLX5HWS_ARG_DATA_SIZE;
leftover = data_size & (MLX5HWS_ARG_DATA_SIZE - 1);
for (i = 0; i < full_iter; i++) {
ctrl = mlx5hws_send_engine_post_start(queue);
mlx5hws_send_engine_post_req_wqe(&ctrl, (void *)&wqe_ctrl, &wqe_len);
memset(wqe_ctrl, 0, wqe_len);
mlx5hws_send_engine_post_req_wqe(&ctrl, (void *)&wqe_arg, &wqe_len);
memcpy(wqe_arg, arg_data, wqe_len);
send_attr.id = arg_idx++;
mlx5hws_send_engine_post_end(&ctrl, &send_attr);
/* Move to next argument data */
arg_data += MLX5HWS_ARG_DATA_SIZE;
}
if (leftover) {
ctrl = mlx5hws_send_engine_post_start(queue);
mlx5hws_send_engine_post_req_wqe(&ctrl, (void *)&wqe_ctrl, &wqe_len);
memset(wqe_ctrl, 0, wqe_len);
mlx5hws_send_engine_post_req_wqe(&ctrl, (void *)&wqe_arg, &wqe_len);
memcpy(wqe_arg, arg_data, leftover);
send_attr.id = arg_idx;
mlx5hws_send_engine_post_end(&ctrl, &send_attr);
}
}
int mlx5hws_arg_write_inline_arg_data(struct mlx5hws_context *ctx,
u32 arg_idx,
u8 *arg_data,
size_t data_size)
{
struct mlx5hws_send_engine *queue;
int ret;
mutex_lock(&ctx->ctrl_lock);
/* Get the control queue */
queue = &ctx->send_queue[ctx->queues - 1];
mlx5hws_arg_write(queue, arg_data, arg_idx, arg_data, data_size);
mlx5hws_send_engine_flush_queue(queue);
/* Poll for completion */
ret = mlx5hws_send_queue_action(ctx, ctx->queues - 1,
MLX5HWS_SEND_QUEUE_ACTION_DRAIN_SYNC);
if (ret)
mlx5hws_err(ctx, "Failed to drain arg queue\n");
mutex_unlock(&ctx->ctrl_lock);
return ret;
}
bool mlx5hws_arg_is_valid_arg_request_size(struct mlx5hws_context *ctx,
u32 arg_size)
{
if (arg_size < ctx->caps->log_header_modify_argument_granularity ||
arg_size > ctx->caps->log_header_modify_argument_max_alloc) {
return false;
}
return true;
}
int mlx5hws_arg_create(struct mlx5hws_context *ctx,
u8 *data,
size_t data_sz,
u32 log_bulk_sz,
bool write_data,
u32 *arg_id)
{
u16 single_arg_log_sz;
u16 multi_arg_log_sz;
int ret;
u32 id;
single_arg_log_sz = mlx5hws_arg_data_size_to_arg_log_size(data_sz);
multi_arg_log_sz = single_arg_log_sz + log_bulk_sz;
if (single_arg_log_sz >= MLX5HWS_ARG_CHUNK_SIZE_MAX) {
mlx5hws_err(ctx, "Requested single arg %u not supported\n", single_arg_log_sz);
return -EOPNOTSUPP;
}
if (!mlx5hws_arg_is_valid_arg_request_size(ctx, multi_arg_log_sz)) {
mlx5hws_err(ctx, "Argument log size %d not supported by FW\n", multi_arg_log_sz);
return -EOPNOTSUPP;
}
/* Alloc bulk of args */
ret = mlx5hws_cmd_arg_create(ctx->mdev, multi_arg_log_sz, ctx->pd_num, &id);
if (ret) {
mlx5hws_err(ctx, "Failed allocating arg in order: %d\n", multi_arg_log_sz);
return ret;
}
if (write_data) {
ret = mlx5hws_arg_write_inline_arg_data(ctx, id,
data, data_sz);
if (ret) {
mlx5hws_err(ctx, "Failed writing arg data\n");
mlx5hws_cmd_arg_destroy(ctx->mdev, id);
return ret;
}
}
*arg_id = id;
return ret;
}
void mlx5hws_arg_destroy(struct mlx5hws_context *ctx, u32 arg_id)
{
mlx5hws_cmd_arg_destroy(ctx->mdev, arg_id);
}
int mlx5hws_arg_create_modify_header_arg(struct mlx5hws_context *ctx,
__be64 *data,
u8 num_of_actions,
u32 log_bulk_sz,
bool write_data,
u32 *arg_id)
{
size_t data_sz = num_of_actions * MLX5HWS_MODIFY_ACTION_SIZE;
int ret;
ret = mlx5hws_arg_create(ctx,
(u8 *)data,
data_sz,
log_bulk_sz,
write_data,
arg_id);
if (ret)
mlx5hws_err(ctx, "Failed creating modify header arg\n");
return ret;
}
static int
hws_action_modify_check_field_limitation(u8 action_type, __be64 *pattern)
{
/* Need to check field limitation here, but for now - return OK */
return 0;
}
#define INVALID_FIELD 0xffff
static void
hws_action_modify_get_target_fields(u8 action_type, __be64 *pattern,
u16 *src_field, u16 *dst_field)
{
switch (action_type) {
case MLX5_ACTION_TYPE_SET:
case MLX5_ACTION_TYPE_ADD:
*src_field = MLX5_GET(set_action_in, pattern, field);
*dst_field = INVALID_FIELD;
break;
case MLX5_ACTION_TYPE_COPY:
*src_field = MLX5_GET(copy_action_in, pattern, src_field);
*dst_field = MLX5_GET(copy_action_in, pattern, dst_field);
break;
default:
pr_warn("HWS: invalid modify header action type %d\n", action_type);
}
}
bool mlx5hws_pat_verify_actions(struct mlx5hws_context *ctx, __be64 pattern[], size_t sz)
{
size_t i;
for (i = 0; i < sz / MLX5HWS_MODIFY_ACTION_SIZE; i++) {
u8 action_type =
MLX5_GET(set_action_in, &pattern[i], action_type);
if (action_type >= MLX5_MODIFICATION_TYPE_MAX) {
mlx5hws_err(ctx, "Unsupported action id %d\n", action_type);
return false;
}
if (hws_action_modify_check_field_limitation(action_type, &pattern[i])) {
mlx5hws_err(ctx, "Unsupported action number %zu\n", i);
return false;
}
}
return true;
}
void mlx5hws_pat_calc_nope(__be64 *pattern, size_t num_actions,
size_t max_actions, size_t *new_size,
u32 *nope_location, __be64 *new_pat)
{
u16 prev_src_field = 0, prev_dst_field = 0;
u16 src_field, dst_field;
u8 action_type;
size_t i, j;
*new_size = num_actions;
*nope_location = 0;
if (num_actions == 1)
return;
for (i = 0, j = 0; i < num_actions; i++, j++) {
action_type = MLX5_GET(set_action_in, &pattern[i], action_type);
hws_action_modify_get_target_fields(action_type, &pattern[i],
&src_field, &dst_field);
if (i % 2) {
if (action_type == MLX5_ACTION_TYPE_COPY &&
(prev_src_field == src_field ||
prev_dst_field == dst_field)) {
/* need Nope */
*new_size += 1;
*nope_location |= BIT(i);
memset(&new_pat[j], 0, MLX5HWS_MODIFY_ACTION_SIZE);
MLX5_SET(set_action_in, &new_pat[j],
action_type,
MLX5_MODIFICATION_TYPE_NOP);
j++;
} else if (prev_src_field == src_field) {
/* need Nope*/
*new_size += 1;
*nope_location |= BIT(i);
MLX5_SET(set_action_in, &new_pat[j],
action_type,
MLX5_MODIFICATION_TYPE_NOP);
j++;
}
}
memcpy(&new_pat[j], &pattern[i], MLX5HWS_MODIFY_ACTION_SIZE);
/* check if no more space */
if (j > max_actions) {
*new_size = num_actions;
*nope_location = 0;
return;
}
prev_src_field = src_field;
prev_dst_field = dst_field;
}
}

View File

@ -0,0 +1,101 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#ifndef MLX5HWS_PAT_ARG_H_
#define MLX5HWS_PAT_ARG_H_
/* Modify-header arg pool */
enum mlx5hws_arg_chunk_size {
MLX5HWS_ARG_CHUNK_SIZE_1,
/* Keep MIN updated when changing */
MLX5HWS_ARG_CHUNK_SIZE_MIN = MLX5HWS_ARG_CHUNK_SIZE_1,
MLX5HWS_ARG_CHUNK_SIZE_2,
MLX5HWS_ARG_CHUNK_SIZE_3,
MLX5HWS_ARG_CHUNK_SIZE_4,
MLX5HWS_ARG_CHUNK_SIZE_MAX,
};
enum {
MLX5HWS_MODIFY_ACTION_SIZE = 8,
MLX5HWS_ARG_DATA_SIZE = 64,
};
struct mlx5hws_pattern_cache {
struct mutex lock; /* Protect pattern list */
struct list_head ptrn_list;
};
struct mlx5hws_pattern_cache_item {
struct {
u32 pattern_id;
u8 *data;
u16 num_of_actions;
} mh_data;
u32 refcount;
struct list_head ptrn_list_node;
};
enum mlx5hws_arg_chunk_size
mlx5hws_arg_get_arg_log_size(u16 num_of_actions);
u32 mlx5hws_arg_get_arg_size(u16 num_of_actions);
enum mlx5hws_arg_chunk_size
mlx5hws_arg_data_size_to_arg_log_size(u16 data_size);
u32 mlx5hws_arg_data_size_to_arg_size(u16 data_size);
int mlx5hws_pat_init_pattern_cache(struct mlx5hws_pattern_cache **cache);
void mlx5hws_pat_uninit_pattern_cache(struct mlx5hws_pattern_cache *cache);
bool mlx5hws_pat_verify_actions(struct mlx5hws_context *ctx, __be64 pattern[], size_t sz);
int mlx5hws_arg_create(struct mlx5hws_context *ctx,
u8 *data,
size_t data_sz,
u32 log_bulk_sz,
bool write_data,
u32 *arg_id);
void mlx5hws_arg_destroy(struct mlx5hws_context *ctx, u32 arg_id);
int mlx5hws_arg_create_modify_header_arg(struct mlx5hws_context *ctx,
__be64 *data,
u8 num_of_actions,
u32 log_bulk_sz,
bool write_data,
u32 *modify_hdr_arg_id);
int mlx5hws_pat_get_pattern(struct mlx5hws_context *ctx,
__be64 *pattern,
size_t pattern_sz,
u32 *ptrn_id);
void mlx5hws_pat_put_pattern(struct mlx5hws_context *ctx,
u32 ptrn_id);
bool mlx5hws_arg_is_valid_arg_request_size(struct mlx5hws_context *ctx,
u32 arg_size);
bool mlx5hws_pat_require_reparse(__be64 *actions, u16 num_of_actions);
void mlx5hws_arg_write(struct mlx5hws_send_engine *queue,
void *comp_data,
u32 arg_idx,
u8 *arg_data,
size_t data_size);
void mlx5hws_arg_decapl3_write(struct mlx5hws_send_engine *queue,
u32 arg_idx,
u8 *arg_data,
u16 num_of_actions);
int mlx5hws_arg_write_inline_arg_data(struct mlx5hws_context *ctx,
u32 arg_idx,
u8 *arg_data,
size_t data_size);
void mlx5hws_pat_calc_nope(__be64 *pattern, size_t num_actions, size_t max_actions,
size_t *new_size, u32 *nope_location, __be64 *new_pat);
#endif /* MLX5HWS_PAT_ARG_H_ */

View File

@ -0,0 +1,640 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#include "mlx5hws_internal.h"
#include "mlx5hws_buddy.h"
static void hws_pool_free_one_resource(struct mlx5hws_pool_resource *resource)
{
switch (resource->pool->type) {
case MLX5HWS_POOL_TYPE_STE:
mlx5hws_cmd_ste_destroy(resource->pool->ctx->mdev, resource->base_id);
break;
case MLX5HWS_POOL_TYPE_STC:
mlx5hws_cmd_stc_destroy(resource->pool->ctx->mdev, resource->base_id);
break;
default:
break;
}
kfree(resource);
}
static void hws_pool_resource_free(struct mlx5hws_pool *pool,
int resource_idx)
{
hws_pool_free_one_resource(pool->resource[resource_idx]);
pool->resource[resource_idx] = NULL;
if (pool->tbl_type == MLX5HWS_TABLE_TYPE_FDB) {
hws_pool_free_one_resource(pool->mirror_resource[resource_idx]);
pool->mirror_resource[resource_idx] = NULL;
}
}
static struct mlx5hws_pool_resource *
hws_pool_create_one_resource(struct mlx5hws_pool *pool, u32 log_range,
u32 fw_ft_type)
{
struct mlx5hws_cmd_ste_create_attr ste_attr;
struct mlx5hws_cmd_stc_create_attr stc_attr;
struct mlx5hws_pool_resource *resource;
u32 obj_id = 0;
int ret;
resource = kzalloc(sizeof(*resource), GFP_KERNEL);
if (!resource)
return NULL;
switch (pool->type) {
case MLX5HWS_POOL_TYPE_STE:
ste_attr.log_obj_range = log_range;
ste_attr.table_type = fw_ft_type;
ret = mlx5hws_cmd_ste_create(pool->ctx->mdev, &ste_attr, &obj_id);
break;
case MLX5HWS_POOL_TYPE_STC:
stc_attr.log_obj_range = log_range;
stc_attr.table_type = fw_ft_type;
ret = mlx5hws_cmd_stc_create(pool->ctx->mdev, &stc_attr, &obj_id);
break;
default:
ret = -EINVAL;
}
if (ret) {
mlx5hws_err(pool->ctx, "Failed to allocate resource objects\n");
goto free_resource;
}
resource->pool = pool;
resource->range = 1 << log_range;
resource->base_id = obj_id;
return resource;
free_resource:
kfree(resource);
return NULL;
}
static int
hws_pool_resource_alloc(struct mlx5hws_pool *pool, u32 log_range, int idx)
{
struct mlx5hws_pool_resource *resource;
u32 fw_ft_type, opt_log_range;
fw_ft_type = mlx5hws_table_get_res_fw_ft_type(pool->tbl_type, false);
opt_log_range = pool->opt_type == MLX5HWS_POOL_OPTIMIZE_ORIG ? 0 : log_range;
resource = hws_pool_create_one_resource(pool, opt_log_range, fw_ft_type);
if (!resource) {
mlx5hws_err(pool->ctx, "Failed allocating resource\n");
return -EINVAL;
}
pool->resource[idx] = resource;
if (pool->tbl_type == MLX5HWS_TABLE_TYPE_FDB) {
struct mlx5hws_pool_resource *mirror_resource;
fw_ft_type = mlx5hws_table_get_res_fw_ft_type(pool->tbl_type, true);
opt_log_range = pool->opt_type == MLX5HWS_POOL_OPTIMIZE_MIRROR ? 0 : log_range;
mirror_resource = hws_pool_create_one_resource(pool, opt_log_range, fw_ft_type);
if (!mirror_resource) {
mlx5hws_err(pool->ctx, "Failed allocating mirrored resource\n");
hws_pool_free_one_resource(resource);
pool->resource[idx] = NULL;
return -EINVAL;
}
pool->mirror_resource[idx] = mirror_resource;
}
return 0;
}
static unsigned long *hws_pool_create_and_init_bitmap(u32 log_range)
{
unsigned long *cur_bmp;
cur_bmp = bitmap_zalloc(1 << log_range, GFP_KERNEL);
if (!cur_bmp)
return NULL;
bitmap_fill(cur_bmp, 1 << log_range);
return cur_bmp;
}
static void hws_pool_buddy_db_put_chunk(struct mlx5hws_pool *pool,
struct mlx5hws_pool_chunk *chunk)
{
struct mlx5hws_buddy_mem *buddy;
buddy = pool->db.buddy_manager->buddies[chunk->resource_idx];
if (!buddy) {
mlx5hws_err(pool->ctx, "No such buddy (%d)\n", chunk->resource_idx);
return;
}
mlx5hws_buddy_free_mem(buddy, chunk->offset, chunk->order);
}
static struct mlx5hws_buddy_mem *
hws_pool_buddy_get_next_buddy(struct mlx5hws_pool *pool, int idx,
u32 order, bool *is_new_buddy)
{
static struct mlx5hws_buddy_mem *buddy;
u32 new_buddy_size;
buddy = pool->db.buddy_manager->buddies[idx];
if (buddy)
return buddy;
new_buddy_size = max(pool->alloc_log_sz, order);
*is_new_buddy = true;
buddy = mlx5hws_buddy_create(new_buddy_size);
if (!buddy) {
mlx5hws_err(pool->ctx, "Failed to create buddy order: %d index: %d\n",
new_buddy_size, idx);
return NULL;
}
if (hws_pool_resource_alloc(pool, new_buddy_size, idx) != 0) {
mlx5hws_err(pool->ctx, "Failed to create resource type: %d: size %d index: %d\n",
pool->type, new_buddy_size, idx);
mlx5hws_buddy_cleanup(buddy);
return NULL;
}
pool->db.buddy_manager->buddies[idx] = buddy;
return buddy;
}
static int hws_pool_buddy_get_mem_chunk(struct mlx5hws_pool *pool,
int order,
u32 *buddy_idx,
int *seg)
{
struct mlx5hws_buddy_mem *buddy;
bool new_mem = false;
int ret = 0;
int i;
*seg = -1;
/* Find the next free place from the buddy array */
while (*seg == -1) {
for (i = 0; i < MLX5HWS_POOL_RESOURCE_ARR_SZ; i++) {
buddy = hws_pool_buddy_get_next_buddy(pool, i,
order,
&new_mem);
if (!buddy) {
ret = -ENOMEM;
goto out;
}
*seg = mlx5hws_buddy_alloc_mem(buddy, order);
if (*seg != -1)
goto found;
if (pool->flags & MLX5HWS_POOL_FLAGS_ONE_RESOURCE) {
mlx5hws_err(pool->ctx,
"Fail to allocate seg for one resource pool\n");
ret = -ENOMEM;
goto out;
}
if (new_mem) {
/* We have new memory pool, should be place for us */
mlx5hws_err(pool->ctx,
"No memory for order: %d with buddy no: %d\n",
order, i);
ret = -ENOMEM;
goto out;
}
}
}
found:
*buddy_idx = i;
out:
return ret;
}
static int hws_pool_buddy_db_get_chunk(struct mlx5hws_pool *pool,
struct mlx5hws_pool_chunk *chunk)
{
int ret = 0;
/* Go over the buddies and find next free slot */
ret = hws_pool_buddy_get_mem_chunk(pool, chunk->order,
&chunk->resource_idx,
&chunk->offset);
if (ret)
mlx5hws_err(pool->ctx, "Failed to get free slot for chunk with order: %d\n",
chunk->order);
return ret;
}
static void hws_pool_buddy_db_uninit(struct mlx5hws_pool *pool)
{
struct mlx5hws_buddy_mem *buddy;
int i;
for (i = 0; i < MLX5HWS_POOL_RESOURCE_ARR_SZ; i++) {
buddy = pool->db.buddy_manager->buddies[i];
if (buddy) {
mlx5hws_buddy_cleanup(buddy);
kfree(buddy);
pool->db.buddy_manager->buddies[i] = NULL;
}
}
kfree(pool->db.buddy_manager);
}
static int hws_pool_buddy_db_init(struct mlx5hws_pool *pool, u32 log_range)
{
pool->db.buddy_manager = kzalloc(sizeof(*pool->db.buddy_manager), GFP_KERNEL);
if (!pool->db.buddy_manager)
return -ENOMEM;
if (pool->flags & MLX5HWS_POOL_FLAGS_ALLOC_MEM_ON_CREATE) {
bool new_buddy;
if (!hws_pool_buddy_get_next_buddy(pool, 0, log_range, &new_buddy)) {
mlx5hws_err(pool->ctx,
"Failed allocating memory on create log_sz: %d\n", log_range);
kfree(pool->db.buddy_manager);
return -ENOMEM;
}
}
pool->p_db_uninit = &hws_pool_buddy_db_uninit;
pool->p_get_chunk = &hws_pool_buddy_db_get_chunk;
pool->p_put_chunk = &hws_pool_buddy_db_put_chunk;
return 0;
}
static int hws_pool_create_resource_on_index(struct mlx5hws_pool *pool,
u32 alloc_size, int idx)
{
int ret = hws_pool_resource_alloc(pool, alloc_size, idx);
if (ret) {
mlx5hws_err(pool->ctx, "Failed to create resource type: %d: size %d index: %d\n",
pool->type, alloc_size, idx);
return ret;
}
return 0;
}
static struct mlx5hws_pool_elements *
hws_pool_element_create_new_elem(struct mlx5hws_pool *pool, u32 order, int idx)
{
struct mlx5hws_pool_elements *elem;
u32 alloc_size;
alloc_size = pool->alloc_log_sz;
elem = kzalloc(sizeof(*elem), GFP_KERNEL);
if (!elem)
return NULL;
/* Sharing the same resource, also means that all the elements are with size 1 */
if ((pool->flags & MLX5HWS_POOL_FLAGS_FIXED_SIZE_OBJECTS) &&
!(pool->flags & MLX5HWS_POOL_FLAGS_RESOURCE_PER_CHUNK)) {
/* Currently all chunks in size 1 */
elem->bitmap = hws_pool_create_and_init_bitmap(alloc_size - order);
if (!elem->bitmap) {
mlx5hws_err(pool->ctx,
"Failed to create bitmap type: %d: size %d index: %d\n",
pool->type, alloc_size, idx);
goto free_elem;
}
elem->log_size = alloc_size - order;
}
if (hws_pool_create_resource_on_index(pool, alloc_size, idx)) {
mlx5hws_err(pool->ctx, "Failed to create resource type: %d: size %d index: %d\n",
pool->type, alloc_size, idx);
goto free_db;
}
pool->db.element_manager->elements[idx] = elem;
return elem;
free_db:
bitmap_free(elem->bitmap);
free_elem:
kfree(elem);
return NULL;
}
static int hws_pool_element_find_seg(struct mlx5hws_pool_elements *elem, int *seg)
{
unsigned int segment, size;
size = 1 << elem->log_size;
segment = find_first_bit(elem->bitmap, size);
if (segment >= size) {
elem->is_full = true;
return -ENOMEM;
}
bitmap_clear(elem->bitmap, segment, 1);
*seg = segment;
return 0;
}
static int
hws_pool_onesize_element_get_mem_chunk(struct mlx5hws_pool *pool, u32 order,
u32 *idx, int *seg)
{
struct mlx5hws_pool_elements *elem;
elem = pool->db.element_manager->elements[0];
if (!elem)
elem = hws_pool_element_create_new_elem(pool, order, 0);
if (!elem)
goto err_no_elem;
if (hws_pool_element_find_seg(elem, seg) != 0) {
mlx5hws_err(pool->ctx, "No more resources (last request order: %d)\n", order);
return -ENOMEM;
}
*idx = 0;
elem->num_of_elements++;
return 0;
err_no_elem:
mlx5hws_err(pool->ctx, "Failed to allocate element for order: %d\n", order);
return -ENOMEM;
}
static int
hws_pool_general_element_get_mem_chunk(struct mlx5hws_pool *pool, u32 order,
u32 *idx, int *seg)
{
int ret, i;
for (i = 0; i < MLX5HWS_POOL_RESOURCE_ARR_SZ; i++) {
if (!pool->resource[i]) {
ret = hws_pool_create_resource_on_index(pool, order, i);
if (ret)
goto err_no_res;
*idx = i;
*seg = 0; /* One memory slot in that element */
return 0;
}
}
mlx5hws_err(pool->ctx, "No more resources (last request order: %d)\n", order);
return -ENOMEM;
err_no_res:
mlx5hws_err(pool->ctx, "Failed to allocate element for order: %d\n", order);
return -ENOMEM;
}
static int hws_pool_general_element_db_get_chunk(struct mlx5hws_pool *pool,
struct mlx5hws_pool_chunk *chunk)
{
int ret;
/* Go over all memory elements and find/allocate free slot */
ret = hws_pool_general_element_get_mem_chunk(pool, chunk->order,
&chunk->resource_idx,
&chunk->offset);
if (ret)
mlx5hws_err(pool->ctx, "Failed to get free slot for chunk with order: %d\n",
chunk->order);
return ret;
}
static void hws_pool_general_element_db_put_chunk(struct mlx5hws_pool *pool,
struct mlx5hws_pool_chunk *chunk)
{
if (unlikely(!pool->resource[chunk->resource_idx]))
pr_warn("HWS: invalid resource with index %d\n", chunk->resource_idx);
if (pool->flags & MLX5HWS_POOL_FLAGS_RELEASE_FREE_RESOURCE)
hws_pool_resource_free(pool, chunk->resource_idx);
}
static void hws_pool_general_element_db_uninit(struct mlx5hws_pool *pool)
{
(void)pool;
}
/* This memory management works as the following:
* - At start doesn't allocate no mem at all.
* - When new request for chunk arrived:
* allocate resource and give it.
* - When free that chunk:
* the resource is freed.
*/
static int hws_pool_general_element_db_init(struct mlx5hws_pool *pool)
{
pool->p_db_uninit = &hws_pool_general_element_db_uninit;
pool->p_get_chunk = &hws_pool_general_element_db_get_chunk;
pool->p_put_chunk = &hws_pool_general_element_db_put_chunk;
return 0;
}
static void hws_onesize_element_db_destroy_element(struct mlx5hws_pool *pool,
struct mlx5hws_pool_elements *elem,
struct mlx5hws_pool_chunk *chunk)
{
if (unlikely(!pool->resource[chunk->resource_idx]))
pr_warn("HWS: invalid resource with index %d\n", chunk->resource_idx);
hws_pool_resource_free(pool, chunk->resource_idx);
kfree(elem);
pool->db.element_manager->elements[chunk->resource_idx] = NULL;
}
static void hws_onesize_element_db_put_chunk(struct mlx5hws_pool *pool,
struct mlx5hws_pool_chunk *chunk)
{
struct mlx5hws_pool_elements *elem;
if (unlikely(chunk->resource_idx))
pr_warn("HWS: invalid resource with index %d\n", chunk->resource_idx);
elem = pool->db.element_manager->elements[chunk->resource_idx];
if (!elem) {
mlx5hws_err(pool->ctx, "No such element (%d)\n", chunk->resource_idx);
return;
}
bitmap_set(elem->bitmap, chunk->offset, 1);
elem->is_full = false;
elem->num_of_elements--;
if (pool->flags & MLX5HWS_POOL_FLAGS_RELEASE_FREE_RESOURCE &&
!elem->num_of_elements)
hws_onesize_element_db_destroy_element(pool, elem, chunk);
}
static int hws_onesize_element_db_get_chunk(struct mlx5hws_pool *pool,
struct mlx5hws_pool_chunk *chunk)
{
int ret = 0;
/* Go over all memory elements and find/allocate free slot */
ret = hws_pool_onesize_element_get_mem_chunk(pool, chunk->order,
&chunk->resource_idx,
&chunk->offset);
if (ret)
mlx5hws_err(pool->ctx, "Failed to get free slot for chunk with order: %d\n",
chunk->order);
return ret;
}
static void hws_onesize_element_db_uninit(struct mlx5hws_pool *pool)
{
struct mlx5hws_pool_elements *elem;
int i;
for (i = 0; i < MLX5HWS_POOL_RESOURCE_ARR_SZ; i++) {
elem = pool->db.element_manager->elements[i];
if (elem) {
bitmap_free(elem->bitmap);
kfree(elem);
pool->db.element_manager->elements[i] = NULL;
}
}
kfree(pool->db.element_manager);
}
/* This memory management works as the following:
* - At start doesn't allocate no mem at all.
* - When new request for chunk arrived:
* aloocate the first and only slot of memory/resource
* when it ended return error.
*/
static int hws_pool_onesize_element_db_init(struct mlx5hws_pool *pool)
{
pool->db.element_manager = kzalloc(sizeof(*pool->db.element_manager), GFP_KERNEL);
if (!pool->db.element_manager)
return -ENOMEM;
pool->p_db_uninit = &hws_onesize_element_db_uninit;
pool->p_get_chunk = &hws_onesize_element_db_get_chunk;
pool->p_put_chunk = &hws_onesize_element_db_put_chunk;
return 0;
}
static int hws_pool_db_init(struct mlx5hws_pool *pool,
enum mlx5hws_db_type db_type)
{
int ret;
if (db_type == MLX5HWS_POOL_DB_TYPE_GENERAL_SIZE)
ret = hws_pool_general_element_db_init(pool);
else if (db_type == MLX5HWS_POOL_DB_TYPE_ONE_SIZE_RESOURCE)
ret = hws_pool_onesize_element_db_init(pool);
else
ret = hws_pool_buddy_db_init(pool, pool->alloc_log_sz);
if (ret) {
mlx5hws_err(pool->ctx, "Failed to init general db : %d (ret: %d)\n", db_type, ret);
return ret;
}
return 0;
}
static void hws_pool_db_unint(struct mlx5hws_pool *pool)
{
pool->p_db_uninit(pool);
}
int mlx5hws_pool_chunk_alloc(struct mlx5hws_pool *pool,
struct mlx5hws_pool_chunk *chunk)
{
int ret;
mutex_lock(&pool->lock);
ret = pool->p_get_chunk(pool, chunk);
mutex_unlock(&pool->lock);
return ret;
}
void mlx5hws_pool_chunk_free(struct mlx5hws_pool *pool,
struct mlx5hws_pool_chunk *chunk)
{
mutex_lock(&pool->lock);
pool->p_put_chunk(pool, chunk);
mutex_unlock(&pool->lock);
}
struct mlx5hws_pool *
mlx5hws_pool_create(struct mlx5hws_context *ctx, struct mlx5hws_pool_attr *pool_attr)
{
enum mlx5hws_db_type res_db_type;
struct mlx5hws_pool *pool;
pool = kzalloc(sizeof(*pool), GFP_KERNEL);
if (!pool)
return NULL;
pool->ctx = ctx;
pool->type = pool_attr->pool_type;
pool->alloc_log_sz = pool_attr->alloc_log_sz;
pool->flags = pool_attr->flags;
pool->tbl_type = pool_attr->table_type;
pool->opt_type = pool_attr->opt_type;
/* Support general db */
if (pool->flags == (MLX5HWS_POOL_FLAGS_RELEASE_FREE_RESOURCE |
MLX5HWS_POOL_FLAGS_RESOURCE_PER_CHUNK))
res_db_type = MLX5HWS_POOL_DB_TYPE_GENERAL_SIZE;
else if (pool->flags == (MLX5HWS_POOL_FLAGS_ONE_RESOURCE |
MLX5HWS_POOL_FLAGS_FIXED_SIZE_OBJECTS))
res_db_type = MLX5HWS_POOL_DB_TYPE_ONE_SIZE_RESOURCE;
else
res_db_type = MLX5HWS_POOL_DB_TYPE_BUDDY;
pool->alloc_log_sz = pool_attr->alloc_log_sz;
if (hws_pool_db_init(pool, res_db_type))
goto free_pool;
mutex_init(&pool->lock);
return pool;
free_pool:
kfree(pool);
return NULL;
}
int mlx5hws_pool_destroy(struct mlx5hws_pool *pool)
{
int i;
mutex_destroy(&pool->lock);
for (i = 0; i < MLX5HWS_POOL_RESOURCE_ARR_SZ; i++)
if (pool->resource[i])
hws_pool_resource_free(pool, i);
hws_pool_db_unint(pool);
kfree(pool);
return 0;
}

View File

@ -0,0 +1,151 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#ifndef MLX5HWS_POOL_H_
#define MLX5HWS_POOL_H_
#define MLX5HWS_POOL_STC_LOG_SZ 15
#define MLX5HWS_POOL_RESOURCE_ARR_SZ 100
enum mlx5hws_pool_type {
MLX5HWS_POOL_TYPE_STE,
MLX5HWS_POOL_TYPE_STC,
};
struct mlx5hws_pool_chunk {
u32 resource_idx;
/* Internal offset, relative to base index */
int offset;
int order;
};
struct mlx5hws_pool_resource {
struct mlx5hws_pool *pool;
u32 base_id;
u32 range;
};
enum mlx5hws_pool_flags {
/* Only a one resource in that pool */
MLX5HWS_POOL_FLAGS_ONE_RESOURCE = 1 << 0,
MLX5HWS_POOL_FLAGS_RELEASE_FREE_RESOURCE = 1 << 1,
/* No sharing resources between chunks */
MLX5HWS_POOL_FLAGS_RESOURCE_PER_CHUNK = 1 << 2,
/* All objects are in the same size */
MLX5HWS_POOL_FLAGS_FIXED_SIZE_OBJECTS = 1 << 3,
/* Managed by buddy allocator */
MLX5HWS_POOL_FLAGS_BUDDY_MANAGED = 1 << 4,
/* Allocate pool_type memory on pool creation */
MLX5HWS_POOL_FLAGS_ALLOC_MEM_ON_CREATE = 1 << 5,
/* These values should be used by the caller */
MLX5HWS_POOL_FLAGS_FOR_STC_POOL =
MLX5HWS_POOL_FLAGS_ONE_RESOURCE |
MLX5HWS_POOL_FLAGS_FIXED_SIZE_OBJECTS,
MLX5HWS_POOL_FLAGS_FOR_MATCHER_STE_POOL =
MLX5HWS_POOL_FLAGS_RELEASE_FREE_RESOURCE |
MLX5HWS_POOL_FLAGS_RESOURCE_PER_CHUNK,
MLX5HWS_POOL_FLAGS_FOR_STE_ACTION_POOL =
MLX5HWS_POOL_FLAGS_ONE_RESOURCE |
MLX5HWS_POOL_FLAGS_BUDDY_MANAGED |
MLX5HWS_POOL_FLAGS_ALLOC_MEM_ON_CREATE,
};
enum mlx5hws_pool_optimize {
MLX5HWS_POOL_OPTIMIZE_NONE = 0x0,
MLX5HWS_POOL_OPTIMIZE_ORIG = 0x1,
MLX5HWS_POOL_OPTIMIZE_MIRROR = 0x2,
};
struct mlx5hws_pool_attr {
enum mlx5hws_pool_type pool_type;
enum mlx5hws_table_type table_type;
enum mlx5hws_pool_flags flags;
enum mlx5hws_pool_optimize opt_type;
/* Allocation size once memory is depleted */
size_t alloc_log_sz;
};
enum mlx5hws_db_type {
/* Uses for allocating chunk of big memory, each element has its own resource in the FW*/
MLX5HWS_POOL_DB_TYPE_GENERAL_SIZE,
/* One resource only, all the elements are with same one size */
MLX5HWS_POOL_DB_TYPE_ONE_SIZE_RESOURCE,
/* Many resources, the memory allocated with buddy mechanism */
MLX5HWS_POOL_DB_TYPE_BUDDY,
};
struct mlx5hws_buddy_manager {
struct mlx5hws_buddy_mem *buddies[MLX5HWS_POOL_RESOURCE_ARR_SZ];
};
struct mlx5hws_pool_elements {
u32 num_of_elements;
unsigned long *bitmap;
u32 log_size;
bool is_full;
};
struct mlx5hws_element_manager {
struct mlx5hws_pool_elements *elements[MLX5HWS_POOL_RESOURCE_ARR_SZ];
};
struct mlx5hws_pool_db {
enum mlx5hws_db_type type;
union {
struct mlx5hws_element_manager *element_manager;
struct mlx5hws_buddy_manager *buddy_manager;
};
};
typedef int (*mlx5hws_pool_db_get_chunk)(struct mlx5hws_pool *pool,
struct mlx5hws_pool_chunk *chunk);
typedef void (*mlx5hws_pool_db_put_chunk)(struct mlx5hws_pool *pool,
struct mlx5hws_pool_chunk *chunk);
typedef void (*mlx5hws_pool_unint_db)(struct mlx5hws_pool *pool);
struct mlx5hws_pool {
struct mlx5hws_context *ctx;
enum mlx5hws_pool_type type;
enum mlx5hws_pool_flags flags;
struct mutex lock; /* protect the pool */
size_t alloc_log_sz;
enum mlx5hws_table_type tbl_type;
enum mlx5hws_pool_optimize opt_type;
struct mlx5hws_pool_resource *resource[MLX5HWS_POOL_RESOURCE_ARR_SZ];
struct mlx5hws_pool_resource *mirror_resource[MLX5HWS_POOL_RESOURCE_ARR_SZ];
/* DB */
struct mlx5hws_pool_db db;
/* Functions */
mlx5hws_pool_unint_db p_db_uninit;
mlx5hws_pool_db_get_chunk p_get_chunk;
mlx5hws_pool_db_put_chunk p_put_chunk;
};
struct mlx5hws_pool *
mlx5hws_pool_create(struct mlx5hws_context *ctx,
struct mlx5hws_pool_attr *pool_attr);
int mlx5hws_pool_destroy(struct mlx5hws_pool *pool);
int mlx5hws_pool_chunk_alloc(struct mlx5hws_pool *pool,
struct mlx5hws_pool_chunk *chunk);
void mlx5hws_pool_chunk_free(struct mlx5hws_pool *pool,
struct mlx5hws_pool_chunk *chunk);
static inline u32
mlx5hws_pool_chunk_get_base_id(struct mlx5hws_pool *pool,
struct mlx5hws_pool_chunk *chunk)
{
return pool->resource[chunk->resource_idx]->base_id;
}
static inline u32
mlx5hws_pool_chunk_get_base_mirror_id(struct mlx5hws_pool *pool,
struct mlx5hws_pool_chunk *chunk)
{
return pool->mirror_resource[chunk->resource_idx]->base_id;
}
#endif /* MLX5HWS_POOL_H_ */

View File

@ -0,0 +1,514 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#ifndef MLX5_PRM_H_
#define MLX5_PRM_H_
#define MLX5_MAX_ACTIONS_DATA_IN_HEADER_MODIFY 512
/* Action type of header modification. */
enum {
MLX5_MODIFICATION_TYPE_SET = 0x1,
MLX5_MODIFICATION_TYPE_ADD = 0x2,
MLX5_MODIFICATION_TYPE_COPY = 0x3,
MLX5_MODIFICATION_TYPE_INSERT = 0x4,
MLX5_MODIFICATION_TYPE_REMOVE = 0x5,
MLX5_MODIFICATION_TYPE_NOP = 0x6,
MLX5_MODIFICATION_TYPE_REMOVE_WORDS = 0x7,
MLX5_MODIFICATION_TYPE_ADD_FIELD = 0x8,
MLX5_MODIFICATION_TYPE_MAX,
};
/* The field of packet to be modified. */
enum mlx5_modification_field {
MLX5_MODI_OUT_NONE = -1,
MLX5_MODI_OUT_SMAC_47_16 = 1,
MLX5_MODI_OUT_SMAC_15_0,
MLX5_MODI_OUT_ETHERTYPE,
MLX5_MODI_OUT_DMAC_47_16,
MLX5_MODI_OUT_DMAC_15_0,
MLX5_MODI_OUT_IP_DSCP,
MLX5_MODI_OUT_TCP_FLAGS,
MLX5_MODI_OUT_TCP_SPORT,
MLX5_MODI_OUT_TCP_DPORT,
MLX5_MODI_OUT_IPV4_TTL,
MLX5_MODI_OUT_UDP_SPORT,
MLX5_MODI_OUT_UDP_DPORT,
MLX5_MODI_OUT_SIPV6_127_96,
MLX5_MODI_OUT_SIPV6_95_64,
MLX5_MODI_OUT_SIPV6_63_32,
MLX5_MODI_OUT_SIPV6_31_0,
MLX5_MODI_OUT_DIPV6_127_96,
MLX5_MODI_OUT_DIPV6_95_64,
MLX5_MODI_OUT_DIPV6_63_32,
MLX5_MODI_OUT_DIPV6_31_0,
MLX5_MODI_OUT_SIPV4,
MLX5_MODI_OUT_DIPV4,
MLX5_MODI_OUT_FIRST_VID,
MLX5_MODI_IN_SMAC_47_16 = 0x31,
MLX5_MODI_IN_SMAC_15_0,
MLX5_MODI_IN_ETHERTYPE,
MLX5_MODI_IN_DMAC_47_16,
MLX5_MODI_IN_DMAC_15_0,
MLX5_MODI_IN_IP_DSCP,
MLX5_MODI_IN_TCP_FLAGS,
MLX5_MODI_IN_TCP_SPORT,
MLX5_MODI_IN_TCP_DPORT,
MLX5_MODI_IN_IPV4_TTL,
MLX5_MODI_IN_UDP_SPORT,
MLX5_MODI_IN_UDP_DPORT,
MLX5_MODI_IN_SIPV6_127_96,
MLX5_MODI_IN_SIPV6_95_64,
MLX5_MODI_IN_SIPV6_63_32,
MLX5_MODI_IN_SIPV6_31_0,
MLX5_MODI_IN_DIPV6_127_96,
MLX5_MODI_IN_DIPV6_95_64,
MLX5_MODI_IN_DIPV6_63_32,
MLX5_MODI_IN_DIPV6_31_0,
MLX5_MODI_IN_SIPV4,
MLX5_MODI_IN_DIPV4,
MLX5_MODI_OUT_IPV6_HOPLIMIT,
MLX5_MODI_IN_IPV6_HOPLIMIT,
MLX5_MODI_META_DATA_REG_A,
MLX5_MODI_META_DATA_REG_B = 0x50,
MLX5_MODI_META_REG_C_0,
MLX5_MODI_META_REG_C_1,
MLX5_MODI_META_REG_C_2,
MLX5_MODI_META_REG_C_3,
MLX5_MODI_META_REG_C_4,
MLX5_MODI_META_REG_C_5,
MLX5_MODI_META_REG_C_6,
MLX5_MODI_META_REG_C_7,
MLX5_MODI_OUT_TCP_SEQ_NUM,
MLX5_MODI_IN_TCP_SEQ_NUM,
MLX5_MODI_OUT_TCP_ACK_NUM,
MLX5_MODI_IN_TCP_ACK_NUM = 0x5C,
MLX5_MODI_GTP_TEID = 0x6E,
MLX5_MODI_OUT_IP_ECN = 0x73,
MLX5_MODI_TUNNEL_HDR_DW_1 = 0x75,
MLX5_MODI_GTPU_FIRST_EXT_DW_0 = 0x76,
MLX5_MODI_HASH_RESULT = 0x81,
MLX5_MODI_IN_MPLS_LABEL_0 = 0x8a,
MLX5_MODI_IN_MPLS_LABEL_1,
MLX5_MODI_IN_MPLS_LABEL_2,
MLX5_MODI_IN_MPLS_LABEL_3,
MLX5_MODI_IN_MPLS_LABEL_4,
MLX5_MODI_OUT_IP_PROTOCOL = 0x4A,
MLX5_MODI_OUT_IPV6_NEXT_HDR = 0x4A,
MLX5_MODI_META_REG_C_8 = 0x8F,
MLX5_MODI_META_REG_C_9 = 0x90,
MLX5_MODI_META_REG_C_10 = 0x91,
MLX5_MODI_META_REG_C_11 = 0x92,
MLX5_MODI_META_REG_C_12 = 0x93,
MLX5_MODI_META_REG_C_13 = 0x94,
MLX5_MODI_META_REG_C_14 = 0x95,
MLX5_MODI_META_REG_C_15 = 0x96,
MLX5_MODI_OUT_IPV4_TOTAL_LEN = 0x11D,
MLX5_MODI_OUT_IPV6_PAYLOAD_LEN = 0x11E,
MLX5_MODI_OUT_IPV4_IHL = 0x11F,
MLX5_MODI_OUT_TCP_DATA_OFFSET = 0x120,
MLX5_MODI_OUT_ESP_SPI = 0x5E,
MLX5_MODI_OUT_ESP_SEQ_NUM = 0x82,
MLX5_MODI_OUT_IPSEC_NEXT_HDR = 0x126,
MLX5_MODI_INVALID = INT_MAX,
};
enum {
MLX5_GET_HCA_CAP_OP_MOD_NIC_FLOW_TABLE = 0x7 << 1,
MLX5_GET_HCA_CAP_OP_MOD_ESW_FLOW_TABLE = 0x8 << 1,
MLX5_SET_HCA_CAP_OP_MOD_ESW = 0x9 << 1,
MLX5_GET_HCA_CAP_OP_MOD_WQE_BASED_FLOW_TABLE = 0x1B << 1,
MLX5_GET_HCA_CAP_OP_MOD_GENERAL_DEVICE_2 = 0x20 << 1,
};
enum mlx5_ifc_rtc_update_mode {
MLX5_IFC_RTC_STE_UPDATE_MODE_BY_HASH = 0x0,
MLX5_IFC_RTC_STE_UPDATE_MODE_BY_OFFSET = 0x1,
};
enum mlx5_ifc_rtc_access_mode {
MLX5_IFC_RTC_STE_ACCESS_MODE_BY_HASH = 0x0,
MLX5_IFC_RTC_STE_ACCESS_MODE_LINEAR = 0x1,
};
enum mlx5_ifc_rtc_ste_format {
MLX5_IFC_RTC_STE_FORMAT_8DW = 0x4,
MLX5_IFC_RTC_STE_FORMAT_11DW = 0x5,
MLX5_IFC_RTC_STE_FORMAT_RANGE = 0x7,
};
enum mlx5_ifc_rtc_reparse_mode {
MLX5_IFC_RTC_REPARSE_NEVER = 0x0,
MLX5_IFC_RTC_REPARSE_ALWAYS = 0x1,
MLX5_IFC_RTC_REPARSE_BY_STC = 0x2,
};
#define MLX5_IFC_RTC_LINEAR_LOOKUP_TBL_LOG_MAX 16
struct mlx5_ifc_rtc_bits {
u8 modify_field_select[0x40];
u8 reserved_at_40[0x40];
u8 update_index_mode[0x2];
u8 reparse_mode[0x2];
u8 num_match_ste[0x4];
u8 pd[0x18];
u8 reserved_at_a0[0x9];
u8 access_index_mode[0x3];
u8 num_hash_definer[0x4];
u8 update_method[0x1];
u8 reserved_at_b1[0x2];
u8 log_depth[0x5];
u8 log_hash_size[0x8];
u8 ste_format_0[0x8];
u8 table_type[0x8];
u8 ste_format_1[0x8];
u8 reserved_at_d8[0x8];
u8 match_definer_0[0x20];
u8 stc_id[0x20];
u8 ste_table_base_id[0x20];
u8 ste_table_offset[0x20];
u8 reserved_at_160[0x8];
u8 miss_flow_table_id[0x18];
u8 match_definer_1[0x20];
u8 reserved_at_1a0[0x260];
};
enum mlx5_ifc_stc_action_type {
MLX5_IFC_STC_ACTION_TYPE_NOP = 0x00,
MLX5_IFC_STC_ACTION_TYPE_COPY = 0x05,
MLX5_IFC_STC_ACTION_TYPE_SET = 0x06,
MLX5_IFC_STC_ACTION_TYPE_ADD = 0x07,
MLX5_IFC_STC_ACTION_TYPE_REMOVE_WORDS = 0x08,
MLX5_IFC_STC_ACTION_TYPE_HEADER_REMOVE = 0x09,
MLX5_IFC_STC_ACTION_TYPE_HEADER_INSERT = 0x0b,
MLX5_IFC_STC_ACTION_TYPE_TAG = 0x0c,
MLX5_IFC_STC_ACTION_TYPE_ACC_MODIFY_LIST = 0x0e,
MLX5_IFC_STC_ACTION_TYPE_CRYPTO_IPSEC_ENCRYPTION = 0x10,
MLX5_IFC_STC_ACTION_TYPE_CRYPTO_IPSEC_DECRYPTION = 0x11,
MLX5_IFC_STC_ACTION_TYPE_ASO = 0x12,
MLX5_IFC_STC_ACTION_TYPE_TRAILER = 0x13,
MLX5_IFC_STC_ACTION_TYPE_COUNTER = 0x14,
MLX5_IFC_STC_ACTION_TYPE_ADD_FIELD = 0x1b,
MLX5_IFC_STC_ACTION_TYPE_JUMP_TO_STE_TABLE = 0x80,
MLX5_IFC_STC_ACTION_TYPE_JUMP_TO_TIR = 0x81,
MLX5_IFC_STC_ACTION_TYPE_JUMP_TO_FT = 0x82,
MLX5_IFC_STC_ACTION_TYPE_DROP = 0x83,
MLX5_IFC_STC_ACTION_TYPE_ALLOW = 0x84,
MLX5_IFC_STC_ACTION_TYPE_JUMP_TO_VPORT = 0x85,
MLX5_IFC_STC_ACTION_TYPE_JUMP_TO_UPLINK = 0x86,
};
enum mlx5_ifc_stc_reparse_mode {
MLX5_IFC_STC_REPARSE_IGNORE = 0x0,
MLX5_IFC_STC_REPARSE_NEVER = 0x1,
MLX5_IFC_STC_REPARSE_ALWAYS = 0x2,
};
struct mlx5_ifc_stc_ste_param_ste_table_bits {
u8 ste_obj_id[0x20];
u8 match_definer_id[0x20];
u8 reserved_at_40[0x3];
u8 log_hash_size[0x5];
u8 reserved_at_48[0x38];
};
struct mlx5_ifc_stc_ste_param_tir_bits {
u8 reserved_at_0[0x8];
u8 tirn[0x18];
u8 reserved_at_20[0x60];
};
struct mlx5_ifc_stc_ste_param_table_bits {
u8 reserved_at_0[0x8];
u8 table_id[0x18];
u8 reserved_at_20[0x60];
};
struct mlx5_ifc_stc_ste_param_flow_counter_bits {
u8 flow_counter_id[0x20];
};
enum {
MLX5_ASO_CT_NUM_PER_OBJ = 1,
MLX5_ASO_METER_NUM_PER_OBJ = 2,
MLX5_ASO_IPSEC_NUM_PER_OBJ = 1,
MLX5_ASO_FIRST_HIT_NUM_PER_OBJ = 512,
};
struct mlx5_ifc_stc_ste_param_execute_aso_bits {
u8 aso_object_id[0x20];
u8 return_reg_id[0x4];
u8 aso_type[0x4];
u8 reserved_at_28[0x18];
};
struct mlx5_ifc_stc_ste_param_ipsec_encrypt_bits {
u8 ipsec_object_id[0x20];
};
struct mlx5_ifc_stc_ste_param_ipsec_decrypt_bits {
u8 ipsec_object_id[0x20];
};
struct mlx5_ifc_stc_ste_param_trailer_bits {
u8 reserved_at_0[0x8];
u8 command[0x4];
u8 reserved_at_c[0x2];
u8 type[0x2];
u8 reserved_at_10[0xa];
u8 length[0x6];
};
struct mlx5_ifc_stc_ste_param_header_modify_list_bits {
u8 header_modify_pattern_id[0x20];
u8 header_modify_argument_id[0x20];
};
enum mlx5_ifc_header_anchors {
MLX5_HEADER_ANCHOR_PACKET_START = 0x0,
MLX5_HEADER_ANCHOR_MAC = 0x1,
MLX5_HEADER_ANCHOR_FIRST_VLAN_START = 0x2,
MLX5_HEADER_ANCHOR_IPV6_IPV4 = 0x07,
MLX5_HEADER_ANCHOR_ESP = 0x08,
MLX5_HEADER_ANCHOR_TCP_UDP = 0x09,
MLX5_HEADER_ANCHOR_TUNNEL_HEADER = 0x0a,
MLX5_HEADER_ANCHOR_INNER_MAC = 0x13,
MLX5_HEADER_ANCHOR_INNER_IPV6_IPV4 = 0x19,
MLX5_HEADER_ANCHOR_INNER_TCP_UDP = 0x1a,
MLX5_HEADER_ANCHOR_L4_PAYLOAD = 0x1b,
MLX5_HEADER_ANCHOR_INNER_L4_PAYLOAD = 0x1c
};
struct mlx5_ifc_stc_ste_param_remove_bits {
u8 action_type[0x4];
u8 decap[0x1];
u8 reserved_at_5[0x5];
u8 remove_start_anchor[0x6];
u8 reserved_at_10[0x2];
u8 remove_end_anchor[0x6];
u8 reserved_at_18[0x8];
};
struct mlx5_ifc_stc_ste_param_remove_words_bits {
u8 action_type[0x4];
u8 reserved_at_4[0x6];
u8 remove_start_anchor[0x6];
u8 reserved_at_10[0x1];
u8 remove_offset[0x7];
u8 reserved_at_18[0x2];
u8 remove_size[0x6];
};
struct mlx5_ifc_stc_ste_param_insert_bits {
u8 action_type[0x4];
u8 encap[0x1];
u8 inline_data[0x1];
u8 reserved_at_6[0x4];
u8 insert_anchor[0x6];
u8 reserved_at_10[0x1];
u8 insert_offset[0x7];
u8 reserved_at_18[0x1];
u8 insert_size[0x7];
u8 insert_argument[0x20];
};
struct mlx5_ifc_stc_ste_param_vport_bits {
u8 eswitch_owner_vhca_id[0x10];
u8 vport_number[0x10];
u8 eswitch_owner_vhca_id_valid[0x1];
u8 reserved_at_21[0x5f];
};
union mlx5_ifc_stc_param_bits {
struct mlx5_ifc_stc_ste_param_ste_table_bits ste_table;
struct mlx5_ifc_stc_ste_param_tir_bits tir;
struct mlx5_ifc_stc_ste_param_table_bits table;
struct mlx5_ifc_stc_ste_param_flow_counter_bits counter;
struct mlx5_ifc_stc_ste_param_header_modify_list_bits modify_header;
struct mlx5_ifc_stc_ste_param_execute_aso_bits aso;
struct mlx5_ifc_stc_ste_param_remove_bits remove_header;
struct mlx5_ifc_stc_ste_param_insert_bits insert_header;
struct mlx5_ifc_set_action_in_bits add;
struct mlx5_ifc_set_action_in_bits set;
struct mlx5_ifc_copy_action_in_bits copy;
struct mlx5_ifc_stc_ste_param_vport_bits vport;
struct mlx5_ifc_stc_ste_param_ipsec_encrypt_bits ipsec_encrypt;
struct mlx5_ifc_stc_ste_param_ipsec_decrypt_bits ipsec_decrypt;
struct mlx5_ifc_stc_ste_param_trailer_bits trailer;
u8 reserved_at_0[0x80];
};
enum {
MLX5_IFC_MODIFY_STC_FIELD_SELECT_NEW_STC = BIT(0),
};
struct mlx5_ifc_stc_bits {
u8 modify_field_select[0x40];
u8 reserved_at_40[0x46];
u8 reparse_mode[0x2];
u8 table_type[0x8];
u8 ste_action_offset[0x8];
u8 action_type[0x8];
u8 reserved_at_a0[0x60];
union mlx5_ifc_stc_param_bits stc_param;
u8 reserved_at_180[0x280];
};
struct mlx5_ifc_ste_bits {
u8 modify_field_select[0x40];
u8 reserved_at_40[0x48];
u8 table_type[0x8];
u8 reserved_at_90[0x370];
};
struct mlx5_ifc_definer_bits {
u8 modify_field_select[0x40];
u8 reserved_at_40[0x50];
u8 format_id[0x10];
u8 reserved_at_60[0x60];
u8 format_select_dw3[0x8];
u8 format_select_dw2[0x8];
u8 format_select_dw1[0x8];
u8 format_select_dw0[0x8];
u8 format_select_dw7[0x8];
u8 format_select_dw6[0x8];
u8 format_select_dw5[0x8];
u8 format_select_dw4[0x8];
u8 reserved_at_100[0x18];
u8 format_select_dw8[0x8];
u8 reserved_at_120[0x20];
u8 format_select_byte3[0x8];
u8 format_select_byte2[0x8];
u8 format_select_byte1[0x8];
u8 format_select_byte0[0x8];
u8 format_select_byte7[0x8];
u8 format_select_byte6[0x8];
u8 format_select_byte5[0x8];
u8 format_select_byte4[0x8];
u8 reserved_at_180[0x40];
u8 ctrl[0xa0];
u8 match_mask[0x160];
};
struct mlx5_ifc_arg_bits {
u8 rsvd0[0x88];
u8 access_pd[0x18];
};
struct mlx5_ifc_header_modify_pattern_in_bits {
u8 modify_field_select[0x40];
u8 reserved_at_40[0x40];
u8 pattern_length[0x8];
u8 reserved_at_88[0x18];
u8 reserved_at_a0[0x60];
u8 pattern_data[MLX5_MAX_ACTIONS_DATA_IN_HEADER_MODIFY * 8];
};
struct mlx5_ifc_create_rtc_in_bits {
struct mlx5_ifc_general_obj_in_cmd_hdr_bits hdr;
struct mlx5_ifc_rtc_bits rtc;
};
struct mlx5_ifc_create_stc_in_bits {
struct mlx5_ifc_general_obj_in_cmd_hdr_bits hdr;
struct mlx5_ifc_stc_bits stc;
};
struct mlx5_ifc_create_ste_in_bits {
struct mlx5_ifc_general_obj_in_cmd_hdr_bits hdr;
struct mlx5_ifc_ste_bits ste;
};
struct mlx5_ifc_create_definer_in_bits {
struct mlx5_ifc_general_obj_in_cmd_hdr_bits hdr;
struct mlx5_ifc_definer_bits definer;
};
struct mlx5_ifc_create_arg_in_bits {
struct mlx5_ifc_general_obj_in_cmd_hdr_bits hdr;
struct mlx5_ifc_arg_bits arg;
};
struct mlx5_ifc_create_header_modify_pattern_in_bits {
struct mlx5_ifc_general_obj_in_cmd_hdr_bits hdr;
struct mlx5_ifc_header_modify_pattern_in_bits pattern;
};
struct mlx5_ifc_generate_wqe_in_bits {
u8 opcode[0x10];
u8 uid[0x10];
u8 reserved_at_20[0x10];
u8 op_mode[0x10];
u8 reserved_at_40[0x40];
u8 reserved_at_80[0x8];
u8 pdn[0x18];
u8 reserved_at_a0[0x160];
u8 wqe_ctrl[0x80];
u8 wqe_gta_ctrl[0x180];
u8 wqe_gta_data_0[0x200];
u8 wqe_gta_data_1[0x200];
};
struct mlx5_ifc_generate_wqe_out_bits {
u8 status[0x8];
u8 reserved_at_8[0x18];
u8 syndrome[0x20];
u8 reserved_at_40[0x1c0];
u8 cqe_data[0x200];
};
enum mlx5_access_aso_opc_mod {
ASO_OPC_MOD_IPSEC = 0x0,
ASO_OPC_MOD_CONNECTION_TRACKING = 0x1,
ASO_OPC_MOD_POLICER = 0x2,
ASO_OPC_MOD_RACE_AVOIDANCE = 0x3,
ASO_OPC_MOD_FLOW_HIT = 0x4,
};
enum {
MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION = BIT(0),
MLX5_IFC_MODIFY_FLOW_TABLE_RTC_ID = BIT(1),
};
enum {
MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION_DEFAULT = 0,
MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION_GOTO_TBL = 1,
};
struct mlx5_ifc_alloc_packet_reformat_out_bits {
u8 status[0x8];
u8 reserved_at_8[0x18];
u8 syndrome[0x20];
u8 packet_reformat_id[0x20];
u8 reserved_at_60[0x20];
};
struct mlx5_ifc_dealloc_packet_reformat_in_bits {
u8 opcode[0x10];
u8 reserved_at_10[0x10];
u8 reserved_at_20[0x10];
u8 op_mod[0x10];
u8 packet_reformat_id[0x20];
u8 reserved_at_60[0x20];
};
struct mlx5_ifc_dealloc_packet_reformat_out_bits {
u8 status[0x8];
u8 reserved_at_8[0x18];
u8 syndrome[0x20];
u8 reserved_at_40[0x40];
};
#endif /* MLX5_PRM_H_ */

View File

@ -0,0 +1,780 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#include "mlx5hws_internal.h"
static void hws_rule_skip(struct mlx5hws_matcher *matcher,
struct mlx5hws_match_template *mt,
u32 flow_source,
bool *skip_rx, bool *skip_tx)
{
/* By default FDB rules are added to both RX and TX */
*skip_rx = false;
*skip_tx = false;
if (flow_source == MLX5_FLOW_CONTEXT_FLOW_SOURCE_LOCAL_VPORT) {
*skip_rx = true;
} else if (flow_source == MLX5_FLOW_CONTEXT_FLOW_SOURCE_UPLINK) {
*skip_tx = true;
} else {
/* If no flow source was set for current rule,
* check for flow source in matcher attributes.
*/
if (matcher->attr.optimize_flow_src) {
*skip_tx =
matcher->attr.optimize_flow_src == MLX5HWS_MATCHER_FLOW_SRC_WIRE;
*skip_rx =
matcher->attr.optimize_flow_src == MLX5HWS_MATCHER_FLOW_SRC_VPORT;
return;
}
}
}
static void
hws_rule_update_copy_tag(struct mlx5hws_rule *rule,
struct mlx5hws_wqe_gta_data_seg_ste *wqe_data,
bool is_jumbo)
{
struct mlx5hws_rule_match_tag *tag;
if (!mlx5hws_matcher_is_resizable(rule->matcher)) {
tag = &rule->tag;
} else {
struct mlx5hws_wqe_gta_data_seg_ste *data_seg =
(struct mlx5hws_wqe_gta_data_seg_ste *)(void *)rule->resize_info->data_seg;
tag = (struct mlx5hws_rule_match_tag *)(void *)data_seg->action;
}
if (is_jumbo)
memcpy(wqe_data->jumbo, tag->jumbo, MLX5HWS_JUMBO_TAG_SZ);
else
memcpy(wqe_data->tag, tag->match, MLX5HWS_MATCH_TAG_SZ);
}
static void hws_rule_init_dep_wqe(struct mlx5hws_send_ring_dep_wqe *dep_wqe,
struct mlx5hws_rule *rule,
struct mlx5hws_match_template *mt,
struct mlx5hws_rule_attr *attr)
{
struct mlx5hws_matcher *matcher = rule->matcher;
struct mlx5hws_table *tbl = matcher->tbl;
bool skip_rx, skip_tx;
dep_wqe->rule = rule;
dep_wqe->user_data = attr->user_data;
dep_wqe->direct_index = mlx5hws_matcher_is_insert_by_idx(matcher) ?
attr->rule_idx : 0;
if (tbl->type == MLX5HWS_TABLE_TYPE_FDB) {
hws_rule_skip(matcher, mt, attr->flow_source, &skip_rx, &skip_tx);
if (!skip_rx) {
dep_wqe->rtc_0 = matcher->match_ste.rtc_0_id;
dep_wqe->retry_rtc_0 = matcher->col_matcher ?
matcher->col_matcher->match_ste.rtc_0_id : 0;
} else {
dep_wqe->rtc_0 = 0;
dep_wqe->retry_rtc_0 = 0;
}
if (!skip_tx) {
dep_wqe->rtc_1 = matcher->match_ste.rtc_1_id;
dep_wqe->retry_rtc_1 = matcher->col_matcher ?
matcher->col_matcher->match_ste.rtc_1_id : 0;
} else {
dep_wqe->rtc_1 = 0;
dep_wqe->retry_rtc_1 = 0;
}
} else {
pr_warn("HWS: invalid tbl->type: %d\n", tbl->type);
}
}
static void hws_rule_move_get_rtc(struct mlx5hws_rule *rule,
struct mlx5hws_send_ste_attr *ste_attr)
{
struct mlx5hws_matcher *dst_matcher = rule->matcher->resize_dst;
if (rule->resize_info->rtc_0) {
ste_attr->rtc_0 = dst_matcher->match_ste.rtc_0_id;
ste_attr->retry_rtc_0 = dst_matcher->col_matcher ?
dst_matcher->col_matcher->match_ste.rtc_0_id : 0;
}
if (rule->resize_info->rtc_1) {
ste_attr->rtc_1 = dst_matcher->match_ste.rtc_1_id;
ste_attr->retry_rtc_1 = dst_matcher->col_matcher ?
dst_matcher->col_matcher->match_ste.rtc_1_id : 0;
}
}
static void hws_rule_gen_comp(struct mlx5hws_send_engine *queue,
struct mlx5hws_rule *rule,
bool err,
void *user_data,
enum mlx5hws_rule_status rule_status_on_succ)
{
enum mlx5hws_flow_op_status comp_status;
if (!err) {
comp_status = MLX5HWS_FLOW_OP_SUCCESS;
rule->status = rule_status_on_succ;
} else {
comp_status = MLX5HWS_FLOW_OP_ERROR;
rule->status = MLX5HWS_RULE_STATUS_FAILED;
}
mlx5hws_send_engine_inc_rule(queue);
mlx5hws_send_engine_gen_comp(queue, user_data, comp_status);
}
static void
hws_rule_save_resize_info(struct mlx5hws_rule *rule,
struct mlx5hws_send_ste_attr *ste_attr,
bool is_update)
{
if (!mlx5hws_matcher_is_resizable(rule->matcher))
return;
if (likely(!is_update)) {
rule->resize_info = kzalloc(sizeof(*rule->resize_info), GFP_KERNEL);
if (unlikely(!rule->resize_info)) {
pr_warn("HWS: resize info isn't allocated for rule\n");
return;
}
rule->resize_info->max_stes =
rule->matcher->action_ste[MLX5HWS_ACTION_STE_IDX_ANY].max_stes;
rule->resize_info->action_ste_pool[0] = rule->matcher->action_ste[0].max_stes ?
rule->matcher->action_ste[0].pool :
NULL;
rule->resize_info->action_ste_pool[1] = rule->matcher->action_ste[1].max_stes ?
rule->matcher->action_ste[1].pool :
NULL;
}
memcpy(rule->resize_info->ctrl_seg, ste_attr->wqe_ctrl,
sizeof(rule->resize_info->ctrl_seg));
memcpy(rule->resize_info->data_seg, ste_attr->wqe_data,
sizeof(rule->resize_info->data_seg));
}
void mlx5hws_rule_clear_resize_info(struct mlx5hws_rule *rule)
{
if (mlx5hws_matcher_is_resizable(rule->matcher) &&
rule->resize_info) {
kfree(rule->resize_info);
rule->resize_info = NULL;
}
}
static void
hws_rule_save_delete_info(struct mlx5hws_rule *rule,
struct mlx5hws_send_ste_attr *ste_attr)
{
struct mlx5hws_match_template *mt = rule->matcher->mt;
bool is_jumbo = mlx5hws_matcher_mt_is_jumbo(mt);
if (mlx5hws_matcher_is_resizable(rule->matcher))
return;
if (is_jumbo)
memcpy(&rule->tag.jumbo, ste_attr->wqe_data->jumbo, MLX5HWS_JUMBO_TAG_SZ);
else
memcpy(&rule->tag.match, ste_attr->wqe_data->tag, MLX5HWS_MATCH_TAG_SZ);
}
static void
hws_rule_clear_delete_info(struct mlx5hws_rule *rule)
{
/* nothing to do here */
}
static void
hws_rule_load_delete_info(struct mlx5hws_rule *rule,
struct mlx5hws_send_ste_attr *ste_attr)
{
if (unlikely(!mlx5hws_matcher_is_resizable(rule->matcher))) {
ste_attr->wqe_tag = &rule->tag;
} else {
struct mlx5hws_wqe_gta_data_seg_ste *data_seg =
(struct mlx5hws_wqe_gta_data_seg_ste *)(void *)rule->resize_info->data_seg;
struct mlx5hws_rule_match_tag *tag =
(struct mlx5hws_rule_match_tag *)(void *)data_seg->action;
ste_attr->wqe_tag = tag;
}
}
static int hws_rule_alloc_action_ste_idx(struct mlx5hws_rule *rule,
u8 action_ste_selector)
{
struct mlx5hws_matcher *matcher = rule->matcher;
struct mlx5hws_matcher_action_ste *action_ste;
struct mlx5hws_pool_chunk ste = {0};
int ret;
action_ste = &matcher->action_ste[action_ste_selector];
ste.order = ilog2(roundup_pow_of_two(action_ste->max_stes));
ret = mlx5hws_pool_chunk_alloc(action_ste->pool, &ste);
if (unlikely(ret)) {
mlx5hws_err(matcher->tbl->ctx,
"Failed to allocate STE for rule actions");
return ret;
}
rule->action_ste_idx = ste.offset;
return 0;
}
static void hws_rule_free_action_ste_idx(struct mlx5hws_rule *rule,
u8 action_ste_selector)
{
struct mlx5hws_matcher *matcher = rule->matcher;
struct mlx5hws_pool_chunk ste = {0};
struct mlx5hws_pool *pool;
u8 max_stes;
if (mlx5hws_matcher_is_resizable(matcher)) {
/* Free the original action pool if rule was resized */
max_stes = rule->resize_info->max_stes;
pool = rule->resize_info->action_ste_pool[action_ste_selector];
} else {
max_stes = matcher->action_ste[action_ste_selector].max_stes;
pool = matcher->action_ste[action_ste_selector].pool;
}
/* This release is safe only when the rule match part was deleted */
ste.order = ilog2(roundup_pow_of_two(max_stes));
ste.offset = rule->action_ste_idx;
mlx5hws_pool_chunk_free(pool, &ste);
}
static int hws_rule_alloc_action_ste(struct mlx5hws_rule *rule,
struct mlx5hws_rule_attr *attr)
{
int action_ste_idx;
int ret;
ret = hws_rule_alloc_action_ste_idx(rule, 0);
if (unlikely(ret))
return ret;
action_ste_idx = rule->action_ste_idx;
ret = hws_rule_alloc_action_ste_idx(rule, 1);
if (unlikely(ret)) {
hws_rule_free_action_ste_idx(rule, 0);
return ret;
}
/* Both pools have to return the same index */
if (unlikely(rule->action_ste_idx != action_ste_idx)) {
pr_warn("HWS: allocation of action STE failed - pool indexes mismatch\n");
return -EINVAL;
}
return 0;
}
void mlx5hws_rule_free_action_ste(struct mlx5hws_rule *rule)
{
if (rule->action_ste_idx > -1) {
hws_rule_free_action_ste_idx(rule, 1);
hws_rule_free_action_ste_idx(rule, 0);
}
}
static void hws_rule_create_init(struct mlx5hws_rule *rule,
struct mlx5hws_send_ste_attr *ste_attr,
struct mlx5hws_actions_apply_data *apply,
bool is_update)
{
struct mlx5hws_matcher *matcher = rule->matcher;
struct mlx5hws_table *tbl = matcher->tbl;
struct mlx5hws_context *ctx = tbl->ctx;
/* Init rule before reuse */
if (!is_update) {
/* In update we use these rtc's */
rule->rtc_0 = 0;
rule->rtc_1 = 0;
rule->action_ste_selector = 0;
} else {
rule->action_ste_selector = !rule->action_ste_selector;
}
rule->pending_wqes = 0;
rule->action_ste_idx = -1;
rule->status = MLX5HWS_RULE_STATUS_CREATING;
/* Init default send STE attributes */
ste_attr->gta_opcode = MLX5HWS_WQE_GTA_OP_ACTIVATE;
ste_attr->send_attr.opmod = MLX5HWS_WQE_GTA_OPMOD_STE;
ste_attr->send_attr.opcode = MLX5HWS_WQE_OPCODE_TBL_ACCESS;
ste_attr->send_attr.len = MLX5HWS_WQE_SZ_GTA_CTRL + MLX5HWS_WQE_SZ_GTA_DATA;
/* Init default action apply */
apply->tbl_type = tbl->type;
apply->common_res = &ctx->common_res[tbl->type];
apply->jump_to_action_stc = matcher->action_ste[0].stc.offset;
apply->require_dep = 0;
}
static void hws_rule_move_init(struct mlx5hws_rule *rule,
struct mlx5hws_rule_attr *attr)
{
/* Save the old RTC IDs to be later used in match STE delete */
rule->resize_info->rtc_0 = rule->rtc_0;
rule->resize_info->rtc_1 = rule->rtc_1;
rule->resize_info->rule_idx = attr->rule_idx;
rule->rtc_0 = 0;
rule->rtc_1 = 0;
rule->pending_wqes = 0;
rule->action_ste_idx = -1;
rule->action_ste_selector = 0;
rule->status = MLX5HWS_RULE_STATUS_CREATING;
rule->resize_info->state = MLX5HWS_RULE_RESIZE_STATE_WRITING;
}
bool mlx5hws_rule_move_in_progress(struct mlx5hws_rule *rule)
{
return mlx5hws_matcher_is_in_resize(rule->matcher) &&
rule->resize_info &&
rule->resize_info->state != MLX5HWS_RULE_RESIZE_STATE_IDLE;
}
static int hws_rule_create_hws(struct mlx5hws_rule *rule,
struct mlx5hws_rule_attr *attr,
u8 mt_idx,
u32 *match_param,
u8 at_idx,
struct mlx5hws_rule_action rule_actions[])
{
struct mlx5hws_action_template *at = &rule->matcher->at[at_idx];
struct mlx5hws_match_template *mt = &rule->matcher->mt[mt_idx];
bool is_jumbo = mlx5hws_matcher_mt_is_jumbo(mt);
struct mlx5hws_matcher *matcher = rule->matcher;
struct mlx5hws_context *ctx = matcher->tbl->ctx;
struct mlx5hws_send_ste_attr ste_attr = {0};
struct mlx5hws_send_ring_dep_wqe *dep_wqe;
struct mlx5hws_actions_wqe_setter *setter;
struct mlx5hws_actions_apply_data apply;
struct mlx5hws_send_engine *queue;
u8 total_stes, action_stes;
bool is_update;
int i, ret;
is_update = !match_param;
setter = &at->setters[at->num_of_action_stes];
total_stes = at->num_of_action_stes + (is_jumbo && !at->only_term);
action_stes = total_stes - 1;
queue = &ctx->send_queue[attr->queue_id];
if (unlikely(mlx5hws_send_engine_err(queue)))
return -EIO;
hws_rule_create_init(rule, &ste_attr, &apply, is_update);
/* Allocate dependent match WQE since rule might have dependent writes.
* The queued dependent WQE can be later aborted or kept as a dependency.
* dep_wqe buffers (ctrl, data) are also reused for all STE writes.
*/
dep_wqe = mlx5hws_send_add_new_dep_wqe(queue);
hws_rule_init_dep_wqe(dep_wqe, rule, mt, attr);
ste_attr.wqe_ctrl = &dep_wqe->wqe_ctrl;
ste_attr.wqe_data = &dep_wqe->wqe_data;
apply.wqe_ctrl = &dep_wqe->wqe_ctrl;
apply.wqe_data = (__force __be32 *)&dep_wqe->wqe_data;
apply.rule_action = rule_actions;
apply.queue = queue;
if (action_stes) {
/* Allocate action STEs for rules that need more than match STE */
if (!is_update) {
ret = hws_rule_alloc_action_ste(rule, attr);
if (ret) {
mlx5hws_err(ctx, "Failed to allocate action memory %d", ret);
mlx5hws_send_abort_new_dep_wqe(queue);
return ret;
}
}
/* Skip RX/TX based on the dep_wqe init */
ste_attr.rtc_0 = dep_wqe->rtc_0 ?
matcher->action_ste[rule->action_ste_selector].rtc_0_id : 0;
ste_attr.rtc_1 = dep_wqe->rtc_1 ?
matcher->action_ste[rule->action_ste_selector].rtc_1_id : 0;
/* Action STEs are written to a specific index last to first */
ste_attr.direct_index = rule->action_ste_idx + action_stes;
apply.next_direct_idx = ste_attr.direct_index;
} else {
apply.next_direct_idx = 0;
}
for (i = total_stes; i-- > 0;) {
mlx5hws_action_apply_setter(&apply, setter--, !i && is_jumbo);
if (i == 0) {
/* Handle last match STE.
* For hash split / linear lookup RTCs, packets reaching any STE
* will always match and perform the specified actions, which
* makes the tag irrelevant.
*/
if (likely(!mlx5hws_matcher_is_insert_by_idx(matcher) && !is_update))
mlx5hws_definer_create_tag(match_param, mt->fc, mt->fc_sz,
(u8 *)dep_wqe->wqe_data.action);
else if (is_update)
hws_rule_update_copy_tag(rule, &dep_wqe->wqe_data, is_jumbo);
/* Rule has dependent WQEs, match dep_wqe is queued */
if (action_stes || apply.require_dep)
break;
/* Rule has no dependencies, abort dep_wqe and send WQE now */
mlx5hws_send_abort_new_dep_wqe(queue);
ste_attr.wqe_tag_is_jumbo = is_jumbo;
ste_attr.send_attr.notify_hw = !attr->burst;
ste_attr.send_attr.user_data = dep_wqe->user_data;
ste_attr.send_attr.rule = dep_wqe->rule;
ste_attr.rtc_0 = dep_wqe->rtc_0;
ste_attr.rtc_1 = dep_wqe->rtc_1;
ste_attr.used_id_rtc_0 = &rule->rtc_0;
ste_attr.used_id_rtc_1 = &rule->rtc_1;
ste_attr.retry_rtc_0 = dep_wqe->retry_rtc_0;
ste_attr.retry_rtc_1 = dep_wqe->retry_rtc_1;
ste_attr.direct_index = dep_wqe->direct_index;
} else {
apply.next_direct_idx = --ste_attr.direct_index;
}
mlx5hws_send_ste(queue, &ste_attr);
}
/* Backup TAG on the rule for deletion and resize info for
* moving rules to a new matcher, only after insertion.
*/
if (!is_update)
hws_rule_save_delete_info(rule, &ste_attr);
hws_rule_save_resize_info(rule, &ste_attr, is_update);
mlx5hws_send_engine_inc_rule(queue);
if (!attr->burst)
mlx5hws_send_all_dep_wqe(queue);
return 0;
}
static void hws_rule_destroy_failed_hws(struct mlx5hws_rule *rule,
struct mlx5hws_rule_attr *attr)
{
struct mlx5hws_context *ctx = rule->matcher->tbl->ctx;
struct mlx5hws_send_engine *queue;
queue = &ctx->send_queue[attr->queue_id];
hws_rule_gen_comp(queue, rule, false,
attr->user_data, MLX5HWS_RULE_STATUS_DELETED);
/* Rule failed now we can safely release action STEs */
mlx5hws_rule_free_action_ste(rule);
/* Clear complex tag */
hws_rule_clear_delete_info(rule);
/* Clear info that was saved for resizing */
mlx5hws_rule_clear_resize_info(rule);
/* If a rule that was indicated as burst (need to trigger HW) has failed
* insertion we won't ring the HW as nothing is being written to the WQ.
* In such case update the last WQE and ring the HW with that work
*/
if (attr->burst)
return;
mlx5hws_send_all_dep_wqe(queue);
mlx5hws_send_engine_flush_queue(queue);
}
static int hws_rule_destroy_hws(struct mlx5hws_rule *rule,
struct mlx5hws_rule_attr *attr)
{
bool is_jumbo = mlx5hws_matcher_mt_is_jumbo(rule->matcher->mt);
struct mlx5hws_context *ctx = rule->matcher->tbl->ctx;
struct mlx5hws_matcher *matcher = rule->matcher;
struct mlx5hws_wqe_gta_ctrl_seg wqe_ctrl = {0};
struct mlx5hws_send_ste_attr ste_attr = {0};
struct mlx5hws_send_engine *queue;
queue = &ctx->send_queue[attr->queue_id];
if (unlikely(mlx5hws_send_engine_err(queue))) {
hws_rule_destroy_failed_hws(rule, attr);
return 0;
}
/* Rule is not completed yet */
if (rule->status == MLX5HWS_RULE_STATUS_CREATING)
return -EBUSY;
/* Rule failed and doesn't require cleanup */
if (rule->status == MLX5HWS_RULE_STATUS_FAILED) {
hws_rule_destroy_failed_hws(rule, attr);
return 0;
}
if (rule->skip_delete) {
/* Rule shouldn't be deleted in HW.
* Generate completion as if write succeeded, and we can
* safely release action STEs and clear resize info.
*/
hws_rule_gen_comp(queue, rule, false,
attr->user_data, MLX5HWS_RULE_STATUS_DELETED);
mlx5hws_rule_free_action_ste(rule);
mlx5hws_rule_clear_resize_info(rule);
return 0;
}
mlx5hws_send_engine_inc_rule(queue);
/* Send dependent WQE */
if (!attr->burst)
mlx5hws_send_all_dep_wqe(queue);
rule->status = MLX5HWS_RULE_STATUS_DELETING;
ste_attr.send_attr.opmod = MLX5HWS_WQE_GTA_OPMOD_STE;
ste_attr.send_attr.opcode = MLX5HWS_WQE_OPCODE_TBL_ACCESS;
ste_attr.send_attr.len = MLX5HWS_WQE_SZ_GTA_CTRL + MLX5HWS_WQE_SZ_GTA_DATA;
ste_attr.send_attr.rule = rule;
ste_attr.send_attr.notify_hw = !attr->burst;
ste_attr.send_attr.user_data = attr->user_data;
ste_attr.rtc_0 = rule->rtc_0;
ste_attr.rtc_1 = rule->rtc_1;
ste_attr.used_id_rtc_0 = &rule->rtc_0;
ste_attr.used_id_rtc_1 = &rule->rtc_1;
ste_attr.wqe_ctrl = &wqe_ctrl;
ste_attr.wqe_tag_is_jumbo = is_jumbo;
ste_attr.gta_opcode = MLX5HWS_WQE_GTA_OP_DEACTIVATE;
if (unlikely(mlx5hws_matcher_is_insert_by_idx(matcher)))
ste_attr.direct_index = attr->rule_idx;
hws_rule_load_delete_info(rule, &ste_attr);
mlx5hws_send_ste(queue, &ste_attr);
hws_rule_clear_delete_info(rule);
return 0;
}
static int hws_rule_enqueue_precheck(struct mlx5hws_rule *rule,
struct mlx5hws_rule_attr *attr)
{
struct mlx5hws_context *ctx = rule->matcher->tbl->ctx;
if (unlikely(!attr->user_data))
return -EINVAL;
/* Check if there is room in queue */
if (unlikely(mlx5hws_send_engine_full(&ctx->send_queue[attr->queue_id])))
return -EBUSY;
return 0;
}
static int hws_rule_enqueue_precheck_move(struct mlx5hws_rule *rule,
struct mlx5hws_rule_attr *attr)
{
if (unlikely(rule->status != MLX5HWS_RULE_STATUS_CREATED))
return -EINVAL;
return hws_rule_enqueue_precheck(rule, attr);
}
static int hws_rule_enqueue_precheck_create(struct mlx5hws_rule *rule,
struct mlx5hws_rule_attr *attr)
{
if (unlikely(mlx5hws_matcher_is_in_resize(rule->matcher)))
/* Matcher in resize - new rules are not allowed */
return -EAGAIN;
return hws_rule_enqueue_precheck(rule, attr);
}
static int hws_rule_enqueue_precheck_update(struct mlx5hws_rule *rule,
struct mlx5hws_rule_attr *attr)
{
struct mlx5hws_matcher *matcher = rule->matcher;
if (unlikely(!mlx5hws_matcher_is_resizable(rule->matcher) &&
!matcher->attr.optimize_using_rule_idx &&
!mlx5hws_matcher_is_insert_by_idx(matcher))) {
return -EOPNOTSUPP;
}
if (unlikely(rule->status != MLX5HWS_RULE_STATUS_CREATED))
return -EBUSY;
return hws_rule_enqueue_precheck_create(rule, attr);
}
int mlx5hws_rule_move_hws_remove(struct mlx5hws_rule *rule,
void *queue_ptr,
void *user_data)
{
bool is_jumbo = mlx5hws_matcher_mt_is_jumbo(rule->matcher->mt);
struct mlx5hws_wqe_gta_ctrl_seg empty_wqe_ctrl = {0};
struct mlx5hws_matcher *matcher = rule->matcher;
struct mlx5hws_send_engine *queue = queue_ptr;
struct mlx5hws_send_ste_attr ste_attr = {0};
mlx5hws_send_all_dep_wqe(queue);
rule->resize_info->state = MLX5HWS_RULE_RESIZE_STATE_DELETING;
ste_attr.send_attr.fence = 0;
ste_attr.send_attr.opmod = MLX5HWS_WQE_GTA_OPMOD_STE;
ste_attr.send_attr.opcode = MLX5HWS_WQE_OPCODE_TBL_ACCESS;
ste_attr.send_attr.len = MLX5HWS_WQE_SZ_GTA_CTRL + MLX5HWS_WQE_SZ_GTA_DATA;
ste_attr.send_attr.rule = rule;
ste_attr.send_attr.notify_hw = 1;
ste_attr.send_attr.user_data = user_data;
ste_attr.rtc_0 = rule->resize_info->rtc_0;
ste_attr.rtc_1 = rule->resize_info->rtc_1;
ste_attr.used_id_rtc_0 = &rule->resize_info->rtc_0;
ste_attr.used_id_rtc_1 = &rule->resize_info->rtc_1;
ste_attr.wqe_ctrl = &empty_wqe_ctrl;
ste_attr.wqe_tag_is_jumbo = is_jumbo;
ste_attr.gta_opcode = MLX5HWS_WQE_GTA_OP_DEACTIVATE;
if (unlikely(mlx5hws_matcher_is_insert_by_idx(matcher)))
ste_attr.direct_index = rule->resize_info->rule_idx;
hws_rule_load_delete_info(rule, &ste_attr);
mlx5hws_send_ste(queue, &ste_attr);
return 0;
}
int mlx5hws_rule_move_hws_add(struct mlx5hws_rule *rule,
struct mlx5hws_rule_attr *attr)
{
bool is_jumbo = mlx5hws_matcher_mt_is_jumbo(rule->matcher->mt);
struct mlx5hws_context *ctx = rule->matcher->tbl->ctx;
struct mlx5hws_matcher *matcher = rule->matcher;
struct mlx5hws_send_ste_attr ste_attr = {0};
struct mlx5hws_send_engine *queue;
int ret;
ret = hws_rule_enqueue_precheck_move(rule, attr);
if (unlikely(ret))
return ret;
queue = &ctx->send_queue[attr->queue_id];
ret = mlx5hws_send_engine_err(queue);
if (ret)
return ret;
hws_rule_move_init(rule, attr);
hws_rule_move_get_rtc(rule, &ste_attr);
ste_attr.send_attr.opmod = MLX5HWS_WQE_GTA_OPMOD_STE;
ste_attr.send_attr.opcode = MLX5HWS_WQE_OPCODE_TBL_ACCESS;
ste_attr.send_attr.len = MLX5HWS_WQE_SZ_GTA_CTRL + MLX5HWS_WQE_SZ_GTA_DATA;
ste_attr.gta_opcode = MLX5HWS_WQE_GTA_OP_ACTIVATE;
ste_attr.wqe_tag_is_jumbo = is_jumbo;
ste_attr.send_attr.rule = rule;
ste_attr.send_attr.fence = 0;
ste_attr.send_attr.notify_hw = !attr->burst;
ste_attr.send_attr.user_data = attr->user_data;
ste_attr.used_id_rtc_0 = &rule->rtc_0;
ste_attr.used_id_rtc_1 = &rule->rtc_1;
ste_attr.wqe_ctrl = (struct mlx5hws_wqe_gta_ctrl_seg *)rule->resize_info->ctrl_seg;
ste_attr.wqe_data = (struct mlx5hws_wqe_gta_data_seg_ste *)rule->resize_info->data_seg;
ste_attr.direct_index = mlx5hws_matcher_is_insert_by_idx(matcher) ?
attr->rule_idx : 0;
mlx5hws_send_ste(queue, &ste_attr);
mlx5hws_send_engine_inc_rule(queue);
if (!attr->burst)
mlx5hws_send_all_dep_wqe(queue);
return 0;
}
int mlx5hws_rule_create(struct mlx5hws_matcher *matcher,
u8 mt_idx,
u32 *match_param,
u8 at_idx,
struct mlx5hws_rule_action rule_actions[],
struct mlx5hws_rule_attr *attr,
struct mlx5hws_rule *rule_handle)
{
int ret;
rule_handle->matcher = matcher;
ret = hws_rule_enqueue_precheck_create(rule_handle, attr);
if (unlikely(ret))
return ret;
if (unlikely(!(matcher->num_of_mt >= mt_idx) ||
!(matcher->num_of_at >= at_idx) ||
!match_param)) {
pr_warn("HWS: Invalid rule creation parameters (MTs, ATs or match params)\n");
return -EINVAL;
}
ret = hws_rule_create_hws(rule_handle,
attr,
mt_idx,
match_param,
at_idx,
rule_actions);
return ret;
}
int mlx5hws_rule_destroy(struct mlx5hws_rule *rule,
struct mlx5hws_rule_attr *attr)
{
int ret;
ret = hws_rule_enqueue_precheck(rule, attr);
if (unlikely(ret))
return -ret;
ret = hws_rule_destroy_hws(rule, attr);
return -ret;
}
int mlx5hws_rule_action_update(struct mlx5hws_rule *rule,
u8 at_idx,
struct mlx5hws_rule_action rule_actions[],
struct mlx5hws_rule_attr *attr)
{
int ret;
ret = hws_rule_enqueue_precheck_update(rule, attr);
if (unlikely(ret))
return -ret;
ret = hws_rule_create_hws(rule,
attr,
0,
NULL,
at_idx,
rule_actions);
return -ret;
}

View File

@ -0,0 +1,84 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#ifndef MLX5HWS_RULE_H_
#define MLX5HWS_RULE_H_
enum {
MLX5HWS_STE_CTRL_SZ = 20,
MLX5HWS_ACTIONS_SZ = 12,
MLX5HWS_MATCH_TAG_SZ = 32,
MLX5HWS_JUMBO_TAG_SZ = 44,
};
enum mlx5hws_rule_status {
MLX5HWS_RULE_STATUS_UNKNOWN,
MLX5HWS_RULE_STATUS_CREATING,
MLX5HWS_RULE_STATUS_CREATED,
MLX5HWS_RULE_STATUS_DELETING,
MLX5HWS_RULE_STATUS_DELETED,
MLX5HWS_RULE_STATUS_FAILING,
MLX5HWS_RULE_STATUS_FAILED,
};
enum mlx5hws_rule_move_state {
MLX5HWS_RULE_RESIZE_STATE_IDLE,
MLX5HWS_RULE_RESIZE_STATE_WRITING,
MLX5HWS_RULE_RESIZE_STATE_DELETING,
};
enum mlx5hws_rule_jumbo_match_tag_offset {
MLX5HWS_RULE_JUMBO_MATCH_TAG_OFFSET_DW0 = 8,
};
struct mlx5hws_rule_match_tag {
union {
u8 jumbo[MLX5HWS_JUMBO_TAG_SZ];
struct {
u8 reserved[MLX5HWS_ACTIONS_SZ];
u8 match[MLX5HWS_MATCH_TAG_SZ];
};
};
};
struct mlx5hws_rule_resize_info {
struct mlx5hws_pool *action_ste_pool[2];
u32 rtc_0;
u32 rtc_1;
u32 rule_idx;
u8 state;
u8 max_stes;
u8 ctrl_seg[MLX5HWS_WQE_SZ_GTA_CTRL]; /* Ctrl segment of STE: 48 bytes */
u8 data_seg[MLX5HWS_WQE_SZ_GTA_DATA]; /* Data segment of STE: 64 bytes */
};
struct mlx5hws_rule {
struct mlx5hws_matcher *matcher;
union {
struct mlx5hws_rule_match_tag tag;
struct mlx5hws_rule_resize_info *resize_info;
};
u32 rtc_0; /* The RTC into which the STE was inserted */
u32 rtc_1; /* The RTC into which the STE was inserted */
int action_ste_idx; /* STE array index */
u8 status; /* enum mlx5hws_rule_status */
u8 action_ste_selector; /* For rule update - which action STE is in use */
u8 pending_wqes;
bool skip_delete; /* For complex rules - another rule with same tag
* still exists, so don't actually delete this rule.
*/
};
void mlx5hws_rule_free_action_ste(struct mlx5hws_rule *rule);
int mlx5hws_rule_move_hws_remove(struct mlx5hws_rule *rule,
void *queue, void *user_data);
int mlx5hws_rule_move_hws_add(struct mlx5hws_rule *rule,
struct mlx5hws_rule_attr *attr);
bool mlx5hws_rule_move_in_progress(struct mlx5hws_rule *rule);
void mlx5hws_rule_clear_resize_info(struct mlx5hws_rule *rule);
#endif /* MLX5HWS_RULE_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,270 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#ifndef MLX5HWS_SEND_H_
#define MLX5HWS_SEND_H_
/* As a single operation requires at least two WQEBBS.
* This means a maximum of 16 such operations per rule.
*/
#define MAX_WQES_PER_RULE 32
enum mlx5hws_wqe_opcode {
MLX5HWS_WQE_OPCODE_TBL_ACCESS = 0x2c,
};
enum mlx5hws_wqe_opmod {
MLX5HWS_WQE_OPMOD_GTA_STE = 0,
MLX5HWS_WQE_OPMOD_GTA_MOD_ARG = 1,
};
enum mlx5hws_wqe_gta_opcode {
MLX5HWS_WQE_GTA_OP_ACTIVATE = 0,
MLX5HWS_WQE_GTA_OP_DEACTIVATE = 1,
};
enum mlx5hws_wqe_gta_opmod {
MLX5HWS_WQE_GTA_OPMOD_STE = 0,
MLX5HWS_WQE_GTA_OPMOD_MOD_ARG = 1,
};
enum mlx5hws_wqe_gta_sz {
MLX5HWS_WQE_SZ_GTA_CTRL = 48,
MLX5HWS_WQE_SZ_GTA_DATA = 64,
};
/* WQE Control segment. */
struct mlx5hws_wqe_ctrl_seg {
__be32 opmod_idx_opcode;
__be32 qpn_ds;
__be32 flags;
__be32 imm;
};
struct mlx5hws_wqe_gta_ctrl_seg {
__be32 op_dirix;
__be32 stc_ix[5];
__be32 rsvd0[6];
};
struct mlx5hws_wqe_gta_data_seg_ste {
__be32 rsvd0_ctr_id;
__be32 rsvd1_definer;
__be32 rsvd2[3];
union {
struct {
__be32 action[3];
__be32 tag[8];
};
__be32 jumbo[11];
};
};
struct mlx5hws_wqe_gta_data_seg_arg {
__be32 action_args[8];
};
struct mlx5hws_wqe_gta {
struct mlx5hws_wqe_gta_ctrl_seg gta_ctrl;
union {
struct mlx5hws_wqe_gta_data_seg_ste seg_ste;
struct mlx5hws_wqe_gta_data_seg_arg seg_arg;
};
};
struct mlx5hws_send_ring_cq {
struct mlx5_core_dev *mdev;
struct mlx5_cqwq wq;
struct mlx5_wq_ctrl wq_ctrl;
struct mlx5_core_cq mcq;
u16 poll_wqe;
};
struct mlx5hws_send_ring_priv {
struct mlx5hws_rule *rule;
void *user_data;
u32 num_wqebbs;
u32 id;
u32 retry_id;
u32 *used_id;
};
struct mlx5hws_send_ring_dep_wqe {
struct mlx5hws_wqe_gta_ctrl_seg wqe_ctrl;
struct mlx5hws_wqe_gta_data_seg_ste wqe_data;
struct mlx5hws_rule *rule;
u32 rtc_0;
u32 rtc_1;
u32 retry_rtc_0;
u32 retry_rtc_1;
u32 direct_index;
void *user_data;
};
struct mlx5hws_send_ring_sq {
struct mlx5_core_dev *mdev;
u16 cur_post;
u16 buf_mask;
struct mlx5hws_send_ring_priv *wr_priv;
unsigned int last_idx;
struct mlx5hws_send_ring_dep_wqe *dep_wqe;
unsigned int head_dep_idx;
unsigned int tail_dep_idx;
u32 sqn;
struct mlx5_wq_cyc wq;
struct mlx5_wq_ctrl wq_ctrl;
void __iomem *uar_map;
};
struct mlx5hws_send_ring {
struct mlx5hws_send_ring_cq send_cq;
struct mlx5hws_send_ring_sq send_sq;
};
struct mlx5hws_completed_poll_entry {
void *user_data;
enum mlx5hws_flow_op_status status;
};
struct mlx5hws_completed_poll {
struct mlx5hws_completed_poll_entry *entries;
u16 ci;
u16 pi;
u16 mask;
};
struct mlx5hws_send_engine {
struct mlx5hws_send_ring send_ring;
struct mlx5_uars_page *uar; /* Uar is shared between rings of a queue */
struct mlx5hws_completed_poll completed;
u16 used_entries;
u16 num_entries;
bool err;
struct mutex lock; /* Protects the send engine */
};
struct mlx5hws_send_engine_post_ctrl {
struct mlx5hws_send_engine *queue;
struct mlx5hws_send_ring *send_ring;
size_t num_wqebbs;
};
struct mlx5hws_send_engine_post_attr {
u8 opcode;
u8 opmod;
u8 notify_hw;
u8 fence;
u8 match_definer_id;
u8 range_definer_id;
size_t len;
struct mlx5hws_rule *rule;
u32 id;
u32 retry_id;
u32 *used_id;
void *user_data;
};
struct mlx5hws_send_ste_attr {
u32 rtc_0;
u32 rtc_1;
u32 retry_rtc_0;
u32 retry_rtc_1;
u32 *used_id_rtc_0;
u32 *used_id_rtc_1;
bool wqe_tag_is_jumbo;
u8 gta_opcode;
u32 direct_index;
struct mlx5hws_send_engine_post_attr send_attr;
struct mlx5hws_rule_match_tag *wqe_tag;
struct mlx5hws_rule_match_tag *range_wqe_tag;
struct mlx5hws_wqe_gta_ctrl_seg *wqe_ctrl;
struct mlx5hws_wqe_gta_data_seg_ste *wqe_data;
struct mlx5hws_wqe_gta_data_seg_ste *range_wqe_data;
};
struct mlx5hws_send_ring_dep_wqe *
mlx5hws_send_add_new_dep_wqe(struct mlx5hws_send_engine *queue);
void mlx5hws_send_abort_new_dep_wqe(struct mlx5hws_send_engine *queue);
void mlx5hws_send_all_dep_wqe(struct mlx5hws_send_engine *queue);
void mlx5hws_send_queue_close(struct mlx5hws_send_engine *queue);
int mlx5hws_send_queue_open(struct mlx5hws_context *ctx,
struct mlx5hws_send_engine *queue,
u16 queue_size);
void mlx5hws_send_queues_close(struct mlx5hws_context *ctx);
int mlx5hws_send_queues_open(struct mlx5hws_context *ctx,
u16 queues,
u16 queue_size);
int mlx5hws_send_queue_action(struct mlx5hws_context *ctx,
u16 queue_id,
u32 actions);
int mlx5hws_send_test(struct mlx5hws_context *ctx,
u16 queues,
u16 queue_size);
struct mlx5hws_send_engine_post_ctrl
mlx5hws_send_engine_post_start(struct mlx5hws_send_engine *queue);
void mlx5hws_send_engine_post_req_wqe(struct mlx5hws_send_engine_post_ctrl *ctrl,
char **buf, size_t *len);
void mlx5hws_send_engine_post_end(struct mlx5hws_send_engine_post_ctrl *ctrl,
struct mlx5hws_send_engine_post_attr *attr);
void mlx5hws_send_ste(struct mlx5hws_send_engine *queue,
struct mlx5hws_send_ste_attr *ste_attr);
void mlx5hws_send_stes_fw(struct mlx5hws_context *ctx,
struct mlx5hws_send_engine *queue,
struct mlx5hws_send_ste_attr *ste_attr);
void mlx5hws_send_engine_flush_queue(struct mlx5hws_send_engine *queue);
static inline bool mlx5hws_send_engine_empty(struct mlx5hws_send_engine *queue)
{
struct mlx5hws_send_ring_sq *send_sq = &queue->send_ring.send_sq;
struct mlx5hws_send_ring_cq *send_cq = &queue->send_ring.send_cq;
return ((send_sq->cur_post & send_sq->buf_mask) == send_cq->poll_wqe);
}
static inline bool mlx5hws_send_engine_full(struct mlx5hws_send_engine *queue)
{
return queue->used_entries >= queue->num_entries;
}
static inline void mlx5hws_send_engine_inc_rule(struct mlx5hws_send_engine *queue)
{
queue->used_entries++;
}
static inline void mlx5hws_send_engine_dec_rule(struct mlx5hws_send_engine *queue)
{
queue->used_entries--;
}
static inline void mlx5hws_send_engine_gen_comp(struct mlx5hws_send_engine *queue,
void *user_data,
int comp_status)
{
struct mlx5hws_completed_poll *comp = &queue->completed;
comp->entries[comp->pi].status = comp_status;
comp->entries[comp->pi].user_data = user_data;
comp->pi = (comp->pi + 1) & comp->mask;
}
static inline bool mlx5hws_send_engine_err(struct mlx5hws_send_engine *queue)
{
return queue->err;
}
#endif /* MLX5HWS_SEND_H_ */

View File

@ -0,0 +1,493 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#include "mlx5hws_internal.h"
u32 mlx5hws_table_get_id(struct mlx5hws_table *tbl)
{
return tbl->ft_id;
}
static void hws_table_init_next_ft_attr(struct mlx5hws_table *tbl,
struct mlx5hws_cmd_ft_create_attr *ft_attr)
{
ft_attr->type = tbl->fw_ft_type;
if (tbl->type == MLX5HWS_TABLE_TYPE_FDB)
ft_attr->level = tbl->ctx->caps->fdb_ft.max_level - 1;
else
ft_attr->level = tbl->ctx->caps->nic_ft.max_level - 1;
ft_attr->rtc_valid = true;
}
static void hws_table_set_cap_attr(struct mlx5hws_table *tbl,
struct mlx5hws_cmd_ft_create_attr *ft_attr)
{
/* Enabling reformat_en or decap_en for the first flow table
* must be done when all VFs are down.
* However, HWS doesn't know when it is required to create the first FT.
* On the other hand, HWS doesn't use all these FT capabilities at all
* (the API doesn't even provide a way to specify these flags), so we'll
* just set these caps on all the flow tables.
* If HCA_CAP.fdb_dynamic_tunnel is set, this constraint is N/A.
*/
if (!MLX5_CAP_ESW_FLOWTABLE(tbl->ctx->mdev, fdb_dynamic_tunnel)) {
ft_attr->reformat_en = true;
ft_attr->decap_en = true;
}
}
static int hws_table_up_default_fdb_miss_tbl(struct mlx5hws_table *tbl)
{
struct mlx5hws_cmd_ft_create_attr ft_attr = {0};
struct mlx5hws_cmd_set_fte_attr fte_attr = {0};
struct mlx5hws_cmd_forward_tbl *default_miss;
struct mlx5hws_cmd_set_fte_dest dest = {0};
struct mlx5hws_context *ctx = tbl->ctx;
u8 tbl_type = tbl->type;
if (tbl->type != MLX5HWS_TABLE_TYPE_FDB)
return 0;
if (ctx->common_res[tbl_type].default_miss) {
ctx->common_res[tbl_type].default_miss->refcount++;
return 0;
}
ft_attr.type = tbl->fw_ft_type;
ft_attr.level = tbl->ctx->caps->fdb_ft.max_level; /* The last level */
ft_attr.rtc_valid = false;
dest.destination_type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
dest.destination_id = ctx->caps->eswitch_manager_vport_number;
fte_attr.action_flags = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
fte_attr.dests_num = 1;
fte_attr.dests = &dest;
default_miss = mlx5hws_cmd_forward_tbl_create(ctx->mdev, &ft_attr, &fte_attr);
if (!default_miss) {
mlx5hws_err(ctx, "Failed to default miss table type: 0x%x\n", tbl_type);
return -EINVAL;
}
/* ctx->ctrl_lock must be held here */
ctx->common_res[tbl_type].default_miss = default_miss;
ctx->common_res[tbl_type].default_miss->refcount++;
return 0;
}
/* Called under ctx->ctrl_lock */
static void hws_table_down_default_fdb_miss_tbl(struct mlx5hws_table *tbl)
{
struct mlx5hws_cmd_forward_tbl *default_miss;
struct mlx5hws_context *ctx = tbl->ctx;
u8 tbl_type = tbl->type;
if (tbl->type != MLX5HWS_TABLE_TYPE_FDB)
return;
default_miss = ctx->common_res[tbl_type].default_miss;
if (--default_miss->refcount)
return;
mlx5hws_cmd_forward_tbl_destroy(ctx->mdev, default_miss);
ctx->common_res[tbl_type].default_miss = NULL;
}
static int hws_table_connect_to_default_miss_tbl(struct mlx5hws_table *tbl, u32 ft_id)
{
struct mlx5hws_cmd_ft_modify_attr ft_attr = {0};
int ret;
if (unlikely(tbl->type != MLX5HWS_TABLE_TYPE_FDB))
pr_warn("HWS: invalid table type %d\n", tbl->type);
mlx5hws_cmd_set_attr_connect_miss_tbl(tbl->ctx,
tbl->fw_ft_type,
tbl->type,
&ft_attr);
ret = mlx5hws_cmd_flow_table_modify(tbl->ctx->mdev, &ft_attr, ft_id);
if (ret) {
mlx5hws_err(tbl->ctx, "Failed to connect FT to default FDB FT\n");
return ret;
}
return 0;
}
int mlx5hws_table_create_default_ft(struct mlx5_core_dev *mdev,
struct mlx5hws_table *tbl,
u32 *ft_id)
{
struct mlx5hws_cmd_ft_create_attr ft_attr = {0};
int ret;
hws_table_init_next_ft_attr(tbl, &ft_attr);
hws_table_set_cap_attr(tbl, &ft_attr);
ret = mlx5hws_cmd_flow_table_create(mdev, &ft_attr, ft_id);
if (ret) {
mlx5hws_err(tbl->ctx, "Failed creating default ft\n");
return ret;
}
if (tbl->type == MLX5HWS_TABLE_TYPE_FDB) {
/* Take/create ref over the default miss */
ret = hws_table_up_default_fdb_miss_tbl(tbl);
if (ret) {
mlx5hws_err(tbl->ctx, "Failed to get default fdb miss\n");
goto free_ft_obj;
}
ret = hws_table_connect_to_default_miss_tbl(tbl, *ft_id);
if (ret) {
mlx5hws_err(tbl->ctx, "Failed connecting to default miss tbl\n");
goto down_miss_tbl;
}
}
return 0;
down_miss_tbl:
hws_table_down_default_fdb_miss_tbl(tbl);
free_ft_obj:
mlx5hws_cmd_flow_table_destroy(mdev, ft_attr.type, *ft_id);
return ret;
}
void mlx5hws_table_destroy_default_ft(struct mlx5hws_table *tbl,
u32 ft_id)
{
mlx5hws_cmd_flow_table_destroy(tbl->ctx->mdev, tbl->fw_ft_type, ft_id);
hws_table_down_default_fdb_miss_tbl(tbl);
}
static int hws_table_init_check_hws_support(struct mlx5hws_context *ctx,
struct mlx5hws_table *tbl)
{
if (!(ctx->flags & MLX5HWS_CONTEXT_FLAG_HWS_SUPPORT)) {
mlx5hws_err(ctx, "HWS not supported, cannot create mlx5hws_table\n");
return -EOPNOTSUPP;
}
return 0;
}
static int hws_table_init(struct mlx5hws_table *tbl)
{
struct mlx5hws_context *ctx = tbl->ctx;
int ret;
ret = hws_table_init_check_hws_support(ctx, tbl);
if (ret)
return ret;
if (mlx5hws_table_get_fw_ft_type(tbl->type, (u8 *)&tbl->fw_ft_type)) {
pr_warn("HWS: invalid table type %d\n", tbl->type);
return -EOPNOTSUPP;
}
mutex_lock(&ctx->ctrl_lock);
ret = mlx5hws_table_create_default_ft(tbl->ctx->mdev, tbl, &tbl->ft_id);
if (ret) {
mlx5hws_err(tbl->ctx, "Failed to create flow table object\n");
mutex_unlock(&ctx->ctrl_lock);
return ret;
}
ret = mlx5hws_action_get_default_stc(ctx, tbl->type);
if (ret)
goto tbl_destroy;
INIT_LIST_HEAD(&tbl->matchers_list);
INIT_LIST_HEAD(&tbl->default_miss.head);
mutex_unlock(&ctx->ctrl_lock);
return 0;
tbl_destroy:
mlx5hws_table_destroy_default_ft(tbl, tbl->ft_id);
mutex_unlock(&ctx->ctrl_lock);
return ret;
}
static void hws_table_uninit(struct mlx5hws_table *tbl)
{
mutex_lock(&tbl->ctx->ctrl_lock);
mlx5hws_action_put_default_stc(tbl->ctx, tbl->type);
mlx5hws_table_destroy_default_ft(tbl, tbl->ft_id);
mutex_unlock(&tbl->ctx->ctrl_lock);
}
struct mlx5hws_table *mlx5hws_table_create(struct mlx5hws_context *ctx,
struct mlx5hws_table_attr *attr)
{
struct mlx5hws_table *tbl;
int ret;
if (attr->type > MLX5HWS_TABLE_TYPE_FDB) {
mlx5hws_err(ctx, "Invalid table type %d\n", attr->type);
return NULL;
}
tbl = kzalloc(sizeof(*tbl), GFP_KERNEL);
if (!tbl)
return NULL;
tbl->ctx = ctx;
tbl->type = attr->type;
tbl->level = attr->level;
ret = hws_table_init(tbl);
if (ret) {
mlx5hws_err(ctx, "Failed to initialise table\n");
goto free_tbl;
}
mutex_lock(&ctx->ctrl_lock);
list_add(&tbl->tbl_list_node, &ctx->tbl_list);
mutex_unlock(&ctx->ctrl_lock);
return tbl;
free_tbl:
kfree(tbl);
return NULL;
}
int mlx5hws_table_destroy(struct mlx5hws_table *tbl)
{
struct mlx5hws_context *ctx = tbl->ctx;
int ret;
mutex_lock(&ctx->ctrl_lock);
if (!list_empty(&tbl->matchers_list)) {
mlx5hws_err(tbl->ctx, "Cannot destroy table containing matchers\n");
ret = -EBUSY;
goto unlock_err;
}
if (!list_empty(&tbl->default_miss.head)) {
mlx5hws_err(tbl->ctx, "Cannot destroy table pointed by default miss\n");
ret = -EBUSY;
goto unlock_err;
}
list_del_init(&tbl->tbl_list_node);
mutex_unlock(&ctx->ctrl_lock);
hws_table_uninit(tbl);
kfree(tbl);
return 0;
unlock_err:
mutex_unlock(&ctx->ctrl_lock);
return ret;
}
static u32 hws_table_get_last_ft(struct mlx5hws_table *tbl)
{
struct mlx5hws_matcher *matcher;
if (list_empty(&tbl->matchers_list))
return tbl->ft_id;
matcher = list_last_entry(&tbl->matchers_list, struct mlx5hws_matcher, list_node);
return matcher->end_ft_id;
}
int mlx5hws_table_ft_set_default_next_ft(struct mlx5hws_table *tbl, u32 ft_id)
{
struct mlx5hws_cmd_ft_modify_attr ft_attr = {0};
int ret;
/* Due to FW limitation, resetting the flow table to default action will
* disconnect RTC when ignore_flow_level_rtc_valid is not supported.
*/
if (!tbl->ctx->caps->nic_ft.ignore_flow_level_rtc_valid)
return 0;
if (tbl->type == MLX5HWS_TABLE_TYPE_FDB)
return hws_table_connect_to_default_miss_tbl(tbl, ft_id);
ft_attr.type = tbl->fw_ft_type;
ft_attr.modify_fs = MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION;
ft_attr.table_miss_action = MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION_DEFAULT;
ret = mlx5hws_cmd_flow_table_modify(tbl->ctx->mdev, &ft_attr, ft_id);
if (ret) {
mlx5hws_err(tbl->ctx, "Failed to set FT default miss action\n");
return ret;
}
return 0;
}
int mlx5hws_table_ft_set_next_rtc(struct mlx5hws_context *ctx,
u32 ft_id,
u32 fw_ft_type,
u32 rtc_0_id,
u32 rtc_1_id)
{
struct mlx5hws_cmd_ft_modify_attr ft_attr = {0};
ft_attr.modify_fs = MLX5_IFC_MODIFY_FLOW_TABLE_RTC_ID;
ft_attr.type = fw_ft_type;
ft_attr.rtc_id_0 = rtc_0_id;
ft_attr.rtc_id_1 = rtc_1_id;
return mlx5hws_cmd_flow_table_modify(ctx->mdev, &ft_attr, ft_id);
}
static int hws_table_ft_set_next_ft(struct mlx5hws_context *ctx,
u32 ft_id,
u32 fw_ft_type,
u32 next_ft_id)
{
struct mlx5hws_cmd_ft_modify_attr ft_attr = {0};
ft_attr.modify_fs = MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION;
ft_attr.table_miss_action = MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION_GOTO_TBL;
ft_attr.type = fw_ft_type;
ft_attr.table_miss_id = next_ft_id;
return mlx5hws_cmd_flow_table_modify(ctx->mdev, &ft_attr, ft_id);
}
int mlx5hws_table_update_connected_miss_tables(struct mlx5hws_table *dst_tbl)
{
struct mlx5hws_table *src_tbl;
int ret;
if (list_empty(&dst_tbl->default_miss.head))
return 0;
list_for_each_entry(src_tbl, &dst_tbl->default_miss.head, default_miss.next) {
ret = mlx5hws_table_connect_to_miss_table(src_tbl, dst_tbl);
if (ret) {
mlx5hws_err(dst_tbl->ctx,
"Failed to update source miss table, unexpected behavior\n");
return ret;
}
}
return 0;
}
int mlx5hws_table_connect_to_miss_table(struct mlx5hws_table *src_tbl,
struct mlx5hws_table *dst_tbl)
{
struct mlx5hws_matcher *matcher;
u32 last_ft_id;
int ret;
last_ft_id = hws_table_get_last_ft(src_tbl);
if (dst_tbl) {
if (list_empty(&dst_tbl->matchers_list)) {
/* Connect src_tbl last_ft to dst_tbl start anchor */
ret = hws_table_ft_set_next_ft(src_tbl->ctx,
last_ft_id,
src_tbl->fw_ft_type,
dst_tbl->ft_id);
if (ret)
return ret;
/* Reset last_ft RTC to default RTC */
ret = mlx5hws_table_ft_set_next_rtc(src_tbl->ctx,
last_ft_id,
src_tbl->fw_ft_type,
0, 0);
if (ret)
return ret;
} else {
/* Connect src_tbl last_ft to first matcher RTC */
matcher = list_first_entry(&dst_tbl->matchers_list,
struct mlx5hws_matcher,
list_node);
ret = mlx5hws_table_ft_set_next_rtc(src_tbl->ctx,
last_ft_id,
src_tbl->fw_ft_type,
matcher->match_ste.rtc_0_id,
matcher->match_ste.rtc_1_id);
if (ret)
return ret;
/* Reset next miss FT to default */
ret = mlx5hws_table_ft_set_default_next_ft(src_tbl, last_ft_id);
if (ret)
return ret;
}
} else {
/* Reset next miss FT to default */
ret = mlx5hws_table_ft_set_default_next_ft(src_tbl, last_ft_id);
if (ret)
return ret;
/* Reset last_ft RTC to default RTC */
ret = mlx5hws_table_ft_set_next_rtc(src_tbl->ctx,
last_ft_id,
src_tbl->fw_ft_type,
0, 0);
if (ret)
return ret;
}
src_tbl->default_miss.miss_tbl = dst_tbl;
return 0;
}
static int hws_table_set_default_miss_not_valid(struct mlx5hws_table *tbl,
struct mlx5hws_table *miss_tbl)
{
if (!tbl->ctx->caps->nic_ft.ignore_flow_level_rtc_valid) {
mlx5hws_err(tbl->ctx, "Default miss table is not supported\n");
return -EOPNOTSUPP;
}
if ((miss_tbl && miss_tbl->type != tbl->type)) {
mlx5hws_err(tbl->ctx, "Invalid arguments\n");
return -EINVAL;
}
return 0;
}
int mlx5hws_table_set_default_miss(struct mlx5hws_table *tbl,
struct mlx5hws_table *miss_tbl)
{
struct mlx5hws_context *ctx = tbl->ctx;
struct mlx5hws_table *old_miss_tbl;
int ret;
ret = hws_table_set_default_miss_not_valid(tbl, miss_tbl);
if (ret)
return ret;
mutex_lock(&ctx->ctrl_lock);
old_miss_tbl = tbl->default_miss.miss_tbl;
ret = mlx5hws_table_connect_to_miss_table(tbl, miss_tbl);
if (ret)
goto out;
if (old_miss_tbl)
list_del_init(&tbl->default_miss.next);
old_miss_tbl = tbl->default_miss.miss_tbl;
if (old_miss_tbl)
list_del_init(&old_miss_tbl->default_miss.head);
if (miss_tbl)
list_add(&tbl->default_miss.next, &miss_tbl->default_miss.head);
mutex_unlock(&ctx->ctrl_lock);
return 0;
out:
mutex_unlock(&ctx->ctrl_lock);
return -ret;
}

View File

@ -0,0 +1,68 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#ifndef MLX5HWS_TABLE_H_
#define MLX5HWS_TABLE_H_
struct mlx5hws_default_miss {
/* My miss table */
struct mlx5hws_table *miss_tbl;
struct list_head next;
/* Tables missing to my table */
struct list_head head;
};
struct mlx5hws_table {
struct mlx5hws_context *ctx;
u32 ft_id;
enum mlx5hws_table_type type;
u32 fw_ft_type;
u32 level;
struct list_head matchers_list;
struct list_head tbl_list_node;
struct mlx5hws_default_miss default_miss;
};
static inline
u32 mlx5hws_table_get_fw_ft_type(enum mlx5hws_table_type type,
u8 *ret_type)
{
if (type != MLX5HWS_TABLE_TYPE_FDB)
return -EOPNOTSUPP;
*ret_type = FS_FT_FDB;
return 0;
}
static inline
u32 mlx5hws_table_get_res_fw_ft_type(enum mlx5hws_table_type tbl_type,
bool is_mirror)
{
if (tbl_type == MLX5HWS_TABLE_TYPE_FDB)
return is_mirror ? FS_FT_FDB_TX : FS_FT_FDB_RX;
return 0;
}
int mlx5hws_table_create_default_ft(struct mlx5_core_dev *mdev,
struct mlx5hws_table *tbl,
u32 *ft_id);
void mlx5hws_table_destroy_default_ft(struct mlx5hws_table *tbl,
u32 ft_id);
int mlx5hws_table_connect_to_miss_table(struct mlx5hws_table *src_tbl,
struct mlx5hws_table *dst_tbl);
int mlx5hws_table_update_connected_miss_tables(struct mlx5hws_table *dst_tbl);
int mlx5hws_table_ft_set_default_next_ft(struct mlx5hws_table *tbl, u32 ft_id);
int mlx5hws_table_ft_set_next_rtc(struct mlx5hws_context *ctx,
u32 ft_id,
u32 fw_ft_type,
u32 rtc_0_id,
u32 rtc_1_id);
#endif /* MLX5HWS_TABLE_H_ */

View File

@ -0,0 +1,86 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#include "mlx5hws_internal.h"
int mlx5hws_vport_init_vports(struct mlx5hws_context *ctx)
{
int ret;
if (!ctx->caps->eswitch_manager)
return 0;
xa_init(&ctx->vports.vport_gvmi_xa);
/* Set gvmi for eswitch manager and uplink vports only. Rest of the vports
* (vport 0 of other function, VFs and SFs) will be queried dynamically.
*/
ret = mlx5hws_cmd_query_gvmi(ctx->mdev, false, 0, &ctx->vports.esw_manager_gvmi);
if (ret)
return ret;
ctx->vports.uplink_gvmi = 0;
return 0;
}
void mlx5hws_vport_uninit_vports(struct mlx5hws_context *ctx)
{
if (ctx->caps->eswitch_manager)
xa_destroy(&ctx->vports.vport_gvmi_xa);
}
static int hws_vport_add_gvmi(struct mlx5hws_context *ctx, u16 vport)
{
u16 vport_gvmi;
int ret;
ret = mlx5hws_cmd_query_gvmi(ctx->mdev, true, vport, &vport_gvmi);
if (ret)
return -EINVAL;
ret = xa_insert(&ctx->vports.vport_gvmi_xa, vport,
xa_mk_value(vport_gvmi), GFP_KERNEL);
if (ret)
mlx5hws_dbg(ctx, "Couldn't insert new vport gvmi into xarray (%d)\n", ret);
return ret;
}
static bool hws_vport_is_esw_mgr_vport(struct mlx5hws_context *ctx, u16 vport)
{
return ctx->caps->is_ecpf ? vport == MLX5_VPORT_ECPF :
vport == MLX5_VPORT_PF;
}
int mlx5hws_vport_get_gvmi(struct mlx5hws_context *ctx, u16 vport, u16 *vport_gvmi)
{
void *entry;
int ret;
if (!ctx->caps->eswitch_manager)
return -EINVAL;
if (hws_vport_is_esw_mgr_vport(ctx, vport)) {
*vport_gvmi = ctx->vports.esw_manager_gvmi;
return 0;
}
if (vport == MLX5_VPORT_UPLINK) {
*vport_gvmi = ctx->vports.uplink_gvmi;
return 0;
}
load_entry:
entry = xa_load(&ctx->vports.vport_gvmi_xa, vport);
if (!xa_is_value(entry)) {
ret = hws_vport_add_gvmi(ctx, vport);
if (ret && ret != -EBUSY)
return ret;
goto load_entry;
}
*vport_gvmi = (u16)xa_to_value(entry);
return 0;
}

View File

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
#ifndef MLX5HWS_VPORT_H_
#define MLX5HWS_VPORT_H_
int mlx5hws_vport_init_vports(struct mlx5hws_context *ctx);
void mlx5hws_vport_uninit_vports(struct mlx5hws_context *ctx);
int mlx5hws_vport_get_gvmi(struct mlx5hws_context *ctx, u16 vport, u16 *vport_gvmi);
#endif /* MLX5HWS_VPORT_H_ */

View File

@ -80,23 +80,15 @@ enum {
enum {
MLX5_OBJ_TYPE_SW_ICM = 0x0008,
MLX5_OBJ_TYPE_HEADER_MODIFY_ARGUMENT = 0x23,
};
enum {
MLX5_GENERAL_OBJ_TYPES_CAP_SW_ICM = (1ULL << MLX5_OBJ_TYPE_SW_ICM),
MLX5_GENERAL_OBJ_TYPES_CAP_GENEVE_TLV_OPT = (1ULL << 11),
MLX5_GENERAL_OBJ_TYPES_CAP_VIRTIO_NET_Q = (1ULL << 13),
MLX5_GENERAL_OBJ_TYPES_CAP_HEADER_MODIFY_ARGUMENT =
(1ULL << MLX5_OBJ_TYPE_HEADER_MODIFY_ARGUMENT),
MLX5_GENERAL_OBJ_TYPES_CAP_MACSEC_OFFLOAD = (1ULL << 39),
};
enum {
MLX5_OBJ_TYPE_GENEVE_TLV_OPT = 0x000b,
MLX5_OBJ_TYPE_VIRTIO_NET_Q = 0x000d,
MLX5_OBJ_TYPE_VIRTIO_Q_COUNTERS = 0x001c,
MLX5_OBJ_TYPE_MATCH_DEFINER = 0x0018,
MLX5_OBJ_TYPE_HEADER_MODIFY_ARGUMENT = 0x23,
MLX5_OBJ_TYPE_STC = 0x0040,
MLX5_OBJ_TYPE_RTC = 0x0041,
MLX5_OBJ_TYPE_STE = 0x0042,
MLX5_OBJ_TYPE_MODIFY_HDR_PATTERN = 0x0043,
MLX5_OBJ_TYPE_PAGE_TRACK = 0x46,
MLX5_OBJ_TYPE_MKEY = 0xff01,
MLX5_OBJ_TYPE_QP = 0xff02,
@ -112,6 +104,16 @@ enum {
MLX5_OBJ_TYPE_RQT = 0xff0e,
MLX5_OBJ_TYPE_FLOW_COUNTER = 0xff0f,
MLX5_OBJ_TYPE_CQ = 0xff10,
MLX5_OBJ_TYPE_FT_ALIAS = 0xff15,
};
enum {
MLX5_GENERAL_OBJ_TYPES_CAP_SW_ICM = (1ULL << MLX5_OBJ_TYPE_SW_ICM),
MLX5_GENERAL_OBJ_TYPES_CAP_GENEVE_TLV_OPT = (1ULL << 11),
MLX5_GENERAL_OBJ_TYPES_CAP_VIRTIO_NET_Q = (1ULL << 13),
MLX5_GENERAL_OBJ_TYPES_CAP_HEADER_MODIFY_ARGUMENT =
(1ULL << MLX5_OBJ_TYPE_HEADER_MODIFY_ARGUMENT),
MLX5_GENERAL_OBJ_TYPES_CAP_MACSEC_OFFLOAD = (1ULL << 39),
};
enum {
@ -313,6 +315,7 @@ enum {
MLX5_CMD_OP_MODIFY_VHCA_STATE = 0xb0e,
MLX5_CMD_OP_SYNC_CRYPTO = 0xb12,
MLX5_CMD_OP_ALLOW_OTHER_VHCA_ACCESS = 0xb16,
MLX5_CMD_OP_GENERATE_WQE = 0xb17,
MLX5_CMD_OP_MAX
};
@ -485,7 +488,13 @@ struct mlx5_ifc_flow_table_prop_layout_bits {
u8 reserved_at_66[0x2];
u8 reformat_add_macsec[0x1];
u8 reformat_remove_macsec[0x1];
u8 reserved_at_6a[0xe];
u8 reparse[0x1];
u8 reserved_at_6b[0x1];
u8 cross_vhca_object[0x1];
u8 reformat_l2_to_l3_audp_tunnel[0x1];
u8 reformat_l3_audp_tunnel_to_l2[0x1];
u8 ignore_flow_level_rtc_valid[0x1];
u8 reserved_at_70[0x8];
u8 log_max_ft_num[0x8];
u8 reserved_at_80[0x10];
@ -522,7 +531,15 @@ struct mlx5_ifc_ipv6_layout_bits {
u8 ipv6[16][0x8];
};
struct mlx5_ifc_ipv6_simple_layout_bits {
u8 ipv6_127_96[0x20];
u8 ipv6_95_64[0x20];
u8 ipv6_63_32[0x20];
u8 ipv6_31_0[0x20];
};
union mlx5_ifc_ipv6_layout_ipv4_layout_auto_bits {
struct mlx5_ifc_ipv6_simple_layout_bits ipv6_simple_layout;
struct mlx5_ifc_ipv6_layout_bits ipv6_layout;
struct mlx5_ifc_ipv4_layout_bits ipv4_layout;
u8 reserved_at_0[0x80];
@ -911,7 +928,9 @@ struct mlx5_ifc_flow_table_eswitch_cap_bits {
u8 reserved_at_8[0x5];
u8 fdb_uplink_hairpin[0x1];
u8 fdb_multi_path_any_table_limit_regc[0x1];
u8 reserved_at_f[0x3];
u8 reserved_at_f[0x1];
u8 fdb_dynamic_tunnel[0x1];
u8 reserved_at_11[0x1];
u8 fdb_multi_path_any_table[0x1];
u8 reserved_at_13[0x2];
u8 fdb_modify_header_fwd_to_table[0x1];
@ -950,6 +969,73 @@ struct mlx5_ifc_flow_table_eswitch_cap_bits {
u8 reserved_at_1900[0x6700];
};
struct mlx5_ifc_wqe_based_flow_table_cap_bits {
u8 reserved_at_0[0x3];
u8 log_max_num_ste[0x5];
u8 reserved_at_8[0x3];
u8 log_max_num_stc[0x5];
u8 reserved_at_10[0x3];
u8 log_max_num_rtc[0x5];
u8 reserved_at_18[0x3];
u8 log_max_num_header_modify_pattern[0x5];
u8 rtc_hash_split_table[0x1];
u8 rtc_linear_lookup_table[0x1];
u8 reserved_at_22[0x1];
u8 stc_alloc_log_granularity[0x5];
u8 reserved_at_28[0x3];
u8 stc_alloc_log_max[0x5];
u8 reserved_at_30[0x3];
u8 ste_alloc_log_granularity[0x5];
u8 reserved_at_38[0x3];
u8 ste_alloc_log_max[0x5];
u8 reserved_at_40[0xb];
u8 rtc_reparse_mode[0x5];
u8 reserved_at_50[0x3];
u8 rtc_index_mode[0x5];
u8 reserved_at_58[0x3];
u8 rtc_log_depth_max[0x5];
u8 reserved_at_60[0x10];
u8 ste_format[0x10];
u8 stc_action_type[0x80];
u8 header_insert_type[0x10];
u8 header_remove_type[0x10];
u8 trivial_match_definer[0x20];
u8 reserved_at_140[0x1b];
u8 rtc_max_num_hash_definer_gen_wqe[0x5];
u8 reserved_at_160[0x18];
u8 access_index_mode[0x8];
u8 reserved_at_180[0x10];
u8 ste_format_gen_wqe[0x10];
u8 linear_match_definer_reg_c3[0x20];
u8 fdb_jump_to_tir_stc[0x1];
u8 reserved_at_1c1[0x1f];
};
struct mlx5_ifc_esw_cap_bits {
u8 reserved_at_0[0x1d];
u8 merged_eswitch[0x1];
u8 reserved_at_1e[0x2];
u8 reserved_at_20[0x40];
u8 esw_manager_vport_number_valid[0x1];
u8 reserved_at_61[0xf];
u8 esw_manager_vport_number[0x10];
u8 reserved_at_80[0x780];
};
enum {
MLX5_COUNTER_SOURCE_ESWITCH = 0x0,
MLX5_COUNTER_FLOW_ESWITCH = 0x1,
@ -1443,9 +1529,13 @@ enum {
};
enum {
MLX5_FLEX_IPV4_OVER_VXLAN_ENABLED = 1 << 0,
MLX5_FLEX_IPV6_OVER_VXLAN_ENABLED = 1 << 1,
MLX5_FLEX_IPV6_OVER_IP_ENABLED = 1 << 2,
MLX5_FLEX_PARSER_GENEVE_ENABLED = 1 << 3,
MLX5_FLEX_PARSER_MPLS_OVER_GRE_ENABLED = 1 << 4,
MLX5_FLEX_PARSER_MPLS_OVER_UDP_ENABLED = 1 << 5,
MLX5_FLEX_P_BIT_VXLAN_GPE_ENABLED = 1 << 6,
MLX5_FLEX_PARSER_VXLAN_GPE_ENABLED = 1 << 7,
MLX5_FLEX_PARSER_ICMP_V4_ENABLED = 1 << 8,
MLX5_FLEX_PARSER_ICMP_V6_ENABLED = 1 << 9,
@ -1650,7 +1740,8 @@ struct mlx5_ifc_cmd_hca_cap_bits {
u8 pci_sync_for_fw_update_event[0x1];
u8 reserved_at_1f2[0x6];
u8 init2_lag_tx_port_affinity[0x1];
u8 reserved_at_1fa[0x3];
u8 reserved_at_1fa[0x2];
u8 wqe_based_flow_table_update_cap[0x1];
u8 cqe_version[0x4];
u8 compact_address_vector[0x1];
@ -1959,7 +2050,7 @@ struct mlx5_ifc_cmd_hca_cap_bits {
u8 reserved_at_760[0x3];
u8 log_max_num_header_modify_argument[0x5];
u8 reserved_at_768[0x4];
u8 log_header_modify_argument_granularity_offset[0x4];
u8 log_header_modify_argument_granularity[0x4];
u8 reserved_at_770[0x3];
u8 log_header_modify_argument_max_alloc[0x5];
@ -2006,7 +2097,8 @@ struct mlx5_ifc_cmd_hca_cap_2_bits {
u8 reserved_at_140[0x60];
u8 flow_table_type_2_type[0x8];
u8 reserved_at_1a8[0x3];
u8 reserved_at_1a8[0x2];
u8 format_select_dw_8_6_ext[0x1];
u8 log_min_mkey_entity_size[0x5];
u8 reserved_at_1b0[0x10];
@ -2022,6 +2114,16 @@ struct mlx5_ifc_cmd_hca_cap_2_bits {
u8 reserved_at_250[0x10];
u8 reserved_at_260[0x120];
u8 format_select_dw_gtpu_dw_0[0x8];
u8 format_select_dw_gtpu_dw_1[0x8];
u8 format_select_dw_gtpu_dw_2[0x8];
u8 format_select_dw_gtpu_first_ext_dw_0[0x8];
u8 generate_wqe_type[0x20];
u8 reserved_at_2c0[0xc0];
u8 reserved_at_380[0xb];
u8 min_mkey_log_entity_size_fixed_buffer[0x5];
u8 ec_vf_vport_base[0x10];
@ -2037,9 +2139,11 @@ struct mlx5_ifc_cmd_hca_cap_2_bits {
u8 reserved_at_400[0x1];
u8 min_mkey_log_entity_size_fixed_buffer_valid[0x1];
u8 reserved_at_402[0x1e];
u8 reserved_at_402[0xe];
u8 return_reg_id[0x10];
u8 reserved_at_420[0x20];
u8 reserved_at_420[0x1c];
u8 flow_table_hash_type[0x4];
u8 reserved_at_440[0x8];
u8 max_num_eqs_24b[0x18];
@ -2086,7 +2190,7 @@ struct mlx5_ifc_extended_dest_format_bits {
u8 reserved_at_60[0x20];
};
union mlx5_ifc_dest_format_struct_flow_counter_list_auto_bits {
union mlx5_ifc_dest_format_flow_counter_list_auto_bits {
struct mlx5_ifc_extended_dest_format_bits extended_dest_format;
struct mlx5_ifc_flow_counter_list_bits flow_counter_list;
};
@ -2178,7 +2282,10 @@ struct mlx5_ifc_wq_bits {
u8 reserved_at_139[0x4];
u8 log_wqe_stride_size[0x3];
u8 reserved_at_140[0x80];
u8 dbr_umem_id[0x20];
u8 wq_umem_id[0x20];
u8 wq_umem_offset[0x40];
u8 headers_mkey[0x20];
@ -3562,6 +3669,8 @@ union mlx5_ifc_hca_cap_union_bits {
struct mlx5_ifc_per_protocol_networking_offload_caps_bits per_protocol_networking_offload_caps;
struct mlx5_ifc_flow_table_nic_cap_bits flow_table_nic_cap;
struct mlx5_ifc_flow_table_eswitch_cap_bits flow_table_eswitch_cap;
struct mlx5_ifc_wqe_based_flow_table_cap_bits wqe_based_flow_table_cap;
struct mlx5_ifc_esw_cap_bits esw_cap;
struct mlx5_ifc_e_switch_cap_bits e_switch_cap;
struct mlx5_ifc_port_selection_cap_bits port_selection_cap;
struct mlx5_ifc_qos_cap_bits qos_cap;
@ -3678,7 +3787,7 @@ struct mlx5_ifc_flow_context_bits {
u8 reserved_at_1300[0x500];
union mlx5_ifc_dest_format_struct_flow_counter_list_auto_bits destination[];
union mlx5_ifc_dest_format_flow_counter_list_auto_bits destination[];
};
enum {
@ -3919,7 +4028,8 @@ struct mlx5_ifc_sqc_bits {
u8 reg_umr[0x1];
u8 allow_swp[0x1];
u8 hairpin[0x1];
u8 reserved_at_f[0xb];
u8 non_wire[0x1];
u8 reserved_at_10[0xa];
u8 ts_format[0x2];
u8 reserved_at_1c[0x4];
@ -4961,6 +5071,16 @@ struct mlx5_ifc_set_fte_in_bits {
struct mlx5_ifc_flow_context_bits flow_context;
};
struct mlx5_ifc_dest_format_bits {
u8 destination_type[0x8];
u8 destination_id[0x18];
u8 destination_eswitch_owner_vhca_id_valid[0x1];
u8 packet_reformat[0x1];
u8 reserved_at_22[0xe];
u8 destination_eswitch_owner_vhca_id[0x10];
};
struct mlx5_ifc_rts2rts_qp_out_bits {
u8 status[0x8];
u8 reserved_at_8[0x18];
@ -6127,7 +6247,8 @@ struct mlx5_ifc_flow_table_context_bits {
u8 termination_table[0x1];
u8 table_miss_action[0x4];
u8 level[0x8];
u8 reserved_at_10[0x8];
u8 rtc_valid[0x1];
u8 reserved_at_11[0x7];
u8 log_size[0x8];
u8 reserved_at_20[0x8];
@ -6137,11 +6258,21 @@ struct mlx5_ifc_flow_table_context_bits {
u8 lag_master_next_table_id[0x18];
u8 reserved_at_60[0x60];
union {
struct {
u8 sw_owner_icm_root_1[0x40];
u8 sw_owner_icm_root_1[0x40];
u8 sw_owner_icm_root_0[0x40];
} sws;
struct {
u8 rtc_id_0[0x20];
u8 sw_owner_icm_root_0[0x40];
u8 rtc_id_1[0x20];
u8 reserved_at_100[0x40];
} hws;
};
};
struct mlx5_ifc_query_flow_table_out_bits {
@ -8923,7 +9054,9 @@ struct mlx5_ifc_create_qp_in_bits {
struct mlx5_ifc_qpc_bits qpc;
u8 reserved_at_800[0x60];
u8 wq_umem_offset[0x40];
u8 wq_umem_id[0x20];
u8 wq_umem_valid[0x1];
u8 reserved_at_861[0x1f];

View File

@ -149,6 +149,7 @@ enum {
MLX5_WQE_CTRL_CQ_UPDATE = 2 << 2,
MLX5_WQE_CTRL_CQ_UPDATE_AND_EQE = 3 << 2,
MLX5_WQE_CTRL_SOLICITED = 1 << 1,
MLX5_WQE_CTRL_INITIATOR_SMALL_FENCE = 1 << 5,
};
enum {