Qemu virtio-blk 后端驱动开发 - PureFlash对接

本文以PureFlash为例,介绍了如何将一个新的存储类型对接到qemu虚拟化平台下,为虚机提供存储能力。

关于virtio-blk以及其工作原理这里就不介绍了,网上有很多分析的文章。总之就是如果我们想给虚机提供一种新的存储类型(不同于标准的块设备,文件,或者iSCSI等linux内置接口 ),我们就需要为qemu增加新的virtio-blk后端驱动来达到这个目的。典型的比如ceph rbd驱动. 

说到virtio协议,其核心是通过virtio queue在guest-host系统之间进行交互,这方面的资料目前还是比较多的。不过这部分对于我们开发后端驱动而言其实不需要触碰,qemu已经定义了一套更易理解的接口,实现方按部就班实现这些API接口就可以了。

virtio-blk后端接口有两种模式,旧的接口使用线程模式,新的接口使用协程模式。这里的新旧是指qemu的演进过程。这里我们使用协程模式。

一、virioblk IO接口

virtio-blk接口的关键是在自己的代码中定义并填充struct BlockDriver 结构体,通过这个结构体向qemu传递自己这个后端实现的信息以及API实现。我们先对这个结构体里的几个关键字段做下说明:

BlockDriver字段解释
const char *format_name驱动名称
int instance_size实例化一个块设备/虚拟盘,这个设备对象的大小(不是盘的大小,是表示这个盘的内存结构体大小)
const char *protocol_name在qemu启动的命令行上,通过<protocol>:filename 指定一个后端设备后,通过这个名字qemu才能知道对于一个块设备调用哪个驱动。通常这个名字设置成和format_name相同
 int GRAPH_UNLOCKED_PTR (*bdrv_file_open)(
        BlockDriverState *bs, QDict *options, int flags, Error **errp);
用于为qemu打开一个块设备,也就是当qemu需要创建一个新的块设备时首先调用这个函数进行打开操作
    void (*bdrv_parse_filename)(const char *filename, QDict *options, Error **errp);

把filename 字符串解析成方便使用的参数字典,即QDict类型的options。

通常qemu会接收一个复杂的字符串作为文件名,比如 "pfbd:test_v1", 其中pfbd是protocol_name, 后半部分是volume name。这个函数将解析后的结果保存到options中。后面这个options被传递给brv_file_open函数使用。

    int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_preadv)(BlockDriverState *bs,
        int64_t offset, int64_t bytes, QEMUIOVector *qiov,
        BdrvRequestFlags flags);

读操作函数,实现这个函数以提供读操作。

BlockDriver接口里定义了多个读操作函数,实现方只需要实现其中的一个即可。 

    int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pwritev)(
        BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov,
        BdrvRequestFlags flags);
写操作函数,和上面的读操作函数类似原理。

BlockDriver接口里定义了多个读写操作函数, 比如 bdrv_aio_preadv, bdrv_co_readv, bdrv_co_preadv, bdrv_co_preadv_part 都可以用来实现读操作,实现方根据自己的情况实现任意一个函数就可以。

这样就明确了,实现virtio-blk后端驱动的重点就是实现一个read函数和一个write函数。本文以实现bdrv_co_preadv、bdrv_co_pwritev为例。正如名字里的co暗示的,这两个函数以协程的模式工作,协程模型也是qemu新版本力推。

相比业界一些专门的协程库(比如 Argobots,...),qemu里的协程是一个非常轻量化的协程实现,只有创建协程,进入协程,出让执行权这几个操作。其他的甚至基本的锁操作都没有。

二、IO代码路径

当虚拟机里的guest OS执行IO操作时,首先把IO请求放入到virtio-queue里面,然后执行一个kick动作。这个kick动作就是一个对PCIe寄存器的写入操作:

但是虚拟设备是没有PCIe硬件存在的,这个写入操作引发VM_EXIT,进入到KVM的代码,KVM之后通过eventfd通知qemu,触发qemu的主事件循环里处理:

其中blk_aio_prwv的代码如下,这里创建IO协程:

我们编写的IO接口函数就是在这个协程里面被调用的:

三、接口实现

前面的IO路径已经跟到了pfbd_co_pwritev函数,作为接口实现的工作就是提供实现这一函数。

