* 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 "pbl_feature_loader.h"
#include "pbl_uda.h"
#include "svm_kern_log.h"
#include "svm_proc_fs.h"
#include "svm_task.h"
#include "framework_dev.h"
#include "svm_slab.h"
#include "svm_dev.h"
static u32 svm_udev_max_num;
static struct array_ctx_domain *svm_dev_domain = NULL;
static u32 dev_feature_num = 0;
u32 svm_dev_obtain_feature_id(void)
{
return dev_feature_num++;
}
int svm_dev_set_feature_priv(void *dev_ctx, u32 feature_id, const char *feature_name, void *priv)
{
struct svm_dev_ctx *d_ctx = (struct svm_dev_ctx *)dev_ctx;
if (feature_id >= dev_feature_num) {
return -EINVAL;
}
d_ctx->feature[feature_id].name = feature_name;
d_ctx->feature[feature_id].priv = priv;
return 0;
}
void *svm_dev_get_feature_priv(void *dev_ctx, u32 feature_id)
{
struct svm_dev_ctx *d_ctx = (struct svm_dev_ctx *)dev_ctx;
if (feature_id >= dev_feature_num) {
return NULL;
}
return d_ctx->feature[feature_id].priv;
}
static void svm_dev_release_check_feature(struct svm_dev_ctx *d_ctx)
{
u32 i;
for (i = 0; i < dev_feature_num; i++) {
struct svm_dev_feature *feature = &d_ctx->feature[i];
if (feature->name != NULL) {
svm_err("Feature not exit. (udevid=%u; name=%s)\n", d_ctx->udevid, feature->name);
}
}
}
static struct svm_dev_ctx *_svm_dev_ctx_get(u32 udevid)
{
struct array_ctx *ctx = array_ctx_get(svm_dev_domain, udevid);
if (ctx == NULL) {
return NULL;
}
return (struct svm_dev_ctx *)array_ctx_priv(ctx);
}
void *svm_dev_ctx_get(u32 udevid)
{
return _svm_dev_ctx_get(udevid);
}
static void _svm_dev_ctx_put(struct svm_dev_ctx *dev_ctx)
{
array_ctx_put(dev_ctx->ctx);
}
void svm_dev_ctx_put(void *dev_ctx)
{
_svm_dev_ctx_put((struct svm_dev_ctx *)dev_ctx);
}
static void svm_add_dev_feature_fs(struct svm_dev_ctx *d_ctx)
{
static u32 inst = 0;
d_ctx->entry = svm_proc_fs_add_dev(d_ctx->udevid, inst++);
if (d_ctx->entry != NULL) {
u32 i;
for (i = 0; i < dev_feature_num; i++) {
if (d_ctx->feature[i].name != NULL) {
svm_proc_dev_add_feature(d_ctx->udevid, d_ctx->entry, i, d_ctx->feature[i].name);
}
}
}
}
static void svm_del_dev_feature_fs(struct svm_dev_ctx *d_ctx)
{
svm_proc_fs_del_dev(d_ctx->entry);
}
static void svm_dev_ctx_release(struct array_ctx *ctx)
{
struct svm_dev_ctx *d_ctx = (struct svm_dev_ctx *)array_ctx_priv(ctx);
svm_inst_trace("Release dev success. (udevid=%u)\n", d_ctx->udevid);
svm_dev_release_check_feature(d_ctx);
svm_del_dev_feature_fs(d_ctx);
task_ctx_domain_destroy(d_ctx->domain);
svm_vfree(d_ctx);
}
static int _svm_add_dev(u32 udevid)
{
struct svm_dev_ctx *d_ctx = NULL;
unsigned long size = sizeof(*d_ctx) + dev_feature_num * sizeof(struct svm_dev_feature);
int ret;
d_ctx = svm_vzalloc(size);
if (d_ctx == NULL) {
svm_err("Vmalloc dev ctx fail. (size=%lu)\n", size);
return -ENOMEM;
}
d_ctx->udevid = udevid;
d_ctx->domain = task_ctx_domain_create("svm_task", SVM_MAX_TASK_PER_DEV);
if (d_ctx->domain == NULL) {
svm_vfree(d_ctx);
svm_err("Create task domain fail. (udevid=%u)\n", udevid);
return -ENOMEM;
}
ret = array_ctx_create(svm_dev_domain, udevid, d_ctx, svm_dev_ctx_release);
if (ret != 0) {
task_ctx_domain_destroy(d_ctx->domain);
svm_vfree(d_ctx);
svm_err("Create fail. (ret=%d; udevid=%u)\n", ret, udevid);
return ret;
}
d_ctx->ctx = array_ctx_get(svm_dev_domain, udevid);
array_ctx_put(d_ctx->ctx);
return 0;
}
static int _svm_del_dev(u32 udevid)
{
struct svm_dev_ctx *d_ctx = _svm_dev_ctx_get(udevid);
if (d_ctx == NULL) {
return -EINVAL;
}
_svm_dev_ctx_put(d_ctx);
svm_inst_trace("Before destroy. (udevid=%u; ref_cnt=%d)\n", udevid, array_ctx_ref_cnt(d_ctx->ctx));
array_ctx_destroy(d_ctx->ctx);
return 0;
}
int svm_add_dev(u32 udevid)
{
struct svm_dev_ctx *dev_ctx = NULL;
int ret;
ret = _svm_add_dev(udevid);
if (ret != 0) {
return ret;
}
ret = module_feature_auto_init_dev(udevid);
if (ret != 0) {
svm_err("Init feature failed. (udevid=%u)\n", udevid);
(void)_svm_del_dev(udevid);
return ret;
}
dev_ctx = _svm_dev_ctx_get(udevid);
svm_add_dev_feature_fs(dev_ctx);
_svm_dev_ctx_put(dev_ctx);
svm_inst_trace("Add dev success. (udevid=%u)\n", udevid);
return 0;
}
int svm_del_dev(u32 udevid)
{
struct svm_dev_ctx *dev_ctx = _svm_dev_ctx_get(udevid);
if (dev_ctx != NULL) {
task_ctx_for_each_safe(dev_ctx->domain, NULL, svm_task_ctx_recycle);
module_feature_auto_uninit_dev(udevid);
_svm_dev_ctx_put(dev_ctx);
}
return _svm_del_dev(udevid);
}
static void svm_dev_ctx_recycle(struct array_ctx *ctx, void *priv)
{
svm_warn("Recycle dev ctx. (udevid=%u)\n", ctx->id);
(void)svm_del_dev(ctx->id);
}
static int svm_pre_hotreset(u32 udevid)
{
struct svm_dev_ctx *dev_ctx = NULL;
int ret;
if (uda_is_phy_dev(udevid) == false) {
return 0;
}
dev_ctx = _svm_dev_ctx_get(udevid);
if (dev_ctx == NULL) {
return -ENODEV;
}
ret = task_ctx_domain_set_inactive(dev_ctx->domain);
_svm_dev_ctx_put(dev_ctx);
return ret;
}
static int svm_pre_hotreset_cancel(u32 udevid)
{
struct svm_dev_ctx *dev_ctx = NULL;
if (uda_is_phy_dev(udevid) == false) {
return 0;
}
dev_ctx = _svm_dev_ctx_get(udevid);
if (dev_ctx == NULL) {
return -ENODEV;
}
task_ctx_domain_set_active(dev_ctx->domain);
_svm_dev_ctx_put(dev_ctx);
return 0;
}
static int svm_uda_notifier(u32 udevid, enum uda_notified_action action)
{
int ret = 0;
if (udevid >= svm_udev_max_num) {
svm_err("Invalid para. (udevid=%u)\n", udevid);
return -EINVAL;
}
if (action == UDA_INIT) {
ret = svm_add_dev(udevid);
} else if (action == UDA_UNINIT) {
ret = svm_del_dev(udevid);
} else if (action == UDA_PRE_HOTRESET) {
ret = svm_pre_hotreset(udevid);
} else if (action == UDA_HOTRESET) {
ret = svm_pre_hotreset(udevid);
} else if (action == UDA_PRE_HOTRESET_CANCEL) {
ret = svm_pre_hotreset_cancel(udevid);
} else if (action == UDA_HOTRESET_CANCEL) {
ret = svm_pre_hotreset_cancel(udevid);
}
svm_inst_trace("notifier action. (udevid=%u; action=%d; ret=%d)\n", udevid, action, ret);
return ret;
}
static int svm_uda_notifier_register(void)
{
struct uda_dev_type type;
int ret;
uda_davinci_local_real_agent_type_pack(&type);
ret = uda_real_virtual_notifier_register("svm_agent", &type, UDA_PRI2, svm_uda_notifier);
if (ret != 0) {
svm_err("Uda register agent notifier failed. (ret=%d)\n", ret);
return ret;
}
uda_davinci_near_real_entity_type_pack(&type);
ret = uda_real_virtual_notifier_register("svm_master", &type, UDA_PRI2, svm_uda_notifier);
if (ret != 0) {
svm_err("Uda register master notifier failed. (ret=%d)\n", ret);
uda_davinci_local_real_agent_type_pack(&type);
(void)uda_real_virtual_notifier_unregister("svm_agent", &type);
return ret;
}
return 0;
}
static void svm_uda_notifier_unregister(void)
{
struct uda_dev_type type;
uda_davinci_local_real_agent_type_pack(&type);
(void)uda_real_virtual_notifier_unregister("svm_agent", &type);
uda_davinci_near_real_entity_type_pack(&type);
(void)uda_real_virtual_notifier_unregister("svm_master", &type);
}
int svm_dev_init(void)
{
int ret;
svm_udev_max_num = uda_get_udev_max_num();
svm_dev_domain = array_ctx_domain_create("svm_dev", svm_udev_max_num);
if (svm_dev_domain == NULL) {
svm_err("Create dev domain fail.\n");
return -ENOMEM;
}
ret = svm_uda_notifier_register();
if (ret != 0) {
array_ctx_domain_destroy(svm_dev_domain);
svm_dev_domain = NULL;
return ret;
}
return 0;
}
DECLAER_FEATURE_AUTO_INIT(svm_dev_init, FEATURE_LOADER_STAGE_7);
void svm_dev_uninit(void)
{
svm_uda_notifier_unregister();
array_ctx_for_each_safe(svm_dev_domain, NULL, svm_dev_ctx_recycle);
array_ctx_domain_destroy(svm_dev_domain);
svm_dev_domain = NULL;
}
DECLAER_FEATURE_AUTO_UNINIT(svm_dev_uninit, FEATURE_LOADER_STAGE_7);