* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "ka_task_pub.h"
#include "ka_list_pub.h"
#include "ka_memory_pub.h"
#include "ka_common_pub.h"
#include "ka_kernel_def_pub.h"
#include "ka_hashtable_pub.h"
#include "ka_system_pub.h"
#include "securec.h"
#include "pbl_kref_safe.h"
#include "trs_msg.h"
#include "trs_id.h"
#define MAX_TRS_ID_BATCH_NUM 64U
static ka_mutex_t g_trs_id_mutex;
#ifdef CFG_FEATURE_ID_NODE_KMEM_CACHE
static ka_kmem_cache_t *trs_id_cache;
#endif
struct trs_id_rsv_node {
u32 id;
ka_pid_t pid;
ka_list_head_t list;
};
#define TRS_ISOLATE_NUM_MAX 16
struct trs_id_node {
u32 offset[TRS_ISOLATE_NUM_MAX];
u32 id_start;
u32 id_avail_num;
u32 id_total_num;
ka_pid_t pid;
u32 is_in_idle;
ka_hlist_node_t link;
ka_list_head_t list;
};
#define ID_POOL_DFX_MAX_TIMES 3
#define TRS_ID_HASH_TABLE_BIT 10
struct trs_id_pool {
struct trs_id_inst inst;
int type;
struct trs_id_attr attr;
u32 allocatable_num;
u32 alloc_num;
u32 rsv_num;
ka_list_head_t idle_head;
KA_DECLARE_HASHTABLE(idle_htable, TRS_ID_HASH_TABLE_BIT);
KA_DECLARE_HASHTABLE(allocated_htable, TRS_ID_HASH_TABLE_BIT);
ka_list_head_t rsv_head;
struct trs_id_ops ops;
struct kref_safe ref;
ka_mutex_t mutex;
u32 dfx_times;
};
static struct trs_id_pool *g_trs_id_pool[TRS_TS_INST_MAX_NUM][TRS_ID_TYPE_MAX];
size_t trs_id_get_node_size(void);
size_t trs_id_get_pool_size(void);
int init_trs_id(void);
void exit_trs_id(void);
static u32 trs_id_make_hash_key(u32 isolate_num, u32 id)
{
return (id / isolate_num * isolate_num);
}
size_t trs_id_get_node_size(void)
{
return sizeof(struct trs_id_node);
}
size_t trs_id_get_pool_size(void)
{
return sizeof(struct trs_id_pool);
}
static struct trs_id_node *trs_id_node_create(void)
{
struct trs_id_node *node = NULL;
#ifdef CFG_FEATURE_ID_NODE_KMEM_CACHE
node = ka_mm_kmem_cache_alloc(trs_id_cache, KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
#else
node = trs_kmalloc(trs_id_get_node_size(), KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
#endif
return node;
}
static void trs_id_node_destroy(struct trs_id_node *node)
{
if (node != NULL) {
#ifdef CFG_FEATURE_ID_NODE_KMEM_CACHE
ka_mm_kmem_cache_free(trs_id_cache, node);
#else
trs_kfree(node);
#endif
}
}
static struct trs_id_rsv_node *trs_id_rsv_node_create(void)
{
struct trs_id_rsv_node *node = NULL;
#ifdef CFG_FEATURE_ID_NODE_KMEM_CACHE
node = ka_mm_kmem_cache_alloc(trs_id_cache, KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
#else
node = trs_kmalloc(sizeof(struct trs_id_rsv_node), KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
#endif
return node;
}
static void trs_id_rsv_node_destroy(struct trs_id_rsv_node *node)
{
if (node != NULL) {
#ifdef CFG_FEATURE_ID_NODE_KMEM_CACHE
ka_mm_kmem_cache_free(trs_id_cache, node);
#else
trs_kfree(node);
#endif
}
}
static void trs_id_pool_del_rsv_node(struct trs_id_rsv_node *node)
{
ka_list_del(&node->list);
trs_id_rsv_node_destroy(node);
}
static struct trs_id_node *trs_id_pool_get_idle_node_by_id(struct trs_id_pool *id_pool, u32 id)
{
struct trs_id_node *node = NULL;
u32 key = trs_id_make_hash_key(id_pool->attr.isolate_num, id);
ka_hash_for_each_possible(id_pool->idle_htable, node, link, key) {
if ((id >= node->id_start) && (id < (node->id_start + node->id_total_num))) {
return node;
}
}
return NULL;
}
static struct trs_id_node *trs_id_pool_get_allocated_node_by_id(struct trs_id_pool *id_pool, u32 id)
{
struct trs_id_node *node = NULL;
u32 key = trs_id_make_hash_key(id_pool->attr.isolate_num, id);
ka_hash_for_each_possible(id_pool->allocated_htable, node, link, key) {
if ((id >= node->id_start) && (id < (node->id_start + node->id_total_num))) {
return node;
}
}
return NULL;
}
static int trs_id_pool_add_rsv_node(struct trs_id_pool *id_pool, u32 id)
{
struct trs_id_rsv_node *node = trs_id_rsv_node_create();
if (node == NULL) {
trs_err("Alloc id node fail. (devid=%u; tsid=%u; type=%s; id=%u)\n",
id_pool->inst.devid, id_pool->inst.tsid, trs_id_type_to_name(id_pool->type), id);
return -ENOMEM;
}
node->id = id;
node->pid = ka_task_get_current_tgid();
id_pool->rsv_num++;
ka_list_add(&node->list, &id_pool->rsv_head);
id_pool->allocatable_num++;
trs_debug("Add rsv node. (devid=%u; type=%s; allocatable_num=%u; id=%u; pid=%d)\n",
id_pool->inst.devid, trs_id_type_to_name(id_pool->type), id_pool->allocatable_num, id, node->pid);
return 0;
}
static int trs_id_pool_add_new_node(struct trs_id_pool *id_pool, u32 flag, u32 id)
{
struct trs_id_node *node = trs_id_node_create();
if (node == NULL) {
trs_err("Alloc id node fail. (devid=%u; tsid=%u; type=%s; id=%u)\n",
id_pool->inst.devid, id_pool->inst.tsid, trs_id_type_to_name(id_pool->type), id);
return -ENOMEM;
}
node->id_start = trs_id_make_hash_key(id_pool->attr.isolate_num, id);
node->id_total_num = id_pool->attr.isolate_num;
node->pid = 0;
node->is_in_idle = 1;
node->id_avail_num = 1;
(void)memset_s(node->offset, sizeof(u32) * TRS_ISOLATE_NUM_MAX, 0, sizeof(u32) * TRS_ISOLATE_NUM_MAX);
id_pool->allocatable_num++;
ka_hash_add(id_pool->idle_htable, &node->link, node->id_start);
if (id < id_pool->attr.split) {
ka_list_add(&node->list, &id_pool->idle_head);
} else {
ka_list_add_tail(&node->list, &id_pool->idle_head);
}
trs_debug("Add node. (devid=%u; type=%s; node_start=%u; node_total_num=%u; allocatable_num=%u; id=%u)\n",
id_pool->inst.devid, trs_id_type_to_name(id_pool->type), node->id_start, node->id_total_num,
id_pool->allocatable_num, id);
return 0;
}
static int trs_id_pool_add_node(struct trs_id_pool *id_pool, u32 flag, u32 id)
{
struct trs_id_node *node = trs_id_pool_get_allocated_node_by_id(id_pool, id);
if (node == NULL) {
node = trs_id_pool_get_idle_node_by_id(id_pool, id);
}
if (node != NULL) {
node->id_avail_num++;
id_pool->allocatable_num++;
return 0;
}
return trs_id_pool_add_new_node(id_pool, flag, id);
}
#define TRS_NODE_ALLOCATED_TO_IDLE 0
#define TRS_NODE_IDLE_TO_ALLOCATED 1
static void trs_id_pool_move_node(struct trs_id_pool *id_pool, struct trs_id_node *node, u32 direction)
{
ka_hash_del(&node->link);
if (direction == TRS_NODE_ALLOCATED_TO_IDLE) {
ka_hash_add(id_pool->idle_htable, &node->link, node->id_start);
if (node->id_start < id_pool->attr.split) {
ka_list_add(&node->list, &id_pool->idle_head);
} else {
ka_list_add_tail(&node->list, &id_pool->idle_head);
}
node->pid = 0;
node->is_in_idle = 1;
trs_debug("Add to idle list. (devid=%u; type=%s; id_start=%u; id_total_num=%u)\n",
id_pool->inst.devid, trs_id_type_to_name(id_pool->type), node->id_start, node->id_total_num);
} else {
ka_list_del(&node->list);
ka_hash_add(id_pool->allocated_htable, &node->link, node->id_start);
node->is_in_idle = 0;
trs_debug("Add to allocated list. (devid=%u; type=%s; id_start=%u; id_total_num=%u)\n",
id_pool->inst.devid, trs_id_type_to_name(id_pool->type), node->id_start, node->id_total_num);
}
}
static void trs_id_pool_del_node(struct trs_id_node *node)
{
if (node->is_in_idle == 1) {
ka_list_del(&node->list);
}
ka_hash_del(&node->link);
trs_id_node_destroy(node);
}
static void trs_id_pool_del_node_all(struct trs_id_pool *id_pool)
{
struct trs_id_node *node = NULL;
struct trs_id_node *n = NULL;
ka_hlist_node_t *tmp = NULL;
u32 bkt = 0;
ka_hash_for_each_safe(id_pool->allocated_htable, bkt, tmp, node, link) {
trs_id_pool_del_node(node);
}
ka_list_for_each_entry_safe(node, n, &id_pool->idle_head, list) {
trs_id_pool_del_node(node);
}
trs_debug("Id pool del node all. (devid=%u; type=%s; alloc_num=%u; allocatable_num=%u)\n",
id_pool->inst.devid, trs_id_type_to_name(id_pool->type), id_pool->alloc_num, id_pool->allocatable_num);
}
static int trs_id_node_all(struct trs_id_pool *id_pool)
{
int ret;
u32 id;
for (id = id_pool->attr.id_start; id < id_pool->attr.id_end; id++) {
ret = trs_id_pool_add_new_node(id_pool, 0, id);
if (ret != 0) {
trs_id_pool_del_node_all(id_pool);
return -EFAULT;
}
}
return 0;
}
static struct trs_id_pool *trs_id_pool_create(struct trs_id_inst *inst,
int type, struct trs_id_attr *attr, struct trs_id_ops *ops)
{
struct trs_id_pool *id_pool = trs_kzalloc(trs_id_get_pool_size(), KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
if (id_pool == NULL) {
trs_err("Id pool no mem.\n");
return NULL;
}
id_pool->inst = *inst;
id_pool->type = type;
id_pool->attr = *attr;
id_pool->alloc_num = 0;
id_pool->dfx_times = 0;
ka_hash_init(id_pool->idle_htable);
ka_hash_init(id_pool->allocated_htable);
KA_INIT_LIST_HEAD(&id_pool->idle_head);
KA_INIT_LIST_HEAD(&id_pool->rsv_head);
kref_safe_init(&id_pool->ref);
ka_task_mutex_init(&id_pool->mutex);
if (ops == NULL) {
int ret = trs_id_node_all(id_pool);
if (ret != 0) {
trs_err("Id node list init fail. (ret=%d)\n", ret);
goto out;
}
} else {
if ((attr->batch_num == 0) || (attr->batch_num > MAX_TRS_ID_BATCH_NUM)) {
trs_err("Batch num invalid. (batch_num=%u)\n", attr->batch_num);
goto out;
}
id_pool->ops = *ops;
}
return id_pool;
out:
ka_task_mutex_destroy(&id_pool->mutex);
trs_kfree(id_pool);
return NULL;
}
static struct trs_id_node *trs_id_pool_get_available_node(struct trs_id_pool *id_pool)
{
struct trs_id_node *node = NULL;
ka_hlist_node_t *tmp = NULL;
u32 bkt = 0;
if (id_pool->attr.isolate_num > 1) {
ka_hash_for_each_safe(id_pool->allocated_htable, bkt, tmp, node, link) {
if ((node->id_avail_num > 0) && (node->pid == ka_task_get_current_tgid())) {
return node;
}
}
}
if (ka_list_empty_careful(&id_pool->idle_head)) {
return NULL;
}
node = ka_list_first_entry(&id_pool->idle_head, struct trs_id_node, list);
return node;
}
static struct trs_id_node *trs_id_pool_get_idle_node(struct trs_id_pool *id_pool)
{
if (!ka_list_empty_careful(&id_pool->idle_head)) {
return ka_list_first_entry(&id_pool->idle_head, struct trs_id_node, list);
}
return NULL;
}
static inline void trs_id_pool_free_one_id_to_node(struct trs_id_pool *id_pool, struct trs_id_node *node, u32 id)
{
node->offset[id - node->id_start] = 0;
node->id_avail_num++;
if (node->id_avail_num == node->id_total_num) {
trs_id_pool_move_node(id_pool, node, TRS_NODE_ALLOCATED_TO_IDLE);
}
}
static int trs_id_pool_alloc_one_id_in_node(struct trs_id_pool *id_pool, struct trs_id_node *node, u32 *id)
{
int alloc_id = -1;
u32 offset;
for (offset = 0; offset < node->id_total_num; offset++) {
if (node->offset[offset] == 0) {
node->offset[offset] = 1;
alloc_id = node->id_start + offset;
break;
}
}
if (alloc_id == -1) {
trs_err("Node has no resource. (alloc_id=%d; start=%u; total_num=%u; avail_num=%u)\n",
alloc_id, node->id_start, node->id_total_num, node->id_avail_num);
return alloc_id;
}
if (node->pid == 0) {
trs_id_pool_move_node(id_pool, node, TRS_NODE_IDLE_TO_ALLOCATED);
node->pid = ka_task_get_current_tgid();
}
node->id_avail_num--;
id_pool->allocatable_num--;
*id = (u32)alloc_id;
trs_debug("Alloc success. (type=%s; id=%d; start=%u; avail_num=%u; total_num=%u; isolate_num=%u; "
"allocatable_num=%u)\n", trs_id_type_to_name(id_pool->type), alloc_id, node->id_start,
node->id_avail_num, node->id_total_num, id_pool->attr.isolate_num, id_pool->allocatable_num);
return 0;
}
static int trs_id_pool_alloc_one_available_id(struct trs_id_pool *id_pool, u32 *id)
{
struct trs_id_node *node = trs_id_pool_get_available_node(id_pool);
if (node == NULL) {
return -ENOSPC;
}
return trs_id_pool_alloc_one_id_in_node(id_pool, node, id);
}
static int trs_id_pool_alloc_id_in_idle_node(struct trs_id_pool *id_pool, u32 batch_num, u32 *id, u32 *idx)
{
int ret;
struct trs_id_node *node = trs_id_pool_get_idle_node(id_pool);
if (node == NULL) {
return -ENOSPC;
}
for (; (*idx < batch_num) && (node->id_avail_num > 0); (*idx)++) {
ret = trs_id_pool_alloc_one_id_in_node(id_pool, node, &id[*idx]);
if (ret != 0) {
return ret;
}
}
if (node->id_avail_num == 0) {
trs_debug("Del node. (type=%s; start=%u; total_num=%u; avail_num=%u; allocatable_num=%u; alloc=%u)\n",
trs_id_type_to_name(id_pool->type), node->id_start, node->id_total_num, node->id_avail_num,
id_pool->allocatable_num, id_pool->alloc_num);
trs_id_pool_del_node(node);
}
return 0;
}
static int trs_id_pool_try_free_batch(struct trs_id_pool *id_pool)
{
if (id_pool->ops.free_batch == NULL) {
return 0;
}
trs_debug("Free info. (type=%s; batch_num=%u; allocatable_num=%u; rsv_num=%u)\n",
trs_id_type_to_name(id_pool->type), id_pool->attr.batch_num, id_pool->allocatable_num, id_pool->rsv_num);
while (id_pool->allocatable_num > 0) {
u32 batch_num = ka_base_min_t(u32, (id_pool->allocatable_num - id_pool->rsv_num), id_pool->attr.batch_num);
u32 id[MAX_TRS_ID_BATCH_NUM];
u32 idx = 0;
int ret;
while (idx < batch_num) {
if (trs_id_pool_alloc_id_in_idle_node(id_pool, batch_num, id, &idx) != 0) {
batch_num = idx;
trs_debug("(batch_num=%u)\n", batch_num);
break;
}
}
if (batch_num == 0) {
trs_warn("Free batch warn. (type=%s; batch_num=%u; allocatable_num=%u; ret=%d)\n",
trs_id_type_to_name(id_pool->type), batch_num, id_pool->allocatable_num, ret);
break;
}
ret = id_pool->ops.free_batch(&id_pool->inst, id_pool->type, id, batch_num);
if (ret != 0) {
trs_warn("Free batch warn. (type=%s; batch_num=%u; allocatable_num=%u; ret=%d)\n",
trs_id_type_to_name(id_pool->type), batch_num, id_pool->allocatable_num, ret);
for (idx = 0; idx < batch_num; idx++) {
(void)trs_id_pool_add_node(id_pool, 0, id[idx]);
}
return ret;
}
}
return 0;
}
static void _trs_id_pool_destroy(struct trs_id_pool *id_pool)
{
if (id_pool->ops.free_batch == NULL) {
trs_id_pool_del_node_all(id_pool);
} else {
(void)trs_id_pool_try_free_batch(id_pool);
trs_id_pool_del_node_all(id_pool);
}
}
static void trs_id_pool_destroy(struct trs_id_pool *id_pool)
{
if (id_pool != NULL) {
trs_info("id pool destroy. (type=%s)\n", trs_id_type_to_name(id_pool->type));
_trs_id_pool_destroy(id_pool);
ka_task_mutex_destroy(&id_pool->mutex);
trs_kfree(id_pool);
}
}
static int trs_id_pool_add(int type, struct trs_id_pool *id_pool)
{
u32 ts_inst = trs_id_inst_to_ts_inst(&id_pool->inst);
ka_task_mutex_lock(&g_trs_id_mutex);
if (g_trs_id_pool[ts_inst][type] != NULL) {
ka_task_mutex_unlock(&g_trs_id_mutex);
trs_err("[%s] id pool exists. (devid=%u; tsid=%u)\n",
trs_id_type_to_name(type), id_pool->inst.devid, id_pool->inst.tsid);
return -ENODEV;
}
g_trs_id_pool[ts_inst][type] = id_pool;
ka_task_mutex_unlock(&g_trs_id_mutex);
return 0;
}
static void trs_id_pool_release(struct kref_safe * kref)
{
struct trs_id_pool *id_pool = ka_container_of(kref, struct trs_id_pool, ref);
trs_id_pool_destroy(id_pool);
}
static void trs_id_pool_del(struct trs_id_inst *inst, int type)
{
u32 ts_inst = trs_id_inst_to_ts_inst(inst);
struct trs_id_pool *id_pool = NULL;
ka_task_mutex_lock(&g_trs_id_mutex);
id_pool = g_trs_id_pool[ts_inst][type];
g_trs_id_pool[ts_inst][type] = NULL;
ka_task_mutex_unlock(&g_trs_id_mutex);
if (id_pool != NULL) {
kref_safe_put(&id_pool->ref, trs_id_pool_release);
}
}
static int trs_id_type_check(int type)
{
if ((type < TRS_STREAM_ID) || (type >= TRS_ID_TYPE_MAX)) {
trs_err("Unknown Trs id type. (type=%d)\n", type);
return -EINVAL;
}
return 0;
}
static int trs_id_param_check(struct trs_id_inst *inst, int type)
{
int ret;
ret = trs_id_inst_check(inst);
if (ret != 0) {
return ret;
}
ret = trs_id_type_check(type);
if (ret != 0) {
return ret;
}
return 0;
}
static struct trs_id_pool *trs_id_pool_get(struct trs_id_inst *inst, int type)
{
struct trs_id_pool *id_pool = NULL;
u32 ts_inst;
int ret;
ret = trs_id_param_check(inst, type);
if (ret != 0) {
return NULL;
}
ts_inst = trs_id_inst_to_ts_inst(inst);
ka_task_mutex_lock(&g_trs_id_mutex);
id_pool = g_trs_id_pool[ts_inst][type];
if (id_pool != NULL) {
if (ka_system_try_module_get(id_pool->ops.owner)) {
kref_safe_get(&id_pool->ref);
} else {
id_pool = NULL;
}
}
ka_task_mutex_unlock(&g_trs_id_mutex);
return id_pool;
}
static void trs_id_pool_put(struct trs_id_pool *id_pool)
{
ka_system_module_put(id_pool->ops.owner);
kref_safe_put(&id_pool->ref, trs_id_pool_release);
}
static int trs_id_pool_range_check(struct trs_id_pool *id_pool, u32 id)
{
if ((id < id_pool->attr.id_start) || (id >= (id_pool->attr.id_end))) {
trs_err("Trs id range check fail. (id_start=%u; id_end=%u; type=%d; id=%u)\n",
id_pool->attr.id_start, id_pool->attr.id_end, id_pool->type, id);
return -EBADR;
}
return 0;
}
static void trs_id_pool_alloc_batch(struct trs_id_pool *id_pool, u32 flag, u32 specified_id, u32 num)
{
u32 id[MAX_TRS_ID_BATCH_NUM];
u32 i, id_num, remain_num;
int ret;
remain_num = id_pool->attr.id_num - id_pool->alloc_num;
if (remain_num == 0) {
return;
}
if (id_pool->ops.alloc_batch == NULL) {
return;
}
if (id_pool->attr.batch_num <= 1) {
return;
}
if (trs_id_is_specified(flag)) {
id_num = num;
id[0] = specified_id;
} else {
id_num = (remain_num > id_pool->attr.batch_num) ? id_pool->attr.batch_num : remain_num;
}
id_num = ka_base_round_up(id_num, id_pool->attr.isolate_num);
ret = id_pool->ops.alloc_batch(&id_pool->inst, id_pool->type, flag, id, &id_num);
if ((ret != 0) || (id_num > id_pool->attr.batch_num)) {
if (id_pool->dfx_times < ID_POOL_DFX_MAX_TIMES) {
id_pool->dfx_times++;
trs_warn("Alloc batch fail. (devid=%u; tsid=%u; ret=%d; flag=0x%x; real_id_num=%u; batch_num=%u; type=%s)\n",
id_pool->inst.devid, id_pool->inst.tsid, ret, flag, id_num, id_pool->attr.batch_num,
trs_id_type_to_name(id_pool->type));
}
return;
}
for (i = 0; i < id_num; i++) {
ret = trs_id_pool_range_check(id_pool, id[i]);
if (ret == 0) {
ret = trs_id_pool_add_node(id_pool, 0, id[i]);
if (ret != 0) {
(void)id_pool->ops.free_batch(&id_pool->inst, id_pool->type, &id[i], id_num - i);
break;
}
}
}
trs_debug("Alloced batch num. (num=%u; type=%d)\n", id_num, id_pool->type);
}
static int trs_id_alloc_non_cache(struct trs_id_pool *id_pool, u32 flag, u32 *id)
{
u32 id_num = 1;
int ret;
ret = id_pool->ops.alloc_batch(&id_pool->inst, id_pool->type, flag, id, &id_num);
if ((ret != 0) || (id_num > id_pool->attr.batch_num) || (id_num == 0)) {
trs_debug("Alloc batch fail. (devid=%u; tsid=%u; real_id_num=%u; batch_num=%u; type=%s; ret=%d)\n",
id_pool->inst.devid, id_pool->inst.tsid, id_num, id_pool->attr.batch_num,
trs_id_type_to_name(id_pool->type), ret);
return -ENOSPC;
}
id_pool->alloc_num++;
return ret;
}
static int trs_id_pool_alloc_specified_id_in_node(struct trs_id_pool *id_pool, struct trs_id_node *node,
u32 start, u32 end, u32 *id)
{
int tmp_id = -1;
u32 offset, offset_start, offset_end;
offset_start = (start >= (int)node->id_start) ? start - node->id_start : 0;
offset_end = (end <= (int)(node->id_start + node->id_total_num)) ? end - node->id_start : node->id_total_num;
for (offset = offset_start; offset < offset_end; offset++) {
if (node->offset[offset] == 0) {
node->offset[offset] = 1;
tmp_id = node->id_start + offset;
break;
}
}
if (tmp_id == -1) {
trs_err("Node has no resource. (alloc_id=%d; start=%u; total_num=%u; avail_num=%u)\n",
tmp_id, node->id_start, node->id_total_num, node->id_avail_num);
return tmp_id;
}
if (node->pid == 0) {
trs_id_pool_move_node(id_pool, node, TRS_NODE_IDLE_TO_ALLOCATED);
}
node->id_avail_num--;
id_pool->allocatable_num--;
*id = (u32)tmp_id;
trs_debug("Alloc specified id. (devid=%u; type=%s; id=%u; id_start=%d; id_end=%d; node_pid=%d)\n",
id_pool->inst.devid, trs_id_type_to_name(id_pool->type), *id, node->id_start + offset_start,
offset_start + offset_end, node->pid);
return 0;
}
static bool trs_id_pool_find_specified_id(struct trs_id_pool *id_pool, u32 start, u32 end, u32 *id)
{
struct trs_id_rsv_node *rsv_node = NULL;
struct trs_id_rsv_node *rsv_n = NULL;
struct trs_id_node *node = NULL;
struct trs_id_node *n = NULL;
ka_hlist_node_t *tmp = NULL;
u32 bkt = 0;
int ret;
trs_debug("Find specified_id. (id=%u; start=%u; end=%u; pid=%d)\n", *id, start, end, ka_task_get_current_tgid());
ka_list_for_each_entry_safe(rsv_node, rsv_n, &id_pool->rsv_head, list) {
if ((rsv_node->id == *id) && (rsv_node->pid == ka_task_get_current_tgid())) {
id_pool->rsv_num--;
trs_id_pool_del_rsv_node(rsv_node);
id_pool->allocatable_num--;
return true;
}
}
ka_hash_for_each_safe(id_pool->allocated_htable, bkt, tmp, node, link) {
if ((node->pid != ka_task_get_current_tgid()) ||
(node->id_start >= end) || (node->id_start + node->id_total_num <= start)) {
continue;
}
ret = trs_id_pool_alloc_specified_id_in_node(id_pool, node, start, end, id);
if (ret == 0) {
return true;
}
}
ka_list_for_each_entry_safe(node, n, &id_pool->idle_head, list) {
if ((node->id_start >= end) || (node->id_start + node->id_total_num <= start)) {
continue;
}
ret = trs_id_pool_alloc_specified_id_in_node(id_pool, node, start, end, id);
if (ret == 0) {
return true;
}
}
return false;
}
static bool trs_id_pool_has_res_for_alloc(struct trs_id_pool *id_pool)
{
struct trs_id_node *node = NULL;
ka_hlist_node_t *tmp = NULL;
u32 bkt = 0;
if (!ka_list_empty(&id_pool->idle_head)) {
return true;
}
if (id_pool->attr.isolate_num > 1) {
ka_hash_for_each_safe(id_pool->allocated_htable, bkt, tmp, node, link) {
if ((node->id_avail_num > 0) && (node->pid == ka_task_get_current_tgid())) {
return true;
}
}
}
return false;
}
static void trs_id_print_isolated_res_detail(struct trs_id_pool *id_pool)
{
if (id_pool->type == TRS_NOTIFY_ID) {
return;
}
if (id_pool->attr.isolate_num > 1) {
struct trs_id_node *node = NULL;
ka_hlist_node_t *tmp = NULL;
u32 bkt = 0;
trs_err("No res. (devid=%u; tsid=%u; type=%s; allocatable_num=%u; alloc_num=%u)\n",
id_pool->inst.devid, id_pool->inst.tsid, trs_id_type_to_name(id_pool->type),
id_pool->allocatable_num, id_pool->alloc_num);
ka_hash_for_each_safe(id_pool->allocated_htable, bkt, tmp, node, link) {
if (node->id_avail_num > 0) {
trs_err("Other process node. (pid=%d; id_avail_num=%u)\n", node->pid, node->id_avail_num);
}
}
}
}
static int trs_id_alloc_cache(struct trs_id_pool *id_pool, u32 flag, u32 *id, u32 num)
{
bool find_specified_id = true;
if (trs_id_is_specified(flag)) {
find_specified_id = trs_id_pool_find_specified_id(id_pool, *id, (*id) + num, id);
if (find_specified_id) {
id_pool->alloc_num++;
return 0;
}
}
if (!trs_id_pool_has_res_for_alloc(id_pool) || (!find_specified_id)) {
trs_id_pool_alloc_batch(id_pool, flag, *id, num);
if (!trs_id_pool_has_res_for_alloc(id_pool)) {
trs_id_print_isolated_res_detail(id_pool);
return -ENOSPC;
}
}
if (trs_id_is_specified(flag)) {
if (!trs_id_pool_find_specified_id(id_pool, *id, (*id) + num, id)) {
trs_err("Fail to alloc id in range. (devid=%u; type=%s; start=%u; end=%u)\n", id_pool->inst.devid,
trs_id_type_to_name(id_pool->type), *id, (*id) + num);
return -ENOSPC;
}
} else {
int ret;
ret = trs_id_pool_alloc_one_available_id(id_pool, id);
if (ret != 0) {
trs_err("Alloc id failed. (ret=%d; devid=%u; type=%s; allocatable_num=%u; alloc_num=%u)\n", ret,
id_pool->inst.devid, trs_id_type_to_name(id_pool->type), id_pool->allocatable_num, id_pool->alloc_num);
return ret;
}
}
id_pool->alloc_num++;
trs_debug("Alloc id. (devid=%u; type=%s; id=%u; allocatable_num=%u; alloc_num=%u)\n",
id_pool->inst.devid, trs_id_type_to_name(id_pool->type), *id, id_pool->allocatable_num, id_pool->alloc_num);
return 0;
}
static int trs_id_pool_alloc(struct trs_id_pool *id_pool, u32 flag, u32 *id, u32 num)
{
if ((id_pool->ops.is_non_cache_type != NULL) && id_pool->ops.is_non_cache_type(id_pool->type)) {
return trs_id_alloc_non_cache(id_pool, flag, id);
} else {
return trs_id_alloc_cache(id_pool, flag, id, num);
}
}
static int trs_id_free_non_cache(struct trs_id_pool *id_pool, u32 flag, u32 id)
{
int ret;
if (trs_id_is_reserved(flag)) {
trs_warn("No support non cache type free with reserved flag. (flag=0x%x; id=%u)\n", flag, id);
}
ret = id_pool->ops.free_batch(&id_pool->inst, id_pool->type, &id, 1);
if (ret != 0) {
trs_warn("Free batch warn. (type=%s; batch_num=%u; allocatable_num=%u; ret=%d)\n",
trs_id_type_to_name(id_pool->type), id_pool->attr.batch_num, id_pool->allocatable_num, ret);
return ret;
}
id_pool->alloc_num--;
return ret;
}
static int trs_id_free_cache(struct trs_id_pool *id_pool, u32 flag, u32 id)
{
struct trs_id_node *node = NULL;
int ret = 0;
node = trs_id_pool_get_allocated_node_by_id(id_pool, id);
if (node == NULL) {
trs_err("Invalid id. (devid=%u; tsid=%u; type=%s; id=%u)\n", id_pool->inst.devid, id_pool->inst.tsid,
trs_id_type_to_name(id_pool->type), id);
return -EINVAL;
}
if (!trs_id_is_reserved(flag)) {
trs_id_pool_free_one_id_to_node(id_pool, node, id);
id_pool->allocatable_num++;
} else {
ret = trs_id_pool_add_rsv_node(id_pool, id);
if (ret != 0) {
ret = trs_id_free_non_cache(id_pool, flag, id);
}
}
id_pool->alloc_num--;
trs_debug("Free id. (ret=%d; devid=%u; type=%s; id=%u; node_start=%u; avail_num=%u; total=%u; allocatable_num=%u; "
"alloc_num=%u)\n", ret, id_pool->inst.devid, trs_id_type_to_name(id_pool->type), id, node->id_start,
node->id_avail_num, node->id_total_num, id_pool->allocatable_num, id_pool->alloc_num);
return ret;
}
static int trs_id_pool_free(struct trs_id_pool *id_pool, u32 flag, u32 id)
{
int ret;
ret = trs_id_pool_range_check(id_pool, id);
if (ret != 0) {
return ret;
}
if ((id_pool->ops.is_non_cache_type != NULL) && id_pool->ops.is_non_cache_type(id_pool->type)) {
return trs_id_free_non_cache(id_pool, flag, id);
} else {
return trs_id_free_cache(id_pool, flag, id);
}
}
static int trs_id_attr_check(struct trs_id_attr *attr)
{
if (attr == NULL) {
return -EINVAL;
}
if ((attr->id_num == 0) || ((attr->id_start + attr->id_num) < attr->id_start)) {
trs_warn("Id para. (id_num=%u; id_start=%u; id_end=%u)\n", attr->id_num, attr->id_start, attr->id_end);
return -EINVAL;
}
if (attr->batch_num > MAX_TRS_ID_BATCH_NUM) {
trs_err("Invalid para. (batch_num=%u)\n", attr->batch_num);
return -EINVAL;
}
return 0;
}
int trs_id_register(struct trs_id_inst *inst, int type, struct trs_id_attr *attr, struct trs_id_ops *ops)
{
struct trs_id_pool *id_pool = NULL;
int ret;
if (trs_id_param_check(inst, type) != 0) {
return -EINVAL;
}
if (trs_id_attr_check(attr) != 0) {
return -EINVAL;
}
id_pool = trs_id_pool_create(inst, type, attr, ops);
if (id_pool == NULL) {
return -ENOMEM;
}
ret = trs_id_pool_add(type, id_pool);
if (ret != 0) {
trs_id_pool_destroy(id_pool);
return ret;
}
trs_debug("Trs id init. (devid=%u; tsid=%u; type=%s; start=%u; end=%u; num=%u; split=%u; isolate_num=%u)\n",
inst->devid, inst->tsid, trs_id_type_to_name(type), attr->id_start, attr->id_end, attr->id_num,
attr->split, attr->isolate_num);
return 0;
}
KA_EXPORT_SYMBOL_GPL(trs_id_register);
int trs_id_unregister(struct trs_id_inst *inst, int type)
{
if (trs_id_param_check(inst, type) != 0) {
return -EINVAL;
}
trs_id_pool_del(inst, type);
return 0;
}
KA_EXPORT_SYMBOL_GPL(trs_id_unregister);
int trs_id_get_total_num(struct trs_id_inst *inst, int type, u32 *total_num)
{
struct trs_id_pool *id_pool = trs_id_pool_get(inst, type);
int ret = -EFAULT;
if (id_pool != NULL) {
ka_task_mutex_lock(&id_pool->mutex);
if (total_num != NULL) {
*total_num = id_pool->attr.id_num;
ret = 0;
}
ka_task_mutex_unlock(&id_pool->mutex);
trs_id_pool_put(id_pool);
}
return ret;
}
KA_EXPORT_SYMBOL_GPL(trs_id_get_total_num);
int trs_id_get_range(struct trs_id_inst *inst, int type, u32 *start, u32 *end)
{
struct trs_id_pool *id_pool = trs_id_pool_get(inst, type);
int ret = -EFAULT;
if (id_pool != NULL) {
ka_task_mutex_lock(&id_pool->mutex);
if ((start != NULL) && (end != NULL)) {
*start = id_pool->attr.id_start;
*end = id_pool->attr.id_end;
ret = 0;
}
ka_task_mutex_unlock(&id_pool->mutex);
trs_id_pool_put(id_pool);
}
return ret;
}
KA_EXPORT_SYMBOL_GPL(trs_id_get_range);
int trs_id_get_max_id(struct trs_id_inst *inst, int type, u32 *max_id)
{
struct trs_id_pool *id_pool = trs_id_pool_get(inst, type);
int ret = -ENODEV;
if (id_pool != NULL) {
ka_task_mutex_lock(&id_pool->mutex);
if (max_id != NULL) {
*max_id = id_pool->attr.id_end;
ret = 0;
}
ka_task_mutex_unlock(&id_pool->mutex);
trs_id_pool_put(id_pool);
}
return ret;
}
KA_EXPORT_SYMBOL_GPL(trs_id_get_max_id);
int trs_id_get_split(struct trs_id_inst *inst, int type, u32 *split)
{
struct trs_id_pool *id_pool = trs_id_pool_get(inst, type);
int ret = -ENODEV;
if (id_pool != NULL) {
ka_task_mutex_lock(&id_pool->mutex);
if (split != NULL) {
*split = id_pool->attr.split;
ret = 0;
}
ka_task_mutex_unlock(&id_pool->mutex);
trs_id_pool_put(id_pool);
}
return ret;
}
KA_EXPORT_SYMBOL_GPL(trs_id_get_split);
int trs_id_get_avail_num(struct trs_id_inst *inst, int type, u32 *avail_num)
{
struct trs_id_pool *id_pool = trs_id_pool_get(inst, type);
int ret = -ENODEV;
if (id_pool != NULL) {
ka_task_mutex_lock(&id_pool->mutex);
if (avail_num != NULL) {
*avail_num = id_pool->attr.id_num - id_pool->alloc_num;
ret = 0;
}
ka_task_mutex_unlock(&id_pool->mutex);
trs_id_pool_put(id_pool);
}
return ret;
}
KA_EXPORT_SYMBOL_GPL(trs_id_get_avail_num);
int trs_id_get_avail_num_in_pool(struct trs_id_inst *inst, int type, u32 *avail_num)
{
struct trs_id_pool *id_pool = trs_id_pool_get(inst, type);
int ret = -ENODEV;
if (id_pool != NULL) {
ka_task_mutex_lock(&id_pool->mutex);
if (id_pool->ops.avail_query != NULL) {
ret = id_pool->ops.avail_query(inst, type, avail_num);
}
ka_task_mutex_unlock(&id_pool->mutex);
trs_id_pool_put(id_pool);
}
return ret;
}
KA_EXPORT_SYMBOL_GPL(trs_id_get_avail_num_in_pool);
int trs_id_get_used_num(struct trs_id_inst *inst, int type, u32 *used_num)
{
struct trs_id_pool *id_pool = trs_id_pool_get(inst, type);
int ret = -EOPNOTSUPP;
if (id_pool != NULL) {
ka_task_mutex_lock(&id_pool->mutex);
if (used_num != NULL) {
*used_num = id_pool->alloc_num;
ret = 0;
}
ka_task_mutex_unlock(&id_pool->mutex);
trs_id_pool_put(id_pool);
}
return ret;
}
KA_EXPORT_SYMBOL_GPL(trs_id_get_used_num);
int trs_id_get_stat(struct trs_id_inst *inst, int type, struct trs_id_stat *stat)
{
struct trs_id_pool *id_pool = trs_id_pool_get(inst, type);
int ret = -ENODEV;
if (id_pool != NULL) {
ka_task_mutex_lock(&id_pool->mutex);
stat->alloc = id_pool->alloc_num;
stat->allocatable = id_pool->allocatable_num;
stat->rsv_num = id_pool->rsv_num;
ret = 0;
ka_task_mutex_unlock(&id_pool->mutex);
trs_id_pool_put(id_pool);
}
return ret;
}
KA_EXPORT_SYMBOL_GPL(trs_id_get_stat);
int trs_id_alloc_ex(struct trs_id_inst *inst, int type, u32 flag, u32 *id, u32 para)
{
struct trs_id_pool *id_pool = trs_id_pool_get(inst, type);
int ret = -EOPNOTSUPP;
if (id_pool != NULL) {
ka_task_mutex_lock(&id_pool->mutex);
if (id != NULL) {
ret = trs_id_pool_alloc(id_pool, flag, id, para);
}
ka_task_mutex_unlock(&id_pool->mutex);
trs_id_pool_put(id_pool);
}
if ((ret == 0) && (id != NULL)) {
trs_debug("Alloc success. (devid=%u; type=%s; flag=0x%x; para=%u; id=%u)\n",
inst->devid, trs_id_type_to_name(type), flag, para, *id);
}
return ret;
}
KA_EXPORT_SYMBOL_GPL(trs_id_alloc_ex);
int trs_id_free_ex(struct trs_id_inst *inst, int type, u32 flag, u32 id)
{
struct trs_id_pool *id_pool = trs_id_pool_get(inst, type);
int ret = -ENODEV;
trs_debug("Free id. (type=%s; flag=0x%x; id=%u)\n", trs_id_type_to_name(type), flag, id);
if (id_pool != NULL) {
ka_task_mutex_lock(&id_pool->mutex);
ret = trs_id_pool_free(id_pool, flag, id);
ka_task_mutex_unlock(&id_pool->mutex);
trs_id_pool_put(id_pool);
}
return ret;
}
KA_EXPORT_SYMBOL_GPL(trs_id_free_ex);
int trs_id_free_batch_by_type(struct trs_id_inst *inst, int type)
{
struct trs_id_pool *id_pool = trs_id_pool_get(inst, type);
int ret;
if (id_pool == NULL) {
return 0;
}
ka_task_mutex_lock(&id_pool->mutex);
ret = trs_id_pool_try_free_batch(id_pool);
ka_task_mutex_unlock(&id_pool->mutex);
trs_id_pool_put(id_pool);
return ret;
}
KA_EXPORT_SYMBOL_GPL(trs_id_free_batch_by_type);
int trs_id_flush_to_pool(struct trs_id_inst *inst)
{
int type, ret = 0;
for (type = TRS_STREAM_ID; type < TRS_ID_TYPE_MAX; type++) {
if (trs_id_free_batch_by_type(inst, type) != 0) {
ret = -ENODEV;
trs_err("Id fush to pool fail. (devid=%u; tsid=%u; type=%s)\n",
inst->devid, inst->tsid, trs_id_type_to_name(type));
}
}
return ret;
}
KA_EXPORT_SYMBOL_GPL(trs_id_flush_to_pool);
static struct trs_id_node *trs_id_pool_get_current_node_by_id(struct trs_id_pool *id_pool, u32 id)
{
struct trs_id_node *node = NULL;
u32 key = trs_id_make_hash_key(id_pool->attr.isolate_num, id);
ka_hash_for_each_possible(id_pool->allocated_htable, node, link, key) {
if ((id >= node->id_start) && (id < (node->id_start + node->id_total_num)) && (node->pid == ka_task_get_current_tgid())) {
return node;
}
}
return NULL;
}
static void _trs_id_clear_reserved_flag(struct trs_id_pool *id_pool, ka_pid_t pid)
{
struct trs_id_rsv_node *node = NULL;
struct trs_id_rsv_node *n = NULL;
struct trs_id_node *hash_node = NULL;
u32 id;
ka_list_for_each_entry_safe(node, n, &id_pool->rsv_head, list) {
if (node->pid == pid) {
id = node->id;
trs_id_pool_del_rsv_node(node);
id_pool->rsv_num--;
hash_node = trs_id_pool_get_current_node_by_id(id_pool, id);
if (hash_node == NULL) {
trs_warn("Invalid id. (devid=%u; type=%s; id=%u)\n", id_pool->inst.devid,
trs_id_type_to_name(id_pool->type), id);
continue;
}
trs_id_pool_free_one_id_to_node(id_pool, hash_node, id);
}
}
}
static void trs_id_clear_reserved_flag_by_type(struct trs_id_inst *inst, int type, ka_pid_t pid)
{
struct trs_id_pool *id_pool = trs_id_pool_get(inst, type);
if (id_pool == NULL) {
return;
}
ka_task_mutex_lock(&id_pool->mutex);
_trs_id_clear_reserved_flag(id_pool, pid);
ka_task_mutex_unlock(&id_pool->mutex);
trs_id_pool_put(id_pool);
}
void trs_id_clear_reserved_flag(struct trs_id_inst *inst, ka_pid_t pid)
{
int type;
for (type = TRS_STREAM_ID; type < TRS_ID_TYPE_MAX; type++) {
trs_id_clear_reserved_flag_by_type(inst, type, pid);
}
}
KA_EXPORT_SYMBOL_GPL(trs_id_clear_reserved_flag);
int trs_id_to_string(struct trs_id_inst *inst, int type, u32 id, char *msg, u32 msg_len)
{
struct trs_id_pool *id_pool = trs_id_pool_get(inst, type);
int ret = -ENODEV;
if (id_pool != NULL) {
ret = 0;
if (id_pool->ops.trans != NULL) {
u32 phy_id;
ret = id_pool->ops.trans(inst, type, id, &phy_id);
if ((ret == 0) && (id != phy_id)) {
ret = sprintf_s(msg, msg_len, "type(%s),id(%u),phy id(%u).\n", trs_id_type_to_name(type), id, phy_id);
}
}
trs_id_pool_put(id_pool);
}
return ret;
}
KA_EXPORT_SYMBOL_GPL(trs_id_to_string);
int init_trs_id(void)
{
int trs_inst, type;
for (trs_inst = 0; trs_inst < TRS_TS_INST_MAX_NUM; trs_inst++) {
for (type = TRS_STREAM_ID; type < TRS_ID_TYPE_MAX; type++) {
g_trs_id_pool[trs_inst][type] = NULL;
}
}
#ifdef CFG_FEATURE_ID_NODE_KMEM_CACHE
trs_id_cache = ka_mm_kmem_cache_create("trs_id_cache", sizeof(struct trs_id_node), 0, 0, NULL);
if (trs_id_cache == NULL) {
trs_err("kmem cache create fail\n");
return -ENOMEM;
}
#endif
ka_task_mutex_init(&g_trs_id_mutex);
return 0;
}
void exit_trs_id(void)
{
int trs_inst, type;
trs_info("Exit trs id\n");
#ifdef CFG_FEATURE_ID_NODE_KMEM_CACHE
if (trs_id_cache != NULL) {
ka_mm_kmem_cache_destroy(trs_id_cache);
trs_id_cache = NULL;
trs_debug("Trs id cache destroy\n");
}
#endif
for (trs_inst = 0; trs_inst < TRS_TS_INST_MAX_NUM; trs_inst++) {
for (type = TRS_STREAM_ID; type < TRS_ID_TYPE_MAX; type++) {
trs_id_pool_destroy(g_trs_id_pool[trs_inst][type]);
g_trs_id_pool[trs_inst][type] = NULL;
}
}
ka_task_mutex_destroy(&g_trs_id_mutex);
}