转载自:

作者:LoyenWang

出处:https://www.cnblogs.com/LoyenWang/

公众号:LoyenWang

版权:本文版权归作者和博客园共有

转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任

背景

  • Read the fucking source code! --By 鲁迅
  • A picture is worth a thousand words. --By 高尔基

说明:

  1. KVM版本:5.9.1
  2. QEMU版本:5.0.0
  3. 工具:Source Insight 3.5, Visio
  4. 文章同步在博客园:https://www.cnblogs.com/LoyenWang/

1. 概述

汪汪汪,最近忙成狗了,一下子把我更新的节奏打乱了,草率的道个歉。

  • 前边系列将Virtio Device和Virtio Driver都已经讲完,本文将分析virtqueue;
  • virtqueue用于前后端之间的数据交换,一看到这种数据队列,首先想到的就是ring-buffer,实际的实现会是怎么样的呢?

2. 数据结构

先看一下核心的数据结构:

  • 通常Virtio设备操作Virtqueue时,都是通过struct virtqueue结构体,这个可以理解成对外的一个接口,而Virtqueue机制的实现依赖于struct vring_virtqueue结构体;
  • Virtqueue有三个核心的数据结构,由struct vring负责组织:
    1. struct vring_desc:描述符表,每一项描述符指向一片内存,内存类型可以分为out类型和in类型,分别代表输出和输入,而内存的管理都由驱动来负责。该结构体中的next字段,可用于将多个描述符构成一个描述符链,而flag字段用于描述属性,比如只读只写等;
    2. struct vring_avail:可用描述符区域,用于记录设备可用的描述符ID,它的主体是数组ring,实际就是一个环形缓冲区;
    3. struct vring_used:已用描述符区域,用于记录设备已经处理完的描述符ID,同样,它的ring数组也是环形缓冲区,与struct vring_avail不同的是,它还记录了设备写回的数据长度;

这么看,当然是有点不太直观,所以,下图来了:

  • 简单来说,驱动会分配好内存(scatterlist),并通过virtqueue_add添加到描述表中,这样描述符表中的条目就都能对应到具体的物理地址了,其实可以把它理解成一个资源池子;
  • 驱动可以将可用的资源更新到struct vring_avail中,也就是将可用的描述符ID添加到ring数组中,熟悉环形缓冲区的同学应该清楚它的机制,通过维护头尾两个指针来进行管理,Driver负责更新头指针(idx),Device负责更新尾指针(Qemu中的Device负责维护一个last_avail_idx),头尾指针,你追我赶,生生不息;
  • 当设备使用完了后,将已用的描述符ID更新到struct vring_used中,vring_virtqueue自身维护了last_used_idx,机制与struct vring_avail一致;

3. 流程分析

3.1 发送

当驱动需要把数据发送给设备时,流程如上图所示:

  1. ①A表示分配一个Buffer并添加到Virtqueue中,①B表示从Used队列中获取一个Buffer,这两种中选择一种方式;
  2. ②表示将Data拷贝到Buffer中,用于传送;
  3. ③表示更新Avail队列中的描述符索引值,注意,驱动中需要执行memory barrier操作,确保Device能看到正确的值;
  4. ④与⑤表示Driver通知Device来取数据;
  5. ⑥表示Device从Avail队列中获取到描述符索引值;
  6. ⑦表示将描述符索引对应的地址中的数据取出来;
  7. ⑧表示Device更新Used队列中的描述符索引;
  8. ⑨与⑩表示Device通知Driver数据已经取完了;

3.2 接收

当驱动从设备接收数据时,流程如上图所示:

  1. ①表示Device从Avail队列中获取可用描述符索引值;
  2. ②表示将数据拷贝至描述符索引对应的地址上;
  3. ③表示更新Used队列中的描述符索引值;
  4. ④与⑤表示Device通知Driver来取数据;
  5. ⑥表示Driver从Used队列中获取已用描述符索引值;
  6. ⑦表示将描述符索引对应地址中的数据取出来;
  7. ⑧表示将Avail队列中的描述符索引值进行更新;
  8. ⑨与⑩表示Driver通知Device有新的可用描述符;

