* 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.
*/
#ifndef DRV_HOST
#include <linux/profile.h>
#endif
#include "pbl/pbl_davinci_api.h"
#include "uda_notifier.h"
#include "pbl_mem_alloc_interface.h"
#include "pbl/pbl_runenv_config.h"
#include "uda_dev.h"
#ifdef CFG_FEATURE_KA
#include "pbl_kernel_adapt.h"
#endif
#ifdef CFG_FEATURE_ASCEND950_STUB
#include "uda_fops.h"
#endif
#include "ka_dfx_pub.h"
#include "ka_memory_pub.h"
#include "ka_kernel_def_pub.h"
#include "ka_common_pub.h"
#include "ka_fs_pub.h"
#include "ka_errno_pub.h"
#include "ka_driver_pub.h"
#include "ka_task_pub.h"
#include "ka_list_pub.h"
#include "ka_base_pub.h"
#include "ka_system_pub.h"
#include "uda_access_adapt.h"
#define UDA_HOST_ROOT_TGID 1
#define FREE_NS_NODE_TRY_COUNT 20
#define FREE_NS_NODE_INTERVAL_MS 50
struct uda_dev_shared_check {
u32 udevid;
u32 share_occupied;
};
static u32 uda_detected_dev_num = 1;
static ka_dev_t uda_dev;
static ka_class_t *uda_class;
static ka_work_struct_t host_ns_init_work;
static ka_mnt_namespace_t *host_ns = NULL;
static ka_mutex_t access_ns_mutex;
ka_list_head_t access_ns_head;
static u32 uda_ns_id[UDA_NS_NUM];
static ka_kuid_t g_phy_dev_uid = GLOBAL_ROOT_UID;
static ka_kgid_t g_phy_dev_gid = GLOBAL_ROOT_GID;
static umode_t g_phy_dev_mode = DEV_MODE_PERMISSION;
static u8 g_dev_shared[UDA_MAX_PHY_DEV_NUM];
void uda_init_udev_ns_id(u32 udevid, struct uda_access *access, ka_mnt_namespace_t *ns, u32 ns_id);
int uda_occupy_dev_by_ns(u32 udevid, struct uda_access *access, ka_mnt_namespace_t *ns);
void uda_unoccupy_dev_by_ns(u32 udevid, struct uda_access *access, ka_mnt_namespace_t *ns);
bool uda_is_ns_in_udev_shared_list(ka_mnt_namespace_t *ns, struct uda_access *access);
bool uda_tgid_exited(u32 tgid, TASK_TIME_TYPE start_time);
void uda_ns_node_destroy_work(ka_work_struct_t *p_work);
u32 uda_get_ns_pid_count(ka_mnt_namespace_t *ns);
static void uda_destroy_ns_node(struct uda_ns_node *ns_node, bool from_work);
static u32 uda_alloc_ns_id(void)
{
u32 i;
for (i = 0; i < UDA_NS_NUM; i++) {
if (uda_ns_id[i] == 0) {
uda_ns_id[i] = 1;
return i;
}
}
return UDA_NS_NUM;
}
static void uda_free_ns_id(u32 ns_id)
{
uda_ns_id[ns_id] = 0;
}
static ka_mnt_namespace_t *uda_get_task_ns(ka_task_struct_t *task)
{
return (task->nsproxy != NULL) ? task->nsproxy->mnt_ns : NULL;
}
ka_mnt_namespace_t *uda_get_current_ns(void)
{
return uda_get_task_ns(current);
}
static ka_mnt_namespace_t *uda_get_host_ns(void)
{
return host_ns;
}
static void uda_host_ns_init_work(ka_work_struct_t *work)
{
host_ns = uda_get_current_ns();
uda_info("Ns info. (ns=%pK; init.task ns=%pK)\n", host_ns, init_task.nsproxy->mnt_ns);
}
static bool uda_is_admin_task(ka_task_struct_t *task)
{
ka_kernel_cap_t privileged = ka_system_get_privileged_kernel_cap();
const ka_cred_t *cred = NULL;
ka_kernel_cap_t effective;
u32 user_id;
ka_task_rcu_read_lock();
cred = __ka_task_task_cred(task);
effective = cred->cap_effective;
user_id = cred->uid.val;
ka_task_rcu_read_unlock();
#ifndef EMU_ST
if (cred->user_ns != &init_user_ns) {
return false;
}
#endif
return ka_system_kernel_cap_compare(effective, privileged);
}
bool uda_cur_is_host(void)
{
return (uda_get_current_ns() == uda_get_host_ns());
}
static bool cgroup_mount_is_ro(void)
{
ka_path_t kernel_path;
int ret;
bool is_ro = false;
ret = ka_fs_kern_path("/sys/fs/cgroup/memory", 0, &kernel_path);
if (ret != 0) {
return false;
}
if (kernel_path.mnt == NULL) {
ka_fs_path_put(&kernel_path);
uda_err("Cgroup memory path mount is NULL.\n");
return false;
}
is_ro = __ka_fs_mnt_is_readonly(kernel_path.mnt);
ka_fs_path_put(&kernel_path);
return is_ro;
}
static bool uda_current_is_admin(void)
{
if (!uda_is_admin_task(current)) {
return false;
}
if (cgroup_mount_is_ro()) {
return false;
}
return true;
}
bool uda_cur_is_admin(void)
{
return (uda_cur_is_host() || (uda_current_is_admin()));
}
#if (defined DRV_HOST) && (defined CFG_FEATURE_PROCESS_GROUP)
static bool uda_process_group_permission_allow(struct uda_dev_inst *dev_inst)
{
ka_file_t *filp = NULL;
char dev_name[UDA_DEV_NAME_LEN] = { 0 };
unsigned int udevid = dev_inst->udevid;
unsigned int char_flag = dev_inst->para.char_flag;
unsigned int logic_id = dev_inst->para.logic_id;
const char *name = uda_is_phy_dev(udevid) ? UDA_PHY_DEV_NAME : UDA_MIA_DEV_NAME;
unsigned int char_id =
((char_flag == UDA_ENABLE_CHAR_NUMBERING) && (logic_id != UDA_INVALID_UDEVID)) ? logic_id : udevid;
(void)sprintf_s(dev_name, UDA_DEV_NAME_LEN, "/dev/%s%u", name, char_id);
filp = ka_fs_filp_open(dev_name, O_WRONLY, 0);
if (KA_IS_ERR_OR_NULL(filp)) {
return false;
}
ka_fs_filp_close(filp, NULL);
return true;
}
#endif
static bool uda_devcgroup_permission_allow(struct uda_dev_inst *dev_inst)
{
#ifdef CFG_FEATURE_ASCEND950_STUB
if (uda_get_raw_proc_is_contain_flag() == 0) {
return true;
}
#endif
ka_fs_filp_open also check file user, device multi-user configuration can not use */
#if (defined DRV_HOST) && (defined CFG_FEATURE_PROCESS_GROUP)
return uda_process_group_permission_allow(dev_inst);
#else
(void)dev_inst;
return true;
#endif
}
static void uda_init_ns_node_dev(struct uda_ns_node *ns_node, u32 root_tgid, TASK_TIME_TYPE tgid_time)
{
u32 udevid;
u32 max_num = uda_cur_is_admin() ? UDA_MAX_PHY_DEV_NUM : UDA_UDEV_MAX_NUM;
#ifdef CFG_FEATURE_ASCEND950_STUB
max_num = (uda_cur_is_host() && (root_tgid != UDA_HOST_ROOT_TGID)) ? UDA_UDEV_MAX_NUM : max_num;
#endif
ns_node->root_tgid = root_tgid;
ns_node->tgid_time = tgid_time;
ns_node->dev_num = 0;
for (udevid = 0; udevid < max_num; udevid++) {
struct uda_dev_inst *dev_inst = NULL;
if (!uda_can_access_udevid(udevid)) {
continue;
}
dev_inst = uda_dev_inst_get(udevid);
if (dev_inst == NULL) {
uda_err("Should get dev inst. (udevid=%u)", udevid);
continue;
}
if (uda_cur_is_host()) {
if (!uda_devcgroup_permission_allow(dev_inst)) {
uda_dev_inst_put(dev_inst);
continue;
}
}
if (ns_node->dev_num >= UDA_DEV_MAX_NUM) {
uda_warn("Dev num in ns is overflow. (ns_id=%u; ns=%pK; dev_num=%u)",
ns_node->ns_id, ns_node->ns, ns_node->dev_num);
uda_dev_inst_put(dev_inst);
break;
}
ns_node->devid_to_udevid[ns_node->dev_num] = udevid;
ns_node->dev_num++;
uda_init_udev_ns_id(udevid, &dev_inst->access, ns_node->ns, ns_node->ns_id);
(void)uda_notifier_call(udevid, &dev_inst->type, UDA_OCCUPY);
uda_dev_inst_put(dev_inst);
}
}
static void uda_uninit_ns_node_dev(struct uda_ns_node *ns_node)
{
u32 devid;
if (ns_node->ns_id >= UDA_NS_NUM) {
return;
}
for (devid = 0; devid < ns_node->dev_num; devid++) {
u32 udevid = ns_node->devid_to_udevid[devid];
struct uda_dev_inst *dev_inst = uda_dev_inst_get(udevid);
if (dev_inst != NULL) {
(void)uda_notifier_call(udevid, &dev_inst->type, UDA_UNOCCUPY);
uda_unoccupy_dev_by_ns(udevid, &dev_inst->access, ns_node->ns);
uda_dev_inst_put(dev_inst);
}
}
}
void uda_for_each_ns_node_safe(void *priv, void (*func)(struct uda_ns_node *ns_node, void *priv))
{
struct uda_ns_node *ns_node = NULL, *tmp_node = NULL;
ka_task_mutex_lock(&access_ns_mutex);
ka_list_for_each_entry_safe(ns_node, tmp_node, &access_ns_head, node) {
func(ns_node, priv);
}
ka_task_mutex_unlock(&access_ns_mutex);
}
static struct uda_ns_node *uda_find_ns_node(ka_mnt_namespace_t *ns, u32 root_tgid)
{
struct uda_ns_node *ns_node = NULL, *first_ns_node = NULL;
ka_list_for_each_entry(ns_node, &access_ns_head, node) {
if (ns_node->ns == ns) {
if (ns_node->root_tgid == root_tgid) {
return ns_node;
}
if (first_ns_node == NULL) {
first_ns_node = ns_node;
}
}
}
if (first_ns_node == NULL) {
return NULL;
}
return ((ns == uda_get_host_ns()) && (first_ns_node->root_tgid != UDA_HOST_ROOT_TGID)) ? NULL : first_ns_node;
}
static bool uda_is_admin_ns_node(struct uda_ns_node *ns_node)
{
if (ns_node->ns_id < UDA_NS_NUM) {
return false;
}
if (ns_node->ns != uda_get_host_ns()) {
return true;
}
if (ns_node->root_tgid == UDA_HOST_ROOT_TGID) {
return true;
}
return false;
}
int uda_set_dev_ns_identify(u32 udevid, u64 identify)
{
struct uda_dev_inst *dev_inst = NULL;
ka_mnt_namespace_t *ns = NULL;
struct uda_ns_node *ns_node = NULL;
int ret = -EINVAL;
if (!uda_can_access_udevid(udevid)) {
uda_err("Can not access udev. (udevid=%u)\n", udevid);
return -EFAULT;
}
dev_inst = uda_dev_inst_get(udevid);
if (dev_inst == NULL) {
uda_err("Invalid udev. (udevid=%u)\n", udevid);
return -EINVAL;
}
ns = (dev_inst->access.ns != NULL) ? dev_inst->access.ns : uda_get_current_ns();
ns_node = uda_find_ns_node(ns, ka_task_get_current_tgid());
if (ns_node != NULL) {
ns_node->identify = identify;
ret = 0;
}
uda_dev_inst_put(dev_inst);
return ret;
}
KA_EXPORT_SYMBOL(uda_set_dev_ns_identify);
int uda_get_dev_ns_identify(u32 udevid, u64 *identify)
{
struct uda_dev_inst *dev_inst = NULL;
int ret = -EINVAL;
if (identify == NULL) {
uda_err("Null ptr.\n");
return -EINVAL;
}
dev_inst = uda_dev_inst_get(udevid);
if (dev_inst == NULL) {
uda_err("Invalid udev. (udevid=%u)\n", udevid);
return -EINVAL;
}
if (dev_inst->access.ns != NULL) {
struct uda_ns_node *ns_node = NULL;
ka_task_mutex_lock(&access_ns_mutex);
ns_node = uda_find_ns_node(dev_inst->access.ns, ka_task_get_current_tgid());
if (ns_node != NULL) {
*identify = ns_node->identify;
ret = 0;
}
ka_task_mutex_unlock(&access_ns_mutex);
}
uda_dev_inst_put(dev_inst);
return ret;
}
KA_EXPORT_SYMBOL(uda_get_dev_ns_identify);
int uda_get_cur_ns_id(u32 *ns_id)
{
struct uda_ns_node *ns_node = NULL;
int ret = -EEXIST;
if (ns_id == NULL) {
uda_err("Null ptr.\n");
return -EINVAL;
}
ka_task_mutex_lock(&access_ns_mutex);
ns_node = uda_find_ns_node(uda_get_current_ns(), ka_task_get_current_tgid());
if (ns_node != NULL) {
*ns_id = (ns_node->ns_id > UDA_NS_NUM)? UDA_NS_NUM : ns_node->ns_id;
ret = 0;
}
ka_task_mutex_unlock(&access_ns_mutex);
return ret;
}
KA_EXPORT_SYMBOL(uda_get_cur_ns_id);
int uda_get_container_docker_id(u32 *docker_id)
{
int ret;
if (!run_in_normal_docker()) {
*docker_id = UDA_NS_NUM;
return 0;
}
ret = uda_get_cur_ns_id(docker_id);
if (ret != 0) {
uda_err("Query ns id failed\n");
return ret;
}
return 0;
}
KA_EXPORT_SYMBOL(uda_get_container_docker_id);
static bool uda_dev_is_occupied(struct uda_access *access)
{
struct uda_ns_node *ns_node = NULL;
unsigned int i;
if (access->ns == NULL) {
return false;
}
ns_node = uda_find_ns_node(access->ns, ka_task_get_current_tgid());
if (ns_node == NULL) {
uda_info("not found access ns ns_node.(name=%s)\n", access->name);
return false;
}
if (!uda_tgid_exited(ns_node->root_tgid, ns_node->tgid_time)) {
uda_info("ns_node already exist. (ns=%pK; ns_node->ns_id=%u; ns_node->root_tgid=%d; current_tgid=%d)\n",
ns_node->ns, ns_node->ns_id, ns_node->root_tgid, ka_task_get_current_tgid());
return true;
}
for (i = 0; i < FREE_NS_NODE_TRY_COUNT; i++) {
if (uda_get_ns_pid_count(ns_node->ns) == 0) {
break;
}
ka_system_msleep(FREE_NS_NODE_INTERVAL_MS);
}
uda_info("old ns_node not used, free and reoccupy.(ns_id=%u; tgid=%u; i=%u)\n",
ns_node->ns_id, ns_node->root_tgid, i);
ka_task_mutex_unlock(&access->mutex);
uda_uninit_ns_node_dev(ns_node);
ka_task_mutex_lock(&access->mutex);
uda_destroy_ns_node(ns_node, false);
return false;
}
void uda_release_idle_ns_by_vdev_id(unsigned int phy_id, unsigned int vdev_id)
{
#if defined(DRV_HOST) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0))
u32 udevid, udevid_end;
struct uda_dev_inst *dev_inst = NULL;
struct uda_access *access = NULL;
if (vdev_id == 0xffff) {
udevid = UDA_MIA_UDEV_OFFSET + phy_id * UDA_SUB_DEV_MAX_NUM;
udevid_end = UDA_MIA_UDEV_OFFSET + (phy_id + 1) * UDA_SUB_DEV_MAX_NUM;
} else {
udevid = vdev_id;
udevid_end = vdev_id + 1;
}
for (; udevid < udevid_end; udevid++) {
dev_inst = uda_dev_inst_get(udevid);
if (dev_inst == NULL) {
continue;
}
access = &dev_inst->access;
ka_task_mutex_lock(&access_ns_mutex);
ka_task_mutex_lock(&access->mutex);
(void)uda_dev_is_occupied(access);
ka_task_mutex_unlock(&access->mutex);
ka_task_mutex_unlock(&access_ns_mutex);
uda_dev_inst_put(dev_inst);
}
#endif
}
KA_EXPORT_SYMBOL_GPL(uda_release_idle_ns_by_vdev_id);
static struct uda_ns_node *uda_create_ns_node(ka_mnt_namespace_t *ns)
{
static u32 ns_id = UDA_NS_NUM;
struct uda_ns_node *ns_node = dbl_kzalloc(sizeof(struct uda_ns_node), KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
if (ns_node == NULL) {
uda_err("Out of memory.\n");
return NULL;
}
ns_node->ns = ns;
if (uda_cur_is_admin()) {
ns_node->ns_id = ns_id++;
if (ns_node->ns_id < UDA_NS_NUM) {
ns_id = UDA_NS_NUM;
ns_node->ns_id = UDA_NS_NUM;
}
} else {
ns_node->ns_id = uda_alloc_ns_id();
if (ns_node->ns_id == UDA_NS_NUM) {
uda_err("Alloc ns id fail.\n");
dbl_kfree(ns_node);
return NULL;
}
}
KA_TASK_INIT_DELAYED_WORK(&ns_node->wait_destroy_work, uda_ns_node_destroy_work);
ka_list_add_tail(&ns_node->node, &access_ns_head);
return ns_node;
}
static void uda_destroy_ns_node(struct uda_ns_node *ns_node, bool from_work)
{
if (ns_node->ns_id < UDA_NS_NUM) {
uda_free_ns_id(ns_node->ns_id);
}
if (!from_work) {
(void)ka_task_cancel_delayed_work_sync(&ns_node->wait_destroy_work);
}
ka_list_del(&ns_node->node);
dbl_kfree(ns_node);
}
int uda_ns_node_devid_to_udevid(u32 devid, u32 *udevid)
{
struct uda_ns_node *ns_node = NULL;
ka_task_mutex_lock(&access_ns_mutex);
ns_node = uda_find_ns_node(uda_get_current_ns(), ka_task_get_current_tgid());
if (ns_node == NULL) {
ka_task_mutex_unlock(&access_ns_mutex);
return -EAGAIN;
}
if (devid >= ns_node->dev_num) {
ka_task_mutex_unlock(&access_ns_mutex);
return -ENODEV;
}
*udevid = ns_node->devid_to_udevid[devid];
ka_task_mutex_unlock(&access_ns_mutex);
return 0;
}
KA_EXPORT_SYMBOL(uda_ns_node_devid_to_udevid);
static struct uda_dev_inst *uda_dev_inst_get_by_devid(u32 devid)
{
u32 udevid = UDA_INVALID_UDEVID;
if (devid < UDA_DEV_MAX_NUM) {
int ret;
ret = uda_ns_node_devid_to_udevid(devid, &udevid);
if (ret == -EAGAIN) {
(void)uda_setup_ns_node(UDA_MAX_PHY_DEV_NUM);
if (uda_cur_is_admin()) {
ret = uda_ns_node_devid_to_udevid(devid, &udevid);
}
}
if (ret != 0) {
return NULL;
}
} else if (devid >= UDA_MIA_UDEV_OFFSET) {
if (uda_cur_is_admin()) {
udevid = devid;
}
}
return uda_dev_inst_get(udevid);
}
int uda_devid_to_udevid(u32 devid, u32 *udevid)
{
struct uda_dev_inst *dev_inst = NULL;
if (udevid == NULL) {
return -EINVAL;
}
dev_inst = uda_dev_inst_get_by_devid(devid);
if (dev_inst == NULL) {
return -EINVAL;
}
*udevid = dev_inst->udevid;
uda_dev_inst_put(dev_inst);
return 0;
}
KA_EXPORT_SYMBOL(uda_devid_to_udevid);
int uda_devid_to_udevid_ex(u32 devid, u32 *udevid)
{
if (udevid == NULL) {
return -EINVAL;
}
if (devid != UDA_HOST_ID) {
return uda_devid_to_udevid(devid, udevid);
} else {
*udevid = devid;
}
return 0;
}
KA_EXPORT_SYMBOL(uda_devid_to_udevid_ex);
int uda_devid_to_phy_devid(u32 devid, u32 *phy_devid, u32 *vfid)
{
u32 udevid;
int ret = uda_devid_to_udevid(devid, &udevid);
if (ret != 0) {
uda_err("Trans failed. (devid=%u)\n", devid);
return ret;
}
if ((phy_devid == NULL) || (vfid == NULL)) {
return -EINVAL;
}
if (uda_is_support_udev_mng()) {
*phy_devid = udevid;
*vfid = 0;
} else {
if (uda_is_phy_dev(udevid)) {
*phy_devid = udevid;
*vfid = 0;
} else {
struct uda_mia_dev_para mia_para;
ret = uda_udevid_to_mia_devid(udevid, &mia_para);
if (ret != 0) {
uda_err("Query mia dev failed. (devid=%u; udevid=%u)\n", devid, udevid);
return ret;
}
*phy_devid = mia_para.phy_devid;
*vfid = mia_para.sub_devid + 1;
}
}
return 0;
}
KA_EXPORT_SYMBOL(uda_devid_to_phy_devid);
int uda_udevid_to_devid(u32 udevid, u32 *devid)
{
struct uda_dev_inst *dev_inst = NULL;
if ((devid == NULL) || uda_is_phy_dev(udevid)) {
uda_err("Invalid udevid or null ptr. (udevid=%u)\n", udevid);
return -EINVAL;
}
dev_inst = uda_dev_inst_get(udevid);
if (dev_inst == NULL) {
uda_err("Invalid udevid. (udevid=%u)\n", udevid);
return -EINVAL;
}
*devid = udevid;
uda_dev_inst_put(dev_inst);
return 0;
}
static bool uda_is_admin_ns(ka_task_struct_t *task)
{
struct uda_ns_node *ns_node = NULL;
bool is_admin = false;
if (task == current) {
return uda_current_is_admin();
}
ka_task_mutex_lock(&access_ns_mutex);
ns_node = uda_find_ns_node(uda_get_task_ns(task), task->tgid);
if ((ns_node != NULL) && (ns_node->ns_id >= UDA_NS_NUM)) {
is_admin = true;
}
ka_task_mutex_unlock(&access_ns_mutex);
return is_admin;
}
static bool uda_task_access_judge_ok(ka_task_struct_t *task, struct uda_access *access)
{
ka_mnt_namespace_t *ns = uda_get_task_ns(task);
if (ns == uda_get_host_ns()) {
return true;
}
ka_task_mutex_lock(&access->mutex);
if (ns == access->ns) {
ka_task_mutex_unlock(&access->mutex);
return true;
}
if (uda_is_ns_in_udev_shared_list(ns, access)) {
ka_task_mutex_unlock(&access->mutex);
return true;
}
ka_task_mutex_unlock(&access->mutex);
if (uda_is_admin_ns(task)) {
return true;
}
return false;
}
static bool uda_task_can_access_udevid(ka_task_struct_t *task, u32 udevid)
{
bool ret = false;
struct uda_dev_inst *dev_inst = uda_dev_inst_get(udevid);
if (dev_inst != NULL) {
ret = uda_task_access_judge_ok(task, &dev_inst->access);
uda_dev_inst_put(dev_inst);
}
return ret;
}
bool uda_proc_can_access_udevid(int tgid, u32 udevid)
{
ka_task_struct_t *task = NULL;
ka_struct_pid_t *pid = NULL;
bool ret;
pid = ka_task_find_get_pid(tgid);
if (pid == NULL) {
uda_err("Find pid failed. (tgid=%d)\n", tgid);
return false;
}
task = ka_task_get_pid_task(pid, KA_PIDTYPE_PID);
if (task == NULL) {
ka_task_put_pid(pid);
uda_err("Get task failed. (tgid=%d)\n", tgid);
return false;
}
ret = uda_task_can_access_udevid(task, udevid);
ka_task_put_task_struct(task);
ka_task_put_pid(pid);
return ret;
}
KA_EXPORT_SYMBOL(uda_proc_can_access_udevid);
bool uda_can_access_udevid(u32 udevid)
{
return uda_task_can_access_udevid(current, udevid);
}
KA_EXPORT_SYMBOL(uda_can_access_udevid);
bool uda_task_can_access_udevid_inherit(ka_task_struct_t *task, u32 udevid)
{
struct uda_ns_node *ns_node = NULL;
u32 devid;
u32 tmp_udevid;
struct uda_mia_dev_para mia_para = {0};
bool can_access = false;
if (task == NULL) {
return false;
}
ka_task_mutex_lock(&access_ns_mutex);
ns_node = uda_find_ns_node(uda_get_task_ns(task), task->tgid);
if (ns_node != NULL) {
for (devid = 0; devid < ns_node->dev_num; devid++) {
tmp_udevid = ns_node->devid_to_udevid[devid];
if (tmp_udevid == udevid) {
can_access = true;
break;
}
if (uda_is_phy_dev(tmp_udevid)) {
continue;
}
if ((uda_udevid_to_mia_devid(tmp_udevid, &mia_para) == 0) && (mia_para.phy_devid == udevid)) {
can_access = true;
break;
}
}
}
ka_task_mutex_unlock(&access_ns_mutex);
return can_access;
}
KA_EXPORT_SYMBOL(uda_task_can_access_udevid_inherit);
static u32 uda_get_ns_node_devid_from_udevid(struct uda_ns_node *ns_node, u32 udevid)
{
u32 devid;
for (devid = 0; devid < ns_node->dev_num; devid++) {
if (udevid == ns_node->devid_to_udevid[devid]) {
return devid;
}
}
return UDA_DEV_MAX_NUM;
}
static void uda_dev_restore_ns(struct uda_ns_node *ns_node, void *priv)
{
struct uda_access *access = (struct uda_access *)priv;
u32 udevid = KA_DRIVER_MINOR(access->devno);
if ((ns_node->ns_id >= UDA_NS_NUM) || (access->ns != NULL)) {
return;
}
if (uda_get_ns_node_devid_from_udevid(ns_node, udevid) < UDA_DEV_MAX_NUM) {
ka_task_mutex_lock(&access->mutex);
(void)uda_occupy_dev_by_ns(udevid, access, ns_node->ns);
ka_task_mutex_unlock(&access->mutex);
uda_init_udev_ns_id(udevid, access, ns_node->ns, ns_node->ns_id);
uda_info("Restore namespace. (udevid=%u; ns_id=%u)\n", udevid, ns_node->ns_id);
}
}
static void uda_try_to_restore_ns(struct uda_access *access)
{
uda_for_each_ns_node_safe((void *)access, uda_dev_restore_ns);
}
static void uda_add_udev_to_admin_ns_node(struct uda_ns_node *ns_node, void *priv)
{
struct uda_access *access = (struct uda_access *)priv;
u32 udevid = KA_DRIVER_MINOR(access->devno);
if (!uda_is_admin_ns_node(ns_node)) {
return;
}
if (uda_get_ns_node_devid_from_udevid(ns_node, udevid) < UDA_DEV_MAX_NUM) {
return;
}
ns_node->devid_to_udevid[ns_node->dev_num++] = udevid;
uda_info("Add to namespace. (udevid=%u; ns_id=%u)\n", udevid, ns_node->ns_id);
}
static void uda_try_to_add_udev_to_ns_node(struct uda_access *access)
{
uda_for_each_ns_node_safe((void *)access, uda_add_udev_to_admin_ns_node);
}
static struct uda_access_share_node *uda_find_udev_shared_node(ka_mnt_namespace_t *ns, struct uda_access *access)
{
struct uda_access_share_node *share_node = NULL;
ka_list_for_each_entry(share_node, &access->share_head, node) {
if (share_node->ns == ns) {
return share_node;
}
}
return NULL;
}
bool uda_is_ns_in_udev_shared_list(ka_mnt_namespace_t *ns, struct uda_access *access)
{
return (uda_find_udev_shared_node(ns, access) != NULL);
}
void uda_init_udev_ns_id(u32 udevid, struct uda_access *access, ka_mnt_namespace_t *ns, u32 ns_id)
{
if (ns_id >= UDA_NS_NUM) {
return;
}
ka_task_mutex_lock(&access->mutex);
if (uda_is_dev_shared(udevid)) {
struct uda_access_share_node *share_node = uda_find_udev_shared_node(ns, access);
if (share_node != NULL) {
share_node->ns_id = ns_id;
}
} else {
access->ns_id = ns_id;
}
ka_task_mutex_unlock(&access->mutex);
}
static int uda_flush_invalid_share_occupy(struct uda_access *access)
{
unsigned int node_num = 0;
ka_list_head_t *pos;
struct uda_access_share_node *share_node = NULL, *share_node_n = NULL;
ka_list_for_each(pos, &access->share_head) {
node_num++;
}
if (node_num <= UDA_NS_NUM) {
return 0;
}
uda_info("share node num exceed.(node_num=%u)\n", node_num);
ka_list_for_each_entry_safe(share_node, share_node_n, &access->share_head, node) {
if (share_node->ns_id == UDA_NS_NUM) {
uda_info("find free first invalid share node.\n");
ka_list_del(&share_node->node);
dbl_vfree(share_node);
return 0;
}
}
uda_err("not found invalid share node when node num exceed.\n");
return -ESRCH;
}
static int uda_occupy_shared_dev(u32 udevid, struct uda_access *access, ka_mnt_namespace_t *ns)
{
struct uda_access_share_node *share_node = NULL;
int ret;
if (uda_is_ns_in_udev_shared_list(ns, access)) {
return 0;
}
ret = uda_flush_invalid_share_occupy(access);
if (ret != 0) {
return ret;
}
share_node = dbl_vmalloc(sizeof(struct uda_access_share_node), KA_GFP_KERNEL|__KA_GFP_HIGHMEM|__KA_GFP_ACCOUNT, KA_PAGE_KERNEL);
if (share_node == NULL) {
uda_err("Alloc share node failed. (udevid=%u)\n", udevid);
return -ENOMEM;
}
share_node->ns = ns;
share_node->ns_id = UDA_NS_NUM;
ka_list_add_tail(&share_node->node, &access->share_head);
return 0;
}
static void uda_unoccupy_shared_dev(struct uda_access *access, ka_mnt_namespace_t *ns)
{
struct uda_access_share_node *share_node = uda_find_udev_shared_node(ns, access);
if (share_node != NULL) {
ka_list_del(&share_node->node);
dbl_vfree(share_node);
}
}
int uda_occupy_dev_by_ns(u32 udevid, struct uda_access *access, ka_mnt_namespace_t *ns)
{
if (uda_is_dev_shared(udevid)) {
return uda_occupy_shared_dev(udevid, access, ns);
}
if (access->ns == NULL) {
uda_info("Occupy udevid. (udevid=%u; ns=%pK)\n", udevid, ns);
access->ns = ns;
return 0;
}
if (access->ns == ns) {
return 0;
}
if (!uda_dev_is_occupied(access)) {
uda_info("Preempt udevid. (udevid=%u; old_ns=%pK; new_ns=%pK)\n", udevid, access->ns, ns);
access->ns = ns;
return 0;
}
uda_err("Conflict open udevid. (udevid=%u; access_ns=%pK; ns=%pK)\n", udevid, access->ns, ns);
return -EBUSY;
}
void uda_unoccupy_dev_by_ns(u32 udevid, struct uda_access *access, ka_mnt_namespace_t *ns)
{
ka_task_mutex_lock(&access->mutex);
if (uda_is_dev_shared(udevid)) {
uda_unoccupy_shared_dev(access, ns);
} else {
access->ns = NULL;
access->ns_id = UDA_NS_NUM;
}
ka_task_mutex_unlock(&access->mutex);
}
static int uda_occupy_dev(struct uda_dev_inst *dev_inst)
{
struct uda_access *access = &dev_inst->access;
if (access->dev == NULL) {
uda_err("Dev has been remove. (udevid=%u)\n", dev_inst->udevid);
return -ENODEV;
}
return uda_occupy_dev_by_ns(dev_inst->udevid, access, uda_get_current_ns());
}
static void uda_refresh_dev_ownership(ka_inode_t *inode)
{
g_phy_dev_uid = inode->i_uid;
g_phy_dev_gid = inode->i_gid;
g_phy_dev_mode = inode->i_mode;
}
static int uda_access_open(ka_inode_t *inode, ka_file_t *file)
{
u32 udevid = ka_fs_iminor(inode);
int ret = 0;
struct uda_dev_inst *dev_inst = uda_dev_inst_get(udevid);
if (dev_inst == NULL) {
uda_warn("This davinci does not exist. (udevid=%u)\n", udevid);
return -ENODEV;
}
uda_debug("Cdev open. (udevid=%u; inst_id=%u)\n", udevid, dev_inst->inst_id);
if (uda_is_phy_dev(udevid)) {
uda_refresh_dev_ownership(inode);
}
if (!uda_cur_is_admin()) {
struct uda_access *access = &dev_inst->access;
ka_task_mutex_lock(&access_ns_mutex);
ka_task_mutex_lock(&access->mutex);
ret = uda_occupy_dev(dev_inst);
ka_task_mutex_unlock(&access->mutex);
ka_task_mutex_unlock(&access_ns_mutex);
}
uda_dev_inst_put(dev_inst);
return ret;
}
static int uda_access_release(ka_inode_t *inode, ka_file_t *file)
{
u32 udevid = ka_fs_iminor(inode);
uda_debug("Cdev release. (udevid=%u)\n", udevid);
return 0;
}
static const ka_file_operations_t uda_access_fops = {
ka_fs_init_f_owner(KA_THIS_MODULE)
ka_fs_init_f_open(uda_access_open)
ka_fs_init_f_release(uda_access_release)
};
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
static char *uda_devnode(const ka_device_t *dev, umode_t *mode, ka_kuid_t *uid, ka_kgid_t *gid)
#else
static char *uda_devnode(ka_device_t *dev, umode_t *mode, ka_kuid_t *uid, ka_kgid_t *gid)
#endif
{
if (dev != NULL) {
if (mode != NULL) {
#ifndef EMU_ST
*mode = g_phy_dev_mode;
#endif
}
if ((uid != NULL) && (gid != NULL)) {
*uid = g_phy_dev_uid;
*gid = g_phy_dev_gid;
}
}
return NULL;
}
const ka_device_type_t uda_type = {
.devnode = uda_devnode,
};
static void uda_device_release(ka_device_t *dev)
{
uda_debug("Free dev mem. (udevid=%u)\n", KA_DRIVER_MINOR(dev->devt));
dbl_kfree(dev);
}
static void uda_cdev_set_parent(ka_cdev_t *cdev, ka_kobject_t *kobj)
{
cdev->kobj.parent = kobj;
}
static int uda_cdev_device_add(ka_cdev_t *cdev, ka_device_t *dev)
{
int ret;
uda_cdev_set_parent(cdev, &dev->kobj);
ret = ka_fs_cdev_add(cdev, dev->devt, 1);
if (ret != 0) {
uda_err("Cdev add failed. (ret=%d)\n", ret);
return ret;
}
return ka_fs_device_add(dev);
}
static void uda_cdev_device_del(ka_cdev_t *cdev, ka_device_t *dev)
{
ka_fs_device_del(dev);
ka_fs_cdev_del(cdev);
}
static int uda_create_cdev(u32 udevid, u32 logic_id, struct uda_access *access)
{
const char *name = uda_is_phy_dev(udevid) ? UDA_PHY_DEV_NAME : UDA_MIA_DEV_NAME;
ka_device_t *dev = NULL;
int ret;
unsigned int char_id = (logic_id != UDA_INVALID_UDEVID) ? logic_id : udevid;
(void)sprintf_s(access->name, UDA_DEV_NAME_LEN, "/dev/%s%u", name, char_id);
access->devno = KA_DRIVER_MKDEV(KA_DRIVER_MAJOR(uda_dev), udevid);
access->ns = NULL;
access->ns_id = UDA_NS_NUM;
KA_INIT_LIST_HEAD(&access->share_head);
ka_task_mutex_init(&access->mutex);
if (!uda_is_phy_dev(udevid)) {
uda_try_to_restore_ns(access);
}
access->cdev = ka_fs_cdev_alloc();
if (access->cdev == NULL) {
uda_err("Alloc cdev failed. (udevid=%u; char_id=%u)\n", udevid, char_id);
return -ENOMEM;
}
access->cdev->owner = KA_THIS_MODULE;
access->cdev->ops = &uda_access_fops;
dev = dbl_kzalloc(sizeof(*dev), KA_GFP_KERNEL);
if (dev == NULL) {
ka_fs_cdev_del(access->cdev);
uda_err("Alloc dev failed. (udevid=%u; char_id=%u)\n", udevid, char_id);
return -ENOMEM;
}
ka_base_device_initialize(dev);
dev->devt = access->devno;
dev->class = uda_class;
dev->type = &uda_type;
dev->release = uda_device_release;
(void)ka_fs_kobject_set_name(&dev->kobj, "%s%u", name, char_id);
access->dev = dev;
ret = uda_cdev_device_add(access->cdev, access->dev);
if (ret != 0) {
ka_fs_cdev_del(access->cdev);
dbl_kfree(access->dev);
uda_err("Cdev add device failed. (udevid=%u; char_id=%u; ret=%d)\n", udevid, char_id, ret);
return ret;
}
if (uda_is_phy_dev(udevid)) {
uda_try_to_add_udev_to_ns_node(access);
}
return 0;
}
static void uda_remove_cdev(u32 udevid, struct uda_access *access)
{
uda_cdev_device_del(access->cdev, access->dev);
put_device(access->dev);
}
static void uda_remove_share_list(struct uda_access *access)
{
struct uda_access_share_node *share_node = NULL, *share_node_n = NULL;
ka_list_for_each_entry_safe(share_node, share_node_n, &access->share_head, node) {
ka_list_del(&share_node->node);
dbl_vfree(share_node);
}
}
int uda_access_add_dev(u32 udevid, u32 logic_id, struct uda_access *access)
{
return uda_create_cdev(udevid, logic_id, access);
}
int uda_access_remove_dev(u32 udevid, struct uda_access *access)
{
ka_task_mutex_lock(&access->mutex);
uda_remove_cdev(udevid, access);
uda_remove_share_list(access);
ka_task_mutex_unlock(&access->mutex);
return 0;
}
static void uda_recycle_all_cdev(void)
{
#ifdef CFG_FEATURE_DEVID_TRANS
u32 udevid;
for (udevid = 0; udevid < UDA_UDEV_MAX_NUM; udevid++) {
struct uda_dev_inst *dev_inst = uda_dev_inst_get(udevid);
if (dev_inst != NULL) {
(void)uda_access_remove_dev(udevid, &dev_inst->access);
uda_dev_inst_put(dev_inst);
}
}
#endif
}
static int uda_cdev_class_init(void)
{
#ifdef CFG_FEATURE_DEVID_TRANS
int ret;
ret = ka_fs_alloc_chrdev_region(&uda_dev, 0, UDA_UDEV_MAX_NUM, "devdrv-cdev");
if (ret) {
uda_err("Alloc chrdev region failed. (ret=%d)\n", ret);
return ret;
}
uda_class = ka_driver_class_create(KA_THIS_MODULE, "devdrv-class");
if (KA_IS_ERR(uda_class)) {
ka_fs_unregister_chrdev_region(uda_dev, UDA_UDEV_MAX_NUM);
uda_err("Create class failed.\n");
return -EFAULT;
}
#endif
return 0;
}
static void uda_cdev_class_uninit(void)
{
#ifdef CFG_FEATURE_DEVID_TRANS
ka_driver_class_destroy(uda_class);
ka_fs_unregister_chrdev_region(uda_dev, UDA_UDEV_MAX_NUM);
#endif
}
u32 uda_get_cur_ns_dev_num(void)
{
struct uda_ns_node *ns_node = NULL;
u32 dev_num = 0;
ka_task_mutex_lock(&access_ns_mutex);
ns_node = uda_find_ns_node(uda_get_current_ns(), ka_task_get_current_tgid());
if (ns_node != NULL) {
dev_num = ns_node->dev_num;
}
ka_task_mutex_unlock(&access_ns_mutex);
return dev_num;
}
KA_EXPORT_SYMBOL(uda_get_cur_ns_dev_num);
int uda_get_cur_ns_udevids(u32 udevids[], u32 num)
{
struct uda_ns_node *ns_node = NULL;
u32 devid;
ka_task_mutex_lock(&access_ns_mutex);
ns_node = uda_find_ns_node(uda_get_current_ns(), ka_task_get_current_tgid());
if (ns_node != NULL) {
for (devid = 0; (devid < ns_node->dev_num) && (devid < num); devid++) {
udevids[devid] = ns_node->devid_to_udevid[devid];
}
}
ka_task_mutex_unlock(&access_ns_mutex);
return 0;
}
KA_EXPORT_SYMBOL(uda_get_cur_ns_udevids);
int uda_set_detected_phy_dev_num(u32 dev_num)
{
if ((dev_num == 0) || (dev_num >= UDA_UDEV_MAX_NUM)) {
uda_err("Invalid dev num. (dev_num=%u)\n", dev_num);
return -EINVAL;
}
uda_detected_dev_num = dev_num;
uda_info("Set detected dev num. (total_dev_num=%u; dev_num=%u)\n", uda_detected_dev_num, dev_num);
return 0;
}
KA_EXPORT_SYMBOL(uda_set_detected_phy_dev_num);
u32 uda_get_detected_phy_dev_num(void)
{
return uda_detected_dev_num;
}
KA_EXPORT_SYMBOL(uda_get_detected_phy_dev_num);
bool uda_is_dev_shared(u32 udevid)
{
if (udevid < sizeof(g_dev_shared)) {
return (bool)g_dev_shared[udevid];
}
return false;
}
static void uda_dev_is_share_occupied(struct uda_ns_node *ns_node, void *priv)
{
struct uda_dev_shared_check *check = (struct uda_dev_shared_check *)priv;
if (ns_node->ns_id >= UDA_NS_NUM) {
return;
}
if (uda_get_ns_node_devid_from_udevid(ns_node, check->udevid) < UDA_DEV_MAX_NUM) {
check->share_occupied = true;
}
}
int uda_set_dev_share(u32 udevid, u8 share_flag)
{
struct uda_dev_inst *dev_inst = NULL;
struct uda_dev_shared_check check = {udevid, false};
if (share_flag != true && share_flag != false) {
uda_err("invalid param.(share_flag=%u).\n", share_flag);
return -EINVAL;
}
if (udevid >= sizeof(g_dev_shared)) {
uda_err("invalid param.(udevid=%u).\n", udevid);
return -EINVAL;
}
dev_inst = uda_dev_inst_get(udevid);
if (dev_inst == NULL) {
uda_err("Invalid udev. (udevid=%u)\n", udevid);
return -EINVAL;
}
uda_dev_inst_put(dev_inst);
if (uda_is_dev_shared(udevid) == share_flag) {
return 0;
}
uda_for_each_ns_node_safe(&check, uda_dev_is_share_occupied);
if (check.share_occupied) {
uda_err("udevid is occupied in use, can not set share flag. (udevid=%u; share=%u)\n", udevid, share_flag);
return -EBUSY;
}
uda_info("set dev share flag. (udevid=%u; share_flag=%u)\n", udevid, share_flag);
g_dev_shared[udevid] = share_flag;
return 0;
}
KA_EXPORT_SYMBOL(uda_set_dev_share);
int uda_get_dev_share(u32 udevid, u8 *share_flag)
{
struct uda_dev_inst *dev_inst = NULL;
if (share_flag == NULL) {
uda_err("Null ptr. (udevid=%u)\n", udevid);
return -EINVAL;
}
dev_inst = uda_dev_inst_get(udevid);
if (dev_inst == NULL) {
uda_err("Invalid udev. (udevid=%u)\n", udevid);
return -EINVAL;
}
*share_flag = uda_is_dev_shared(udevid);
uda_dev_inst_put(dev_inst);
return 0;
}
KA_EXPORT_SYMBOL(uda_get_dev_share);
#define UDA_WAIT_TIME 100
#define UDA_WAIT_CNT 1500
static void uda_wait_all_phy_startup(void)
{
int wait_cnt = 0;
#ifndef EMU_ST
while ((uda_detected_dev_num == 0) || (uda_get_dev_num() < uda_detected_dev_num)) {
ka_system_msleep(UDA_WAIT_TIME);
wait_cnt++;
if (wait_cnt > UDA_WAIT_CNT) {
uda_err("Wait timeout. (dev_num=%u; uda_detected_dev_num=%u)\n", uda_get_dev_num(), uda_detected_dev_num);
break;
}
}
#endif
}
static int _uda_setup_ns_node(ka_mnt_namespace_t *ns, u32 root_tgid, TASK_TIME_TYPE tgid_time, u32 dev_num)
{
struct uda_ns_node *ns_node = NULL;
ns_node = uda_find_ns_node(ns, root_tgid);
if ((ns_node != NULL) && (ns_node->root_tgid == root_tgid)) {
return 0;
}
ns_node = uda_create_ns_node(ns);
if (ns_node == NULL) {
return -ENOMEM;
}
uda_init_ns_node_dev(ns_node, root_tgid, tgid_time);
if (!uda_cur_is_admin()) {
if (dev_num > ns_node->dev_num) {
uda_err("Dev num not match. (ns=%pK; dev_num=%u; node_dev_num=%u)\n", ns, dev_num, ns_node->dev_num);
uda_uninit_ns_node_dev(ns_node);
uda_destroy_ns_node(ns_node, false);
return -EBUSY;
} else if (dev_num < ns_node->dev_num) {
uda_warn("More device setup. (ns=%pK; dev_num=%u; node_dev_num=%u)\n", ns, dev_num, ns_node->dev_num);
}
} else if (ns_node->dev_num == 0) {
uda_warn("No device setup.\n");
#ifdef EMU_ST
uda_uninit_ns_node_dev(ns_node);
uda_destroy_ns_node(ns_node, false);
#endif
return -ENODEV;
}
uda_info("Setup success. (ns_id=%u; ns=%pK; root_tgid=%u; dev_num=%u; node_dev_num=%u)\n",
ns_node->ns_id, ns, ns_node->root_tgid, dev_num, ns_node->dev_num);
return 0;
}
static int uda_find_ns_root_pid(ka_mnt_namespace_t *ns, u32 *tgid, TASK_TIME_TYPE *tgid_time)
{
#ifndef EMU_ST
ka_task_struct_t *task = NULL;
int ret = -EFAULT;
ka_task_rcu_read_lock();
ka_for_each_process(task) {
if (task == NULL) {
continue;
}
ka_task_task_lock(task);
if ((task->nsproxy != NULL) && (task->nsproxy->mnt_ns == ns)) {
*tgid = task->tgid;
*tgid_time = task->start_time;
ret = 0;
ka_task_task_unlock(task);
break;
}
ka_task_task_unlock(task);
}
ka_task_rcu_read_unlock();
return ret;
#else
*tgid = ka_task_get_current_tgid();
return 0;
#endif
}
u32 uda_get_ns_pid_count(ka_mnt_namespace_t *ns)
{
ka_task_struct_t *task = NULL;
u32 count = 0;
#ifndef EMU_ST
ka_task_rcu_read_lock();
ka_for_each_process(task) {
if (task == NULL) {
continue;
}
ka_task_task_lock(task);
if ((task->nsproxy != NULL) && (task->nsproxy->mnt_ns == ns)) {
count++;
}
ka_task_task_unlock(task);
}
ka_task_rcu_read_unlock();
#endif
return count;
}
static bool uda_cur_task_is_added_to_dev_cgroup(void)
{
u32 udevid;
for (udevid = 0; udevid < UDA_MAX_PHY_DEV_NUM; udevid++) {
struct uda_dev_inst *dev_inst = uda_dev_inst_get(udevid);
if (dev_inst != NULL) {
added to the cgroup, the processing is the same as that the process is not added to the dev cgroup. */
if (!uda_devcgroup_permission_allow(dev_inst)) {
uda_dev_inst_put(dev_inst);
return true;
}
uda_dev_inst_put(dev_inst);
}
}
return false;
}
#if defined(DRV_HOST) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0))
static void uda_recycle_idle_ns_node_single(struct uda_ns_node *ns_node, void *priv)
{
if (ns_node->root_tgid == UDA_HOST_ROOT_TGID) {
return;
}
if (ns_node->destroy_try_count > 0) {
uda_debug("Has run another work already! count=%u\n", ns_node->destroy_try_count);
return;
}
if (!uda_tgid_exited(ns_node->root_tgid, ns_node->tgid_time)) {
return;
}
ns_node->destroy_try_count++;
ka_task_schedule_delayed_work(&ns_node->wait_destroy_work, ka_system_msecs_to_jiffies(FREE_NS_NODE_INTERVAL_MS));
}
static void uda_recycle_idle_ns_node(void)
{
uda_for_each_ns_node_safe(NULL, uda_recycle_idle_ns_node_single);
}
static void uda_recycle_idle_ns_node_single_immediately(struct uda_ns_node *ns_node, void *priv)
{
u32 pid_count;
if (ns_node->root_tgid == UDA_HOST_ROOT_TGID) {
return;
}
if (!uda_tgid_exited(ns_node->root_tgid, ns_node->tgid_time)) {
return;
}
pid_count = uda_get_ns_pid_count(ns_node->ns);
if (pid_count != 0) {
return;
}
uda_uninit_ns_node_dev(ns_node);
uda_destroy_ns_node(ns_node, false);
}
void uda_recycle_idle_ns_node_immediately(void)
{
uda_for_each_ns_node_safe(NULL, uda_recycle_idle_ns_node_single_immediately);
}
#else
static void uda_recycle_idle_ns_node(void)
{
return;
}
void uda_recycle_idle_ns_node_immediately(void)
{
return;
}
#endif
int uda_setup_ns_node(u32 dev_num)
{
u32 root_tgid = UDA_HOST_ROOT_TGID;
TASK_TIME_TYPE tgid_time;
int ret;
static int first_flag = 1;
ka_mnt_namespace_t *ns = uda_get_current_ns();
if (ns == NULL) {
uda_err("Name space is null.\n");
return -EFAULT;
}
if (first_flag == 1) {
uda_wait_all_phy_startup();
first_flag = 0;
}
(void)memset_s(&tgid_time, sizeof(tgid_time), 0, sizeof(tgid_time));
if (uda_cur_is_host()) {
if (uda_cur_task_is_added_to_dev_cgroup()) {
root_tgid = ka_task_get_current_tgid();
tgid_time = current->start_time;
}
} else {
ret = uda_find_ns_root_pid(ns, &root_tgid, &tgid_time);
if (ret != 0) {
uda_err("Find first tgid failed.\n");
return ret;
}
}
uda_recycle_idle_ns_node();
ka_task_mutex_lock(&access_ns_mutex);
ret = _uda_setup_ns_node(ns, root_tgid, tgid_time, dev_num);
ka_task_mutex_unlock(&access_ns_mutex);
return ret;
}
bool uda_tgid_exited(u32 tgid, TASK_TIME_TYPE start_time)
{
ka_task_struct_t *tsk = NULL;
ka_struct_pid_t *pid;
#ifndef EMU_ST
ka_task_rcu_read_lock();
pid = ka_task_get_pid(ka_task_find_pid_ns(tgid, &init_pid_ns));
ka_task_rcu_read_unlock();
if (pid == NULL) {
return true;
}
tsk = ka_task_get_pid_task(pid, KA_PIDTYPE_PID);
ka_task_put_pid(pid);
if (tsk == NULL) {
return true;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
if (start_time == tsk->start_time) {
ka_task_put_task_struct(tsk);
return false;
}
#else
if (timespec_equal(&start_time, &tsk->start_time)) {
ka_task_put_task_struct(tsk);
return false;
}
#endif
ka_task_put_task_struct(tsk);
return true;
#else
return false;
#endif
}
void uda_ns_node_destroy_work(ka_work_struct_t *p_work)
{
struct uda_ns_node *ns_node = ka_container_of(p_work, struct uda_ns_node, wait_destroy_work.work);
u32 pid_count;
if (ns_node->destroy_try_count < FREE_NS_NODE_TRY_COUNT) {
ns_node->destroy_try_count++;
if (!uda_tgid_exited(ns_node->root_tgid, ns_node->tgid_time)) {
uda_info("root_tgid exist.(ns_id=%u; tgid=%u)\n", ns_node->ns_id, ns_node->root_tgid);
#ifndef EMU_ST
ka_task_schedule_delayed_work(&ns_node->wait_destroy_work, ka_system_msecs_to_jiffies(FREE_NS_NODE_INTERVAL_MS));
#endif
return;
}
pid_count = uda_get_ns_pid_count(ns_node->ns);
if (pid_count != 0) {
uda_info("ns pid exist.(ns_id=%u; tgid=%u; pid_count=%u)\n", ns_node->ns_id, ns_node->root_tgid, pid_count);
#ifndef EMU_ST
ka_task_schedule_delayed_work(&ns_node->wait_destroy_work, ka_system_msecs_to_jiffies(FREE_NS_NODE_INTERVAL_MS));
#endif
return;
}
}
if (!ka_task_mutex_trylock(&access_ns_mutex)) {
uda_info("not get mutex lock, wait next time.(ns_id=%u)\n", ns_node->ns_id);
#ifndef EMU_ST
ka_task_schedule_delayed_work(&ns_node->wait_destroy_work, ka_system_msecs_to_jiffies(FREE_NS_NODE_INTERVAL_MS));
#endif
return;
}
uda_uninit_ns_node_dev(ns_node);
uda_info("work done.(ns_id=%u; tgid=%u; try=%u)\n", ns_node->ns_id, ns_node->root_tgid, ns_node->destroy_try_count);
#ifndef EMU_ST
uda_destroy_ns_node(ns_node, true);
#endif
ka_task_mutex_unlock(&access_ns_mutex);
}
static void uda_cleanup_ns_node(ka_mnt_namespace_t *ns, ka_task_struct_t *task)
{
struct uda_ns_node *ns_node = NULL;
ka_task_mutex_lock(&access_ns_mutex);
ns_node = uda_find_ns_node(ns, task->tgid);
if ((ns_node != NULL) && (ns_node->root_tgid == task->tgid)) {
uda_info("Wait for destroy ns. (ns_id=%u; root_tgid=%u; task_name=%s; task_tgid=%u; task_pid=%u)\n",
ns_node->ns_id, ns_node->root_tgid, task->comm, task->tgid, task->pid);
#ifndef EMU_ST
ka_task_schedule_delayed_work(&ns_node->wait_destroy_work, ka_system_msecs_to_jiffies(FREE_NS_NODE_INTERVAL_MS));
#else
uda_uninit_ns_node_dev(ns_node);
uda_destroy_ns_node(ns_node, false);
#endif
}
ka_task_mutex_unlock(&access_ns_mutex);
}
static void uda_recycle_ns_node_single(struct uda_ns_node *ns_node, void *priv)
{
uda_destroy_ns_node(ns_node, false);
}
void uda_recycle_ns_node(void)
{
uda_for_each_ns_node_safe(NULL, uda_recycle_ns_node_single);
}
static int uda_task_exit_notify(ka_notifier_block_t *self, unsigned long val, void *data)
{
ka_task_struct_t *task = (ka_task_struct_t *)data;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
if ((task != NULL) && (task->tgid != 0) && (task->tgid == task->pid)) {
#else
if ((task != NULL) && (task->mm != NULL) && (task->tgid != 0) && (task->tgid == task->pid)) {
#endif
uda_cleanup_ns_node(uda_get_task_ns(task), task);
}
return 0;
}
static ka_notifier_block_t uda_task_exit_nb = {
.notifier_call = uda_task_exit_notify,
};
static void uda_task_exit_reg(void)
{
#ifdef DRV_HOST
(void)ka_dfx_profile_event_register(KA_PROFILE_TASK_EXIT, &uda_task_exit_nb);
#else
(void)profile_event_register(PROFILE_TASK_EXIT, &uda_task_exit_nb);
#endif
}
static void uda_task_exit_unreg(void)
{
#ifdef DRV_HOST
(void)ka_dfx_profile_event_unregister(KA_PROFILE_TASK_EXIT, &uda_task_exit_nb);
#else
(void)profile_event_unregister(PROFILE_TASK_EXIT, &uda_task_exit_nb);
#endif
}
int uda_access_init(void)
{
int ret;
ka_task_mutex_init(&access_ns_mutex);
KA_INIT_LIST_HEAD(&access_ns_head);
ret = uda_cdev_class_init();
if (ret != 0) {
return ret;
}
uda_task_exit_reg();
KA_TASK_INIT_WORK(&host_ns_init_work, uda_host_ns_init_work);
ka_task_schedule_work(&host_ns_init_work);
uda_init_phy_dev_num();
return 0;
}
void uda_access_uninit(void)
{
(void)ka_task_cancel_work_sync(&host_ns_init_work);
uda_task_exit_unreg();
uda_recycle_ns_node();
uda_recycle_all_cdev();
uda_cdev_class_uninit();
}
#ifdef CFG_FEATURE_DEVID_TRANS
int devdrv_get_devnum(u32 *dev_num)
{
if (dev_num == NULL) {
return -EINVAL;
}
*dev_num = uda_get_detected_phy_dev_num();
return 0;
}
KA_EXPORT_SYMBOL_GPL(devdrv_get_devnum);
int devdrv_get_vdevnum(u32 *dev_num)
{
if (dev_num == NULL) {
return -EINVAL;
}
*dev_num = uda_get_mia_dev_num();
return 0;
}
KA_EXPORT_SYMBOL_GPL(devdrv_get_vdevnum);
u32 devdrv_manager_get_devnum(void)
{
return uda_get_cur_ns_dev_num();
}
KA_EXPORT_SYMBOL_GPL(devdrv_manager_get_devnum);
int devdrv_get_devids(u32 *devices, u32 device_num)
{
return uda_get_cur_ns_udevids(devices, device_num);
}
KA_EXPORT_SYMBOL(devdrv_get_devids);
int devdrv_manager_container_logical_id_to_physical_id(u32 logical_dev_id, u32 *physical_dev_id, u32 *vfid)
{
return uda_devid_to_phy_devid(logical_dev_id, physical_dev_id, vfid);
}
KA_EXPORT_SYMBOL_GPL(devdrv_manager_container_logical_id_to_physical_id);
#endif