virtio协议1.0 -- 基础组件

这里描述符的是virito的通用基础组件,不依赖于总线类型,如PCI,MMIO, Channel IO 等

0. virtio基本基础组件

  • Device status 域
  • Feature bits
  • Device Configuration space
  • 一个或多个 virtqueues

1. Device status 域

驱动在发现和加载设备过程中,会经历一系列加载步骤,device status 就是在底层提示加载步骤的完成状态,底层设备通过这些状态值,知道需要完成的事情

  • 状态
    • ACKNOWLEDGE (1) 客户系统发现了设备,并且知道是一个virtio设备
    • DRIVER (2) :客户系统找到了对应的驱动
    • FAILED (128): 客户系统的驱动有问题,加载失败
    • FEATURES_OK (8) 驱动已经取得了所有的features bit并协商完成了
    • DRIVER_OK (4) :驱动安装完成
    • DEVICE_NEEDS_RESET (64) :设备有问题且问题设备无法恢复
  • 驱动
    • 按照初始化步骤,不断设定device status域
    • 不能清除,只能写入
    • 设置了FAILED,在重新初始化前,必须对设备进行reset
    • 不可以依赖DEVICE_NEEDS_RESET来处理in flight数据,应在在设备重启后,重新发送
  • 设备
    • 设备在reset的时候,device status域必须归零
    • 在 DRIVER_OK 前,不能处理数据
    • 设备进入错误状态的时候,必须设置 DEVICE_NEEDS_RESET 位,然后发送 device configuration change 中断

2. Feature Bits

设备提供所有支持的 features bit,在设备初始化期间,驱动读取 features bit 并告诉设备哪些 features 被接受,一旦协商完成,再需要协商的话,需要reset设备。通过 features bit 可以完成向后兼容,驱动可以选择最新能力的feature bit或者选择不接受没有实现的feature

  • feature 位分配
    • 0 to 23 特定类型设备的 feature bits
    • 24 to 32 queue 的 feature bits 
    • 33 and above 保留.
    • 有些feature bit 会表示设备有新的configuration space域
  • 驱动
    • 不能接受没有实现的特性的feature bits,包括关联的
    • 如果设备提供的feature bits比较旧,驱动应切换为老模式,或者停止初始化
  • 设备
    • 不能提供没有实现的特性的feature bits,包括关联的
    • 接受并校验驱动提供的feature bits的子集
    • 当dirver写回feature bits后,设定FEATURES_OK状态
  • 区别
    • 设备 VIRTIO_F_VERSION_1 bit 没有,说明是 Legacy 设备
    • 驱动不识别 VIRTIO_F_VERSION_1,说明是 Legacy driver,设备需要支持 legacy接口

3. 设备配置空间

Device configuration space 用作设定设备初始化参数,每个configuration域都是根据feature bits
可选的,未来扩展的话,会在尾部增加新的 configuration space,小端序且多字节的

configuration space 都有一个 generation count,每次读写都要校验这个generation,保证修改的内容被及时读取到

  • 驱动
    • 驱动不能假设对大于32bit的读取是原子的
    • 读取 configuration space 必须检测到 feature bit 被设置了
    • 只要设备的 configuration space 比协议规定的大,driver都要接收
  • 设备
    • 在驱动设定FEATURES_OK前,设备允许读取任何的 device-specific configuration 域,包括后面可能驱动不接受的feature bit 对应的域
    • Device Configuration Space 是小端序的
  • 区别
    • 字节序:legacy 认为 configuration space 和客户机保持一致的字节序,而不是PCI的小端序 
    • Device Configuration Space:Legacy 设备没有 configuration generation 域,在配置更新的时候,很可能发生race conditions,影响的有 BLK 的 block capacity 和 NET 的 mac,需要驱动反复读取,直到两次结果是一致的 