3.3 代码分析

代码的分析将围绕下边这个图来展开(Virtio-Net),偷个懒,只分析单向数据发送了:

3.3.1 virtqueue创建

  • 之前的系列文章分析过virtio设备和驱动,Virtio-Net是PCI网卡设备驱动,分别会在virtnet-probevirtio_pci_probe中完成所有的初始化;
  • virtnet_probe函数入口中,通过init_vqs完成Virtqueue的初始化,这个逐级调用关系如图所示,最终会调用到vring_create_virtqueue来创建Virtqueue;
  • 这个创建的过程中,有些细节是忽略的,比如通过PCI去读取设备的配置空间,获取创建Virtqueue所需要的信息等;
  • 最终就是围绕vring_virtqueue数据结构的初始化展开,其中vring数据结构的内存分配也都是在驱动中完成,整个结构体都由驱动来管理与维护;

3.3.2 virtio-net驱动发送

  • 网络数据的传输在驱动中通过start_xmit函数来实现;
  • xmit_skb函数中,sg_init_table初始化sg列表,sg_set_buf将sg指向特定的buffer,skb_to_sgvec将socket buffer中的数据填充sg;
  • 通过virtqueue_add_outbuf将sg添加到Virtqueue中,并更新Avail队列中描述符的索引值;
  • virtqueue_notify通知Device,可以过来取数据了;

3.3.3 Qemu virtio-net设备接收

  • Guest驱动写寄存器操作时,陷入到KVM中,最终Qemu会捕获到进行处理,入口函数为kvm_handle_io
  • Qemu中会针对IO内存区域设置读写的操作函数,当Guest进行IO操作时,最终触发操作函数的调用,针对Virtio-Net,由于它是PCI设备,操作函数为virtio_pci_config_write
  • virtio_pci_config_write函数中,对Guest的写操作进行判断并处理,比如在VIRTIO_PCI_QUEUE_NOTIFY时,调用virtio_queue_notify,用于处理Guest驱动的通知,并最终回调handle_output函数;
  • 针对Virtio-Net设备,发送的回调函数为virtio_net_handle_tx_bh,并在virtio_net_flush_tx中完成操作;
  • 通用的操作模型:通过virtqueue_pop从Avail队列中获取地址,将数据进行处理,通过virtqueue_push将处理完后的描述符索引更新到Used队列中,通过virtio_notify通知Guest驱动;

Virtqueue这种设计思想比较巧妙,不仅用在virtio中,在AMP系统中处理器之间的通信也能看到它的身影。

草草收场了,下回见。

参考

https://www.redhat.com/en/blog/virtqueues-and-virtio-ring-how-data-travels

Virtual I/O Device Version 1.1

欢迎关注个人公众号,不定期更新技术文章。

【转载】Linux虚拟化KVM-Qemu分析(十一)之virtqueue的更多相关文章

  1. [转载]Linux 线程实现机制分析

    本文转自http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/ 支持原创.尊重原创,分享知识! 自从多线程编程的概念出现在 Linux ...

  2. KVM/QEMU/qemu-kvm/libvirt 概念全解

    目录 目录 前言 KVM QEMU KVM 与 QEMU qemu-kvm Libvirt Libvirt 在 OpenStack 中的应用 前言 如果是刚开始接触虚拟机技术的话, 对上述的概念肯定会 ...

  3. 【原创】Linux虚拟化KVM-Qemu分析(三)之KVM源码(1)

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: KVM版本:5.9 ...

  4. Linux虚拟化技术KVM、QEMU与libvirt的关系(转)

    说明:个人理解,KVM是内核虚拟化技术,而内核是不能使用在界面上使用的,那么此时QEMU提供了用户级别的使用界面,相互辅助.当然,单独使用QEMU也是可以实现一整套虚拟机,不过QEMU+KVM基本是标 ...

  5. 关于Linux虚拟化技术KVM的科普 科普三(From OenHan)

    http://oenhan.com/archives,包括<KVM源代码分析1:基本工作原理>.<KVM源代码分析2:虚拟机的创建与运行>.<KVM源代码分析3:CPU虚 ...

  6. 关于Linux虚拟化技术KVM的科普 科普二(KVM虚拟机代码揭秘)

    代码分析文章<KVM虚拟机代码揭秘--QEMU代码结构分析>.<KVM虚拟机代码揭秘--中断虚拟化>.<KVM虚拟机代码揭秘--设备IO虚拟化>.<KVM虚拟 ...

  7. KVM+QEMU虚拟化概念

    概念: KVM,即Kernel-basedvirtual machine,由redhat开发,是一种开源.免费的虚拟化技术.对企业来说,是一种可选的虚拟化解决方案. 定义:基于Linux内核的虚拟机 ...

  8. 【原创】Linux虚拟化KVM-Qemu分析(一)

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: KVM版本:5.9 ...

  9. 【原创】Linux虚拟化KVM-Qemu分析(四)之CPU虚拟化(2)

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: KVM版本:5.9 ...

  10. 【原创】Linux虚拟化KVM-Qemu分析(五)之内存虚拟化

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: KVM版本:5.9 ...

