Merge branch 'mlx5-hw-managed-flow-steering-in-fs-core-level'

Tariq Toukan says:

====================
mlx5 HW-Managed Flow Steering in FS core level

This patchset by Moshe follows Yevgeny's patchsets [1][2] on subject
"HW-Managed Flow Steering in mlx5 driver". As introduced there in HW
managed Flow Steering mode (HWS) the driver is configuring steering
rules directly to the HW using WQs with a special new type of WQE (Work
Queue Element). This way we can reach higher rule insertion/deletion
rate with much lower CPU utilization compared to SW Managed Flow
Steering (SWS).

This patchset adds API to manage namespace, flow tables, flow groups and
prepare FTE (Flow Table Entry) rules. It also adds caching and pool
mechanisms for HWS actions to allow sharing of steering actions among
different rules. The implementation of this API in FS layer, allows FS
core to use HW Managed Flow Steering in addition to the existing FW or
SW Managed Flow Steering.

Patch 13 of this series adds support for configuring HW Managed Flow
Steering mode through devlink param, similar to configuring SW Managed
Flow Steering mode:

 # devlink dev param set pci/0000:08:00.0 name flow_steering_mode \
      cmode runtime value hmfs

In addition, the series contains 2 HWS patches from Yevgeny that
implement flow update support.

[1] https://lore.kernel.org/netdev/20240903031948.78006-1-saeed@kernel.org/
[2] https://lore.kernel.org/all/20250102181415.1477316-1-tariqt@nvidia.com/
====================

Link: https://patch.msgid.link/20250109160546.1733647-1-tariqt@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2025-01-13 19:24:32 -08:00
commit a833fb852e
19 changed files with 2263 additions and 302 deletions

View File

@ -53,6 +53,9 @@ parameters.
* ``smfs`` Software managed flow steering. In SMFS mode, the HW
steering entities are created and manage through the driver without
firmware intervention.
* ``hmfs`` Hardware managed flow steering. In HMFS mode, the driver
is configuring steering rules directly to the HW using Work Queues with
a special new type of WQE (Work Queue Element).
SMFS mode is faster and provides better rule insertion rate compared to
default DMFS mode.

View File

@ -151,8 +151,9 @@ mlx5_core-$(CONFIG_MLX5_HW_STEERING) += steering/hws/cmd.o \
steering/hws/bwc.o \
steering/hws/debug.o \
steering/hws/vport.o \
steering/hws/bwc_complex.o
steering/hws/bwc_complex.o \
steering/hws/fs_hws_pools.o \
steering/hws/fs_hws.o
#
# SF device

View File

