* 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_common_pub.h"
#include "ka_task_pub.h"
#include "ka_memory_pub.h"
#include "ka_list_pub.h"
#include "ka_base_pub.h"
#include "urd_kv.h"
#include "urd_feature.h"
#include "securec.h"
#include "urd_define.h"
struct dms_kv_table g_kv_cb = {0};
#define CHECK_ARG(key, data, data_len, ret) \
do { \
if (((key) == NULL) || ((data) == NULL) || ((data_len) > KV_VALUE_MAX_LEN) \
|| ((data_len) <= 0)) { \
dms_err("Invalid Parameter.\n"); \
return -EINVAL; \
} \
if (ka_base_strnlen(key, KV_KEY_MAX_LEN) >= KV_KEY_MAX_LEN) { \
dms_err("Key too long. (max=%d)\n", KV_KEY_MAX_LEN); \
return -EINVAL; \
} \
} while (0)
static void free_kv(struct dms_kv_node* kv)
{
if (kv != NULL) {
ka_mm_kfree(kv->data);
kv->data = NULL;
ka_mm_kfree(kv->key);
kv->key = NULL;
ka_mm_kfree(kv);
}
}
static inline unsigned int dms_kv_hash_33(const char* tag)
{
unsigned int hash = 0;
while (*tag) {
hash = (hash << 5) + hash + *tag++;
}
return hash;
}
static inline ka_hlist_head_t* dms_kv_get_table_head(const char* key)
{
return &g_kv_cb.table[dms_kv_hash_33(key) % KV_TABLE_SIZE];
}
static struct dms_kv_node* dms_kv_new_node(const char* key, unsigned int key_len, void* value, int len)
{
struct dms_kv_node* node = NULL;
int ret;
node = ka_mm_kzalloc(sizeof(struct dms_kv_node), KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
if (node == NULL) {
dms_err("ka_mm_kmalloc failed. (size=%lu; key=\"%s\")\n", sizeof(struct dms_kv_node), key);
return NULL;
}
node->key = ka_mm_kzalloc(sizeof(char) * (key_len + 1), KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
if (node->key == NULL) {
dms_err("ka_mm_kmalloc failed. (size=%lu; key=\"%s\")\n", (sizeof(char) * (key_len + 1)), key);
goto free_node;
}
ret = strcpy_s(node->key, (key_len + 1), key);
if (ret != 0) {
dms_err("strcpy failed. (ret=%d; size=%lu; key=\"%s\")\n", ret, ka_base_strlen(key), key);
goto free_key;
}
node->data = ka_mm_kzalloc(len, KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
if (node->data == NULL) {
dms_err("ka_mm_kmalloc failed. (size=%d; key=\"%s\")\n", len, key);
goto free_key;
}
ret = memcpy_s(node->data, len, value, len);
if (ret != 0) {
dms_err("memcpy failed. (ret=%d; size=%d; key=\"%s\")\n", ret, len, key);
goto free_data;
}
node->data_len = len;
return node;
free_data:
ka_mm_kfree(node->data);
node->data = NULL;
free_key:
ka_mm_kfree(node->key);
node->key = NULL;
free_node:
ka_mm_kfree(node);
node = NULL;
return NULL;
}
static struct dms_kv_node* dms_kv_search(const char* key)
{
ka_hlist_head_t* table_head = NULL;
struct dms_kv_node* node = NULL;
ka_hlist_node_t* tmp = NULL;
table_head = dms_kv_get_table_head(key);
ka_hlist_for_each_entry_safe(node, tmp, table_head, hnode)
{
if (ka_base_strncmp(node->key, key, ka_base_strlen(key)) == 0) {
break;
}
}
return node;
}
static int dms_kv_add(const char* key, void* value, int len)
{
ka_hlist_head_t* table_head = NULL;
struct dms_kv_node* node = NULL;
node = dms_kv_new_node(key, ka_base_strlen(key), value, len);
if (node == NULL) {
dms_err("Create new node failed. (key=\"%s\")\n", key);
return -ENOMEM;
}
table_head = dms_kv_get_table_head(key);
ka_hlist_add_head(&node->hnode, table_head);
return 0;
}
int dms_kv_del(const char* key)
{
struct dms_kv_node* node = NULL;
if (key == NULL) {
dms_err("Invalid Parameter.\n");
return -EINVAL;
}
ka_task_mutex_lock(&g_kv_cb.lock);
node = dms_kv_search(key);
if (node != NULL) {
ka_hlist_del(&node->hnode);
ka_task_mutex_unlock(&g_kv_cb.lock);
free_kv(node);
return 0;
}
ka_task_mutex_unlock(&g_kv_cb.lock);
dms_err("No such device node. (key=\"%s\")\n", key);
return -ENODEV;
}
int dms_kv_set(const char* key, void* value, int len)
{
ka_hlist_head_t* table_head = NULL;
struct dms_kv_node* node = NULL;
ka_hlist_node_t* tmp = NULL;
int ret;
CHECK_ARG(key, value, len, -EINVAL);
ka_task_mutex_lock(&g_kv_cb.lock);
table_head = dms_kv_get_table_head(key);
ka_hlist_for_each_entry_safe(node, tmp, table_head, hnode)
{
if (ka_base_strcmp(node->key, key) == 0) {
ka_task_mutex_unlock(&g_kv_cb.lock);
dms_err("Repeated registration of the same node, (key=\"%s\")\n", key);
return -EINVAL;
}
}
ret = dms_kv_add(key, value, len);
ka_task_mutex_unlock(&g_kv_cb.lock);
return ret;
}
int dms_kv_get(const char* key, void* value, int len)
{
struct dms_kv_node* node = NULL;
int ret;
CHECK_ARG(key, value, len, -EINVAL);
ka_task_mutex_lock(&g_kv_cb.lock);
node = dms_kv_search(key);
if (node != NULL) {
ret = memcpy_s(value, len, node->data, node->data_len);
ka_task_mutex_unlock(&g_kv_cb.lock);
return ret;
}
ka_task_mutex_unlock(&g_kv_cb.lock);
dms_debug("No such device node. (key=\"%s\")\n", key);
return -ENODEV;
}
int dms_kv_get_ex(const char* key, void* value, int len)
{
struct dms_kv_node* node = NULL;
int ret;
CHECK_ARG(key, value, len, -EINVAL);
ka_task_mutex_lock(&g_kv_cb.lock);
node = dms_kv_search(key);
if (node == NULL) {
ka_task_mutex_unlock(&g_kv_cb.lock);
dms_debug("No such device node. (key=\"%s\")\n", key);
return -ENODEV;
}
ret = memcpy_s(value, len, node->data, node->data_len);
if (ret != 0) {
ka_task_mutex_unlock(&g_kv_cb.lock);
dms_err("Failed to memcpy node data. (key=\"%s\"; ret=%d)\n", key, ret);
return -EINVAL;
}
ret = feature_inc_work(*(DMS_FEATURE_NODE_S**)value);
if (ret != 0) {
ka_task_mutex_unlock(&g_kv_cb.lock);
return ret;
}
ka_task_mutex_unlock(&g_kv_cb.lock);
return ret;
}
void dms_kv_dump(char *buf, ssize_t *offset, key_dump_handle handle)
{
ka_hlist_head_t* table_head = NULL;
struct dms_kv_node* node = NULL;
ka_hlist_node_t* tmp = NULL;
int i;
ssize_t ret;
ka_task_mutex_lock(&g_kv_cb.lock);
for (i = 0; i < KV_TABLE_SIZE; i++) {
table_head = &g_kv_cb.table[i];
ka_hlist_for_each_entry_safe(node, tmp, table_head, hnode)
{
ret = handle(node->data, buf, offset);
if (ret < 0) {
ka_task_mutex_unlock(&g_kv_cb.lock);
return;
}
}
}
ka_task_mutex_unlock(&g_kv_cb.lock);
}
int dms_kv_init(void)
{
int i;
g_kv_cb.table = ka_mm_kzalloc(sizeof(ka_hlist_head_t) * KV_TABLE_SIZE, KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
if (g_kv_cb.table == NULL) {
dms_err("ka_mm_kmalloc failed. (size=%lu)\n", (sizeof(ka_hlist_head_t) * KV_TABLE_SIZE));
return -ENOMEM;
}
for (i = 0; i < KV_TABLE_SIZE; i++) {
KA_INIT_HLIST_HEAD(&g_kv_cb.table[i]);
}
ka_task_mutex_init(&g_kv_cb.lock);
return 0;
}
void dms_kv_uninit(void)
{
ka_hlist_head_t* table_head = NULL;
struct dms_kv_node* node = NULL;
ka_hlist_node_t* tmp = NULL;
int i;
ka_task_mutex_lock(&g_kv_cb.lock);
for (i = 0; i < KV_TABLE_SIZE; i++) {
table_head = &g_kv_cb.table[i];
ka_hlist_for_each_entry_safe(node, tmp, table_head, hnode)
{
ka_hlist_del_init(&node->hnode);
free_kv(node);
node = NULL;
}
}
ka_task_mutex_unlock(&g_kv_cb.lock);
ka_task_mutex_destroy(&g_kv_cb.lock);
ka_mm_kfree(g_kv_cb.table);
g_kv_cb.table = NULL;
return;
}