qemu-kvm virtio-blk设备
virtio-blk为半虚拟驱动,virtio-blk请求处理过程如下:
1.客户机(virtio-blk设备驱动)读写数据方式vring队列
2.客户机执行Virtqueue队列函数kick通知host宿主机(通过virtio-pci硬件寄存器发送通知)
3.宿主机host截获通知信息
4.宿主机host从vring队列获取读写请求(vring队列内容涉及地址为客户机物理地址)
5.宿主机host处理读写请求
6.宿主机host处理结果添加到vring队列
7.宿主机host发送中断(通过virtio-pci中断)
具体设计代码如下:
1.客户机与宿主机通知
notify函数用于通知host主机队列里面已经有消息存在了,s390采用是hypercall ,而其他体系结构使用写寄存器来通知host主机。
/* the notify function used when creating a virt queue */
static void vp_notify(struct virtqueue *vq)
{
struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev);
struct virtio_pci_vq_info *info = vq->priv;
/* we write the queue's selector into the notification register to
* signal the other end */
iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_NOTIFY);
}
2.virtio-blk注册要截获的端口
static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,
uint16_t vendor, uint16_t device,
uint16_t class_code, uint8_t pif)
{
pci_register_bar(&proxy->pci_dev, 0, size, PCI_BASE_ADDRESS_SPACE_IO,
virtio_map);
}
static void virtio_map(PCIDevice *pci_dev, int region_num,
pcibus_t addr, pcibus_t size, int type)
{
VirtIOPCIProxy *proxy = container_of(pci_dev, VirtIOPCIProxy, pci_dev);
VirtIODevice *vdev = proxy->vdev;
unsigned config_len = VIRTIO_PCI_REGION_SIZE(pci_dev) + vdev->config_len;
proxy->addr = addr;
register_ioport_write(addr, config_len, 1, virtio_pci_config_writeb, proxy);
register_ioport_write(addr, config_len, 2, virtio_pci_config_writew, proxy);
register_ioport_write(addr, config_len, 4, virtio_pci_config_writel, proxy);
register_ioport_read(addr, config_len, 1, virtio_pci_config_readb, proxy);
register_ioport_read(addr, config_len, 2, virtio_pci_config_readw, proxy);
register_ioport_read(addr, config_len, 4, virtio_pci_config_readl, proxy);
if (vdev->config_len)
vdev->get_config(vdev, vdev->config);
}
3.vmm要截获端口
addr截获端口,val端口存放数值
static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
{
switch (addr) {
case VIRTIO_PCI_QUEUE_NOTIFY:
if (val < VIRTIO_PCI_QUEUE_MAX) {
virtio_queue_notify(vdev, val);
}
break;
}
通过val可得到相应的virtio设备的队列
void virtio_queue_notify(VirtIODevice *vdev, int n)
{
virtio_queue_notify_vq(&vdev->vq[n]);
}
获取队列的请求,处理队列请求
void virtio_queue_notify_vq(VirtQueue *vq)
{
if (vq->vring.desc) {
VirtIODevice *vdev = vq->vdev;
trace_virtio_queue_notify(vdev, vq - vdev->vq, vq);
vq->handle_output(vdev, vq);
}
}
virio-blk块设备请求数据处理函数,
static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIOBlock *s = to_virtio_blk(vdev);
VirtIOBlockReq *req;
MultiReqBuffer mrb = {
.num_writes = 0,
.old_bs = NULL,
};
while ((req = virtio_blk_get_request(s))) {
virtio_blk_handle_request(req, &mrb);
}
if (mrb.num_writes > 0) {
do_multiwrite(mrb.old_bs, mrb.blkreq, mrb.num_writes);
}
/*
* FIXME: Want to check for completions before returning to guest mode,
* so cached reads and writes are reported as quickly as possible. But
* that should be done in the generic block layer.
*/
}
static void virtio_blk_handle_request(VirtIOBlockReq *req,
MultiReqBuffer *mrb)
{
else if (req->out->type & VIRTIO_BLK_T_OUT) {
qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1],
req->elem.out_num - 1);
virtio_blk_handle_write(mrb->blkreq, &mrb->num_writes,
req, &mrb->old_bs);
} else {
qemu_iovec_init_external(&req->qiov, &req->elem.in_sg[0],
req->elem.in_num - 1);
virtio_blk_handle_read(req);
}
}
virtio-blk数据处理完成,把结果放入队列中
static void virtio_blk_req_complete(VirtIOBlockReq *req, int status)
{
VirtIOBlock *s = req->dev;
trace_virtio_blk_req_complete(req, status);
req->in->status = status;
virtqueue_push(s->vq, &req->elem, req->qiov.size + sizeof(*req->in));
virtio_notify(&s->vdev, s->vq);
}
宿主机发送中断通知客户机
static void virtio_pci_notify(void *opaque, uint16_t vector)
{
VirtIOPCIProxy *proxy = opaque;
if (msix_enabled(&proxy->pci_dev))
msix_notify(&proxy->pci_dev, vector);
else
qemu_set_irq(proxy->pci_dev.irq[0], proxy->vdev->isr & 1);
}
http://blog.csdn.net/zhuriyuxiao/article/details/8824735
阅读(3416) | 评论(0) | 转发(0) |