* 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 "securec.h"
#include "xsmem_framework_log.h"
#include "xsmem_framework.h"
#include "xsmem_prop.h"
#include "ka_task_pub.h"
#include "ka_compiler_pub.h"
#include "ka_list_pub.h"
#include "ka_errno_pub.h"
KA_TASK_DEFINE_MUTEX(prop_list_mutex);
static KA_LIST_HEAD(prop_list_head);
#define PROP_NAME_MAX_LEN 128
struct xsm_prop {
char name[PROP_NAME_MAX_LEN];
unsigned int prop_len;
int owner;
int create_pid;
int refcnt;
unsigned long value;
ka_list_head_t pool_node;
};
struct xsm_task_prop_node {
ka_list_head_t task_node;
struct xsm_prop *prop;
};
static struct xsm_task_prop_node *xsmem_task_prop_node_get(struct xsm_task_pool_node *node, const struct xsm_prop *prop)
{
struct xsm_task_prop_node *prop_node = NULL;
if (node == NULL) {
return NULL;
}
ka_list_for_each_entry(prop_node, &node->task_prop_head, task_node) {
if (prop_node->prop == prop) {
return prop_node;
}
}
return NULL;
}
static int xsmem_add_prop_to_task(struct xsm_task_pool_node *node, struct xsm_prop *prop)
{
struct xsm_task_prop_node *prop_node = NULL;
#ifndef EMU_ST
if (node == NULL) {
return -EFAULT;
}
#endif
if (xsmem_task_prop_node_get(node, prop) != NULL) {
return 0;
}
prop_node = xsmem_drv_kmalloc(sizeof(*prop_node), KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
if (prop_node == NULL) {
xsmem_err("alloc prop node mem %lx failed\n", sizeof(*prop_node));
return -ENOMEM;
}
prop->refcnt++;
prop_node->prop = prop;
ka_list_add_tail(&prop_node->task_node, &node->task_prop_head);
return 0;
}
static struct xsm_prop *xsmem_pool_prop_alloc(struct xsm_task *task, struct xsm_task_pool_node *node,
struct xsm_prop_op_arg *prop_op, char *key)
{
struct xsm_prop *prop = NULL;
int ret;
prop = xsmem_drv_kmalloc(sizeof(*prop), KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
if (prop == NULL) {
#ifndef EMU_ST
xsmem_err_limited("alloc prop mem %lx failed\n", sizeof(*prop));
#endif
return KA_ERR_PTR(-ENOMEM);
}
ret = strcpy_s(prop->name, PROP_NAME_MAX_LEN, key);
if (ret != 0) {
xsmem_drv_kfree(prop);
xsmem_err("Pool add prop strcpy_s failed. (pool=%d; prop=%s; ret=%d)\n", prop_op->pool_id, key, ret);
return KA_ERR_PTR(-EFAULT);
}
prop->prop_len = prop_op->prop_len;
prop->owner = prop_op->owner;
prop->value = prop_op->value;
prop->create_pid = task->pid;
prop->refcnt = 0;
if (prop->owner == XSMEM_PROP_OWNER_TASK_GRP) {
ret = xsmem_add_prop_to_task(node, prop);
if (ret != 0) {
xsmem_drv_kfree(prop);
return KA_ERR_PTR(ret);
}
}
xsmem_debug("Alloc. (task=%d; pool=%d; add prop=%s; owner=%d)\n",
ka_task_get_current()->tgid, prop_op->pool_id, key, prop->owner);
return prop;
}
static void xsmem_pool_prop_free(struct xsm_prop *prop)
{
xsmem_debug("Free. (task=%d; create_pid=%d; del prop=%s)\n",
ka_task_get_current()->tgid, prop->create_pid, prop->name);
ka_list_del(&prop->pool_node);
xsmem_drv_kfree(prop);
}
static int xsmem_pool_prop_del(const struct xsm_task *task, struct xsm_task_pool_node *node, struct xsm_prop *prop)
{
struct xsm_task_prop_node *prop_node = NULL;
xsmem_debug("Delete. (task=%d; create_pid=%d; del prop=%s; owner=%d)\n",
ka_task_get_current()->tgid, prop->create_pid, prop->name, prop->owner);
if (prop->owner != XSMEM_PROP_OWNER_TASK_GRP) {
if (prop->create_pid != task->pid) {
return -EFAULT;
}
xsmem_pool_prop_free(prop);
} else {
prop_node = xsmem_task_prop_node_get(node, prop);
if (prop_node == NULL) {
return -EFAULT;
}
ka_list_del(&prop_node->task_node);
xsmem_drv_kfree(prop_node);
prop->refcnt--;
if (prop->refcnt <= 0) {
xsmem_pool_prop_free(prop);
}
}
return 0;
}
static int xsmem_pool_prop_op(struct xsm_task *task, struct xsm_task_pool_node *node,
struct xsm_prop_op_arg *prop_op, struct xsm_prop_op_arg __ka_user *arg)
{
ka_list_head_t *head = (node == NULL) ? &prop_list_head : &node->pool->prop_list_head;
struct xsm_prop *prop = NULL, *tmp = NULL;
char key[PROP_NAME_MAX_LEN];
if (copy_from_user_safe(key, (char __ka_user *)prop_op->prop, prop_op->prop_len) != 0) {
xsmem_err("copy key from user failed, key_len %d\n", prop_op->prop_len);
return -EFAULT;
}
key[prop_op->prop_len] = '\0';
ka_list_for_each_entry(tmp, head, pool_node) {
if ((prop_op->prop_len == tmp->prop_len) && (ka_base_strncmp(key, tmp->name, tmp->prop_len) == 0)) {
prop = tmp;
break;
}
}
if (prop_op->op == XSMEM_PROP_OP_SET) {
if (prop != NULL) {
return -EEXIST;
}
prop = xsmem_pool_prop_alloc(task, node, prop_op, key);
if (KA_IS_ERR_OR_NULL(prop)) {
return (int)KA_PTR_ERR(prop);
}
ka_list_add_tail(&prop->pool_node, head);
return 0;
} else if (prop_op->op == XSMEM_PROP_OP_GET) {
if (prop == NULL) {
return -ENOENT;
}
if (prop->owner == XSMEM_PROP_OWNER_TASK_GRP) {
int ret = xsmem_add_prop_to_task(node, prop);
if (ret != 0) {
return ret;
}
}
return (int)ka_base_put_user(prop->value, &arg->value);
} else {
if (prop == NULL) {
return -ENOENT;
}
return xsmem_pool_prop_del(task, node, prop);
}
}
void xsmem_proc_grp_prop_del(struct xsm_task_pool_node *node)
{
struct xsm_task_prop_node *prop_node = NULL, *tmp = NULL;
struct xsm_prop *prop = NULL;
ka_task_mutex_lock(&prop_list_mutex);
ka_list_for_each_entry_safe(prop_node, tmp, &node->task_prop_head, task_node) {
prop = prop_node->prop;
ka_list_del(&prop_node->task_node);
xsmem_drv_kfree(prop_node);
prop->refcnt--;
if (prop->refcnt <= 0) {
xsmem_pool_prop_free(prop);
}
}
ka_task_mutex_unlock(&prop_list_mutex);
}
void xsmem_task_prop_del(int pid)
{
struct xsm_prop *prop = NULL, *tmp = NULL;
ka_task_mutex_lock(&prop_list_mutex);
ka_list_for_each_entry_safe(prop, tmp, &prop_list_head, pool_node) {
if ((prop->create_pid == pid) && (prop->owner == XSMEM_PROP_OWNER_TASK)) {
xsmem_pool_prop_free(prop);
}
}
ka_task_mutex_unlock(&prop_list_mutex);
}
void xsmem_pool_prop_clear(struct xsm_pool *xp)
{
struct xsm_prop *prop = NULL, *tmp = NULL;
ka_task_mutex_lock(&prop_list_mutex);
ka_list_for_each_entry_safe(prop, tmp, &xp->prop_list_head, pool_node) {
xsmem_pool_prop_free(prop);
}
ka_task_mutex_unlock(&prop_list_mutex);
}
void xsmem_pool_task_prop_clear(struct xsm_pool *xp, const struct xsm_task *task)
{
struct xsm_prop *prop = NULL, *tmp = NULL;
ka_task_mutex_lock(&prop_list_mutex);
ka_list_for_each_entry_safe(prop, tmp, &xp->prop_list_head, pool_node) {
if ((prop->create_pid == task->pid) && (prop->owner == XSMEM_PROP_OWNER_TASK)) {
xsmem_pool_prop_free(prop);
}
}
ka_task_mutex_unlock(&prop_list_mutex);
}
int ioctl_xsmem_pool_prop_op(struct xsm_task *task, unsigned int cmd, unsigned long arg)
{
struct xsm_task_pool_node *node = NULL;
struct xsm_prop_op_arg prop_op;
struct xsm_pool *xp = NULL;
int pool_id, ret;
if (copy_from_user_safe(&prop_op, (struct xsm_prop_op_arg __ka_user *)(uintptr_t)arg, sizeof(prop_op)) != 0) {
xsmem_err("copy_from_user failed\n");
return -EFAULT;
}
if ((prop_op.op == XSMEM_PROP_OP_SET) &&
(prop_op.owner != XSMEM_PROP_OWNER_TASK) && (prop_op.owner != XSMEM_PROP_OWNER_TASK_GRP)) {
xsmem_err("Owner is invalid. (owner=%d)\n", prop_op.owner);
return -EINVAL;
}
if ((prop_op.prop == NULL) || (prop_op.prop_len == 0) || (prop_op.prop_len >= PROP_NAME_MAX_LEN)) {
xsmem_err("Prop or prop_len is invalid. (prop_len=%u)\n", prop_op.prop_len);
return -EINVAL;
}
pool_id = prop_op.pool_id;
ka_task_mutex_lock(&task->mutex);
if (pool_id >= 0) {
xp = xsmem_pool_get(pool_id);
if (xp == NULL) {
ka_task_mutex_unlock(&task->mutex);
xsmem_err("pool_id %d couldn't find the pool\n", pool_id);
return -ENODEV;
}
node = task_pool_node_find(task, xp);
if (node == NULL) {
ka_task_mutex_unlock(&task->mutex);
xsmem_pool_put(xp);
xsmem_err("Task is not in pool. (pid=%d; pool_id=%d)\n", task->pid, pool_id);
return -EINVAL;
}
} else {
if ((prop_op.op == XSMEM_PROP_OP_SET) && (prop_op.owner == XSMEM_PROP_OWNER_TASK_GRP)) {
ka_task_mutex_unlock(&task->mutex);
xsmem_err("Prop owner is invalid, can not set pool id.\n");
return -EINVAL;
}
}
ka_task_mutex_lock(&prop_list_mutex);
ret = xsmem_pool_prop_op(task, node, &prop_op, (struct xsm_prop_op_arg __ka_user *)arg);
ka_task_mutex_unlock(&prop_list_mutex);
ka_task_mutex_unlock(&task->mutex);
if (pool_id >= 0) {
xsmem_pool_put(xp);
}
return ret;
}