前言
在做OSPP项目的过程中,导师问我要不顺便参加OpenEuler的开源实习,正好libvirt
有几个开源实习的任务,让我顺便一起做了。本来我就已经参加了这个活动,待在MindSpore社区,现在正好在OpenEuler社区也领一些任务做,涨涨积分。看了一下virt-sig组的任务之后,我选择先做libvirt 6.2.0支持vhostuser类型磁盘
这个任务。
任务分析
目前的libvirt 6.2.0
版本不支持vhostuser
类型的磁盘,而最新版已经支持该特性,因此该特性应该在更高的版本中支持的,所以我们需要定位其最初支持的那个版本。定位的方法很简单,就是查询官网上面的Release Changes
,地址在这里,通过搜索可以了解到,该特性最初在libvirt 7.1.0
版本中支持。
了解到该特性最初支持的版本之后,使用Git将仓库切换到v7.1.0
版本,搜索提交日志,将支持该特性相关的commits找出来,与当前v6.2.0
版本的代码进行对比,观察代码的变化,确定哪些是需要进行修改的,哪些是可以保留的。在我一页页翻着Git logs时,导师发了一个bugzilla
的链接过来,里面包含了支持vhostuser
类型磁盘相关的所有commits,使一筹莫展的我醍醐灌顶,根据链接中提到的commits分析,随后回合即可。
回合过程
流程简介
我的回合流程有以下几个步骤:
git cherry-pick
将相关commits合并到当前分支- 解决
cherry-pick
过程中产生的代码冲突,尚不清楚功能的新代码和新文件倾向于保留 - 尝试编译,遇到错误根据错误日志解决问题,直至编译成功
- 运行测试,根据测试结果寻找相应错误地方,并将错误解决
- 删去多余的代码,
cherry-pick
过程中不可避免的拉取了一些多余的代码,将其删去,保证代码精简
问题一:代码冲突
cherry-pick
过程中不可避免地会产生代码冲突,尤其是跨了一个大版本的库,因此我采取的策略如下:
- 对于
Makefile
冲突文件,均采取舍弃的策略。由于6.2.0
的构建系统为make
,而7.1.0
版本的构建系统更换为meson
,因此有关meson.build
的新增文件一律舍弃,并做好文件变动记录,手动在Makefile
中添加需要编译的文件。 - 对于代码发生移动的更改,均采取舍弃的策略。在
7.1.0
版本中,许多代码从原本的文件中抽离出来,使得代码和文件结构更加模块化,而在6.2.0
版本中,由于移动代码涉及到的变动非常大,包括:头文件、函数名、Makefile等均需要做出改动,因此只能采取舍弃的策略。 - 对于代码发生冲突的区域,分析代码的变动,在保证函数功能不发生变化的情况下合入更改。由于发生改动的函数可能被很多其他模块所使用,因此在合入更改的适合需要分析更改是否会影响到函数本身的功能,避免其他模块出现不可预测的错误。
将有关commits全部合入之后,检查发生变动的所有文件,检查是否存在明显的语法错误,检查完毕后便可以尝试手动编译。
问题二:编译错误
下面这张图很好地说明了我在编译过程中遇到的问题
错误日志提供的信息还是很详细的,按照日志定位到错误的地方,解决对应的问题就可以了。我在编译过程中遇到的问题主要是枚举类缺少相应枚举字段
以及switch分支缺少对应case
,将其补充上即可。同时需要注意的是,6.2.0
到7.1.0
中间支持了很多其他特性,这些特性在cherry-pick
的过程中同时写入到了枚举类中,但实际上并没有对应的代码支持这些特性,因此需要将这些“假支持”的特性给删掉。
解决完以上问题后,编译就能成功了,编译成功也意味着合入的代码在编译链接的过程中是没有错误了,剩余的工作就要测试验证功能是否正常工作。
问题三:枚举顺序引发的惨案
由于我没有Jenkins
之类的自动化测试环境,我只能手动地去跑一些跟本次任务相关的测试用例,待导师上班之后再问问他如何自动化运行所有测试。
因为在代码回合的过程中发现修改的测试数据都集中在qemucapabilitiesdata
这个文件夹中,因此跟本次任务相关的单元测试主要是build/tests/*cap*
相关的测试。运行一下测试就会发现报错,并提示运行一个更为详细的debug命令,运行该命令后会得到一个详细的错误报告,提示出现错误的地方。
从上图可以看到,预期输出应为vhost-user-blk
,而实际输出为object.qapified
,这个错误是由枚举类的顺序错误引起的,我将这两个字段顺序放反了,却并没有注意到。这个小错误卡浪费了我一堆时间,最终在对比7.1.0
版本中的文件历史记录时发现并想起这个问题。这也说明枚举类中的字段是按顺序编号的,顺序弄反之后输出的结果也是相反的。
最终,将顺序改正之后再编译一次,再次运行测试,就能全部通过了。
回合总结
回合的过程就是一个Code review
的过程,需要我们去了解每段代码的功能,解决产生的冲突,回合的过程中需要注意的点就是尽量不影响其他代码模块的功能。同时,做好Git commit的记录,操作的步骤切分的详细一点,在出现问题后回滚便会十分方便。
生成补丁
测试确认功能没问题后,就能够使用git format-patch
命令根据commit记录生成Patch补丁,再将补丁应用到libvirt-rpm
制品仓中,构建新版本的软件包,并最终测试功能。
Format-patch
在项目根目录下创建一个文件夹,用于存放生成的Patch文件1
2mkdir patches
git format-patch master -o patches
此时会生成很多补丁文件,由于旧的补丁文件在制品仓中已经存在,我们只要用自己的Commit制作的Patches即可。
构建软件包
将制品仓的补丁和自己制作的补丁都移动到~/rpmbuild/SOURCES/
中,同时编辑libvirt.spec
文件,将自己制作的补丁文件按照顺序写到补丁列表中。1
2
3
4
5
6
7
8
9
10Patch0141: 0142-src-add-missing-virstoragefile.h-includes.patch
Patch0142: 0143-virstoragefile-properly-include-virstoragefile.h-hea.patch
Patch0143: 0144-virstoragefile-change-virStorageSource-drv-to-void-p.patch
Patch0144: 0145-qemu_alias-introduce-qemuDomainGetVhostUserAlias-hel.patch
Patch0145: 0146-docs-introduces-new-vhostuser-disk-type.patch
Patch0146: 0147-conf-implement-support-for-vhostuser-disk.patch
Patch0147: 0148-qemu_capabilities-introduce-vhost-user-blk-capabilit.patch
Patch0148: 0149-qemu-implement-vhost-user-blk-support.patch
Patch0149: 0150-qemu-capabilities-Introduce-QEMU_CAPS_VIRTIO_BLK_SCS.patch
Patch0150: 0151-Set-QEMU_CAPS_VIRTIO_BLK_SCSI_DEFAULT_DISABLED-when-.patch
然后运行构建命令1
2cd ~/rpmbuild
rpmbuild -ba SPECS/libvirt.spec
构建完成后覆盖安装升级1
sudo rpm -ivh RPMS/x86_64/libvirt*
测试功能
由于SELinux的特性会使得vhost-user-blk
的Socket没有连接权限,因此我们需要先将SELinux关掉。1
sudo setenforce 0
接下来创建一个qcow2
硬盘文件,然后用qemu-storage-daemon
创建一个vhost-user-blk
类型的Socket。有几种不同类型的vhostuser
硬盘,具体如何创建Socket可以参考QEMU的文档。1
2
3
4
5
6qemu-img create -f qcow2 share-disk.qcow2 1G
qemu-storage-daemon \
--blockdev driver=file,node-name=share-file,filename=share-disk.qcow2 \
--blockdev driver=qcow2,node-name=share-disk,file=share-file \
--export type=vhost-user-blk,id=share-disk,addr.type=unix,addr.path=share-disk.sock,node-name=share-disk,writable=on
此时在目录下能够看到一个名为share-disk.sock
的Socket文件,虚拟机硬盘配置中就可以使用这个文件了。
接下来编写一个虚拟机配置文件,从欧拉的文档中复制一份下来即可,将其命名为openEuler.xml
。需要注意的是,vhost-user-blk
类型磁盘要求内存的类型是共享的,需要在XML中配置一下memoryBacking
。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56<domain type='qemu'>
<name>openEuler</name>
<memory unit='GiB'>2</memory>
<memoryBacking>
<source type='memfd'/>
<access mode='shared'/>
</memoryBacking>
<vcpu>2</vcpu>
<os>
<type arch='aarch64' machine='virt'>hvm</type>
<loader readonly='yes' type='pflash'>/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw</loader>
<nvram>/var/lib/libvirt/qemu/nvram/openEulerVM.fd</nvram>
</os>
<features>
<acpi/>
<gic version='3'/>
</features>
<cpu>
<topology sockets='1' cores='2' threads='1'/>
</cpu>
<iothreads>1</iothreads>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>restart</on_crash>
<devices>
<emulator>/usr/libexec/qemu-kvm</emulator>
<disk type='vhostuser' device='disk'>
<driver name='qemu' type='qcow2' queues="1"/>
<source type='unix' path='/root/vm/share-disk.sock'/>
<target dev='vda' bus='virtio'/>
<boot order='2'/>
</disk>
<disk type='file' device='cdrom'>
<driver name='qemu' type='raw'/>
<source file='/root/vm/openEuler-22.03-LTS-aarch64-dvd.iso'/>
<readonly/>
<target dev='sdb' bus='scsi'/>
<boot order='1'/>
</disk>
<interface type='bridge'>
<source bridge='br0'/>
<model type='virtio'/>
</interface>
<console type='pty'/>
<video>
<model type='virtio'/>
</video>
<controller type='scsi' index='0' model='virtio-scsi'/>
<controller type='usb' model='ehci'/>
<input type='tablet' bus='usb'/>
<input type='keyboard' bus='usb'/>
<graphics type='vnc' port='5900' listen='0.0.0.0' passwd='n8VfjbFK'/>
</devices>
<seclabel type='dynamic' model='dac' relabel='yes'/>
</domain>
定义并启动虚拟机,使用VNC连接虚拟机屏幕,观察虚拟机是否启动成功,硬盘是否正确地挂载,能够正确地读写文件。1
2virsh define openEuler.xml
virsh start openEuler