* 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_list_pub.h"
#include "ka_task_pub.h"
#include "ka_memory_pub.h"
#include "ka_kernel_def_pub.h"
#include "ka_compiler_pub.h"
#include "virtmngagent_msg.h"
#include "virtmng_msg_common.h"
#include "virtmngagent_vpc_unit.h"
#include "virtmngagent_msg_admin.h"
#include "virtmng_msg_pub.h"
#include "virtmng_msg_admin.h"
#include "virtmng_public_def.h"
#include "vmng_kernel_interface.h"
#include "virtmng_resource.h"
#include "vmng_mem_alloc_interface.h"
struct vmng_msg_dev_head {
ka_list_head_t msg_dev_head;
ka_mutex_t mutex;
};
struct vmng_msg_dev_head g_vmnga_msg_dev_head;
struct vmng_msg_dev *vmnga_get_msg_dev_by_id(u32 dev_id)
{
struct vmng_msg_dev *pos;
ka_list_for_each_entry(pos, &g_vmnga_msg_dev_head.msg_dev_head, list) {
if (pos->dev_id == dev_id) {
return pos;
}
}
return NULL;
}
STATIC void vmnga_msg_dev_list_free(void)
{
struct vmng_msg_dev *pos;
struct vmng_msg_dev *n;
ka_list_for_each_entry_safe(pos, n, &g_vmnga_msg_dev_head.msg_dev_head, list) {
ka_list_del(&pos->list);
vmng_kfree(pos);
pos = NULL;
}
}
STATIC void vmnga_send_int_to_remote(void *msg_dev_in, u32 vector)
{
struct vmng_msg_dev *msg_dev = msg_dev_in;
vmnga_set_doorbell(msg_dev->db_base, vector, 0x1);
}
STATIC ka_irqreturn_t vmnga_tx_finish_msix_hander(int irq, void *data)
{
struct vmng_msg_chan_tx *msg_chan = (struct vmng_msg_chan_tx *)data;
struct vmng_msg_cluster *msg_cluster = NULL;
msg_cluster = msg_chan->msg_cluster;
if (msg_cluster->msg_proc.tx_finish_proc == NULL) {
vmng_err("tx_finish_proc is NULL. (dev_id=%u; chan_type=%u)\n", msg_cluster->dev_id, msg_cluster->chan_type);
return KA_IRQ_NONE;
}
msg_cluster->msg_proc.tx_finish_proc((unsigned long)(uintptr_t)msg_chan);
return KA_IRQ_HANDLED;
}
STATIC int vmnga_tx_irq_init(struct vmng_msg_chan_tx *msg_chan)
{
struct vmng_msg_dev *msg_dev = (struct vmng_msg_dev *)msg_chan->msg_dev;
int ret;
if (msg_chan->tx_finish_irq == 0) {
return 0;
}
ret = vmnga_register_vpc_irq_func(msg_dev->unit, msg_chan->tx_finish_irq, vmnga_tx_finish_msix_hander,
(void *)msg_chan, "vmnga_tx_finish");
if (ret != 0) {
vmng_err("Call vmnga_register_vpc_irq_func failed. (dev_id=%u; tx_finish_irq=%u; ret=%d)\n",
msg_dev->dev_id, msg_chan->tx_finish_irq, ret);
return ret;
}
return 0;
}
STATIC int vmnga_tx_irq_uninit(struct vmng_msg_chan_tx *msg_chan)
{
struct vmng_msg_dev *msg_dev = (struct vmng_msg_dev *)msg_chan->msg_dev;
int ret;
if (msg_chan->tx_finish_irq == 0) {
return 0;
}
ret = vmnga_unregister_vpc_irq_func(msg_dev->unit, msg_chan->tx_finish_irq, (void *)msg_chan);
if (ret != 0) {
vmng_err("Call vmnga_unregister_vpc_irq_func failed. (dev_id=%u; tx_finish_irq=%u; ret=%d)\n",
msg_dev->dev_id, msg_chan->tx_finish_irq, ret);
return ret;
}
vmng_event("Tx irq unregister. (dev_id=%u; tx_irq=%u)\n", msg_dev->dev_id, msg_chan->tx_finish_irq);
return 0;
}
STATIC ka_irqreturn_t vmnga_msg_msix_hander(int irq, void *data)
{
struct vmng_msg_chan_rx *msg_chan = (struct vmng_msg_chan_rx *)data;
vmng_msg_push_rx_queue_work(msg_chan);
return KA_IRQ_HANDLED;
}
int vmnga_rx_irq_init(struct vmng_msg_chan_rx *msg_chan)
{
struct vmng_msg_dev *msg_dev = (struct vmng_msg_dev *)msg_chan->msg_dev;
int ret;
if (msg_chan->rx_recv_irq == 0) {
return 0;
}
ret = vmnga_register_vpc_irq_func(msg_dev->unit, msg_chan->rx_recv_irq, vmnga_msg_msix_hander, (void *)msg_chan,
"vmnga_rx_recv");
if (ret != 0) {
vmng_err("Call vmnga_register_vpc_irq_func failed. (dev_id=%u)\n", msg_dev->dev_id);
return ret;
}
return 0;
}
int vmnga_rx_irq_uninit(struct vmng_msg_chan_rx *msg_chan)
{
struct vmng_msg_dev *msg_dev = (struct vmng_msg_dev *)msg_chan->msg_dev;
int ret;
if (msg_chan->rx_recv_irq == 0) {
return 0;
}
vmng_event("Irq unregister. (dev_id=%u; irq=%u)\n", msg_dev->dev_id, msg_chan->rx_recv_irq);
ret = vmnga_unregister_vpc_irq_func(msg_dev->unit, msg_chan->rx_recv_irq, (void *)msg_chan);
if (ret != 0) {
vmng_err("Call vmnga_unregister_vpc_irq_func failed. (dev_id=%u)\n", msg_dev->dev_id);
return ret;
}
return 0;
}
STATIC struct vmng_msg_dev *vmnga_msg_dev_alloc(struct vmnga_vpc_unit *unit)
{
struct vmng_msg_dev *msg_dev = NULL;
int ret = 0;
vmng_info("Get msg_dev size. (dev_id=%u; size=%lu)\n", unit->dev_id, sizeof(struct vmng_msg_dev));
msg_dev = vmng_kzalloc(sizeof(struct vmng_msg_dev), KA_GFP_KERNEL);
if (msg_dev == NULL) {
vmng_err("Call ka_mm_kzalloc failed. (dev_id=%u; size=%lu)\n", unit->dev_id, sizeof(struct vmng_msg_dev));
return NULL;
}
unit->msg_dev = msg_dev;
msg_dev->unit = unit;
msg_dev->dev_id = unit->dev_id;
msg_dev->fid = 0;
msg_dev->chan_num = VMNG_MSG_CHAN_NUM_MAX;
msg_dev->db_base = unit->db_base;
msg_dev->db_irq_base = VMNG_DB_BASE_MSG;
msg_dev->mem_base = unit->msg_base;
msg_dev->msix_irq_base = VMNG_MSIX_BASE_MSG;
msg_dev->status = VMNG_MSG_DEV_ALIVE;
msg_dev->ops.send_irq_to_remote = vmnga_send_int_to_remote;
msg_dev->ops.tx_irq_init = vmnga_tx_irq_init;
msg_dev->ops.tx_irq_uninit = vmnga_tx_irq_uninit;
msg_dev->ops.rx_irq_init = vmnga_rx_irq_init;
msg_dev->ops.rx_irq_uninit = vmnga_rx_irq_uninit;
msg_dev->work_queue = ka_task_create_workqueue("vmnga_wq");
if (msg_dev->work_queue == NULL) {
vmng_err("Create msg work_queue failed. (dev_id=%u)\n", unit->dev_id);
vmng_kfree(msg_dev);
msg_dev = NULL;
return NULL;
}
ret = vmng_msg_chan_init(VMNG_AGENT_SIDE, msg_dev);
if (ret != 0) {
vmng_err("Call vmng_msg_chan_init failed. (dev_id=%u; ret=%d)\n", unit->dev_id, ret);
ka_task_destroy_workqueue(msg_dev->work_queue);
vmng_kfree(msg_dev);
msg_dev = NULL;
return NULL;
}
ka_task_mutex_lock(&g_vmnga_msg_dev_head.mutex);
ka_list_add(&msg_dev->list, &g_vmnga_msg_dev_head.msg_dev_head);
ka_task_mutex_unlock(&g_vmnga_msg_dev_head.mutex);
return msg_dev;
}
int vmnga_vpc_msg_init(void *unit_in)
{
struct vmnga_vpc_unit *unit = (struct vmnga_vpc_unit *)unit_in;
struct vmng_msg_dev *msg_dev = NULL;
int ret;
if (unit_in == NULL) {
vmng_err("Input parameter is error.\n");
return -EINVAL;
}
msg_dev = vmnga_msg_dev_alloc(unit);
if (msg_dev == NULL) {
vmng_err("Call vmnga_msg_dev_alloc failed. (dev_id=%u)\n", unit->dev_id);
return -EINVAL;
}
ret = vmnga_init_msg_admin(msg_dev);
if (ret != 0) {
vmng_err("Call vmnga_init_msg_admin failed. (dev_id=%u; ret=%d)\n", unit->dev_id, ret);
vmng_free_msg_dev(msg_dev);
}
return ret;
}
KA_EXPORT_SYMBOL(vmnga_vpc_msg_init);
void vmnga_uninit_vpc_msg(struct vmng_msg_dev *msg_dev)
{
vmnga_uninit_vpc_msg_admin(msg_dev);
vmng_free_msg_dev(msg_dev);
}
KA_EXPORT_SYMBOL(vmnga_uninit_vpc_msg);
STATIC int __ka_init vmnga_vpc_init_module(void)
{
vmng_info("Init vmnga vpc module finish.\n");
KA_INIT_LIST_HEAD(&g_vmnga_msg_dev_head.msg_dev_head);
ka_task_mutex_init(&g_vmnga_msg_dev_head.mutex);
return 0;
}
ka_module_init(vmnga_vpc_init_module);
STATIC void __ka_exit vmnga_vpc_exit_module(void)
{
vmng_info("Exit vmnga vpc module finish.\n");
vmnga_msg_dev_list_free();
}
ka_module_exit(vmnga_vpc_exit_module);
KA_MODULE_AUTHOR("Huawei Tech. Co., Ltd.");
KA_MODULE_DESCRIPTION("virt vpc agent driver");
KA_MODULE_LICENSE("GPL");