PureFlash项目提供了用户态的接口库libs5common.a , 这里提供了volume的打开,读写等操作函数。这些函数都是用户态API,用于对接qemu, TCMU( LIO的用户态后端实现,提供iSCSI接口), nbd, ViveNAS等访问。这些函数中用于进行读写操作函数是pf_io_submit/pf_iov_submit。

/*** Submit IO vector.* @param volume, the volume to submit IO* @param iov, io vector* @param iov_cnt, iovec count in param iov* @param length, total length of iovs* @param offset, offset in volume, of this IO to perform in volume* @param callback, the call back handler function to call when IO complete_status* @param cbk_arg, user defined data to passed to callback when IO complete. PureFlash system will not touch this argument* @param is_write, indict this IO is WRITE or READ. 1 for WRITE IO; 0 for READ IO* @return 0 on success, negative number to indicate submit error.* @retval -EINVAL  argument invalidate, length too large or offset not aligned on sector(512B)* @retval -EAGAIN  device is temporary busy, caller can retry this IO later**/
int pf_iov_submit(struct PfClientVolume* volume, const struct iovec *iov, const unsigned int iov_cnt, size_t length, off_t offset,ulp_io_handler callback, void* cbk_arg, int is_write);

和大多数异步操作函数一样,pf_iov_submit的参数里包含了一个回调函数用于在IO完成时调用。但是Qemu的IO接口函数bdrv_co_preadv/bdrv_co_pwritev期望的行为是同步调用。因此接口实现时的一个重要功能就是实现异步/同步行为转换:

//这个函数在Qemu的IO线程中执行
static void pfbd_rw_cb_bh(void* opaque)
{PfbdCoData* data = opaque;data->complete=true;qemu_coroutine_enter(data->co); //唤醒处于等待中的IO 协程
}//这个回调函数在PureFlash s5common库的事件循环线程中调用
static void pfbd_rw_cb(void* opaque, int ret)
{PfbdCoData* data = opaque;data->ret = ret;//调度一个IO后半段操作(Behand half)用于唤醒协程aio_bh_schedule_oneshot(bdrv_get_aio_context(data->bds),pfbd_rw_cb_bh, data);
}static coroutine_fn int pfbd_co_preadv(BlockDriverState* bs,int64_t offset, int64_t bytes,QEMUIOVector* qiov, BdrvRequestFlags flags)
{int r;BDRVPfbdState* s = bs->opaque;PfbdCoData data = {.ret = -EINPROGRESS,.co= qemu_coroutine_self() ,.complete = false,.bds = bs,};// 调用PureFlash的API提交IO操作,传递异步操作回调函数 pfbd_rw_cb。// IO完成时 pfbd_rw_cb会被调用。可以看到这里不几乎不需要对其他参数做// 修改适配,因为虚拟机是PureFlash设计适用的重要场景,API的参数已经根// 据qemu接口做了尽可能适配r = pf_iov_submit(s->vol, qiov->iov, qiov->niov, bytes, offset, pfbd_rw_cb, &data, 0);if(r){error_report("submit IO error, rc:%d", r);return r;}//等待IO完成while (!data.complete) {qemu_coroutine_yield();  //出让协程CPU等待唤醒,因此这里不是忙等待}return data.ret;
}

通过上面的方法实现了IO路径,接下来要把驱动注册到qemu里


static const char *const qemu_pfbd_strong_runtime_opts[] = {"volume","conf","snapshot",NULL
};static BlockDriver bdrv_pfbd = {.format_name            = "pfbd",.instance_size          = sizeof(BDRVPfbdState),.bdrv_parse_filename    = qemu_pfbd_parse_filename,.bdrv_file_open         = qemu_pfbd_open,.bdrv_close             = qemu_pfbd_close,.bdrv_co_get_info       = qemu_pfbd_co_getinfo,.create_opts            = &qemu_pfbd_create_opts,.bdrv_co_getlength         = qemu_pfbd_co_getlength,.protocol_name          = "pfbd",.bdrv_co_preadv = pfbd_co_preadv,.bdrv_co_pwritev = pfbd_co_pwritev,.strong_runtime_opts    = qemu_pfbd_strong_runtime_opts,
};static void bdrv_pfbd_init(void)
{bdrv_register(&bdrv_pfbd);
}block_init(bdrv_pfbd_init);

