之前的系列文章分析过virtio设备和驱动,Virtio-Net是PCI网卡设备驱动,分别会在virtnet-probe和virtio_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
欢迎关注个人公众号,不定期更新技术文章。