#include "internal.h"
static bool hws_matcher_requires_col_tbl(u8 log_num_of_rules)
{
return log_num_of_rules > MLX5HWS_MATCHER_ASSURED_RULES_TH;
}
static u8 hws_matcher_rules_to_tbl_depth(u8 log_num_of_rules)
{
if (hws_matcher_requires_col_tbl(log_num_of_rules))
return MLX5HWS_MATCHER_ASSURED_MAIN_TBL_DEPTH;
return min(log_num_of_rules, MLX5HWS_MATCHER_ASSURED_COL_TBL_DEPTH);
}
static void hws_matcher_destroy_end_ft(struct mlx5hws_matcher *matcher)
{
mlx5hws_table_destroy_default_ft(matcher->tbl, matcher->end_ft_id);
}
int mlx5hws_matcher_update_end_ft_isolated(struct mlx5hws_table *tbl,
u32 miss_ft_id)
{
struct mlx5hws_matcher *tmp_matcher;
if (list_empty(&tbl->matchers_list))
return -EINVAL;
list_for_each_entry(tmp_matcher, &tbl->matchers_list, list_node)
tmp_matcher->attr.isolated_matcher_end_ft_id = miss_ft_id;
tmp_matcher = list_last_entry(&tbl->matchers_list,
struct mlx5hws_matcher,
list_node);
return mlx5hws_table_ft_set_next_ft(tbl->ctx,
tmp_matcher->end_ft_id,
tbl->fw_ft_type,
miss_ft_id);
}
static int hws_matcher_connect_end_ft_isolated(struct mlx5hws_matcher *matcher)
{
struct mlx5hws_table *tbl = matcher->tbl;
u32 end_ft_id;
int ret;
ret = mlx5hws_table_ft_set_next_rtc(tbl->ctx,
matcher->end_ft_id,
matcher->tbl->fw_ft_type,
0, 0);
if (ret) {
mlx5hws_err(tbl->ctx, "Isolated matcher: failed to reset FT's next RTCs\n");
return ret;
}
end_ft_id = matcher->attr.isolated_matcher_end_ft_id;
ret = mlx5hws_table_ft_set_next_ft(tbl->ctx,
matcher->end_ft_id,
matcher->tbl->fw_ft_type,
end_ft_id);
if (ret) {
mlx5hws_err(tbl->ctx, "Isolated matcher: failed to set FT's miss_ft_id\n");
return ret;
}
return 0;
}
static int hws_matcher_create_end_ft_isolated(struct mlx5hws_matcher *matcher)
{
struct mlx5hws_table *tbl = matcher->tbl;
int ret;
ret = mlx5hws_table_create_default_ft(tbl->ctx->mdev,
tbl,
0,
&matcher->end_ft_id);
if (ret) {
mlx5hws_err(tbl->ctx, "Isolated matcher: failed to create end flow table\n");
return ret;
}
ret = hws_matcher_connect_end_ft_isolated(matcher);
if (ret) {
mlx5hws_err(tbl->ctx, "Isolated matcher: failed to connect end FT\n");
goto destroy_default_ft;
}
return 0;
destroy_default_ft:
mlx5hws_table_destroy_default_ft(tbl, matcher->end_ft_id);
return ret;
}
static int hws_matcher_create_end_ft(struct mlx5hws_matcher *matcher)
{
struct mlx5hws_table *tbl = matcher->tbl;
int ret;
if (mlx5hws_matcher_is_isolated(matcher))
ret = hws_matcher_create_end_ft_isolated(matcher);
else
ret = mlx5hws_table_create_default_ft(tbl->ctx->mdev,
tbl,
0,
&matcher->end_ft_id);
if (ret) {
mlx5hws_err(tbl->ctx, "Failed to create matcher end flow table\n");
return ret;
}
return 0;
}
static int hws_matcher_connect_isolated_first(struct mlx5hws_matcher *matcher)
{
struct mlx5hws_table *tbl = matcher->tbl;
struct mlx5hws_context *ctx = tbl->ctx;
int ret;
ret = mlx5hws_table_ft_set_next_rtc(ctx,
tbl->ft_id,
tbl->fw_ft_type,
matcher->match_ste.rtc_0_id,
matcher->match_ste.rtc_1_id);
if (ret) {
mlx5hws_err(ctx, "Isolated matcher: failed to connect start FT to match RTC\n");
return ret;
}
ret = mlx5hws_table_ft_set_default_next_ft(tbl, tbl->ft_id);
if (ret) {
mlx5hws_err(ctx, "Isolated matcher: failed to reset table ft default miss\n");
return ret;
}
list_add(&matcher->list_node, &tbl->matchers_list);
return ret;
}
static int hws_matcher_connect_isolated_last(struct mlx5hws_matcher *matcher)
{
struct mlx5hws_table *tbl = matcher->tbl;
struct mlx5hws_context *ctx = tbl->ctx;
struct mlx5hws_matcher *last;
int ret;
last = list_last_entry(&tbl->matchers_list,
struct mlx5hws_matcher,
list_node);
ret = mlx5hws_table_ft_set_next_rtc(ctx,
last->end_ft_id,
tbl->fw_ft_type,
matcher->match_ste.rtc_0_id,
matcher->match_ste.rtc_1_id);
if (ret) {
mlx5hws_err(ctx,
"Isolated matcher: failed to connect matcher end_ft to new match RTC\n");
return ret;
}
ret = mlx5hws_table_ft_set_default_next_ft(tbl, last->end_ft_id);
if (ret) {
mlx5hws_err(ctx, "Isolated matcher: failed to reset matcher ft default miss\n");
return ret;
}
list_add(&matcher->list_node, &last->list_node);
return 0;
}
static int hws_matcher_connect_isolated(struct mlx5hws_matcher *matcher)
{
if (list_empty(&matcher->tbl->matchers_list))
return hws_matcher_connect_isolated_first(matcher);
return hws_matcher_connect_isolated_last(matcher);
}
static int hws_matcher_connect(struct mlx5hws_matcher *matcher)
{
struct mlx5hws_table *tbl = matcher->tbl;
struct mlx5hws_context *ctx = tbl->ctx;
struct mlx5hws_matcher *prev = NULL;
struct mlx5hws_matcher *next = NULL;
struct mlx5hws_matcher *tmp_matcher;
int ret;
if (mlx5hws_matcher_is_isolated(matcher))
return hws_matcher_connect_isolated(matcher);
if (list_empty(&tbl->matchers_list)) {
list_add(&matcher->list_node, &tbl->matchers_list);
goto connect;
}
list_for_each_entry(tmp_matcher, &tbl->matchers_list, list_node) {
if (tmp_matcher->attr.priority > matcher->attr.priority) {
next = tmp_matcher;
break;
}
prev = tmp_matcher;
}
if (next)
list_add_tail(&matcher->list_node, &next->list_node);
else
list_add(&matcher->list_node, &prev->list_node);
connect:
if (next) {
ret = mlx5hws_table_ft_set_next_rtc(ctx,
matcher->end_ft_id,
tbl->fw_ft_type,
next->match_ste.rtc_0_id,
next->match_ste.rtc_1_id);
if (ret) {
mlx5hws_err(ctx, "Failed to connect new matcher to next RTC\n");
goto remove_from_list;
}
} else {
ret = mlx5hws_table_connect_to_miss_table(tbl, tbl->default_miss.miss_tbl);
if (ret) {
mlx5hws_err(ctx, "Failed connect new matcher to miss_tbl\n");
goto remove_from_list;
}
}
ret = mlx5hws_table_ft_set_next_rtc(ctx,
prev ? prev->end_ft_id : tbl->ft_id,
tbl->fw_ft_type,
matcher->match_ste.rtc_0_id,
matcher->match_ste.rtc_1_id);
if (ret) {
mlx5hws_err(ctx, "Failed to connect new matcher to previous FT\n");
goto remove_from_list;
}
ret = mlx5hws_table_ft_set_default_next_ft(tbl, prev ? prev->end_ft_id : tbl->ft_id);
if (ret) {
mlx5hws_err(ctx, "Failed to reset matcher ft default miss\n");
goto remove_from_list;
}
if (!prev) {
ret = mlx5hws_table_update_connected_miss_tables(tbl);
if (ret) {
mlx5hws_err(ctx, "Fatal error, failed to update connected miss table\n");
goto remove_from_list;
}
}
return 0;
remove_from_list:
list_del_init(&matcher->list_node);
return ret;
}
static int hws_matcher_disconnect_isolated(struct mlx5hws_matcher *matcher)
{
struct mlx5hws_matcher *first, *last, *prev, *next;
struct mlx5hws_table *tbl = matcher->tbl;
struct mlx5hws_context *ctx = tbl->ctx;
u32 end_ft_id;
int ret;
first = list_first_entry(&tbl->matchers_list,
struct mlx5hws_matcher,
list_node);
last = list_last_entry(&tbl->matchers_list,
struct mlx5hws_matcher,
list_node);
prev = list_prev_entry(matcher, list_node);
next = list_next_entry(matcher, list_node);
list_del_init(&matcher->list_node);
if (first == last) {
ret = mlx5hws_table_ft_set_next_rtc(ctx,
tbl->ft_id,
tbl->fw_ft_type,
0, 0);
if (ret) {
mlx5hws_err(tbl->ctx, "Isolated matcher: failed to reset FT's next RTCs\n");
return ret;
}
end_ft_id = matcher->attr.isolated_matcher_end_ft_id;
ret = mlx5hws_table_ft_set_next_ft(tbl->ctx,
tbl->ft_id,
tbl->fw_ft_type,
end_ft_id);
if (ret) {
mlx5hws_err(tbl->ctx, "Isolated matcher: failed to set FT's miss_ft_id\n");
return ret;
}
return 0;
}
if (matcher == first) {
if (!next)
return -EINVAL;
return mlx5hws_table_ft_set_next_rtc(ctx,
tbl->ft_id,
tbl->fw_ft_type,
next->match_ste.rtc_0_id,
next->match_ste.rtc_1_id);
}
if (matcher == last) {
if (!prev)
return -EINVAL;
return hws_matcher_connect_end_ft_isolated(prev);
}
if (!prev || !next ||
!(matcher->flags & MLX5HWS_MATCHER_FLAGS_COLLISION))
return -EINVAL;
return mlx5hws_table_ft_set_next_rtc(ctx,
prev->end_ft_id,
tbl->fw_ft_type,
next->match_ste.rtc_0_id,
next->match_ste.rtc_1_id);
}
static int hws_matcher_disconnect(struct mlx5hws_matcher *matcher)
{
struct mlx5hws_matcher *next = NULL, *prev = NULL;
struct mlx5hws_table *tbl = matcher->tbl;
u32 prev_ft_id = tbl->ft_id;
int ret;
if (mlx5hws_matcher_is_isolated(matcher))
return hws_matcher_disconnect_isolated(matcher);
if (!list_is_first(&matcher->list_node, &tbl->matchers_list)) {
prev = list_prev_entry(matcher, list_node);
prev_ft_id = prev->end_ft_id;
}
if (!list_is_last(&matcher->list_node, &tbl->matchers_list))
next = list_next_entry(matcher, list_node);
list_del_init(&matcher->list_node);
if (next) {
ret = mlx5hws_table_ft_set_next_rtc(tbl->ctx,
prev_ft_id,
tbl->fw_ft_type,
next->match_ste.rtc_0_id,
next->match_ste.rtc_1_id);
if (ret) {
mlx5hws_err(tbl->ctx, "Fatal error, failed to disconnect matcher\n");
return ret;
}
} else {
ret = mlx5hws_table_connect_to_miss_table(tbl, tbl->default_miss.miss_tbl);
if (ret) {
mlx5hws_err(tbl->ctx, "Fatal error, failed to disconnect last matcher\n");
return ret;
}
}
if (prev_ft_id == tbl->ft_id) {
ret = mlx5hws_table_update_connected_miss_tables(tbl);
if (ret) {
mlx5hws_err(tbl->ctx,
"Fatal error, failed to update connected miss table\n");
return ret;
}
}
ret = mlx5hws_table_ft_set_default_next_ft(tbl, prev_ft_id);
if (ret) {
mlx5hws_err(tbl->ctx, "Fatal error, failed to restore matcher ft default miss\n");
return ret;
}
return 0;
}
static void hws_matcher_set_rtc_attr_sz(struct mlx5hws_matcher *matcher,
struct mlx5hws_cmd_rtc_create_attr *rtc_attr,
bool is_mirror)
{
enum mlx5hws_matcher_flow_src flow_src = matcher->attr.optimize_flow_src;
if ((flow_src == MLX5HWS_MATCHER_FLOW_SRC_VPORT && !is_mirror) ||
(flow_src == MLX5HWS_MATCHER_FLOW_SRC_WIRE && is_mirror)) {
rtc_attr->log_size = 0;
rtc_attr->log_depth = 0;
}
}
static int hws_matcher_create_rtc(struct mlx5hws_matcher *matcher)
{
struct mlx5hws_matcher_attr *attr = &matcher->attr;
struct mlx5hws_cmd_rtc_create_attr rtc_attr = {0};
struct mlx5hws_match_template *mt = matcher->mt;
struct mlx5hws_context *ctx = matcher->tbl->ctx;
union mlx5hws_matcher_size *size_rx, *size_tx;
struct mlx5hws_table *tbl = matcher->tbl;
u32 obj_id;
int ret;
size_rx = &attr->size[MLX5HWS_MATCHER_SIZE_TYPE_RX];
size_tx = &attr->size[MLX5HWS_MATCHER_SIZE_TYPE_TX];
rtc_attr.log_size = size_rx->table.sz_row_log;
rtc_attr.log_depth = size_rx->table.sz_col_log;
rtc_attr.is_frst_jumbo = mlx5hws_matcher_mt_is_jumbo(mt);
rtc_attr.is_scnd_range = 0;
rtc_attr.miss_ft_id = matcher->end_ft_id;
if (attr->insert_mode == MLX5HWS_MATCHER_INSERT_BY_HASH) {
rtc_attr.update_index_mode =
MLX5_IFC_RTC_STE_UPDATE_MODE_BY_HASH;
rtc_attr.match_definer_0 = mlx5hws_definer_get_id(mt->definer);
} else if (attr->insert_mode == MLX5HWS_MATCHER_INSERT_BY_INDEX) {
rtc_attr.update_index_mode =
MLX5_IFC_RTC_STE_UPDATE_MODE_BY_OFFSET;
rtc_attr.num_hash_definer = 1;
if (attr->distribute_mode ==
MLX5HWS_MATCHER_DISTRIBUTE_BY_HASH) {
rtc_attr.access_index_mode =
MLX5_IFC_RTC_STE_ACCESS_MODE_BY_HASH;
rtc_attr.match_definer_0 =
mlx5hws_definer_get_id(mt->definer);
} else if (attr->distribute_mode ==
MLX5HWS_MATCHER_DISTRIBUTE_BY_LINEAR) {
rtc_attr.access_index_mode =
MLX5_IFC_RTC_STE_ACCESS_MODE_LINEAR;
rtc_attr.match_definer_0 =
ctx->caps->linear_match_definer;
}
}
rtc_attr.pd = ctx->pd_num;
rtc_attr.ste_base = matcher->match_ste.ste_0_base;
rtc_attr.reparse_mode = mlx5hws_context_get_reparse_mode(ctx);
rtc_attr.table_type = mlx5hws_table_get_res_fw_ft_type(tbl->type, false);
hws_matcher_set_rtc_attr_sz(matcher, &rtc_attr, false);
obj_id = mlx5hws_pool_get_base_id(ctx->stc_pool);
rtc_attr.stc_base = obj_id;
ret = mlx5hws_cmd_rtc_create(ctx->mdev, &rtc_attr,
&matcher->match_ste.rtc_0_id);
if (ret) {
mlx5hws_err(ctx, "Failed to create matcher RTC\n");
return ret;
}
if (tbl->type == MLX5HWS_TABLE_TYPE_FDB) {
rtc_attr.log_size = size_tx->table.sz_row_log;
rtc_attr.log_depth = size_tx->table.sz_col_log;
rtc_attr.ste_base = matcher->match_ste.ste_1_base;
rtc_attr.table_type = mlx5hws_table_get_res_fw_ft_type(tbl->type, true);
obj_id = mlx5hws_pool_get_base_mirror_id(ctx->stc_pool);
rtc_attr.stc_base = obj_id;
hws_matcher_set_rtc_attr_sz(matcher, &rtc_attr, true);
ret = mlx5hws_cmd_rtc_create(ctx->mdev, &rtc_attr,
&matcher->match_ste.rtc_1_id);
if (ret) {
mlx5hws_err(ctx, "Failed to create mirror matcher RTC\n");
goto destroy_rtc_0;
}
}
return 0;
destroy_rtc_0:
mlx5hws_cmd_rtc_destroy(ctx->mdev, matcher->match_ste.rtc_0_id);
return ret;
}
static void hws_matcher_destroy_rtc(struct mlx5hws_matcher *matcher)
{
struct mlx5_core_dev *mdev = matcher->tbl->ctx->mdev;
if (matcher->tbl->type == MLX5HWS_TABLE_TYPE_FDB)
mlx5hws_cmd_rtc_destroy(mdev, matcher->match_ste.rtc_1_id);
mlx5hws_cmd_rtc_destroy(mdev, matcher->match_ste.rtc_0_id);
}
static int
hws_matcher_check_attr_sz(struct mlx5hws_cmd_query_caps *caps,
struct mlx5hws_matcher *matcher)
{
struct mlx5hws_matcher_attr *attr = &matcher->attr;
struct mlx5hws_context *ctx = matcher->tbl->ctx;
union mlx5hws_matcher_size *size;
int i;
for (i = 0; i < 2; i++) {
size = &attr->size[i];
if (size->table.sz_col_log > caps->rtc_log_depth_max) {
mlx5hws_err(ctx, "Matcher depth exceeds limit %d\n",
caps->rtc_log_depth_max);
return -EOPNOTSUPP;
}
if (size->table.sz_col_log + size->table.sz_row_log >
caps->ste_alloc_log_max) {
mlx5hws_err(ctx,
"Total matcher size exceeds limit %d\n",
caps->ste_alloc_log_max);
return -EOPNOTSUPP;
}
if (size->table.sz_col_log + size->table.sz_row_log <
caps->ste_alloc_log_gran) {
mlx5hws_err(ctx, "Total matcher size below limit %d\n",
caps->ste_alloc_log_gran);
return -EOPNOTSUPP;
}
}
return 0;
}
static int hws_matcher_check_and_process_at(struct mlx5hws_matcher *matcher,
struct mlx5hws_action_template *at)
{
struct mlx5hws_context *ctx = matcher->tbl->ctx;
bool valid;
int ret;
valid = mlx5hws_action_check_combo(ctx, at->action_type_arr, matcher->tbl->type);
if (!valid) {
mlx5hws_err(ctx, "Invalid combination in action template\n");
return -EINVAL;
}
ret = mlx5hws_action_template_process(at);
if (ret) {
mlx5hws_err(ctx, "Failed to process action template\n");
return ret;
}
return 0;
}
static int hws_matcher_bind_at(struct mlx5hws_matcher *matcher)
{
bool is_jumbo = mlx5hws_matcher_mt_is_jumbo(matcher->mt);
struct mlx5hws_context *ctx = matcher->tbl->ctx;
u8 required_stes, max_stes;
int i, ret;
if (matcher->flags & MLX5HWS_MATCHER_FLAGS_COLLISION)
return 0;
max_stes = 0;
for (i = 0; i < matcher->num_of_at; i++) {
struct mlx5hws_action_template *at = &matcher->at[i];
ret = hws_matcher_check_and_process_at(matcher, at);
if (ret) {
mlx5hws_err(ctx, "Invalid at %d", i);
return ret;
}
required_stes = at->num_of_action_stes - (!is_jumbo || at->only_term);
max_stes = max(max_stes, required_stes);
}
matcher->num_of_action_stes = max_stes;
return 0;
}
static void hws_matcher_set_ip_version_match(struct mlx5hws_matcher *matcher)
{
int i;
for (i = 0; i < matcher->mt->fc_sz; i++) {
switch (matcher->mt->fc[i].fname) {
case MLX5HWS_DEFINER_FNAME_ETH_TYPE_O:
matcher->matches_outer_ethertype = 1;
break;
case MLX5HWS_DEFINER_FNAME_ETH_L3_TYPE_O:
matcher->matches_outer_ip_version = 1;
break;
case MLX5HWS_DEFINER_FNAME_ETH_TYPE_I:
matcher->matches_inner_ethertype = 1;
break;
case MLX5HWS_DEFINER_FNAME_ETH_L3_TYPE_I:
matcher->matches_inner_ip_version = 1;
break;
default:
break;
}
}
}
static int hws_matcher_bind_mt(struct mlx5hws_matcher *matcher)
{
struct mlx5hws_cmd_ste_create_attr ste_attr = {};
struct mlx5hws_context *ctx = matcher->tbl->ctx;
union mlx5hws_matcher_size *size;
int ret;
if (!(matcher->flags & MLX5HWS_MATCHER_FLAGS_COLLISION)) {
ret = mlx5hws_definer_mt_init(ctx, matcher->mt);
if (ret) {
if (ret == -E2BIG)
mlx5hws_err(ctx, "Failed to set matcher templates with match definers\n");
return ret;
}
}
hws_matcher_set_ip_version_match(matcher);
ste_attr.table_type = FS_FT_FDB_RX;
size = &matcher->attr.size[MLX5HWS_MATCHER_SIZE_TYPE_RX];
ste_attr.log_obj_range =
matcher->attr.optimize_flow_src ==
MLX5HWS_MATCHER_FLOW_SRC_VPORT ?
0 : size->table.sz_col_log + size->table.sz_row_log;
ret = mlx5hws_cmd_ste_create(ctx->mdev, &ste_attr,
&matcher->match_ste.ste_0_base);
if (ret) {
mlx5hws_err(ctx, "Failed to allocate RX STE range (%d)\n", ret);
goto uninit_match_definer;
}
ste_attr.table_type = FS_FT_FDB_TX;
size = &matcher->attr.size[MLX5HWS_MATCHER_SIZE_TYPE_TX];
ste_attr.log_obj_range =
matcher->attr.optimize_flow_src ==
MLX5HWS_MATCHER_FLOW_SRC_WIRE ?
0 : size->table.sz_col_log + size->table.sz_row_log;
ret = mlx5hws_cmd_ste_create(ctx->mdev, &ste_attr,
&matcher->match_ste.ste_1_base);
if (ret) {
mlx5hws_err(ctx, "Failed to allocate TX STE range (%d)\n", ret);
goto destroy_rx_ste_range;
}
return 0;
destroy_rx_ste_range:
mlx5hws_cmd_ste_destroy(ctx->mdev, matcher->match_ste.ste_0_base);
uninit_match_definer:
if (!(matcher->flags & MLX5HWS_MATCHER_FLAGS_COLLISION))
mlx5hws_definer_mt_uninit(ctx, matcher->mt);
return ret;
}
static void hws_matcher_unbind_mt(struct mlx5hws_matcher *matcher)
{
struct mlx5hws_context *ctx = matcher->tbl->ctx;
mlx5hws_cmd_ste_destroy(ctx->mdev, matcher->match_ste.ste_1_base);
mlx5hws_cmd_ste_destroy(ctx->mdev, matcher->match_ste.ste_0_base);
if (!(matcher->flags & MLX5HWS_MATCHER_FLAGS_COLLISION))
mlx5hws_definer_mt_uninit(ctx, matcher->mt);
}
static int
hws_matcher_validate_insert_mode(struct mlx5hws_cmd_query_caps *caps,
struct mlx5hws_matcher *matcher)
{
struct mlx5hws_matcher_attr *attr = &matcher->attr;
struct mlx5hws_context *ctx = matcher->tbl->ctx;
union mlx5hws_matcher_size *size_rx, *size_tx;
size_rx = &matcher->attr.size[MLX5HWS_MATCHER_SIZE_TYPE_RX];
size_tx = &matcher->attr.size[MLX5HWS_MATCHER_SIZE_TYPE_TX];
switch (attr->insert_mode) {
case MLX5HWS_MATCHER_INSERT_BY_HASH:
if (matcher->attr.distribute_mode != MLX5HWS_MATCHER_DISTRIBUTE_BY_HASH) {
mlx5hws_err(ctx, "Invalid matcher distribute mode\n");
return -EOPNOTSUPP;
}
break;
case MLX5HWS_MATCHER_INSERT_BY_INDEX:
if (size_rx->table.sz_col_log || size_tx->table.sz_col_log) {
mlx5hws_err(ctx, "Matcher with INSERT_BY_INDEX supports only Nx1 table size\n");
return -EOPNOTSUPP;
}
if (attr->distribute_mode == MLX5HWS_MATCHER_DISTRIBUTE_BY_HASH) {
if (!caps->rtc_hash_split_table) {
mlx5hws_err(ctx, "FW doesn't support insert by index and hash distribute\n");
return -EOPNOTSUPP;
}
} else if (attr->distribute_mode == MLX5HWS_MATCHER_DISTRIBUTE_BY_LINEAR) {
if (!caps->rtc_linear_lookup_table ||
!IS_BIT_SET(caps->access_index_mode,
MLX5_IFC_RTC_STE_ACCESS_MODE_LINEAR)) {
mlx5hws_err(ctx, "FW doesn't support insert by index and linear distribute\n");
return -EOPNOTSUPP;
}
if (size_rx->table.sz_row_log >
MLX5_IFC_RTC_LINEAR_LOOKUP_TBL_LOG_MAX ||
size_tx->table.sz_row_log >
MLX5_IFC_RTC_LINEAR_LOOKUP_TBL_LOG_MAX) {
mlx5hws_err(ctx, "Matcher with linear distribute: rows exceed limit %d",
MLX5_IFC_RTC_LINEAR_LOOKUP_TBL_LOG_MAX);
return -EOPNOTSUPP;
}
} else {
mlx5hws_err(ctx, "Matcher has unsupported distribute mode\n");
return -EOPNOTSUPP;
}
break;
default:
mlx5hws_err(ctx, "Matcher has unsupported insert mode\n");
return -EOPNOTSUPP;
}
return 0;
}
static int
hws_matcher_process_attr(struct mlx5hws_cmd_query_caps *caps,
struct mlx5hws_matcher *matcher)
{
struct mlx5hws_matcher_attr *attr = &matcher->attr;
union mlx5hws_matcher_size *size_rx, *size_tx;
size_rx = &attr->size[MLX5HWS_MATCHER_SIZE_TYPE_RX];
size_tx = &attr->size[MLX5HWS_MATCHER_SIZE_TYPE_TX];
if (hws_matcher_validate_insert_mode(caps, matcher))
return -EOPNOTSUPP;
if (matcher->tbl->type != MLX5HWS_TABLE_TYPE_FDB && attr->optimize_flow_src) {
mlx5hws_err(matcher->tbl->ctx, "NIC domain doesn't support flow_src\n");
return -EOPNOTSUPP;
}
if (attr->mode == MLX5HWS_MATCHER_RESOURCE_MODE_RULE &&
attr->insert_mode == MLX5HWS_MATCHER_INSERT_BY_HASH) {
size_rx->table.sz_col_log =
hws_matcher_rules_to_tbl_depth(size_rx->rule.num_log);
size_tx->table.sz_col_log =
hws_matcher_rules_to_tbl_depth(size_tx->rule.num_log);
}
matcher->flags |= attr->resizable ? MLX5HWS_MATCHER_FLAGS_RESIZABLE : 0;
matcher->flags |= attr->isolated_matcher_end_ft_id ?
MLX5HWS_MATCHER_FLAGS_ISOLATED : 0;
return hws_matcher_check_attr_sz(caps, matcher);
}
static int hws_matcher_create_and_connect(struct mlx5hws_matcher *matcher)
{
int ret;
ret = hws_matcher_bind_mt(matcher);
if (ret)
return ret;
ret = hws_matcher_bind_at(matcher);
if (ret)
goto unbind_mt;
ret = hws_matcher_create_end_ft(matcher);
if (ret)
goto unbind_mt;
ret = hws_matcher_create_rtc(matcher);
if (ret)
goto destroy_end_ft;
ret = hws_matcher_connect(matcher);
if (ret)
goto destroy_rtc;
return 0;
destroy_rtc:
hws_matcher_destroy_rtc(matcher);
destroy_end_ft:
hws_matcher_destroy_end_ft(matcher);
unbind_mt:
hws_matcher_unbind_mt(matcher);
return ret;
}
static void hws_matcher_destroy_and_disconnect(struct mlx5hws_matcher *matcher)
{
hws_matcher_disconnect(matcher);
hws_matcher_destroy_rtc(matcher);
hws_matcher_destroy_end_ft(matcher);
hws_matcher_unbind_mt(matcher);
}
static int
hws_matcher_create_col_matcher(struct mlx5hws_matcher *matcher)
{
struct mlx5hws_context *ctx = matcher->tbl->ctx;
union mlx5hws_matcher_size *size_rx, *size_tx;
struct mlx5hws_matcher *col_matcher;
int i, ret;
size_rx = &matcher->attr.size[MLX5HWS_MATCHER_SIZE_TYPE_RX];
size_tx = &matcher->attr.size[MLX5HWS_MATCHER_SIZE_TYPE_TX];
if (matcher->attr.mode != MLX5HWS_MATCHER_RESOURCE_MODE_RULE ||
matcher->attr.insert_mode == MLX5HWS_MATCHER_INSERT_BY_INDEX)
return 0;
if (!hws_matcher_requires_col_tbl(size_rx->rule.num_log) &&
!hws_matcher_requires_col_tbl(size_tx->rule.num_log))
return 0;
col_matcher = kzalloc_obj(*matcher);
if (!col_matcher)
return -ENOMEM;
col_matcher->tbl = matcher->tbl;
col_matcher->mt = matcher->mt;
col_matcher->at = matcher->at;
col_matcher->num_of_at = matcher->num_of_at;
col_matcher->num_of_mt = matcher->num_of_mt;
col_matcher->attr.priority = matcher->attr.priority;
col_matcher->flags = matcher->flags;
col_matcher->flags |= MLX5HWS_MATCHER_FLAGS_COLLISION;
col_matcher->attr.mode = MLX5HWS_MATCHER_RESOURCE_MODE_HTABLE;
col_matcher->attr.optimize_flow_src = matcher->attr.optimize_flow_src;
for (i = 0; i < 2; i++) {
union mlx5hws_matcher_size *dst = &col_matcher->attr.size[i];
union mlx5hws_matcher_size *src = &matcher->attr.size[i];
dst->table.sz_row_log = src->rule.num_log;
dst->table.sz_col_log = MLX5HWS_MATCHER_ASSURED_COL_TBL_DEPTH;
if (dst->table.sz_row_log > MLX5HWS_MATCHER_ASSURED_ROW_RATIO)
dst->table.sz_row_log -=
MLX5HWS_MATCHER_ASSURED_ROW_RATIO;
}
col_matcher->attr.max_num_of_at_attach = matcher->attr.max_num_of_at_attach;
col_matcher->attr.isolated_matcher_end_ft_id =
matcher->attr.isolated_matcher_end_ft_id;
ret = hws_matcher_process_attr(ctx->caps, col_matcher);
if (ret)
goto free_col_matcher;
ret = hws_matcher_create_and_connect(col_matcher);
if (ret)
goto free_col_matcher;
matcher->col_matcher = col_matcher;
return 0;
free_col_matcher:
kfree(col_matcher);
mlx5hws_err(ctx, "Failed to create assured collision matcher\n");
return ret;
}
static void
hws_matcher_destroy_col_matcher(struct mlx5hws_matcher *matcher)
{
if (matcher->attr.mode != MLX5HWS_MATCHER_RESOURCE_MODE_RULE ||
matcher->attr.insert_mode == MLX5HWS_MATCHER_INSERT_BY_INDEX)
return;
if (matcher->col_matcher) {
hws_matcher_destroy_and_disconnect(matcher->col_matcher);
kfree(matcher->col_matcher);
}
}
static int hws_matcher_init(struct mlx5hws_matcher *matcher)
{
struct mlx5hws_context *ctx = matcher->tbl->ctx;
int ret;
mutex_lock(&ctx->ctrl_lock);
ret = hws_matcher_create_and_connect(matcher);
if (ret)
goto unlock_err;
ret = hws_matcher_create_col_matcher(matcher);
if (ret)
goto destory_and_disconnect;
mutex_unlock(&ctx->ctrl_lock);
return 0;
destory_and_disconnect:
hws_matcher_destroy_and_disconnect(matcher);
unlock_err:
mutex_unlock(&ctx->ctrl_lock);
return ret;
}
static int hws_matcher_uninit(struct mlx5hws_matcher *matcher)
{
struct mlx5hws_context *ctx = matcher->tbl->ctx;
mutex_lock(&ctx->ctrl_lock);
hws_matcher_destroy_col_matcher(matcher);
hws_matcher_destroy_and_disconnect(matcher);
mutex_unlock(&ctx->ctrl_lock);
return 0;
}
static int hws_matcher_grow_at_array(struct mlx5hws_matcher *matcher)
{
void *p;
if (matcher->size_of_at_array >= MLX5HWS_MATCHER_MAX_AT)
return -ENOMEM;
matcher->size_of_at_array *= 2;
p = krealloc(matcher->at,
matcher->size_of_at_array * sizeof(*matcher->at),
__GFP_ZERO | GFP_KERNEL);
if (!p) {
matcher->size_of_at_array /= 2;
return -ENOMEM;
}
matcher->at = p;
return 0;
}
int mlx5hws_matcher_attach_at(struct mlx5hws_matcher *matcher,
struct mlx5hws_action_template *at)
{
bool is_jumbo = mlx5hws_matcher_mt_is_jumbo(matcher->mt);
u32 required_stes;
int ret;
if (unlikely(matcher->num_of_at >= matcher->size_of_at_array)) {
ret = hws_matcher_grow_at_array(matcher);
if (ret)
return ret;
if (matcher->col_matcher) {
ret = hws_matcher_grow_at_array(matcher->col_matcher);
if (ret)
return ret;
}
}
ret = hws_matcher_check_and_process_at(matcher, at);
if (ret)
return ret;
required_stes = at->num_of_action_stes - (!is_jumbo || at->only_term);
if (matcher->num_of_action_stes < required_stes)
matcher->num_of_action_stes = required_stes;
matcher->at[matcher->num_of_at] = *at;
matcher->num_of_at += 1;
if (matcher->col_matcher)
matcher->col_matcher->num_of_at = matcher->num_of_at;
return 0;
}
static int
hws_matcher_set_templates(struct mlx5hws_matcher *matcher,
struct mlx5hws_match_template *mt[],
u8 num_of_mt,
struct mlx5hws_action_template *at[],
u8 num_of_at)
{
struct mlx5hws_context *ctx = matcher->tbl->ctx;
int ret = 0;
int i;
if (!num_of_mt || !num_of_at) {
mlx5hws_err(ctx, "Number of action/match template cannot be zero\n");
return -EOPNOTSUPP;
}
matcher->mt = kzalloc_objs(*matcher->mt, num_of_mt);
if (!matcher->mt)
return -ENOMEM;
matcher->size_of_at_array =
num_of_at + matcher->attr.max_num_of_at_attach;
matcher->at = kvzalloc_objs(*matcher->at, matcher->size_of_at_array);
if (!matcher->at) {
mlx5hws_err(ctx, "Failed to allocate action template array\n");
ret = -ENOMEM;
goto free_mt;
}
for (i = 0; i < num_of_mt; i++)
matcher->mt[i] = *mt[i];
for (i = 0; i < num_of_at; i++)
matcher->at[i] = *at[i];
matcher->num_of_mt = num_of_mt;
matcher->num_of_at = num_of_at;
return 0;
free_mt:
kfree(matcher->mt);
return ret;
}
static void
hws_matcher_unset_templates(struct mlx5hws_matcher *matcher)
{
kvfree(matcher->at);
kfree(matcher->mt);
}
struct mlx5hws_matcher *
mlx5hws_matcher_create(struct mlx5hws_table *tbl,
struct mlx5hws_match_template *mt[],
u8 num_of_mt,
struct mlx5hws_action_template *at[],
u8 num_of_at,
struct mlx5hws_matcher_attr *attr)
{
struct mlx5hws_context *ctx = tbl->ctx;
struct mlx5hws_matcher *matcher;
int ret;
matcher = kzalloc_obj(*matcher);
if (!matcher)
return NULL;
matcher->tbl = tbl;
matcher->attr = *attr;
ret = hws_matcher_process_attr(tbl->ctx->caps, matcher);
if (ret)
goto free_matcher;
ret = hws_matcher_set_templates(matcher, mt, num_of_mt, at, num_of_at);
if (ret)
goto free_matcher;
ret = hws_matcher_init(matcher);
if (ret) {
mlx5hws_err(ctx, "Failed to initialise matcher: %d\n", ret);
goto unset_templates;
}
return matcher;
unset_templates:
hws_matcher_unset_templates(matcher);
free_matcher:
kfree(matcher);
return NULL;
}
int mlx5hws_matcher_destroy(struct mlx5hws_matcher *matcher)
{
hws_matcher_uninit(matcher);
hws_matcher_unset_templates(matcher);
kfree(matcher);
return 0;
}
struct mlx5hws_match_template *
mlx5hws_match_template_create(struct mlx5hws_context *ctx,
u32 *match_param,
u32 match_param_sz,
u8 match_criteria_enable)
{
struct mlx5hws_match_template *mt;
mt = kzalloc_obj(*mt);
if (!mt)
return NULL;
mt->match_param = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
if (!mt->match_param)
goto free_template;
memcpy(mt->match_param, match_param, match_param_sz);
mt->match_criteria_enable = match_criteria_enable;
return mt;
free_template:
kfree(mt);
return NULL;
}
int mlx5hws_match_template_destroy(struct mlx5hws_match_template *mt)
{
kfree(mt->match_param);
kfree(mt);
return 0;
}
static int hws_matcher_resize_precheck(struct mlx5hws_matcher *src_matcher,
struct mlx5hws_matcher *dst_matcher)
{
struct mlx5hws_context *ctx = src_matcher->tbl->ctx;
int i;
if (src_matcher->tbl->type != dst_matcher->tbl->type) {
mlx5hws_err(ctx, "Table type mismatch for src/dst matchers\n");
return -EINVAL;
}
if (!mlx5hws_matcher_is_resizable(src_matcher) ||
!mlx5hws_matcher_is_resizable(dst_matcher)) {
mlx5hws_err(ctx, "Src/dst matcher is not resizable\n");
return -EINVAL;
}
if (mlx5hws_matcher_is_insert_by_idx(src_matcher) !=
mlx5hws_matcher_is_insert_by_idx(dst_matcher)) {
mlx5hws_err(ctx, "Src/dst matchers insert mode mismatch\n");
return -EINVAL;
}
if (mlx5hws_matcher_is_in_resize(src_matcher) ||
mlx5hws_matcher_is_in_resize(dst_matcher)) {
mlx5hws_err(ctx, "Src/dst matcher is already in resize\n");
return -EINVAL;
}
if (src_matcher->num_of_mt != dst_matcher->num_of_mt) {
mlx5hws_err(ctx, "Src/dst matcher match templates mismatch\n");
return -EINVAL;
}
if (src_matcher->num_of_action_stes > dst_matcher->num_of_action_stes) {
mlx5hws_err(ctx, "Src/dst matcher max STEs mismatch\n");
return -EINVAL;
}
for (i = 0; i < src_matcher->num_of_mt; i++) {
if (mlx5hws_definer_compare(src_matcher->mt[i].definer,
dst_matcher->mt[i].definer)) {
mlx5hws_err(ctx, "Src/dst matcher definers mismatch\n");
return -EINVAL;
}
}
return 0;
}
int mlx5hws_matcher_resize_set_target(struct mlx5hws_matcher *src_matcher,
struct mlx5hws_matcher *dst_matcher)
{
int ret = 0;
mutex_lock(&src_matcher->tbl->ctx->ctrl_lock);
ret = hws_matcher_resize_precheck(src_matcher, dst_matcher);
if (ret)
goto out;
src_matcher->resize_dst = dst_matcher;
out:
mutex_unlock(&src_matcher->tbl->ctx->ctrl_lock);
return ret;
}
int mlx5hws_matcher_resize_rule_move(struct mlx5hws_matcher *src_matcher,
struct mlx5hws_rule *rule,
struct mlx5hws_rule_attr *attr)
{
struct mlx5hws_context *ctx = src_matcher->tbl->ctx;
if (unlikely(!mlx5hws_matcher_is_in_resize(src_matcher))) {
mlx5hws_err(ctx, "Matcher is not resizable or not in resize\n");
return -EINVAL;
}
if (unlikely(src_matcher != rule->matcher)) {
mlx5hws_err(ctx, "Rule doesn't belong to src matcher\n");
return -EINVAL;
}
return mlx5hws_rule_move_hws_add(rule, attr);
}