Linux 内核的virtio框架

VirtIO 是一个用来实现“虚拟IO”的通用框架,典型的用于虚拟机上面实现虚拟的pci,网卡,磁盘等虚拟设备,kvm等都使用了这个技术。大概浏览了一下相关的内核代码,这个virtio应该是专门应用于那种“半虚拟化?(部分虚拟化)”的虚拟机的来虚拟各种通用io设备的,好像不是很适合单纯的用来虚拟设备。

参考资料

Virtio:针对 Linux 的 I/O 虚拟化框架 http://www.ibm.com/developerworks/cn/linux/l-virtio/index.html?ca=drs-cn-0304

KVM的virtio说明文档:

http://www.linux-kvm.org/page/Virtio

http://www.linux-kvm.org/wiki/images/d/dd/KvmForum2007%24kvm_pv_drv.pdf

内核相关示例源码有;

http://lxr.linux.no/linux+v2.6.35.7/drivers/net/virtio_net.c

http://lxr.linux.no/linux+v2.6.35.7/drivers/block/virtio_blk.c

http://lxr.linux.no/linux+v2.6.35.7/drivers/virtio/virtio.c

前段接口(guest端)的例子:

http://lxr.linux.no/linux+v2.6.35.7/drivers/s390/kvm/kvm_virtio.c

http://lxr.linux.no/linux+v2.6.35.7/drivers/lguest/lguest_device.c

如果想用virtio来实现一个虚拟设备大概流程是这样的:

1、自己实现scan设备的功能,然后调用register_virtio_device 函数来注册发现的虚拟设备。参考drivers/lguest/lguest_device.c 里面的scan_devices 函数。一个典型的实现是,虚拟机主机提供的共享内存配置,里面提供了虚拟设备的列表一样的配置,然后自己根据配置注册device。

      注:pci总线等应该也是要扫描各个插槽,然后发现新的pci设备,然后创建device结构,再调用到驱动的probe函数里面的,应该各个总线都有发现新设备的机制,device一般是scan的时候创建。

2、为virtio_device 设备准备好virtqueue 队列。参考drivers/s390/kvm/kvm_virtio.c 里面的kvm_find_vq 函数。特别的是要提供自己的virtqueue 结构的notify函数。notify函数用于通知host主机队列里面已经有消息存在了,一般是hypercall ,就是通过vmcall指令或者traps(中断?)来通知主机。例如这个kvm代码。


160/*
161 * When the virtio_ring code wants to notify the Host, it calls us here and we
162 * make a hypercall. We hand the address of the virtqueue so the Host
163 * knows which virtqueue we're talking about.
164 */
165static void kvm_notify(struct virtqueue *vq)
166{
167        struct kvm_vqconfig *config = vq->priv;
168
169        kvm_hypercall1(KVM_S390_VIRTIO_NOTIFY, config->address);
170}
171

http://lxr.linux.no/linux+v2.6.35.7/arch/x86/include/asm/kvm_para.h

69
70/* This instruction is vmcall. On non-VT architectures, it will generate a
71 * trap that we will then rewrite to the appropriate instruction.
72 */
73#define KVM_HYPERCALL ".byte 0x0f,0x01,0xc1"
74
75/* For KVM hypercalls, a three-byte sequence of either the vmrun or the vmmrun
76 * instruction. The hypervisor may replace it with something else but only the
77 * instructions are guaranteed to be supported.
78 *
79 * Up to four arguments may be passed in rbx, rcx, rdx, and rsi respectively.
80 * The hypercall number should be placed in rax and the return value will be
81 * placed in rax. No other registers will be clobbered unless explicited
82 * noted by the particular hypercall.
83 */
84
85static inline long kvm_hypercall0(unsigned int nr)
86{
87        long ret;
88        asm volatile(KVM_HYPERCALL
89                     : "=a"(ret)
90                     : "a"(nr)
91                     : "memory");
92        return ret;
93}
94
95static inline long kvm_hypercall1(unsigned int nr, unsigned long p1)
96{
97        long ret;
98        asm volatile(KVM_HYPERCALL
99                     : "=a"(ret)
100                     : "a"(nr), "b"(p1)
101                     : "memory");
102        return ret;
103}
104

3、应该/drivers/net/virtio_net.c 等代码已经实现了前端的网卡,磁盘等驱动了。所以只要你register_virtio_device注册了之后,客户机应该是可以看到相应的虚拟网卡的了。

这些驱动使用virtio的框架方法大概是这样的,

virtqueue_get_buf    这个函数用来获取virtqueue队列里面收到的消息。

virtqueue_add_buf 把发送到消息缓存写到virtqueue队列,然后 调用virtqueue_kick函数来通知host主机队列里面有消息需要它接受。 virtqueue_kick将调用到virtqueue结构的notify函数,notify函数通过中断或者高级的虚拟机指令mcall通知到host主机。 host主机,根据实现可能是共享内存的hypercall的,就可以直接复制相应的内存过去就可以了。可以去看相应的虚拟机的hypercall的实现代码。

virtio的作用就是提供一种居于virtqueue的通用的虚拟io框架。

你可能感兴趣的:(Linux 内核的virtio框架)