随机推荐

  1. LNMP搭建静态网页服务器

    chattr -i default/.user.ini LNMP搭建使用 1.安装screen,命令或者操作可以一直运行下去 yum install screen 2.获取及安装 LNMP wget ...

  2. 生产事故-走近科学之消失的JWT

    入职多年,面对生产环境,尽管都是小心翼翼,慎之又慎,还是难免捅出篓子.轻则满头大汗,面红耳赤.重则系统停摆,损失资金.每一个生产事故的背后,都是宝贵的经验和教训,都是项目成员的血泪史.为了更好地防范和 ...

  3. 目标检测(Object Detection)

    文章目录 目标检测(Object Detection) 一.基本概念 1. 什么是目标检测 2. 目标检测的核心问题 3. 目标检测算法分类 1)Tow Stage 2)One Stage 4. 目标 ...

  4. [C++核心编程] 1、内存分区模型

    文章目录 1 内存分区模型 1.1 程序运行前 1.2 程序运行后 1.3 new操作符 1 内存分区模型 C++程序在执行时,将内存大方向划分为4个区域 代码区:存放函数体的二进制代码,由操作系统进 ...

  5. 【Redis】Cluster集群

    一.Redis Cluster 工作原理 在引入哨兵机制后,解决了Redis主从架构Master故障时的主从切换问题,保证了Redis服务可用性.但依旧无法解决单机节点出现的写入性能瓶颈(网卡速率.单 ...

  6. Pwn系列之Protostar靶场 Stack3题解

    (gdb) disass main Dump of assembler code for function main: 0x08048438 <main+0>: push ebp 0x08 ...

  7. 文心一言 VS chatgpt (6)-- 算法导论2.3 1~2题

    一.使用图 2-4作为模型,说明归并排序在数组 A=(3,41,52,26,38,57,9,49)上的操作. 文心一言: 使用图 2-4作为模型,说明归并排序在数组 A=(3,41,52,26,38, ...

  8. 2020-11-10:golang中的接口,类型不空,值为空,如何判断是nil?

    福哥答案2020-11-10: reflect.ValueOf(接口变量).IsNil(),用这个即可判断.对于值类型,会panic.两种方法如下:1.异常判断:recover捕获.2.类型判断:re ...

  9. 2022-07-29:一共有n个人,从左到右排列,依次编号0~n-1, h[i]是第i个人的身高, v[i]是第i个人的分数, 要求从左到右选出一个子序列,在这个子序列中的人,从左到右身高是不下降的。

    2022-07-29:一共有n个人,从左到右排列,依次编号0~n-1, h[i]是第i个人的身高, v[i]是第i个人的分数, 要求从左到右选出一个子序列,在这个子序列中的人,从左到右身高是不下降的. ...

  10. 2021-05-13:数组中所有数都异或起来的结果,叫做异或和。给定一个数组arr,返回arr的最大子数组异或和。

    2021-05-13:数组中所有数都异或起来的结果,叫做异或和.给定一个数组arr,返回arr的最大子数组异或和. 前缀树.一个数,用二进制表示,0走左边分支,1走右边分支. 时间复杂度:O(N). ...