4. Virtqueues

  • 定义
    • 数据传输机制是 virtqueue
    • 每个设备有一个或多个Virtqueues
    • 每个队列 16bit queue size说明队列深度,2的幂,最大32768
  • virtqueue组成
    • Descriptor 表
    • Available Ring
    • Used Ring
  •  virtqueue 地址
    • 每部分都使用客户机连续的物理内存 
    • 每部分不同的对齐要求
    • 地址算法
  • 发送过程
    • 驱动填写buffer到desc 表中的desc(可能成链)
    • 驱动把(收个)desc index写入 available ring
    • 驱动发送kick到设备
    • 设备处理完buffer
    • 设备把(收个) desc index 写入 used ring
    • 设备发送中断
  • 驱动
    • 驱动需要保证地址是对齐的
  • 区别
    • legacy interface 的 virtqueue内存布局
      • 三部分是连读的,占用两个或多个物理连续页面,中间靠padding对齐
      • 必须读取qsize,并一次分配足够的内存
      • virqueue 布局结构
    • 字节序
    • legacy interface 使用客户机的字节序
       
  • 消息帧格式
    • 格式
      • 12字节virtio头,后面跟着网络包
      • 可以使用12字节desc+1514 字节 desc,也可以使用 1526 字节 desc包含头和报文,或者更多个desc
      • 设备可以限制所有desc的总大小
    • 设备
      • 不能假设desc的存放方式
      • 可限制desc链的长度
    • 驱动
      • 设备可写desc必须放在设备只读desc之后
      • 使用限制的desc链长度
    • 区别
      • legacy interface 使用 VIRTIO_F_ANY_LAYOUT 特性来提示帧格式是任意的
    • Descriptor 表
      • desc 引用了buffer,addr是物理地址,通过next形成链
      • desc flag 标志位表示是设备可读还是设备可写,链里面可以包含两种
      • queue size 是desc表中 desc的数量,也是desc链的最大长度
      • desc 的结构是 struct vring_desc
      • 设备
        • 不能写入只读desc 
        • 不能读取只写desc(调试或诊断使用) 
      • 驱动 
        • desc链总长度不能超过2^32字节
        • desc链不许打环 
      • 间接desc表
        • 处理数量多,消息大的请求
        • VIRTIO_F_INDIRECT_DESC
        • 扩展ring的能力,ind desc表存在内存任意处
        • ring中desc 的flag设置 VIRTQ_DESC_F_INDIRECT,addr 内存指向 ind desc表,len为间接表字节总长度
        • 第一个desc在间接表开始(idx = 0),下面通过desc链接,没有设置VIRTQ_DESC_F_NEXT 标志的desc就是结尾
        • 一个间接表内可以包含只读、只写两种desc
        • 设备
          • ring中的间接表desc的VIRTQ_DESC_F_WRITEb标志会被忽略
          • 有VIRTQ_DESC_F_INDIRECTb标志的desc后面可以成链(极其特殊) 
        • 驱动
          • VIRTIO_F_INDIRECT_DESC特性协商了,才可以有 VIRTQ_DESC_F_INDIRECT类型的desc
          • desc链最大长度是queue size
          • VIRTQ_DESC_F_INDIRECT and VIRTQ_DESC_F_NEXT 不能同时设置
             
      • Available Ring
        • 格式
        • 驱动给设备发包
        • 每个ring元素是一个desc链的头
        • 驱动写,设备读
        • idx 是客户机本次生产位置的下一个位置(取模queue_size),从0 开始单向增长
        • legacy中结构是 vring_avail,常量是 RING_AVAIL_F_NO_INTERRUPT而不是VIRTIO_F_EVENT_IDX,其他一样
      • Interrupt 抑制
        • 没有协商 VIRTIO_F_EVENT_IDX,可以通过 available ring 的 flag 域来提示设备在返回used 时候不要发送中断,比较暴力
        • 协商了 VIRTIO_F_EVENT_IDX ,通过 available ring 的 used_event 来告诉设备返回多少used 才发送一个中断
        • 都不可靠,但是对性能优化有帮助
        • 驱动
          • 不支持 VIRTIO_F_EVENT_IDX没协商
            • available ring->flag为0或1
            • available ring->flag 为1表示设备返回used后不要发送中断
          • 支持 VIRTIO_F_EVENT_IDX 协商
            • available ring->flag 必须为0
            • 可能使用 available ring-> used_event 通知设备生产used_idx到used_event值,才产生一个中断(此时 used_idx = used_event  + 1)
        • 设备
          • 不支持 VIRTIO_F_EVENT_IDX
            • 忽略 available ring->used_event
            • 写 desc idx 到 used ring后,flag 为1 不发送中断,flag为0发送中断
          • 支持 VIRTIO_F_EVENT_IDX
            • 忽略 available ring->flag
            • 写 desc idx 到 used ring后,used ring -> idx(写入位置) == available ring->used_event,设备发送中断,否则不发送中断
      • Used Ring
        • 结构
          • used ring 设备处理完buffer后返回给驱动使用
          • 设备写,驱动读
          • 每个ring元素是一个结构,id是描述符链的开始desc idx,len是buffer的总字节数
          • idx 表示设备生产到used ring的哪个位置,从0开始,单向递增
          • legacy 叫做 vring_used 和 vring_used_elem,使用常量 VRING_USED_F_NO_NOTIFY 而不是 VIRTIO_F_EVENT_IDX,其他一样
        • 区别
          • 驱动可以忽略len,设备也可以设置len为不同的值
          • legacy interface忽略len值
          • len必须和可写desc总长度相等(cq)
        • 设备
          • 必须先写len,再更新 used idx
          • 设备必须从第一个可写的desc,写入len字节,写完后才可以更新 used idx
          • 设备可能写入的字节多余len
        • 驱动
          • 驱动应该忽略超过len字节的数据内容
      • KICK 抑制
        • 抑制对设备的KICK
        • 操作 used ring 的flag 和 avail_event 的方式达到抑制的目的
        • 驱动
          • 当分配used ring时,必须初始化 used ring->flag 为0
            • 不支持 VIRTIO_F_EVENT_IDX
              • 必须忽略 used ring -> avail_event
              • 在驱动向available ring写入 desc->idx 后
                • used ring -> flag 为1, 不发送KICK
                • used ring -> flag 为 0,发送KICK
            • 支持 VIRTIO_F_EVENT_IDX
              • 忽略 used ring -> flag
              • 在驱动向available ring写入 desc->idx 后
                • 如果 available ring->idx (写入位置) == used ring -> avail_event, 发送 KICK
                • 不相等,不发送
        • 设备
          • 不支持 VIRTIO_F_EVENT_IDX
            • 必须设置 used ring->flag 为 0 或 1
            • used ring->flag 设置为1,表示不希望驱动发送KICK
          • 支持 VIRTIO_F_EVENT_IDX
          • 必须设置 used ring->flag 为0
          • 可以设置 used ring -> avail_event 抑制驱动的KICK,直到驱动写入的 desc 位置 available ring->idx == used ring -> avail_event,才发送KICK(之后驱动会增加 available ring->idx++,此时 available ring->idx = used ring -> avail_event + 1)
  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值