三、使用方法

使用下面的命令在启动qemu时使用PureFlash 卷

qemu/build//qemu-system-x86_64  -enable-kvm -cpu host -smp 1 -drive if=ide,file=./centos8.qcow2,cache=none \-drive if=virtio,file=pfbd:test_v1,cache=none \-m 4G -vnc :12 \-nic user,hostfwd=tcp::5555-:22

这个命令里面为qemu挂载了test_v1这个卷作为数据盘。

在虚机运行之前需要启动PureFlash集群(单个容器也可以提供服务),并创建名字为test_v1的卷,这个操作可以参考PureFlash存储系统介绍与上手指南-CSDN博客

本文里完整的代码: block/pfbd.c · cocalele/qemu - 码云 - 开源中国 (gitee.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/49939.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【日常记录】【JS】对一个数组,按照某个字段的值,进行分组

文章目录 1. 前言2. lodash 的分组3. Object.groupBy()参考链接 1. 前言 在开发中&#xff0c;经常会遇到一组数据&#xff0c;要按照某个字段进行分组&#xff0c;这个时候会有很多种方法&#xff0c;可以使用 forEach、reduce、等其他方法 reduce 方法 function groupBy(arr…

LLama 405B 技术报告解读

LLama 405B 技术报告解读 果然传的消息都是真的&#xff0c;meta在24号凌晨发布了llama 3的405B版本&#xff0c;这次还是做一个技术报告解读。 值得一提的是&#xff0c;在技术报告的开头&#xff0c;meta特意强调了一个 Managing complexity&#xff0c;大意是管控复杂度。…

主题公园- 海豹主题式风格餐厅设计【AIGC应用】

业务背景&#xff1a;海洋馆针对细分客群增设一个打卡主题点位&#xff0c;以海豹主题式餐厅为打卡卖点&#xff0c;效果参见海豹主题式风格。 AIGC概念图制作平台&#xff1a;&#xff08;可灵&#xff09; https://klingai.kuaishou.com/ 关键词&#xff1a; 海豹主题餐厅…

Blender插入关键帧的位置报错

在操作过程中&#xff0c;有时候是误操作或者是做动画选择了活动插帧集&#xff0c;导致按i键插入关键帧一直报提示&#xff1a;插入关键帧的帧位置或者是其他的报错弹窗。 1、解决方法是&#xff1a;在时间线的抠像(插帧)选项里&#xff0c;将活动插帧集给清空 2、若是骨骼动画…

Ubuntu 修改源地址

注意事项&#xff1a;版本说明&#xff01;&#xff01;&#xff01; Ubuntu24.04的源地址配置文件发生改变。 不再使用以前的 sources.list 文件&#xff0c;该文件内容变成了一行注释&#xff1a; # Ubuntu sources have moved to /etc/apt/sources.list.d/ubuntu.sources…

操作系统面试知识点总结2

#来自ウルトラマンメビウス&#xff08;梦比优斯&#xff09; 1 进程与线程 1.1 进程的概念和特征 更好地描述和控制程序并发执行&#xff0c;实现操作系统的并发性和共享性。 进程控制块&#xff08;PCB&#xff09;&#xff1a;更好的描述进程的基本情况和运行状态&#xff…

Eclipse 搭建 C/C++ 开发环境以及eclipse的使用

一、下载、安装 MinGW 1、下载: 下载地址&#xff1a;MinGW - Minimalist GNU for Windows - Browse Files at SourceForge.net 点击“Download Latest Version”即可 下载完成后&#xff0c;得到一个名为 mingw-get-setup.exe 的安装文件。双击运行&#xff0c;安装即可。 …

大数据、区块链与人工智能

大数据、区块链与人工智能&#xff1a;技术融合与未来展望 摘要 本文旨在探讨大数据、区块链和人工智能这三个技术领域的基本概念、发展历程、应用场景及其相互之间的融合。文章首先分别介绍这三个技术的定义和特点&#xff0c;然后分析它们在不同行业中的实际应用&#xff0…

Llama 3.1发布,性能媲美最强闭源大模型

美国太平洋时间 7 月 23 日&#xff0c;Meta 公司发布了其最新的 AI 模型 Llama 3.1&#xff0c;这是一个里程碑时刻。Llama 3.1 的发布让我们看到了开源 LLM 有与闭源 LLM 一较高下的能力。 Meta 表示&#xff0c;“到目前为止&#xff0c;开源 LLM 在功能和性能方面大多落后于…

C++高性能通信:了解Iceoryx与零拷贝技术的实现与应用

文章目录 0. 引言1. Iceoryx使用到的零拷贝技术1.1 零拷贝技术概述1.2 零拷贝的优势1.3 Iceoryx零拷贝的实现1.4 信息轮询与信号触发 2. Iceoryx的核心概念3. Iceoryx使用示例3.1 发布者程序3.2 订阅者程序3.3 编译和运行3.4 压力测试脚本 4. 参考文章 0. 引言 Iceoryx是一个开…

星环科技携手东华软件推出一表通报送联合解决方案

随着国家金融监督管理总局“一表通”试点工作的持续推进&#xff0c;星环科技携手东华软件推出了基于星环科技分布式分析型数据库ArgoDB和大数据基础平台TDH的一表通报送联合解决方案&#xff0c;并已在多地实施落地中得到充分验证。 星环科技与东华软件作为战略合作伙伴&…

深度学习复盘与论文复现E

文章目录 一、项目复现的问题及其解决方案1、 Cannot find DGL C graphbolt library2、 “is“ with a literal. Did you mean ““?”3、运行SEG、SPG查看GATNet的网络结构4、关于LI-FPN项目找不到数据粒度不匹配问题5、关于LI-FPN项目num_samples为空6、解决路径问题7、 !ss…

Java | Leetcode Java题解之第273题整数转换英文表示

题目&#xff1a; 题解&#xff1a; class Solution {String[] singles {"", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine"};String[] t…

PHP基础语法(四)

一、字符串类型 1、字符串定义语法 1&#xff09;单引号字符串&#xff1a;在单引号内部&#xff0c;所有的字符都会按照字面意义解释&#xff0c;不会进行变量替换或转义处理&#xff0c;除了 \ 表示单引号本身。 $str1 Hello, World!;2&#xff09;双引号字符串&#xff…

数据库——单表查询

一、建立数据库mydb8_worker mysql> use mydb8_worker; 二、建立表 1.创建表 mysql> create table t_worker(department_id int(11) not null comment 部门号,-> worder_id int(11) primary key not null comment 职工号,-> worker_date date not null comment…

qt SQLite学习记录

1. 查看qt中数据库的驱动的类型的支持 QStringList drivers QSqlDatabase::drivers();//获取qt中所支持的数据库驱动类型foreach(QString driver,drivers){qDebug()<<driver;}2. Qt SQL 模块包含的主要类的功能介绍 Qt SQL 模块包含了一些主要的类&#xff0c;用于在 …

传输层协议——TCP

TCP协议 TCP全称为“传输控制协议”&#xff0c;要对数据的传输进行一个详细的控制。 特点 面向连接的可靠性字节流 TCP的协议段格式 源/目的端口&#xff1a;表示数据从哪个进程来&#xff0c;到哪个进程4位首部长度&#xff1a;表示该TCP头部有多少字节&#xff08;注意它…

大数据-46 Redis 持久化 RDB AOF 配置参数 混合模式 具体原理 触发方式 优点与缺点

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

SQL注入万字详解,基于sqli-labs(手注+sqlmap)

目录 一、什么是SQL 1.什么是SQL 2.SQL的作用 3.MySQL基础知识 4.SQL增、删、改语句 *5.SQL查询语句 二、什么是SQL注入 1.SQL注入原理&#xff1a; 2.SQL注入&#xff1a; 3.SQL注入危害&#xff1a; 4.SQL注入技术分类&#xff1a; 5.防御方法&#xff1a;使用参…

javaEE-01-tomcat

文章目录 javaWebTomcat启动 Tomcat 服务器测试服务器是否成功停止tomcat服务器修改服务器的端口号 Idea整合tomcat服务器 javaWeb 所有通过 Java 语言编写可以通过浏览器访问的程序的总称,是基于请求和响应来开发的。 请求: 客户端给服务器发送数据(Request)响应: 服务器给客…