设为首页 收藏本站
查看: 1827|回复: 0

[经验分享] qemu-kvm virtio-blk设备

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2015-12-24 14:27:58 | 显示全部楼层 |阅读模式
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);
}

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-155788-1-1.html 上篇帖子: AMD平台上实现KVM虚拟化技术 下篇帖子: qemu-kvm 命令启动时,网络的设置
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表