* 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 "kernel_version_adapt.h"
#include "xsmem_res_dispatch.h"
#include "xsmem_framework_log.h"
#include "xsmem_framework.h"
#include "xsmem_proc_fs.h"
#include "ka_system_pub.h"
#include "ka_list_pub.h"
#include "ka_fs_pub.h"
#define PROC_FS_NAME_LEN 32
#define PROC_FS_MODE 0444
extern ka_mutex_t xsmem_mutex;
extern ka_idr_t xsmem_idr;
static ka_proc_dir_entry_t *top_entry = NULL;
static ka_proc_dir_entry_t *xp_entry = NULL;
static ka_proc_dir_entry_t *task_entry = NULL;
static int task_node_show(ka_seq_file_t *seq, void *offset)
{
struct xsm_task_pool_node *node = (struct xsm_task_pool_node *)seq->private;
struct xsm_pool *xp = node->pool;
struct xsm_task_block_node *task_blk_node = NULL;
int num = 0;
ka_fs_seq_printf(seq, "task id %d pid %d\n", node->task_id, node->task->pid);
ka_fs_seq_printf(seq, "block id: offset alloc_size real_size alloc_pid refcnt\n");
ka_task_mutex_lock(&xp->xp_block_mutex);
ka_list_for_each_entry(task_blk_node, &node->task_blk_head, node_list) {
struct xsm_block *blk = task_blk_node->blk;
ka_fs_seq_printf(seq, " %-4d:%-8pK %-8lu %-8lu %-4d %-4ld\n", blk->id,
(void *)(uintptr_t)blk->offset, blk->alloc_size, blk->real_size, blk->pid, blk->refcnt);
num++;
}
ka_task_mutex_unlock(&xp->xp_block_mutex);
ka_fs_seq_printf(seq, "total block %d, total alloc size %llu, total real size %llu\n",
num, node->alloc_size, node->real_alloc_size);
return 0;
}
STATIC int task_node_open(ka_inode_t *inode, ka_file_t *file)
{
return ka_single_open(inode, file, task_node_show);
}
STATIC_PROCFS_FILE_FUNC_OPS_OPEN(task_node_ops, task_node_open);
static void proc_fs_format_task_dir_name(const struct xsm_task *task, char *name, int len)
{
if (sprintf_s(name, (u32)len, "%d", task->pid) <= 0) {
xsmem_warn("pid %d sprintf_s not success\n", task->pid);
}
}
static ka_proc_dir_entry_t *proc_fs_mk_task_dir(struct xsm_task *task, ka_proc_dir_entry_t *parent)
{
char name[PROC_FS_NAME_LEN];
proc_fs_format_task_dir_name(task, name, PROC_FS_NAME_LEN);
return ka_fs_proc_mkdir((const char *)name, parent);
}
static void proc_fs_rm_task_dir(struct xsm_task *task, ka_proc_dir_entry_t *parent)
{
char name[PROC_FS_NAME_LEN];
proc_fs_format_task_dir_name(task, name, PROC_FS_NAME_LEN);
(void)ka_fs_remove_proc_subtree((const char *)name, parent);
}
void proc_fs_xsmem_pool_add_task(struct xsm_task_pool_node *node)
{
ka_proc_dir_entry_t *entry = NULL;
struct xsm_pool *xp = node->pool;
struct xsm_task *task = node->task;
if (xp->entry != NULL) {
entry = proc_fs_mk_task_dir(task, xp->entry);
if (entry != NULL) {
(void)ka_fs_proc_create_data("block", PROC_FS_MODE, entry, &task_node_ops, node);
}
}
if (task->entry != NULL) {
entry = ka_fs_proc_mkdir(xp->key, task->entry);
if (entry != NULL) {
(void)ka_fs_proc_create_data("block", PROC_FS_MODE, entry, &task_node_ops, node);
}
}
}
void proc_fs_xsmem_pool_del_task(struct xsm_task_pool_node *node)
{
struct xsm_pool *xp = node->pool;
struct xsm_task *task = node->task;
if (xp->entry != NULL) {
proc_fs_rm_task_dir(task, xp->entry);
}
if (task->entry != NULL) {
(void)ka_fs_remove_proc_subtree(xp->key, task->entry);
}
}
void proc_fs_add_xsmem_pool(struct xsm_pool *xp)
{
if (xp_entry != NULL) {
xp->entry = ka_fs_proc_mkdir(xp->key, xp_entry);
}
}
void proc_fs_del_xsmem_pool(struct xsm_pool *xp)
{
if (xp_entry != NULL) {
(void)ka_fs_remove_proc_subtree(xp->key, xp_entry);
}
}
static void per_task_info_show(ka_seq_file_t *seq, struct xsm_task *task)
{
struct xsm_task_pool_node *node = NULL;
u64 alloc_size = 0, real_alloc_size = 0;
ka_fs_seq_printf(seq, "pool id(name): alloc_size real_size peak_size\n");
ka_list_for_each_entry(node, &task->node_list_head, task_node) {
ka_fs_seq_printf(seq, "%d(%s) %-8llu %-8llu %-8llu\n", node->pool->pool_id, node->pool->key,
node->alloc_size, node->real_alloc_size, node->alloc_peak_size);
alloc_size += node->alloc_size;
real_alloc_size += node->real_alloc_size;
}
ka_fs_seq_printf(seq, "summary: %-8llu %-8llu\n", alloc_size, real_alloc_size);
}
static int task_info_show(ka_seq_file_t *seq, void *offset)
{
struct xsm_task *task = (struct xsm_task *)seq->private;
ka_task_mutex_lock(&task->mutex);
ka_fs_seq_printf(seq, "task pid %d pool cnt %d\n", task->pid, task->attached_pool_count);
per_task_info_show(seq, task);
ka_task_mutex_unlock(&task->mutex);
return 0;
}
STATIC int task_sum_open(ka_inode_t *inode, ka_file_t *file)
{
return ka_single_open(inode, file, task_info_show);
}
STATIC_PROCFS_FILE_FUNC_OPS_OPEN(task_sum_ops, task_sum_open);
void proc_fs_add_task(struct xsm_task *task)
{
if (task_entry == NULL) {
return;
}
task->entry = proc_fs_mk_task_dir(task, task_entry);
if (task->entry != NULL) {
(void)ka_fs_proc_create_data("summary", PROC_FS_MODE, task->entry, &task_sum_ops, task);
}
}
void proc_fs_del_task(struct xsm_task *task)
{
if (task_entry == NULL) {
return;
}
proc_fs_rm_task_dir(task, task_entry);
}
static int per_pool_info_show(int id, void *p, void *data)
{
struct xsm_pool *xp = p;
ka_seq_file_t *seq = data;
ka_fs_seq_printf(seq, "id:%d\n key:%s, create_pid %d, refcnt:%d, algo:%s, alloc_size %lu, real_alloc_size %lu, "
"alloc_peak_size %lu\n", id, xp->key, xp->create_pid, ka_base_atomic_read(&xp->refcnt), xp->algo->name,
xp->alloc_size, xp->real_alloc_size, xp->alloc_peak_size);
if (xp->algo->xsm_pool_show) {
ka_task_mutex_lock(&xp->xp_block_mutex);
xp->algo->xsm_pool_show(xp, seq);
ka_task_mutex_unlock(&xp->xp_block_mutex);
}
return 0;
}
static int pool_info_show(ka_seq_file_t *seq, void *offset)
{
int ret;
ka_fs_seq_printf(seq, "pool list:\n");
ka_task_mutex_lock(&xsmem_mutex);
ret = ka_base_idr_for_each(&xsmem_idr, per_pool_info_show, seq);
ka_task_mutex_unlock(&xsmem_mutex);
return ret;
}
STATIC int xsmem_pool_sum_open(ka_inode_t *inode, ka_file_t *file)
{
return ka_single_open(inode, file, pool_info_show);
}
STATIC_PROCFS_FILE_FUNC_OPS_OPEN(pool_sum_ops, xsmem_pool_sum_open);
void xsmem_proc_fs_init(void)
{
top_entry = ka_fs_proc_mkdir("xsmem", NULL);
if (top_entry != NULL) {
xp_entry = ka_fs_proc_mkdir("pool", top_entry);
if (xp_entry != NULL) {
(void)ka_fs_proc_create_data("summary", PROC_FS_MODE, xp_entry, &pool_sum_ops, NULL);
}
task_entry = ka_fs_proc_mkdir("task", top_entry);
}
}
void xsmem_proc_fs_uninit(void)
{
if (top_entry != NULL) {
(void)ka_fs_remove_proc_subtree("xsmem", NULL);
}
}