首页 > 系统相关 >linux 核间通讯rpmsg架构分析【转】

linux 核间通讯rpmsg架构分析【转】

时间:2023-06-26 18:24:12浏览次数:79  
标签:virtio struct rpmsg vq virtqueue linux imx 核间

转自:https://blog.csdn.net/wind0419/article/details/123277545

以imx8为例

在最底层硬件上,A核和M核通讯是靠硬件来进行的,称为MU,如图

 

 

Linux RPMsg 是在virtio framework上实现的一个消息传递机制
VirtIO 是一个用来实现“虚拟IO”的通用框架,典型虚拟的pci,网卡,磁盘等虚拟设备,kvm等都使用了这个技术

与virtio对应的还有一个virtio-ring,其实现了 virtio 的具体通信机制和数据流程。

virtio 层属于控制层,负责前后端之间的通知机制(kick,notify)和控制流程,而 virtio-vring 则负责具体数据流转发

从整体架构上看,关系如下:

 

最底层有platform_bus,负责从dts获取配置来初始化相关对象,如virtio_device,初始化其config操作函数列表以及devID等,同时注册到virtio_bus

dts相关配置:
&rpmsg{
/*
* 64K for one rpmsg instance:
*/
vdev-nums = <2>;
reg = <0x0 0x90000000 0x0 0x20000>;
status = "okay";
};
主要初始化过程在imx_rpmsg_probe中,关键操作有:
注册MU相关的硬件中断

ret = request_irq(irq, imx_mu_rpmsg_isr, IRQF_EARLY_RESUME | IRQF_SHARED,

"imx-mu-rpmsg", rpdev);

初始化MU硬件

ret = imx_rpmsg_mu_init(rpdev);

创建工作队列用于处理MU中断数据

INIT_DELAYED_WORK(&(rpdev->rpmsg_work), rpmsg_work_handler);

创建通知链用于对接virtio queue

BLOCKING_INIT_NOTIFIER_HEAD(&(rpdev->notifier));

初始化virtio_device并注册

for (j = 0; j < rpdev->vdev_nums; j++) {
pr_debug("%s rpdev%d vdev%d: vring0 0x%x, vring1 0x%x\n",
__func__, rpdev->core_id, rpdev->vdev_nums,
rpdev->ivdev[j].vring[0],
rpdev->ivdev[j].vring[1]);
rpdev->ivdev[j].vdev.id.device = VIRTIO_ID_RPMSG;
rpdev->ivdev[j].vdev.config = &imx_rpmsg_config_ops;
rpdev->ivdev[j].vdev.dev.parent = &pdev->dev;
rpdev->ivdev[j].vdev.dev.release = imx_rpmsg_vproc_release;
rpdev->ivdev[j].base_vq_id = j * 2;

ret = register_virtio_device(&rpdev->ivdev[j].vdev);
if (ret) {
pr_err("%s failed to register rpdev: %d\n",
__func__, ret);
return ret;
}

}
值得注意的是virtio_device的config结构 rpdev->ivdev[j].vdev.config = &imx_rpmsg_config_ops;

static struct virtio_config_ops imx_rpmsg_config_ops = {

.get_features = imx_rpmsg_get_features,

.finalize_features = imx_rpmsg_finalize_features,

.find_vqs = imx_rpmsg_find_vqs,

.del_vqs = imx_rpmsg_del_vqs,

.reset = imx_rpmsg_reset,

.set_status = imx_rpmsg_set_status,

.get_status = imx_rpmsg_get_status,

};

imx_rpmsg_find_vqs

--> rp_find_vq

-->ioremap_nocache

-->vring_new_virtqueue(...imx_rpmsg_notify, callback...)

需要注意的是callback的注册过程,在rpmsg_bus中
rpmsg_probe

-->vq_callback_t *vq_cbs[] = { rpmsg_recv_done, rpmsg_xmit_done };

-->virtio_find_vqs(vdev, 2, vqs, vq_cbs, names, NULL);

在此处注册的imx_rpmsg_notify 和 callback 将被virtio_bus框架所调用

中间virtio_bus承上启下,并负责提供统一标准的virtio queue操作接口,如virtqueue_add,virtqueue_kick等

针对struct virtqueue,对外只有一个callback函数,用于表示queue的数据变化

struct virtqueue {
struct list_head list;
void (*callback)(struct virtqueue *vq);
const char *name;
struct virtio_device *vdev;
unsigned int index;
unsigned int num_free;
void *priv;
};
其实virtqueue只是提供一层标准queue的操作接口,其具体实现依靠vring_virtqueue

