* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
#ifndef UT_TEST
#include <stdlib.h>
#include <sys/prctl.h>
#include <semaphore.h>
#include <signal.h>
#include <time.h>
#include <pthread.h>
#include "drv_user_common.h"
#include "prof_adapt.h"
#include "prof_buff.h"
#include "prof_adapt_user.h"
static struct prof_user_kernel_ops g_user_kernel_ops = {NULL};
void prof_user_regiser_kernel_ops(struct prof_user_kernel_ops *ops)
{
g_user_kernel_ops.chan_register = ops->chan_register;
g_user_kernel_ops.chan_query = ops->chan_query;
g_user_kernel_ops.chan_writer = ops->chan_writer;
}
STATIC struct prof_user_kernel_ops *prof_user_get_kernel_ops(void)
{
return &g_user_kernel_ops;
}
struct prof_user_chan_node {
struct list_head list_node;
uint32_t dev_id;
uint32_t chan_id;
struct prof_sample_ops ops;
* so it is necessary to ensure that the semaphore memory is always available */
sem_t sem;
};
static LIST_HEAD(g_prof_user_chan_list);
static pthread_mutex_t g_chan_list_mutex = PTHREAD_MUTEX_INITIALIZER;
STATIC drvError_t prof_user_chan_add(uint32_t dev_id, uint32_t chan_id, struct prof_sample_register_para *para)
{
struct prof_user_chan_node *node = NULL;
node = (struct prof_user_chan_node *)malloc(sizeof(*node));
if (node == NULL) {
PROF_ERR("Failed to alloc chan_node. (dev_id=%u, chan_id=%u)\n", dev_id, chan_id);
return DRV_ERROR_MALLOC_FAIL;
}
node->dev_id = dev_id;
node->chan_id = chan_id;
node->ops.start_func = para->ops.start_func;
node->ops.sample_func = para->ops.sample_func;
node->ops.flush_func = para->ops.flush_func;
node->ops.stop_func = para->ops.stop_func;
(void)pthread_mutex_lock(&g_chan_list_mutex);
drv_user_list_add_tail(&node->list_node, &g_prof_user_chan_list);
(void)pthread_mutex_unlock(&g_chan_list_mutex);
return DRV_ERROR_NONE;
}
STATIC void prof_user_chan_del(uint32_t dev_id, uint32_t chan_id)
{
struct prof_user_chan_node *node = NULL, *chan_node = NULL;
struct list_head *pos = NULL, *n = NULL;
(void)pthread_mutex_lock(&g_chan_list_mutex);
list_for_each_safe(pos, n, &g_prof_user_chan_list) {
node = list_entry(pos, struct prof_user_chan_node, list_node);
if ((node->dev_id == dev_id) && (node->chan_id == chan_id)) {
drv_user_list_del(&node->list_node);
chan_node = node;
break;
}
}
(void)pthread_mutex_unlock(&g_chan_list_mutex);
if (chan_node != NULL) {
free(chan_node);
}
}
STATIC drvError_t prof_user_chan_get(uint32_t dev_id, uint32_t chan_id, struct prof_sample_ops *ops, sem_t **sem)
{
struct prof_user_chan_node *node = NULL;
struct list_head *pos = NULL, *n = NULL;
(void)pthread_mutex_lock(&g_chan_list_mutex);
list_for_each_safe(pos, n, &g_prof_user_chan_list) {
node = list_entry(pos, struct prof_user_chan_node, list_node);
if ((node->dev_id == dev_id) && (node->chan_id == chan_id)) {
ops->start_func = node->ops.start_func;
ops->sample_func = node->ops.sample_func;
ops->flush_func = node->ops.flush_func;
ops->stop_func = node->ops.stop_func;
*sem = &node->sem;
(void)pthread_mutex_unlock(&g_chan_list_mutex);
return DRV_ERROR_NONE;
}
}
(void)pthread_mutex_unlock(&g_chan_list_mutex);
return DRV_ERROR_NOT_EXIST;
}
STATIC bool prof_user_is_need_kernel_reg(uint32_t chan_id)
{
uint32_t support_chan_list[] = {CHANNEL_AICPU, CHANNEL_CUS_AICPU, CHANNEL_ADPROF};
uint32_t i;
for (i = 0; i < sizeof(support_chan_list) / sizeof(uint32_t); i++) {
if (support_chan_list[i] == chan_id) {
return true;
}
}
return false;
}
drvError_t prof_user_register_channel(uint32_t dev_id, uint32_t chan_id, struct prof_sample_register_para *para)
{
struct prof_user_kernel_ops *kernel_ops = NULL;
drvError_t ret;
ret = prof_user_chan_add(dev_id, chan_id, para);
if (ret != DRV_ERROR_NONE) {
PROF_ERR("Failed to register channel to user. (dev_id=%u, chan_id=%u, ret=%d)\n", dev_id, chan_id, (int)ret);
return ret;
}
if (prof_user_is_need_kernel_reg(chan_id) == false) {
return DRV_ERROR_NONE;
}
kernel_ops = prof_user_get_kernel_ops();
if (kernel_ops->chan_register != NULL) {
ret = kernel_ops->chan_register(dev_id, chan_id);
if (ret != DRV_ERROR_NONE) {
prof_user_chan_del(dev_id, chan_id);
PROF_ERR("Failed to register channel to kernel. (dev_id=%u, chan_id=%u, ret=%d)\n", dev_id, chan_id, (int)ret);
return ret;
}
}
return DRV_ERROR_NONE;
}
enum prof_user_sample_type {
PROF_SAMPLE_LOCAL_MODE,
PROF_SAMPLE_REMOTER_MODE
};
struct prof_user_chan_priv {
uint32_t dev_id;
uint32_t chan_id;
uint32_t sample_mode;
uint32_t sample_thread_flag;
pthread_t sample_thread;
timer_t timer_fd;
sem_t *sem;
struct prof_sample_ops ops;
uint8_t *buff;
};
STATIC drvError_t prof_user_chan_init(uint32_t dev_id, uint32_t chan_id, bool event_flag, char **priv)
{
struct prof_user_chan_priv *chan_priv = NULL;
drvError_t ret;
chan_priv = (struct prof_user_chan_priv *)malloc(sizeof(*chan_priv));
if (chan_priv == NULL) {
PROF_ERR("Failed to alloc chan_priv. (dev_id=%u, chan_id=%u)\n", dev_id, chan_id);
return DRV_ERROR_MALLOC_FAIL;
}
ret = prof_user_chan_get(dev_id, chan_id, &chan_priv->ops, &chan_priv->sem);
if (ret != DRV_ERROR_NONE) {
free(chan_priv);
return DRV_ERROR_NOT_SUPPORT;
}
chan_priv->dev_id = dev_id;
chan_priv->chan_id = chan_id;
chan_priv->sample_mode = event_flag ? PROF_SAMPLE_REMOTER_MODE : PROF_SAMPLE_LOCAL_MODE;
chan_priv->sample_thread_flag = 0;
chan_priv->timer_fd = NULL;
(void)sem_init(chan_priv->sem, 0, 0);
if (chan_priv->sample_mode == PROF_SAMPLE_LOCAL_MODE) {
ret = prof_buff_init(chan_id, &chan_priv->buff);
if (ret != DRV_ERROR_NONE) {
free(chan_priv);
PROF_ERR("Failed to init buff. (dev_id=%u, chan_id=%u)\n", dev_id, chan_id);
return ret;
}
} else {
chan_priv->buff = NULL;
}
*priv = (char *)chan_priv;
return DRV_ERROR_NONE;
}
STATIC void prof_user_chan_uninit(char **priv)
{
struct prof_user_chan_priv *chan_priv = (struct prof_user_chan_priv *)(*priv);
if (chan_priv->sample_mode == PROF_SAMPLE_LOCAL_MODE) {
prof_buff_uninit(&chan_priv->buff);
}
free(chan_priv);
*priv = NULL;
}
STATIC drvError_t prof_user_sample_start_check(struct prof_user_chan_priv *chan_priv,
struct prof_user_start_para *para)
{
if (para->sample_period == 0) {
return DRV_ERROR_NONE;
}
if ((para->sample_period < PROF_PERIOD_MIN) || (para->sample_period > PROF_PERIOD_MAX)) {
PROF_ERR("Invalid sample period. (dev_id=%u, chan_id=%u, sample_period=%ums)\n",
chan_priv->dev_id, chan_priv->chan_id, para->sample_period);
return DRV_ERROR_INVALID_VALUE;
}
if (chan_priv->ops.sample_func == NULL) {
PROF_ERR("No sample_func. (dev_id=%u, chan_id=%u, sample_period=%ums)\n",
chan_priv->dev_id, chan_priv->chan_id, para->sample_period);
return DRV_ERROR_INVALID_VALUE;
}
return DRV_ERROR_NONE;
}
STATIC drvError_t prof_user_sample_start(struct prof_user_chan_priv *chan_priv, struct prof_user_start_para *para)
{
struct prof_sample_start_para start_para = {0};
int ret;
if (chan_priv->ops.start_func != NULL) {
start_para.dev_id = chan_priv->dev_id;
start_para.user_data = para->user_data;
start_para.user_data_len = para->user_data_size;
ret = chan_priv->ops.start_func(&start_para);
if (ret != PROF_OK) {
PROF_ERR("Failed to invoke start func. (dev_id=%u, chan_id=%u, ret=%d)\n",
chan_priv->dev_id, chan_priv->chan_id, ret);
return DRV_ERROR_INVALID_HANDLE;
}
}
return DRV_ERROR_NONE;
}
STATIC void prof_user_sample_stop(struct prof_user_chan_priv *chan_priv)
{
struct prof_sample_stop_para stop_para = {0};
int ret;
if (chan_priv->ops.stop_func != NULL) {
stop_para.dev_id = chan_priv->dev_id;
ret = chan_priv->ops.stop_func(&stop_para);
if (ret != PROF_OK) {
PROF_ERR("Failed to invoke stop func. (dev_id=%u, chan_id=%u, ret=%d)\n",
chan_priv->dev_id, chan_priv->chan_id, ret);
}
}
}
STATIC void *prof_user_sample_thread(void *arg)
{
(void)prctl(PR_SET_NAME, "prof_sample");
struct prof_user_chan_priv *chan_priv = (struct prof_user_chan_priv *)arg;
struct prof_adapt_core_notifier *notifier = prof_adapt_get_notifier();
struct prof_sample_para para = {0};
uint32_t dev_id = chan_priv->dev_id;
uint32_t chan_id = chan_priv->chan_id;
uint32_t buff_len = 100 * 1024;
uint8_t *buff;
int ret;
PROF_INFO("Sample thread start. (dev_id=%u, chan_id=%u)\n", dev_id, chan_id);
buff = (uint8_t *)malloc(buff_len);
if (buff == NULL) {
PROF_ERR("Failed to malloc buff mem. (dev_id=%u, chan_id=%u)\n", dev_id, chan_id);
return NULL;
}
para.sample_flag = SAMPLE_DATA_WITH_HEADER;
while (chan_priv->sample_thread_flag) {
(void)sem_wait(chan_priv->sem);
para.dev_id = dev_id;
para.target_pid = getpid();
para.buff = buff;
para.buff_len = buff_len;
para.report_len = 0;
ret = chan_priv->ops.sample_func(¶);
if (ret == DRV_ERROR_CALL_NO_RETRY) {
break;
}
if ((ret != 0) || (para.report_len > buff_len)) {
continue;
}
if (para.report_len != 0) {
(void)notifier->chan_report(dev_id, chan_id, para.buff, para.report_len, false);
}
para.sample_flag = SAMPLE_DATA_ONLY;
}
free(buff);
PROF_INFO("Sample thread exit. (dev_id=%u, chan_id=%u)\n", dev_id, chan_id);
return NULL;
}
STATIC drvError_t prof_user_sample_thread_enable(struct prof_user_chan_priv *chan_priv)
{
pthread_attr_t attr;
int ret;
chan_priv->sample_thread_flag = 1;
(void)pthread_attr_init(&attr);
(void)pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
ret = pthread_create(&chan_priv->sample_thread, &attr, prof_user_sample_thread, chan_priv);
if (ret != 0) {
chan_priv->sample_thread_flag = 0;
PROF_ERR("Failed to create the sample thread. (ret=%d)\n", ret);
return DRV_ERROR_INNER_ERR;
}
(void)pthread_attr_destroy(&attr);
return DRV_ERROR_NONE;
}
STATIC void prof_user_sample_thread_disable(struct prof_user_chan_priv *chan_priv)
{
if (chan_priv->sample_thread_flag == 0) {
return;
}
chan_priv->sample_thread_flag = 0;
(void)sem_post(chan_priv->sem);
(void)pthread_join(chan_priv->sample_thread, NULL);
}
STATIC void prof_user_sample_timer_handle(union sigval v)
{
sem_t *sem = (sem_t *)v.sival_ptr;
(void)sem_post(sem);
}
STATIC drvError_t prof_user_sample_timer_init(struct prof_user_chan_priv *chan_priv, uint32_t sample_period)
{
struct sigevent sig_ev = {0};
struct itimerspec ts;
int ret;
sig_ev.sigev_value.sival_ptr = chan_priv->sem;
sig_ev.sigev_notify = SIGEV_THREAD;
sig_ev.sigev_notify_function = prof_user_sample_timer_handle;
ret = timer_create(CLOCK_MONOTONIC, &sig_ev, &chan_priv->timer_fd);
if (ret != 0) {
PROF_ERR("Failed to create timer. (ret=%d)\n", ret);
return DRV_ERROR_INNER_ERR;
}
ts.it_interval.tv_sec = sample_period / 1000;
ts.it_interval.tv_nsec = (sample_period % 1000) * 1000000;
ts.it_value.tv_sec = ts.it_interval.tv_sec;
ts.it_value.tv_nsec = ts.it_interval.tv_nsec;
ret = timer_settime(chan_priv->timer_fd, 0, &ts, NULL);
if (ret != 0) {
(void)timer_delete(chan_priv->timer_fd);
PROF_ERR("Failed to set timer. (ret=%d)\n", ret);
return DRV_ERROR_INNER_ERR;
}
return DRV_ERROR_NONE;
}
STATIC void prof_user_sample_timer_uninit(struct prof_user_chan_priv *chan_priv)
{
int ret;
if (chan_priv->timer_fd == NULL) {
return;
}
ret = timer_delete(chan_priv->timer_fd);
if (ret != 0) {
PROF_ERR("Failed to delete timer. (ret=%d)\n", ret);
}
chan_priv->timer_fd = NULL;
}
STATIC drvError_t prof_user_chan_start(uint32_t dev_id, uint32_t chan_id, struct prof_user_start_para *para,
char *priv)
{
struct prof_user_chan_priv *chan_priv = (struct prof_user_chan_priv *)priv;
drvError_t ret;
ret = prof_user_sample_start_check(chan_priv, para);
if (ret != DRV_ERROR_NONE) {
PROF_ERR("Failed to check para. (dev_id=%u, chan_id=%u, ret=%d)\n", dev_id, chan_id, (int)ret);
return ret;
}
ret = prof_user_sample_start(chan_priv, para);
if (ret != DRV_ERROR_NONE) {
PROF_ERR("Failed to start user sample. (dev_id=%u, chan_id=%u, ret=%d)\n", dev_id, chan_id, (int)ret);
return ret;
}
if (para->sample_period == 0) {
return DRV_ERROR_NONE;
}
ret = prof_user_sample_thread_enable(chan_priv);
if (ret != DRV_ERROR_NONE) {
prof_user_sample_stop(chan_priv);
PROF_ERR("Failed to enable thread. (dev_id=%u, chan_id=%u, ret=%d)\n", dev_id, chan_id, (int)ret);
return ret;
}
ret = prof_user_sample_timer_init(chan_priv, para->sample_period);
if (ret != DRV_ERROR_NONE) {
prof_user_sample_thread_disable(chan_priv);
prof_user_sample_stop(chan_priv);
PROF_ERR("Failed to init timer. (dev_id=%u, chan_id=%u, ret=%d)\n", dev_id, chan_id, (int)ret);
return ret;
}
return DRV_ERROR_NONE;
}
STATIC drvError_t prof_user_chan_stop(uint32_t dev_id, uint32_t chan_id, struct prof_user_stop_para *para,
char *priv)
{
(void)para;
struct prof_user_chan_priv *chan_priv = (struct prof_user_chan_priv *)priv;
prof_user_sample_timer_uninit(chan_priv);
prof_user_sample_thread_disable(chan_priv);
prof_user_sample_stop(chan_priv);
if (chan_priv->sample_mode == PROF_SAMPLE_LOCAL_MODE) {
prof_buff_wait_read_empty(chan_priv->buff, dev_id, chan_id);
}
return DRV_ERROR_NONE;
}
STATIC drvError_t prof_user_chan_flush(uint32_t dev_id, uint32_t chan_id, uint32_t *data_len, char *priv)
{
(void)dev_id;
(void)chan_id;
struct prof_user_chan_priv *chan_priv = (struct prof_user_chan_priv *)priv;
if (chan_priv->sample_mode == PROF_SAMPLE_LOCAL_MODE) {
*data_len = prof_buff_get_data_len(chan_priv->buff);
return DRV_ERROR_NONE;
} else {
return DRV_ERROR_NOT_SUPPORT;
}
}
STATIC int prof_user_chan_read(uint32_t dev_id, uint32_t chan_id, prof_user_read_para_t *read_para, char *priv)
{
(void)dev_id;
(void)chan_id;
struct prof_user_chan_priv *chan_priv = (struct prof_user_chan_priv *)priv;
if (chan_priv->sample_mode == PROF_SAMPLE_LOCAL_MODE) {
return prof_buff_read(chan_priv->buff, read_para->out_buf, read_para->buf_size);
} else {
return 0;
}
}
STATIC drvError_t prof_user_chan_query(uint32_t dev_id, uint32_t chan_id, uint32_t *avail_len, char *priv)
{
struct prof_user_chan_priv *chan_priv = (struct prof_user_chan_priv *)priv;
struct prof_user_kernel_ops *kernel_ops = NULL;
drvError_t ret;
if (chan_priv->sample_mode == PROF_SAMPLE_LOCAL_MODE) {
*avail_len = prof_buff_get_avail_len(chan_priv->buff);
return DRV_ERROR_NONE;
}
kernel_ops = prof_user_get_kernel_ops();
if (kernel_ops->chan_query != NULL) {
ret = kernel_ops->chan_query(dev_id, chan_id, avail_len);
if (ret != DRV_ERROR_NONE) {
PROF_ERR("Failed to query avail_len from kernel. (dev_id=%u, chan_id=%u, ret=%d)\n",
dev_id, chan_id, (int)ret);
return ret;
}
}
return DRV_ERROR_NONE;
}
STATIC drvError_t prof_user_chan_report(uint32_t dev_id, uint32_t chan_id, void *data, uint32_t data_len,
char *priv)
{
struct prof_user_chan_priv *chan_priv = (struct prof_user_chan_priv *)priv;
struct prof_adapt_core_notifier *notifier = NULL;
struct prof_user_kernel_ops *kernel_ops = NULL;
drvError_t ret;
if (chan_priv->sample_mode == PROF_SAMPLE_LOCAL_MODE) {
ret = prof_buff_write(chan_priv->buff, data, data_len);
if (ret != DRV_ERROR_NONE) {
PROF_ERR("Failed to write data to local buff. (dev_id=%u, chan_id=%u, data_len=%u, ret=%d)\n",
dev_id, chan_id, data_len, (int)ret);
return ret;
}
notifier = prof_adapt_get_notifier();
notifier->poll_report(dev_id, chan_id);
return DRV_ERROR_NONE;
}
kernel_ops = prof_user_get_kernel_ops();
if (kernel_ops->chan_writer != NULL) {
ret = kernel_ops->chan_writer(dev_id, chan_id, data, data_len);
if (ret != DRV_ERROR_NONE) {
PROF_ERR("Failed to write data to kernel. (dev_id=%u, chan_id=%u, data_len=%u, ret=%d)\n",
dev_id, chan_id, data_len, (int)ret);
return ret;
}
}
return DRV_ERROR_NONE;
}
static struct prof_chan_ops prof_user_chan_ops = {
.init = prof_user_chan_init,
.uninit = prof_user_chan_uninit,
.start = prof_user_chan_start,
.stop = prof_user_chan_stop,
.flush = prof_user_chan_flush,
.read = prof_user_chan_read,
.query = prof_user_chan_query,
.report = prof_user_chan_report,
};
drvError_t prof_user_get_chan_ops(struct prof_chan_ops **ops)
{
*ops = &prof_user_chan_ops;
return DRV_ERROR_NONE;
}
#else
int prof_adapt_user_ut_test(void)
{
return 0;
}
#endif