#include "dr_types.h"
#define DR_ICM_MODIFY_HDR_GRANULARITY_4K 12
enum dr_arg_chunk_size {
DR_ARG_CHUNK_SIZE_1,
DR_ARG_CHUNK_SIZE_MIN = DR_ARG_CHUNK_SIZE_1,
DR_ARG_CHUNK_SIZE_2,
DR_ARG_CHUNK_SIZE_3,
DR_ARG_CHUNK_SIZE_4,
DR_ARG_CHUNK_SIZE_MAX,
};
struct dr_arg_pool {
enum dr_arg_chunk_size log_chunk_size;
struct mlx5dr_domain *dmn;
struct list_head free_list;
struct mutex mutex;
};
struct mlx5dr_arg_mgr {
struct mlx5dr_domain *dmn;
struct dr_arg_pool *pools[DR_ARG_CHUNK_SIZE_MAX];
};
static int dr_arg_pool_alloc_objs(struct dr_arg_pool *pool)
{
struct mlx5dr_arg_obj *arg_obj, *tmp_arg;
struct list_head cur_list;
u16 object_range;
int num_of_objects;
u32 obj_id = 0;
int i, ret;
INIT_LIST_HEAD(&cur_list);
object_range =
pool->dmn->info.caps.log_header_modify_argument_granularity;
object_range =
max_t(u32, pool->dmn->info.caps.log_header_modify_argument_granularity,
DR_ICM_MODIFY_HDR_GRANULARITY_4K);
object_range =
min_t(u32, pool->dmn->info.caps.log_header_modify_argument_max_alloc,
object_range);
if (pool->log_chunk_size > object_range) {
mlx5dr_err(pool->dmn, "Required chunk size (%d) is not supported\n",
pool->log_chunk_size);
return -ENOMEM;
}
num_of_objects = (1 << (object_range - pool->log_chunk_size));
ret = mlx5dr_cmd_create_modify_header_arg(pool->dmn->mdev,
object_range,
pool->dmn->pdn,
&obj_id);
if (ret) {
mlx5dr_err(pool->dmn, "failed allocating object with range: %d:\n",
object_range);
return -EAGAIN;
}
for (i = 0; i < num_of_objects; i++) {
arg_obj = kzalloc(sizeof(*arg_obj), GFP_KERNEL);
if (!arg_obj) {
ret = -ENOMEM;
goto clean_arg_obj;
}
arg_obj->log_chunk_size = pool->log_chunk_size;
list_add_tail(&arg_obj->list_node, &cur_list);
arg_obj->obj_id = obj_id;
arg_obj->obj_offset = i * (1 << pool->log_chunk_size);
}
list_splice_tail_init(&cur_list, &pool->free_list);
return 0;
clean_arg_obj:
mlx5dr_cmd_destroy_modify_header_arg(pool->dmn->mdev, obj_id);
list_for_each_entry_safe(arg_obj, tmp_arg, &cur_list, list_node) {
list_del(&arg_obj->list_node);
kfree(arg_obj);
}
return ret;
}
static struct mlx5dr_arg_obj *dr_arg_pool_get_arg_obj(struct dr_arg_pool *pool)
{
struct mlx5dr_arg_obj *arg_obj = NULL;
int ret;
mutex_lock(&pool->mutex);
if (list_empty(&pool->free_list)) {
ret = dr_arg_pool_alloc_objs(pool);
if (ret)
goto out;
}
arg_obj = list_first_entry_or_null(&pool->free_list,
struct mlx5dr_arg_obj,
list_node);
WARN(!arg_obj, "couldn't get dr arg obj from pool");
if (arg_obj)
list_del_init(&arg_obj->list_node);
out:
mutex_unlock(&pool->mutex);
return arg_obj;
}
static void dr_arg_pool_put_arg_obj(struct dr_arg_pool *pool,
struct mlx5dr_arg_obj *arg_obj)
{
mutex_lock(&pool->mutex);
list_add(&arg_obj->list_node, &pool->free_list);
mutex_unlock(&pool->mutex);
}
static struct dr_arg_pool *dr_arg_pool_create(struct mlx5dr_domain *dmn,
enum dr_arg_chunk_size chunk_size)
{
struct dr_arg_pool *pool;
pool = kzalloc(sizeof(*pool), GFP_KERNEL);
if (!pool)
return NULL;
pool->dmn = dmn;
INIT_LIST_HEAD(&pool->free_list);
mutex_init(&pool->mutex);
pool->log_chunk_size = chunk_size;
if (dr_arg_pool_alloc_objs(pool))
goto free_pool;
return pool;
free_pool:
kfree(pool);
return NULL;
}
static void dr_arg_pool_destroy(struct dr_arg_pool *pool)
{
struct mlx5dr_arg_obj *arg_obj, *tmp_arg;
list_for_each_entry_safe(arg_obj, tmp_arg, &pool->free_list, list_node) {
list_del(&arg_obj->list_node);
if (!arg_obj->obj_offset)
mlx5dr_cmd_destroy_modify_header_arg(pool->dmn->mdev, arg_obj->obj_id);
kfree(arg_obj);
}
mutex_destroy(&pool->mutex);
kfree(pool);
}
static enum dr_arg_chunk_size dr_arg_get_chunk_size(u16 num_of_actions)
{
if (num_of_actions <= 8)
return DR_ARG_CHUNK_SIZE_1;
if (num_of_actions <= 16)
return DR_ARG_CHUNK_SIZE_2;
if (num_of_actions <= 32)
return DR_ARG_CHUNK_SIZE_3;
if (num_of_actions <= 64)
return DR_ARG_CHUNK_SIZE_4;
return DR_ARG_CHUNK_SIZE_MAX;
}
u32 mlx5dr_arg_get_obj_id(struct mlx5dr_arg_obj *arg_obj)
{
return (arg_obj->obj_id + arg_obj->obj_offset);
}
struct mlx5dr_arg_obj *mlx5dr_arg_get_obj(struct mlx5dr_arg_mgr *mgr,
u16 num_of_actions,
u8 *data)
{
u32 size = dr_arg_get_chunk_size(num_of_actions);
struct mlx5dr_arg_obj *arg_obj;
int ret;
if (size >= DR_ARG_CHUNK_SIZE_MAX)
return NULL;
arg_obj = dr_arg_pool_get_arg_obj(mgr->pools[size]);
if (!arg_obj) {
mlx5dr_err(mgr->dmn, "Failed allocating args object for modify header\n");
return NULL;
}
ret = mlx5dr_send_postsend_args(mgr->dmn,
mlx5dr_arg_get_obj_id(arg_obj),
num_of_actions, data);
if (ret) {
mlx5dr_err(mgr->dmn, "Failed writing args object\n");
goto put_obj;
}
return arg_obj;
put_obj:
mlx5dr_arg_put_obj(mgr, arg_obj);
return NULL;
}
void mlx5dr_arg_put_obj(struct mlx5dr_arg_mgr *mgr,
struct mlx5dr_arg_obj *arg_obj)
{
dr_arg_pool_put_arg_obj(mgr->pools[arg_obj->log_chunk_size], arg_obj);
}
struct mlx5dr_arg_mgr*
mlx5dr_arg_mgr_create(struct mlx5dr_domain *dmn)
{
struct mlx5dr_arg_mgr *pool_mgr;
int i;
if (!mlx5dr_domain_is_support_ptrn_arg(dmn))
return NULL;
pool_mgr = kzalloc(sizeof(*pool_mgr), GFP_KERNEL);
if (!pool_mgr)
return NULL;
pool_mgr->dmn = dmn;
for (i = 0; i < DR_ARG_CHUNK_SIZE_MAX; i++) {
pool_mgr->pools[i] = dr_arg_pool_create(dmn, i);
if (!pool_mgr->pools[i])
goto clean_pools;
}
return pool_mgr;
clean_pools:
for (i--; i >= 0; i--)
dr_arg_pool_destroy(pool_mgr->pools[i]);
kfree(pool_mgr);
return NULL;
}
void mlx5dr_arg_mgr_destroy(struct mlx5dr_arg_mgr *mgr)
{
struct dr_arg_pool **pools;
int i;
if (!mgr)
return;
pools = mgr->pools;
for (i = 0; i < DR_ARG_CHUNK_SIZE_MAX; i++)
dr_arg_pool_destroy(pools[i]);
kfree(mgr);
}