Briefly speaking, FEMU is a fast, accurate, scalable, and extensible NVMe SSD Emulator. Based upon QEMU/KVM, FEMU is exposed to Guest OS (Linux) as an NVMe block device (e.g. /dev/nvme0nX). It supports emulating different types of SSDs
# 邮件内容 Hi, Thank you again for your interest in FEMU! The tarball size is 1.4GB. After decompression, the VM image file size is ~14GB. Downloading and decompression might take a while depending on your Internet connection speed and CPU processing capability. Please wait patiently! Below are the steps to download, decompress, and verify the FEMU VM image. mkdir -p ~/images cd ~/images wget http://people.cs.uchicago.edu/~huaicheng/femu/femu-vm.tar.xz tar xJvf femu-vm.tar.xz After these steps, you will get two files: "u20s.qcow2" and "u20s.md5sum". You can verify the integrity of the VM image file by doing: md5sum u20s.qcow2 > tmp.md5sum diff tmp.md5sum u20s.md5sum If diff complains that the above two files differ, then the VM image file is corrupted. Please redo the above steps. The user account and guest OS of the VM: - username: femu - passwd : femu - Guest OS: Ubuntu 20.04.1 server, with kernel 5.4 If you think FEMU is useful to your project, we appreciate you could consider starring the FEMU github repo to support us, thanks! Best, Huaicheng
以root权限运行nvme list能看到zns ssd,则表示搭建环境成功
1 2 3 4
root@fvm /h/f/libnvme (master)# nvme list Node SN Model Namespace Usage Format FW Rev --------------------- -------------------- ---------------------------------------- --------- -------------------------- ---------------- -------- /dev/nvme0n1 vZNSSD0 FEMU ZNS-SSD Controller 1 4.29 GB / 4.29 GB 512 B + 0 B 1.0
可使用ssh连接虚拟机,端口号即为hostfwd定义的端口号(8080),vscode连接更方便
自己制作镜像经验 源码编译qemu
1 2 3 4 5 6 7
sudo apt-get install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison meson wget https://download.qemu.org/qemu-8.0.0.tar.xz tar xvJf qemu-8.0.0.tar.xz cd qemu-8.0.0 ./configure --enable-kvm --enable-debug --enable-vnc --enable-werror --target-list="x86_64-softmmu" --enable-sdl make make install
This is the libnvme development C library. libnvme provides type definitions for NVMe specification structures, enumerations, and bit fields, helper functions to construct, dispatch, and decode commands and payloads, and utilities to connect, scan, and manage nvme devices on a Linux system.
使用以下命令编译源文件
1
gcc zns_rw.c -o zns_rw -lnvme
执行
1 2
./zns_rw ./zns_rw: error while loading shared libraries: libnvme.so.1: cannot open shared object file: No such file or directory
使用ldd列出文件依赖的动态库
1 2 3 4 5
ldd zns_rw linux-vdso.so.1 (0x00007ffe90b75000) libnvme.so.1 => not found libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f396cb49000) /lib64/ld-linux-x86-64.so.2 (0x00007f396cd49000)
meson install -C .build ninja: Entering directory `.build' ninja: no work to do. Installing src/libnvme.so.1.4.0 to /usr/local/lib64 Installing src/libnvme-mi.so.1.4.0 to /usr/local/lib64 Installing /home/femu/libnvme/src/libnvme.h to /usr/local/include Installing /home/femu/libnvme/src/libnvme-mi.h to /usr/local/include Installing /home/femu/libnvme/src/nvme/api-types.h to /usr/local/include/nvme Installing /home/femu/libnvme/src/nvme/fabrics.h to /usr/local/include/nvme Installing /home/femu/libnvme/src/nvme/filters.h to /usr/local/include/nvme Installing /home/femu/libnvme/src/nvme/ioctl.h to /usr/local/include/nvme Installing /home/femu/libnvme/src/nvme/linux.h to /usr/local/include/nvme Installing /home/femu/libnvme/src/nvme/log.h to /usr/local/include/nvme Installing /home/femu/libnvme/src/nvme/nbft.h to /usr/local/include/nvme Installing /home/femu/libnvme/src/nvme/tree.h to /usr/local/include/nvme Installing /home/femu/libnvme/src/nvme/types.h to /usr/local/include/nvme Installing /home/femu/libnvme/src/nvme/util.h to /usr/local/include/nvme Installing /home/femu/libnvme/src/nvme/mi.h to /usr/local/include/nvme Installing /home/femu/libnvme/.build/meson-private/libnvme.pc to /usr/local/lib64/pkgconfig Installing /home/femu/libnvme/.build/meson-private/libnvme-mi.pc to /usr/local/lib64/pkgconfig
man gcc | grep "Wl" -Wl,option Pass option as an option to the linker. If option contains commas, it is split into multiple options at the commas. You can use this syntax to pass an argument to the option. For example, -Wl,-Map,output.map passes -Map output.map to the linker. When using the GNU linker, you can also get the same effect with -Wl,-Map=output.map.
ld --help | grep "rpath" Just link symbols (if directory, same as --rpath) -rpath PATH Set runtime shared library search path -rpath-link PATH Set link time shared library search path
# 编译输出 Compilation host CPU : x86_64 host endianness : little C compiler : cc -m64 -mcx16 Host C compiler : cc -m64 -mcx16 C++ compiler : c++ -m64 -mcx16 CFLAGS : -g -O0 CXXFLAGS : -g -O0
二:gdb运行程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
gdb --args x86_64-softmmu/qemu-system-x86_64 \ -name "FEMU-ZNSSD-VM" \ -enable-kvm \ -cpu host \ -smp 4 \ -m 4G \ -device virtio-scsi-pci,id=scsi0 \ -device scsi-hd,drive=hd0 \ -drive file=/root/images/u20s.qcow2,if=none,aio=native,cache=none,format=qcow2,id=hd0 \ -device femu,devsz_mb=4096,femu_mode=3 \ -net user,hostfwd=tcp::8080-:22 \ -net nic,model=virtio \ -nographic \ -qmp unix:./qmp-sock,server,nowait 2>&1 | teelog (gdb) handle SIGUSR1 nostop Signal Stop Print Pass to program Description SIGUSR1 No Yes Yes User defined signal 1
三: 调试演示 而后就是常规的gdb操作了,拿一个错误的程序来调试
1 2 3 4
(gdb) b zns.c:1110 Breakpoint 1 at 0x489f0b: file ../hw/femu/zns/zns.c, line 1110. (gdb) r # 开启虚拟机
// SPDX-License-Identifier: LGPL-2.1-or-later /** * This file is part of libnvme. * Copyright (c) 2020 Western Digital Corporation or its affiliates. * * Authors: Keith Busch <keith.busch@wdc.com> */
/** * Search out for ZNS type namespaces, and if found, report their properties. */ #include<stdio.h> #include<string.h> #include<stdbool.h> #include<stdlib.h> #include<libnvme.h> #include<inttypes.h> #include<errno.h> #define TEST_SIZE (4096) #define TEST_BLOCK (8)
fvm login: [Switching to Thread 0x7ffff5c2d700 (LWP 423223)]
Thread 9"qemu-system-x86" hit Breakpoint 1, zns_do_write (n=0x555557cae9d0, req=0x7ffedc36a3a0, append=true, wrz=false) at ../hw/femu/zns/zns.c:1110 1110printf("****************Append Failed***************\n"); (gdb) p/x status $1 = 0x4002 (gdb)
也可以在执行测试程序前再打断点 类似于以下操作步骤
1 2 3 4 5 6 7 8 9 10 11
Thread 1 "qemu-system-x86" received signal SIGINT, Interrupt. 0x00007ffff75c2a96 in __ppoll (fds=0x555556f074a0, nfds=20, timeout=<optimized out>, sigmask=0x0) at ../sysdeps/unix/sysv/linux/ppoll.c:44 44 ../sysdeps/unix/sysv/linux/ppoll.c: 没有那个文件或目录. (gdb) b zns_do_write Breakpoint 1 at 0x5555559ddcab: file ../hw/femu/zns/zns.c, line 1051. (gdb) c Continuing. [Switching to Thread 0x7ffff5c2d700 (LWP 426245)]
Thread 9 "qemu-system-x86" hit Breakpoint 1, zns_do_write (n=0x1, req=0x7ffedc0eb000, append=false, wrz=true) at ../hw/femu/zns/zns.c:1051 1051 {
// SPDX-License-Identifier: LGPL-2.1-or-later /** * This file is part of libnvme. * Copyright (c) 2020 Western Digital Corporation or its affiliates. * * Authors: Keith Busch <keith.busch@wdc.com> */
/** * Search out for ZNS type namespaces, and if found, report their properties. */ #include<stdio.h> #include<string.h> #include<stdbool.h> #include<stdlib.h> #include<libnvme.h> #include<inttypes.h> #include<errno.h>
四:解决问题的经历 1 刚开始的时候想要调试femu,就想使用6.081 qemu调试内核的方式,但也不知道符号表在哪,并且femu虚拟出来的是一个IO设备,总觉得和调试内核有点不一样,后面看到博客qemu侧 块设备调试记录(一),具体内容没怎么看,只是发现原来可以直接将qemu当作可执行程序来调试,故用同样的方法试了试femu,发现可以。 2 gdb运行的时候时不时有输出 “Program received signal SIGUSR1, User defined signal 1.” 打断运行,故执行“handle SIGUSR1 nostop”,使得用户定义中断不停止运行。 3 错误击中断点时用bt发现各个变量都优化掉了“optimized out”,故需要修改编译选项 看到执行femu-compile.sh的输出中有如下信息
1 2 3 4 5 6 7 8
Compilation host CPU : x86_64 host endianness : little C compiler : cc -m64 -mcx16 Host C compiler : cc -m64 -mcx16 C++ compiler : c++ -m64 -mcx16 CFLAGS : -g -O2 CXXFLAGS : -g -O2
加上 --enable-debug选项后 Compilation host CPU : x86_64 host endianness : little C compiler : cc -m64 -mcx16 Host C compiler : cc -m64 -mcx16 C++ compiler : c++ -m64 -mcx16 CFLAGS : -g -O0 CXXFLAGS : -g -O0
/** * blk_end_sync_rq - executes a completion event on a request * @rq: request to complete * @error: end I/O status of the request */ staticvoidblk_end_sync_rq(struct request *rq, blk_status_t error) { structcompletion *waiting = rq->end_io_data;
rq->end_io_data = NULL;
/* * complete last, if this is a stack request the process (and thus * the rq pointer) could be invalid right after this complete() */ complete(waiting); }