| // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
| // Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. |
| |
| #include "dr_types.h" |
| |
| #define DR_ICM_MODIFY_HDR_GRANULARITY_4K 12 |
| |
| /* modify-header arg pool */ |
| enum dr_arg_chunk_size { |
| DR_ARG_CHUNK_SIZE_1, |
| DR_ARG_CHUNK_SIZE_MIN = DR_ARG_CHUNK_SIZE_1, /* keep updated when changing */ |
| DR_ARG_CHUNK_SIZE_2, |
| DR_ARG_CHUNK_SIZE_3, |
| DR_ARG_CHUNK_SIZE_4, |
| DR_ARG_CHUNK_SIZE_MAX, |
| }; |
| |
| /* argument pool area */ |
| struct dr_arg_pool { |
| enum dr_arg_chunk_size log_chunk_size; |
| struct mlx5dr_domain *dmn; |
| struct list_head free_list; |
| struct mutex mutex; /* protect arg pool */ |
| }; |
| |
| 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)); |
| /* Only one devx object per range */ |
| 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) /* the first in range */ |
| 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; |
| } |
| |
| /* write it into the hw */ |
| 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); |
| } |