virtio-blk简易驱动

virtio 是一种 I/O 半虚拟化解决方案,是一套通用 I/O 设备虚拟化的程序,是对半虚拟化 Hypervisor 中的一组通用 I/O 设备的抽象。对比其他设备有宿主计算机模拟,virtio设备效率更高。

virtio架构图:

最上面一排是不同的设备,如块设备,网络设备,控制台等

virtio 层属于控制层,负责设备跟宿主OS之间的通知机制(kick,notify)和控制流程,而 virtio-vring 则负责具体数据流转发。

vring 包含三个部分,描述符数组 desc,可用的 available ring 和使用过的 used ring。

struct vring_desc {
        /* Address (guest-physical). */
        u64 addr;
        /* Length. */
        u32 len;
        /* The flags as indicated above. */
        u16 flags;
        /* We chain unused descriptors via this, too */
        u16 next;
};

vring描述符结构。

struct vring_avail {
        u16 flags;
        u16 idx;
        u16 ring[];
};
struct vring_used_elem {
        /* Index of start of used descriptor chain. */
        u32 id;
        /* Total length of the descriptor chain which was used (written to) */
        u32 len;
};

struct vring_used {
        u16 flags;
        u16 idx;
        struct vring_used_elem ring[];
};

avail跟used结构。

static inline void vring_init(struct vring *vr, unsigned int num, void *p,
                              unsigned long align)
{
        vr->num = num;
        vr->desc = p;
        vr->avail = (void *)((char *)p + num*sizeof(struct vring_desc));
        vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + sizeof(u16)
                + align-1) & ~(align - 1));
}

static inline unsigned vring_size(unsigned int num, unsigned long align)
{
        return ((sizeof(struct vring_desc) * num + sizeof(u16) * (3 + num)
                 + align - 1) & ~(align - 1))
                + sizeof(u16) * 3 + sizeof(struct vring_used_elem) * num;
}

vring_size计算vring需要的内存大小,num是vring的描述符数量,为2^N,如128,256,512等。

首先放的是num个描述符vring_desc,然后是vring_avail,在vring_avail结尾有个16字位的used_event,因此是是(3+num)。之后放得是vring_used,同样,后面有个16位的avail_event,因此也是sizeof(u16) * 3。

vring_used需要4K对齐。

used_event用来通知宿主os,读取到哪里了。avail_event用来通知宿主os,写到哪里了。

设置avail_event,用来通知宿主os有新命令需要处理。宿主os完成之后产生中断,处理完成之后设置used_event,表示数据处理过了,这样宿主os才会有数据的时候继续发生中断。

desc 用于存储一些关联的描述符,每个描述符记录一个对 buffer 的描述,available ring 则用于 guest 端表示当前有哪些描述符是可用的,而 used ring 则表示 host 端哪些描述符已经被使用。

Virtio 使用 virtqueue 来实现 I/O 机制,每个 virtqueue 就是一个承载大量数据的队列,具体使用多少个队列取决于需求,例如,virtio 网络驱动程序(virtio-net)使用两个队列(一个用于接受,另一个用于发送),而 virtio 块驱动程序(virtio-blk)仅使用一个队列。

比如读取硬盘。取得virtqueue队列,virtio-blk就一个队列。然后往里面添加3条结构化数据。

struct addr_size {
  unsigned long  vp_addr;           /* 物理地址 */
  u32 vp_size;               /* 大小 */
  u32 vp_flag;   /*标记,如读,写*/
};

第一条是请求命令数据:

 struct blk_outhdr {
            /* VIRTIO_BLK_T* */
            u32 type;
            /* io priority. */
            u32 ioprio;
            /* Sector (ie. 512 byte offset) */
            u64 sector;
  };

告诉驱动是读还是写(type),优先级,扇区号。

第二条是输出缓冲区,即扇区读取到哪里,缓冲区大小。

第三条就1个字节,用来指示操作结果,0表示成功。

module/blk/virtio_blk.c

struct blk_req {
    struct blk_outhdr hdr;
    uchar status;
};
static struct blk_req rq;
static void virtio_blk_read(ulong sector)
{
    struct addr_size phys[3];
    rq.hdr.type = VIRTIO_BLK_T_IN;
    rq.hdr.ioprio = 0;
    rq.hdr.sector = sector;
    phys[0].vp_addr = V2P((ulong)&rq.hdr);
    phys[0].vp_size = sizeof(rq.hdr);
    phys[0].vp_flag = VRING_DESC_F_READ;
    phys[1].vp_addr = V2P((ulong)buf);
    phys[1].vp_size = 512;
    phys[1].vp_flag = VRING_DESC_F_WRITE;
    uchar status = 0;
    phys[2].vp_addr = V2P((ulong)&rq.status);
    phys[2].vp_size = sizeof(status);
    pyhs[2].vp_flag = VRING_DESC_F_WRITE;
    virtio_to_queue(to_virtio_dev_t(&pci_vblk), 0, phys, 3, &rq);

}

virtio_to_queue把3个描述符写入vring,然后kick通知宿主os。

libs/libvirtio/virtio.c

int
virtio_to_queue(virtio_dev_t dev, int q
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值