* 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 "ka_kernel_def_pub.h"
#include "ka_task_pub.h"
#include "virtmngagent_msg_admin.h"
#include "virtmngagent_msg.h"
#include "virtmngagent_vpc.h"
#include "virtmngagent_msg_common.h"
#include "virtmng_msg_admin.h"
#include "virtmng_public_def.h"
#include "virtmng_resource.h"
STATIC int vmnga_admin_para_check(const struct vmng_msg_dev *msg_dev,
const struct vmng_msg_chan_rx_proc_info *proc_info)
{
if (msg_dev == NULL) {
vmng_err("Input parameter is error.\n");
return -EINVAL;
}
if (proc_info == NULL) {
vmng_err("Input parameter is error. (dev_id=%d)\n", msg_dev->dev_id);
return -EINVAL;
}
if (proc_info->data == NULL) {
vmng_err("Input parameter is error. (dev_id=%d)\n", msg_dev->dev_id);
return -EINVAL;
}
return 0;
}
STATIC void vmnga_parse_irq_from_cmd(struct vmng_create_cluster_cmd *cmd, struct vmng_msg_chan_irqs *int_irq_ary)
{
int_irq_ary->rx_recv_irq = (u32 *)(&(cmd->int_irq[0]));
int_irq_ary->rx_resp_irq = int_irq_ary->rx_recv_irq + cmd->res.tx_num;
int_irq_ary->tx_send_irq = int_irq_ary->rx_recv_irq + (u64)cmd->res.tx_num * VMNG_MSG_SQ_TXRX;
int_irq_ary->tx_finish_irq = int_irq_ary->rx_recv_irq + (u64)cmd->res.tx_num * VMNG_MSG_SQ_TXRX + cmd->res.rx_num;
}
STATIC int vmnga_fill_msg_cluster_res(struct vmng_msg_chan_res *res, u32 tx_base, u32 tx_num, u32 rx_base, u32 rx_num)
{
u32 tx_max;
u32 rx_max;
tx_max = tx_base + tx_num;
rx_max = rx_base + rx_num;
if ((tx_base >= VMNG_MSG_CHAN_NUM_MAX) || (tx_num >= VMNG_MSG_CHAN_NUM_MAX) || (rx_base >= VMNG_MSG_CHAN_NUM_MAX) ||
(rx_num >= VMNG_MSG_CHAN_NUM_MAX) || (tx_max > VMNG_MSG_CHAN_NUM_MAX) || (rx_max > VMNG_MSG_CHAN_NUM_MAX)) {
vmng_err("Prarmeter check failed. (tx_base=%u; tx_num=%u; rx_base=%u; rx_num=%u)\n",
tx_base, tx_num, rx_base, rx_num);
return -EINVAL;
}
res->tx_base = rx_base;
res->tx_num = rx_num;
res->rx_base = tx_base;
res->rx_num = tx_num;
return 0;
}
STATIC int vmnga_assign_msg_func(enum vmng_msg_chan_type chan_type, struct vmng_msg_proc *msg_proc)
{
if (chan_type == VMNG_MSG_CHAN_TYPE_COMMON) {
msg_proc->rx_recv_proc = vmnga_msg_cluster_recv_common;
msg_proc->tx_finish_proc = NULL;
} else if (vmng_is_vpc_chan(chan_type) == true) {
msg_proc->rx_recv_proc = vmnga_msg_cluster_recv_vpc;
msg_proc->tx_finish_proc = NULL;
} else if (vmng_is_blk_chan(chan_type) == true) {
msg_proc->rx_recv_proc = vmnga_msg_cluster_recv_vpc;
msg_proc->tx_finish_proc = vmng_msg_tx_finish_task;
} else {
vmng_err("Unknown channel type. (chan_type=%u)\n", chan_type);
return -EINVAL;
}
return 0;
}
STATIC int vmnga_admin_rx_alloc_msg_cluster(struct vmng_msg_dev *msg_dev, struct vmng_msg_chan_rx_proc_info *proc_info)
{
struct vmng_create_cluster_cmd *cmd = NULL;
struct vmng_create_cluster_reply *reply = NULL;
struct vmng_msg_chan_res res;
enum vmng_msg_chan_type chan_type;
struct vmng_msg_chan_irqs int_irq_ary;
struct vmng_msg_proc msg_proc;
int ret;
ret = vmnga_admin_para_check(msg_dev, proc_info);
if (ret != 0) {
vmng_err("Call vmnga_admin_para_check failed.\n");
return ret;
}
cmd = (struct vmng_create_cluster_cmd *)proc_info->data;
ret = vmnga_fill_msg_cluster_res(&res, cmd->res.tx_base, cmd->res.tx_num, cmd->res.rx_base, cmd->res.rx_num);
if (ret != 0) {
vmng_err("Fill msg_cluster failed. (dev_id=%u; chan_type=%u)\n", msg_dev->dev_id, cmd->chan_type);
return ret;
}
chan_type = cmd->chan_type;
if (chan_type >= VMNG_MSG_CHAN_TYPE_MAX) {
vmng_err("chan_type is invalid. (dev_id=%u; chan_type=%u)\n", msg_dev->dev_id, chan_type);
return -EINVAL;
}
vmnga_parse_irq_from_cmd(cmd, &int_irq_ary);
ret = vmnga_assign_msg_func(chan_type, &msg_proc);
if (ret != 0) {
vmng_err("chan_type is invalid. (dev_id=%u; chan_type=%u)\n", msg_dev->dev_id, chan_type);
return ret;
}
ret = vmng_alloc_local_msg_cluster(msg_dev, chan_type, &res, &int_irq_ary, &msg_proc);
if (ret != 0) {
vmng_err("Alloc cluster failed. (dev_id=%u; chan_type=%u; ret=%d)\n", msg_dev->dev_id, chan_type, ret);
return ret;
}
msg_dev->msg_cluster[chan_type].status = VMNG_MSG_CLUSTER_STATUS_ENABLE;
vmng_info("Alloc cluster success. (dev_id=%u; chan_type=%u; tx_base=%u; tx_num=%u; rx_base=%u; rx_num=%u)\n",
msg_dev->dev_id, cmd->chan_type, cmd->res.tx_base, cmd->res.tx_num, cmd->res.rx_base, cmd->res.rx_num);
reply = (struct vmng_create_cluster_reply *)proc_info->data;
reply->remote_alloc_finish = VMNG_CREATE_CLUSTER_FINISH;
*(proc_info->real_out_len) = sizeof(struct vmng_create_cluster_reply);
return ret;
}
static vmnga_admin_func g_vmnga_msg_admin_func_ops[] = {
vmnga_admin_rx_alloc_msg_cluster,
NULL
};
#define VMNG_ADMIN_PROC_OPCODE_MAX (sizeof(g_vmnga_msg_admin_func_ops) / sizeof(void *))
int vmnga_register_admin_rx_func(int opcode, vmnga_admin_func admin_func)
{
if (opcode >= VMNG_ADMIN_PROC_OPCODE_MAX) {
vmng_err("opcode is invalid. (opcode=%u)\n", opcode);
return -EINVAL;
}
if (g_vmnga_msg_admin_func_ops[opcode] != NULL) {
vmng_err("opcode has registered. (opcode=%u)\n", opcode);
return -EINVAL;
}
g_vmnga_msg_admin_func_ops[opcode] = admin_func;
return 0;
}
KA_EXPORT_SYMBOL(vmnga_register_admin_rx_func);
void vmnga_unregister_admin_rx_func(int opcode)
{
if (opcode >= VMNG_ADMIN_PROC_OPCODE_MAX) {
vmng_err("opcode is invalid. (opcode=%u)\n", opcode);
return;
}
g_vmnga_msg_admin_func_ops[opcode] = NULL;
}
KA_EXPORT_SYMBOL(vmnga_unregister_admin_rx_func);
STATIC int vmnga_admin_rx_msg_proc(void *msg_chan, struct vmng_msg_chan_rx_proc_info *proc_info)
{
struct vmng_msg_chan_rx *admin_msg_chan = (struct vmng_msg_chan_rx *)msg_chan;
struct vmng_msg_dev *msg_dev = (struct vmng_msg_dev *)admin_msg_chan->msg_dev;
u32 opcode;
int ret;
if (proc_info == NULL) {
vmng_err("Input parameter is error. (dev_id=%u)\n", msg_dev->dev_id);
return -EINVAL;
}
if (proc_info->data == NULL) {
vmng_err("Input parameter is error. (dev_id=%u)\n", msg_dev->dev_id);
return -EINVAL;
}
opcode = proc_info->opcode_d2;
if (opcode >= VMNG_ADMIN_PROC_OPCODE_MAX) {
vmng_err("opcode is invalid. (dev_id=%u; opcode=%u)\n", msg_dev->dev_id, opcode);
return -ENOSYS;
}
if (g_vmnga_msg_admin_func_ops[opcode] == NULL) {
vmng_err("Msg admin proc function is NULL. (dev_id=%u;opcode=%d)\n", msg_dev->dev_id, opcode);
return -EINVAL;
}
ret = g_vmnga_msg_admin_func_ops[opcode](msg_dev, proc_info);
if (ret != 0) {
vmng_err("Admin func ops error. (dev_id=%u; ret=%d)\n", msg_dev->dev_id, ret);
return -EINVAL;
}
return ret;
}
int vmnga_init_msg_admin(struct vmng_msg_dev *msg_dev)
{
struct vmng_msg_cluster *msg_cluster = NULL;
struct vmng_msg_chan_rx *msg_chan_rx = NULL;
struct vmng_msg_chan_tx *msg_chan_tx = NULL;
int ret;
vmng_info("Init message admin. (dev_id=%u)\n", msg_dev->dev_id);
msg_cluster = &(msg_dev->msg_cluster[VMNG_MSG_CHAN_TYPE_ADMIN]);
ka_task_mutex_init(&msg_cluster->mutex);
msg_dev->admin_tx = &(msg_dev->msg_chan_tx[VMNG_MSG_CHAN_TYPE_ADMIN]);
msg_chan_tx = msg_dev->admin_tx;
msg_chan_tx->tx_send_irq = msg_dev->db_irq_base + VMNG_DB_MSG_ADMIN;
msg_chan_tx->tx_finish_irq = 0;
msg_chan_tx->send_irq_to_remote = msg_dev->ops.send_irq_to_remote;
ka_task_mutex_init(&msg_chan_tx->mutex);
msg_dev->admin_rx = &(msg_dev->msg_chan_rx[VMNG_MSG_CHAN_TYPE_ADMIN]);
msg_chan_rx = msg_dev->admin_rx;
msg_chan_rx->rx_recv_irq = msg_dev->msix_irq_base + VMNG_MSIX_MSG_ADMIN;
msg_chan_rx->rx_proc = vmnga_admin_rx_msg_proc;
msg_chan_rx->rx_wq = ka_task_create_singlethread_workqueue("vpc_admin_msg_chan_proc");
if (msg_chan_rx->rx_wq == NULL) {
vmng_err("Create workqueue failed. (dev_id=%u)\n", msg_dev->dev_id);
return -EINVAL;
}
KA_TASK_INIT_WORK(&msg_chan_rx->rx_work, vmng_msg_rx_msg_task);
ret = vmnga_rx_irq_init(msg_chan_rx);
if (ret != 0) {
ka_task_destroy_workqueue(msg_chan_rx->rx_wq);
msg_chan_rx->rx_wq = NULL;
vmng_err("Call vmnga_rx_irq_init failed. (dev_id=%u)\n", msg_dev->dev_id);
return -EINVAL;
}
msg_chan_tx->status = VMNG_MSG_CHAN_STATUS_IDLE;
msg_chan_rx->status = VMNG_MSG_CHAN_STATUS_IDLE;
vmng_info("Admin init OK. (dev_id=%u; tx_int=%u; rx_int=%u) \n", msg_dev->dev_id, msg_chan_tx->tx_send_irq,
msg_chan_rx->rx_recv_irq);
return 0;
}
void vmnga_uninit_vpc_msg_admin(struct vmng_msg_dev *msg_dev)
{
struct vmng_msg_chan_rx *msg_chan_rx = msg_dev->admin_rx;
struct vmng_msg_chan_tx *msg_chan_tx = msg_dev->admin_tx;
int ret;
ret = vmnga_rx_irq_uninit(msg_chan_rx);
if (ret != 0) {
vmng_err("Call vmnga_rx_irq_uninit failed. (dev_id=%u)\n", msg_dev->dev_id);
}
msg_chan_rx->status = VMNG_MSG_CHAN_STATUS_DISABLE;
msg_chan_tx->status = VMNG_MSG_CHAN_STATUS_DISABLE;
if (msg_chan_rx->rx_wq != NULL) {
ka_task_destroy_workqueue(msg_chan_rx->rx_wq);
msg_chan_rx->rx_wq = NULL;
}
}