// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
|
// Copyright (c) 2020 Mellanox Technologies
|
|
#include <linux/jhash.h>
|
#include "mod_hdr.h"
|
|
#define MLX5_MH_ACT_SZ MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)
|
|
struct mod_hdr_key {
|
int num_actions;
|
void *actions;
|
};
|
|
struct mlx5e_mod_hdr_handle {
|
/* a node of a hash table which keeps all the mod_hdr entries */
|
struct hlist_node mod_hdr_hlist;
|
|
struct mod_hdr_key key;
|
|
struct mlx5_modify_hdr *modify_hdr;
|
|
refcount_t refcnt;
|
struct completion res_ready;
|
int compl_result;
|
};
|
|
static u32 hash_mod_hdr_info(struct mod_hdr_key *key)
|
{
|
return jhash(key->actions,
|
key->num_actions * MLX5_MH_ACT_SZ, 0);
|
}
|
|
static int cmp_mod_hdr_info(struct mod_hdr_key *a, struct mod_hdr_key *b)
|
{
|
if (a->num_actions != b->num_actions)
|
return 1;
|
|
return memcmp(a->actions, b->actions,
|
a->num_actions * MLX5_MH_ACT_SZ);
|
}
|
|
void mlx5e_mod_hdr_tbl_init(struct mod_hdr_tbl *tbl)
|
{
|
mutex_init(&tbl->lock);
|
hash_init(tbl->hlist);
|
}
|
|
void mlx5e_mod_hdr_tbl_destroy(struct mod_hdr_tbl *tbl)
|
{
|
mutex_destroy(&tbl->lock);
|
}
|
|
static struct mlx5e_mod_hdr_handle *mod_hdr_get(struct mod_hdr_tbl *tbl,
|
struct mod_hdr_key *key,
|
u32 hash_key)
|
{
|
struct mlx5e_mod_hdr_handle *mh, *found = NULL;
|
|
hash_for_each_possible(tbl->hlist, mh, mod_hdr_hlist, hash_key) {
|
if (!cmp_mod_hdr_info(&mh->key, key)) {
|
refcount_inc(&mh->refcnt);
|
found = mh;
|
break;
|
}
|
}
|
|
return found;
|
}
|
|
struct mlx5e_mod_hdr_handle *
|
mlx5e_mod_hdr_attach(struct mlx5_core_dev *mdev,
|
struct mod_hdr_tbl *tbl,
|
enum mlx5_flow_namespace_type namespace,
|
struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts)
|
{
|
int num_actions, actions_size, err;
|
struct mlx5e_mod_hdr_handle *mh;
|
struct mod_hdr_key key;
|
u32 hash_key;
|
|
num_actions = mod_hdr_acts->num_actions;
|
actions_size = MLX5_MH_ACT_SZ * num_actions;
|
|
key.actions = mod_hdr_acts->actions;
|
key.num_actions = num_actions;
|
|
hash_key = hash_mod_hdr_info(&key);
|
|
mutex_lock(&tbl->lock);
|
mh = mod_hdr_get(tbl, &key, hash_key);
|
if (mh) {
|
mutex_unlock(&tbl->lock);
|
wait_for_completion(&mh->res_ready);
|
|
if (mh->compl_result < 0) {
|
err = -EREMOTEIO;
|
goto attach_header_err;
|
}
|
goto attach_header;
|
}
|
|
mh = kzalloc(sizeof(*mh) + actions_size, GFP_KERNEL);
|
if (!mh) {
|
mutex_unlock(&tbl->lock);
|
return ERR_PTR(-ENOMEM);
|
}
|
|
mh->key.actions = (void *)mh + sizeof(*mh);
|
memcpy(mh->key.actions, key.actions, actions_size);
|
mh->key.num_actions = num_actions;
|
refcount_set(&mh->refcnt, 1);
|
init_completion(&mh->res_ready);
|
|
hash_add(tbl->hlist, &mh->mod_hdr_hlist, hash_key);
|
mutex_unlock(&tbl->lock);
|
|
mh->modify_hdr = mlx5_modify_header_alloc(mdev, namespace,
|
mh->key.num_actions,
|
mh->key.actions);
|
if (IS_ERR(mh->modify_hdr)) {
|
err = PTR_ERR(mh->modify_hdr);
|
mh->compl_result = err;
|
goto alloc_header_err;
|
}
|
mh->compl_result = 1;
|
complete_all(&mh->res_ready);
|
|
attach_header:
|
return mh;
|
|
alloc_header_err:
|
complete_all(&mh->res_ready);
|
attach_header_err:
|
mlx5e_mod_hdr_detach(mdev, tbl, mh);
|
return ERR_PTR(err);
|
}
|
|
void mlx5e_mod_hdr_detach(struct mlx5_core_dev *mdev,
|
struct mod_hdr_tbl *tbl,
|
struct mlx5e_mod_hdr_handle *mh)
|
{
|
if (!refcount_dec_and_mutex_lock(&mh->refcnt, &tbl->lock))
|
return;
|
hash_del(&mh->mod_hdr_hlist);
|
mutex_unlock(&tbl->lock);
|
|
if (mh->compl_result > 0)
|
mlx5_modify_header_dealloc(mdev, mh->modify_hdr);
|
|
kfree(mh);
|
}
|
|
struct mlx5_modify_hdr *mlx5e_mod_hdr_get(struct mlx5e_mod_hdr_handle *mh)
|
{
|
return mh->modify_hdr;
|
}
|