赞
踩
转载自:http://blog.chinaunix.net/uid-26000137-id-3826914.html
virtio设备是作为pci设备被使用的,因此具有pci设备的所有属性:
virtio header占用pci设备的24字节的配置空间:32 * (0 - 5)
virtio header后面跟随一个device specific的config结构
virtio header包括:
/* A 32-bit r/o bitmask of the features supported by the host */
#define VIRTIO_PCI_HOST_FEATURES 0
/* A 32-bit r/w bitmask of features activated by the guest */
#define VIRTIO_PCI_GUEST_FEATURES 4
/* A 32-bit r/w PFN for the currently selected queue */
#define VIRTIO_PCI_QUEUE_PFN 8
/* A 16-bit r/o queue size for the currently selected queue */
#define VIRTIO_PCI_QUEUE_NUM 12
/* A 16-bit r/w queue selector */
#define VIRTIO_PCI_QUEUE_SEL 14
/* A 16-bit r/w queue notifier */
#define VIRTIO_PCI_QUEUE_NOTIFY 16
/* An 8-bit device status register. */
#define VIRTIO_PCI_STATUS 18
/* An 8-bit r/o interrupt status register. Reading the value will
return the
* current contents of the ISR and will also clear it. This is
effectively
* a read-and-acknowledge. */
#define VIRTIO_PCI_ISR 19
/* The bit of the ISR which indicates a device configuration change. */
#define VIRTIO_PCI_ISR_CONFIG 0x2
/* MSI-X registers: only enabled if MSI-X is enabled. */
/* A 16-bit vector for configuration changes. */
#define VIRTIO_MSI_CONFIG_VECTOR 20
/* A 16-bit vector for selected queue notifications. */
#define VIRTIO_MSI_QUEUE_VECTOR 22
/* Vector value used to disable MSI for queue */
#define VIRTIO_MSI_NO_VECTOR 0xffff
每个特定设备还有自己的配置结构体,以上是通用部分
guest读写方式:
/* Select the queue we're interested in */
iowrite16(index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
/* Check if queue is either not available or already active. */
num = ioread16(vp_dev->ioaddr + VIRTIO_PCI_QUEUE_NUM);
virtio驱动通过读取和写入这些配置空间实现通用的guest特性,队列,中断等等
方面同qemu-kvm交互的功能
qemu-kvm提供了下面这个函数结构体来响应guest的不同读写请求。
static const MemoryRegionPortio virtio_portio[] = {
{ 0, 0x10000, 1, .write = virtio_pci_config_writeb, },
{ 0, 0x10000, 2, .write = virtio_pci_config_writew, },
{ 0, 0x10000, 4, .write = virtio_pci_config_writel, },
{ 0, 0x10000, 1, .read = virtio_pci_config_readb, },
{ 0, 0x10000, 2, .read = virtio_pci_config_readw, },
{ 0, 0x10000, 4, .read = virtio_pci_config_readl, },
PORTIO_END_OF_LIST()
};
static const MemoryRegionOps virtio_pci_config_ops = {
.old_portio = virtio_portio,
.endianness = DEVICE_LITTLE_ENDIAN,
};
virtio设备作为pci设备被guest识别,guest通过读写pci设备配置空间的信息来
同qemu-kvm交互。
所以实际上是guest为queue分配vring表的空间,然后通过上面这种交互方式将地
址传递给qemu-kvm,qemu-kvm把地址复制给guest选定的queue,这样guest和
qemu-kvm就共用这一片vring表的空间。
guest通过queue发送消息,实际上就是向这一片共享的vring表空间写入信息并标
识为等待qemu-kvm读取,qemu-kvm读取并处理后又将结果写进去(guest写的信息
里面预留空间给qemu-kvm来写了)再标识为已处理,同时发一个中断,guest接到
中断就去读取处理结果。
/*
函数功能:为目标设备获取一个queue
vdev:目标设备
index:给目标设备使用的queue的编号
callback:queue的回调函数
name:queue的名字
msix_vec:给queue使用的msix vector的编号
*/
- static struct virtqueue *setup_vq(struct virtio_device *vdev,
- unsigned index, void (*callback)(struct virtqueue *vq),
- const char *name, u16 msix_vec)
- {
- struct virtio_pci_device *vp_dev = to_vp_device(vdev);
- struct virtio_pci_vq_info *info;
- struct virtqueue *vq;
- unsigned long flags, size;
- u16 num;
- int err;
- /* 通知guest我们想要使用的queue的号码 */
- iowrite16(index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
- /* 返回刚刚选中的queue的可用列表数,从而判断该queue是否可用 */
- num = ioread16(vp_dev->ioaddr + VIRTIO_PCI_QUEUE_NUM);
- /* 如果num为0,则该queue不可用,因为没空闲列表了。
- 返回刚刚选中的queue的地址,如果该queue的地址非空,则它已经
- 在被使用了,该queue不可用。
- */
- if (!num || ioread32(vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN))
- return ERR_PTR(-ENOENT);
- /* allocate and fill out our structure the represents an active
- * queue */
- info = kmalloc(sizeof(struct virtio_pci_vq_info), GFP_KERNEL);
- if (!info)
- return ERR_PTR(-ENOMEM);
- info->num = num;
- info->msix_vector = msix_vec;
- size = PAGE_ALIGN(vring_size(num, VIRTIO_PCI_VRING_ALIGN));
- /* 为vring表分配空间 */
- info->queue = alloc_pages_exact(size, GFP_KERNEL|__GFP_ZERO);
- if (info->queue == NULL) {
- err = -ENOMEM;
- goto out_info;
- }
- /* activate the queue */
- /* 将vring表的地址传递给qemu-kvm,guest将和qemu-kvm通过共享
- 这一共同的vring表来完成信息交互:
- 1.guest通过queue发信息就是向vring表写入信息并标识为等待
- qemu-kvm端virtio驱动读取,同时通过notify设备通知qemu-kvm
- 端virtio驱动。
- 2.qemu-kvm端virtio驱动处理后将结果也写入vring表(guest发的
- 信息预留了返回结果的空间)并标识为已处理,同时发送中断通
- 知guest。
- 3.guest中断处理函数判明中断类型,读取返回结果。
- */
- iowrite32(virt_to_phys(info->queue) >>
- VIRTIO_PCI_QUEUE_ADDR_SHIFT, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
- /* create the vring */
- /* 创建vring_virtqueue结构体,作为queue和vring协同工作的桥梁,
- queue和vring都嵌入在vring_virtqueue结构体之中,返回给驱动的
- queue实际就是vring_virtqueue的成员,queue是vring_virtqueue结构体提供给
- 上层驱动的接口,vring结构体则负责管理vring表,是操作vring表的接口。
- */
- vq = vring_new_virtqueue(index, info->num,
- VIRTIO_PCI_VRING_ALIGN, vdev,true, info->queue, vp_notify, callback,
- name);
- if (!vq) {
- err = -ENOMEM;
- goto out_activate_queue;
- }
- vq->priv = info;
- info->vq = vq;
- /* 如果使用msix vector,则将msix vector编号写入到设备的配置空间
- 去,再读取的目的就是判断qemu-kvm端是否能够支持。
- */
- if (msix_vec != VIRTIO_MSI_NO_VECTOR) {
- iowrite16(msix_vec, vp_dev->ioaddr +
- VIRTIO_MSI_QUEUE_VECTOR);
- msix_vec = ioread16(vp_dev->ioaddr +
- VIRTIO_MSI_QUEUE_VECTOR);
- if (msix_vec == VIRTIO_MSI_NO_VECTOR) {
- err = -EBUSY;
- goto out_assign;
- }
- }
- if (callback) {
- spin_lock_irqsave(&vp_dev->lock, flags);
- list_add(&info->node, &vp_dev->virtqueues);
- spin_unlock_irqrestore(&vp_dev->lock, flags);
- } else {
- INIT_LIST_HEAD(&info->node);
- }
- return vq;
- out_assign:
- vring_del_virtqueue(vq);
- out_activate_queue:
- iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
- free_pages_exact(info->queue, size);
- out_info:
- kfree(info);
- return ERR_PTR(err);
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。