struct vring_virtqueue {
struct virtqueue vq;

/* Actual memory layout for this queue */
struct vring vring
{
//queue的具体实现
unsigned int num;

struct vring_desc *desc;

struct vring_avail *avail;

struct vring_used *used;
};


/* How to notify other side. FIXME: commonalize hcalls! */
bool (*notify)(struct virtqueue *vq);

...

/* Per-descriptor state. */
struct vring_desc_state desc_state[];
};
其触发过程在vring_interrupt

irqreturn_t vring_interrupt(int irq, void *_vq)
{
##对外只有virtqueue,找到其包装vring_virtqueue
struct vring_virtqueue *vq = to_vvq(_vq);

if (!more_used(vq)) {
pr_debug("virtqueue interrupt with no work for %p\n", vq);
return IRQ_NONE;
}

if (unlikely(vq->broken))
return IRQ_HANDLED;

pr_debug("virtqueue callback for %p (%p)\n", vq, vq->vq.callback);
##调用virtqueue的callback
if (vq->vq.callback)
vq->vq.callback(&vq->vq);

return IRQ_HANDLED;
}
结合中断,整体流程如下:
imx_mu_rpmsg_isr

-->rpmsg_work_handler

-->vring_interrupt

-->virtqueue.callback

关于vring_virtqueue,包含一个notify,用于通知queue有变化

virtqueue_add 和 virtqueue_kick 以及 virtqueue_notify 都能够触发notify

最终notify的实现在imx_rpmsg_notify,其内容为设置MU寄存器,发送数据

/* kick the remote processor, and let it know which virtqueue to poke at */
static bool imx_rpmsg_notify(struct virtqueue *vq)
{
unsigned int mu_rpmsg = 0;
struct imx_rpmsg_vq_info *rpvq = vq->priv;

mu_rpmsg = rpvq->vq_id << 16;
mutex_lock(&rpvq->rpdev->lock);
/*
* Send the index of the triggered virtqueue as the mu payload.
* Use the timeout MU send message here.
* Since that M4 core may not be loaded, and the first MSG may
* not be handled by M4 when multi-vdev is enabled.
* To make sure that the message wound't be discarded when M4
* is running normally or in the suspend mode. Only use
* the timeout mechanism by the first notify when the vdev is
* registered.
*/
if (unlikely(rpvq->rpdev->first_notify > 0)) {
rpvq->rpdev->first_notify--;
MU_SendMessageTimeout(rpvq->rpdev->mu_base, 1, mu_rpmsg, 200);
} else {
MU_SendMessage(rpvq->rpdev->mu_base, 1, mu_rpmsg);
}
mutex_unlock(&rpvq->rpdev->lock);
return true;
}
最上面可看成基于rpmsg的应用,挂载到rpmsg_bus总线,针对rpmsg也有对应的标准操作接口,如rpmsg_send,rpmsg_sendto,rpmsg_poll等等

在rpmsg_bus这一层,还有一个rpmsg_endpoint概念,其对应有一个rpmsg_endpoint_ops,包含send,send_to等接口,目前还未对其深入研究

static const struct rpmsg_endpoint_ops virtio_endpoint_ops = {

.destroy_ept = virtio_rpmsg_destroy_ept,

.send = virtio_rpmsg_send,

.sendto = virtio_rpmsg_sendto,

.send_offchannel = virtio_rpmsg_send_offchannel,

.trysend = virtio_rpmsg_trysend,

.trysendto = virtio_rpmsg_trysendto,

.trysend_offchannel = virtio_rpmsg_trysend_offchannel,

};

发送流程为
rpmsg_send

-->rpmsg_endpoint.ops->send

-->virtio_rpmsg_send

-->virtqueue_add_outbuf 往queue填充数据

-->virtqueue_kick 通知对端

-->virtqueue_notify

-->imx_rpmsg_notify

-->MU_REG_WRITE

rpmsg_bus总线默认提供两个回调rpmsg_recv_done和rpmsg_xmit_done以便通知给上层rpmsg应用,分别表示收到数据及发送完成

接收处理流程:
imx_mu_rpmsg_isr

-->rpmsg_work_handler

-->vring_interrupt

-->virtqueue.callback

-->rpmsg_recv_done or rpmsg_xmit_done

/* called when an rx buffer is used, and it's time to digest a message */
static void rpmsg_recv_done(struct virtqueue *rvq)
{
struct virtproc_info *vrp = rvq->vdev->priv;
struct device *dev = &rvq->vdev->dev;
struct rpmsg_hdr *msg;
unsigned int len, msgs_received = 0;
int err;

msg = virtqueue_get_buf(rvq, &len);
if (!msg) {
dev_err(dev, "uhm, incoming signal, but no used buffer ?\n");
return;
}

while (msg) {
err = rpmsg_recv_single(vrp, dev, msg, len);
if (err)
break;

msgs_received++;

msg = virtqueue_get_buf(rvq, &len);
}

dev_dbg(dev, "Received %u messages\n", msgs_received);

/* tell the remote processor we added another available rx buffer */
if (msgs_received)
## 通知接收queue
virtqueue_kick(vrp->rvq);
}
static void rpmsg_xmit_done(struct virtqueue *svq)
{
struct virtproc_info *vrp = svq->vdev->priv;

dev_dbg(&svq->vdev->dev, "%s\n", __func__);

/* wake up potential senders that are waiting for a tx buffer */
wake_up_interruptible(&vrp->sendq);
}
整体过程如下:

 


