前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >virtio代码分析(二)-kernel vhost-net部分

virtio代码分析(二)-kernel vhost-net部分

作者头像
惠伟
发布2021-02-24 11:25:58
1.7K0
发布2021-02-24 11:25:58
举报
文章被收录于专栏:虚拟化笔记虚拟化笔记

惠伟:virtio代码分析(一)-qemu部分​zhuanlan.zhihu.com

假如我们加个参数vhost=on,vhost定义了一堆api,qemu把virtio收发包和用于通知收发包的功能offload给kernel vhost-net了,这包就不用从kernel到用户态的qemu,再从qemu共享给guest,直接从kernel共享给guest,减少一次kernel到用户态qemu的复制开销。

qemu对vhost-net初始化,重点关注qemu把address_space发给kernel vhost-net了,同时内核创建了一个线程叫做vhost-worker,是真正收发包干活的。

代码语言:javascript
复制
vhost_dev_init
  ├─vhost_virtqueue_init
  |   └─vhost_set_vring_call
  └─memory_listener_register
      └─listener_add_address_space
          ├─vhost_begin
          ├─vhost_log_global_start
          |   └─vhost_migration_log
          |       └─vhost_dev_set_log
          |           └─vhost_kernel_set_vring_add
          ├─vhost_log_start
          ├─vhost_region_addnop
          |   └─vhost_region_add_section
          └─vhost_commit
              └─vhost_kernel_set_mem_table(VHOST_SET_MEM_TABLE)
                  └─调用到内核vhost_set_memory
                      └─vhost_new_umem_range

通知机制这样处理,把负责通知的fd分别给了kvm和vhost-net,以后kvm就通知给vhost-net了,不再和qemu通信。如果qemu模拟中断,处理中断注入和开关中断 。还有一点就把把内核vring的三个地址设置和用户态qemu一样,而qemu又来于guest中virtio-net driver写pci配置,所以最终host kernel vhost net就和guest中driver指向同一个地址了。

代码语言:javascript
复制
vhost_net_start
  ├─virtio_pci_set_guest_notifiers
  |   └─virtio_pci_set_guest_notifier
  ├─vhost_net_start_one
  |   ├─vhost_net_start_one
  |   |   └─vhost_dev_enable_notifiers
  |   |       └─virtio_bus_set_host_notifier
  |   |           ├─event_notifier_init
  |   |           └─virtio_pci_ioeventfd_assign
  |   └─vhost_dev_start
  |        └─vhost_virtqueue_start
  |             ├─vhost_kernel_set_vring_num
  |             ├─vhost_kernel_set_vring_base把内核vring的三个地址设置和用户态一样
  |             ├─vhost_kernel_set_vring_endian
  |             ├─vhost_virtqueue_set_addr
  |             |   └─vhost_kernel_set_vring_addr
  |             ├─vhost_kernel_set_vring_kick
  |             └─vhost_kernel_set_vring_call
  └─vhost_set_vring_enable

内核vhost的结构体,很虚拟vhost_net内嵌vhost_dev,vhost_dev有2+2*queue个vhost_poll。

代码语言:javascript
复制
struct vhost_net {
	struct vhost_dev dev;
	struct vhost_net_virtqueue vqs[VHOST_NET_VQ_MAX];
	struct vhost_poll poll[VHOST_NET_VQ_MAX];
};

内核初始化

代码语言:javascript
复制
vhost_net_open
  ├─vhost_dev_init
  |   └─vhost_poll_init
  ├─vhost_poll_init
  └─vhost_poll_init   

vhost_net_ioctl
  ├─vhost_net_set_backend
  |   └─vhost_net_enable_vq
  |       └─vhost_poll_start//把tap的fd给vhost
  ├─vhost_net_set_features
  ├─vhost_net_set_owner
  ├─vhost_dev_ioctl
  |   ├─vhost_set_memory
  |   ├─vhost_set_log_base
  |   └─vhost_set_log_fd
  └─vhost_vring_ioctl
      ├─set vring相关的那一坨
      └─vhost_poll_start    

