Virtio的实际执行流程,以收包为例详细讲讲

这是这系列文章的第二篇,我们第一篇粗略看了看Virtio的数据结构,接下来我们大概来看看VIrtio设备具体的数据流转流程,看看究竟数据在device端和driver端是如何交互的。
前面有文章已经讲了virtQueue的具体数据结构,不了解的大家可以打开看看。
聊聊VirtIO的数据结构 —— Split virt queue

我们这里先不讨论通知的方式,单独讨论数据以及数据结构的流转,通知的方式我们后面单开文章再说。

device提供avail过程的总结

其实整体的流程不是特别复杂,我们以Virtio1.0及以下的Split 模式的Virt queue来看看队列具体是如何进行工作的。
总结起来其实就以下几步:

  1. driver提供buffer,放到desc table之中(如果是chain模式的话,可能是多个buffer组成的chain,简化来看我们不用管它)
  2. 更新avail ring, driver 将已经放入buffer的desc ring的idx信息(如果是chain模式,则是chain的头部的idx)放入avail ring中
  3. 以上步骤1和步骤2可能执行多次
  4. driver需要提供一个内存屏障来保证device能够在以上步骤之后正确取到desc ring中的信息和avail ring中的desc idx信息。
  5. 更新avail ring的header中的idx, driver尝试更新avail ring中的idx(与上述2步骤区别,2步骤更新的是desc ring中的信息和avail ring中的信息,这里更新的是avail ring的header中的idx!)
  6. driver提供内存屏障,保证device在检查通知抑制的时候avail ring的header中的idx已经更新了
  7. 发送notify到device, driver发送notify到device,通知设备已经有包的buffer来了可以用了哥们儿。(当然中断抑制不能是开的)

driver回used的过程

数据路径上,不讨论通知的形式的时候,其实设备写used是相对简单的一个流程。
检查avail ring的idx,去desc table中对应的idx找到实际的地址以及len信息,取到对应的buffer或者数据。完成之后通知driver即可。(通知的流程这篇文章我们先不讨论,后面再细说)

avail更新的详细的流程叙述

1. driver提供bufffer

buffer可能是由一个或者多个元素组成。我们假设一个buffer的元素b

  1. 获取下一个可用的desc table中的entry,我们称为d
  2. d.addr指向我们的buffer b的开头
  3. d.len设置为我们buffer b的长度
  4. 如果b是设备可写的,那么去设置一下VIRTQ_DESC_F_WRITE这个flag,否则设置为0
  5. 如果接下来还有buffer:
    5.1 d.next指向下面的buffer的开头
    5.2 设置VIRTQ_DESC_F_NEXT这个表示位
    如果d.next设置了,那么就是chain模式了,以链表的形式串联了一堆buffer。

2. 更新avail ring

之前我们已经看过了avail ring的数据结构:

struct vring_avail {
   __virtio16 flags;
   __virtio16 idx;
   __virtio16 ring[q_size];
}; 

这里更新的是

avail->ring[avail->idx % qsz] = head;

注意,未更新的avail->idx指向的是下一次可使用ring的idx。

3. 更新idx

这里才会去更新idx,这里和第二步之间需要有内存屏障强保序

avail->idx += added;
  • 21
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值