————————————————
版权声明:本文为CSDN博主「WindLOR」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wind0419/article/details/123277545

标签:virtio,struct,rpmsg,vq,virtqueue,linux,imx,核间
From: https://www.cnblogs.com/sky-heaven/p/17506433.html

相关文章

  • linux下搭建mysql
    Linux下搭建mysql一、上传解压mysql压缩包1.上传mysql压缩包到linux下任意目录比如/usr/src(新系统需要给该目录添加权限:chmod777/usr/src)2.解压mysql压缩包unzipmysql-community-5.7-all.zip二、安装mysql应用程序1.严格按顺序安装rpm程序rpm-ivhmysql-community-common-5.7.......
  • Linux基础26 rsync服务, 三种传输方式
    Rsync服务一、备份1.什么是备份?备份就是把重要的数据或文件再次复制一份保存起来(给源文件增加一个副本)2.为什么要备份?数据很重要!!!出现故障之后,需要恢复数据(软件服务出现问题几率很小,80%都是人为故障)3.能不能不做备份对于不重要的数据,可以不做备份对于不必要的数据可......
  • linux下docker安装与使用
    linux下docker安装1.安装依赖包执行命令:yuminstall-yyum-utilsdevice-mapper-persistent-datalvm22.安装docker执行命令:yuminstall-ydocker3.启动和关闭docker启动:systemctlstartdocker关闭:systemctlstopdocker......
  • linux下redis安装与使用
    linux下redis安装与使用一、redis安装1.上传reids压缩包到任意目录,一般与mysql数据库放一起。/usr/src2.redis压缩包解压tarxvfredis-7.0.4.tar.gz3.用gcc编译C语言,先安装gccyuminstall–ygcc4.进入到解压后的redis目录下,执行编译make5.执行安装命令makeinstall默认安装在:/u......
  • Linux系统搭建Jmeter环境
    Linux系统搭建Jmeter环境一、把Jmeter的压缩包传到Linux系统/home目录下1.给/home添加读写权限chmod777/home2.进入到/home目录下,双击Jmeter压缩包,上传文件3.解压Jmeter压缩包unzipapache-jmeter-5.5.zip如果没有解压提示没有unzip命令,则需要安装unzip工具。在线安装命令如下:yu......
  • Linux系统搭建JDK环境
    Linux系统搭建JDK环境一、把jdk1.8的压缩包传到Linux系统/usr/local目录下1.给/usr/local添加读写权限,否则无法上传Chmod777/usr/local2.在Xftp中双击JDK文件上传到/usr/local目录下点击向上的箭头号,一路返回到根目录,然后进入到/usr/local在Linux中查询是否上传成功cd/usr/loca......
  • Linux基础25 架构, 安装模板机
    架构一、名词的介绍1.项目:手机的APP,买一个APP就是一个项目,针对互联网行业,一家电商公司就是一个项目2.架构:维护一个项目所有组件组成一个整体lnmp:linuxnginxmysqlphplamp:linuxApachemysqlphplnmt:linuxnginxmysqltomcatlamt:linuxApacheMysqlTomcat#除了这些......
  • Linux用户管理笔记1
    useradd创建用户命令: useraddwork    #创建名为work的一般用户以及用户所属组,用来日常完成工作的用户,普通用户下不能够新建普通用户。 useradd-rwork #创建名为work的系统用户以及所属组群,默认情况下不能登录服务器,只能去调用某个服务程序......
  • Linux系统中常见的CPU问题及其解决方法!
    Linux系统跟Windows系统在操作上,还是有很多不同之处的,有些刚开始使用Linux系统的小伙伴,常常会不知该如何操作,今天老男孩教育小编给大家讲解一下Linux系统常见的CPU问题及其解决方法,以下是详细的内容:1、频繁的CPU过载CPU过载是一种常见的问题,尤其是那些运行重负载应用程......
  • Linux实例常用内核网络参数与常见问题处理
    查看和修改Linux实例内核参数方法一、通过 /proc/sys/ 目录查看内核参数:使用 cat 查看对应文件的内容,例如执行命令 cat/proc/sys/net/ipv4/tcp_tw_recycle 查看 net.ipv4.tcp_tw_recycle 的值。修改内核参数:使用 echo 修改内核参数对应的文件,例如执行命令 echo"......