qemu进程系统调用init所有poll

代码语言:javascript
复制
vhost_poll_init
  ├─init_waitqueue_func_entry(&poll->wait, vhost_poll_wakeup)
  ├─init_poll_funcptr(&poll->table, vhost_poll_func)
  └─vhost_work_init

qemu对/dev/vhost-net开始poll那2个poll

代码语言:javascript
复制
vhost_poll_start(n->poll)
  ├─tun_chr_poll
  |   └─poll_wait
  |       └─vhost_poll_func
  |           └─add_wait_queue加到tun文件的wqh中,
                  注意vhost_poll中wqh是一个指针指向文件wqh
  └─vhost_poll_wakeup
      └─vhost_poll_queue
          └─vhost_work_queue
              └─wake_up_process把worker加入到work_list中,让vhost-worker干活

对qemu传递过来的kick event fd开始pool,guest kick kvm,kvm再通知vhost-net,开始poll再2*queue个poll

代码语言:javascript
复制
vhost_vring_ioctl(VHOST_SET_VRING_KICK)
  └─vhost_poll_start(vq->pool)

vhost-woker调用,kick通知过来的是vq,需要转换成vhost-net结构体。translate_desc是难点,guest写了一个自己的物理地址在vring.addr中,guest的整体物理内存映射在qemu的虚拟地址空间中,guest物理地址开始是p1,在qemu中开始是u1,然后这个地址p-p1+u1就是报文要放置的qemu的地址。

代码语言:javascript
复制
vhost_worker
  ├─handle_rx_net
  |   └─handle_rx
  |          ├─get_rx_bufs
  |          |   └─vhost_get_vq_desc
  |          |       └─translate_desc
  |          ├─tun_recvmsg
  |          |   └─tun_do_read
  |          |       └─tun_ring_recv
  |          |           └─skb_array_consume如果有skb则返回,
                             没有则把currentadd_wait_queue然后调度走等来了skb再wake_up
  |          └─vhost_add_used_and_signal_n
  |              ├─vhost_add_used
  |              └─vhost_signal通知guest发送完成
  ├─handle_tx_net
  |   └─handle_tx
  |       ├─vhost_get_vq_desc
  |       |   └─translate_desc
  |       ├─tun_sendmsg
  |       |   └─netif_rx_ni
  |       |       └─netif_rx_internal
  |       |           └─enqueue_to_backlog
  |       |               └─____napi_schedule触发do_softIRQ
  |       ├─vhost_add_used_and_signal_n
  |       |   ├─vhost_add_used
  |       |   └─vhost_signal通知guest接收完成
  |       └─vhost_log_write记录dirty page
  ├─handle_tx_kick
  |   └─handle_tx
  └─handle_rx_kick
      └─handle_tx

qemu进程调用,只有在vhost-net releas/stop/set_owner/set_backend时操作

代码语言:javascript
复制
vhost_poll_flush
  └─vhost_work_flush
      ├─init_completion
      ├─vhost_work_init
      ├─vhost_work_queue
      |   └─wake_up_process
      └─wait_for_completion

vhost-worker调用

代码语言:javascript
复制
vhost_flush_work
  └─complete

热迁移时获取dirty page,因为qemu和vhost-net共享,直接读写就行了

代码语言:javascript
复制
migration_bitmap_sync
  └─memory_global_dirty_log_sync
      └─memory_region_sync_dirty_bitmap
          └─vhost_log_sync
              └─vhost_sync_dirty_bitmap
                  └─vhost_dev_sync_region
                      └─memory_region_set_dirty

vhost-worker触发softirq,softirq收包给了openvswitch

代码语言:javascript
复制
process_backlog
  └─__netif_receive_skb
      └─__netif_receive_skb_core
          └─rx_handler也就是netdev_frame_hook
             └─netdev_port_receive

openvswitch发包wake_up vhost-worker

代码语言:javascript
复制
ovs_vport_send
  └─tun_net_xmit
      ├─skb_array_produce
      └─wake_up_interruptible_poll(socket)

待补充开启vq->iotlb

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云代码分析
腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档