@ -3535,35 +3535,42 @@ static int mlx5_fs_mode_validate(struct devlink *devlink, u32 id,
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
char *value = val.vstr;
int err = 0;
u8 eswitch_mode;
if (!strcmp(value, "dmfs")) {
if (!strcmp(value, "dmfs"))
return 0;
} else if (!strcmp(value, "smfs")) {
u8 eswitch_mode;
bool smfs_cap;
eswitch_mode = mlx5_eswitch_mode(dev);
smfs_cap = mlx5_fs_dr_is_supported(dev);
if (!strcmp(value, "smfs")) {
bool smfs_cap = mlx5_fs_dr_is_supported(dev);
if (!smfs_cap) {
err = -EOPNOTSUPP;
NL_SET_ERR_MSG_MOD(extack,
"Software managed steering is not supported by current device");
return -EOPNOTSUPP;
}
} else if (!strcmp(value, "hmfs")) {
bool hmfs_cap = mlx5_fs_hws_is_supported(dev);
else if (eswitch_mode == MLX5_ESWITCH_OFFLOADS) {
if (!hmfs_cap) {
NL_SET_ERR_MSG_MOD(extack,
"Software managed steering is not supported when eswitch offloads enabled.");
err = -EOPNOTSUPP;
"Hardware steering is not supported by current device");
return -EOPNOTSUPP;
}
} else {
NL_SET_ERR_MSG_MOD(extack,
"Bad parameter: supported values are [\"dmfs\", \"smfs\"]");
err = -EINVAL;
"Bad parameter: supported values are [\"dmfs\", \"smfs\", \"hmfs\"]");
return -EINVAL;
}
return err;
eswitch_mode = mlx5_eswitch_mode(dev);
if (eswitch_mode == MLX5_ESWITCH_OFFLOADS) {
NL_SET_ERR_MSG_FMT_MOD(extack,
"Moving to %s is not supported when eswitch offloads enabled.",
value);
return -EOPNOTSUPP;
}
return 0;
}
static int mlx5_fs_mode_set(struct devlink *devlink, u32 id,
@ -3575,6 +3582,8 @@ static int mlx5_fs_mode_set(struct devlink *devlink, u32 id,
if (!strcmp(ctx->val.vstr, "smfs"))
mode = MLX5_FLOW_STEERING_MODE_SMFS;
else if (!strcmp(ctx->val.vstr, "hmfs"))
mode = MLX5_FLOW_STEERING_MODE_HMFS;
else
mode = MLX5_FLOW_STEERING_MODE_DMFS;
dev->priv.steering->mode = mode;
@ -3587,10 +3596,17 @@ static int mlx5_fs_mode_get(struct devlink *devlink, u32 id,
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
if (dev->priv.steering->mode == MLX5_FLOW_STEERING_MODE_SMFS)
switch (dev->priv.steering->mode) {
case MLX5_FLOW_STEERING_MODE_SMFS:
strscpy(ctx->val.vstr, "smfs", sizeof(ctx->val.vstr));
else
break;
case MLX5_FLOW_STEERING_MODE_HMFS:
strscpy(ctx->val.vstr, "hmfs", sizeof(ctx->val.vstr));
break;
default:
strscpy(ctx->val.vstr, "dmfs", sizeof(ctx->val.vstr));
}
return 0;
}
@ -4009,6 +4025,8 @@ int mlx5_flow_namespace_set_mode(struct mlx5_flow_namespace *ns,
if (mode == MLX5_FLOW_STEERING_MODE_SMFS)
cmds = mlx5_fs_cmd_get_dr_cmds();
else if (mode == MLX5_FLOW_STEERING_MODE_HMFS)
cmds = mlx5_fs_cmd_get_hws_cmds();
else
cmds = mlx5_fs_cmd_get_fw_cmds();
if (!cmds)

View File

@ -38,6 +38,7 @@
#include <linux/rhashtable.h>
#include <linux/llist.h>
#include <steering/sws/fs_dr.h>
#include <steering/hws/fs_hws.h>
#define FDB_TC_MAX_CHAIN 3
#define FDB_FT_CHAIN (FDB_TC_MAX_CHAIN + 1)
@ -64,6 +65,7 @@ struct mlx5_modify_hdr {
enum mlx5_flow_resource_owner owner;
union {
struct mlx5_fs_dr_action fs_dr_action;
struct mlx5_fs_hws_action fs_hws_action;
u32 id;
};
};
@ -74,6 +76,7 @@ struct mlx5_pkt_reformat {
enum mlx5_flow_resource_owner owner;
union {
struct mlx5_fs_dr_action fs_dr_action;
struct mlx5_fs_hws_action fs_hws_action;
u32 id;
};
};
@ -126,7 +129,8 @@ enum fs_fte_status {
enum mlx5_flow_steering_mode {
MLX5_FLOW_STEERING_MODE_DMFS,
MLX5_FLOW_STEERING_MODE_SMFS
MLX5_FLOW_STEERING_MODE_SMFS,
MLX5_FLOW_STEERING_MODE_HMFS,
};
enum mlx5_flow_steering_capabilty {
@ -190,7 +194,10 @@ struct mlx5_flow_handle {
/* Type of children is mlx5_flow_group */
struct mlx5_flow_table {
struct fs_node node;
struct mlx5_fs_dr_table fs_dr_table;
union {
struct mlx5_fs_dr_table fs_dr_table;
struct mlx5_fs_hws_table fs_hws_table;
};
u32 id;
u16 vport;
unsigned int max_fte;
@ -247,7 +254,10 @@ struct fs_fte_dup {
/* Type of children is mlx5_flow_rule */
struct fs_fte {
struct fs_node node;
struct mlx5_fs_dr_rule fs_dr_rule;
union {
struct mlx5_fs_dr_rule fs_dr_rule;
struct mlx5_fs_hws_rule fs_hws_rule;
};
u32 val[MLX5_ST_SZ_DW_MATCH_PARAM];
struct fs_fte_action act_dests;
struct fs_fte_dup *dup;
@ -280,7 +290,10 @@ struct mlx5_flow_group_mask {
/* Type of children is fs_fte */
struct mlx5_flow_group {
struct fs_node node;
struct mlx5_fs_dr_matcher fs_dr_matcher;
union {
struct mlx5_fs_dr_matcher fs_dr_matcher;
struct mlx5_fs_hws_matcher fs_hws_matcher;
};
struct mlx5_flow_group_mask mask;
u32 start_index;
u32 max_ftes;
@ -293,7 +306,10 @@ struct mlx5_flow_group {
struct mlx5_flow_root_namespace {
struct mlx5_flow_namespace ns;
enum mlx5_flow_steering_mode mode;
struct mlx5_fs_dr_domain fs_dr_domain;
union {
struct mlx5_fs_dr_domain fs_dr_domain;
struct mlx5_fs_hws_context fs_hws_context;
};
enum fs_flow_table_type table_type;
struct mlx5_core_dev *dev;
struct mlx5_flow_table *root_ft;
@ -303,6 +319,42 @@ struct mlx5_flow_root_namespace {
const struct mlx5_flow_cmds *cmds;
};
enum mlx5_fc_type {
MLX5_FC_TYPE_ACQUIRED = 0,
MLX5_FC_TYPE_LOCAL,
};
struct mlx5_fc_cache {
u64 packets;
u64 bytes;
u64 lastuse;
};
struct mlx5_fc {
u32 id;
bool aging;
enum mlx5_fc_type type;
struct mlx5_fc_bulk *bulk;
struct mlx5_fc_cache cache;
/* last{packets,bytes} are used for calculating deltas since last reading. */
u64 lastpackets;
u64 lastbytes;
};
struct mlx5_fc_bulk_hws_data {
struct mlx5hws_action *hws_action;
struct mutex lock; /* protects hws_action */
refcount_t hws_action_refcount;
};
struct mlx5_fc_bulk {
struct mlx5_fs_bulk fs_bulk;
u32 base_id;
struct mlx5_fc_bulk_hws_data hws_data;
struct mlx5_fc fcs[];
};
u32 mlx5_fc_get_base_id(struct mlx5_fc *counter);
int mlx5_init_fc_stats(struct mlx5_core_dev *dev);
void mlx5_cleanup_fc_stats(struct mlx5_core_dev *dev);
void mlx5_fc_queue_stats_work(struct mlx5_core_dev *dev,

View File

@ -44,28 +44,6 @@
#define MLX5_FC_POOL_MAX_THRESHOLD BIT(18)
#define MLX5_FC_POOL_USED_BUFF_RATIO 10
enum mlx5_fc_type {
MLX5_FC_TYPE_ACQUIRED = 0,
MLX5_FC_TYPE_LOCAL,
};
struct mlx5_fc_cache {
u64 packets;
u64 bytes;
u64 lastuse;
};
struct mlx5_fc {
u32 id;
bool aging;
enum mlx5_fc_type type;
struct mlx5_fc_bulk *bulk;
struct mlx5_fc_cache cache;
/* last{packets,bytes} are used for calculating deltas since last reading. */
u64 lastpackets;
u64 lastbytes;
};
struct mlx5_fc_stats {
struct xarray counters;
@ -434,13 +412,7 @@ void mlx5_fc_update_sampling_interval(struct mlx5_core_dev *dev,
fc_stats->sampling_interval);
}
/* Flow counter bluks */
struct mlx5_fc_bulk {
struct mlx5_fs_bulk fs_bulk;
u32 base_id;
struct mlx5_fc fcs[];
};
/* Flow counter bulks */
static void mlx5_fc_init(struct mlx5_fc *counter, struct mlx5_fc_bulk *bulk,
u32 id)
@ -449,7 +421,13 @@ static void mlx5_fc_init(struct mlx5_fc *counter, struct mlx5_fc_bulk *bulk,
counter->id = id;
}
static struct mlx5_fs_bulk *mlx5_fc_bulk_create(struct mlx5_core_dev *dev)
u32 mlx5_fc_get_base_id(struct mlx5_fc *counter)
{
return counter->bulk->base_id;
}
static struct mlx5_fs_bulk *mlx5_fc_bulk_create(struct mlx5_core_dev *dev,
void *pool_ctx)
{
enum mlx5_fc_bulk_alloc_bitmask alloc_bitmask;
struct mlx5_fc_bulk *fc_bulk;
@ -473,6 +451,8 @@ static struct mlx5_fs_bulk *mlx5_fc_bulk_create(struct mlx5_core_dev *dev)
for (i = 0; i < bulk_len; i++)
mlx5_fc_init(&fc_bulk->fcs[i], fc_bulk, base_id + i);
refcount_set(&fc_bulk->hws_data.hws_action_refcount, 0);
mutex_init(&fc_bulk->hws_data.lock);
return &fc_bulk->fs_bulk;
fs_bulk_cleanup:
@ -518,7 +498,7 @@ static const struct mlx5_fs_pool_ops mlx5_fc_pool_ops = {
static void
mlx5_fc_pool_init(struct mlx5_fs_pool *fc_pool, struct mlx5_core_dev *dev)
{
mlx5_fs_pool_init(fc_pool, dev, &mlx5_fc_pool_ops);
mlx5_fs_pool_init(fc_pool, dev, &mlx5_fc_pool_ops, NULL);
}
static void mlx5_fc_pool_cleanup(struct mlx5_fs_pool *fc_pool)

View File

@ -56,11 +56,12 @@ static int mlx5_fs_bulk_release_index(struct mlx5_fs_bulk *fs_bulk, int index)
}
void mlx5_fs_pool_init(struct mlx5_fs_pool *pool, struct mlx5_core_dev *dev,
const struct mlx5_fs_pool_ops *ops)
const struct mlx5_fs_pool_ops *ops, void *pool_ctx)
{
WARN_ON_ONCE(!ops || !ops->bulk_destroy || !ops->bulk_create ||
!ops->update_threshold);
pool->dev = dev;
pool->pool_ctx = pool_ctx;
mutex_init(&pool->pool_lock);
INIT_LIST_HEAD(&pool->fully_used);
INIT_LIST_HEAD(&pool->partially_used);
@ -91,7 +92,7 @@ mlx5_fs_pool_alloc_new_bulk(struct mlx5_fs_pool *fs_pool)
struct mlx5_core_dev *dev = fs_pool->dev;
struct mlx5_fs_bulk *new_bulk;
new_bulk = fs_pool->ops->bulk_create(dev);
new_bulk = fs_pool->ops->bulk_create(dev, fs_pool->pool_ctx);
if (new_bulk)
fs_pool->available_units += new_bulk->bulk_len;
fs_pool->ops->update_threshold(fs_pool);

View File

@ -21,7 +21,8 @@ struct mlx5_fs_pool;
struct mlx5_fs_pool_ops {
int (*bulk_destroy)(struct mlx5_core_dev *dev, struct mlx5_fs_bulk *bulk);
struct mlx5_fs_bulk * (*bulk_create)(struct mlx5_core_dev *dev);
struct mlx5_fs_bulk * (*bulk_create)(struct mlx5_core_dev *dev,
void *pool_ctx);
void (*update_threshold)(struct mlx5_fs_pool *pool);
};
@ -44,7 +45,7 @@ void mlx5_fs_bulk_cleanup(struct mlx5_fs_bulk *fs_bulk);
int mlx5_fs_bulk_get_free_amount(struct mlx5_fs_bulk *bulk);
void mlx5_fs_pool_init(struct mlx5_fs_pool *pool, struct mlx5_core_dev *dev,
const struct mlx5_fs_pool_ops *ops);
const struct mlx5_fs_pool_ops *ops, void *pool_ctx);
void mlx5_fs_pool_cleanup(struct mlx5_fs_pool *pool);
int mlx5_fs_pool_acquire_index(struct mlx5_fs_pool *fs_pool,
struct mlx5_fs_pool_index *pool_index);

View File

@ -148,8 +148,8 @@ static int hws_debug_dump_matcher(struct seq_file *f, struct mlx5hws_matcher *ma
matcher->match_ste.rtc_1_id,
(int)ste_1_id);
ste = &matcher->action_ste[0].ste;
ste_pool = matcher->action_ste[0].pool;
ste = &matcher->action_ste.ste;
ste_pool = matcher->action_ste.pool;
if (ste_pool) {
ste_0_id = mlx5hws_pool_chunk_get_base_id(ste_pool, ste);
if (tbl_type == MLX5HWS_TABLE_TYPE_FDB)
@ -171,10 +171,8 @@ static int hws_debug_dump_matcher(struct seq_file *f, struct mlx5hws_matcher *ma
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,
matcher->action_ste.rtc_0_id, (int)ste_0_id,
matcher->action_ste.rtc_1_id, (int)ste_1_id,
0,
mlx5hws_debug_icm_to_idx(icm_addr_0),
mlx5hws_debug_icm_to_idx(icm_addr_1));

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,80 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2025 NVIDIA Corporation & Affiliates */
#ifndef _MLX5_FS_HWS_
#define _MLX5_FS_HWS_
#include "mlx5hws.h"
#include "fs_hws_pools.h"
struct mlx5_fs_hws_actions_pool {
struct mlx5hws_action *tag_action;
struct mlx5hws_action *pop_vlan_action;
struct mlx5hws_action *push_vlan_action;
struct mlx5hws_action *drop_action;
struct mlx5hws_action *decapl2_action;
struct mlx5hws_action *remove_hdr_vlan_action;
struct mlx5_fs_pool insert_hdr_pool;
struct mlx5_fs_pool dl3tnltol2_pool;
struct xarray el2tol3tnl_pools;
struct xarray el2tol2tnl_pools;
struct xarray mh_pools;
struct xarray table_dests;
struct xarray vport_vhca_dests;
struct xarray vport_dests;
};
struct mlx5_fs_hws_context {
struct mlx5hws_context *hws_ctx;
struct mlx5_fs_hws_actions_pool hws_pool;
};
struct mlx5_fs_hws_table {
struct mlx5hws_table *hws_table;
bool miss_ft_set;
};
struct mlx5_fs_hws_action {
struct mlx5hws_action *hws_action;
struct mlx5_fs_pool *fs_pool;
struct mlx5_fs_hws_pr *pr_data;
struct mlx5_fs_hws_mh *mh_data;
};
struct mlx5_fs_hws_matcher {
struct mlx5hws_bwc_matcher *matcher;
};
struct mlx5_fs_hws_rule_action {
struct mlx5hws_action *action;
union {
struct mlx5_fc *counter;
};
};
struct mlx5_fs_hws_rule {
struct mlx5hws_bwc_rule *bwc_rule;
struct mlx5_fs_hws_rule_action *hws_fs_actions;
int num_fs_actions;
};
#ifdef CONFIG_MLX5_HW_STEERING
bool mlx5_fs_hws_is_supported(struct mlx5_core_dev *dev);
const struct mlx5_flow_cmds *mlx5_fs_cmd_get_hws_cmds(void);
#else
static inline bool mlx5_fs_hws_is_supported(struct mlx5_core_dev *dev)
{
return false;
}
static inline const struct mlx5_flow_cmds *mlx5_fs_cmd_get_hws_cmds(void)
{
return NULL;
}
#endif /* CONFIG_MLX5_HWS_STEERING */
#endif

View File

@ -0,0 +1,450 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2025 NVIDIA Corporation & Affiliates */
#include <mlx5_core.h>
#include "fs_hws_pools.h"
#define MLX5_FS_HWS_DEFAULT_BULK_LEN 65536
#define MLX5_FS_HWS_POOL_MAX_THRESHOLD BIT(18)
#define MLX5_FS_HWS_POOL_USED_BUFF_RATIO 10
static struct mlx5hws_action *
mlx5_fs_dl3tnltol2_bulk_action_create(struct mlx5hws_context *ctx)
{
struct mlx5hws_action_reformat_header reformat_hdr[2] = {};
u32 flags = MLX5HWS_ACTION_FLAG_HWS_FDB;
enum mlx5hws_action_type reformat_type;
u32 log_bulk_size;
reformat_type = MLX5HWS_ACTION_TYP_REFORMAT_TNL_L3_TO_L2;
reformat_hdr[MLX5_FS_DL3TNLTOL2_MAC_HDR_IDX].sz = ETH_HLEN;
reformat_hdr[MLX5_FS_DL3TNLTOL2_MAC_VLAN_HDR_IDX].sz = ETH_HLEN + VLAN_HLEN;
log_bulk_size = ilog2(MLX5_FS_HWS_DEFAULT_BULK_LEN);
return mlx5hws_action_create_reformat(ctx, reformat_type, 2,
reformat_hdr, log_bulk_size, flags);
}
static struct mlx5hws_action *
mlx5_fs_el2tol3tnl_bulk_action_create(struct mlx5hws_context *ctx, size_t data_size)
{
struct mlx5hws_action_reformat_header reformat_hdr = {};
u32 flags = MLX5HWS_ACTION_FLAG_HWS_FDB;
enum mlx5hws_action_type reformat_type;
u32 log_bulk_size;
reformat_type = MLX5HWS_ACTION_TYP_REFORMAT_L2_TO_TNL_L3;
reformat_hdr.sz = data_size;
log_bulk_size = ilog2(MLX5_FS_HWS_DEFAULT_BULK_LEN);
return mlx5hws_action_create_reformat(ctx, reformat_type, 1,
&reformat_hdr, log_bulk_size, flags);
}
static struct mlx5hws_action *
mlx5_fs_el2tol2tnl_bulk_action_create(struct mlx5hws_context *ctx, size_t data_size)
{
struct mlx5hws_action_reformat_header reformat_hdr = {};
u32 flags = MLX5HWS_ACTION_FLAG_HWS_FDB;
enum mlx5hws_action_type reformat_type;
u32 log_bulk_size;
reformat_type = MLX5HWS_ACTION_TYP_REFORMAT_L2_TO_TNL_L2;
reformat_hdr.sz = data_size;
log_bulk_size = ilog2(MLX5_FS_HWS_DEFAULT_BULK_LEN);
return mlx5hws_action_create_reformat(ctx, reformat_type, 1,
&reformat_hdr, log_bulk_size, flags);
}
static struct mlx5hws_action *
mlx5_fs_insert_hdr_bulk_action_create(struct mlx5hws_context *ctx)
{
struct mlx5hws_action_insert_header insert_hdr = {};
u32 flags = MLX5HWS_ACTION_FLAG_HWS_FDB;
u32 log_bulk_size;
log_bulk_size = ilog2(MLX5_FS_HWS_DEFAULT_BULK_LEN);
insert_hdr.hdr.sz = MLX5_FS_INSERT_HDR_VLAN_SIZE;
insert_hdr.anchor = MLX5_FS_INSERT_HDR_VLAN_ANCHOR;
insert_hdr.offset = MLX5_FS_INSERT_HDR_VLAN_OFFSET;
return mlx5hws_action_create_insert_header(ctx, 1, &insert_hdr,
log_bulk_size, flags);
}
static struct mlx5hws_action *
mlx5_fs_pr_bulk_action_create(struct mlx5_core_dev *dev,
struct mlx5_fs_hws_pr_pool_ctx *pr_pool_ctx)
{
struct mlx5_flow_root_namespace *root_ns;
struct mlx5hws_context *ctx;
size_t encap_data_size;
root_ns = mlx5_get_root_namespace(dev, MLX5_FLOW_NAMESPACE_FDB);
if (!root_ns || root_ns->mode != MLX5_FLOW_STEERING_MODE_HMFS)
return NULL;
ctx = root_ns->fs_hws_context.hws_ctx;
if (!ctx)
return NULL;
encap_data_size = pr_pool_ctx->encap_data_size;
switch (pr_pool_ctx->reformat_type) {
case MLX5HWS_ACTION_TYP_REFORMAT_TNL_L3_TO_L2:
return mlx5_fs_dl3tnltol2_bulk_action_create(ctx);
case MLX5HWS_ACTION_TYP_REFORMAT_L2_TO_TNL_L3:
return mlx5_fs_el2tol3tnl_bulk_action_create(ctx, encap_data_size);
case MLX5HWS_ACTION_TYP_REFORMAT_L2_TO_TNL_L2:
return mlx5_fs_el2tol2tnl_bulk_action_create(ctx, encap_data_size);
case MLX5HWS_ACTION_TYP_INSERT_HEADER:
return mlx5_fs_insert_hdr_bulk_action_create(ctx);
default:
return NULL;
}
return NULL;
}
static struct mlx5_fs_bulk *
mlx5_fs_hws_pr_bulk_create(struct mlx5_core_dev *dev, void *pool_ctx)
{
struct mlx5_fs_hws_pr_pool_ctx *pr_pool_ctx;
struct mlx5_fs_hws_pr_bulk *pr_bulk;
int bulk_len;
int i;
if (!pool_ctx)
return NULL;
pr_pool_ctx = pool_ctx;
bulk_len = MLX5_FS_HWS_DEFAULT_BULK_LEN;
pr_bulk = kvzalloc(struct_size(pr_bulk, prs_data, bulk_len), GFP_KERNEL);
if (!pr_bulk)
return NULL;
if (mlx5_fs_bulk_init(dev, &pr_bulk->fs_bulk, bulk_len))
goto free_pr_bulk;
for (i = 0; i < bulk_len; i++) {
pr_bulk->prs_data[i].bulk = pr_bulk;
pr_bulk->prs_data[i].offset = i;
}
pr_bulk->hws_action = mlx5_fs_pr_bulk_action_create(dev, pr_pool_ctx);
if (!pr_bulk->hws_action)
goto cleanup_fs_bulk;
return &pr_bulk->fs_bulk;
cleanup_fs_bulk:
mlx5_fs_bulk_cleanup(&pr_bulk->fs_bulk);
free_pr_bulk:
kvfree(pr_bulk);
return NULL;
}
static int
mlx5_fs_hws_pr_bulk_destroy(struct mlx5_core_dev *dev, struct mlx5_fs_bulk *fs_bulk)
{
struct mlx5_fs_hws_pr_bulk *pr_bulk;
pr_bulk = container_of(fs_bulk, struct mlx5_fs_hws_pr_bulk, fs_bulk);
if (mlx5_fs_bulk_get_free_amount(fs_bulk) < fs_bulk->bulk_len) {
mlx5_core_err(dev, "Freeing bulk before all reformats were released\n");
return -EBUSY;
}
mlx5hws_action_destroy(pr_bulk->hws_action);
mlx5_fs_bulk_cleanup(fs_bulk);
kvfree(pr_bulk);
return 0;
}
static void mlx5_hws_pool_update_threshold(struct mlx5_fs_pool *hws_pool)
{
hws_pool->threshold = min_t(int, MLX5_FS_HWS_POOL_MAX_THRESHOLD,
hws_pool->used_units / MLX5_FS_HWS_POOL_USED_BUFF_RATIO);
}
static const struct mlx5_fs_pool_ops mlx5_fs_hws_pr_pool_ops = {
.bulk_create = mlx5_fs_hws_pr_bulk_create,
.bulk_destroy = mlx5_fs_hws_pr_bulk_destroy,
.update_threshold = mlx5_hws_pool_update_threshold,
};
int mlx5_fs_hws_pr_pool_init(struct mlx5_fs_pool *pr_pool,
struct mlx5_core_dev *dev, size_t encap_data_size,
enum mlx5hws_action_type reformat_type)
{
struct mlx5_fs_hws_pr_pool_ctx *pr_pool_ctx;
if (reformat_type != MLX5HWS_ACTION_TYP_INSERT_HEADER &&
reformat_type != MLX5HWS_ACTION_TYP_REFORMAT_TNL_L3_TO_L2 &&
reformat_type != MLX5HWS_ACTION_TYP_REFORMAT_L2_TO_TNL_L3 &&
reformat_type != MLX5HWS_ACTION_TYP_REFORMAT_L2_TO_TNL_L2)
return -EOPNOTSUPP;
pr_pool_ctx = kzalloc(sizeof(*pr_pool_ctx), GFP_KERNEL);
if (!pr_pool_ctx)
return -ENOMEM;
pr_pool_ctx->reformat_type = reformat_type;
pr_pool_ctx->encap_data_size = encap_data_size;
mlx5_fs_pool_init(pr_pool, dev, &mlx5_fs_hws_pr_pool_ops, pr_pool_ctx);
return 0;
}
void mlx5_fs_hws_pr_pool_cleanup(struct mlx5_fs_pool *pr_pool)
{
struct mlx5_fs_hws_pr_pool_ctx *pr_pool_ctx;
mlx5_fs_pool_cleanup(pr_pool);
pr_pool_ctx = pr_pool->pool_ctx;
if (!pr_pool_ctx)
return;
kfree(pr_pool_ctx);
}
struct mlx5_fs_hws_pr *
mlx5_fs_hws_pr_pool_acquire_pr(struct mlx5_fs_pool *pr_pool)
{
struct mlx5_fs_pool_index pool_index = {};
struct mlx5_fs_hws_pr_bulk *pr_bulk;
int err;
err = mlx5_fs_pool_acquire_index(pr_pool, &pool_index);
if (err)
return ERR_PTR(err);
pr_bulk = container_of(pool_index.fs_bulk, struct mlx5_fs_hws_pr_bulk,
fs_bulk);
return &pr_bulk->prs_data[pool_index.index];
}
void mlx5_fs_hws_pr_pool_release_pr(struct mlx5_fs_pool *pr_pool,
struct mlx5_fs_hws_pr *pr_data)
{
struct mlx5_fs_bulk *fs_bulk = &pr_data->bulk->fs_bulk;
struct mlx5_fs_pool_index pool_index = {};
struct mlx5_core_dev *dev = pr_pool->dev;
pool_index.fs_bulk = fs_bulk;
pool_index.index = pr_data->offset;
if (mlx5_fs_pool_release_index(pr_pool, &pool_index))
mlx5_core_warn(dev, "Attempted to release packet reformat which is not acquired\n");
}
struct mlx5hws_action *mlx5_fs_hws_pr_get_action(struct mlx5_fs_hws_pr *pr_data)
{
return pr_data->bulk->hws_action;
}
static struct mlx5hws_action *
mlx5_fs_mh_bulk_action_create(struct mlx5hws_context *ctx,
struct mlx5hws_action_mh_pattern *pattern)
{
u32 flags = MLX5HWS_ACTION_FLAG_HWS_FDB;
u32 log_bulk_size;
log_bulk_size = ilog2(MLX5_FS_HWS_DEFAULT_BULK_LEN);
return mlx5hws_action_create_modify_header(ctx, 1, pattern,
log_bulk_size, flags);
}
static struct mlx5_fs_bulk *
mlx5_fs_hws_mh_bulk_create(struct mlx5_core_dev *dev, void *pool_ctx)
{
struct mlx5hws_action_mh_pattern *pattern;
struct mlx5_flow_root_namespace *root_ns;
struct mlx5_fs_hws_mh_bulk *mh_bulk;
struct mlx5hws_context *ctx;
int bulk_len;
if (!pool_ctx)
return NULL;
root_ns = mlx5_get_root_namespace(dev, MLX5_FLOW_NAMESPACE_FDB);
if (!root_ns || root_ns->mode != MLX5_FLOW_STEERING_MODE_HMFS)
return NULL;
ctx = root_ns->fs_hws_context.hws_ctx;
if (!ctx)
return NULL;
pattern = pool_ctx;
bulk_len = MLX5_FS_HWS_DEFAULT_BULK_LEN;
mh_bulk = kvzalloc(struct_size(mh_bulk, mhs_data, bulk_len), GFP_KERNEL);
if (!mh_bulk)
return NULL;
if (mlx5_fs_bulk_init(dev, &mh_bulk->fs_bulk, bulk_len))
goto free_mh_bulk;
for (int i = 0; i < bulk_len; i++) {
mh_bulk->mhs_data[i].bulk = mh_bulk;
mh_bulk->mhs_data[i].offset = i;
}
mh_bulk->hws_action = mlx5_fs_mh_bulk_action_create(ctx, pattern);
if (!mh_bulk->hws_action)
goto cleanup_fs_bulk;
return &mh_bulk->fs_bulk;
cleanup_fs_bulk:
mlx5_fs_bulk_cleanup(&mh_bulk->fs_bulk);
free_mh_bulk:
kvfree(mh_bulk);
return NULL;
}
static int
mlx5_fs_hws_mh_bulk_destroy(struct mlx5_core_dev *dev,
struct mlx5_fs_bulk *fs_bulk)
{
struct mlx5_fs_hws_mh_bulk *mh_bulk;
mh_bulk = container_of(fs_bulk, struct mlx5_fs_hws_mh_bulk, fs_bulk);
if (mlx5_fs_bulk_get_free_amount(fs_bulk) < fs_bulk->bulk_len) {
mlx5_core_err(dev, "Freeing bulk before all modify header were released\n");
return -EBUSY;
}
mlx5hws_action_destroy(mh_bulk->hws_action);
mlx5_fs_bulk_cleanup(fs_bulk);
kvfree(mh_bulk);
return 0;
}
static const struct mlx5_fs_pool_ops mlx5_fs_hws_mh_pool_ops = {
.bulk_create = mlx5_fs_hws_mh_bulk_create,
.bulk_destroy = mlx5_fs_hws_mh_bulk_destroy,
.update_threshold = mlx5_hws_pool_update_threshold,
};
int mlx5_fs_hws_mh_pool_init(struct mlx5_fs_pool *fs_hws_mh_pool,
struct mlx5_core_dev *dev,
struct mlx5hws_action_mh_pattern *pattern)
{
struct mlx5hws_action_mh_pattern *pool_pattern;
pool_pattern = kzalloc(sizeof(*pool_pattern), GFP_KERNEL);
if (!pool_pattern)
return -ENOMEM;
pool_pattern->data = kmemdup(pattern->data, pattern->sz, GFP_KERNEL);
if (!pool_pattern->data) {
kfree(pool_pattern);
return -ENOMEM;
}
pool_pattern->sz = pattern->sz;
mlx5_fs_pool_init(fs_hws_mh_pool, dev, &mlx5_fs_hws_mh_pool_ops,
pool_pattern);
return 0;
}
void mlx5_fs_hws_mh_pool_cleanup(struct mlx5_fs_pool *fs_hws_mh_pool)
{
struct mlx5hws_action_mh_pattern *pool_pattern;
mlx5_fs_pool_cleanup(fs_hws_mh_pool);
pool_pattern = fs_hws_mh_pool->pool_ctx;
if (!pool_pattern)
return;
kfree(pool_pattern->data);
kfree(pool_pattern);
}
struct mlx5_fs_hws_mh *
mlx5_fs_hws_mh_pool_acquire_mh(struct mlx5_fs_pool *mh_pool)
{
struct mlx5_fs_pool_index pool_index = {};
struct mlx5_fs_hws_mh_bulk *mh_bulk;
int err;
err = mlx5_fs_pool_acquire_index(mh_pool, &pool_index);
if (err)
return ERR_PTR(err);
mh_bulk = container_of(pool_index.fs_bulk, struct mlx5_fs_hws_mh_bulk,
fs_bulk);
return &mh_bulk->mhs_data[pool_index.index];
}
void mlx5_fs_hws_mh_pool_release_mh(struct mlx5_fs_pool *mh_pool,
struct mlx5_fs_hws_mh *mh_data)
{
struct mlx5_fs_bulk *fs_bulk = &mh_data->bulk->fs_bulk;
struct mlx5_fs_pool_index pool_index = {};
struct mlx5_core_dev *dev = mh_pool->dev;
pool_index.fs_bulk = fs_bulk;
pool_index.index = mh_data->offset;
if (mlx5_fs_pool_release_index(mh_pool, &pool_index))
mlx5_core_warn(dev, "Attempted to release modify header which is not acquired\n");
}
bool mlx5_fs_hws_mh_pool_match(struct mlx5_fs_pool *mh_pool,
struct mlx5hws_action_mh_pattern *pattern)
{
struct mlx5hws_action_mh_pattern *pool_pattern;
int num_actions, i;
pool_pattern = mh_pool->pool_ctx;
if (WARN_ON_ONCE(!pool_pattern))
return false;
if (pattern->sz != pool_pattern->sz)
return false;
num_actions = pattern->sz / MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto);
for (i = 0; i < num_actions; i++) {
if ((__force __be32)pattern->data[i] !=
(__force __be32)pool_pattern->data[i])
return false;
}
return true;
}
struct mlx5hws_action *mlx5_fc_get_hws_action(struct mlx5hws_context *ctx,
struct mlx5_fc *counter)
{
u32 flags = MLX5HWS_ACTION_FLAG_HWS_FDB | MLX5HWS_ACTION_FLAG_SHARED;
struct mlx5_fc_bulk *fc_bulk = counter->bulk;
struct mlx5_fc_bulk_hws_data *fc_bulk_hws;
fc_bulk_hws = &fc_bulk->hws_data;
/* try avoid locking if not necessary */
if (refcount_inc_not_zero(&fc_bulk_hws->hws_action_refcount))
return fc_bulk_hws->hws_action;
mutex_lock(&fc_bulk_hws->lock);
if (refcount_inc_not_zero(&fc_bulk_hws->hws_action_refcount)) {
mutex_unlock(&fc_bulk_hws->lock);
return fc_bulk_hws->hws_action;
}
fc_bulk_hws->hws_action =
mlx5hws_action_create_counter(ctx, fc_bulk->base_id, flags);
if (!fc_bulk_hws->hws_action) {
mutex_unlock(&fc_bulk_hws->lock);
return NULL;
}
refcount_set(&fc_bulk_hws->hws_action_refcount, 1);
mutex_unlock(&fc_bulk_hws->lock);
return fc_bulk_hws->hws_action;
}
void mlx5_fc_put_hws_action(struct mlx5_fc *counter)
{
struct mlx5_fc_bulk_hws_data *fc_bulk_hws = &counter->bulk->hws_data;
/* try avoid locking if not necessary */
if (refcount_dec_not_one(&fc_bulk_hws->hws_action_refcount))
return;
mutex_lock(&fc_bulk_hws->lock);
if (!refcount_dec_and_test(&fc_bulk_hws->hws_action_refcount)) {
mutex_unlock(&fc_bulk_hws->lock);
return;
}
mlx5hws_action_destroy(fc_bulk_hws->hws_action);
fc_bulk_hws->hws_action = NULL;
mutex_unlock(&fc_bulk_hws->lock);
}

View File

@ -0,0 +1,73 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2025 NVIDIA Corporation & Affiliates */
#ifndef __MLX5_FS_HWS_POOLS_H__
#define __MLX5_FS_HWS_POOLS_H__
#include <linux/if_vlan.h>
#include "fs_pool.h"
#include "fs_core.h"
#define MLX5_FS_INSERT_HDR_VLAN_ANCHOR MLX5_REFORMAT_CONTEXT_ANCHOR_MAC_START
#define MLX5_FS_INSERT_HDR_VLAN_OFFSET offsetof(struct vlan_ethhdr, h_vlan_proto)
#define MLX5_FS_INSERT_HDR_VLAN_SIZE sizeof(struct vlan_hdr)
enum {
MLX5_FS_DL3TNLTOL2_MAC_HDR_IDX = 0,
MLX5_FS_DL3TNLTOL2_MAC_VLAN_HDR_IDX,
};
struct mlx5_fs_hws_pr {
struct mlx5_fs_hws_pr_bulk *bulk;
u32 offset;
u8 hdr_idx;
u8 *data;
size_t data_size;
};
struct mlx5_fs_hws_pr_bulk {
struct mlx5_fs_bulk fs_bulk;
struct mlx5hws_action *hws_action;
struct mlx5_fs_hws_pr prs_data[];
};
struct mlx5_fs_hws_pr_pool_ctx {
enum mlx5hws_action_type reformat_type;
size_t encap_data_size;
};
struct mlx5_fs_hws_mh {
struct mlx5_fs_hws_mh_bulk *bulk;
u32 offset;
u8 *data;
};
struct mlx5_fs_hws_mh_bulk {
struct mlx5_fs_bulk fs_bulk;
struct mlx5_fs_pool *mh_pool;
struct mlx5hws_action *hws_action;
struct mlx5_fs_hws_mh mhs_data[];
};
int mlx5_fs_hws_pr_pool_init(struct mlx5_fs_pool *pr_pool,
struct mlx5_core_dev *dev, size_t encap_data_size,
enum mlx5hws_action_type reformat_type);
void mlx5_fs_hws_pr_pool_cleanup(struct mlx5_fs_pool *pr_pool);
struct mlx5_fs_hws_pr *mlx5_fs_hws_pr_pool_acquire_pr(struct mlx5_fs_pool *pr_pool);
void mlx5_fs_hws_pr_pool_release_pr(struct mlx5_fs_pool *pr_pool,
struct mlx5_fs_hws_pr *pr_data);
struct mlx5hws_action *mlx5_fs_hws_pr_get_action(struct mlx5_fs_hws_pr *pr_data);
int mlx5_fs_hws_mh_pool_init(struct mlx5_fs_pool *fs_hws_mh_pool,
struct mlx5_core_dev *dev,
struct mlx5hws_action_mh_pattern *pattern);
void mlx5_fs_hws_mh_pool_cleanup(struct mlx5_fs_pool *fs_hws_mh_pool);
struct mlx5_fs_hws_mh *mlx5_fs_hws_mh_pool_acquire_mh(struct mlx5_fs_pool *mh_pool);
void mlx5_fs_hws_mh_pool_release_mh(struct mlx5_fs_pool *mh_pool,
struct mlx5_fs_hws_mh *mh_data);
bool mlx5_fs_hws_mh_pool_match(struct mlx5_fs_pool *mh_pool,
struct mlx5hws_action_mh_pattern *pattern);
struct mlx5hws_action *mlx5_fc_get_hws_action(struct mlx5hws_context *ctx,
struct mlx5_fc *counter);
void mlx5_fc_put_hws_action(struct mlx5_fc *counter);
#endif /* __MLX5_FS_HWS_POOLS_H__ */

View File

@ -39,7 +39,6 @@
#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)
{

View File

@ -200,7 +200,7 @@ static void hws_matcher_set_rtc_attr_sz(struct mlx5hws_matcher *matcher,
enum mlx5hws_matcher_rtc_type rtc_type,
bool is_mirror)
{
struct mlx5hws_pool_chunk *ste = &matcher->action_ste[MLX5HWS_ACTION_STE_IDX_ANY].ste;
struct mlx5hws_pool_chunk *ste = &matcher->action_ste.ste;
enum mlx5hws_matcher_flow_src flow_src = matcher->attr.optimize_flow_src;
bool is_match_rtc = rtc_type == HWS_MATCHER_RTC_TYPE_MATCH;
@ -217,8 +217,7 @@ static void hws_matcher_set_rtc_attr_sz(struct mlx5hws_matcher *matcher,
}
static int hws_matcher_create_rtc(struct mlx5hws_matcher *matcher,
enum mlx5hws_matcher_rtc_type rtc_type,
u8 action_ste_selector)
enum mlx5hws_matcher_rtc_type rtc_type)
{
struct mlx5hws_matcher_attr *attr = &matcher->attr;
struct mlx5hws_cmd_rtc_create_attr rtc_attr = {0};
@ -278,14 +277,19 @@ static int hws_matcher_create_rtc(struct mlx5hws_matcher *matcher,
break;
case HWS_MATCHER_RTC_TYPE_STE_ARRAY:
action_ste = &matcher->action_ste[action_ste_selector];
action_ste = &matcher->action_ste;
rtc_0_id = &action_ste->rtc_0_id;
rtc_1_id = &action_ste->rtc_1_id;
ste_pool = action_ste->pool;
ste = &action_ste->ste;
/* Action RTC size calculation:
* log((max number of rules in matcher) *
* (max number of action STEs per rule) *
* (2 to support writing new STEs for update rule))
*/
ste->order = ilog2(roundup_pow_of_two(action_ste->max_stes)) +
attr->table.sz_row_log;
attr->table.sz_row_log + 1;
rtc_attr.log_size = ste->order;
rtc_attr.log_depth = 0;
rtc_attr.update_index_mode = MLX5_IFC_RTC_STE_UPDATE_MODE_BY_OFFSET;
@ -350,8 +354,7 @@ static int hws_matcher_create_rtc(struct mlx5hws_matcher *matcher,
}
static void hws_matcher_destroy_rtc(struct mlx5hws_matcher *matcher,
enum mlx5hws_matcher_rtc_type rtc_type,
u8 action_ste_selector)
enum mlx5hws_matcher_rtc_type rtc_type)
{
struct mlx5hws_matcher_action_ste *action_ste;
struct mlx5hws_table *tbl = matcher->tbl;
@ -367,7 +370,7 @@ static void hws_matcher_destroy_rtc(struct mlx5hws_matcher *matcher,
ste = &matcher->match_ste.ste;
break;
case HWS_MATCHER_RTC_TYPE_STE_ARRAY:
action_ste = &matcher->action_ste[action_ste_selector];
action_ste = &matcher->action_ste;
rtc_0_id = action_ste->rtc_0_id;
rtc_1_id = action_ste->rtc_1_id;
ste_pool = action_ste->pool;
@ -458,20 +461,13 @@ static int hws_matcher_resize_init(struct mlx5hws_matcher *src_matcher)
if (!resize_data)
return -ENOMEM;
resize_data->max_stes = src_matcher->action_ste[MLX5HWS_ACTION_STE_IDX_ANY].max_stes;
resize_data->max_stes = src_matcher->action_ste.max_stes;
resize_data->action_ste[0].stc = src_matcher->action_ste[0].stc;
resize_data->action_ste[0].rtc_0_id = src_matcher->action_ste[0].rtc_0_id;
resize_data->action_ste[0].rtc_1_id = src_matcher->action_ste[0].rtc_1_id;
resize_data->action_ste[0].pool = src_matcher->action_ste[0].max_stes ?
src_matcher->action_ste[0].pool :
NULL;
resize_data->action_ste[1].stc = src_matcher->action_ste[1].stc;
resize_data->action_ste[1].rtc_0_id = src_matcher->action_ste[1].rtc_0_id;
resize_data->action_ste[1].rtc_1_id = src_matcher->action_ste[1].rtc_1_id;
resize_data->action_ste[1].pool = src_matcher->action_ste[1].max_stes ?
src_matcher->action_ste[1].pool :
NULL;
resize_data->stc = src_matcher->action_ste.stc;
resize_data->rtc_0_id = src_matcher->action_ste.rtc_0_id;
resize_data->rtc_1_id = src_matcher->action_ste.rtc_1_id;
resize_data->pool = src_matcher->action_ste.max_stes ?
src_matcher->action_ste.pool : NULL;
/* Place the new resized matcher on the dst matcher's list */
list_add(&resize_data->list_node, &src_matcher->resize_dst->resize_data);
@ -504,109 +500,31 @@ static void hws_matcher_resize_uninit(struct mlx5hws_matcher *matcher)
if (resize_data->max_stes) {
mlx5hws_action_free_single_stc(matcher->tbl->ctx,
matcher->tbl->type,
&resize_data->action_ste[1].stc);
mlx5hws_action_free_single_stc(matcher->tbl->ctx,
matcher->tbl->type,
&resize_data->action_ste[0].stc);
&resize_data->stc);
if (matcher->tbl->type == MLX5HWS_TABLE_TYPE_FDB) {
if (matcher->tbl->type == MLX5HWS_TABLE_TYPE_FDB)
mlx5hws_cmd_rtc_destroy(matcher->tbl->ctx->mdev,
resize_data->action_ste[1].rtc_1_id);
mlx5hws_cmd_rtc_destroy(matcher->tbl->ctx->mdev,
resize_data->action_ste[0].rtc_1_id);
}
resize_data->rtc_1_id);
mlx5hws_cmd_rtc_destroy(matcher->tbl->ctx->mdev,
resize_data->action_ste[1].rtc_0_id);
mlx5hws_cmd_rtc_destroy(matcher->tbl->ctx->mdev,
resize_data->action_ste[0].rtc_0_id);
if (resize_data->action_ste[MLX5HWS_ACTION_STE_IDX_ANY].pool) {
mlx5hws_pool_destroy(resize_data->action_ste[1].pool);
mlx5hws_pool_destroy(resize_data->action_ste[0].pool);
}
resize_data->rtc_0_id);
if (resize_data->pool)
mlx5hws_pool_destroy(resize_data->pool);
}
kfree(resize_data);
}
}
static int
hws_matcher_bind_at_idx(struct mlx5hws_matcher *matcher, u8 action_ste_selector)
static int hws_matcher_bind_at(struct mlx5hws_matcher *matcher)
{
bool is_jumbo = mlx5hws_matcher_mt_is_jumbo(matcher->mt);
struct mlx5hws_cmd_stc_modify_attr stc_attr = {0};
struct mlx5hws_matcher_action_ste *action_ste;
struct mlx5hws_table *tbl = matcher->tbl;
struct mlx5hws_pool_attr pool_attr = {0};
struct mlx5hws_context *ctx = tbl->ctx;
int ret;
action_ste = &matcher->action_ste[action_ste_selector];
/* Allocate action STE mempool */
pool_attr.table_type = tbl->type;
pool_attr.pool_type = MLX5HWS_POOL_TYPE_STE;
pool_attr.flags = MLX5HWS_POOL_FLAGS_FOR_STE_ACTION_POOL;
pool_attr.alloc_log_sz = ilog2(roundup_pow_of_two(action_ste->max_stes)) +
matcher->attr.table.sz_row_log;
hws_matcher_set_pool_attr(&pool_attr, matcher);
action_ste->pool = mlx5hws_pool_create(ctx, &pool_attr);
if (!action_ste->pool) {
mlx5hws_err(ctx, "Failed to create action ste pool\n");
return -EINVAL;
}
/* Allocate action RTC */
ret = hws_matcher_create_rtc(matcher, HWS_MATCHER_RTC_TYPE_STE_ARRAY, action_ste_selector);
if (ret) {
mlx5hws_err(ctx, "Failed to create action RTC\n");
goto free_ste_pool;
}
/* Allocate STC for jumps to STE */
stc_attr.action_offset = MLX5HWS_ACTION_OFFSET_HIT;
stc_attr.action_type = MLX5_IFC_STC_ACTION_TYPE_JUMP_TO_STE_TABLE;
stc_attr.reparse_mode = MLX5_IFC_STC_REPARSE_IGNORE;
stc_attr.ste_table.ste = action_ste->ste;
stc_attr.ste_table.ste_pool = action_ste->pool;
stc_attr.ste_table.match_definer_id = ctx->caps->trivial_match_definer;
ret = mlx5hws_action_alloc_single_stc(ctx, &stc_attr, tbl->type,
&action_ste->stc);
if (ret) {
mlx5hws_err(ctx, "Failed to create action jump to table STC\n");
goto free_rtc;
}
return 0;
free_rtc:
hws_matcher_destroy_rtc(matcher, HWS_MATCHER_RTC_TYPE_STE_ARRAY, action_ste_selector);
free_ste_pool:
mlx5hws_pool_destroy(action_ste->pool);
return ret;
}
static void hws_matcher_unbind_at_idx(struct mlx5hws_matcher *matcher, u8 action_ste_selector)
{
struct mlx5hws_matcher_action_ste *action_ste;
struct mlx5hws_table *tbl = matcher->tbl;
action_ste = &matcher->action_ste[action_ste_selector];
if (!action_ste->max_stes ||
matcher->flags & MLX5HWS_MATCHER_FLAGS_COLLISION ||
mlx5hws_matcher_is_in_resize(matcher))
return;
mlx5hws_action_free_single_stc(tbl->ctx, tbl->type, &action_ste->stc);
hws_matcher_destroy_rtc(matcher, HWS_MATCHER_RTC_TYPE_STE_ARRAY, action_ste_selector);
mlx5hws_pool_destroy(action_ste->pool);
}
static int hws_matcher_bind_at(struct mlx5hws_matcher *matcher)
{
bool is_jumbo = mlx5hws_matcher_mt_is_jumbo(matcher->mt);
struct mlx5hws_table *tbl = matcher->tbl;
struct mlx5hws_context *ctx = tbl->ctx;
u32 required_stes;
u8 max_stes = 0;
int i, ret;
@ -633,28 +551,70 @@ static int hws_matcher_bind_at(struct mlx5hws_matcher *matcher)
if (!max_stes)
return 0;
matcher->action_ste[0].max_stes = max_stes;
matcher->action_ste[1].max_stes = max_stes;
matcher->action_ste.max_stes = max_stes;
ret = hws_matcher_bind_at_idx(matcher, 0);
if (ret)
return ret;
action_ste = &matcher->action_ste;
ret = hws_matcher_bind_at_idx(matcher, 1);
if (ret)
goto free_at_0;
/* Allocate action STE mempool */
pool_attr.table_type = tbl->type;
pool_attr.pool_type = MLX5HWS_POOL_TYPE_STE;
pool_attr.flags = MLX5HWS_POOL_FLAGS_FOR_STE_ACTION_POOL;
/* Pool size is similar to action RTC size */
pool_attr.alloc_log_sz = ilog2(roundup_pow_of_two(action_ste->max_stes)) +
matcher->attr.table.sz_row_log + 1;
hws_matcher_set_pool_attr(&pool_attr, matcher);
action_ste->pool = mlx5hws_pool_create(ctx, &pool_attr);
if (!action_ste->pool) {
mlx5hws_err(ctx, "Failed to create action ste pool\n");
return -EINVAL;
}
/* Allocate action RTC */
ret = hws_matcher_create_rtc(matcher, HWS_MATCHER_RTC_TYPE_STE_ARRAY);
if (ret) {
mlx5hws_err(ctx, "Failed to create action RTC\n");
goto free_ste_pool;
}
/* Allocate STC for jumps to STE */
stc_attr.action_offset = MLX5HWS_ACTION_OFFSET_HIT;
stc_attr.action_type = MLX5_IFC_STC_ACTION_TYPE_JUMP_TO_STE_TABLE;
stc_attr.reparse_mode = MLX5_IFC_STC_REPARSE_IGNORE;
stc_attr.ste_table.ste = action_ste->ste;
stc_attr.ste_table.ste_pool = action_ste->pool;
stc_attr.ste_table.match_definer_id = ctx->caps->trivial_match_definer;
ret = mlx5hws_action_alloc_single_stc(ctx, &stc_attr, tbl->type,
&action_ste->stc);
if (ret) {
mlx5hws_err(ctx, "Failed to create action jump to table STC\n");
goto free_rtc;
}
return 0;
free_at_0:
hws_matcher_unbind_at_idx(matcher, 0);
free_rtc:
hws_matcher_destroy_rtc(matcher, HWS_MATCHER_RTC_TYPE_STE_ARRAY);
free_ste_pool:
mlx5hws_pool_destroy(action_ste->pool);
return ret;
}
static void hws_matcher_unbind_at(struct mlx5hws_matcher *matcher)
{
hws_matcher_unbind_at_idx(matcher, 1);
hws_matcher_unbind_at_idx(matcher, 0);
struct mlx5hws_matcher_action_ste *action_ste;
struct mlx5hws_table *tbl = matcher->tbl;
action_ste = &matcher->action_ste;
if (!action_ste->max_stes ||
matcher->flags & MLX5HWS_MATCHER_FLAGS_COLLISION ||
mlx5hws_matcher_is_in_resize(matcher))
return;
mlx5hws_action_free_single_stc(tbl->ctx, tbl->type, &action_ste->stc);
hws_matcher_destroy_rtc(matcher, HWS_MATCHER_RTC_TYPE_STE_ARRAY);
mlx5hws_pool_destroy(action_ste->pool);
}
static int hws_matcher_bind_mt(struct mlx5hws_matcher *matcher)
@ -802,7 +762,7 @@ static int hws_matcher_create_and_connect(struct mlx5hws_matcher *matcher)
goto unbind_at;
/* Allocate the RTC for the new matcher */
ret = hws_matcher_create_rtc(matcher, HWS_MATCHER_RTC_TYPE_MATCH, 0);
ret = hws_matcher_create_rtc(matcher, HWS_MATCHER_RTC_TYPE_MATCH);
if (ret)
goto destroy_end_ft;
@ -814,7 +774,7 @@ static int hws_matcher_create_and_connect(struct mlx5hws_matcher *matcher)
return 0;
destroy_rtc:
hws_matcher_destroy_rtc(matcher, HWS_MATCHER_RTC_TYPE_MATCH, 0);
hws_matcher_destroy_rtc(matcher, HWS_MATCHER_RTC_TYPE_MATCH);
destroy_end_ft:
hws_matcher_destroy_end_ft(matcher);
unbind_at:
@ -828,7 +788,7 @@ static void hws_matcher_destroy_and_disconnect(struct mlx5hws_matcher *matcher)
{
hws_matcher_resize_uninit(matcher);
hws_matcher_disconnect(matcher);
hws_matcher_destroy_rtc(matcher, HWS_MATCHER_RTC_TYPE_MATCH, 0);
hws_matcher_destroy_rtc(matcher, HWS_MATCHER_RTC_TYPE_MATCH);
hws_matcher_destroy_end_ft(matcher);
hws_matcher_unbind_at(matcher);
hws_matcher_unbind_mt(matcher);
@ -962,10 +922,9 @@ int mlx5hws_matcher_attach_at(struct mlx5hws_matcher *matcher,
return ret;
required_stes = at->num_of_action_stes - (!is_jumbo || at->only_term);
if (matcher->action_ste[MLX5HWS_ACTION_STE_IDX_ANY].max_stes < required_stes) {
if (matcher->action_ste.max_stes < required_stes) {
mlx5hws_dbg(ctx, "Required STEs [%d] exceeds initial action template STE [%d]\n",
required_stes,
matcher->action_ste[MLX5HWS_ACTION_STE_IDX_ANY].max_stes);
required_stes, matcher->action_ste.max_stes);
return -ENOMEM;
}
@ -1149,8 +1108,7 @@ static int hws_matcher_resize_precheck(struct mlx5hws_matcher *src_matcher,
return -EINVAL;
}
if (src_matcher->action_ste[MLX5HWS_ACTION_STE_IDX_ANY].max_stes >
dst_matcher->action_ste[0].max_stes) {
if (src_matcher->action_ste.max_stes > dst_matcher->action_ste.max_stes) {
mlx5hws_err(ctx, "Src/dst matcher max STEs mismatch\n");
return -EINVAL;
}

View File

@ -52,15 +52,11 @@ struct mlx5hws_matcher_action_ste {
u8 max_stes;
};
struct mlx5hws_matcher_resize_data_node {
struct mlx5hws_matcher_resize_data {
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;
};
@ -78,7 +74,7 @@ struct mlx5hws_matcher {
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 mlx5hws_matcher_action_ste action_ste;
struct list_head list_node;
struct list_head resize_data;
};

View File

@ -129,27 +129,18 @@ static void hws_rule_gen_comp(struct mlx5hws_send_engine *queue,
static void
hws_rule_save_resize_info(struct mlx5hws_rule *rule,
struct mlx5hws_send_ste_attr *ste_attr,
bool is_update)
struct mlx5hws_send_ste_attr *ste_attr)
{
if (!mlx5hws_matcher_is_resizable(rule->matcher))
return;
if (likely(!is_update)) {
/* resize_info might already exist (if we're in update flow) */
if (likely(!rule->resize_info)) {
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,
@ -204,15 +195,14 @@ hws_rule_load_delete_info(struct mlx5hws_rule *rule,
}
}
static int hws_rule_alloc_action_ste_idx(struct mlx5hws_rule *rule,
u8 action_ste_selector)
static int hws_rule_alloc_action_ste(struct mlx5hws_rule *rule)
{
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];
action_ste = &matcher->action_ste;
ste.order = ilog2(roundup_pow_of_two(action_ste->max_stes));
ret = mlx5hws_pool_chunk_alloc(action_ste->pool, &ste);
if (unlikely(ret)) {
@ -220,68 +210,29 @@ static int hws_rule_alloc_action_ste_idx(struct mlx5hws_rule *rule,
"Failed to allocate STE for rule actions");
return ret;
}
rule->action_ste_idx = ste.offset;
rule->action_ste.pool = matcher->action_ste.pool;
rule->action_ste.num_stes = matcher->action_ste.max_stes;
rule->action_ste.index = ste.offset;
return 0;
}
static void hws_rule_free_action_ste_idx(struct mlx5hws_rule *rule,
u8 action_ste_selector)
void mlx5hws_rule_free_action_ste(struct mlx5hws_rule_action_ste_info *action_ste)
{
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;
}
if (!action_ste->num_stes)
return;
/* 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;
ste.order = ilog2(roundup_pow_of_two(action_ste->num_stes));
ste.offset = action_ste->index;
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);
}
/* This release is safe only when the rule match STE was deleted
* (when the rule is being deleted) or replaced with the new STE that
* isn't pointing to old action STEs (when the rule is being updated).
*/
mlx5hws_pool_chunk_free(action_ste->pool, &ste);
}
static void hws_rule_create_init(struct mlx5hws_rule *rule,
@ -298,14 +249,24 @@ static void hws_rule_create_init(struct mlx5hws_rule *rule,
/* In update we use these rtc's */
rule->rtc_0 = 0;
rule->rtc_1 = 0;
rule->action_ste_selector = 0;
rule->action_ste.pool = NULL;
rule->action_ste.num_stes = 0;
rule->action_ste.index = -1;
rule->status = MLX5HWS_RULE_STATUS_CREATING;
} else {
rule->action_ste_selector = !rule->action_ste_selector;
rule->status = MLX5HWS_RULE_STATUS_UPDATING;
}
/* Initialize the old action STE info - shallow-copy action_ste.
* In create flow this will set old_action_ste fields to initial values.
* In update flow this will save the existing action STE info,
* so that we will later use it to free old STEs.
*/
rule->old_action_ste = rule->action_ste;
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;
@ -316,7 +277,7 @@ static void hws_rule_create_init(struct mlx5hws_rule *rule,
/* Init default action apply */
apply->tbl_type = tbl->type;
apply->common_res = &ctx->common_res;
apply->jump_to_action_stc = matcher->action_ste[0].stc.offset;
apply->jump_to_action_stc = matcher->action_ste.stc.offset;
apply->require_dep = 0;
}
@ -332,8 +293,6 @@ static void hws_rule_move_init(struct mlx5hws_rule *rule,
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;
}
@ -394,21 +353,17 @@ static int hws_rule_create_hws(struct mlx5hws_rule *rule,
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;
}
ret = hws_rule_alloc_action_ste(rule);
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;
ste_attr.rtc_0 = dep_wqe->rtc_0 ? matcher->action_ste.rtc_0_id : 0;
ste_attr.rtc_1 = dep_wqe->rtc_1 ? matcher->action_ste.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;
ste_attr.direct_index = rule->action_ste.index + action_stes;
apply.next_direct_idx = ste_attr.direct_index;
} else {
apply.next_direct_idx = 0;
@ -459,7 +414,7 @@ static int hws_rule_create_hws(struct mlx5hws_rule *rule,
if (!is_update)
hws_rule_save_delete_info(rule, &ste_attr);
hws_rule_save_resize_info(rule, &ste_attr, is_update);
hws_rule_save_resize_info(rule, &ste_attr);
mlx5hws_send_engine_inc_rule(queue);
if (!attr->burst)
@ -480,7 +435,10 @@ static void hws_rule_destroy_failed_hws(struct mlx5hws_rule *rule,
attr->user_data, MLX5HWS_RULE_STATUS_DELETED);
/* Rule failed now we can safely release action STEs */
mlx5hws_rule_free_action_ste(rule);
mlx5hws_rule_free_action_ste(&rule->action_ste);
/* Perhaps the rule failed updating - release old action STEs as well */
mlx5hws_rule_free_action_ste(&rule->old_action_ste);
/* Clear complex tag */
hws_rule_clear_delete_info(rule);
@ -517,7 +475,8 @@ static int hws_rule_destroy_hws(struct mlx5hws_rule *rule,
}
/* Rule is not completed yet */
if (rule->status == MLX5HWS_RULE_STATUS_CREATING)
if (rule->status == MLX5HWS_RULE_STATUS_CREATING ||
rule->status == MLX5HWS_RULE_STATUS_UPDATING)
return -EBUSY;
/* Rule failed and doesn't require cleanup */
@ -534,7 +493,7 @@ static int hws_rule_destroy_hws(struct mlx5hws_rule *rule,
hws_rule_gen_comp(queue, rule, false,
attr->user_data, MLX5HWS_RULE_STATUS_DELETED);
mlx5hws_rule_free_action_ste(rule);
mlx5hws_rule_free_action_ste(&rule->action_ste);
mlx5hws_rule_clear_resize_info(rule);
return 0;
}

View File

@ -15,6 +15,8 @@ enum mlx5hws_rule_status {
MLX5HWS_RULE_STATUS_UNKNOWN,
MLX5HWS_RULE_STATUS_CREATING,
MLX5HWS_RULE_STATUS_CREATED,
MLX5HWS_RULE_STATUS_UPDATING,
MLX5HWS_RULE_STATUS_UPDATED,
MLX5HWS_RULE_STATUS_DELETING,
MLX5HWS_RULE_STATUS_DELETED,
MLX5HWS_RULE_STATUS_FAILING,
@ -41,13 +43,17 @@ struct mlx5hws_rule_match_tag {
};
};
struct mlx5hws_rule_action_ste_info {
struct mlx5hws_pool *pool;
int index; /* STE array index */
u8 num_stes;
};
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 */
};
@ -58,18 +64,18 @@ struct mlx5hws_rule {
struct mlx5hws_rule_match_tag tag;
struct mlx5hws_rule_resize_info *resize_info;
};
struct mlx5hws_rule_action_ste_info action_ste;
struct mlx5hws_rule_action_ste_info old_action_ste;
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);
void mlx5hws_rule_free_action_ste(struct mlx5hws_rule_action_ste_info *action_ste);
int mlx5hws_rule_move_hws_remove(struct mlx5hws_rule *rule,
void *queue, void *user_data);

View File

@ -377,17 +377,25 @@ static void hws_send_engine_update_rule(struct mlx5hws_send_engine *queue,
*status = MLX5HWS_FLOW_OP_ERROR;
} else {
/* Increase the status, this only works on good flow as the enum
* is arrange it away creating -> created -> deleting -> deleted
/* Increase the status, this only works on good flow as
* the enum is arranged this way:
* - creating -> created
* - updating -> updated
* - deleting -> deleted
*/
priv->rule->status++;
*status = MLX5HWS_FLOW_OP_SUCCESS;
/* Rule was deleted now we can safely release action STEs
* and clear resize info
*/
if (priv->rule->status == MLX5HWS_RULE_STATUS_DELETED) {
mlx5hws_rule_free_action_ste(priv->rule);
/* Rule was deleted, now we can safely release
* action STEs and clear resize info
*/
mlx5hws_rule_free_action_ste(&priv->rule->action_ste);
mlx5hws_rule_clear_resize_info(priv->rule);
} else if (priv->rule->status == MLX5HWS_RULE_STATUS_UPDATED) {
/* Rule was updated, free the old action STEs */
mlx5hws_rule_free_action_ste(&priv->rule->old_action_ste);
/* Update completed - move the rule back to "created" */
priv->rule->status = MLX5HWS_RULE_STATUS_CREATED;
}
}
}

View File

@ -7025,6 +7025,7 @@ struct mlx5_ifc_alloc_packet_reformat_context_out_bits {
enum {
MLX5_REFORMAT_CONTEXT_ANCHOR_MAC_START = 0x1,
MLX5_REFORMAT_CONTEXT_ANCHOR_VLAN_START = 0x2,
MLX5_REFORMAT_CONTEXT_ANCHOR_IP_START = 0x7,
MLX5_REFORMAT_CONTEXT_ANCHOR_TCP_UDP_START = 0x9,
};