├── .gitignore ├── .gitmodules ├── README.md ├── start.sh └── work ├── output ├── test_completions.txt └── timing.txt ├── task1 ├── README.assets │ ├── 635faf3d2be54.png │ ├── 635fb81de8d1e.png │ ├── 635fb8418d0a4.png │ ├── 635fbdfe4ccc0.png │ ├── 635fbf2cdf5d0.png │ ├── 635fbfa8c7fc5.png │ ├── 635fc03468e02.png │ ├── 635fc08dc06a2.png │ ├── 635fc0d4199a7.png │ ├── 635fca0fd42fc.png │ ├── 635fca20aab4c.png │ ├── 635fca48491ea.png │ ├── 635fcab69ac93.png │ ├── 635fcb4d8c791.png │ ├── 635fcbf6077aa.png │ ├── 635fcc28b76dd.png │ ├── 635fcc67c97fa.png │ ├── 635fcdc0ccafa.png │ ├── 635fcf80a19cc.png │ ├── 6360886ec34de.png │ └── 63608a87877e4.png ├── README.md ├── README.pdf └── 计算机系统综合实验一.pptx ├── task2 ├── README.assets │ ├── 6368ba03d55a9.png │ ├── 6368bcd9d3d17.png │ ├── 636a53dbe0a1f.png │ └── 636a53efee093.png ├── README.md ├── README.pdf ├── hello_miracle │ ├── .gitignore │ ├── Makefile │ └── hello_miracle.c └── 计算机系统综合实验二.pptx ├── task3 ├── README.assets │ ├── 6371f9c5ae7ee.png │ └── image-20221115171945500.png ├── README.md ├── README.pdf ├── miracle_bdev │ ├── .gitignore │ ├── Makefile │ ├── miracle_bdev.c │ └── miracle_bdev.json └── 计算机系统综合实验三.pptx ├── task4 ├── README.assets │ ├── 637b29ae8c6c6.png │ └── 637b35a31bcee.png ├── README.md ├── README.pdf ├── miracle_blob │ ├── .gitignore │ ├── Makefile │ ├── miracle_bdev.json │ └── miracle_blob.c └── 计算机系统综合实验四.pptx ├── task5 ├── README.assets │ ├── 637d83787772d.png │ └── 637d83b9b3efa.png ├── README.md ├── README.pdf └── 计算机系统综合实验五.pptx └── task6 ├── README.assets ├── image-20221222194509364.png ├── image-20221222194635804.png ├── image-20221222194639115.png ├── image-20221222195029020.png ├── image-20221222195226455.png ├── image-20221222195243972.png ├── image-20221222201015837.png ├── image-20221222201134790.png ├── image-20221222201241336.png └── image-20221222202053431.png ├── README.md ├── README.pdf ├── bitcask ├── .gitignore ├── Makefile ├── bitcask-intro.pdf ├── bitcask.cpp ├── bitcask.h └── main.cpp └── 计算机系统综合实验六.pptx /.gitignore: -------------------------------------------------------------------------------- 1 | /env-resource/ 2 | /env/ 3 | /.vscode/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "work/spdk"] 2 | path = work/spdk 3 | url = git@github.com:spdk/spdk.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CS_Exp_ZNS 2 | 3 | > 文本来源:XJTU2022计算机系统综合实验 王老师PPT (侵删) 4 | 5 | ## 实验目的 6 | 7 | - 本实验涉及到计算机组成原理、操作系统、程序设计等多门课程知识,锻炼同学们对业界最新软硬件技术全栈开发能力和学习能力。 8 | - NVMe协议已成为最新SSD访问协议,ZNS SSD是最新SSD技术发展成果,NVMe最新协议已增加对ZNS SSD支持, ZNS SSD对于存储系统性能提升有重要意义。本实验通过使用QEMU模拟ZNS SSD设备,基于Intel SPDK框架,在应用中使用ZNS SSD作为底层存储并实现I/O访问,理解从应用层到物理存储设备之间数据读写原理。 9 | 10 | ## 背景知识 11 | 12 | ### NVMe 13 | 14 | - NVMe协议标准由NVM Express公司监管,这是一个由100多个组织组成的联盟,这些组织致力于开发更快的协议以提高非易失性存储的性能。该组织由一个13家公司组成的董事会领导,其中包括Cavium、Cisco、Dell EMC、Facebook、英特尔、Micron、Microsemi、微软、NetApp、三星、希捷、东芝内存和Western Digital。 15 | - NVMe是一种高性能、高度可扩展的存储协议,用于连接主机和内存子系统。NVMe是专门为闪存等非易失性存储设计,建立在高速PCIe通道上。 16 | - NVMe的本质是建立了多个计算机与存储设备的通路。NVMe在单个消息队列中支持64000个命令,最多支持65535个I/O队列。相比之下,SAS设备的队列深度通常在一个队列中最多支持256个命令,而SATA驱动器最多支持32个命令。类比来说,如果SATA是一条普通的小道,每次只能通过32辆车的话,那NVMe就是一条拥有65535条车道的高速公路,每条车道能通过64000辆车。 17 | 18 | ### QEMU 19 | 20 | - QEMU是一个通用的开源模拟器,由 Fabrice Bellard 编写。它可以独立模拟出整台计算机,包括 CPU,内存,IO 设备。 21 | - QEMU+KVM 则可以通过 KVM 模块提供的虚拟化技术,提高 CPU 和内存性能,并为虚拟机提供加速功能。 22 | - QEMU对最新硬件设备支持较好,最新版本可支持ZNS SSD。 23 | - QEMU可以模拟ARM、RISC-V等多种处理器架构。 24 | 25 | ### ZNS SSD 26 | 27 | - ZNS SSD是在Open Channel SSD基础上发展而来的,因此它继承了Open Channel SSD I/O分离、可预测性延迟等优势;另一方面,ZNS协议将NVMe 2.0中的一部分进行了标准化处理,简化了软件架构,化解了Open Channel过于灵活发散、不利于规范化、标准化的难题,使得企业更加易于根据自身场景需进行特定软件开发。 28 | - 相比普通SSD,新一代ZNS SSD具有三大优势 29 | - 采用顺序写入方式大幅缩减了整盘的写放大,提升了SSD使用寿命 30 | - NAND闪存颗粒控制机制对应用透明化,有效提升读写性能、降低了延迟 31 | - 普通SSD保存每TB数据需要提供1GB预留空间作为FTL(Flash Translation Layer)闪存转换层,这就好比每本字典需要留几页作为目录,ZNS SSD基于顺序写入技术降低了整盘的预留空间需求 32 | 33 | ### SPDK 34 | 35 | SPDK是一套存储开发套件,专门为专用设备(NVME)设计。全称是The Storage Performance Development Kit。SPDK提供了一系列的高性能、可扩展、用户态下面的工具和库。在SPDK中,存储设备的驱动代码运行在用户态,不会运行在内核态,避免了内核的上下文切换节省了大量的处理开销,节省下来的CPU时间片可以用于实际的数据处理,比如重复数据删除、压缩、加密。SPDK的原则是通过消除每一处额外的软件开销来提供最少的延迟和最高的效率。 36 | 37 | ## 相关资源 38 | 39 | [QEMU 7.1.0](https://download.qemu.org/qemu-7.1.0.tar.xz) 40 | 41 | [Ubuntu Server 22.04](https://mirror.linux-ia64.org/ubuntu-releases/22.04.1/ubuntu-22.04.1-live-server-amd64.iso) 42 | 43 | ## 实验细节 44 | 45 | [Miracle24's BLOG](https://miracle24.site/?s=%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%B3%BB%E7%BB%9F%E7%BB%BC%E5%90%88%E8%AE%BE%E8%AE%A1%E5%AE%9E%E9%AA%8C) 46 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | if [ $1 == ssd ]; then 4 | qemu-system-x86_64 --enable-kvm \ 5 | -nographic \ 6 | -name cs-exp-zns \ 7 | -m 8G \ 8 | -cpu host -smp 4 \ 9 | -hda ./env/ubuntu.qcow2 \ 10 | -net user,hostfwd=tcp:127.0.0.1:7777-:22 -net nic \ 11 | -drive file=./env/ssd.qcow2,id=nvm,if=none \ 12 | -device nvme,serial=deadbeef,drive=nvm \ 13 | -fsdev local,id=fsdev0,path=./work/,security_model=none \ 14 | -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=hostshare 15 | else 16 | qemu-system-x86_64 --enable-kvm \ 17 | -nographic \ 18 | -name cs-exp-zns \ 19 | -m 8G \ 20 | -cpu host -smp 4 \ 21 | -hda ./env/ubuntu.qcow2 \ 22 | -net user,hostfwd=tcp:127.0.0.1:7777-:22 -net nic \ 23 | -drive file=./env/znsssd.qcow2,id=mynvme,if=none \ 24 | -device nvme,serial=baz,id=nvme2 \ 25 | -device nvme-ns,id=ns2,drive=mynvme,nsid=2,logical_block_size=4096,physical_block_size=4096,zoned=true,zoned.zone_size=131072,zoned.zone_capacity=131072,zoned.max_open=0,zoned.max_active=0,bus=nvme2 \ 26 | -fsdev local,id=fsdev0,path=./work/,security_model=none \ 27 | -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=hostshare 28 | fi -------------------------------------------------------------------------------- /work/output/test_completions.txt: -------------------------------------------------------------------------------- 1 | top_level unittest_pci_event 2 | top_level unittest_include 3 | top_level unittest_bdev 4 | top_level unittest_blob_blobfs 5 | top_level unittest_event 6 | top_level unittest_ftl 7 | top_level unittest_accel 8 | top_level unittest_ioat 9 | top_level unittest_iscsi 10 | top_level unittest_json 11 | top_level unittest_rpc 12 | top_level unittest_notify 13 | top_level unittest_nvme 14 | top_level unittest_log 15 | top_level unittest_lvol 16 | top_level unittest_nvmf 17 | top_level unittest_scsi 18 | top_level unittest_sock 19 | top_level unittest_thread 20 | top_level unittest_util 21 | top_level unittest_vhost 22 | top_level unittest_dma 23 | top_level unittest_init 24 | top_level unittest_pci_event 25 | top_level unittest_include 26 | top_level unittest_bdev 27 | top_level unittest_blob_blobfs 28 | top_level unittest_event 29 | top_level unittest_ftl 30 | top_level unittest_accel 31 | top_level unittest_ioat 32 | top_level unittest_iscsi 33 | top_level unittest_json 34 | top_level unittest_rpc 35 | top_level unittest_notify 36 | top_level unittest_nvme 37 | top_level unittest_log 38 | top_level unittest_lvol 39 | top_level unittest_nvmf 40 | top_level unittest_scsi 41 | top_level unittest_sock 42 | top_level unittest_thread 43 | top_level unittest_util 44 | top_level unittest_vhost 45 | top_level unittest_dma 46 | top_level unittest_init 47 | -------------------------------------------------------------------------------- /work/output/timing.txt: -------------------------------------------------------------------------------- 1 | unittest_pci_event 0 2 | unittest_include 0 3 | unittest_bdev 2 4 | unittest_blob_blobfs 3 5 | unittest_event 0 6 | unittest_ftl 0 7 | unittest_accel 0 8 | unittest_ioat 0 9 | unittest_iscsi 1 10 | unittest_json 0 11 | unittest_rpc 0 12 | unittest_notify 0 13 | unittest_nvme 1 14 | unittest_log 0 15 | unittest_lvol 0 16 | unittest_nvmf 0 17 | unittest_scsi 1 18 | unittest_sock 0 19 | unittest_thread 0 20 | unittest_util 0 21 | unittest_vhost 0 22 | unittest_dma 0 23 | unittest_init 0 24 | unittest_pci_event 0 25 | unittest_include 0 26 | unittest_bdev 2 27 | unittest_blob_blobfs 4 28 | unittest_event 0 29 | unittest_ftl 1 30 | unittest_accel 0 31 | unittest_ioat 0 32 | unittest_iscsi 1 33 | unittest_json 0 34 | unittest_rpc 0 35 | unittest_notify 0 36 | unittest_nvme 1 37 | unittest_log 0 38 | unittest_lvol 0 39 | unittest_nvmf 1 40 | unittest_scsi 0 41 | unittest_sock 0 42 | unittest_thread 0 43 | unittest_util 0 44 | unittest_vhost 0 45 | unittest_dma 0 46 | unittest_init 0 47 | -------------------------------------------------------------------------------- /work/task1/README.assets/635faf3d2be54.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635faf3d2be54.png -------------------------------------------------------------------------------- /work/task1/README.assets/635fb81de8d1e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635fb81de8d1e.png -------------------------------------------------------------------------------- /work/task1/README.assets/635fb8418d0a4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635fb8418d0a4.png -------------------------------------------------------------------------------- /work/task1/README.assets/635fbdfe4ccc0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635fbdfe4ccc0.png -------------------------------------------------------------------------------- /work/task1/README.assets/635fbf2cdf5d0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635fbf2cdf5d0.png -------------------------------------------------------------------------------- /work/task1/README.assets/635fbfa8c7fc5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635fbfa8c7fc5.png -------------------------------------------------------------------------------- /work/task1/README.assets/635fc03468e02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635fc03468e02.png -------------------------------------------------------------------------------- /work/task1/README.assets/635fc08dc06a2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635fc08dc06a2.png -------------------------------------------------------------------------------- /work/task1/README.assets/635fc0d4199a7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635fc0d4199a7.png -------------------------------------------------------------------------------- /work/task1/README.assets/635fca0fd42fc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635fca0fd42fc.png -------------------------------------------------------------------------------- /work/task1/README.assets/635fca20aab4c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635fca20aab4c.png -------------------------------------------------------------------------------- /work/task1/README.assets/635fca48491ea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635fca48491ea.png -------------------------------------------------------------------------------- /work/task1/README.assets/635fcab69ac93.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635fcab69ac93.png -------------------------------------------------------------------------------- /work/task1/README.assets/635fcb4d8c791.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635fcb4d8c791.png -------------------------------------------------------------------------------- /work/task1/README.assets/635fcbf6077aa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635fcbf6077aa.png -------------------------------------------------------------------------------- /work/task1/README.assets/635fcc28b76dd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635fcc28b76dd.png -------------------------------------------------------------------------------- /work/task1/README.assets/635fcc67c97fa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635fcc67c97fa.png -------------------------------------------------------------------------------- /work/task1/README.assets/635fcdc0ccafa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635fcdc0ccafa.png -------------------------------------------------------------------------------- /work/task1/README.assets/635fcf80a19cc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/635fcf80a19cc.png -------------------------------------------------------------------------------- /work/task1/README.assets/6360886ec34de.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/6360886ec34de.png -------------------------------------------------------------------------------- /work/task1/README.assets/63608a87877e4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.assets/63608a87877e4.png -------------------------------------------------------------------------------- /work/task1/README.md: -------------------------------------------------------------------------------- 1 | # 实验一、QEMU基本环境搭建 2 | ## 实验目的 3 | 4 | 1. 掌握QEMU基本用法 5 | 2. 掌握ZNS SSD设备模拟方法 6 | 7 | ## 实验内容 8 | 9 | 1. 下载QEMU源代码并编译安装 10 | 2. 下载ubuntu 22.04镜像并在QEMU中安装 11 | 3. 在QEMU中模拟zns ssd 12 | 4. 启动QEMU的ubuntu操作系统,观察zns ssd是否安装成功 13 | 14 | ## 实验过程和步骤 15 | 16 | ### 相关文件 17 | 18 | - [qemu-7.1.0](https://download.qemu.org/qemu-7.1.0.tar.xz) 19 | - [Ubuntu Server 22.04](https://mirror.linux-ia64.org/ubuntu-releases/22.04.1/ubuntu-22.04.1-live-server-amd64.iso) 20 | 21 | ### 安装QEMU 22 | #### 安装依赖 23 | Required additional packages 24 | ```bash 25 | sudo apt install git libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev ninja-build 26 | ``` 27 | Recommended additional packages 28 | ```bash 29 | sudo apt install git-email 30 | sudo apt install libaio-dev libbluetooth-dev libcapstone-dev libbrlapi-dev libbz2-dev 31 | sudo apt install libcap-ng-dev libcurl4-gnutls-dev libgtk-3-dev 32 | sudo apt install libibverbs-dev libjpeg8-dev libncurses5-dev libnuma-dev 33 | sudo apt install librbd-dev librdmacm-dev 34 | sudo apt install libsasl2-dev libsdl2-dev libseccomp-dev libsnappy-dev libssh-dev 35 | sudo apt install libvde-dev libvdeplug-dev libvte-2.91-dev libxen-dev liblzo2-dev 36 | sudo apt install valgrind xfslibs-dev 37 | sudo apt install libnfs-dev libiscsi-dev 38 | ``` 39 | #### 编译安装QEMU 40 | ```bash 41 | wget https://download.qemu.org/qemu-7.1.0.tar.xz 42 | tar xvJf qemu-7.1.0.tar.xz 43 | cd qemu-7.1.0 44 | ./configure 45 | make 46 | sudo make install 47 | ``` 48 | #### 检验 49 | ```bash 50 | qemu-system-x86_64 --version 51 | ``` 52 | ![file](README.assets/635faf3d2be54.png) 53 | 54 | ### 安装Ubuntu 55 | #### 创建虚拟盘 56 | - -f qcow2:磁盘格式为qcow2 57 | - ubuntu.qcow2:磁盘名 58 | - 30G:磁盘大小 59 | 60 | ```bash 61 | qemu-img create -f qcow2 ubuntu.qcow2 30G 62 | ``` 63 | ![file](README.assets/635fb8418d0a4.png) 64 | 创建的虚拟磁盘文件默认在当前目录下,可以使用`ls`查看 65 | 66 | ![file](README.assets/635fbdfe4ccc0.png) 67 | 68 | 查看磁盘信息 69 | 70 | ```bash 71 | qemu-img info ubuntu.qcow2 72 | ``` 73 | ![file](README.assets/635fb81de8d1e.png) 74 | #### 设置并启动虚拟机 75 | - --enable-kvm:使用KVM虚拟化 76 | - -m 8G:8G虚拟内存 77 | - -smp 2:模拟的SMP架构中CPU的个数为2 78 | - -boot order=dc:引导顺序为 d(第一个光驱)->c(第一块硬盘) 79 | - -hda /path/to/file:指定虚拟盘文件路径为/path/to/file 80 | - -cdrom /path/to/iso:指定CD-ROM镜像文件的路径为/path/to/iso 81 | 82 | ```bash 83 | qemu-system-x86_64 --enable-kvm -m 8G -smp 2 -boot order=dc -hda ./env/ubuntu.qcow2 -cdrom ./env-resource/ubuntu-22.04.1-live-server-amd64.iso 84 | ``` 85 | 1. 选择Try or Install Ubuntu Server 86 | 87 | ![file](README.assets/635fbf2cdf5d0.png) 88 | 89 | 2. 语言选择English 90 | 91 | ![file](README.assets/635fbfa8c7fc5.png) 92 | 93 | 3. 不更新版本 94 | 95 | ![file](README.assets/635fc03468e02.png) 96 | 97 | 4. 键盘保持默认 98 | 99 | ![file](README.assets/635fc08dc06a2.png) 100 | 101 | 5. 选择完整安装 102 | 103 | ![file](README.assets/635fc0d4199a7.png) 104 | 105 | 6. 网络部分选择DHCP自动获取,之后会通过端口映射方式暴露虚拟机端口 106 | 107 | **QEMU网络相关参数** 108 | 109 | >QEMU命令使用“-net user”参数配置用户模式网络,命令格式如下: 110 | >`qemu-kvm -net nic -net user [, opion[, option[, ... ] ] ] myVM.img` 111 | >用户模式的参数选项描述如下: 112 | > 113 | >- vlan=vlan编号,将用户模式网络栈连接到编号为n的VLAN中(默认值为0)。 114 | >- name=名称,分配一个网络名称,可以用来在QEMU monitor中识别该网络。 115 | >- net=地址[/掩码],设置客户机所在子网,缺省值是10.0.2.0/24。 116 | >- host=地址,设置客户机看到的宿主机IP地址,缺省值为客户机所在网络的第2个IP地址10.0.2.2。 117 | >- restrict=开关,如果将此选项打开(y或yes),则客户机不能与宿主机通信,也不能通过宿主机路由到外部网络。缺省设置为n或no。 118 | >- hostname=名称,设置在宿主机DHCP服务器中保存的客户机主机名。 119 | >- dhcpstart=地址,设置能够分配给客户机的第一个IP,QEMU内嵌的DHCP服务器有16个IP地址可供分配,缺省地址范围是10.0.2.15-10.0.2.30。 120 | >- dns=地址,指定虚拟DNS的地址,其缺省值是网络中的第3个IP地址10.0.2.3,不能与“host= ”中指定的相同。 121 | >- hostfwd=[tcpludp] [宿主机地址]:宿主机端口- [客户机地址]:客户机端口,将访问宿主机指定端口的TCP/UDP连接重定向到客户机端口上。该选项可以在一个命令行中可以多次重复使用。 122 | 123 | ![file](README.assets/635fca0fd42fc.png) 124 | 125 | 7. 不使用代理,此处留空即可 126 | 127 | ![file](README.assets/635fca20aab4c.png) 128 | 129 | 8. 镜像源保持默认即可,也可以换成阿里、清华等其他源 130 | 131 | ![file](README.assets/635fca48491ea.png) 132 | 133 | 9. 没必要使用LVM逻辑卷,这里直接取消勾选 134 | 135 | ![file](README.assets/635fcab69ac93.png) 136 | 137 | 10. 确认分区没问题就下一步 138 | 139 | ![file](README.assets/635fcb4d8c791.png) 140 | 141 | 11. 设置主机名、用户名和密码 142 | 143 | ![file](README.assets/635fcbf6077aa.png) 144 | 145 | 12. 勾选SSH 146 | ![file](README.assets/635fcc28b76dd.png) 147 | 148 | 13. 其他的软件包用不着,直接下一步 149 | ![file](README.assets/635fcc67c97fa.png) 150 | 151 | 14. 等待安装完成后,点击左上角Machine选择Quit即可 152 | ![file](README.assets/635fcdc0ccafa.png) 153 | 154 | ### 再创建一块虚拟盘用于模拟NVMe ZNS SSD 155 | ```bash 156 | qemu-img create -f qcow2 znsssd.qcow2 10G 157 | ``` 158 | 此时目录下应该有两个虚拟磁盘文件,一个是前文安装Ubuntu Server的系统盘,一个是用于模拟ZNS的虚拟盘 159 | ![file](README.assets/635fcf80a19cc.png) 160 | 161 | ### 再次启动虚拟机并挂载两块硬盘 162 | > [QEMU NVMe模拟 官方文档](https://www.qemu.org/docs/master/system/devices/nvme.html) 163 | 164 | - -name:虚拟机名称 165 | - -m:虚拟内存 166 | - --enable-kvm:使用KVM虚拟化 167 | - -cpu host:CPU虚拟化模型 168 | - x86 base:base CPU model type with no features enabled 169 | - x86 host :processor with all supported host features 170 | - x86 max:Enables all features supported by the accelerator in the current host 171 | - -smp 4:虚拟CPU个数 172 | - -hda:系统盘路径 173 | - -net user, -net nic:网络设置 174 | - -drive:驱动 175 | - file:硬件映像文件路径 176 | - id:名称 177 | - format:格式 178 | - if:接口类型,即控制器类型,如ide、scsi、sd、mtd、floppy、pflash及virtio等 179 | - -device nvme:添加NVMe设备(仅支持一个单独的namespace id为1的namespace) 180 | - serial:序列号 181 | - id:设备ID 182 | - -device nvme-ns:为支持多个namespaces和额外特性,必须使用"nvme-ns"设备。 由"nvme-ns"设备定义的namespaces将连结到由"nvme"设备创建的"nvme-bus"。Namespace id自动分配,从1开始。 183 | - id:设备ID 184 | - drive:Node name or ID of a block device to use as a backend 185 | - nsid:namespace id 186 | - logical_block_size:逻辑块大小,512 B ~ 2 MiB (一般由1个或者多个物理块组合成一个逻辑块) 187 | - physical_block_size:物理块大小,512 B ~ 2 MiB 188 | - zoned:Zoned Namespace 189 | - zone_size:zone大小 190 | - zone_capacity:zone容量。若设置为0,zone容量等于zone大小 191 | - max_open:最大打开的资源数目,为0允许所有zone都打开 192 | - max_active:最大有效的资源数目,为0允许所有zone都有效 193 | - bus:将namespace关联到特定的nvme设备 194 | 195 | ```bash 196 | qemu-system-x86_64 -name cs-exp-zns -m 8G --enable-kvm -cpu host -smp 4 \ 197 | -hda ./env/ubuntu.qcow2 \ 198 | -net user,hostfwd=tcp:127.0.0.1:7777-:22,hostfwd=tcp:127.0.0.1:2222-:2000 -net nic \ 199 | -drive file=./env/znsssd.qcow2,id=mynvme,format=qcow2,if=none \ 200 | -device nvme,serial=baz,id=nvme2 \ 201 | -device nvme-ns,id=ns2,drive=mynvme,nsid=2,logical_block_size=4096,physical_block_size=4096,zoned=true,zoned.zone_size=131072,zoned.zone_capacity=131072,zoned.max_open=0,zoned.max_active=0,bus=nvme2 202 | ``` 203 | ### 查看NVMe设备 204 | 启动虚拟机后登陆,在dev下查看nvme设备是否存在 205 | 206 | ```bash 207 | ls /dev/ | grep nv 208 | ``` 209 | ![file](README.assets/6360886ec34de.png) 210 | 用nvme cli命令查看zns ssd设备信息 211 | 212 | ```bash 213 | sudo apt update 214 | sudo apt full-upgrade 215 | reboot 216 | ``` 217 | ```bash 218 | sudo apt install nvme-cli 219 | sudo nvme zns id-ns /dev/nvme0n1 -H 220 | ``` 221 | ![file](README.assets/63608a87877e4.png) 222 | 223 | ### 额外工作 224 | #### 目录共享 225 | 添加共享目录优点 226 | 227 | - [x] 便于保存LOG文件至宿主机 228 | - [x] 可以在宿主机直接编写代码 229 | - [x] 便于文件传输 230 | - [ ] ... 231 | 232 | 在启动参数中添加共享配置 233 | 234 | - path:主机目录路径 235 | - mount_tag:mount标签,后续在虚拟机里挂载会用到 236 | 237 | ```bash 238 | -fsdev local,id=fsdev0,path=./work/,security_model=none \ 239 | -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=hostshare 240 | ``` 241 | 启动虚拟机后挂载共享目录 242 | 243 | ```bash 244 | cd ~ 245 | mkdir work 246 | sudo mount hostshare -t 9p ./work 247 | ``` 248 | 这样当前目录下的更改就会实时同步到虚拟机和宿主机中了 249 | 250 | #### 保存启动配置 251 | `vim start.sh` 252 | 253 | ```shell 254 | #! /bin/bash 255 | cd ~/Documents/CS_Exp_ZNS 256 | qemu-system-x86_64 -name cs-exp-zns -m 8G --enable-kvm -cpu host -smp 4 \ 257 | -hda ./env/ubuntu.qcow2 \ 258 | -net user,hostfwd=tcp:127.0.0.1:7777-:22,hostfwd=tcp:127.0.0.1:2222-:2000 -net nic \ 259 | -drive file=./env/znsssd.qcow2,id=mynvme,format=qcow2,if=none \ 260 | -device nvme,serial=baz,id=nvme2 \ 261 | -device nvme-ns,id=ns2,drive=mynvme,nsid=2,logical_block_size=4096,physical_block_size=4096,zoned=true,zoned.zone_size=131072,zoned.zone_capacity=131072,zoned.max_open=0,zoned.max_active=0,bus=nvme2 \ 262 | -fsdev local,id=fsdev0,path=./work/,security_model=none \ 263 | -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=hostshare 264 | ``` 265 | `chmod +x ./start.sh` 266 | 之后使用`./start.sh`就可以快速启动虚拟机了 267 | 268 | ## 实验结论和心得体会 269 | 270 | 本次实验成功编译安装了QEMU并掌握了基于QEMU的NVMe ZNS SSD模拟方法。 271 | -------------------------------------------------------------------------------- /work/task1/README.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/README.pdf -------------------------------------------------------------------------------- /work/task1/计算机系统综合实验一.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task1/计算机系统综合实验一.pptx -------------------------------------------------------------------------------- /work/task2/README.assets/6368ba03d55a9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task2/README.assets/6368ba03d55a9.png -------------------------------------------------------------------------------- /work/task2/README.assets/6368bcd9d3d17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task2/README.assets/6368bcd9d3d17.png -------------------------------------------------------------------------------- /work/task2/README.assets/636a53dbe0a1f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task2/README.assets/636a53dbe0a1f.png -------------------------------------------------------------------------------- /work/task2/README.assets/636a53efee093.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task2/README.assets/636a53efee093.png -------------------------------------------------------------------------------- /work/task2/README.md: -------------------------------------------------------------------------------- 1 | # 实验二、SPDK安装和使用 2 | 3 | ## 实验目的 4 | - 本实验涉及到计算机组成原理、操作系统、程序设计等多门课程知识,锻炼同学们对业界最新软硬件技术全栈开发能力和学习能力 5 | - NVMe协议已成为最新SSD访问协议,ZNS SSD是最新SSD技术发展成果,NVMe最新协议已增加对ZNS SSD支持, ZNS SSD对于存储系统性能提升有重要意义。本实验通过使用QEMU模拟ZNS SSD设备,基于Intel SPDK框架,在应用中使用ZNS SSD作为底层存储并实现I/O访问,理解从应用层到物理存储设备之间数据读写原理 6 | 7 | ## 实验内容 8 | - 下载SPDK源代码并编译安装 9 | - 运行NVME hello world程序 10 | - 通过分析NVME hello world源码学习SPDK基本原理 11 | - 修改hello world,实现zns命令I/O读写 12 | 13 | ## 实验过程和步骤 14 | ### 下载SPDK源代码并编译安装 15 | 1. 下载源码 16 | ```bash 17 | git clone https://github.com/spdk/spdk 18 | cd spdk 19 | git submodule update --init 20 | ``` 21 | 2. 下载依赖 22 | ```bash 23 | sudo scripts/pkgdep.sh –all 24 | ``` 25 | > - The scripts/pkgdep.sh script will automatically install the bare minimum dependencies required to build SPDK. Use --help to see information on installing dependencies for optional components 26 | > - Option –all will install all dependencies needed by SPDK features 27 | 28 | 3. 编译 29 | ```bash 30 | ./configure 31 | make 32 | ``` 33 | 4. 单元测试 34 | ```bash 35 | ./test/unit/unittest.sh 36 | ``` 37 | > You will see several error messages when running the unit tests, but they are part of the test suite. The final message at the end of the script indicates success or failure 38 | > 39 | > ![file](./README.assets/6368ba03d55a9.png) 40 | 41 | 5. 安装 42 | ```bash 43 | sudo make install 44 | ``` 45 | 46 | ### 运行NVME hello world程序 47 | 48 | 1. 页分配与设备解绑 49 | ```bash 50 | sudo scripts/setup.sh 51 | ``` 52 | > - Before running an SPDK application, some hugepages must be allocated and any NVMe and I/OAT devices must be unbound from the native kernel drivers. SPDK includes a script to automate this process on both Linux and FreeBSD. This script should be run as root. It only needs to be run once on the system 53 | > - To rebind devices back to the kernel, you can run 54 | > ```bash 55 | > sudo scripts/setup.sh reset 56 | > ``` 57 | > - By default, the script allocates 2048MB of hugepages. To change this number, specify HUGEMEM (in MB) as follows: 58 | > ```bash 59 | > sudo HUGEMEM=4096 scripts/setup.sh 60 | > ``` 61 | > On Linux machines HUGEMEM will be rounded up to system-default huge page size boundary 62 | 63 | 2. 运行hello world程序 64 | ```bash 65 | sudo ./build/examples/hello_world 66 | ``` 67 | ![file](./README.assets/6368bcd9d3d17.png) 68 | 69 | ### 通过分析NVME hello world源码学习SPDK基本原理 70 | 在 `spdk/examples/nvme/hello_world/hello_world.c` 下查看hello world源码并对关键部分进行分析 71 | 72 | 1. `main()` 函数处理流程 73 | 74 | ```c 75 | int main(int argc, char **argv) 76 | { 77 | int rc; 78 | struct spdk_env_opts opts; 79 | 80 | /* 81 | * SPDK relies on an abstraction around the local environment 82 | * named env that handles memory allocation and PCI device operations. 83 | * This library must be initialized first. 84 | * 85 | */ 86 | spdk_env_opts_init(&opts); 87 | rc = parse_args(argc, argv, &opts); 88 | if (rc != 0) { 89 | return rc; 90 | } 91 | 92 | opts.name = "hello_world"; 93 | if (spdk_env_init(&opts) < 0) { 94 | fprintf(stderr, "Unable to initialize SPDK env\n"); 95 | return 1; 96 | } 97 | 98 | printf("Initializing NVMe Controllers\n"); 99 | 100 | if (g_vmd && spdk_vmd_init()) { 101 | fprintf(stderr, "Failed to initialize VMD." 102 | " Some NVMe devices can be unavailable.\n"); 103 | } 104 | 105 | /* 106 | * Start the SPDK NVMe enumeration process. probe_cb will be called 107 | * for each NVMe controller found, giving our application a choice on 108 | * whether to attach to each controller. attach_cb will then be 109 | * called for each controller after the SPDK NVMe driver has completed 110 | * initializing the controller we chose to attach. 111 | */ 112 | rc = spdk_nvme_probe(&g_trid, NULL, probe_cb, attach_cb, NULL); 113 | if (rc != 0) { 114 | fprintf(stderr, "spdk_nvme_probe() failed\n"); 115 | rc = 1; 116 | goto exit; 117 | } 118 | 119 | if (TAILQ_EMPTY(&g_controllers)) { 120 | fprintf(stderr, "no NVMe controllers found\n"); 121 | rc = 1; 122 | goto exit; 123 | } 124 | 125 | printf("Initialization complete.\n"); 126 | hello_world(); 127 | cleanup(); 128 | if (g_vmd) { 129 | spdk_vmd_fini(); 130 | } 131 | 132 | exit: 133 | cleanup(); 134 | spdk_env_fini(); 135 | return rc; 136 | } 137 | 138 | ``` 139 | 140 | 可以看到 `main()` 函数的主要流程为 141 | ```mermaid 142 | graph TB 143 | 1["spdk_env_opts_init()"] --> 2["spdk_env_init()"] --> 3["spdk_nvme_probe()"] --> 4["hello_world()"] --> 5["cleanup()"] 144 | ``` 145 | 其中: 146 | - `spdk_env_opts_init()` 初始化opts参数 147 | - `spdk_env_init()` 初始化环境 148 | - `spdk_nvme_probe()` 加载NVMe设备:通过transport ID枚举总线,并将用户空间NVMe驱动程序附加到发现的每个设备上,具体参数如下 149 | > `int spdk_nvme_probe(const struct spdk_nvme_transport_id *trid, void *cb_ctx, spdk_nvme_probe_cb probe_cb, spdk_nvme_attach_cb attach_cb, spdk_nvme_remove_cb remove_cb)` 150 | > - trid – The transport ID indicating which bus to enumerate. If the trtype is PCIe or trid is NULL, this will scan the local PCIe bus. If the trtype is RDMA, the traddr and trsvcid must point at the location of an NVMe-oF discovery service. 151 | > - cb_ctx – Opaque value which will be passed back in cb_ctx parameter of the callbacks. 152 | > - probe_cb – will be called once per NVMe device found in the system. 153 | > - attach_cb – will be called for devices for which probe_cb returned true once that NVMe controller has been attached to the userspace driver. 154 | > - remove_cb – will be called for devices that were attached in a previous spdk_nvme_probe() call but are no longer attached to the system. Optional; specify NULL if removal notices are not desired. 155 | > 156 | > 返回: 0 on success, -1 on failure. 157 | - `hello_world()` 进行读写操作 158 | - `cleanup()` 释放NVMe设备 159 | 160 | 接着对 `hello_world()` 函数进行分析 161 | ```c 162 | static void hello_world(void) 163 | { 164 | struct ns_entry *ns_entry; 165 | struct hello_world_sequence sequence; 166 | int rc; 167 | size_t sz; 168 | 169 | TAILQ_FOREACH(ns_entry, &g_namespaces, link) { 170 | /* 171 | * Allocate an I/O qpair that we can use to submit read/write requests 172 | * to namespaces on the controller. NVMe controllers typically support 173 | * many qpairs per controller. Any I/O qpair allocated for a controller 174 | * can submit I/O to any namespace on that controller. 175 | * 176 | * The SPDK NVMe driver provides no synchronization for qpair accesses - 177 | * the application must ensure only a single thread submits I/O to a 178 | * qpair, and that same thread must also check for completions on that 179 | * qpair. This enables extremely efficient I/O processing by making all 180 | * I/O operations completely lockless. 181 | */ 182 | ns_entry->qpair = spdk_nvme_ctrlr_alloc_io_qpair(ns_entry->ctrlr, NULL, 0); 183 | if (ns_entry->qpair == NULL) { 184 | printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n"); 185 | return; 186 | } 187 | 188 | /* 189 | * Use spdk_dma_zmalloc to allocate a 4KB zeroed buffer. This memory 190 | * will be pinned, which is required for data buffers used for SPDK NVMe 191 | * I/O operations. 192 | */ 193 | sequence.using_cmb_io = 1; 194 | sequence.buf = spdk_nvme_ctrlr_map_cmb(ns_entry->ctrlr, &sz); 195 | if (sequence.buf == NULL || sz < 0x1000) { 196 | sequence.using_cmb_io = 0; 197 | sequence.buf = spdk_zmalloc(0x1000, 0x1000, NULL, SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA); 198 | } 199 | if (sequence.buf == NULL) { 200 | printf("ERROR: write buffer allocation failed\n"); 201 | return; 202 | } 203 | if (sequence.using_cmb_io) { 204 | printf("INFO: using controller memory buffer for IO\n"); 205 | } else { 206 | printf("INFO: using host memory buffer for IO\n"); 207 | } 208 | sequence.is_completed = 0; 209 | sequence.ns_entry = ns_entry; 210 | 211 | /* 212 | * If the namespace is a Zoned Namespace, rather than a regular 213 | * NVM namespace, we need to reset the first zone, before we 214 | * write to it. This not needed for regular NVM namespaces. 215 | */ 216 | if (spdk_nvme_ns_get_csi(ns_entry->ns) == SPDK_NVME_CSI_ZNS) { 217 | reset_zone_and_wait_for_completion(&sequence); 218 | } 219 | 220 | /* 221 | * Print "Hello world!" to sequence.buf. We will write this data to LBA 222 | * 0 on the namespace, and then later read it back into a separate buffer 223 | * to demonstrate the full I/O path. 224 | */ 225 | snprintf(sequence.buf, 0x1000, "%s", "Hello world!\n"); 226 | 227 | /* 228 | * Write the data buffer to LBA 0 of this namespace. "write_complete" and 229 | * "&sequence" are specified as the completion callback function and 230 | * argument respectively. write_complete() will be called with the 231 | * value of &sequence as a parameter when the write I/O is completed. 232 | * This allows users to potentially specify different completion 233 | * callback routines for each I/O, as well as pass a unique handle 234 | * as an argument so the application knows which I/O has completed. 235 | * 236 | * Note that the SPDK NVMe driver will only check for completions 237 | * when the application calls spdk_nvme_qpair_process_completions(). 238 | * It is the responsibility of the application to trigger the polling 239 | * process. 240 | */ 241 | rc = spdk_nvme_ns_cmd_write(ns_entry->ns, ns_entry->qpair, sequence.buf, 242 | 0, /* LBA start */ 243 | 1, /* number of LBAs */ 244 | write_complete, &sequence, 0); 245 | if (rc != 0) { 246 | fprintf(stderr, "starting write I/O failed\n"); 247 | exit(1); 248 | } 249 | 250 | /* 251 | * Poll for completions. 0 here means process all available completions. 252 | * In certain usage models, the caller may specify a positive integer 253 | * instead of 0 to signify the maximum number of completions it should 254 | * process. This function will never block - if there are no 255 | * completions pending on the specified qpair, it will return immediately. 256 | * 257 | * When the write I/O completes, write_complete() will submit a new I/O 258 | * to read LBA 0 into a separate buffer, specifying read_complete() as its 259 | * completion routine. When the read I/O completes, read_complete() will 260 | * print the buffer contents and set sequence.is_completed = 1. That will 261 | * break this loop and then exit the program. 262 | */ 263 | while (!sequence.is_completed) { 264 | spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); 265 | } 266 | 267 | /* 268 | * Free the I/O qpair. This typically is done when an application exits. 269 | * But SPDK does support freeing and then reallocating qpairs during 270 | * operation. It is the responsibility of the caller to ensure all 271 | * pending I/O are completed before trying to free the qpair. 272 | */ 273 | spdk_nvme_ctrlr_free_io_qpair(ns_entry->qpair); 274 | } 275 | } 276 | ``` 277 | 可以看到 `hello_world()` 函数的主要流程为 278 | ```mermaid 279 | graph TB 280 | 1["spdk_nvme_ctrlr_alloc_io_qpair()"] --> 2["spdk_zmalloc()"] --> 3["snprintf(sequence.buf, ...)"] --> 4["spdk_nvme_ns_cmd_write()"] --> 5["spdk_nvme_qpair_process_completions()"] --> 6["spdk_nvme_ctrlr_free_io_qpair"] 281 | ``` 282 | 其中: 283 | - `spdk_nvme_ctrlr_alloc_io_qpair()` 为控制器分配I/O qpair 284 | - `spdk_zmalloc()` 分配buffer 285 | - `snprintf(sequence.buf, ...)` 写入数据到buffer 286 | - `spdk_nvme_ns_cmd_write()` 从buffer写入namespace的LBA 0处 287 | - `spdk_nvme_qpair_process_completions()` 处理I/O Completions 288 | - `spdk_nvme_ctrlr_free_io_qpair` 释放I/O qpair 289 | 290 | 在本程序中,当写 I/O 完成时, `write_complete()` 中调用了 `spdk_nvme_ns_cmd_read()` 进行读取操作。 当读取 I/O 完成时,`read_complete()` 将打印缓冲区内容并设置 `sequence.is_completed = 1` 291 | 292 | `write_complete()` 函数 293 | ```c 294 | static void write_complete(void *arg, const struct spdk_nvme_cpl *completion) 295 | { 296 | struct hello_world_sequence *sequence = arg; 297 | struct ns_entry *ns_entry = sequence->ns_entry; 298 | int rc; 299 | 300 | /* See if an error occurred. If so, display information 301 | * about it, and set completion value so that I/O 302 | * caller is aware that an error occurred. 303 | */ 304 | if (spdk_nvme_cpl_is_error(completion)) { 305 | spdk_nvme_qpair_print_completion(sequence->ns_entry->qpair, (struct spdk_nvme_cpl *)completion); 306 | fprintf(stderr, "I/O error status: %s\n", spdk_nvme_cpl_get_status_string(&completion->status)); 307 | fprintf(stderr, "Write I/O failed, aborting run\n"); 308 | sequence->is_completed = 2; 309 | exit(1); 310 | } 311 | /* 312 | * The write I/O has completed. Free the buffer associated with 313 | * the write I/O and allocate a new zeroed buffer for reading 314 | * the data back from the NVMe namespace. 315 | */ 316 | if (sequence->using_cmb_io) { 317 | spdk_nvme_ctrlr_unmap_cmb(ns_entry->ctrlr); 318 | } else { 319 | spdk_free(sequence->buf); 320 | } 321 | sequence->buf = spdk_zmalloc(0x1000, 0x1000, NULL, SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA); 322 | 323 | rc = spdk_nvme_ns_cmd_read(ns_entry->ns, ns_entry->qpair, sequence->buf, 324 | 0, /* LBA start */ 325 | 1, /* number of LBAs */ 326 | read_complete, (void *)sequence, 0); 327 | if (rc != 0) { 328 | fprintf(stderr, "starting read I/O failed\n"); 329 | exit(1); 330 | } 331 | } 332 | ``` 333 | `write_complete()` 流程 334 | ```mermaid 335 | graph TB 336 | 1["spdk_free()"] --> 2["spdk_zmalloc()"] --> 3["spdk_nvme_ns_cmd_read()"] 337 | ``` 338 | 其中: 339 | - `spdk_free()` 释放write时分配的buffer 340 | - `spdk_zmalloc()` 为read分配buffer 341 | - `spdk_nvme_ns_cmd_read()` 将namespace的LBA 0读入到buffer中 342 | 343 | `read_complete()` 函数 344 | ```c 345 | static void read_complete(void *arg, const struct spdk_nvme_cpl *completion) 346 | { 347 | struct hello_world_sequence *sequence = arg; 348 | 349 | /* Assume the I/O was successful */ 350 | sequence->is_completed = 1; 351 | /* See if an error occurred. If so, display information 352 | * about it, and set completion value so that I/O 353 | * caller is aware that an error occurred. 354 | */ 355 | if (spdk_nvme_cpl_is_error(completion)) { 356 | spdk_nvme_qpair_print_completion(sequence->ns_entry->qpair, (struct spdk_nvme_cpl *)completion); 357 | fprintf(stderr, "I/O error status: %s\n", spdk_nvme_cpl_get_status_string(&completion->status)); 358 | fprintf(stderr, "Read I/O failed, aborting run\n"); 359 | sequence->is_completed = 2; 360 | exit(1); 361 | } 362 | 363 | /* 364 | * The read I/O has completed. Print the contents of the 365 | * buffer, free the buffer, then mark the sequence as 366 | * completed. This will trigger the hello_world() function 367 | * to exit its polling loop. 368 | */ 369 | printf("%s", sequence->buf); 370 | spdk_free(sequence->buf); 371 | } 372 | ``` 373 | `read_complete()` 流程 374 | ```mermaid 375 | graph TB 376 | 1["sequence->is_completed = 1"] --> 2["printf(sequence->buf)"] --> 3["spdk_free()"] 377 | ``` 378 | 其中: 379 | - `sequence->is_completed = 1` 修改完成标识,使hello_world中while退出 380 | - `printf("%s", sequence->buf)` 将buffer中数据输出 381 | - `spdk_free()` 释放read的buffer 382 | 383 | ### 修改hello world,实现zns命令I/O读写 384 | 385 | #### hello_miracle源代码 386 | 387 | ```c 388 | #include "spdk/stdinc.h" 389 | #include "spdk/nvme.h" 390 | #include "spdk/vmd.h" 391 | #include "spdk/nvme_zns.h" 392 | #include "spdk/env.h" 393 | #include "spdk/string.h" 394 | #include "spdk/log.h" 395 | 396 | struct ctrlr_entry 397 | { 398 | struct spdk_nvme_ctrlr *ctrlr; 399 | TAILQ_ENTRY(ctrlr_entry) link; 400 | char name[1024]; 401 | }; 402 | 403 | struct ns_entry 404 | { 405 | struct spdk_nvme_ctrlr *ctrlr; 406 | struct spdk_nvme_ns *ns; 407 | TAILQ_ENTRY(ns_entry) link; 408 | struct spdk_nvme_qpair *qpair; 409 | }; 410 | 411 | struct my_sequence 412 | { 413 | struct ns_entry *ns_entry; 414 | char *buf; 415 | unsigned using_cmb_io; 416 | int is_completed; 417 | }; 418 | 419 | static TAILQ_HEAD(, ctrlr_entry) g_controllers = TAILQ_HEAD_INITIALIZER(g_controllers); 420 | static TAILQ_HEAD(, ns_entry) g_namespaces = TAILQ_HEAD_INITIALIZER(g_namespaces); 421 | static struct spdk_nvme_transport_id g_trid = {}; 422 | 423 | static bool g_vmd = false; 424 | 425 | static void register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns) 426 | { 427 | struct ns_entry *entry; 428 | 429 | if (!spdk_nvme_ns_is_active(ns)) 430 | { 431 | return; 432 | } 433 | 434 | entry = malloc(sizeof(struct ns_entry)); 435 | if (entry == NULL) 436 | { 437 | perror("ns_entry malloc"); 438 | exit(1); 439 | } 440 | 441 | entry->ctrlr = ctrlr; 442 | entry->ns = ns; 443 | TAILQ_INSERT_TAIL(&g_namespaces, entry, link); 444 | 445 | printf(" Namespace ID: %d size: %juGB\n", spdk_nvme_ns_get_id(ns), 446 | spdk_nvme_ns_get_size(ns) / 1000000000); 447 | } 448 | 449 | static bool probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, struct spdk_nvme_ctrlr_opts *opts) 450 | { 451 | printf("Attaching to %s\n", trid->traddr); 452 | return true; 453 | } 454 | 455 | static void attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts) 456 | { 457 | int nsid; 458 | struct ctrlr_entry *entry; 459 | struct spdk_nvme_ns *ns; 460 | const struct spdk_nvme_ctrlr_data *cdata; 461 | 462 | entry = malloc(sizeof(struct ctrlr_entry)); 463 | if (entry == NULL) 464 | { 465 | perror("ctrlr_entry malloc"); 466 | exit(1); 467 | } 468 | 469 | printf("Attached to %s\n", trid->traddr); 470 | 471 | cdata = spdk_nvme_ctrlr_get_data(ctrlr); 472 | 473 | snprintf(entry->name, sizeof(entry->name), "%-20.20s (%-20.20s)", cdata->mn, cdata->sn); 474 | 475 | entry->ctrlr = ctrlr; 476 | TAILQ_INSERT_TAIL(&g_controllers, entry, link); 477 | 478 | for (nsid = spdk_nvme_ctrlr_get_first_active_ns(ctrlr); nsid != 0; 479 | nsid = spdk_nvme_ctrlr_get_next_active_ns(ctrlr, nsid)) 480 | { 481 | ns = spdk_nvme_ctrlr_get_ns(ctrlr, nsid); 482 | if (ns == NULL) 483 | { 484 | continue; 485 | } 486 | register_ns(ctrlr, ns); 487 | } 488 | } 489 | 490 | static void cleanup(void) 491 | { 492 | struct ns_entry *ns_entry, *tmp_ns_entry; 493 | struct ctrlr_entry *ctrlr_entry, *tmp_ctrlr_entry; 494 | struct spdk_nvme_detach_ctx *detach_ctx = NULL; 495 | 496 | TAILQ_FOREACH_SAFE(ns_entry, &g_namespaces, link, tmp_ns_entry) 497 | { 498 | TAILQ_REMOVE(&g_namespaces, ns_entry, link); 499 | free(ns_entry); 500 | } 501 | 502 | TAILQ_FOREACH_SAFE(ctrlr_entry, &g_controllers, link, tmp_ctrlr_entry) 503 | { 504 | TAILQ_REMOVE(&g_controllers, ctrlr_entry, link); 505 | spdk_nvme_detach_async(ctrlr_entry->ctrlr, &detach_ctx); 506 | free(ctrlr_entry); 507 | } 508 | 509 | if (detach_ctx) 510 | { 511 | spdk_nvme_detach_poll(detach_ctx); 512 | } 513 | } 514 | 515 | static void usage(const char *program_name) 516 | { 517 | printf("%s [options]", program_name); 518 | printf("\t\n"); 519 | printf("options:\n"); 520 | printf("\t[-d DPDK huge memory size in MB]\n"); 521 | printf("\t[-g use single file descriptor for DPDK memory segments]\n"); 522 | printf("\t[-i shared memory group ID]\n"); 523 | printf("\t[-r remote NVMe over Fabrics target address]\n"); 524 | printf("\t[-V enumerate VMD]\n"); 525 | #ifdef DEBUG 526 | printf("\t[-L enable debug logging]\n"); 527 | #else 528 | printf("\t[-L enable debug logging (flag disabled, must reconfigure with --enable-debug)]\n"); 529 | #endif 530 | } 531 | 532 | static int parse_args(int argc, char **argv, struct spdk_env_opts *env_opts) 533 | { 534 | int op, rc; 535 | 536 | spdk_nvme_trid_populate_transport(&g_trid, SPDK_NVME_TRANSPORT_PCIE); 537 | snprintf(g_trid.subnqn, sizeof(g_trid.subnqn), "%s", SPDK_NVMF_DISCOVERY_NQN); 538 | 539 | while ((op = getopt(argc, argv, "d:gi:r:L:V")) != -1) 540 | { 541 | switch (op) 542 | { 543 | case 'V': 544 | g_vmd = true; 545 | break; 546 | case 'i': 547 | env_opts->shm_id = spdk_strtol(optarg, 10); 548 | if (env_opts->shm_id < 0) 549 | { 550 | fprintf(stderr, "Invalid shared memory ID\n"); 551 | return env_opts->shm_id; 552 | } 553 | break; 554 | case 'g': 555 | env_opts->hugepage_single_segments = true; 556 | break; 557 | case 'r': 558 | if (spdk_nvme_transport_id_parse(&g_trid, optarg) != 0) 559 | { 560 | fprintf(stderr, "Error parsing transport address\n"); 561 | return 1; 562 | } 563 | break; 564 | case 'd': 565 | env_opts->mem_size = spdk_strtol(optarg, 10); 566 | if (env_opts->mem_size < 0) 567 | { 568 | fprintf(stderr, "Invalid DPDK memory size\n"); 569 | return env_opts->mem_size; 570 | } 571 | break; 572 | case 'L': 573 | rc = spdk_log_set_flag(optarg); 574 | if (rc < 0) 575 | { 576 | fprintf(stderr, "unknown flag\n"); 577 | usage(argv[0]); 578 | exit(EXIT_FAILURE); 579 | } 580 | #ifdef DEBUG 581 | spdk_log_set_print_level(SPDK_LOG_DEBUG); 582 | #endif 583 | break; 584 | default: 585 | usage(argv[0]); 586 | return 1; 587 | } 588 | } 589 | 590 | return 0; 591 | } 592 | 593 | static void reset_zone_complete(void *arg, const struct spdk_nvme_cpl *completion) 594 | { 595 | struct my_sequence *sequence = arg; 596 | 597 | sequence->is_completed = 1; 598 | if (spdk_nvme_cpl_is_error(completion)) { 599 | spdk_nvme_qpair_print_completion(sequence->ns_entry->qpair, (struct spdk_nvme_cpl *)completion); 600 | fprintf(stderr, "I/O error status: %s\n", spdk_nvme_cpl_get_status_string(&completion->status)); 601 | fprintf(stderr, "Reset zone I/O failed, aborting run\n"); 602 | sequence->is_completed = 2; 603 | exit(1); 604 | } 605 | } 606 | 607 | static void reset_zone_and_wait_for_completion(struct my_sequence *sequence) 608 | { 609 | if (spdk_nvme_zns_reset_zone(sequence->ns_entry->ns, sequence->ns_entry->qpair, 610 | 0, /* starting LBA of the zone to reset */ 611 | false, /* don't reset all zones */ 612 | reset_zone_complete, 613 | sequence)) { 614 | fprintf(stderr, "starting reset zone I/O failed\n"); 615 | exit(1); 616 | } 617 | while (!sequence->is_completed) { 618 | spdk_nvme_qpair_process_completions(sequence->ns_entry->qpair, 0); 619 | } 620 | sequence->is_completed = 0; 621 | } 622 | 623 | bool io_completed; 624 | static void check_completion(void *arg, const struct spdk_nvme_cpl *cpl) 625 | { 626 | if (spdk_nvme_cpl_is_error(cpl)) 627 | { 628 | printf("I/O Option Failed\n"); 629 | } 630 | io_completed = true; 631 | } 632 | 633 | static void print_zns_zone(uint8_t *report, uint32_t index, uint32_t zdes) 634 | { 635 | struct spdk_nvme_zns_zone_desc *desc; 636 | uint32_t i, zds, zrs, zd_index; 637 | 638 | zrs = sizeof(struct spdk_nvme_zns_zone_report); 639 | zds = sizeof(struct spdk_nvme_zns_zone_desc); 640 | zd_index = zrs + index * (zds + zdes); 641 | 642 | desc = (struct spdk_nvme_zns_zone_desc *)(report + zd_index); 643 | 644 | printf("ZSLBA: 0x%016"PRIx64" ZCAP: 0x%016"PRIx64" WP: 0x%016"PRIx64" ZS: ", desc->zslba, 645 | desc->zcap, desc->wp); 646 | switch (desc->zs) { 647 | case SPDK_NVME_ZONE_STATE_EMPTY: 648 | printf("Empty"); 649 | break; 650 | case SPDK_NVME_ZONE_STATE_IOPEN: 651 | printf("Implicit open"); 652 | break; 653 | case SPDK_NVME_ZONE_STATE_EOPEN: 654 | printf("Explicit open"); 655 | break; 656 | case SPDK_NVME_ZONE_STATE_CLOSED: 657 | printf("Closed"); 658 | break; 659 | case SPDK_NVME_ZONE_STATE_RONLY: 660 | printf("Read only"); 661 | break; 662 | case SPDK_NVME_ZONE_STATE_FULL: 663 | printf("Full"); 664 | break; 665 | case SPDK_NVME_ZONE_STATE_OFFLINE: 666 | printf("Offline"); 667 | break; 668 | default: 669 | printf("Reserved"); 670 | } 671 | printf(" ZT: %s ZA: %x\n", (desc->zt == SPDK_NVME_ZONE_TYPE_SEQWR) ? "SWR" : "Reserved", 672 | desc->za.raw); 673 | 674 | if (!desc->za.bits.zdev) { 675 | return; 676 | } 677 | 678 | for (i = 0; i < zdes; i += 8) { 679 | printf("zone_desc_ext[%d] : 0x%"PRIx64"\n", i, 680 | *(uint64_t *)(report + zd_index + zds + i)); 681 | } 682 | } 683 | 684 | static void hello_miracle(void) 685 | { 686 | struct ns_entry *ns_entry; 687 | struct my_sequence sequence; 688 | int rc; 689 | size_t sz; 690 | 691 | ns_entry = g_namespaces.tqh_first; 692 | ns_entry->qpair = spdk_nvme_ctrlr_alloc_io_qpair(ns_entry->ctrlr, NULL, 0); 693 | if (ns_entry->qpair == NULL) { 694 | printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n"); 695 | return; 696 | } 697 | sequence.using_cmb_io = 1; 698 | sequence.buf = spdk_nvme_ctrlr_map_cmb(ns_entry->ctrlr, &sz); 699 | if (sequence.buf == NULL || sz < 0x1000) { 700 | sequence.using_cmb_io = 0; 701 | sequence.buf = spdk_zmalloc(0x1000, 0x1000, NULL, SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA); 702 | } 703 | if (sequence.buf == NULL) { 704 | printf("ERROR: write buffer allocation failed\n"); 705 | return; 706 | } 707 | if (sequence.using_cmb_io) { 708 | printf("INFO: using controller memory buffer for IO\n"); 709 | } else { 710 | printf("INFO: using host memory buffer for IO\n"); 711 | } 712 | sequence.is_completed = 0; 713 | sequence.ns_entry = ns_entry; 714 | 715 | reset_zone_and_wait_for_completion(&sequence); 716 | 717 | 718 | /** 719 | * @brief zns ssd info 720 | * 721 | */ 722 | uint64_t num_zones = spdk_nvme_zns_ns_get_num_zones(ns_entry->ns); 723 | uint64_t zone_size = spdk_nvme_zns_ns_get_zone_size(ns_entry->ns); 724 | uint32_t zone_append_size_limit = spdk_nvme_zns_ctrlr_get_max_zone_append_size(ns_entry->ctrlr); 725 | const struct spdk_nvme_ns_data *ref_ns_data = spdk_nvme_ns_get_data(ns_entry->ns); 726 | const struct spdk_nvme_zns_ns_data *ref_ns_zns_data = spdk_nvme_zns_ns_get_data(ns_entry->ns); 727 | printf("************ NVMe Information ************\n"); 728 | printf("Number of Zone: %lu\n", num_zones); 729 | printf("Size of LBA: %lu\n", ref_ns_data->nsze); 730 | printf("Size of Zone: %lu (%lu * %lu)\n", zone_size, ref_ns_zns_data->lbafe->zsze, ref_ns_data->nsze); 731 | printf("Append Size Limit of Zone: %u\n", zone_append_size_limit); 732 | printf("****************** END *******************\n"); 733 | 734 | uint8_t *report_buf; 735 | size_t report_buf_size; 736 | uint64_t nr_zones = 0; 737 | uint64_t max_zones_per_buf; 738 | uint32_t zds, zrs, zd_index; 739 | size_t zdes = 0; 740 | 741 | zrs = sizeof(struct spdk_nvme_zns_zone_report); 742 | zds = sizeof(struct spdk_nvme_zns_zone_desc); 743 | 744 | report_buf_size = spdk_nvme_ns_get_max_io_xfer_size(ns_entry->ns); 745 | report_buf = calloc(1, report_buf_size); 746 | if (!report_buf) 747 | { 748 | printf("Zone report allocation failed!\n"); 749 | return; 750 | } 751 | memset(report_buf, 0, report_buf_size); 752 | 753 | 754 | max_zones_per_buf = (report_buf_size - zrs) / zds; 755 | rc = spdk_nvme_zns_report_zones(ns_entry->ns, ns_entry->qpair, report_buf, report_buf_size, 0, SPDK_NVME_ZRA_LIST_ALL, true, check_completion, NULL); 756 | if (rc) 757 | { 758 | fprintf(stderr, "Report zones failed\n"); 759 | return; 760 | } 761 | 762 | io_completed = false; 763 | while (!io_completed) 764 | { 765 | spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); 766 | } 767 | nr_zones = report_buf[0]; 768 | if (nr_zones > max_zones_per_buf) 769 | { 770 | fprintf(stderr, "nr_zones too big\n"); 771 | return; 772 | } 773 | if (!nr_zones) 774 | { 775 | return; 776 | } 777 | 778 | printf("************ Zone Information ************\n"); 779 | uint32_t i; 780 | for (i = 0; i < nr_zones && i < num_zones; ++ i) 781 | { 782 | print_zns_zone(report_buf, i, zdes); 783 | } 784 | printf("****************** END *******************\n"); 785 | 786 | struct spdk_nvme_zns_zone_desc *first_zone_info; 787 | zd_index = zrs + 0 * (zds + zdes); 788 | first_zone_info = (struct spdk_nvme_zns_zone_desc *)(report_buf + zd_index); 789 | 790 | printf("Writing Data to Buffer ...\n"); 791 | snprintf(sequence.buf, 0x1000, "%s", "Hello Miracle!\n"); 792 | printf("Writing Buffer to the first LBA of the first Zone ...\n"); 793 | 794 | io_completed = false; 795 | rc = spdk_nvme_zns_zone_append(ns_entry->ns, ns_entry->qpair, sequence.buf, first_zone_info->zslba, 1, check_completion, NULL, 0); 796 | if (rc != 0) { 797 | fprintf(stderr, "starting write I/O failed\n"); 798 | exit(1); 799 | } 800 | 801 | while (!io_completed) { 802 | spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); 803 | } 804 | 805 | printf("Finish Writing!\n"); 806 | printf("Reading Data from the first LBA of the first Zone ...\n"); 807 | 808 | spdk_free(sequence.buf); 809 | sequence.buf = spdk_zmalloc(0x1000, 0x1000, NULL, SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA); 810 | 811 | io_completed = false; 812 | rc = spdk_nvme_ns_cmd_read(ns_entry->ns, ns_entry->qpair, sequence.buf, first_zone_info->zslba, 1, check_completion, NULL, 0); 813 | if (rc != 0) { 814 | fprintf(stderr, "starting read I/O failed\n"); 815 | exit(1); 816 | } 817 | 818 | while (!io_completed) { 819 | spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); 820 | } 821 | 822 | printf("Finish Reading, Data is: %s", sequence.buf); 823 | spdk_free(sequence.buf); 824 | free(report_buf); 825 | 826 | spdk_nvme_ctrlr_free_io_qpair(ns_entry->qpair); 827 | } 828 | 829 | int main(int argc, char **argv) 830 | { 831 | int rc; 832 | struct spdk_env_opts opts; 833 | 834 | spdk_env_opts_init(&opts); 835 | rc = parse_args(argc, argv, &opts); 836 | if (rc != 0) 837 | { 838 | return rc; 839 | } 840 | 841 | opts.name = "hello_miracle"; 842 | if (spdk_env_init(&opts) < 0) 843 | { 844 | fprintf(stderr, "Unable to initialize SPDK env\n"); 845 | return 1; 846 | } 847 | printf("Initializing NVMe Controllers\n"); 848 | 849 | if (g_vmd && spdk_vmd_init()) 850 | { 851 | fprintf(stderr, "Failed to initialize VMD." 852 | " Some NVMe devices can be unavailable.\n"); 853 | } 854 | 855 | rc = spdk_nvme_probe(&g_trid, NULL, probe_cb, attach_cb, NULL); 856 | if (rc != 0) 857 | { 858 | fprintf(stderr, "spdk_nvme_probe() failed\n"); 859 | rc = 1; 860 | goto exit; 861 | } 862 | 863 | if (TAILQ_EMPTY(&g_controllers)) 864 | { 865 | fprintf(stderr, "no NVMe controllers found\n"); 866 | rc = 1; 867 | goto exit; 868 | } 869 | 870 | printf("Initialization complete.\n"); 871 | 872 | hello_miracle(); 873 | // test(); 874 | 875 | cleanup(); 876 | 877 | if (g_vmd) 878 | { 879 | spdk_vmd_fini(); 880 | } 881 | 882 | exit: 883 | cleanup(); 884 | spdk_env_fini(); 885 | return rc; 886 | } 887 | ``` 888 | 889 | #### Makefile 890 | 891 | ```makefile 892 | SPDK_ROOT_DIR := $(abspath $(CURDIR)/../spdk) 893 | 894 | APP = hello_miracle 895 | 896 | include $(SPDK_ROOT_DIR)/mk/nvme.libtest.mk 897 | 898 | run: all 899 | @ rm -f hello_miracle.d hello_miracle.o 900 | @ sudo ./hello_miracle 901 | ``` 902 | 903 | #### 关键输出 904 | 905 | ![file](./README.assets/636a53dbe0a1f.png) 906 | 907 | ![file](./README.assets/636a53efee093.png) 908 | 909 | #### 完整日志 910 | 911 | ```bash 912 | miracle@cs-exp-zns:~/work/task2/hello_miracle$ make run 913 | /bin/sh: 1: pkg-config: not found 914 | CC hello_miracle/hello_miracle.o 915 | LINK hello_miracle 916 | TELEMETRY: No legacy callbacks, legacy socket not created 917 | Initializing NVMe Controllers 918 | Attaching to 0000:00:04.0 919 | Attached to 0000:00:04.0 920 | Namespace ID: 2 size: 10GB 921 | Initialization complete. 922 | INFO: using host memory buffer for IO 923 | ************ NVMe Information ************ 924 | Number of Zone: 81920 925 | Size of LBA: 2621440 926 | Size of Zone: 131072 (32 * 2621440) 927 | Append Size Limit of Zone: 524288 928 | ****************** END ******************* 929 | ************ Zone Information ************ 930 | ZSLBA: 0x0000000000000000 ZCAP: 0x0000000000000020 WP: 0x0000000000000000 ZS: Empty ZT: SWR ZA: 0 931 | ZSLBA: 0x0000000000000020 ZCAP: 0x0000000000000020 WP: 0x0000000000000020 ZS: Empty ZT: SWR ZA: 0 932 | ZSLBA: 0x0000000000000040 ZCAP: 0x0000000000000020 WP: 0x0000000000000040 ZS: Empty ZT: SWR ZA: 0 933 | ZSLBA: 0x0000000000000060 ZCAP: 0x0000000000000020 WP: 0x0000000000000060 ZS: Empty ZT: SWR ZA: 0 934 | ZSLBA: 0x0000000000000080 ZCAP: 0x0000000000000020 WP: 0x0000000000000080 ZS: Empty ZT: SWR ZA: 0 935 | ZSLBA: 0x00000000000000a0 ZCAP: 0x0000000000000020 WP: 0x00000000000000a0 ZS: Empty ZT: SWR ZA: 0 936 | ZSLBA: 0x00000000000000c0 ZCAP: 0x0000000000000020 WP: 0x00000000000000c0 ZS: Empty ZT: SWR ZA: 0 937 | ZSLBA: 0x00000000000000e0 ZCAP: 0x0000000000000020 WP: 0x00000000000000e0 ZS: Empty ZT: SWR ZA: 0 938 | ZSLBA: 0x0000000000000100 ZCAP: 0x0000000000000020 WP: 0x0000000000000100 ZS: Empty ZT: SWR ZA: 0 939 | ZSLBA: 0x0000000000000120 ZCAP: 0x0000000000000020 WP: 0x0000000000000120 ZS: Empty ZT: SWR ZA: 0 940 | ZSLBA: 0x0000000000000140 ZCAP: 0x0000000000000020 WP: 0x0000000000000140 ZS: Empty ZT: SWR ZA: 0 941 | ZSLBA: 0x0000000000000160 ZCAP: 0x0000000000000020 WP: 0x0000000000000160 ZS: Empty ZT: SWR ZA: 0 942 | ZSLBA: 0x0000000000000180 ZCAP: 0x0000000000000020 WP: 0x0000000000000180 ZS: Empty ZT: SWR ZA: 0 943 | ZSLBA: 0x00000000000001a0 ZCAP: 0x0000000000000020 WP: 0x00000000000001a0 ZS: Empty ZT: SWR ZA: 0 944 | ZSLBA: 0x00000000000001c0 ZCAP: 0x0000000000000020 WP: 0x00000000000001c0 ZS: Empty ZT: SWR ZA: 0 945 | ZSLBA: 0x00000000000001e0 ZCAP: 0x0000000000000020 WP: 0x00000000000001e0 ZS: Empty ZT: SWR ZA: 0 946 | ZSLBA: 0x0000000000000200 ZCAP: 0x0000000000000020 WP: 0x0000000000000200 ZS: Empty ZT: SWR ZA: 0 947 | ZSLBA: 0x0000000000000220 ZCAP: 0x0000000000000020 WP: 0x0000000000000220 ZS: Empty ZT: SWR ZA: 0 948 | ZSLBA: 0x0000000000000240 ZCAP: 0x0000000000000020 WP: 0x0000000000000240 ZS: Empty ZT: SWR ZA: 0 949 | ZSLBA: 0x0000000000000260 ZCAP: 0x0000000000000020 WP: 0x0000000000000260 ZS: Empty ZT: SWR ZA: 0 950 | ZSLBA: 0x0000000000000280 ZCAP: 0x0000000000000020 WP: 0x0000000000000280 ZS: Empty ZT: SWR ZA: 0 951 | ZSLBA: 0x00000000000002a0 ZCAP: 0x0000000000000020 WP: 0x00000000000002a0 ZS: Empty ZT: SWR ZA: 0 952 | ZSLBA: 0x00000000000002c0 ZCAP: 0x0000000000000020 WP: 0x00000000000002c0 ZS: Empty ZT: SWR ZA: 0 953 | ZSLBA: 0x00000000000002e0 ZCAP: 0x0000000000000020 WP: 0x00000000000002e0 ZS: Empty ZT: SWR ZA: 0 954 | ZSLBA: 0x0000000000000300 ZCAP: 0x0000000000000020 WP: 0x0000000000000300 ZS: Empty ZT: SWR ZA: 0 955 | ZSLBA: 0x0000000000000320 ZCAP: 0x0000000000000020 WP: 0x0000000000000320 ZS: Empty ZT: SWR ZA: 0 956 | ZSLBA: 0x0000000000000340 ZCAP: 0x0000000000000020 WP: 0x0000000000000340 ZS: Empty ZT: SWR ZA: 0 957 | ZSLBA: 0x0000000000000360 ZCAP: 0x0000000000000020 WP: 0x0000000000000360 ZS: Empty ZT: SWR ZA: 0 958 | ZSLBA: 0x0000000000000380 ZCAP: 0x0000000000000020 WP: 0x0000000000000380 ZS: Empty ZT: SWR ZA: 0 959 | ZSLBA: 0x00000000000003a0 ZCAP: 0x0000000000000020 WP: 0x00000000000003a0 ZS: Empty ZT: SWR ZA: 0 960 | ZSLBA: 0x00000000000003c0 ZCAP: 0x0000000000000020 WP: 0x00000000000003c0 ZS: Empty ZT: SWR ZA: 0 961 | ZSLBA: 0x00000000000003e0 ZCAP: 0x0000000000000020 WP: 0x00000000000003e0 ZS: Empty ZT: SWR ZA: 0 962 | ZSLBA: 0x0000000000000400 ZCAP: 0x0000000000000020 WP: 0x0000000000000400 ZS: Empty ZT: SWR ZA: 0 963 | ZSLBA: 0x0000000000000420 ZCAP: 0x0000000000000020 WP: 0x0000000000000420 ZS: Empty ZT: SWR ZA: 0 964 | ZSLBA: 0x0000000000000440 ZCAP: 0x0000000000000020 WP: 0x0000000000000440 ZS: Empty ZT: SWR ZA: 0 965 | ZSLBA: 0x0000000000000460 ZCAP: 0x0000000000000020 WP: 0x0000000000000460 ZS: Empty ZT: SWR ZA: 0 966 | ZSLBA: 0x0000000000000480 ZCAP: 0x0000000000000020 WP: 0x0000000000000480 ZS: Empty ZT: SWR ZA: 0 967 | ZSLBA: 0x00000000000004a0 ZCAP: 0x0000000000000020 WP: 0x00000000000004a0 ZS: Empty ZT: SWR ZA: 0 968 | ZSLBA: 0x00000000000004c0 ZCAP: 0x0000000000000020 WP: 0x00000000000004c0 ZS: Empty ZT: SWR ZA: 0 969 | ZSLBA: 0x00000000000004e0 ZCAP: 0x0000000000000020 WP: 0x00000000000004e0 ZS: Empty ZT: SWR ZA: 0 970 | ZSLBA: 0x0000000000000500 ZCAP: 0x0000000000000020 WP: 0x0000000000000500 ZS: Empty ZT: SWR ZA: 0 971 | ZSLBA: 0x0000000000000520 ZCAP: 0x0000000000000020 WP: 0x0000000000000520 ZS: Empty ZT: SWR ZA: 0 972 | ZSLBA: 0x0000000000000540 ZCAP: 0x0000000000000020 WP: 0x0000000000000540 ZS: Empty ZT: SWR ZA: 0 973 | ZSLBA: 0x0000000000000560 ZCAP: 0x0000000000000020 WP: 0x0000000000000560 ZS: Empty ZT: SWR ZA: 0 974 | ZSLBA: 0x0000000000000580 ZCAP: 0x0000000000000020 WP: 0x0000000000000580 ZS: Empty ZT: SWR ZA: 0 975 | ZSLBA: 0x00000000000005a0 ZCAP: 0x0000000000000020 WP: 0x00000000000005a0 ZS: Empty ZT: SWR ZA: 0 976 | ZSLBA: 0x00000000000005c0 ZCAP: 0x0000000000000020 WP: 0x00000000000005c0 ZS: Empty ZT: SWR ZA: 0 977 | ZSLBA: 0x00000000000005e0 ZCAP: 0x0000000000000020 WP: 0x00000000000005e0 ZS: Empty ZT: SWR ZA: 0 978 | ZSLBA: 0x0000000000000600 ZCAP: 0x0000000000000020 WP: 0x0000000000000600 ZS: Empty ZT: SWR ZA: 0 979 | ZSLBA: 0x0000000000000620 ZCAP: 0x0000000000000020 WP: 0x0000000000000620 ZS: Empty ZT: SWR ZA: 0 980 | ZSLBA: 0x0000000000000640 ZCAP: 0x0000000000000020 WP: 0x0000000000000640 ZS: Empty ZT: SWR ZA: 0 981 | ZSLBA: 0x0000000000000660 ZCAP: 0x0000000000000020 WP: 0x0000000000000660 ZS: Empty ZT: SWR ZA: 0 982 | ZSLBA: 0x0000000000000680 ZCAP: 0x0000000000000020 WP: 0x0000000000000680 ZS: Empty ZT: SWR ZA: 0 983 | ZSLBA: 0x00000000000006a0 ZCAP: 0x0000000000000020 WP: 0x00000000000006a0 ZS: Empty ZT: SWR ZA: 0 984 | ZSLBA: 0x00000000000006c0 ZCAP: 0x0000000000000020 WP: 0x00000000000006c0 ZS: Empty ZT: SWR ZA: 0 985 | ZSLBA: 0x00000000000006e0 ZCAP: 0x0000000000000020 WP: 0x00000000000006e0 ZS: Empty ZT: SWR ZA: 0 986 | ZSLBA: 0x0000000000000700 ZCAP: 0x0000000000000020 WP: 0x0000000000000700 ZS: Empty ZT: SWR ZA: 0 987 | ZSLBA: 0x0000000000000720 ZCAP: 0x0000000000000020 WP: 0x0000000000000720 ZS: Empty ZT: SWR ZA: 0 988 | ZSLBA: 0x0000000000000740 ZCAP: 0x0000000000000020 WP: 0x0000000000000740 ZS: Empty ZT: SWR ZA: 0 989 | ZSLBA: 0x0000000000000760 ZCAP: 0x0000000000000020 WP: 0x0000000000000760 ZS: Empty ZT: SWR ZA: 0 990 | ZSLBA: 0x0000000000000780 ZCAP: 0x0000000000000020 WP: 0x0000000000000780 ZS: Empty ZT: SWR ZA: 0 991 | ZSLBA: 0x00000000000007a0 ZCAP: 0x0000000000000020 WP: 0x00000000000007a0 ZS: Empty ZT: SWR ZA: 0 992 | ZSLBA: 0x00000000000007c0 ZCAP: 0x0000000000000020 WP: 0x00000000000007c0 ZS: Empty ZT: SWR ZA: 0 993 | ZSLBA: 0x00000000000007e0 ZCAP: 0x0000000000000020 WP: 0x00000000000007e0 ZS: Empty ZT: SWR ZA: 0 994 | ZSLBA: 0x0000000000000800 ZCAP: 0x0000000000000020 WP: 0x0000000000000800 ZS: Empty ZT: SWR ZA: 0 995 | ZSLBA: 0x0000000000000820 ZCAP: 0x0000000000000020 WP: 0x0000000000000820 ZS: Empty ZT: SWR ZA: 0 996 | ZSLBA: 0x0000000000000840 ZCAP: 0x0000000000000020 WP: 0x0000000000000840 ZS: Empty ZT: SWR ZA: 0 997 | ZSLBA: 0x0000000000000860 ZCAP: 0x0000000000000020 WP: 0x0000000000000860 ZS: Empty ZT: SWR ZA: 0 998 | ZSLBA: 0x0000000000000880 ZCAP: 0x0000000000000020 WP: 0x0000000000000880 ZS: Empty ZT: SWR ZA: 0 999 | ZSLBA: 0x00000000000008a0 ZCAP: 0x0000000000000020 WP: 0x00000000000008a0 ZS: Empty ZT: SWR ZA: 0 1000 | ZSLBA: 0x00000000000008c0 ZCAP: 0x0000000000000020 WP: 0x00000000000008c0 ZS: Empty ZT: SWR ZA: 0 1001 | ZSLBA: 0x00000000000008e0 ZCAP: 0x0000000000000020 WP: 0x00000000000008e0 ZS: Empty ZT: SWR ZA: 0 1002 | ZSLBA: 0x0000000000000900 ZCAP: 0x0000000000000020 WP: 0x0000000000000900 ZS: Empty ZT: SWR ZA: 0 1003 | ZSLBA: 0x0000000000000920 ZCAP: 0x0000000000000020 WP: 0x0000000000000920 ZS: Empty ZT: SWR ZA: 0 1004 | ZSLBA: 0x0000000000000940 ZCAP: 0x0000000000000020 WP: 0x0000000000000940 ZS: Empty ZT: SWR ZA: 0 1005 | ZSLBA: 0x0000000000000960 ZCAP: 0x0000000000000020 WP: 0x0000000000000960 ZS: Empty ZT: SWR ZA: 0 1006 | ZSLBA: 0x0000000000000980 ZCAP: 0x0000000000000020 WP: 0x0000000000000980 ZS: Empty ZT: SWR ZA: 0 1007 | ZSLBA: 0x00000000000009a0 ZCAP: 0x0000000000000020 WP: 0x00000000000009a0 ZS: Empty ZT: SWR ZA: 0 1008 | ZSLBA: 0x00000000000009c0 ZCAP: 0x0000000000000020 WP: 0x00000000000009c0 ZS: Empty ZT: SWR ZA: 0 1009 | ZSLBA: 0x00000000000009e0 ZCAP: 0x0000000000000020 WP: 0x00000000000009e0 ZS: Empty ZT: SWR ZA: 0 1010 | ZSLBA: 0x0000000000000a00 ZCAP: 0x0000000000000020 WP: 0x0000000000000a00 ZS: Empty ZT: SWR ZA: 0 1011 | ZSLBA: 0x0000000000000a20 ZCAP: 0x0000000000000020 WP: 0x0000000000000a20 ZS: Empty ZT: SWR ZA: 0 1012 | ZSLBA: 0x0000000000000a40 ZCAP: 0x0000000000000020 WP: 0x0000000000000a40 ZS: Empty ZT: SWR ZA: 0 1013 | ZSLBA: 0x0000000000000a60 ZCAP: 0x0000000000000020 WP: 0x0000000000000a60 ZS: Empty ZT: SWR ZA: 0 1014 | ZSLBA: 0x0000000000000a80 ZCAP: 0x0000000000000020 WP: 0x0000000000000a80 ZS: Empty ZT: SWR ZA: 0 1015 | ZSLBA: 0x0000000000000aa0 ZCAP: 0x0000000000000020 WP: 0x0000000000000aa0 ZS: Empty ZT: SWR ZA: 0 1016 | ZSLBA: 0x0000000000000ac0 ZCAP: 0x0000000000000020 WP: 0x0000000000000ac0 ZS: Empty ZT: SWR ZA: 0 1017 | ZSLBA: 0x0000000000000ae0 ZCAP: 0x0000000000000020 WP: 0x0000000000000ae0 ZS: Empty ZT: SWR ZA: 0 1018 | ZSLBA: 0x0000000000000b00 ZCAP: 0x0000000000000020 WP: 0x0000000000000b00 ZS: Empty ZT: SWR ZA: 0 1019 | ZSLBA: 0x0000000000000b20 ZCAP: 0x0000000000000020 WP: 0x0000000000000b20 ZS: Empty ZT: SWR ZA: 0 1020 | ZSLBA: 0x0000000000000b40 ZCAP: 0x0000000000000020 WP: 0x0000000000000b40 ZS: Empty ZT: SWR ZA: 0 1021 | ZSLBA: 0x0000000000000b60 ZCAP: 0x0000000000000020 WP: 0x0000000000000b60 ZS: Empty ZT: SWR ZA: 0 1022 | ZSLBA: 0x0000000000000b80 ZCAP: 0x0000000000000020 WP: 0x0000000000000b80 ZS: Empty ZT: SWR ZA: 0 1023 | ZSLBA: 0x0000000000000ba0 ZCAP: 0x0000000000000020 WP: 0x0000000000000ba0 ZS: Empty ZT: SWR ZA: 0 1024 | ZSLBA: 0x0000000000000bc0 ZCAP: 0x0000000000000020 WP: 0x0000000000000bc0 ZS: Empty ZT: SWR ZA: 0 1025 | ZSLBA: 0x0000000000000be0 ZCAP: 0x0000000000000020 WP: 0x0000000000000be0 ZS: Empty ZT: SWR ZA: 0 1026 | ZSLBA: 0x0000000000000c00 ZCAP: 0x0000000000000020 WP: 0x0000000000000c00 ZS: Empty ZT: SWR ZA: 0 1027 | ZSLBA: 0x0000000000000c20 ZCAP: 0x0000000000000020 WP: 0x0000000000000c20 ZS: Empty ZT: SWR ZA: 0 1028 | ZSLBA: 0x0000000000000c40 ZCAP: 0x0000000000000020 WP: 0x0000000000000c40 ZS: Empty ZT: SWR ZA: 0 1029 | ZSLBA: 0x0000000000000c60 ZCAP: 0x0000000000000020 WP: 0x0000000000000c60 ZS: Empty ZT: SWR ZA: 0 1030 | ZSLBA: 0x0000000000000c80 ZCAP: 0x0000000000000020 WP: 0x0000000000000c80 ZS: Empty ZT: SWR ZA: 0 1031 | ZSLBA: 0x0000000000000ca0 ZCAP: 0x0000000000000020 WP: 0x0000000000000ca0 ZS: Empty ZT: SWR ZA: 0 1032 | ZSLBA: 0x0000000000000cc0 ZCAP: 0x0000000000000020 WP: 0x0000000000000cc0 ZS: Empty ZT: SWR ZA: 0 1033 | ZSLBA: 0x0000000000000ce0 ZCAP: 0x0000000000000020 WP: 0x0000000000000ce0 ZS: Empty ZT: SWR ZA: 0 1034 | ZSLBA: 0x0000000000000d00 ZCAP: 0x0000000000000020 WP: 0x0000000000000d00 ZS: Empty ZT: SWR ZA: 0 1035 | ZSLBA: 0x0000000000000d20 ZCAP: 0x0000000000000020 WP: 0x0000000000000d20 ZS: Empty ZT: SWR ZA: 0 1036 | ZSLBA: 0x0000000000000d40 ZCAP: 0x0000000000000020 WP: 0x0000000000000d40 ZS: Empty ZT: SWR ZA: 0 1037 | ZSLBA: 0x0000000000000d60 ZCAP: 0x0000000000000020 WP: 0x0000000000000d60 ZS: Empty ZT: SWR ZA: 0 1038 | ZSLBA: 0x0000000000000d80 ZCAP: 0x0000000000000020 WP: 0x0000000000000d80 ZS: Empty ZT: SWR ZA: 0 1039 | ZSLBA: 0x0000000000000da0 ZCAP: 0x0000000000000020 WP: 0x0000000000000da0 ZS: Empty ZT: SWR ZA: 0 1040 | ZSLBA: 0x0000000000000dc0 ZCAP: 0x0000000000000020 WP: 0x0000000000000dc0 ZS: Empty ZT: SWR ZA: 0 1041 | ZSLBA: 0x0000000000000de0 ZCAP: 0x0000000000000020 WP: 0x0000000000000de0 ZS: Empty ZT: SWR ZA: 0 1042 | ZSLBA: 0x0000000000000e00 ZCAP: 0x0000000000000020 WP: 0x0000000000000e00 ZS: Empty ZT: SWR ZA: 0 1043 | ZSLBA: 0x0000000000000e20 ZCAP: 0x0000000000000020 WP: 0x0000000000000e20 ZS: Empty ZT: SWR ZA: 0 1044 | ZSLBA: 0x0000000000000e40 ZCAP: 0x0000000000000020 WP: 0x0000000000000e40 ZS: Empty ZT: SWR ZA: 0 1045 | ZSLBA: 0x0000000000000e60 ZCAP: 0x0000000000000020 WP: 0x0000000000000e60 ZS: Empty ZT: SWR ZA: 0 1046 | ZSLBA: 0x0000000000000e80 ZCAP: 0x0000000000000020 WP: 0x0000000000000e80 ZS: Empty ZT: SWR ZA: 0 1047 | ZSLBA: 0x0000000000000ea0 ZCAP: 0x0000000000000020 WP: 0x0000000000000ea0 ZS: Empty ZT: SWR ZA: 0 1048 | ZSLBA: 0x0000000000000ec0 ZCAP: 0x0000000000000020 WP: 0x0000000000000ec0 ZS: Empty ZT: SWR ZA: 0 1049 | ZSLBA: 0x0000000000000ee0 ZCAP: 0x0000000000000020 WP: 0x0000000000000ee0 ZS: Empty ZT: SWR ZA: 0 1050 | ZSLBA: 0x0000000000000f00 ZCAP: 0x0000000000000020 WP: 0x0000000000000f00 ZS: Empty ZT: SWR ZA: 0 1051 | ZSLBA: 0x0000000000000f20 ZCAP: 0x0000000000000020 WP: 0x0000000000000f20 ZS: Empty ZT: SWR ZA: 0 1052 | ZSLBA: 0x0000000000000f40 ZCAP: 0x0000000000000020 WP: 0x0000000000000f40 ZS: Empty ZT: SWR ZA: 0 1053 | ZSLBA: 0x0000000000000f60 ZCAP: 0x0000000000000020 WP: 0x0000000000000f60 ZS: Empty ZT: SWR ZA: 0 1054 | ZSLBA: 0x0000000000000f80 ZCAP: 0x0000000000000020 WP: 0x0000000000000f80 ZS: Empty ZT: SWR ZA: 0 1055 | ZSLBA: 0x0000000000000fa0 ZCAP: 0x0000000000000020 WP: 0x0000000000000fa0 ZS: Empty ZT: SWR ZA: 0 1056 | ZSLBA: 0x0000000000000fc0 ZCAP: 0x0000000000000020 WP: 0x0000000000000fc0 ZS: Empty ZT: SWR ZA: 0 1057 | ZSLBA: 0x0000000000000fe0 ZCAP: 0x0000000000000020 WP: 0x0000000000000fe0 ZS: Empty ZT: SWR ZA: 0 1058 | ZSLBA: 0x0000000000001000 ZCAP: 0x0000000000000020 WP: 0x0000000000001000 ZS: Empty ZT: SWR ZA: 0 1059 | ZSLBA: 0x0000000000001020 ZCAP: 0x0000000000000020 WP: 0x0000000000001020 ZS: Empty ZT: SWR ZA: 0 1060 | ZSLBA: 0x0000000000001040 ZCAP: 0x0000000000000020 WP: 0x0000000000001040 ZS: Empty ZT: SWR ZA: 0 1061 | ZSLBA: 0x0000000000001060 ZCAP: 0x0000000000000020 WP: 0x0000000000001060 ZS: Empty ZT: SWR ZA: 0 1062 | ZSLBA: 0x0000000000001080 ZCAP: 0x0000000000000020 WP: 0x0000000000001080 ZS: Empty ZT: SWR ZA: 0 1063 | ZSLBA: 0x00000000000010a0 ZCAP: 0x0000000000000020 WP: 0x00000000000010a0 ZS: Empty ZT: SWR ZA: 0 1064 | ZSLBA: 0x00000000000010c0 ZCAP: 0x0000000000000020 WP: 0x00000000000010c0 ZS: Empty ZT: SWR ZA: 0 1065 | ZSLBA: 0x00000000000010e0 ZCAP: 0x0000000000000020 WP: 0x00000000000010e0 ZS: Empty ZT: SWR ZA: 0 1066 | ZSLBA: 0x0000000000001100 ZCAP: 0x0000000000000020 WP: 0x0000000000001100 ZS: Empty ZT: SWR ZA: 0 1067 | ZSLBA: 0x0000000000001120 ZCAP: 0x0000000000000020 WP: 0x0000000000001120 ZS: Empty ZT: SWR ZA: 0 1068 | ZSLBA: 0x0000000000001140 ZCAP: 0x0000000000000020 WP: 0x0000000000001140 ZS: Empty ZT: SWR ZA: 0 1069 | ZSLBA: 0x0000000000001160 ZCAP: 0x0000000000000020 WP: 0x0000000000001160 ZS: Empty ZT: SWR ZA: 0 1070 | ZSLBA: 0x0000000000001180 ZCAP: 0x0000000000000020 WP: 0x0000000000001180 ZS: Empty ZT: SWR ZA: 0 1071 | ZSLBA: 0x00000000000011a0 ZCAP: 0x0000000000000020 WP: 0x00000000000011a0 ZS: Empty ZT: SWR ZA: 0 1072 | ZSLBA: 0x00000000000011c0 ZCAP: 0x0000000000000020 WP: 0x00000000000011c0 ZS: Empty ZT: SWR ZA: 0 1073 | ZSLBA: 0x00000000000011e0 ZCAP: 0x0000000000000020 WP: 0x00000000000011e0 ZS: Empty ZT: SWR ZA: 0 1074 | ZSLBA: 0x0000000000001200 ZCAP: 0x0000000000000020 WP: 0x0000000000001200 ZS: Empty ZT: SWR ZA: 0 1075 | ZSLBA: 0x0000000000001220 ZCAP: 0x0000000000000020 WP: 0x0000000000001220 ZS: Empty ZT: SWR ZA: 0 1076 | ZSLBA: 0x0000000000001240 ZCAP: 0x0000000000000020 WP: 0x0000000000001240 ZS: Empty ZT: SWR ZA: 0 1077 | ZSLBA: 0x0000000000001260 ZCAP: 0x0000000000000020 WP: 0x0000000000001260 ZS: Empty ZT: SWR ZA: 0 1078 | ZSLBA: 0x0000000000001280 ZCAP: 0x0000000000000020 WP: 0x0000000000001280 ZS: Empty ZT: SWR ZA: 0 1079 | ZSLBA: 0x00000000000012a0 ZCAP: 0x0000000000000020 WP: 0x00000000000012a0 ZS: Empty ZT: SWR ZA: 0 1080 | ZSLBA: 0x00000000000012c0 ZCAP: 0x0000000000000020 WP: 0x00000000000012c0 ZS: Empty ZT: SWR ZA: 0 1081 | ZSLBA: 0x00000000000012e0 ZCAP: 0x0000000000000020 WP: 0x00000000000012e0 ZS: Empty ZT: SWR ZA: 0 1082 | ZSLBA: 0x0000000000001300 ZCAP: 0x0000000000000020 WP: 0x0000000000001300 ZS: Empty ZT: SWR ZA: 0 1083 | ZSLBA: 0x0000000000001320 ZCAP: 0x0000000000000020 WP: 0x0000000000001320 ZS: Empty ZT: SWR ZA: 0 1084 | ZSLBA: 0x0000000000001340 ZCAP: 0x0000000000000020 WP: 0x0000000000001340 ZS: Empty ZT: SWR ZA: 0 1085 | ZSLBA: 0x0000000000001360 ZCAP: 0x0000000000000020 WP: 0x0000000000001360 ZS: Empty ZT: SWR ZA: 0 1086 | ZSLBA: 0x0000000000001380 ZCAP: 0x0000000000000020 WP: 0x0000000000001380 ZS: Empty ZT: SWR ZA: 0 1087 | ZSLBA: 0x00000000000013a0 ZCAP: 0x0000000000000020 WP: 0x00000000000013a0 ZS: Empty ZT: SWR ZA: 0 1088 | ZSLBA: 0x00000000000013c0 ZCAP: 0x0000000000000020 WP: 0x00000000000013c0 ZS: Empty ZT: SWR ZA: 0 1089 | ZSLBA: 0x00000000000013e0 ZCAP: 0x0000000000000020 WP: 0x00000000000013e0 ZS: Empty ZT: SWR ZA: 0 1090 | ZSLBA: 0x0000000000001400 ZCAP: 0x0000000000000020 WP: 0x0000000000001400 ZS: Empty ZT: SWR ZA: 0 1091 | ZSLBA: 0x0000000000001420 ZCAP: 0x0000000000000020 WP: 0x0000000000001420 ZS: Empty ZT: SWR ZA: 0 1092 | ZSLBA: 0x0000000000001440 ZCAP: 0x0000000000000020 WP: 0x0000000000001440 ZS: Empty ZT: SWR ZA: 0 1093 | ZSLBA: 0x0000000000001460 ZCAP: 0x0000000000000020 WP: 0x0000000000001460 ZS: Empty ZT: SWR ZA: 0 1094 | ZSLBA: 0x0000000000001480 ZCAP: 0x0000000000000020 WP: 0x0000000000001480 ZS: Empty ZT: SWR ZA: 0 1095 | ZSLBA: 0x00000000000014a0 ZCAP: 0x0000000000000020 WP: 0x00000000000014a0 ZS: Empty ZT: SWR ZA: 0 1096 | ZSLBA: 0x00000000000014c0 ZCAP: 0x0000000000000020 WP: 0x00000000000014c0 ZS: Empty ZT: SWR ZA: 0 1097 | ZSLBA: 0x00000000000014e0 ZCAP: 0x0000000000000020 WP: 0x00000000000014e0 ZS: Empty ZT: SWR ZA: 0 1098 | ZSLBA: 0x0000000000001500 ZCAP: 0x0000000000000020 WP: 0x0000000000001500 ZS: Empty ZT: SWR ZA: 0 1099 | ZSLBA: 0x0000000000001520 ZCAP: 0x0000000000000020 WP: 0x0000000000001520 ZS: Empty ZT: SWR ZA: 0 1100 | ZSLBA: 0x0000000000001540 ZCAP: 0x0000000000000020 WP: 0x0000000000001540 ZS: Empty ZT: SWR ZA: 0 1101 | ZSLBA: 0x0000000000001560 ZCAP: 0x0000000000000020 WP: 0x0000000000001560 ZS: Empty ZT: SWR ZA: 0 1102 | ZSLBA: 0x0000000000001580 ZCAP: 0x0000000000000020 WP: 0x0000000000001580 ZS: Empty ZT: SWR ZA: 0 1103 | ZSLBA: 0x00000000000015a0 ZCAP: 0x0000000000000020 WP: 0x00000000000015a0 ZS: Empty ZT: SWR ZA: 0 1104 | ZSLBA: 0x00000000000015c0 ZCAP: 0x0000000000000020 WP: 0x00000000000015c0 ZS: Empty ZT: SWR ZA: 0 1105 | ZSLBA: 0x00000000000015e0 ZCAP: 0x0000000000000020 WP: 0x00000000000015e0 ZS: Empty ZT: SWR ZA: 0 1106 | ZSLBA: 0x0000000000001600 ZCAP: 0x0000000000000020 WP: 0x0000000000001600 ZS: Empty ZT: SWR ZA: 0 1107 | ZSLBA: 0x0000000000001620 ZCAP: 0x0000000000000020 WP: 0x0000000000001620 ZS: Empty ZT: SWR ZA: 0 1108 | ZSLBA: 0x0000000000001640 ZCAP: 0x0000000000000020 WP: 0x0000000000001640 ZS: Empty ZT: SWR ZA: 0 1109 | ZSLBA: 0x0000000000001660 ZCAP: 0x0000000000000020 WP: 0x0000000000001660 ZS: Empty ZT: SWR ZA: 0 1110 | ZSLBA: 0x0000000000001680 ZCAP: 0x0000000000000020 WP: 0x0000000000001680 ZS: Empty ZT: SWR ZA: 0 1111 | ZSLBA: 0x00000000000016a0 ZCAP: 0x0000000000000020 WP: 0x00000000000016a0 ZS: Empty ZT: SWR ZA: 0 1112 | ZSLBA: 0x00000000000016c0 ZCAP: 0x0000000000000020 WP: 0x00000000000016c0 ZS: Empty ZT: SWR ZA: 0 1113 | ZSLBA: 0x00000000000016e0 ZCAP: 0x0000000000000020 WP: 0x00000000000016e0 ZS: Empty ZT: SWR ZA: 0 1114 | ZSLBA: 0x0000000000001700 ZCAP: 0x0000000000000020 WP: 0x0000000000001700 ZS: Empty ZT: SWR ZA: 0 1115 | ZSLBA: 0x0000000000001720 ZCAP: 0x0000000000000020 WP: 0x0000000000001720 ZS: Empty ZT: SWR ZA: 0 1116 | ZSLBA: 0x0000000000001740 ZCAP: 0x0000000000000020 WP: 0x0000000000001740 ZS: Empty ZT: SWR ZA: 0 1117 | ZSLBA: 0x0000000000001760 ZCAP: 0x0000000000000020 WP: 0x0000000000001760 ZS: Empty ZT: SWR ZA: 0 1118 | ZSLBA: 0x0000000000001780 ZCAP: 0x0000000000000020 WP: 0x0000000000001780 ZS: Empty ZT: SWR ZA: 0 1119 | ZSLBA: 0x00000000000017a0 ZCAP: 0x0000000000000020 WP: 0x00000000000017a0 ZS: Empty ZT: SWR ZA: 0 1120 | ZSLBA: 0x00000000000017c0 ZCAP: 0x0000000000000020 WP: 0x00000000000017c0 ZS: Empty ZT: SWR ZA: 0 1121 | ZSLBA: 0x00000000000017e0 ZCAP: 0x0000000000000020 WP: 0x00000000000017e0 ZS: Empty ZT: SWR ZA: 0 1122 | ZSLBA: 0x0000000000001800 ZCAP: 0x0000000000000020 WP: 0x0000000000001800 ZS: Empty ZT: SWR ZA: 0 1123 | ZSLBA: 0x0000000000001820 ZCAP: 0x0000000000000020 WP: 0x0000000000001820 ZS: Empty ZT: SWR ZA: 0 1124 | ZSLBA: 0x0000000000001840 ZCAP: 0x0000000000000020 WP: 0x0000000000001840 ZS: Empty ZT: SWR ZA: 0 1125 | ZSLBA: 0x0000000000001860 ZCAP: 0x0000000000000020 WP: 0x0000000000001860 ZS: Empty ZT: SWR ZA: 0 1126 | ZSLBA: 0x0000000000001880 ZCAP: 0x0000000000000020 WP: 0x0000000000001880 ZS: Empty ZT: SWR ZA: 0 1127 | ZSLBA: 0x00000000000018a0 ZCAP: 0x0000000000000020 WP: 0x00000000000018a0 ZS: Empty ZT: SWR ZA: 0 1128 | ZSLBA: 0x00000000000018c0 ZCAP: 0x0000000000000020 WP: 0x00000000000018c0 ZS: Empty ZT: SWR ZA: 0 1129 | ZSLBA: 0x00000000000018e0 ZCAP: 0x0000000000000020 WP: 0x00000000000018e0 ZS: Empty ZT: SWR ZA: 0 1130 | ZSLBA: 0x0000000000001900 ZCAP: 0x0000000000000020 WP: 0x0000000000001900 ZS: Empty ZT: SWR ZA: 0 1131 | ZSLBA: 0x0000000000001920 ZCAP: 0x0000000000000020 WP: 0x0000000000001920 ZS: Empty ZT: SWR ZA: 0 1132 | ZSLBA: 0x0000000000001940 ZCAP: 0x0000000000000020 WP: 0x0000000000001940 ZS: Empty ZT: SWR ZA: 0 1133 | ZSLBA: 0x0000000000001960 ZCAP: 0x0000000000000020 WP: 0x0000000000001960 ZS: Empty ZT: SWR ZA: 0 1134 | ZSLBA: 0x0000000000001980 ZCAP: 0x0000000000000020 WP: 0x0000000000001980 ZS: Empty ZT: SWR ZA: 0 1135 | ZSLBA: 0x00000000000019a0 ZCAP: 0x0000000000000020 WP: 0x00000000000019a0 ZS: Empty ZT: SWR ZA: 0 1136 | ZSLBA: 0x00000000000019c0 ZCAP: 0x0000000000000020 WP: 0x00000000000019c0 ZS: Empty ZT: SWR ZA: 0 1137 | ZSLBA: 0x00000000000019e0 ZCAP: 0x0000000000000020 WP: 0x00000000000019e0 ZS: Empty ZT: SWR ZA: 0 1138 | ZSLBA: 0x0000000000001a00 ZCAP: 0x0000000000000020 WP: 0x0000000000001a00 ZS: Empty ZT: SWR ZA: 0 1139 | ZSLBA: 0x0000000000001a20 ZCAP: 0x0000000000000020 WP: 0x0000000000001a20 ZS: Empty ZT: SWR ZA: 0 1140 | ZSLBA: 0x0000000000001a40 ZCAP: 0x0000000000000020 WP: 0x0000000000001a40 ZS: Empty ZT: SWR ZA: 0 1141 | ZSLBA: 0x0000000000001a60 ZCAP: 0x0000000000000020 WP: 0x0000000000001a60 ZS: Empty ZT: SWR ZA: 0 1142 | ZSLBA: 0x0000000000001a80 ZCAP: 0x0000000000000020 WP: 0x0000000000001a80 ZS: Empty ZT: SWR ZA: 0 1143 | ZSLBA: 0x0000000000001aa0 ZCAP: 0x0000000000000020 WP: 0x0000000000001aa0 ZS: Empty ZT: SWR ZA: 0 1144 | ZSLBA: 0x0000000000001ac0 ZCAP: 0x0000000000000020 WP: 0x0000000000001ac0 ZS: Empty ZT: SWR ZA: 0 1145 | ZSLBA: 0x0000000000001ae0 ZCAP: 0x0000000000000020 WP: 0x0000000000001ae0 ZS: Empty ZT: SWR ZA: 0 1146 | ZSLBA: 0x0000000000001b00 ZCAP: 0x0000000000000020 WP: 0x0000000000001b00 ZS: Empty ZT: SWR ZA: 0 1147 | ZSLBA: 0x0000000000001b20 ZCAP: 0x0000000000000020 WP: 0x0000000000001b20 ZS: Empty ZT: SWR ZA: 0 1148 | ZSLBA: 0x0000000000001b40 ZCAP: 0x0000000000000020 WP: 0x0000000000001b40 ZS: Empty ZT: SWR ZA: 0 1149 | ZSLBA: 0x0000000000001b60 ZCAP: 0x0000000000000020 WP: 0x0000000000001b60 ZS: Empty ZT: SWR ZA: 0 1150 | ZSLBA: 0x0000000000001b80 ZCAP: 0x0000000000000020 WP: 0x0000000000001b80 ZS: Empty ZT: SWR ZA: 0 1151 | ZSLBA: 0x0000000000001ba0 ZCAP: 0x0000000000000020 WP: 0x0000000000001ba0 ZS: Empty ZT: SWR ZA: 0 1152 | ZSLBA: 0x0000000000001bc0 ZCAP: 0x0000000000000020 WP: 0x0000000000001bc0 ZS: Empty ZT: SWR ZA: 0 1153 | ZSLBA: 0x0000000000001be0 ZCAP: 0x0000000000000020 WP: 0x0000000000001be0 ZS: Empty ZT: SWR ZA: 0 1154 | ZSLBA: 0x0000000000001c00 ZCAP: 0x0000000000000020 WP: 0x0000000000001c00 ZS: Empty ZT: SWR ZA: 0 1155 | ZSLBA: 0x0000000000001c20 ZCAP: 0x0000000000000020 WP: 0x0000000000001c20 ZS: Empty ZT: SWR ZA: 0 1156 | ZSLBA: 0x0000000000001c40 ZCAP: 0x0000000000000020 WP: 0x0000000000001c40 ZS: Empty ZT: SWR ZA: 0 1157 | ZSLBA: 0x0000000000001c60 ZCAP: 0x0000000000000020 WP: 0x0000000000001c60 ZS: Empty ZT: SWR ZA: 0 1158 | ZSLBA: 0x0000000000001c80 ZCAP: 0x0000000000000020 WP: 0x0000000000001c80 ZS: Empty ZT: SWR ZA: 0 1159 | ZSLBA: 0x0000000000001ca0 ZCAP: 0x0000000000000020 WP: 0x0000000000001ca0 ZS: Empty ZT: SWR ZA: 0 1160 | ZSLBA: 0x0000000000001cc0 ZCAP: 0x0000000000000020 WP: 0x0000000000001cc0 ZS: Empty ZT: SWR ZA: 0 1161 | ZSLBA: 0x0000000000001ce0 ZCAP: 0x0000000000000020 WP: 0x0000000000001ce0 ZS: Empty ZT: SWR ZA: 0 1162 | ZSLBA: 0x0000000000001d00 ZCAP: 0x0000000000000020 WP: 0x0000000000001d00 ZS: Empty ZT: SWR ZA: 0 1163 | ZSLBA: 0x0000000000001d20 ZCAP: 0x0000000000000020 WP: 0x0000000000001d20 ZS: Empty ZT: SWR ZA: 0 1164 | ZSLBA: 0x0000000000001d40 ZCAP: 0x0000000000000020 WP: 0x0000000000001d40 ZS: Empty ZT: SWR ZA: 0 1165 | ZSLBA: 0x0000000000001d60 ZCAP: 0x0000000000000020 WP: 0x0000000000001d60 ZS: Empty ZT: SWR ZA: 0 1166 | ZSLBA: 0x0000000000001d80 ZCAP: 0x0000000000000020 WP: 0x0000000000001d80 ZS: Empty ZT: SWR ZA: 0 1167 | ZSLBA: 0x0000000000001da0 ZCAP: 0x0000000000000020 WP: 0x0000000000001da0 ZS: Empty ZT: SWR ZA: 0 1168 | ZSLBA: 0x0000000000001dc0 ZCAP: 0x0000000000000020 WP: 0x0000000000001dc0 ZS: Empty ZT: SWR ZA: 0 1169 | ZSLBA: 0x0000000000001de0 ZCAP: 0x0000000000000020 WP: 0x0000000000001de0 ZS: Empty ZT: SWR ZA: 0 1170 | ZSLBA: 0x0000000000001e00 ZCAP: 0x0000000000000020 WP: 0x0000000000001e00 ZS: Empty ZT: SWR ZA: 0 1171 | ZSLBA: 0x0000000000001e20 ZCAP: 0x0000000000000020 WP: 0x0000000000001e20 ZS: Empty ZT: SWR ZA: 0 1172 | ZSLBA: 0x0000000000001e40 ZCAP: 0x0000000000000020 WP: 0x0000000000001e40 ZS: Empty ZT: SWR ZA: 0 1173 | ZSLBA: 0x0000000000001e60 ZCAP: 0x0000000000000020 WP: 0x0000000000001e60 ZS: Empty ZT: SWR ZA: 0 1174 | ZSLBA: 0x0000000000001e80 ZCAP: 0x0000000000000020 WP: 0x0000000000001e80 ZS: Empty ZT: SWR ZA: 0 1175 | ZSLBA: 0x0000000000001ea0 ZCAP: 0x0000000000000020 WP: 0x0000000000001ea0 ZS: Empty ZT: SWR ZA: 0 1176 | ZSLBA: 0x0000000000001ec0 ZCAP: 0x0000000000000020 WP: 0x0000000000001ec0 ZS: Empty ZT: SWR ZA: 0 1177 | ZSLBA: 0x0000000000001ee0 ZCAP: 0x0000000000000020 WP: 0x0000000000001ee0 ZS: Empty ZT: SWR ZA: 0 1178 | ZSLBA: 0x0000000000001f00 ZCAP: 0x0000000000000020 WP: 0x0000000000001f00 ZS: Empty ZT: SWR ZA: 0 1179 | ZSLBA: 0x0000000000001f20 ZCAP: 0x0000000000000020 WP: 0x0000000000001f20 ZS: Empty ZT: SWR ZA: 0 1180 | ZSLBA: 0x0000000000001f40 ZCAP: 0x0000000000000020 WP: 0x0000000000001f40 ZS: Empty ZT: SWR ZA: 0 1181 | ZSLBA: 0x0000000000001f60 ZCAP: 0x0000000000000020 WP: 0x0000000000001f60 ZS: Empty ZT: SWR ZA: 0 1182 | ZSLBA: 0x0000000000001f80 ZCAP: 0x0000000000000020 WP: 0x0000000000001f80 ZS: Empty ZT: SWR ZA: 0 1183 | ZSLBA: 0x0000000000001fa0 ZCAP: 0x0000000000000020 WP: 0x0000000000001fa0 ZS: Empty ZT: SWR ZA: 0 1184 | ZSLBA: 0x0000000000001fc0 ZCAP: 0x0000000000000020 WP: 0x0000000000001fc0 ZS: Empty ZT: SWR ZA: 0 1185 | ****************** END ******************* 1186 | Writing Data to Buffer ... 1187 | Writing Buffer to the first LBA of the first Zone ... 1188 | Finish Writing! 1189 | Reading Data from the first LBA of the first Zone ... 1190 | Finish Reading, Data is: Hello Miracle! 1191 | ``` 1192 | 1193 | ## 实验结论和心得体会 1194 | 1195 | 本次实验编译安装了SPDK;通过运行并分析NVME hello world源码学习了SPDK基本原理;并在此基础上修改hello world,实现了zns命令I/O读写。 1196 | -------------------------------------------------------------------------------- /work/task2/README.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task2/README.pdf -------------------------------------------------------------------------------- /work/task2/hello_miracle/.gitignore: -------------------------------------------------------------------------------- 1 | hello_miracle 2 | # Prerequisites 3 | *.d 4 | 5 | # Object files 6 | *.o 7 | *.ko 8 | *.obj 9 | *.elf 10 | 11 | # Linker output 12 | *.ilk 13 | *.map 14 | *.exp 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | *.la 24 | *.lo 25 | 26 | # Shared objects (inc. Windows DLLs) 27 | *.dll 28 | *.so 29 | *.so.* 30 | *.dylib 31 | 32 | # Executables 33 | *.exe 34 | *.out 35 | *.app 36 | *.i*86 37 | *.x86_64 38 | *.hex 39 | 40 | # Debug files 41 | *.dSYM/ 42 | *.su 43 | *.idb 44 | *.pdb 45 | 46 | # Kernel Module Compile Results 47 | *.mod* 48 | *.cmd 49 | .tmp_versions/ 50 | modules.order 51 | Module.symvers 52 | Mkfile.old 53 | dkms.conf 54 | -------------------------------------------------------------------------------- /work/task2/hello_miracle/Makefile: -------------------------------------------------------------------------------- 1 | SPDK_ROOT_DIR := /home/miracle/work/spdk 2 | 3 | APP = hello_miracle 4 | 5 | include $(SPDK_ROOT_DIR)/mk/nvme.libtest.mk 6 | 7 | run: all 8 | @ rm -f hello_miracle.d hello_miracle.o 9 | @ sudo ./hello_miracle -------------------------------------------------------------------------------- /work/task2/hello_miracle/hello_miracle.c: -------------------------------------------------------------------------------- 1 | #include "spdk/stdinc.h" 2 | #include "spdk/nvme.h" 3 | #include "spdk/vmd.h" 4 | #include "spdk/nvme_zns.h" 5 | #include "spdk/env.h" 6 | #include "spdk/string.h" 7 | #include "spdk/log.h" 8 | 9 | struct ctrlr_entry 10 | { 11 | struct spdk_nvme_ctrlr *ctrlr; 12 | TAILQ_ENTRY(ctrlr_entry) link; 13 | char name[1024]; 14 | }; 15 | 16 | struct ns_entry 17 | { 18 | struct spdk_nvme_ctrlr *ctrlr; 19 | struct spdk_nvme_ns *ns; 20 | TAILQ_ENTRY(ns_entry) link; 21 | struct spdk_nvme_qpair *qpair; 22 | }; 23 | 24 | struct my_sequence 25 | { 26 | struct ns_entry *ns_entry; 27 | char *buf; 28 | unsigned using_cmb_io; 29 | int is_completed; 30 | }; 31 | 32 | static TAILQ_HEAD(, ctrlr_entry) g_controllers = TAILQ_HEAD_INITIALIZER(g_controllers); 33 | static TAILQ_HEAD(, ns_entry) g_namespaces = TAILQ_HEAD_INITIALIZER(g_namespaces); 34 | static struct spdk_nvme_transport_id g_trid = {}; 35 | 36 | static bool g_vmd = false; 37 | 38 | static void register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns) 39 | { 40 | struct ns_entry *entry; 41 | 42 | if (!spdk_nvme_ns_is_active(ns)) 43 | { 44 | return; 45 | } 46 | 47 | entry = malloc(sizeof(struct ns_entry)); 48 | if (entry == NULL) 49 | { 50 | perror("ns_entry malloc"); 51 | exit(1); 52 | } 53 | 54 | entry->ctrlr = ctrlr; 55 | entry->ns = ns; 56 | TAILQ_INSERT_TAIL(&g_namespaces, entry, link); 57 | 58 | printf(" Namespace ID: %d size: %juGB\n", spdk_nvme_ns_get_id(ns), 59 | spdk_nvme_ns_get_size(ns) / 1000000000); 60 | } 61 | 62 | static bool probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, struct spdk_nvme_ctrlr_opts *opts) 63 | { 64 | printf("Attaching to %s\n", trid->traddr); 65 | return true; 66 | } 67 | 68 | static void attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts) 69 | { 70 | int nsid; 71 | struct ctrlr_entry *entry; 72 | struct spdk_nvme_ns *ns; 73 | const struct spdk_nvme_ctrlr_data *cdata; 74 | 75 | entry = malloc(sizeof(struct ctrlr_entry)); 76 | if (entry == NULL) 77 | { 78 | perror("ctrlr_entry malloc"); 79 | exit(1); 80 | } 81 | 82 | printf("Attached to %s\n", trid->traddr); 83 | 84 | cdata = spdk_nvme_ctrlr_get_data(ctrlr); 85 | 86 | snprintf(entry->name, sizeof(entry->name), "%-20.20s (%-20.20s)", cdata->mn, cdata->sn); 87 | 88 | entry->ctrlr = ctrlr; 89 | TAILQ_INSERT_TAIL(&g_controllers, entry, link); 90 | 91 | for (nsid = spdk_nvme_ctrlr_get_first_active_ns(ctrlr); nsid != 0; 92 | nsid = spdk_nvme_ctrlr_get_next_active_ns(ctrlr, nsid)) 93 | { 94 | ns = spdk_nvme_ctrlr_get_ns(ctrlr, nsid); 95 | if (ns == NULL) 96 | { 97 | continue; 98 | } 99 | register_ns(ctrlr, ns); 100 | } 101 | } 102 | 103 | static void cleanup(void) 104 | { 105 | struct ns_entry *ns_entry, *tmp_ns_entry; 106 | struct ctrlr_entry *ctrlr_entry, *tmp_ctrlr_entry; 107 | struct spdk_nvme_detach_ctx *detach_ctx = NULL; 108 | 109 | TAILQ_FOREACH_SAFE(ns_entry, &g_namespaces, link, tmp_ns_entry) 110 | { 111 | TAILQ_REMOVE(&g_namespaces, ns_entry, link); 112 | free(ns_entry); 113 | } 114 | 115 | TAILQ_FOREACH_SAFE(ctrlr_entry, &g_controllers, link, tmp_ctrlr_entry) 116 | { 117 | TAILQ_REMOVE(&g_controllers, ctrlr_entry, link); 118 | spdk_nvme_detach_async(ctrlr_entry->ctrlr, &detach_ctx); 119 | free(ctrlr_entry); 120 | } 121 | 122 | if (detach_ctx) 123 | { 124 | spdk_nvme_detach_poll(detach_ctx); 125 | } 126 | } 127 | 128 | static void usage(const char *program_name) 129 | { 130 | printf("%s [options]", program_name); 131 | printf("\t\n"); 132 | printf("options:\n"); 133 | printf("\t[-d DPDK huge memory size in MB]\n"); 134 | printf("\t[-g use single file descriptor for DPDK memory segments]\n"); 135 | printf("\t[-i shared memory group ID]\n"); 136 | printf("\t[-r remote NVMe over Fabrics target address]\n"); 137 | printf("\t[-V enumerate VMD]\n"); 138 | #ifdef DEBUG 139 | printf("\t[-L enable debug logging]\n"); 140 | #else 141 | printf("\t[-L enable debug logging (flag disabled, must reconfigure with --enable-debug)]\n"); 142 | #endif 143 | } 144 | 145 | static int parse_args(int argc, char **argv, struct spdk_env_opts *env_opts) 146 | { 147 | int op, rc; 148 | 149 | spdk_nvme_trid_populate_transport(&g_trid, SPDK_NVME_TRANSPORT_PCIE); 150 | snprintf(g_trid.subnqn, sizeof(g_trid.subnqn), "%s", SPDK_NVMF_DISCOVERY_NQN); 151 | 152 | while ((op = getopt(argc, argv, "d:gi:r:L:V")) != -1) 153 | { 154 | switch (op) 155 | { 156 | case 'V': 157 | g_vmd = true; 158 | break; 159 | case 'i': 160 | env_opts->shm_id = spdk_strtol(optarg, 10); 161 | if (env_opts->shm_id < 0) 162 | { 163 | fprintf(stderr, "Invalid shared memory ID\n"); 164 | return env_opts->shm_id; 165 | } 166 | break; 167 | case 'g': 168 | env_opts->hugepage_single_segments = true; 169 | break; 170 | case 'r': 171 | if (spdk_nvme_transport_id_parse(&g_trid, optarg) != 0) 172 | { 173 | fprintf(stderr, "Error parsing transport address\n"); 174 | return 1; 175 | } 176 | break; 177 | case 'd': 178 | env_opts->mem_size = spdk_strtol(optarg, 10); 179 | if (env_opts->mem_size < 0) 180 | { 181 | fprintf(stderr, "Invalid DPDK memory size\n"); 182 | return env_opts->mem_size; 183 | } 184 | break; 185 | case 'L': 186 | rc = spdk_log_set_flag(optarg); 187 | if (rc < 0) 188 | { 189 | fprintf(stderr, "unknown flag\n"); 190 | usage(argv[0]); 191 | exit(EXIT_FAILURE); 192 | } 193 | #ifdef DEBUG 194 | spdk_log_set_print_level(SPDK_LOG_DEBUG); 195 | #endif 196 | break; 197 | default: 198 | usage(argv[0]); 199 | return 1; 200 | } 201 | } 202 | 203 | return 0; 204 | } 205 | 206 | static void reset_zone_complete(void *arg, const struct spdk_nvme_cpl *completion) 207 | { 208 | struct my_sequence *sequence = arg; 209 | 210 | sequence->is_completed = 1; 211 | if (spdk_nvme_cpl_is_error(completion)) { 212 | spdk_nvme_qpair_print_completion(sequence->ns_entry->qpair, (struct spdk_nvme_cpl *)completion); 213 | fprintf(stderr, "I/O error status: %s\n", spdk_nvme_cpl_get_status_string(&completion->status)); 214 | fprintf(stderr, "Reset zone I/O failed, aborting run\n"); 215 | sequence->is_completed = 2; 216 | exit(1); 217 | } 218 | } 219 | 220 | static void reset_zone_and_wait_for_completion(struct my_sequence *sequence) 221 | { 222 | if (spdk_nvme_zns_reset_zone(sequence->ns_entry->ns, sequence->ns_entry->qpair, 223 | 0, /* starting LBA of the zone to reset */ 224 | false, /* don't reset all zones */ 225 | reset_zone_complete, 226 | sequence)) { 227 | fprintf(stderr, "starting reset zone I/O failed\n"); 228 | exit(1); 229 | } 230 | while (!sequence->is_completed) { 231 | spdk_nvme_qpair_process_completions(sequence->ns_entry->qpair, 0); 232 | } 233 | sequence->is_completed = 0; 234 | } 235 | 236 | bool io_completed; 237 | static void check_completion(void *arg, const struct spdk_nvme_cpl *cpl) 238 | { 239 | if (spdk_nvme_cpl_is_error(cpl)) 240 | { 241 | printf("I/O Option Failed\n"); 242 | } 243 | io_completed = true; 244 | } 245 | 246 | static void print_zns_zone(uint8_t *report, uint32_t index, uint32_t zdes) 247 | { 248 | struct spdk_nvme_zns_zone_desc *desc; 249 | uint32_t i, zds, zrs, zd_index; 250 | 251 | zrs = sizeof(struct spdk_nvme_zns_zone_report); 252 | zds = sizeof(struct spdk_nvme_zns_zone_desc); 253 | zd_index = zrs + index * (zds + zdes); 254 | 255 | desc = (struct spdk_nvme_zns_zone_desc *)(report + zd_index); 256 | 257 | printf("ZSLBA: 0x%016"PRIx64" ZCAP: 0x%016"PRIx64" WP: 0x%016"PRIx64" ZS: ", desc->zslba, 258 | desc->zcap, desc->wp); 259 | switch (desc->zs) { 260 | case SPDK_NVME_ZONE_STATE_EMPTY: 261 | printf("Empty"); 262 | break; 263 | case SPDK_NVME_ZONE_STATE_IOPEN: 264 | printf("Implicit open"); 265 | break; 266 | case SPDK_NVME_ZONE_STATE_EOPEN: 267 | printf("Explicit open"); 268 | break; 269 | case SPDK_NVME_ZONE_STATE_CLOSED: 270 | printf("Closed"); 271 | break; 272 | case SPDK_NVME_ZONE_STATE_RONLY: 273 | printf("Read only"); 274 | break; 275 | case SPDK_NVME_ZONE_STATE_FULL: 276 | printf("Full"); 277 | break; 278 | case SPDK_NVME_ZONE_STATE_OFFLINE: 279 | printf("Offline"); 280 | break; 281 | default: 282 | printf("Reserved"); 283 | } 284 | printf(" ZT: %s ZA: %x\n", (desc->zt == SPDK_NVME_ZONE_TYPE_SEQWR) ? "SWR" : "Reserved", 285 | desc->za.raw); 286 | 287 | if (!desc->za.bits.zdev) { 288 | return; 289 | } 290 | 291 | for (i = 0; i < zdes; i += 8) { 292 | printf("zone_desc_ext[%d] : 0x%"PRIx64"\n", i, 293 | *(uint64_t *)(report + zd_index + zds + i)); 294 | } 295 | } 296 | 297 | static void hello_miracle(void) 298 | { 299 | struct ns_entry *ns_entry; 300 | struct my_sequence sequence; 301 | int rc; 302 | size_t sz; 303 | 304 | ns_entry = g_namespaces.tqh_first; 305 | ns_entry->qpair = spdk_nvme_ctrlr_alloc_io_qpair(ns_entry->ctrlr, NULL, 0); 306 | if (ns_entry->qpair == NULL) { 307 | printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n"); 308 | return; 309 | } 310 | sequence.using_cmb_io = 1; 311 | sequence.buf = spdk_nvme_ctrlr_map_cmb(ns_entry->ctrlr, &sz); 312 | if (sequence.buf == NULL || sz < 0x1000) { 313 | sequence.using_cmb_io = 0; 314 | sequence.buf = spdk_zmalloc(0x1000, 0x1000, NULL, SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA); 315 | } 316 | if (sequence.buf == NULL) { 317 | printf("ERROR: write buffer allocation failed\n"); 318 | return; 319 | } 320 | if (sequence.using_cmb_io) { 321 | printf("INFO: using controller memory buffer for IO\n"); 322 | } else { 323 | printf("INFO: using host memory buffer for IO\n"); 324 | } 325 | sequence.is_completed = 0; 326 | sequence.ns_entry = ns_entry; 327 | 328 | reset_zone_and_wait_for_completion(&sequence); 329 | 330 | 331 | /** 332 | * @brief zns ssd info 333 | * 334 | */ 335 | uint64_t num_zones = spdk_nvme_zns_ns_get_num_zones(ns_entry->ns); 336 | uint64_t zone_size = spdk_nvme_zns_ns_get_zone_size(ns_entry->ns); 337 | uint32_t zone_append_size_limit = spdk_nvme_zns_ctrlr_get_max_zone_append_size(ns_entry->ctrlr); 338 | const struct spdk_nvme_ns_data *ref_ns_data = spdk_nvme_ns_get_data(ns_entry->ns); 339 | const struct spdk_nvme_zns_ns_data *ref_ns_zns_data = spdk_nvme_zns_ns_get_data(ns_entry->ns); 340 | printf("************ NVMe Information ************\n"); 341 | printf("Number of Zone: %lu\n", num_zones); 342 | printf("Size of LBA: %lu\n", ref_ns_data->nsze); 343 | printf("Size of Zone: %lu (%lu * %lu)\n", zone_size, ref_ns_zns_data->lbafe->zsze, ref_ns_data->nsze); 344 | printf("Append Size Limit of Zone: %u\n", zone_append_size_limit); 345 | printf("****************** END *******************\n"); 346 | 347 | uint8_t *report_buf; 348 | size_t report_buf_size; 349 | uint64_t nr_zones = 0; 350 | uint64_t max_zones_per_buf; 351 | uint32_t zds, zrs, zd_index; 352 | size_t zdes = 0; 353 | 354 | zrs = sizeof(struct spdk_nvme_zns_zone_report); 355 | zds = sizeof(struct spdk_nvme_zns_zone_desc); 356 | 357 | report_buf_size = spdk_nvme_ns_get_max_io_xfer_size(ns_entry->ns); 358 | report_buf = calloc(1, report_buf_size); 359 | if (!report_buf) 360 | { 361 | printf("Zone report allocation failed!\n"); 362 | return; 363 | } 364 | memset(report_buf, 0, report_buf_size); 365 | 366 | 367 | max_zones_per_buf = (report_buf_size - zrs) / zds; 368 | rc = spdk_nvme_zns_report_zones(ns_entry->ns, ns_entry->qpair, report_buf, report_buf_size, 0, SPDK_NVME_ZRA_LIST_ALL, true, check_completion, NULL); 369 | if (rc) 370 | { 371 | fprintf(stderr, "Report zones failed\n"); 372 | return; 373 | } 374 | 375 | io_completed = false; 376 | while (!io_completed) 377 | { 378 | spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); 379 | } 380 | nr_zones = report_buf[0]; 381 | if (nr_zones > max_zones_per_buf) 382 | { 383 | fprintf(stderr, "nr_zones too big\n"); 384 | return; 385 | } 386 | if (!nr_zones) 387 | { 388 | return; 389 | } 390 | 391 | printf("************ Zone Information ************\n"); 392 | uint32_t i; 393 | for (i = 0; i < nr_zones && i < num_zones; ++ i) 394 | { 395 | print_zns_zone(report_buf, i, zdes); 396 | } 397 | printf("****************** END *******************\n"); 398 | 399 | struct spdk_nvme_zns_zone_desc *first_zone_info; 400 | zd_index = zrs + 0 * (zds + zdes); 401 | first_zone_info = (struct spdk_nvme_zns_zone_desc *)(report_buf + zd_index); 402 | 403 | printf("Writing Data to Buffer ...\n"); 404 | snprintf(sequence.buf, 0x1000, "%s", "Hello Miracle!\n"); 405 | printf("Writing Buffer to the first LBA of the first Zone ...\n"); 406 | 407 | io_completed = false; 408 | rc = spdk_nvme_zns_zone_append(ns_entry->ns, ns_entry->qpair, sequence.buf, first_zone_info->zslba, 1, check_completion, NULL, 0); 409 | if (rc != 0) { 410 | fprintf(stderr, "starting write I/O failed\n"); 411 | exit(1); 412 | } 413 | 414 | while (!io_completed) { 415 | spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); 416 | } 417 | 418 | printf("Finish Writing!\n"); 419 | printf("Reading Data from the first LBA of the first Zone ...\n"); 420 | 421 | spdk_free(sequence.buf); 422 | sequence.buf = spdk_zmalloc(0x1000, 0x1000, NULL, SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA); 423 | 424 | io_completed = false; 425 | rc = spdk_nvme_ns_cmd_read(ns_entry->ns, ns_entry->qpair, sequence.buf, first_zone_info->zslba, 1, check_completion, NULL, 0); 426 | if (rc != 0) { 427 | fprintf(stderr, "starting read I/O failed\n"); 428 | exit(1); 429 | } 430 | 431 | while (!io_completed) { 432 | spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); 433 | } 434 | 435 | printf("Finish Reading, Data is: %s", sequence.buf); 436 | spdk_free(sequence.buf); 437 | free(report_buf); 438 | 439 | spdk_nvme_ctrlr_free_io_qpair(ns_entry->qpair); 440 | } 441 | 442 | int main(int argc, char **argv) 443 | { 444 | int rc; 445 | struct spdk_env_opts opts; 446 | 447 | spdk_env_opts_init(&opts); 448 | rc = parse_args(argc, argv, &opts); 449 | if (rc != 0) 450 | { 451 | return rc; 452 | } 453 | 454 | opts.name = "hello_miracle"; 455 | if (spdk_env_init(&opts) < 0) 456 | { 457 | fprintf(stderr, "Unable to initialize SPDK env\n"); 458 | return 1; 459 | } 460 | printf("Initializing NVMe Controllers\n"); 461 | 462 | if (g_vmd && spdk_vmd_init()) 463 | { 464 | fprintf(stderr, "Failed to initialize VMD." 465 | " Some NVMe devices can be unavailable.\n"); 466 | } 467 | 468 | rc = spdk_nvme_probe(&g_trid, NULL, probe_cb, attach_cb, NULL); 469 | if (rc != 0) 470 | { 471 | fprintf(stderr, "spdk_nvme_probe() failed\n"); 472 | rc = 1; 473 | goto exit; 474 | } 475 | 476 | if (TAILQ_EMPTY(&g_controllers)) 477 | { 478 | fprintf(stderr, "no NVMe controllers found\n"); 479 | rc = 1; 480 | goto exit; 481 | } 482 | 483 | printf("Initialization complete.\n"); 484 | 485 | hello_miracle(); 486 | // test(); 487 | 488 | cleanup(); 489 | 490 | if (g_vmd) 491 | { 492 | spdk_vmd_fini(); 493 | } 494 | 495 | exit: 496 | cleanup(); 497 | spdk_env_fini(); 498 | return rc; 499 | } -------------------------------------------------------------------------------- /work/task2/计算机系统综合实验二.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task2/计算机系统综合实验二.pptx -------------------------------------------------------------------------------- /work/task3/README.assets/6371f9c5ae7ee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task3/README.assets/6371f9c5ae7ee.png -------------------------------------------------------------------------------- /work/task3/README.assets/image-20221115171945500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task3/README.assets/image-20221115171945500.png -------------------------------------------------------------------------------- /work/task3/README.md: -------------------------------------------------------------------------------- 1 | # 实验三、bdev原理和源码分析 2 | 3 | ## 实验目的 4 | 5 | - 学习bdeb原理和基本接口操作 6 | 7 | ## 实验内容 8 | 9 | - 配置bdev运行环境 10 | - 运行hello_bdev程序并分析源码 11 | - 通过bdev接口写入数据并读取 12 | 13 | ## 实验过程和步骤 14 | 15 | ### 运行hello_bdev示例 16 | #### 启动虚拟机 17 | ```bash 18 | ./start.sh ssd 19 | ``` 20 | 21 | #### 初始化环境 22 | ```bash 23 | sudo scripts/setup.sh 24 | ``` 25 | #### 生成配置文件 26 | ```bash 27 | ./scripts/gen_nvme.sh --json-with-subsystems > ./build/examples/nvme.json 28 | ``` 29 | #### 运行hello_bdev 30 | ```bash 31 | cd build/examples/ 32 | sudo ./hello_bdev -c nvme.json -b Nvme0n1 33 | ``` 34 | ![file](./README.assets/6371f9c5ae7ee.png) 35 | 36 | ### 分析hello_bdev.c源码 37 | #### `main()` 主函数 38 | ```c 39 | int main(int argc, char **argv) 40 | { 41 | struct spdk_app_opts opts = {}; 42 | int rc = 0; 43 | struct hello_context_t hello_context = {}; 44 | 45 | /* Set default values in opts structure. */ 46 | spdk_app_opts_init(&opts, sizeof(opts)); 47 | opts.name = "hello_bdev"; 48 | 49 | /* 50 | * Parse built-in SPDK command line parameters as well 51 | * as our custom one(s). 52 | */ 53 | if ((rc = spdk_app_parse_args(argc, argv, &opts, "b:", NULL, hello_bdev_parse_arg, 54 | hello_bdev_usage)) != SPDK_APP_PARSE_ARGS_SUCCESS) { 55 | exit(rc); 56 | } 57 | hello_context.bdev_name = g_bdev_name; 58 | 59 | /* 60 | * spdk_app_start() will initialize the SPDK framework, call hello_start(), 61 | * and then block until spdk_app_stop() is called (or if an initialization 62 | * error occurs, spdk_app_start() will return with rc even without calling 63 | * hello_start(). 64 | */ 65 | rc = spdk_app_start(&opts, hello_start, &hello_context); 66 | if (rc) { 67 | SPDK_ERRLOG("ERROR starting application\n"); 68 | } 69 | 70 | /* At this point either spdk_app_stop() was called, or spdk_app_start() 71 | * failed because of internal error. 72 | */ 73 | 74 | /* When the app stops, free up memory that we allocated. */ 75 | spdk_dma_free(hello_context.buff); 76 | 77 | /* Gracefully close out all of the SPDK subsystems. */ 78 | spdk_app_fini(); 79 | return rc; 80 | } 81 | ``` 82 | 主要流程为 83 | ```mermaid 84 | graph TB; 85 | 1["spdk_app_opts_init(&opts, sizeof(opts))"] --> 2["spdk_app_parse_args()"] --> 3["rc = spdk_app_start(&opts, hello_start, &hello_context)"] --> 4["spdk_dma_free(hello_context.buff)"] --> 5["spdk_app_fini()"] 86 | ``` 87 | 其中: 88 | - `spdk_app_opts_init()` 初始化opts参数 89 | - `spdk_app_parse_args()` 载入spdk默认参数和用户自定义参数(如:`-c nvme.json` 等) 90 | - `rc = spdk_app_start(&opts, hello_start, &hello_context)` 载入SPDK框架,调用 `hello_start` 函数,并在调用 `spdk_app_stop()` 后返回状态值 91 | - `spdk_dma_free()` 释放分配的空间 92 | - `spdk_app_fini()` 关闭所有SPDK子系统 93 | 94 | #### `hello_start()` 主任务函数 95 | ```c 96 | static void hello_start(void *arg1) 97 | { 98 | struct hello_context_t *hello_context = arg1; 99 | uint32_t buf_align; 100 | int rc = 0; 101 | hello_context->bdev = NULL; 102 | hello_context->bdev_desc = NULL; 103 | 104 | SPDK_NOTICELOG("Successfully started the application\n"); 105 | 106 | /* 107 | * There can be many bdevs configured, but this application will only use 108 | * the one input by the user at runtime. 109 | * 110 | * Open the bdev by calling spdk_bdev_open_ext() with its name. 111 | * The function will return a descriptor 112 | */ 113 | SPDK_NOTICELOG("Opening the bdev %s\n", hello_context->bdev_name); 114 | rc = spdk_bdev_open_ext(hello_context->bdev_name, true, hello_bdev_event_cb, NULL, 115 | &hello_context->bdev_desc); 116 | if (rc) { 117 | SPDK_ERRLOG("Could not open bdev: %s\n", hello_context->bdev_name); 118 | spdk_app_stop(-1); 119 | return; 120 | } 121 | 122 | /* A bdev pointer is valid while the bdev is opened. */ 123 | hello_context->bdev = spdk_bdev_desc_get_bdev(hello_context->bdev_desc); 124 | 125 | 126 | SPDK_NOTICELOG("Opening io channel\n"); 127 | /* Open I/O channel */ 128 | hello_context->bdev_io_channel = spdk_bdev_get_io_channel(hello_context->bdev_desc); 129 | if (hello_context->bdev_io_channel == NULL) { 130 | SPDK_ERRLOG("Could not create bdev I/O channel!!\n"); 131 | spdk_bdev_close(hello_context->bdev_desc); 132 | spdk_app_stop(-1); 133 | return; 134 | } 135 | 136 | /* Allocate memory for the write buffer. 137 | * Initialize the write buffer with the string "Hello World!" 138 | */ 139 | hello_context->buff_size = spdk_bdev_get_block_size(hello_context->bdev) * 140 | spdk_bdev_get_write_unit_size(hello_context->bdev); 141 | buf_align = spdk_bdev_get_buf_align(hello_context->bdev); 142 | hello_context->buff = spdk_dma_zmalloc(hello_context->buff_size, buf_align, NULL); 143 | if (!hello_context->buff) { 144 | SPDK_ERRLOG("Failed to allocate buffer\n"); 145 | spdk_put_io_channel(hello_context->bdev_io_channel); 146 | spdk_bdev_close(hello_context->bdev_desc); 147 | spdk_app_stop(-1); 148 | return; 149 | } 150 | snprintf(hello_context->buff, hello_context->buff_size, "%s", "Hello World!\n"); 151 | 152 | if (spdk_bdev_is_zoned(hello_context->bdev)) { 153 | hello_reset_zone(hello_context); 154 | /* If bdev is zoned, the callback, reset_zone_complete, will call hello_write() */ 155 | return; 156 | } 157 | 158 | hello_write(hello_context); 159 | } 160 | ``` 161 | 主要流程为 162 | ```mermaid 163 | graph TB; 164 | 1["rc = spdk_bdev_open_ext()"] --> 2["hello_context->bdev = spdk_bdev_desc_get_bdev()"] --> 3["hello_context->bdev_io_channel = spdk_bdev_get_io_channel()"] --> 4["hello_context->buff_size=... , hello_context->buff=... , snprintf(...)"] --> 5["hello_write()"] 165 | ``` 166 | 其中: 167 | - `spdk_bdev_open_ext()` 通过设备名(如运行程序是附加的参数 `-b Nvme0n1`)打开bdev,返回一个descriptor 168 | - `spdk_bdev_desc_get_bdev()` 通过descriptor获取bdev指针 169 | - `spdk_bdev_get_io_channel()` 通过descriptor获取I/O通道 170 | - `hello_context->buff_size=... , hello_context->buff=... , snprintf(...)` 为写入buffer分配空间并写入字符串 171 | - `hello_write()` 调用写入函数 172 | 173 | #### `hello_write()` 写入函数 174 | 下面对写入函数进行分析 175 | ```c 176 | static void hello_write(void *arg) 177 | { 178 | struct hello_context_t *hello_context = arg; 179 | int rc = 0; 180 | 181 | SPDK_NOTICELOG("Writing to the bdev\n"); 182 | rc = spdk_bdev_write(hello_context->bdev_desc, hello_context->bdev_io_channel, 183 | hello_context->buff, 0, hello_context->buff_size, write_complete, 184 | hello_context); 185 | 186 | if (rc == -ENOMEM) { 187 | SPDK_NOTICELOG("Queueing io\n"); 188 | /* In case we cannot perform I/O now, queue I/O */ 189 | hello_context->bdev_io_wait.bdev = hello_context->bdev; 190 | hello_context->bdev_io_wait.cb_fn = hello_write; 191 | hello_context->bdev_io_wait.cb_arg = hello_context; 192 | spdk_bdev_queue_io_wait(hello_context->bdev, hello_context->bdev_io_channel, 193 | &hello_context->bdev_io_wait); 194 | } else if (rc) { 195 | SPDK_ERRLOG("%s error while writing to bdev: %d\n", spdk_strerror(-rc), rc); 196 | spdk_put_io_channel(hello_context->bdev_io_channel); 197 | spdk_bdev_close(hello_context->bdev_desc); 198 | spdk_app_stop(-1); 199 | } 200 | } 201 | ``` 202 | 主要流程为 203 | ```mermaid 204 | graph TB; 205 | 1["rc = spdk_bdev_write()"] --> 2{"rc == ?"} 206 | 2 --> |0| r1["Success"] --> exit 207 | 2 --> |"-EINVAL(-22)"| r2["offset and/or nbytes are not aligned or out of range"] --> err_1 208 | 2 --> |"-ENOMEM(-12)"| r3_1["spdk_bdev_io buffer cannot be allocated"] --> r3_2["spdk_bdev_queue_io_wait()"] --> exit 209 | 2 --> |"-EBADF(-9)"| r4["desc not open for writing"] --> err_1 210 | err_1["spdk_put_io_channel()"] --> err_2["spdk_bdev_close()"] --> err_3["spdk_app_stop(-1)"] 211 | exit["return"] 212 | ``` 213 | 其中: 214 | - `spdk_bdev_write()` 向bdev写入,并在完成后调用相应回调函数 215 | - `spdk_bdev_queue_io_wait()` 加入I/O等待队列 216 | - `spdk_put_io_channel()` 释放I/O通道,并在释放最后一个通道后调用销毁回调函数 217 | - `spdk_bdev_close()` 关闭块设备(bdev,block device) 218 | 219 | #### `write_complete()` 写入回调函数 220 | ```c 221 | static void write_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 222 | { 223 | struct hello_context_t *hello_context = cb_arg; 224 | 225 | /* Complete the I/O */ 226 | spdk_bdev_free_io(bdev_io); 227 | 228 | if (success) { 229 | SPDK_NOTICELOG("bdev io write completed successfully\n"); 230 | } else { 231 | SPDK_ERRLOG("bdev io write error: %d\n", EIO); 232 | spdk_put_io_channel(hello_context->bdev_io_channel); 233 | spdk_bdev_close(hello_context->bdev_desc); 234 | spdk_app_stop(-1); 235 | return; 236 | } 237 | 238 | /* Zero the buffer so that we can use it for reading */ 239 | memset(hello_context->buff, 0, hello_context->buff_size); 240 | 241 | hello_read(hello_context); 242 | } 243 | ``` 244 | 主要流程为 245 | ```mermaid 246 | graph TB; 247 | 1["spdk_bdev_free_io(bdev_io)"] --> 2["memset(hello_context->buff, 0, hello_context->buff_size)"] --> 3["hello_read(hello_context)"] 248 | ``` 249 | 其中: 250 | - `spdk_bdev_free_io()` Free an I/O request 251 | - `memset(...)` 将buffer置为0,便于后续存储读取的数据 252 | - `hello_read()` 调用读取函数 253 | 254 | #### `hello_read()` 读取函数 255 | ```c 256 | static void hello_read(void *arg) 257 | { 258 | struct hello_context_t *hello_context = arg; 259 | int rc = 0; 260 | 261 | SPDK_NOTICELOG("Reading io\n"); 262 | rc = spdk_bdev_read(hello_context->bdev_desc, hello_context->bdev_io_channel, 263 | hello_context->buff, 0, hello_context->buff_size, read_complete, 264 | hello_context); 265 | 266 | if (rc == -ENOMEM) { 267 | SPDK_NOTICELOG("Queueing io\n"); 268 | /* In case we cannot perform I/O now, queue I/O */ 269 | hello_context->bdev_io_wait.bdev = hello_context->bdev; 270 | hello_context->bdev_io_wait.cb_fn = hello_read; 271 | hello_context->bdev_io_wait.cb_arg = hello_context; 272 | spdk_bdev_queue_io_wait(hello_context->bdev, hello_context->bdev_io_channel, 273 | &hello_context->bdev_io_wait); 274 | } else if (rc) { 275 | SPDK_ERRLOG("%s error while reading from bdev: %d\n", spdk_strerror(-rc), rc); 276 | spdk_put_io_channel(hello_context->bdev_io_channel); 277 | spdk_bdev_close(hello_context->bdev_desc); 278 | spdk_app_stop(-1); 279 | } 280 | } 281 | ``` 282 | 主要流程为 283 | ```mermaid 284 | graph TB; 285 | 1["rc = spdk_bdev_read()"] --> 2{"rc == ?"} 286 | 2 --> |0| r1["Success"] --> exit 287 | 2 --> |"-EINVAL(-22)"| r2["offset and/or nbytes are not aligned or out of range"] --> err_1 288 | 2 --> |"-ENOMEM(-12)"| r3_1["spdk_bdev_io buffer cannot be allocated"] --> r3_2["spdk_bdev_queue_io_wait()"] --> exit 289 | err_1["spdk_put_io_channel()"] --> err_2["spdk_bdev_close()"] --> err_3["spdk_app_stop(-1)"] 290 | exit["return"] 291 | ``` 292 | 其中: 293 | - `spdk_bdev_write()` 从bdev读取,并在完成后调用相应回调函数 294 | 295 | #### `read_complete()` 读取回调函数 296 | ```c 297 | static void read_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 298 | { 299 | struct hello_context_t *hello_context = cb_arg; 300 | 301 | if (success) { 302 | SPDK_NOTICELOG("Read string from bdev : %s\n", hello_context->buff); 303 | } else { 304 | SPDK_ERRLOG("bdev io read error\n"); 305 | } 306 | 307 | /* Complete the bdev io and close the channel */ 308 | spdk_bdev_free_io(bdev_io); 309 | spdk_put_io_channel(hello_context->bdev_io_channel); 310 | spdk_bdev_close(hello_context->bdev_desc); 311 | SPDK_NOTICELOG("Stopping app\n"); 312 | spdk_app_stop(success ? 0 : -1); 313 | } 314 | ``` 315 | 主要流程为 316 | ```mermaid 317 | graph TB; 318 | 1["spdk_bdev_free_io()"] --> 2["spdk_put_io_channel()"] --> 3["spdk_bdev_close()"] --> 4["spdk_app_stop()"] 319 | ``` 320 | 321 | ### 修改hello_bdev.c,实现自定义字符串读写 322 | > 生成256KB字符串数据,修改hello_bdev.c源码将字符串数据通过bdev写入,之后再读取,验证结果是否正确 323 | 324 | #### `miracle_bdev.c` 325 | 326 | ```c 327 | #include "spdk/stdinc.h" 328 | #include "spdk/thread.h" 329 | #include "spdk/bdev.h" 330 | #include "spdk/env.h" 331 | #include "spdk/event.h" 332 | #include "spdk/log.h" 333 | #include "spdk/string.h" 334 | #include "spdk/bdev_zone.h" 335 | #include 336 | 337 | static char *g_bdev_name = "Nvme0n1"; 338 | 339 | const int DATA_LENGTH = 256*1024; 340 | 341 | struct my_context 342 | { 343 | struct spdk_bdev *bdev; 344 | struct spdk_bdev_desc *bdev_desc; 345 | struct spdk_io_channel *bdev_io_channel; 346 | char *buff; 347 | uint32_t buff_size; 348 | char *bdev_name; 349 | struct spdk_bdev_io_wait_entry bdev_io_wait; 350 | }; 351 | 352 | static char *generate_str(void) 353 | { 354 | char *str = (char *)malloc(DATA_LENGTH * 8); 355 | memset(str, 0, DATA_LENGTH*8); 356 | if (str) 357 | { 358 | int i; 359 | for (i = 0; i < DATA_LENGTH; ++ i) 360 | { 361 | str[i] = '0'+(i%10); 362 | } 363 | return str; 364 | } 365 | else 366 | { 367 | return NULL; 368 | } 369 | } 370 | 371 | static void save_data(const char *file_path, char *str) 372 | { 373 | FILE *fp = fopen(file_path, "w"); 374 | fprintf(fp, "%s", str); 375 | fclose(fp); 376 | } 377 | 378 | static int miracle_bdev_parse_arg(int ch, char *arg) 379 | { 380 | switch (ch) 381 | { 382 | case 'b': 383 | g_bdev_name = arg; 384 | break; 385 | default: 386 | return -EINVAL; 387 | } 388 | return 0; 389 | } 390 | 391 | static void miracle_bdev_usage(void) 392 | { 393 | printf(" -b name of the bdev to use\n"); 394 | } 395 | 396 | static void bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx) 397 | { 398 | SPDK_NOTICELOG("Unsupported bdev event: type %d\n", type); 399 | } 400 | 401 | static void read_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 402 | { 403 | struct my_context *p = cb_arg; 404 | 405 | if (success) 406 | { 407 | SPDK_NOTICELOG("Reading Successfully, Saveing to data.out!!\n"); 408 | save_data("./data.out", p->buff); 409 | } 410 | else 411 | { 412 | SPDK_ERRLOG("bdev io read error\n"); 413 | } 414 | 415 | spdk_bdev_free_io(bdev_io); 416 | spdk_put_io_channel(p->bdev_io_channel); 417 | spdk_bdev_close(p->bdev_desc); 418 | SPDK_NOTICELOG("Stopping app\n"); 419 | spdk_app_stop(success ? 0 : -1); 420 | } 421 | 422 | static void start_read(void *arg) 423 | { 424 | struct my_context *p = arg; 425 | int rc = 0; 426 | 427 | SPDK_NOTICELOG("Reading io\n"); 428 | rc = spdk_bdev_read(p->bdev_desc, p->bdev_io_channel, p->buff, 0, p->buff_size, read_complete, p); 429 | 430 | if (rc == -ENOMEM) 431 | { 432 | SPDK_NOTICELOG("Queueing io\n"); 433 | p->bdev_io_wait.bdev = p->bdev; 434 | p->bdev_io_wait.cb_fn = start_read; 435 | p->bdev_io_wait.cb_arg = p; 436 | spdk_bdev_queue_io_wait(p->bdev, p->bdev_io_channel, &p->bdev_io_wait); 437 | } 438 | else if (rc) 439 | { 440 | SPDK_ERRLOG("%s error while reading from bdev: %d\n", spdk_strerror(-rc), rc); 441 | spdk_put_io_channel(p->bdev_io_channel); 442 | spdk_bdev_close(p->bdev_desc); 443 | spdk_app_stop(-1); 444 | } 445 | } 446 | 447 | static void write_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 448 | { 449 | struct my_context *p = cb_arg; 450 | 451 | spdk_bdev_free_io(bdev_io); 452 | 453 | if (success) 454 | { 455 | SPDK_NOTICELOG("bdev io write completed successfully\n"); 456 | } 457 | else 458 | { 459 | SPDK_ERRLOG("bdev io write error: %d\n", EIO); 460 | spdk_put_io_channel(p->bdev_io_channel); 461 | spdk_bdev_close(p->bdev_desc); 462 | spdk_app_stop(-1); 463 | return; 464 | } 465 | 466 | memset(p->buff, 0, p->buff_size); 467 | start_read(p); 468 | } 469 | 470 | static void start_write(void *arg) 471 | { 472 | struct my_context *p = arg; 473 | int rc = 0; 474 | 475 | SPDK_NOTICELOG("Writing to the bdev\n"); 476 | rc = spdk_bdev_write(p->bdev_desc, p->bdev_io_channel, p->buff, 0, p->buff_size, write_complete, p); 477 | 478 | if (rc == -ENOMEM) 479 | { 480 | SPDK_NOTICELOG("Queueing io\n"); 481 | p->bdev_io_wait.bdev = p->bdev; 482 | p->bdev_io_wait.cb_fn = start_write; 483 | p->bdev_io_wait.cb_arg = p; 484 | spdk_bdev_queue_io_wait(p->bdev, p->bdev_io_channel, &p->bdev_io_wait); 485 | } 486 | else if (rc) 487 | { 488 | SPDK_ERRLOG("%s error while writing to bdev: %d\n", spdk_strerror(-rc), rc); 489 | spdk_put_io_channel(p->bdev_io_channel); 490 | spdk_bdev_close(p->bdev_desc); 491 | spdk_app_stop(-1); 492 | } 493 | } 494 | 495 | static void miracle_bdev(void *arg) 496 | { 497 | struct my_context *p = arg; 498 | uint32_t buf_align; 499 | uint32_t block_size; 500 | int rc = 0; 501 | p->bdev = NULL; 502 | p->bdev_desc = NULL; 503 | 504 | SPDK_NOTICELOG("Successfully started the application\n"); 505 | 506 | SPDK_NOTICELOG("Opening the bdev %s\n", p->bdev_name); 507 | rc = spdk_bdev_open_ext(p->bdev_name, true, bdev_event_cb, NULL, &p->bdev_desc); 508 | if (rc) 509 | { 510 | SPDK_ERRLOG("Could not open bdev: %s\n", p->bdev_name); 511 | spdk_app_stop(-1); 512 | return; 513 | } 514 | 515 | p->bdev = spdk_bdev_desc_get_bdev(p->bdev_desc); 516 | 517 | SPDK_NOTICELOG("Opening io channel\n"); 518 | p->bdev_io_channel = spdk_bdev_get_io_channel(p->bdev_desc); 519 | if (p->bdev_io_channel == NULL) 520 | { 521 | SPDK_ERRLOG("Could not create bdev I/O channel!!\n"); 522 | spdk_bdev_close(p->bdev_desc); 523 | spdk_app_stop(-1); 524 | return; 525 | } 526 | 527 | block_size = spdk_bdev_get_block_size(p->bdev); 528 | buf_align = spdk_bdev_get_buf_align(p->bdev); 529 | 530 | p->buff_size = ceil((double)(DATA_LENGTH+1)/block_size)*block_size; 531 | p->buff = spdk_dma_zmalloc(p->buff_size, buf_align, NULL); 532 | if (!p->buff) 533 | { 534 | SPDK_ERRLOG("Failed to allocate buffer\n"); 535 | spdk_put_io_channel(p->bdev_io_channel); 536 | spdk_bdev_close(p->bdev_desc); 537 | spdk_app_stop(-1); 538 | return; 539 | } 540 | 541 | SPDK_NOTICELOG("Generating Data\n"); 542 | char *str = generate_str(); 543 | if (str){ 544 | snprintf(p->buff, p->buff_size, "%s", str); 545 | free(str); 546 | SPDK_NOTICELOG("Saving Data to ./data.txt\n"); 547 | save_data("./data.in", p->buff); 548 | start_write(p); 549 | } 550 | else{ 551 | SPDK_ERRLOG("Could not generate data!!\n"); 552 | spdk_put_io_channel(p->bdev_io_channel); 553 | spdk_bdev_close(p->bdev_desc); 554 | spdk_app_stop(-1); 555 | return; 556 | } 557 | } 558 | 559 | int main(int argc, char **argv) 560 | { 561 | struct spdk_app_opts opts = {}; 562 | int rc = 0; 563 | struct my_context context = {}; 564 | 565 | spdk_app_opts_init(&opts, sizeof(opts)); 566 | opts.name = "miracle_bdev"; 567 | rc = spdk_app_parse_args(argc, argv, &opts, "b:", NULL, miracle_bdev_parse_arg, miracle_bdev_usage); 568 | if (rc != SPDK_APP_PARSE_ARGS_SUCCESS) 569 | { 570 | exit(rc); 571 | } 572 | context.bdev_name = g_bdev_name; 573 | 574 | rc = spdk_app_start(&opts, miracle_bdev, &context); 575 | if (rc) 576 | { 577 | SPDK_ERRLOG("ERROR starting applicatoin\n"); 578 | } 579 | 580 | spdk_dma_free(context.buff); 581 | spdk_app_fini(); 582 | 583 | return rc; 584 | } 585 | ``` 586 | 587 | #### `Makefile` 588 | ```makefile 589 | SPDK_ROOT_DIR := /home/miracle/work/task2/spdk 590 | include $(SPDK_ROOT_DIR)/mk/spdk.common.mk 591 | include $(SPDK_ROOT_DIR)/mk/spdk.modules.mk 592 | 593 | APP = miracle_bdev 594 | 595 | C_SRCS := miracle_bdev.c 596 | 597 | SPDK_LIB_LIST = $(ALL_MODULES_LIST) event event_bdev 598 | 599 | include $(SPDK_ROOT_DIR)/mk/spdk.app.mk 600 | 601 | run: all 602 | @ echo "Finished Compiling, Cleaning intermediate files" 603 | @ rm -f miracle_bdev.d miracle_bdev.o 604 | @ echo "Generating bdev-config" 605 | @ $(SPDK_ROOT_DIR)/scripts/gen_nvme.sh --json-with-subsystems > ./miracle_bdev.json 606 | @ echo "Generated bdev-config, Runing Program" 607 | @ sudo ./miracle_bdev -c ./miracle_bdev.json 608 | @ echo "Comparing Writing Data and Reading Data" 609 | @ echo "***************** Data Size *****************" 610 | @ du -h data.* 611 | @ echo "******************** End ********************" 612 | @ echo "Comparing Context ... (Note: Using [diff] command, empty output means no different)" 613 | @ diff data.in data.out 614 | ``` 615 | 616 | #### 运行结果 617 | ![image-20221115171945500](./README.assets/image-20221115171945500.png) 618 | 619 | ## 实验结论和心得体会 620 | 621 | 本次实验配置了bdev运行环境,运行并分析了hello_bdev程序,并最终独立编写实现了通过bdev接口的数据写入与读取。 -------------------------------------------------------------------------------- /work/task3/README.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task3/README.pdf -------------------------------------------------------------------------------- /work/task3/miracle_bdev/.gitignore: -------------------------------------------------------------------------------- 1 | miracle_bdev 2 | 3 | data.in 4 | data.out 5 | 6 | # Prerequisites 7 | *.d 8 | 9 | # Object files 10 | *.o 11 | *.ko 12 | *.obj 13 | *.elf 14 | 15 | # Linker output 16 | *.ilk 17 | *.map 18 | *.exp 19 | 20 | # Precompiled Headers 21 | *.gch 22 | *.pch 23 | 24 | # Libraries 25 | *.lib 26 | *.a 27 | *.la 28 | *.lo 29 | 30 | # Shared objects (inc. Windows DLLs) 31 | *.dll 32 | *.so 33 | *.so.* 34 | *.dylib 35 | 36 | # Executables 37 | *.exe 38 | *.out 39 | *.app 40 | *.i*86 41 | *.x86_64 42 | *.hex 43 | 44 | # Debug files 45 | *.dSYM/ 46 | *.su 47 | *.idb 48 | *.pdb 49 | 50 | # Kernel Module Compile Results 51 | *.mod* 52 | *.cmd 53 | .tmp_versions/ 54 | modules.order 55 | Module.symvers 56 | Mkfile.old 57 | dkms.conf 58 | -------------------------------------------------------------------------------- /work/task3/miracle_bdev/Makefile: -------------------------------------------------------------------------------- 1 | SPDK_ROOT_DIR := /home/miracle/work/spdk 2 | include $(SPDK_ROOT_DIR)/mk/spdk.common.mk 3 | include $(SPDK_ROOT_DIR)/mk/spdk.modules.mk 4 | 5 | APP = miracle_bdev 6 | 7 | C_SRCS := miracle_bdev.c 8 | 9 | SPDK_LIB_LIST = $(ALL_MODULES_LIST) event event_bdev 10 | 11 | include $(SPDK_ROOT_DIR)/mk/spdk.app.mk 12 | 13 | run: all 14 | @ echo "Finished Compiling, Cleaning intermediate files" 15 | @ rm -f miracle_bdev.d miracle_bdev.o 16 | @ echo "Generating bdev-config" 17 | @ $(SPDK_ROOT_DIR)/scripts/gen_nvme.sh --json-with-subsystems > ./miracle_bdev.json 18 | @ echo "Generated bdev-config, Runing Program" 19 | @ sudo ./miracle_bdev -c ./miracle_bdev.json 20 | @ echo "Comparing Writing Data and Reading Data" 21 | @ echo "***************** Data Size *****************" 22 | @ du -h data.* 23 | @ echo "******************** End ********************" 24 | @ echo "Comparing Context ... (Note: Using [diff] command, empty output means no different)" 25 | @ diff data.in data.out -------------------------------------------------------------------------------- /work/task3/miracle_bdev/miracle_bdev.c: -------------------------------------------------------------------------------- 1 | #include "spdk/stdinc.h" 2 | #include "spdk/thread.h" 3 | #include "spdk/bdev.h" 4 | #include "spdk/env.h" 5 | #include "spdk/event.h" 6 | #include "spdk/log.h" 7 | #include "spdk/string.h" 8 | #include "spdk/bdev_zone.h" 9 | 10 | static char *g_bdev_name = "Nvme0n1"; 11 | 12 | const int DATA_LENGTH = 256*1024; 13 | 14 | struct my_context 15 | { 16 | struct spdk_bdev *bdev; 17 | struct spdk_bdev_desc *bdev_desc; 18 | struct spdk_io_channel *bdev_io_channel; 19 | char *buff; 20 | uint32_t buff_size; 21 | char *bdev_name; 22 | struct spdk_bdev_io_wait_entry bdev_io_wait; 23 | }; 24 | 25 | static char *generate_str(void) 26 | { 27 | char *str = (char *)malloc(DATA_LENGTH * 8); 28 | memset(str, 0, DATA_LENGTH*8); 29 | if (str) 30 | { 31 | int i; 32 | for (i = 0; i < DATA_LENGTH; ++ i) 33 | { 34 | str[i] = '0'+(i%10); 35 | } 36 | return str; 37 | } 38 | else 39 | { 40 | return NULL; 41 | } 42 | } 43 | 44 | static void save_data(const char *file_path, char *str) 45 | { 46 | FILE *fp = fopen(file_path, "w"); 47 | fprintf(fp, "%s", str); 48 | fclose(fp); 49 | } 50 | 51 | static int miracle_bdev_parse_arg(int ch, char *arg) 52 | { 53 | switch (ch) 54 | { 55 | case 'b': 56 | g_bdev_name = arg; 57 | break; 58 | default: 59 | return -EINVAL; 60 | } 61 | return 0; 62 | } 63 | 64 | static void miracle_bdev_usage(void) 65 | { 66 | printf(" -b name of the bdev to use\n"); 67 | } 68 | 69 | static void bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx) 70 | { 71 | SPDK_NOTICELOG("Unsupported bdev event: type %d\n", type); 72 | } 73 | 74 | static void read_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 75 | { 76 | struct my_context *p = cb_arg; 77 | 78 | if (success) 79 | { 80 | SPDK_NOTICELOG("Reading Successfully, Saveing to data.out\n"); 81 | save_data("./data.out", p->buff); 82 | } 83 | else 84 | { 85 | SPDK_ERRLOG("bdev io read error\n"); 86 | } 87 | 88 | spdk_bdev_free_io(bdev_io); 89 | spdk_put_io_channel(p->bdev_io_channel); 90 | spdk_bdev_close(p->bdev_desc); 91 | SPDK_NOTICELOG("Stopping app\n"); 92 | spdk_app_stop(success ? 0 : -1); 93 | } 94 | 95 | static void start_read(void *arg) 96 | { 97 | struct my_context *p = arg; 98 | int rc = 0; 99 | 100 | SPDK_NOTICELOG("Reading io\n"); 101 | rc = spdk_bdev_read(p->bdev_desc, p->bdev_io_channel, p->buff, 0, p->buff_size, read_complete, p); 102 | 103 | if (rc == -ENOMEM) 104 | { 105 | SPDK_NOTICELOG("Queueing io\n"); 106 | p->bdev_io_wait.bdev = p->bdev; 107 | p->bdev_io_wait.cb_fn = start_read; 108 | p->bdev_io_wait.cb_arg = p; 109 | spdk_bdev_queue_io_wait(p->bdev, p->bdev_io_channel, &p->bdev_io_wait); 110 | } 111 | else if (rc) 112 | { 113 | SPDK_ERRLOG("%s error while reading from bdev: %d\n", spdk_strerror(-rc), rc); 114 | spdk_put_io_channel(p->bdev_io_channel); 115 | spdk_bdev_close(p->bdev_desc); 116 | spdk_app_stop(-1); 117 | } 118 | } 119 | 120 | static void write_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 121 | { 122 | struct my_context *p = cb_arg; 123 | 124 | spdk_bdev_free_io(bdev_io); 125 | 126 | if (success) 127 | { 128 | SPDK_NOTICELOG("bdev io write completed successfully\n"); 129 | } 130 | else 131 | { 132 | SPDK_ERRLOG("bdev io write error: %d\n", EIO); 133 | spdk_put_io_channel(p->bdev_io_channel); 134 | spdk_bdev_close(p->bdev_desc); 135 | spdk_app_stop(-1); 136 | return; 137 | } 138 | 139 | memset(p->buff, 0, p->buff_size); 140 | start_read(p); 141 | } 142 | 143 | static void start_write(void *arg) 144 | { 145 | struct my_context *p = arg; 146 | int rc = 0; 147 | 148 | SPDK_NOTICELOG("Writing to the bdev\n"); 149 | rc = spdk_bdev_write(p->bdev_desc, p->bdev_io_channel, p->buff, 0, p->buff_size, write_complete, p); 150 | 151 | if (rc == -ENOMEM) 152 | { 153 | SPDK_NOTICELOG("Queueing io\n"); 154 | p->bdev_io_wait.bdev = p->bdev; 155 | p->bdev_io_wait.cb_fn = start_write; 156 | p->bdev_io_wait.cb_arg = p; 157 | spdk_bdev_queue_io_wait(p->bdev, p->bdev_io_channel, &p->bdev_io_wait); 158 | } 159 | else if (rc) 160 | { 161 | SPDK_ERRLOG("%s error while writing to bdev: %d\n", spdk_strerror(-rc), rc); 162 | spdk_put_io_channel(p->bdev_io_channel); 163 | spdk_bdev_close(p->bdev_desc); 164 | spdk_app_stop(-1); 165 | } 166 | } 167 | 168 | static void miracle_bdev(void *arg) 169 | { 170 | struct my_context *p = arg; 171 | uint32_t buf_align; 172 | uint32_t block_size; 173 | int rc = 0; 174 | p->bdev = NULL; 175 | p->bdev_desc = NULL; 176 | 177 | SPDK_NOTICELOG("Successfully started the application\n"); 178 | 179 | SPDK_NOTICELOG("Opening the bdev %s\n", p->bdev_name); 180 | rc = spdk_bdev_open_ext(p->bdev_name, true, bdev_event_cb, NULL, &p->bdev_desc); 181 | if (rc) 182 | { 183 | SPDK_ERRLOG("Could not open bdev: %s\n", p->bdev_name); 184 | spdk_app_stop(-1); 185 | return; 186 | } 187 | 188 | p->bdev = spdk_bdev_desc_get_bdev(p->bdev_desc); 189 | 190 | SPDK_NOTICELOG("Opening io channel\n"); 191 | p->bdev_io_channel = spdk_bdev_get_io_channel(p->bdev_desc); 192 | if (p->bdev_io_channel == NULL) 193 | { 194 | SPDK_ERRLOG("Could not create bdev I/O channel!!\n"); 195 | spdk_bdev_close(p->bdev_desc); 196 | spdk_app_stop(-1); 197 | return; 198 | } 199 | 200 | block_size = spdk_bdev_get_block_size(p->bdev); 201 | buf_align = spdk_bdev_get_buf_align(p->bdev); 202 | 203 | p->buff_size = ceil(1.0*DATA_LENGTH/block_size)*block_size; 204 | p->buff = spdk_dma_zmalloc(p->buff_size, buf_align, NULL); 205 | if (!p->buff) 206 | { 207 | SPDK_ERRLOG("Failed to allocate buffer\n"); 208 | spdk_put_io_channel(p->bdev_io_channel); 209 | spdk_bdev_close(p->bdev_desc); 210 | spdk_app_stop(-1); 211 | return; 212 | } 213 | 214 | SPDK_NOTICELOG("Generating Data\n"); 215 | char *str = generate_str(); 216 | if (str){ 217 | sprintf(p->buff, "%s", str); 218 | free(str); 219 | SPDK_NOTICELOG("Saving Data to ./data.in\n"); 220 | save_data("./data.in", p->buff); 221 | start_write(p); 222 | } 223 | else{ 224 | SPDK_ERRLOG("Could not generate data!!\n"); 225 | spdk_put_io_channel(p->bdev_io_channel); 226 | spdk_bdev_close(p->bdev_desc); 227 | spdk_app_stop(-1); 228 | return; 229 | } 230 | } 231 | 232 | int main(int argc, char **argv) 233 | { 234 | struct spdk_app_opts opts = {}; 235 | int rc = 0; 236 | struct my_context context = {}; 237 | 238 | spdk_app_opts_init(&opts, sizeof(opts)); 239 | opts.name = "miracle_bdev"; 240 | rc = spdk_app_parse_args(argc, argv, &opts, "b:", NULL, miracle_bdev_parse_arg, miracle_bdev_usage); 241 | if (rc != SPDK_APP_PARSE_ARGS_SUCCESS) 242 | { 243 | exit(rc); 244 | } 245 | context.bdev_name = g_bdev_name; 246 | 247 | rc = spdk_app_start(&opts, miracle_bdev, &context); 248 | if (rc) 249 | { 250 | SPDK_ERRLOG("ERROR starting applicatoin\n"); 251 | } 252 | 253 | spdk_dma_free(context.buff); 254 | spdk_app_fini(); 255 | 256 | return rc; 257 | } 258 | -------------------------------------------------------------------------------- /work/task3/miracle_bdev/miracle_bdev.json: -------------------------------------------------------------------------------- 1 | { 2 | "subsystems": [ 3 | { 4 | "subsystem": "bdev", 5 | "config": [ 6 | { 7 | "method": "bdev_nvme_attach_controller", 8 | "params": { 9 | "trtype": "PCIe", 10 | "name":"Nvme0", 11 | "traddr":"0000:00:04.0" 12 | } 13 | } 14 | ] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /work/task3/计算机系统综合实验三.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task3/计算机系统综合实验三.pptx -------------------------------------------------------------------------------- /work/task4/README.assets/637b29ae8c6c6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task4/README.assets/637b29ae8c6c6.png -------------------------------------------------------------------------------- /work/task4/README.assets/637b35a31bcee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task4/README.assets/637b35a31bcee.png -------------------------------------------------------------------------------- /work/task4/README.md: -------------------------------------------------------------------------------- 1 | # 实验四、blobstore原理和源码分析 2 | 3 | ## 实验目的 4 | 5 | - 学习blob原理和基本接口操作 6 | 7 | ## 实验内容 8 | 9 | - 学习Blob基本原理 10 | - 完成hello_blob 程序运行 11 | - 修改底层bdev为nvme 12 | 13 | ## 实验过程和步骤 14 | 15 | ### 运行hello_blob示例 16 | #### 启动虚拟机 17 | ```bash 18 | ./start.sh ssd 19 | ``` 20 | 21 | #### 初始化环境 22 | ```bash 23 | sudo scripts/setup.sh 24 | ``` 25 | 26 | #### 运行hello_blob 27 | 在spdk文件夹下 28 | ```bash 29 | sudo ./build/examples/hello_blob ./examples/blob/hello_world/hello_blob.json 30 | ``` 31 | 32 | ![file](./README.assets/637b29ae8c6c6.png) 33 | 34 | ### 修改hello_blob.c,替换ramdisk为nvme 35 | #### `miracle_blob.c` 36 | ```c 37 | #include "spdk/stdinc.h" 38 | 39 | #include "spdk/bdev.h" 40 | #include "spdk/env.h" 41 | #include "spdk/event.h" 42 | #include "spdk/blob_bdev.h" 43 | #include "spdk/blob.h" 44 | #include "spdk/log.h" 45 | #include "spdk/string.h" 46 | 47 | struct my_context 48 | { 49 | struct spdk_blob_store *bs; 50 | struct spdk_blob *blob; 51 | spdk_blob_id blobid; 52 | struct spdk_io_channel *channel; 53 | uint8_t *read_buff; 54 | uint8_t *write_buff; 55 | uint64_t io_unit_size; 56 | int rc; 57 | }; 58 | 59 | static void cleanup(struct my_context *p) 60 | { 61 | spdk_free(p->read_buff); 62 | spdk_free(p->write_buff); 63 | free(p); 64 | } 65 | 66 | static void unload_complete(void *cb_arg, int bserrno) 67 | { 68 | struct my_context *p = cb_arg; 69 | 70 | SPDK_NOTICELOG("entry\n"); 71 | if (bserrno) 72 | { 73 | SPDK_ERRLOG("Error %d unloading the bobstore\n", bserrno); 74 | p->rc = bserrno; 75 | } 76 | 77 | spdk_app_stop(p->rc); 78 | } 79 | 80 | static void unload_bs(struct my_context *p, char *msg, int bserrno) 81 | { 82 | if (bserrno) 83 | { 84 | SPDK_ERRLOG("%s (err %d)\n", msg, bserrno); 85 | p->rc = bserrno; 86 | } 87 | if (p->bs) 88 | { 89 | if (p->channel) 90 | { 91 | spdk_bs_free_io_channel(p->channel); 92 | } 93 | spdk_bs_unload(p->bs, unload_complete, p); 94 | } 95 | else 96 | { 97 | spdk_app_stop(bserrno); 98 | } 99 | } 100 | 101 | static void delete_complete(void *arg1, int bserrno) 102 | { 103 | struct my_context *p = arg1; 104 | 105 | SPDK_NOTICELOG("entry\n"); 106 | if (bserrno) 107 | { 108 | unload_bs(p, "Error in delete completion", bserrno); 109 | return; 110 | } 111 | 112 | unload_bs(p, "", 0); 113 | } 114 | 115 | static void delete_blob(void *arg1, int bserrno) 116 | { 117 | struct my_context *p = arg1; 118 | 119 | SPDK_NOTICELOG("entry\n"); 120 | if (bserrno) 121 | { 122 | unload_bs(p, "Error in close completion", bserrno); 123 | return; 124 | } 125 | 126 | spdk_bs_delete_blob(p->bs, p->blobid, delete_complete, p); 127 | } 128 | 129 | static void read_complete(void *arg1, int bserrno) 130 | { 131 | struct my_context *p = arg1; 132 | int match_res = -1; 133 | 134 | SPDK_NOTICELOG("entry\n"); 135 | if (bserrno) 136 | { 137 | unload_bs(p, "Error in read completion", bserrno); 138 | return; 139 | } 140 | 141 | match_res = memcmp(p->write_buff, p->read_buff, p->io_unit_size); 142 | if (match_res) 143 | { 144 | unload_bs(p, "Error in data compare", -1); 145 | return; 146 | } 147 | else 148 | { 149 | SPDK_NOTICELOG("read SUCCESS and data matches!\n"); 150 | } 151 | 152 | spdk_blob_close(p->blob, delete_blob, p); 153 | } 154 | 155 | static void read_blob(struct my_context *p) 156 | { 157 | SPDK_NOTICELOG("entry\n"); 158 | 159 | p->read_buff = spdk_malloc(p->io_unit_size, 0x1000, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); 160 | if (p->read_buff == NULL) 161 | { 162 | unload_bs(p, "Error in memory allocation", -ENOMEM); 163 | return; 164 | } 165 | 166 | spdk_blob_io_read(p->blob, p->channel, p->read_buff, 0, 1, read_complete, p); 167 | } 168 | 169 | static void write_complete(void *arg1, int bserrno) 170 | { 171 | struct my_context *p = arg1; 172 | 173 | SPDK_NOTICELOG("entry\n"); 174 | if (bserrno) 175 | { 176 | unload_bs(p, "Error in write completion", bserrno); 177 | return; 178 | } 179 | 180 | read_blob(p); 181 | } 182 | 183 | static void blob_write(struct my_context *p) 184 | { 185 | SPDK_NOTICELOG("entry\n"); 186 | 187 | p->write_buff = spdk_malloc(p->io_unit_size, 0x1000, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); 188 | if (p->write_buff == NULL) 189 | { 190 | unload_bs(p, "Error in allocating memory", -ENOMEM); 191 | return; 192 | } 193 | memset(p->write_buff, 0x5a, p->io_unit_size); 194 | 195 | p->channel = spdk_bs_alloc_io_channel(p->bs); 196 | if (p->channel == NULL) 197 | { 198 | unload_bs(p, "Error in allocating channel", -ENOMEM); 199 | return; 200 | } 201 | 202 | spdk_blob_io_write(p->blob, p->channel, p->write_buff, 0, 1, write_complete, p); 203 | } 204 | 205 | static void sync_complete(void *arg1, int bserrno) 206 | { 207 | struct my_context *p = arg1; 208 | 209 | SPDK_NOTICELOG("entry\n"); 210 | if (bserrno) 211 | { 212 | unload_bs(p, "Error in sync callback", bserrno); 213 | return; 214 | } 215 | 216 | blob_write(p); 217 | } 218 | 219 | static void resize_complete(void *cb_arg, int bserrno) 220 | { 221 | struct my_context *p = cb_arg; 222 | uint64_t total = 0; 223 | 224 | if (bserrno) 225 | { 226 | unload_bs(p, "Error in blob resize", bserrno); 227 | return; 228 | } 229 | 230 | total = spdk_blob_get_num_clusters(p->blob); 231 | SPDK_NOTICELOG("resized blob now has USED clusters of %" PRIu64 "\n", total); 232 | 233 | spdk_blob_sync_md(p->blob, sync_complete, p); 234 | } 235 | 236 | static void open_complete(void *cb_arg, struct spdk_blob *blob, int bserrno) 237 | { 238 | struct my_context *p = cb_arg; 239 | uint64_t free = 0; 240 | 241 | SPDK_NOTICELOG("entry\n"); 242 | if (bserrno) 243 | { 244 | unload_bs(p, "Error in open completion", bserrno); 245 | return; 246 | } 247 | 248 | p->blob = blob; 249 | free = spdk_bs_free_cluster_count(p->bs); 250 | SPDK_NOTICELOG("blobstore has FREE clusters of %" PRIu64 "\n", free); 251 | 252 | spdk_blob_resize(p->blob, free, resize_complete, p); 253 | } 254 | 255 | static void blob_create_complete(void *arg1, spdk_blob_id blobid, int bserrno) 256 | { 257 | struct my_context *p = arg1; 258 | 259 | SPDK_NOTICELOG("entry\n"); 260 | if (bserrno) 261 | { 262 | unload_bs(p, "Error in blob create callback", bserrno); 263 | return; 264 | } 265 | 266 | p->blobid = blobid; 267 | SPDK_NOTICELOG("new blob id %" PRIu64 "\n", p->blobid); 268 | 269 | spdk_bs_open_blob(p->bs, p->blobid, open_complete, p); 270 | } 271 | 272 | static void create_blob(struct my_context *p) 273 | { 274 | SPDK_NOTICELOG("entry\n"); 275 | spdk_bs_create_blob(p->bs, blob_create_complete, p); 276 | } 277 | 278 | static void bs_init_complete(void *cb_arg, struct spdk_blob_store *bs, int bserrno) 279 | { 280 | struct my_context *p = cb_arg; 281 | 282 | SPDK_NOTICELOG("entry\n"); 283 | if (bserrno) 284 | { 285 | unload_bs(p, "Error initing the blobstore", bserrno); 286 | return; 287 | } 288 | 289 | p->bs = bs; 290 | SPDK_NOTICELOG("blobstore: %p\n", p->bs); 291 | 292 | p->io_unit_size = spdk_bs_get_io_unit_size(p->bs); 293 | 294 | create_blob(p); 295 | } 296 | 297 | static void base_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx) 298 | { 299 | SPDK_WARNLOG("Unsupported bdev event: type %d\n", type); 300 | } 301 | 302 | static void hello_start(void *arg1) 303 | { 304 | struct my_context *p = arg1; 305 | struct spdk_bs_dev *bs_dev = NULL; 306 | int rc; 307 | rc = spdk_bdev_create_bs_dev_ext("Nvme0n1", base_bdev_event_cb, NULL, &bs_dev); 308 | if (rc != 0) 309 | { 310 | SPDK_ERRLOG("Could not create blob bdev, %s!!\n", spdk_strerror(-rc)); 311 | spdk_app_stop(-1); 312 | return; 313 | } 314 | 315 | spdk_bs_init(bs_dev, NULL, bs_init_complete, p); 316 | } 317 | 318 | int main(int argc, char **argv) 319 | { 320 | struct spdk_app_opts opts = {}; 321 | int rc = 0; 322 | struct my_context *p = NULL; 323 | 324 | SPDK_NOTICELOG("entry\n"); 325 | 326 | spdk_app_opts_init(&opts, sizeof(opts)); 327 | 328 | opts.name = "hello_miracle"; 329 | opts.json_config_file = argv[1]; 330 | 331 | p = calloc(1, sizeof(struct my_context)); 332 | if (p) 333 | { 334 | rc = spdk_app_start(&opts, hello_start, p); 335 | if (rc) 336 | { 337 | SPDK_NOTICELOG("ERROR!\n"); 338 | } 339 | else 340 | { 341 | SPDK_NOTICELOG("SUCCESS!\n"); 342 | } 343 | cleanup(p); 344 | } 345 | else 346 | { 347 | SPDK_ERRLOG("Could not alloc hello_context struct!!\n"); 348 | rc = -ENOMEM; 349 | } 350 | spdk_app_fini(); 351 | return rc; 352 | } 353 | 354 | ``` 355 | 356 | #### `Makefile` 357 | ```makefile 358 | SPDK_ROOT_DIR := /home/miracle/work/spdk 359 | include $(SPDK_ROOT_DIR)/mk/spdk.common.mk 360 | include $(SPDK_ROOT_DIR)/mk/spdk.modules.mk 361 | 362 | APP = miracle_blob 363 | 364 | C_SRCS := miracle_blob.c 365 | 366 | SPDK_LIB_LIST = $(ALL_MODULES_LIST) event event_bdev 367 | 368 | include $(SPDK_ROOT_DIR)/mk/spdk.app.mk 369 | 370 | run: all 371 | @ rm -f miracle_blob.d miracle_blob.o 372 | @ $(SPDK_ROOT_DIR)/scripts/gen_nvme.sh --json-with-subsystems > ./miracle_bdev.json 373 | @ sudo ./miracle_blob ./miracle_bdev.json 374 | ``` 375 | 376 | #### 运行结果 377 | 378 | ![file](./README.assets/637b35a31bcee.png) 379 | 380 | 381 | 382 | ## 实验结论和心得体会 383 | 384 | 本次实验学习了Blob基本原理,运行并分析了hello_bdev程序,并最修改底层bdev为nvme并成功运行。 -------------------------------------------------------------------------------- /work/task4/README.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task4/README.pdf -------------------------------------------------------------------------------- /work/task4/miracle_blob/.gitignore: -------------------------------------------------------------------------------- 1 | miracle_blob 2 | 3 | # Prerequisites 4 | *.d 5 | 6 | # Object files 7 | *.o 8 | *.ko 9 | *.obj 10 | *.elf 11 | 12 | # Linker output 13 | *.ilk 14 | *.map 15 | *.exp 16 | 17 | # Precompiled Headers 18 | *.gch 19 | *.pch 20 | 21 | # Libraries 22 | *.lib 23 | *.a 24 | *.la 25 | *.lo 26 | 27 | # Shared objects (inc. Windows DLLs) 28 | *.dll 29 | *.so 30 | *.so.* 31 | *.dylib 32 | 33 | # Executables 34 | *.exe 35 | *.out 36 | *.app 37 | *.i*86 38 | *.x86_64 39 | *.hex 40 | 41 | # Debug files 42 | *.dSYM/ 43 | *.su 44 | *.idb 45 | *.pdb 46 | 47 | # Kernel Module Compile Results 48 | *.mod* 49 | *.cmd 50 | .tmp_versions/ 51 | modules.order 52 | Module.symvers 53 | Mkfile.old 54 | dkms.conf 55 | -------------------------------------------------------------------------------- /work/task4/miracle_blob/Makefile: -------------------------------------------------------------------------------- 1 | SPDK_ROOT_DIR := /home/miracle/work/spdk 2 | include $(SPDK_ROOT_DIR)/mk/spdk.common.mk 3 | include $(SPDK_ROOT_DIR)/mk/spdk.modules.mk 4 | 5 | APP = miracle_blob 6 | 7 | C_SRCS := miracle_blob.c 8 | 9 | SPDK_LIB_LIST = $(ALL_MODULES_LIST) event event_bdev 10 | 11 | include $(SPDK_ROOT_DIR)/mk/spdk.app.mk 12 | 13 | run: all 14 | @ rm -f miracle_blob.d miracle_blob.o 15 | @ $(SPDK_ROOT_DIR)/scripts/gen_nvme.sh --json-with-subsystems > ./miracle_bdev.json 16 | @ sudo ./miracle_blob ./miracle_bdev.json -------------------------------------------------------------------------------- /work/task4/miracle_blob/miracle_bdev.json: -------------------------------------------------------------------------------- 1 | { 2 | "subsystems": [ 3 | { 4 | "subsystem": "bdev", 5 | "config": [ 6 | { 7 | "method": "bdev_nvme_attach_controller", 8 | "params": { 9 | "trtype": "PCIe", 10 | "name": "Nvme0", 11 | "traddr": "0000:00:04.0" 12 | } 13 | } 14 | ] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /work/task4/miracle_blob/miracle_blob.c: -------------------------------------------------------------------------------- 1 | #include "spdk/stdinc.h" 2 | 3 | #include "spdk/bdev.h" 4 | #include "spdk/env.h" 5 | #include "spdk/event.h" 6 | #include "spdk/blob_bdev.h" 7 | #include "spdk/blob.h" 8 | #include "spdk/log.h" 9 | #include "spdk/string.h" 10 | 11 | struct my_context 12 | { 13 | struct spdk_blob_store *bs; 14 | struct spdk_blob *blob; 15 | spdk_blob_id blobid; 16 | struct spdk_io_channel *channel; 17 | uint8_t *read_buff; 18 | uint8_t *write_buff; 19 | uint64_t io_unit_size; 20 | int rc; 21 | }; 22 | 23 | static void cleanup(struct my_context *p) 24 | { 25 | spdk_free(p->read_buff); 26 | spdk_free(p->write_buff); 27 | free(p); 28 | } 29 | 30 | static void unload_complete(void *cb_arg, int bserrno) 31 | { 32 | struct my_context *p = cb_arg; 33 | 34 | SPDK_NOTICELOG("entry\n"); 35 | if (bserrno) 36 | { 37 | SPDK_ERRLOG("Error %d unloading the bobstore\n", bserrno); 38 | p->rc = bserrno; 39 | } 40 | 41 | spdk_app_stop(p->rc); 42 | } 43 | 44 | static void unload_bs(struct my_context *p, char *msg, int bserrno) 45 | { 46 | if (bserrno) 47 | { 48 | SPDK_ERRLOG("%s (err %d)\n", msg, bserrno); 49 | p->rc = bserrno; 50 | } 51 | if (p->bs) 52 | { 53 | if (p->channel) 54 | { 55 | spdk_bs_free_io_channel(p->channel); 56 | } 57 | spdk_bs_unload(p->bs, unload_complete, p); 58 | } 59 | else 60 | { 61 | spdk_app_stop(bserrno); 62 | } 63 | } 64 | 65 | static void delete_complete(void *arg1, int bserrno) 66 | { 67 | struct my_context *p = arg1; 68 | 69 | SPDK_NOTICELOG("entry\n"); 70 | if (bserrno) 71 | { 72 | unload_bs(p, "Error in delete completion", bserrno); 73 | return; 74 | } 75 | 76 | unload_bs(p, "", 0); 77 | } 78 | 79 | static void delete_blob(void *arg1, int bserrno) 80 | { 81 | struct my_context *p = arg1; 82 | 83 | SPDK_NOTICELOG("entry\n"); 84 | if (bserrno) 85 | { 86 | unload_bs(p, "Error in close completion", bserrno); 87 | return; 88 | } 89 | 90 | spdk_bs_delete_blob(p->bs, p->blobid, delete_complete, p); 91 | } 92 | 93 | static void read_complete(void *arg1, int bserrno) 94 | { 95 | struct my_context *p = arg1; 96 | int match_res = -1; 97 | 98 | SPDK_NOTICELOG("entry\n"); 99 | if (bserrno) 100 | { 101 | unload_bs(p, "Error in read completion", bserrno); 102 | return; 103 | } 104 | 105 | match_res = memcmp(p->write_buff, p->read_buff, p->io_unit_size); 106 | if (match_res) 107 | { 108 | unload_bs(p, "Error in data compare", -1); 109 | return; 110 | } 111 | else 112 | { 113 | SPDK_NOTICELOG("read SUCCESS and data matches!\n"); 114 | } 115 | 116 | spdk_blob_close(p->blob, delete_blob, p); 117 | } 118 | 119 | static void read_blob(struct my_context *p) 120 | { 121 | SPDK_NOTICELOG("entry\n"); 122 | 123 | p->read_buff = spdk_malloc(p->io_unit_size, 0x1000, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); 124 | if (p->read_buff == NULL) 125 | { 126 | unload_bs(p, "Error in memory allocation", -ENOMEM); 127 | return; 128 | } 129 | 130 | spdk_blob_io_read(p->blob, p->channel, p->read_buff, 0, 1, read_complete, p); 131 | } 132 | 133 | static void write_complete(void *arg1, int bserrno) 134 | { 135 | struct my_context *p = arg1; 136 | 137 | SPDK_NOTICELOG("entry\n"); 138 | if (bserrno) 139 | { 140 | unload_bs(p, "Error in write completion", bserrno); 141 | return; 142 | } 143 | 144 | read_blob(p); 145 | } 146 | 147 | static void blob_write(struct my_context *p) 148 | { 149 | SPDK_NOTICELOG("entry\n"); 150 | 151 | p->write_buff = spdk_malloc(p->io_unit_size, 0x1000, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); 152 | if (p->write_buff == NULL) 153 | { 154 | unload_bs(p, "Error in allocating memory", -ENOMEM); 155 | return; 156 | } 157 | memset(p->write_buff, 0x5a, p->io_unit_size); 158 | 159 | p->channel = spdk_bs_alloc_io_channel(p->bs); 160 | if (p->channel == NULL) 161 | { 162 | unload_bs(p, "Error in allocating channel", -ENOMEM); 163 | return; 164 | } 165 | 166 | spdk_blob_io_write(p->blob, p->channel, p->write_buff, 0, 1, write_complete, p); 167 | } 168 | 169 | static void sync_complete(void *arg1, int bserrno) 170 | { 171 | struct my_context *p = arg1; 172 | 173 | SPDK_NOTICELOG("entry\n"); 174 | if (bserrno) 175 | { 176 | unload_bs(p, "Error in sync callback", bserrno); 177 | return; 178 | } 179 | 180 | blob_write(p); 181 | } 182 | 183 | static void resize_complete(void *cb_arg, int bserrno) 184 | { 185 | struct my_context *p = cb_arg; 186 | uint64_t total = 0; 187 | 188 | if (bserrno) 189 | { 190 | unload_bs(p, "Error in blob resize", bserrno); 191 | return; 192 | } 193 | 194 | total = spdk_blob_get_num_clusters(p->blob); 195 | SPDK_NOTICELOG("resized blob now has USED clusters of %" PRIu64 "\n", total); 196 | 197 | spdk_blob_sync_md(p->blob, sync_complete, p); 198 | } 199 | 200 | static void open_complete(void *cb_arg, struct spdk_blob *blob, int bserrno) 201 | { 202 | struct my_context *p = cb_arg; 203 | uint64_t free = 0; 204 | 205 | SPDK_NOTICELOG("entry\n"); 206 | if (bserrno) 207 | { 208 | unload_bs(p, "Error in open completion", bserrno); 209 | return; 210 | } 211 | 212 | p->blob = blob; 213 | free = spdk_bs_free_cluster_count(p->bs); 214 | SPDK_NOTICELOG("blobstore has FREE clusters of %" PRIu64 "\n", free); 215 | 216 | spdk_blob_resize(p->blob, free, resize_complete, p); 217 | } 218 | 219 | static void blob_create_complete(void *arg1, spdk_blob_id blobid, int bserrno) 220 | { 221 | struct my_context *p = arg1; 222 | 223 | SPDK_NOTICELOG("entry\n"); 224 | if (bserrno) 225 | { 226 | unload_bs(p, "Error in blob create callback", bserrno); 227 | return; 228 | } 229 | 230 | p->blobid = blobid; 231 | SPDK_NOTICELOG("new blob id %" PRIu64 "\n", p->blobid); 232 | 233 | spdk_bs_open_blob(p->bs, p->blobid, open_complete, p); 234 | } 235 | 236 | static void create_blob(struct my_context *p) 237 | { 238 | SPDK_NOTICELOG("entry\n"); 239 | spdk_bs_create_blob(p->bs, blob_create_complete, p); 240 | } 241 | 242 | static void bs_init_complete(void *cb_arg, struct spdk_blob_store *bs, int bserrno) 243 | { 244 | struct my_context *p = cb_arg; 245 | 246 | SPDK_NOTICELOG("entry\n"); 247 | if (bserrno) 248 | { 249 | unload_bs(p, "Error initing the blobstore", bserrno); 250 | return; 251 | } 252 | 253 | p->bs = bs; 254 | SPDK_NOTICELOG("blobstore: %p\n", p->bs); 255 | 256 | p->io_unit_size = spdk_bs_get_io_unit_size(p->bs); 257 | 258 | create_blob(p); 259 | } 260 | 261 | static void base_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx) 262 | { 263 | SPDK_WARNLOG("Unsupported bdev event: type %d\n", type); 264 | } 265 | 266 | static void hello_start(void *arg1) 267 | { 268 | struct my_context *p = arg1; 269 | struct spdk_bs_dev *bs_dev = NULL; 270 | int rc; 271 | rc = spdk_bdev_create_bs_dev_ext("Nvme0n1", base_bdev_event_cb, NULL, &bs_dev); 272 | if (rc != 0) 273 | { 274 | SPDK_ERRLOG("Could not create blob bdev, %s!!\n", spdk_strerror(-rc)); 275 | spdk_app_stop(-1); 276 | return; 277 | } 278 | 279 | spdk_bs_init(bs_dev, NULL, bs_init_complete, p); 280 | } 281 | 282 | int main(int argc, char **argv) 283 | { 284 | struct spdk_app_opts opts = {}; 285 | int rc = 0; 286 | struct my_context *p = NULL; 287 | 288 | SPDK_NOTICELOG("entry\n"); 289 | 290 | spdk_app_opts_init(&opts, sizeof(opts)); 291 | 292 | opts.name = "hello_miracle"; 293 | opts.json_config_file = argv[1]; 294 | 295 | p = calloc(1, sizeof(struct my_context)); 296 | if (p) 297 | { 298 | rc = spdk_app_start(&opts, hello_start, p); 299 | if (rc) 300 | { 301 | SPDK_NOTICELOG("ERROR!\n"); 302 | } 303 | else 304 | { 305 | SPDK_NOTICELOG("SUCCESS!\n"); 306 | } 307 | cleanup(p); 308 | } 309 | else 310 | { 311 | SPDK_ERRLOG("Could not alloc hello_context struct!!\n"); 312 | rc = -ENOMEM; 313 | } 314 | spdk_app_fini(); 315 | return rc; 316 | } 317 | -------------------------------------------------------------------------------- /work/task4/计算机系统综合实验四.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task4/计算机系统综合实验四.pptx -------------------------------------------------------------------------------- /work/task5/README.assets/637d83787772d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task5/README.assets/637d83787772d.png -------------------------------------------------------------------------------- /work/task5/README.assets/637d83b9b3efa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task5/README.assets/637d83b9b3efa.png -------------------------------------------------------------------------------- /work/task5/README.md: -------------------------------------------------------------------------------- 1 | # 实验五、blobFS原理和源码分析 2 | 3 | ## 实验目的 4 | 5 | - 学习BlobFS基本原理,创建并挂载BlobFS 6 | 7 | ## 实验内容 8 | 9 | - 学习BlobFS基本原理 10 | - 在Nvme上创建BlobFS 11 | - 通过Fuse挂载BlobFS 12 | 13 | ## 实验过程和步骤 14 | 15 | ### 启动虚拟机 16 | 17 | ```bash 18 | ./start.sh ssd 19 | ``` 20 | 21 | ### 初始化环境 22 | 23 | 在spdk目录下 24 | 25 | ```bash 26 | sudo HUGEMEM=5120 ./scripts/setup.sh 27 | ``` 28 | 29 | ### 安装fuse依赖 30 | 31 | ```bash 32 | sudo apt install libfuse3-dev 33 | ``` 34 | 35 | ### 编译 36 | 37 | ```bash 38 | ./configure --with-fuse 39 | make -j$(nproc) 40 | ``` 41 | 42 | ### 生成NVMe配置文件 43 | 44 | ```bash 45 | ./scripts/gen_nvme.sh --json-with-subsystems > ./test/blobfs/nvme.json 46 | ``` 47 | 48 | ### 创建一个空的SPDK blobfs 49 | 50 | 在 `spdk/test/blobfs/`目录下 51 | 52 | ```bash 53 | sudo ./mkfs/mkfs ./nvme.json Nvme0n1 54 | ``` 55 | 56 | ![file](./README.assets/637d83787772d.png) 57 | 58 | ### 创建挂载的目录 59 | 60 | ```bash 61 | sudo mkdir /mnt/fuse 62 | ``` 63 | 64 | ### 运行fuse示例程序 65 | 66 | 在 `spdk/test/blobfs/`目录下 67 | 68 | ```bash 69 | sudo ./fuse/fuse ./nvme.json Nvme0n1 /mnt/fuse/ 70 | ``` 71 | 72 | ![file](./README.assets/637d83b9b3efa.png) 73 | 74 | ## 实验结论和心得体会 75 | 76 | 本次实验通过学习BlobFS基本原理在Nvme上创建了BlobFS并通过Fuse对其进行挂载。 77 | -------------------------------------------------------------------------------- /work/task5/README.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task5/README.pdf -------------------------------------------------------------------------------- /work/task5/计算机系统综合实验五.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task5/计算机系统综合实验五.pptx -------------------------------------------------------------------------------- /work/task6/README.assets/image-20221222194509364.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task6/README.assets/image-20221222194509364.png -------------------------------------------------------------------------------- /work/task6/README.assets/image-20221222194635804.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task6/README.assets/image-20221222194635804.png -------------------------------------------------------------------------------- /work/task6/README.assets/image-20221222194639115.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task6/README.assets/image-20221222194639115.png -------------------------------------------------------------------------------- /work/task6/README.assets/image-20221222195029020.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task6/README.assets/image-20221222195029020.png -------------------------------------------------------------------------------- /work/task6/README.assets/image-20221222195226455.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task6/README.assets/image-20221222195226455.png -------------------------------------------------------------------------------- /work/task6/README.assets/image-20221222195243972.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task6/README.assets/image-20221222195243972.png -------------------------------------------------------------------------------- /work/task6/README.assets/image-20221222201015837.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task6/README.assets/image-20221222201015837.png -------------------------------------------------------------------------------- /work/task6/README.assets/image-20221222201134790.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task6/README.assets/image-20221222201134790.png -------------------------------------------------------------------------------- /work/task6/README.assets/image-20221222201241336.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task6/README.assets/image-20221222201241336.png -------------------------------------------------------------------------------- /work/task6/README.assets/image-20221222202053431.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task6/README.assets/image-20221222202053431.png -------------------------------------------------------------------------------- /work/task6/README.md: -------------------------------------------------------------------------------- 1 | # 实验六、综合设计实验 2 | 3 | ## 实验目的 4 | 5 | 基于BlobFS,设计一个key-value数据库,支持open,put,get,close操作 6 | 7 | ## 实验内容 8 | 9 | 基于BlobFS,设计一个key-value数据库,支持open,put,get,close操作 10 | 11 | ## 实验过程和步骤 12 | 13 | ### 实验思路 14 | 15 | 本次基于BlobFS设计KV数据库。考虑到BlobFS仅支持追加写的特性,本次实验选择使用C++复现Bitcask数据库。 16 | 17 | - BlobFS的限制![image-20221222194509364](./README.assets/image-20221222194509364.png) 18 | 19 | ### Bitcask数据库原理 20 | 21 | #### 日志型数据存储 22 | 23 | 所有写操作只追加而不修改老的数据,这样做目的是能保证最大程度的顺序 IO ,压榨出机械硬盘的顺序写性能。 24 | 25 | 在Bitcask模型中,数据文件以日志型只增不减的写入文件,而文件有一定的大小限制,当文件大小增加到相应的限制时,就会产生一个新的文件,老的文件将只读不写。 26 | 27 | 在任意时间点,只有一个文件是可写的,在Bitcask模型中称其为active data file,而其他的已经达到限制大小的文件,称为older data file,如下图: 28 | 29 | ![image-20221222195029020](./README.assets/image-20221222195029020.png) 30 | 31 | 文件中的数据结构非常简单,是一条一条的数据写入操作,每一条数据的结构如下: 32 | 33 | ![image-20221222194639115](./README.assets/image-20221222194639115.png) 34 | 35 | #### 基于hash表的索引数据 36 | 37 | 日志类型的数据文件会让我们的写入操作非常快,而如果在这样的日志型数据上进行key值查找,那将是一件非常低效的事情。于是我们需要使用一些方法来提高查找效率。 38 | 39 | 例如在Bigtable中,使用bloom-filter算法为每一个数据文件维护一个bloom-filter 的数据块,以此来判定一个值是否在某一个数据文件中。 40 | 在Bitcask模型中,除了存储在磁盘上的数据文件,还有另外一块数据,那就是存储在内存中的hash表,hash表的作用是通过key值快速的定位到value的位置。hash表的结构大致如下图所示: 41 | 42 | ![image-20221222195243972](./README.assets/image-20221222195243972.png) 43 | 44 | hash表对应的这个结构中包括了三个用于定位数据value的信息,分别是文件id号(file_id),value值在文件中的位置(value_pos),value值的大小(value_sz),于是我们通过读取file_id对应文件的value_pos开始的value_sz个字节,就得到了我们需要的value值。整个过程如下图所示: 45 | 46 | ![image-20221222195226455](./README.assets/image-20221222195226455.png) 47 | 48 | 由于多了一个hash表的存在,我们的写操作就需要多更新一块内容,即这个hash表的对应关系。于是一个写操作就需要进行一次顺序的磁盘写入和一次内存操作。 49 | 50 | ### 代码实现 51 | 52 | #### `bitcask.h` 53 | 54 | ```cpp 55 | #ifndef BITCASK_H_ 56 | #define BITCASK_H_ 57 | 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | 72 | using namespace std; 73 | 74 | #define filemax 1024 * 4096 // 4MB 75 | 76 | const string fileprev = "bitcask_data"; 77 | const string cmd_prompt = ">>> bitcask : "; 78 | const string cmd = ">>> "; 79 | const int number = 0; 80 | // data 81 | struct bitcask_data 82 | { 83 | string key; 84 | int key_len; 85 | string value; 86 | int value_len; 87 | boost::posix_time::ptime timestamp; 88 | uint32_t crc; 89 | // crc 90 | template 91 | void serialize(Archive &ar, const unsigned int version) 92 | { 93 | ar &key_len; 94 | ar &key; 95 | ar &value_len; 96 | ar &value; 97 | ar &crc; 98 | ar ×tamp; 99 | } 100 | }; 101 | 102 | // index 103 | struct bitcask_index 104 | { 105 | string key; 106 | string file_id; 107 | int value_pos; 108 | int value_len; 109 | boost::posix_time::ptime timestamp; 110 | bool value_valid; 111 | 112 | template 113 | void serialize(Archive &ar, const unsigned int version) 114 | { 115 | ar &key; 116 | ar &file_id; 117 | ar &value_pos; 118 | ar &value_len; 119 | ar ×tamp; 120 | ar &value_valid; 121 | } 122 | 123 | bitcask_index() 124 | { 125 | value_valid = false; 126 | } 127 | }; 128 | 129 | // bitcask 130 | class bitcask 131 | { 132 | private: 133 | unordered_map index; 134 | int _activefile; 135 | bool _start; 136 | bool _finish; 137 | string _response; 138 | string filepath; 139 | 140 | private: 141 | void init(string path); 142 | uint32_t crc32(string value); 143 | void insert_data(string key, string value); 144 | void write_data(bitcask_data newdata); 145 | void write_index(bitcask_index newindex); 146 | bitcask_data read_data(string key); 147 | void read_datainfo(string key); 148 | bitcask_index read_index(string key); 149 | void delete_data(string key); 150 | void update_data(string key, string value); 151 | void update_index(bitcask_index upindex, string key); 152 | void merge(); 153 | void flush(); // flush index :hint.bin 154 | 155 | public: 156 | bitcask(); 157 | string Get(string key); 158 | void Put(string key, string value); 159 | void Open(string path); 160 | void Close(); 161 | ~bitcask(); 162 | 163 | }; 164 | 165 | #endif /* BITCASK_H_ */ 166 | ``` 167 | 168 | 169 | 170 | --- 171 | 172 | 173 | 174 | #### `bitcask.cpp` 175 | 176 | ```cpp 177 | #include 178 | #include 179 | #include 180 | #include 181 | #include 182 | #include 183 | #include 184 | #include "bitcask.h" 185 | using namespace std; 186 | 187 | bitcask::bitcask() 188 | { 189 | _start = false; 190 | _activefile = 0; 191 | _finish = false; 192 | _response = ""; 193 | filepath = ""; 194 | } 195 | 196 | bitcask::~bitcask() 197 | { 198 | if (this->_start) 199 | { 200 | merge(); 201 | flush(); 202 | } 203 | } 204 | 205 | void bitcask::init(string path) 206 | { 207 | this->filepath = path; 208 | this->_start = true; 209 | long len; 210 | fstream hint; 211 | hint.open(filepath + "hint.bin", ios::binary | ios::out | ios::app); 212 | if (!hint) 213 | { 214 | cout << "the file hint.bin open failure or maybe not exist!\n"; 215 | } 216 | len = hint.tellg(); 217 | if (len == 0) 218 | { 219 | cout << "create file hint.bin successful!\n"; 220 | } 221 | else 222 | { 223 | /* 224 | load index to memory 225 | */ 226 | // cout << "loading index" << endl; 227 | bitcask_index search; 228 | fstream hint; 229 | hint.open(filepath + "hint.bin", ios::binary | ios::in); 230 | if (!hint) 231 | { 232 | cout << "the file hint.bin open failure or maybe not exist!\n"; 233 | } 234 | 235 | while (hint) 236 | { 237 | boost::archive::binary_iarchive ia(hint, boost::archive::no_header); 238 | try 239 | { 240 | ia >> search; 241 | } 242 | catch (const exception &e) 243 | { 244 | // cout << "read end" << endl; 245 | goto do_load; 246 | } 247 | // cout << "loading:" << search.key << endl; 248 | bitcask_index insert; 249 | insert.file_id = search.file_id; 250 | insert.value_pos = search.value_pos; 251 | insert.value_len = search.value_len; 252 | insert.timestamp = search.timestamp; 253 | insert.value_valid = search.value_valid; 254 | index[search.key] = insert; 255 | } 256 | } 257 | do_load: 258 | fstream filelog; 259 | filelog.open(filepath + "filelog.bin", ios::binary | ios::in); 260 | if (!filelog) 261 | { 262 | cout << "the file filelog.bin open failure or maybe not exist!\n"; 263 | } 264 | filelog.read((char *)(&_activefile), sizeof(int)); 265 | filelog.close(); 266 | if (_activefile == 0) 267 | { 268 | cout << "create file filelog.bin successful!\n"; 269 | _activefile = 1; 270 | filelog.open(filepath + "filelog.bin", ios::binary | ios::out | ios::app); 271 | filelog.write((char *)(&_activefile), sizeof(int)); 272 | filelog.close(); 273 | return; 274 | } 275 | _start = true; 276 | } 277 | 278 | uint32_t bitcask::crc32(string value) 279 | { 280 | boost::crc_32_type result; 281 | result.process_bytes(value.c_str(), value.length()); 282 | return result.checksum(); 283 | } 284 | 285 | void bitcask::insert_data(string key, string value) 286 | { 287 | bitcask_index search = read_index(key); 288 | if (search.key != "") 289 | { 290 | cout << "the data " + key + " already exist!\n"; 291 | return update_data(key, value); 292 | } 293 | // add data 294 | bitcask_data newdata; 295 | newdata.key = key; 296 | newdata.key_len = int(key.length()); 297 | newdata.value = value; 298 | newdata.value_len = int(value.length()); 299 | newdata.crc = crc32(value); 300 | // newdata.timestamp=time(0); 301 | newdata.timestamp = boost::posix_time::microsec_clock::universal_time(); 302 | 303 | // add index 304 | fstream datafile; 305 | bitcask_index newindex; 306 | newindex.key = key; 307 | newindex.file_id = fileprev + to_string(_activefile); 308 | datafile.open(filepath + newindex.file_id, ios::binary | ios::out | ios::app); 309 | if (!datafile) 310 | cout << cmd_prompt + newindex.file_id + " open failure\n"; 311 | newindex.value_pos = datafile.tellg(); 312 | if (newindex.value_pos > filemax || filemax - newindex.value_pos < sizeof(newdata)) 313 | { 314 | _activefile++; 315 | newindex.file_id = fileprev + to_string(_activefile); 316 | } 317 | newindex.timestamp = newdata.timestamp; 318 | newindex.value_len = sizeof(newdata); 319 | newindex.value_valid = true; 320 | datafile.close(); 321 | write_data(newdata); 322 | write_index(newindex); 323 | // add to memory index array 324 | index[key] = newindex; 325 | // cout << "the data " + key + " insert successful\n"; 326 | } 327 | 328 | void bitcask::write_data(bitcask_data newdata) 329 | { 330 | string file = fileprev + to_string(_activefile); 331 | fstream datafile; 332 | datafile.open(filepath + file, ios::binary | ios::out | ios::app); 333 | if (!datafile) 334 | cout << file + " open file " + file + " failure!\n"; 335 | // datafile.write((char *)(&newdata),sizeof(newdata)); 336 | // data_append(datafile, newdata); 337 | boost::archive::binary_oarchive oa(datafile, boost::archive::no_header); 338 | oa << newdata; 339 | datafile.close(); 340 | } 341 | 342 | void bitcask::write_index(bitcask_index newindex) 343 | { 344 | fstream hint; 345 | hint.open(filepath + "hint.bin", ios::binary | ios::out | ios::app); 346 | if (!hint) 347 | { 348 | cout << "the file hint.bin open failure!\n"; 349 | } 350 | // hint.write((char *)(&newindex), sizeof(newindex)); 351 | // cout << "writing index: " << newindex.key << newindex.timestamp << endl; 352 | boost::archive::binary_oarchive oa(hint, boost::archive::no_header); 353 | oa << newindex; 354 | hint.close(); 355 | } 356 | 357 | bitcask_index bitcask::read_index(string key) 358 | { 359 | 360 | bitcask_index search; 361 | if ("" == key) 362 | { 363 | return search; 364 | } 365 | for (auto indexinfo : index) 366 | { 367 | if (indexinfo.first == key) 368 | { 369 | { 370 | 371 | search.key = indexinfo.first; 372 | search.file_id = indexinfo.second.file_id; 373 | search.value_pos = indexinfo.second.value_pos; 374 | search.value_len = indexinfo.second.value_len; 375 | search.value_valid = indexinfo.second.value_valid; 376 | search.timestamp = indexinfo.second.timestamp; 377 | // cout << "got key for " << key << " ,valid " << search.value_valid << endl; 378 | return search; 379 | } 380 | } 381 | } 382 | return search; 383 | } 384 | 385 | bitcask_data bitcask::read_data(string key) 386 | { 387 | bitcask_data search_data; 388 | bitcask_index search_index = read_index(key); 389 | if (search_index.value_valid == true) 390 | { 391 | string file = search_index.file_id; 392 | // cout << "reading file" << file << endl; 393 | fstream datafile; 394 | datafile.open(filepath + file, ios::binary | ios::in); 395 | if (!datafile) 396 | cout << "open file " + file + " failure\n"; 397 | 398 | boost::archive::binary_iarchive ia(datafile, boost::archive::no_header); 399 | if (search_index.value_pos) 400 | { 401 | datafile.seekg(search_index.value_pos, ios::beg); 402 | // cout << "seeking to" << search_index.value_pos << endl; 403 | } 404 | // datafile.read((char *)(&search_data),sizeof(search_data)); 405 | // cout << "reading datafile" << filepath + file << endl; 406 | // cout << "key = " << search_index.key << endl; 407 | // cout << "value_pos = " << search_index.value_pos << endl; 408 | // cout << "ts = " << search_index.timestamp << endl; 409 | // cout << "file pos = " << datafile.tellp() << endl; 410 | // search_data.serialize(ia, 0); 411 | ia >> search_data; 412 | // cout << "file pos = " << datafile.tellp() << endl; 413 | // cout << "on-disk ts = " << search_data.timestamp << endl; 414 | // return search_data; 415 | } 416 | // else 417 | // cout << "the data " + key + " does not exist!\n"; 418 | return search_data; 419 | } 420 | 421 | void bitcask::read_datainfo(string key) 422 | { 423 | bitcask_data data = read_data(key); 424 | bitcask_index index = read_index(key); 425 | if (index.value_valid == true) 426 | { 427 | _response += cmd + "key :" + key + "\n"; 428 | _response += cmd + "value :" + data.value + "\n"; 429 | _response += cmd + "crc :" + to_string(data.crc) + "\n"; 430 | _response += cmd + "file id :" + index.file_id + "\n"; 431 | _response += cmd + "value pos :" + to_string(index.value_pos) + "\n"; 432 | _response += cmd + "value length :" + to_string(data.value_len) + "\n"; 433 | // cout << _response; 434 | //_response+=cmd+"time :"+ data.timestamp; 435 | //_response+=cmd+"time :"+ctime(&data.timestamp); 436 | } 437 | else 438 | return; 439 | } 440 | 441 | void bitcask::delete_data(string key) 442 | { 443 | bitcask_index delindex = read_index(key); 444 | if (delindex.key != "") 445 | { 446 | delindex.value_valid = false; 447 | index[key] = delindex; 448 | cout << "the data " + key + " already delete!\n"; 449 | } 450 | else 451 | _response += cmd_prompt + "the data " + key + " does not exist!\n"; 452 | } 453 | 454 | void bitcask::update_data(string key, string value) 455 | { 456 | 457 | bitcask_index search = read_index(key); 458 | if (search.key == "") 459 | { 460 | cout << "the data " + key + " does not exist!\n"; 461 | return; 462 | } 463 | // update data 464 | fstream datafile, hintfile; 465 | bitcask_data updata; 466 | bitcask_index upindex = read_index(key); 467 | updata.key = key; 468 | updata.key_len = int(key.length()); 469 | updata.value = value; 470 | updata.value_len = int(value.length()); 471 | // updata.timestamp=time(0); 472 | updata.timestamp = boost::posix_time::microsec_clock::universal_time(); 473 | 474 | // update index 475 | upindex.file_id = fileprev + to_string(_activefile); 476 | datafile.open(filepath + upindex.file_id, ios::binary | ios::in | ios::app); 477 | if (!datafile) 478 | _response += cmd_prompt + "the file " + upindex.file_id + " open failure!\n"; 479 | upindex.value_pos = datafile.tellg(); 480 | if (upindex.value_pos > filemax || filemax - upindex.value_pos < sizeof(updata)) 481 | { 482 | _activefile++; 483 | } 484 | upindex.timestamp = updata.timestamp; 485 | upindex.value_len = sizeof(updata); 486 | upindex.value_valid = true; 487 | datafile.close(); 488 | write_data(updata); 489 | update_index(upindex, key); 490 | _response += cmd_prompt + "the data " + key + " update successful\n"; 491 | } 492 | 493 | void bitcask::update_index(bitcask_index upindex, string key) 494 | { 495 | bitcask_index seaindex = read_index(key); 496 | seaindex.key = key; 497 | seaindex.file_id = upindex.file_id; 498 | seaindex.value_pos = upindex.value_pos; 499 | seaindex.value_len = upindex.value_len; 500 | seaindex.value_valid = upindex.value_valid; 501 | seaindex.timestamp = upindex.timestamp; 502 | index[key] = seaindex; 503 | } 504 | 505 | void bitcask::merge() 506 | { 507 | /* 508 | merge data in file 509 | function: delete data in file 510 | */ 511 | int beans = 1; 512 | long value_pos; 513 | vector data_array; 514 | // cout << "merge begin: activefile" << _activefile << endl; 515 | for (; beans <= _activefile; beans++) 516 | { 517 | 518 | string file = fileprev + to_string(beans); 519 | fstream datafile; 520 | datafile.open(filepath + file, ios::binary | ios::in); 521 | 522 | if (!datafile) 523 | { 524 | _response += cmd_prompt + "the data file " + file + " open failure!\n"; 525 | } 526 | bitcask_data beans_data; 527 | bitcask_index beans_index; 528 | datafile.seekg(0, ios::beg); 529 | // load to memory 530 | while (datafile) 531 | { 532 | boost::archive::binary_iarchive ia(datafile, boost::archive::no_header); 533 | try 534 | { 535 | ia >> beans_data; 536 | 537 | beans_index = read_index(beans_data.key); 538 | // cout << "ts:" << beans_data.timestamp << " vs " << beans_index.timestamp << endl; 539 | } 540 | catch (const exception &e) 541 | { 542 | goto do_merge; 543 | } 544 | 545 | if (beans_index.value_valid == true && beans_data.timestamp == beans_index.timestamp) 546 | { 547 | // cout << "pushing:" << beans_index.key << endl; 548 | data_array.push_back(beans_data); 549 | } 550 | } 551 | 552 | do_merge: 553 | for (auto data : data_array) 554 | { 555 | // cout << "dataarray = " << data.key << endl; 556 | } 557 | 558 | datafile.close(); 559 | // write to file 560 | datafile.open(filepath + file, ios::binary | ios::out); 561 | if (!datafile) 562 | { 563 | _response += cmd_prompt + "the data file " + file + " open failure!\n"; 564 | } 565 | datafile.seekg(0, ios::beg); 566 | 567 | for (auto data : data_array) 568 | { 569 | value_pos = datafile.tellg(); 570 | // datafile.write((char *)(&data),sizeof(data)); 571 | boost::archive::binary_oarchive oa(datafile, boost::archive::no_header); 572 | oa << data; 573 | bitcask_index seaindex = read_index(data.key); 574 | seaindex.value_pos = value_pos; 575 | index[data.key] = seaindex; 576 | } 577 | datafile.close(); 578 | data_array.clear(); 579 | } 580 | /* 581 | TODO merge file 582 | */ 583 | if (_activefile >= 2) 584 | { 585 | while (_activefile > 1) 586 | { 587 | string file = fileprev + to_string(_activefile); 588 | fstream datafile; 589 | datafile.open(filepath + file, ios::binary | ios::in); 590 | if (!datafile) 591 | { 592 | _response += cmd_prompt + "the data file " + file + " open failure!\n"; 593 | } 594 | bitcask_data beans_data; 595 | bitcask_index beans_index; 596 | datafile.seekg(0, ios::beg); 597 | // TODO load to memory maybe too big? 598 | while (datafile) 599 | { 600 | boost::archive::binary_iarchive ia(datafile, boost::archive::no_header); 601 | try 602 | { 603 | ia >> beans_data; 604 | } 605 | catch (const exception &e) 606 | { 607 | goto do_merge_2; 608 | } 609 | 610 | data_array.push_back(beans_data); 611 | } 612 | 613 | do_merge_2: 614 | datafile.close(); 615 | // write to file 616 | for (int pos = 1; pos < _activefile; pos++) 617 | { 618 | string mergefile = fileprev + to_string(pos); 619 | fstream datafile; 620 | datafile.open(filepath + mergefile, ios::binary | ios::out | ios::app); 621 | if (!datafile) 622 | { 623 | _response += cmd_prompt + "the data file " + file + " open failure!\n"; 624 | } 625 | long mergefile_end = datafile.tellg(); 626 | bitcask_data merge_data = data_array.back(); 627 | bitcask_index merge_index = read_index(merge_data.key); 628 | while (mergefile_end < filemax && (filemax - mergefile_end) < sizeof(merge_data)) 629 | { 630 | // datafile.write((char *)(&merge_data), sizeof(merge_data)); 631 | boost::archive::binary_oarchive oa(datafile, boost::archive::no_header); 632 | oa << merge_data; 633 | merge_data = data_array.back(); 634 | merge_index = read_index(merge_data.key); 635 | merge_index.value_pos = mergefile_end; 636 | merge_index.file_id = mergefile; 637 | index[merge_data.key] = merge_index; 638 | mergefile_end += sizeof(merge_data); 639 | data_array.pop_back(); 640 | merge_data = data_array.back(); 641 | } 642 | datafile.close(); 643 | } 644 | if (data_array.size() == 0) 645 | { 646 | _activefile--; 647 | } 648 | else 649 | { 650 | fstream newdatafile; 651 | newdatafile.open(filepath + file, ios::binary | ios::out); 652 | if (!newdatafile) 653 | { 654 | _response += cmd_prompt + "the data file " + file + " open failure!\n"; 655 | } 656 | newdatafile.seekg(0, ios::beg); 657 | for (auto data : data_array) 658 | { 659 | // newdatafile.write((char *)(&data), sizeof(data)); 660 | boost::archive::binary_oarchive oa(newdatafile, boost::archive::no_header); 661 | oa << data; 662 | } 663 | newdatafile.close(); 664 | break; 665 | } 666 | } 667 | } 668 | } 669 | 670 | void bitcask::flush() 671 | { 672 | // write index file to index file hint.bin 673 | fstream hint; 674 | hint.open(filepath + "hint.bin", ios::binary | ios::out); 675 | if (!hint) 676 | { 677 | _response += cmd_prompt + "the file hint.bin open failure!\n"; 678 | } 679 | hint.seekg(0, ios::beg); 680 | for (auto indexinfo : index) 681 | { 682 | if (indexinfo.second.value_valid == true) 683 | { 684 | // hint.write((char *)(&indexinfo.second), sizeof(indexinfo.second)); 685 | boost::archive::binary_oarchive oa(hint, boost::archive::no_header); 686 | oa << indexinfo.second; 687 | } 688 | } 689 | // hint.close(); 690 | // write active file number to file filelog.bin 691 | fstream filelog; 692 | filelog.open(filepath + "filelog.bin", ios::binary | ios::out); 693 | if (!filelog) 694 | { 695 | _response += cmd_prompt + "the filelog.bin open failuer!\n"; 696 | } 697 | filelog.write((char *)(&_activefile), sizeof(int)); 698 | } 699 | 700 | string bitcask::Get(string key) 701 | { 702 | if (this->_start == true) 703 | { 704 | bitcask_data bc_data = read_data(key); 705 | if (bc_data.key.length() == 0) 706 | cout << key + " does not exist!" << endl; 707 | return bc_data.value; 708 | } 709 | else 710 | { 711 | cout << "please open a database first" << endl; 712 | return ""; 713 | } 714 | } 715 | 716 | void bitcask::Put(string key, string value) 717 | { 718 | if (this->_start == true) 719 | insert_data(key, value); 720 | else 721 | cout << "please open a database first" << endl; 722 | } 723 | 724 | void bitcask::Open(string path) 725 | { 726 | if (this->_start == false) 727 | init(path); 728 | else 729 | cout << "already open a database, please close first" << endl; 730 | } 731 | 732 | void bitcask::Close() 733 | { 734 | if (this->_start) 735 | { 736 | // merge(); 737 | flush(); 738 | _start = false; 739 | _activefile = 0; 740 | _finish = false; 741 | _response = ""; 742 | filepath = ""; 743 | } 744 | else 745 | cout << "please open a database first" << endl; 746 | } 747 | ``` 748 | 749 | 750 | 751 | --- 752 | 753 | 754 | 755 | #### `main.cpp` 756 | 757 | ```cpp 758 | #include "bitcask.h" 759 | 760 | #define db_path "/home/miracle/work/task6/bitcask/db/" 761 | 762 | int main() 763 | { 764 | bitcask *db = new bitcask; 765 | db->Open(db_path); 766 | 767 | db->Put("key1", "value1"); 768 | cout << db->Get("key1") << endl; 769 | db->Close(); 770 | cout << db->Get("key1") << endl; 771 | db->Open(db_path); 772 | cout << db->Get("key1") << endl; 773 | cout << db->Get("key2") << endl; 774 | return 0; 775 | } 776 | 777 | ``` 778 | 779 | 780 | 781 | ### 实验结果 782 | 783 | #### 挂载BlobFS 784 | 785 | 将BlobFS挂载到当前目录下的db文件夹,用作数据库存储 786 | 787 | ![image-20221222201015837](./README.assets/image-20221222201015837.png) 788 | 789 | #### 运行测试程序 790 | 791 | ![image-20221222201134790](./README.assets/image-20221222201134790.png) 792 | 793 | ![image-20221222201241336](./README.assets/image-20221222201241336.png) 794 | 795 | 对比测试流程,可以看到 796 | 797 | 1. 先打开了数据库,程序检测到该处没有任何文件于是进行初始化,并提示hint及filelog文件创建成功 798 | 2. `Put("Key1", "value1")` 799 | 3. `Read("Key1")` 此时输出了对应的值 `value1` 800 | 4. 关闭数据库 801 | 5. 再次读取 `Key1`的值(`Read("Key1")`),提示数据库未打开 802 | 6. 重新打开数据库 803 | 7. 分别获取`Key1`, `Key2` 的值 804 | 8. 可以看到获取`Key1`值时输出了正确的`value1`,获取`Key2`值时提示`Key2`不存在 805 | 806 | 经过以上测试通过了Open,Close,Put,Get等基本语法要求 807 | 808 | #### 检查二进制存储文件 809 | 810 | ![image-20221222202053431](./README.assets/image-20221222202053431.png) 811 | 812 | 可以看出符合之前设计的存储结构 813 | 814 | ## 实验结论和心得体会 815 | 816 | 本次实验学习了BlobFS相关特性并在其上实现了KV数据库的Open,Close,Put,Get操作。 817 | -------------------------------------------------------------------------------- /work/task6/README.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task6/README.pdf -------------------------------------------------------------------------------- /work/task6/bitcask/.gitignore: -------------------------------------------------------------------------------- 1 | main 2 | 3 | # Prerequisites 4 | *.d 5 | 6 | # Object files 7 | *.o 8 | *.ko 9 | *.obj 10 | *.elf 11 | 12 | # Linker output 13 | *.ilk 14 | *.map 15 | *.exp 16 | 17 | # Precompiled Headers 18 | *.gch 19 | *.pch 20 | 21 | # Libraries 22 | *.lib 23 | *.a 24 | *.la 25 | *.lo 26 | 27 | # Shared objects (inc. Windows DLLs) 28 | *.dll 29 | *.so 30 | *.so.* 31 | *.dylib 32 | 33 | # Executables 34 | *.exe 35 | *.out 36 | *.app 37 | *.i*86 38 | *.x86_64 39 | *.hex 40 | 41 | # Debug files 42 | *.dSYM/ 43 | *.su 44 | *.idb 45 | *.pdb 46 | 47 | # Kernel Module Compile Results 48 | *.mod* 49 | *.cmd 50 | .tmp_versions/ 51 | modules.order 52 | Module.symvers 53 | Mkfile.old 54 | dkms.conf 55 | -------------------------------------------------------------------------------- /work/task6/bitcask/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | @ g++ -std=c++11 main.cpp bitcask.cpp -o main -lboost_serialization -lboost_date_time 3 | 4 | run: all 5 | @ ./main 6 | @ rm -rf main 7 | 8 | run_once: run clean 9 | 10 | clean: 11 | @ rm -rf ./db/* 12 | @ rm -rf main -------------------------------------------------------------------------------- /work/task6/bitcask/bitcask-intro.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task6/bitcask/bitcask-intro.pdf -------------------------------------------------------------------------------- /work/task6/bitcask/bitcask.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "bitcask.h" 9 | using namespace std; 10 | 11 | bitcask::bitcask() 12 | { 13 | _start = false; 14 | _activefile = 0; 15 | _finish = false; 16 | _response = ""; 17 | filepath = ""; 18 | } 19 | 20 | bitcask::~bitcask() 21 | { 22 | if (this->_start) 23 | { 24 | merge(); 25 | flush(); 26 | } 27 | } 28 | 29 | void bitcask::init(string path) 30 | { 31 | this->filepath = path; 32 | this->_start = true; 33 | long len; 34 | fstream hint; 35 | hint.open(filepath + "hint.bin", ios::binary | ios::out | ios::app); 36 | if (!hint) 37 | { 38 | cout << "the file hint.bin open failure or maybe not exist!\n"; 39 | } 40 | len = hint.tellg(); 41 | if (len == 0) 42 | { 43 | cout << "create file hint.bin successful!\n"; 44 | } 45 | else 46 | { 47 | /* 48 | load index to memory 49 | */ 50 | // cout << "loading index" << endl; 51 | bitcask_index search; 52 | fstream hint; 53 | hint.open(filepath + "hint.bin", ios::binary | ios::in); 54 | if (!hint) 55 | { 56 | cout << "the file hint.bin open failure or maybe not exist!\n"; 57 | } 58 | 59 | while (hint) 60 | { 61 | boost::archive::binary_iarchive ia(hint, boost::archive::no_header); 62 | try 63 | { 64 | ia >> search; 65 | } 66 | catch (const exception &e) 67 | { 68 | // cout << "read end" << endl; 69 | goto do_load; 70 | } 71 | // cout << "loading:" << search.key << endl; 72 | bitcask_index insert; 73 | insert.file_id = search.file_id; 74 | insert.value_pos = search.value_pos; 75 | insert.value_len = search.value_len; 76 | insert.timestamp = search.timestamp; 77 | insert.value_valid = search.value_valid; 78 | index[search.key] = insert; 79 | } 80 | } 81 | do_load: 82 | fstream filelog; 83 | filelog.open(filepath + "filelog.bin", ios::binary | ios::in); 84 | if (!filelog) 85 | { 86 | cout << "the file filelog.bin open failure or maybe not exist!\n"; 87 | } 88 | filelog.read((char *)(&_activefile), sizeof(int)); 89 | filelog.close(); 90 | if (_activefile == 0) 91 | { 92 | cout << "create file filelog.bin successful!\n"; 93 | _activefile = 1; 94 | filelog.open(filepath + "filelog.bin", ios::binary | ios::out | ios::app); 95 | filelog.write((char *)(&_activefile), sizeof(int)); 96 | filelog.close(); 97 | return; 98 | } 99 | _start = true; 100 | } 101 | 102 | uint32_t bitcask::crc32(string value) 103 | { 104 | boost::crc_32_type result; 105 | result.process_bytes(value.c_str(), value.length()); 106 | return result.checksum(); 107 | } 108 | 109 | void bitcask::insert_data(string key, string value) 110 | { 111 | bitcask_index search = read_index(key); 112 | if (search.key != "") 113 | { 114 | cout << "the data " + key + " already exist!\n"; 115 | return update_data(key, value); 116 | } 117 | // add data 118 | bitcask_data newdata; 119 | newdata.key = key; 120 | newdata.key_len = int(key.length()); 121 | newdata.value = value; 122 | newdata.value_len = int(value.length()); 123 | newdata.crc = crc32(value); 124 | // newdata.timestamp=time(0); 125 | newdata.timestamp = boost::posix_time::microsec_clock::universal_time(); 126 | 127 | // add index 128 | fstream datafile; 129 | bitcask_index newindex; 130 | newindex.key = key; 131 | newindex.file_id = fileprev + to_string(_activefile); 132 | datafile.open(filepath + newindex.file_id, ios::binary | ios::out | ios::app); 133 | if (!datafile) 134 | cout << cmd_prompt + newindex.file_id + " open failure\n"; 135 | newindex.value_pos = datafile.tellg(); 136 | if (newindex.value_pos > filemax || filemax - newindex.value_pos < sizeof(newdata)) 137 | { 138 | _activefile++; 139 | newindex.file_id = fileprev + to_string(_activefile); 140 | } 141 | newindex.timestamp = newdata.timestamp; 142 | newindex.value_len = sizeof(newdata); 143 | newindex.value_valid = true; 144 | datafile.close(); 145 | write_data(newdata); 146 | write_index(newindex); 147 | // add to memory index array 148 | index[key] = newindex; 149 | // cout << "the data " + key + " insert successful\n"; 150 | } 151 | 152 | void bitcask::write_data(bitcask_data newdata) 153 | { 154 | string file = fileprev + to_string(_activefile); 155 | fstream datafile; 156 | datafile.open(filepath + file, ios::binary | ios::out | ios::app); 157 | if (!datafile) 158 | cout << file + " open file " + file + " failure!\n"; 159 | // datafile.write((char *)(&newdata),sizeof(newdata)); 160 | // data_append(datafile, newdata); 161 | boost::archive::binary_oarchive oa(datafile, boost::archive::no_header); 162 | oa << newdata; 163 | datafile.close(); 164 | } 165 | 166 | void bitcask::write_index(bitcask_index newindex) 167 | { 168 | fstream hint; 169 | hint.open(filepath + "hint.bin", ios::binary | ios::out | ios::app); 170 | if (!hint) 171 | { 172 | cout << "the file hint.bin open failure!\n"; 173 | } 174 | // hint.write((char *)(&newindex), sizeof(newindex)); 175 | // cout << "writing index: " << newindex.key << newindex.timestamp << endl; 176 | boost::archive::binary_oarchive oa(hint, boost::archive::no_header); 177 | oa << newindex; 178 | hint.close(); 179 | } 180 | 181 | bitcask_index bitcask::read_index(string key) 182 | { 183 | 184 | bitcask_index search; 185 | if ("" == key) 186 | { 187 | return search; 188 | } 189 | for (auto indexinfo : index) 190 | { 191 | if (indexinfo.first == key) 192 | { 193 | { 194 | 195 | search.key = indexinfo.first; 196 | search.file_id = indexinfo.second.file_id; 197 | search.value_pos = indexinfo.second.value_pos; 198 | search.value_len = indexinfo.second.value_len; 199 | search.value_valid = indexinfo.second.value_valid; 200 | search.timestamp = indexinfo.second.timestamp; 201 | // cout << "got key for " << key << " ,valid " << search.value_valid << endl; 202 | return search; 203 | } 204 | } 205 | } 206 | return search; 207 | } 208 | 209 | bitcask_data bitcask::read_data(string key) 210 | { 211 | bitcask_data search_data; 212 | bitcask_index search_index = read_index(key); 213 | if (search_index.value_valid == true) 214 | { 215 | string file = search_index.file_id; 216 | // cout << "reading file" << file << endl; 217 | fstream datafile; 218 | datafile.open(filepath + file, ios::binary | ios::in); 219 | if (!datafile) 220 | cout << "open file " + file + " failure\n"; 221 | 222 | boost::archive::binary_iarchive ia(datafile, boost::archive::no_header); 223 | if (search_index.value_pos) 224 | { 225 | datafile.seekg(search_index.value_pos, ios::beg); 226 | // cout << "seeking to" << search_index.value_pos << endl; 227 | } 228 | // datafile.read((char *)(&search_data),sizeof(search_data)); 229 | // cout << "reading datafile" << filepath + file << endl; 230 | // cout << "key = " << search_index.key << endl; 231 | // cout << "value_pos = " << search_index.value_pos << endl; 232 | // cout << "ts = " << search_index.timestamp << endl; 233 | // cout << "file pos = " << datafile.tellp() << endl; 234 | // search_data.serialize(ia, 0); 235 | ia >> search_data; 236 | // cout << "file pos = " << datafile.tellp() << endl; 237 | // cout << "on-disk ts = " << search_data.timestamp << endl; 238 | // return search_data; 239 | } 240 | // else 241 | // cout << "the data " + key + " does not exist!\n"; 242 | return search_data; 243 | } 244 | 245 | void bitcask::read_datainfo(string key) 246 | { 247 | bitcask_data data = read_data(key); 248 | bitcask_index index = read_index(key); 249 | if (index.value_valid == true) 250 | { 251 | _response += cmd + "key :" + key + "\n"; 252 | _response += cmd + "value :" + data.value + "\n"; 253 | _response += cmd + "crc :" + to_string(data.crc) + "\n"; 254 | _response += cmd + "file id :" + index.file_id + "\n"; 255 | _response += cmd + "value pos :" + to_string(index.value_pos) + "\n"; 256 | _response += cmd + "value length :" + to_string(data.value_len) + "\n"; 257 | // cout << _response; 258 | //_response+=cmd+"time :"+ data.timestamp; 259 | //_response+=cmd+"time :"+ctime(&data.timestamp); 260 | } 261 | else 262 | return; 263 | } 264 | 265 | void bitcask::delete_data(string key) 266 | { 267 | bitcask_index delindex = read_index(key); 268 | if (delindex.key != "") 269 | { 270 | delindex.value_valid = false; 271 | index[key] = delindex; 272 | cout << "the data " + key + " already delete!\n"; 273 | } 274 | else 275 | _response += cmd_prompt + "the data " + key + " does not exist!\n"; 276 | } 277 | 278 | void bitcask::update_data(string key, string value) 279 | { 280 | 281 | bitcask_index search = read_index(key); 282 | if (search.key == "") 283 | { 284 | cout << "the data " + key + " does not exist!\n"; 285 | return; 286 | } 287 | // update data 288 | fstream datafile, hintfile; 289 | bitcask_data updata; 290 | bitcask_index upindex = read_index(key); 291 | updata.key = key; 292 | updata.key_len = int(key.length()); 293 | updata.value = value; 294 | updata.value_len = int(value.length()); 295 | // updata.timestamp=time(0); 296 | updata.timestamp = boost::posix_time::microsec_clock::universal_time(); 297 | 298 | // update index 299 | upindex.file_id = fileprev + to_string(_activefile); 300 | datafile.open(filepath + upindex.file_id, ios::binary | ios::in | ios::app); 301 | if (!datafile) 302 | _response += cmd_prompt + "the file " + upindex.file_id + " open failure!\n"; 303 | upindex.value_pos = datafile.tellg(); 304 | if (upindex.value_pos > filemax || filemax - upindex.value_pos < sizeof(updata)) 305 | { 306 | _activefile++; 307 | } 308 | upindex.timestamp = updata.timestamp; 309 | upindex.value_len = sizeof(updata); 310 | upindex.value_valid = true; 311 | datafile.close(); 312 | write_data(updata); 313 | update_index(upindex, key); 314 | _response += cmd_prompt + "the data " + key + " update successful\n"; 315 | } 316 | 317 | void bitcask::update_index(bitcask_index upindex, string key) 318 | { 319 | bitcask_index seaindex = read_index(key); 320 | seaindex.key = key; 321 | seaindex.file_id = upindex.file_id; 322 | seaindex.value_pos = upindex.value_pos; 323 | seaindex.value_len = upindex.value_len; 324 | seaindex.value_valid = upindex.value_valid; 325 | seaindex.timestamp = upindex.timestamp; 326 | index[key] = seaindex; 327 | } 328 | 329 | void bitcask::merge() 330 | { 331 | /* 332 | merge data in file 333 | function: delete data in file 334 | */ 335 | int beans = 1; 336 | long value_pos; 337 | vector data_array; 338 | // cout << "merge begin: activefile" << _activefile << endl; 339 | for (; beans <= _activefile; beans++) 340 | { 341 | 342 | string file = fileprev + to_string(beans); 343 | fstream datafile; 344 | datafile.open(filepath + file, ios::binary | ios::in); 345 | 346 | if (!datafile) 347 | { 348 | _response += cmd_prompt + "the data file " + file + " open failure!\n"; 349 | } 350 | bitcask_data beans_data; 351 | bitcask_index beans_index; 352 | datafile.seekg(0, ios::beg); 353 | // load to memory 354 | while (datafile) 355 | { 356 | boost::archive::binary_iarchive ia(datafile, boost::archive::no_header); 357 | try 358 | { 359 | ia >> beans_data; 360 | 361 | beans_index = read_index(beans_data.key); 362 | // cout << "ts:" << beans_data.timestamp << " vs " << beans_index.timestamp << endl; 363 | } 364 | catch (const exception &e) 365 | { 366 | goto do_merge; 367 | } 368 | 369 | if (beans_index.value_valid == true && beans_data.timestamp == beans_index.timestamp) 370 | { 371 | // cout << "pushing:" << beans_index.key << endl; 372 | data_array.push_back(beans_data); 373 | } 374 | } 375 | 376 | do_merge: 377 | for (auto data : data_array) 378 | { 379 | // cout << "dataarray = " << data.key << endl; 380 | } 381 | 382 | datafile.close(); 383 | // write to file 384 | datafile.open(filepath + file, ios::binary | ios::out); 385 | if (!datafile) 386 | { 387 | _response += cmd_prompt + "the data file " + file + " open failure!\n"; 388 | } 389 | datafile.seekg(0, ios::beg); 390 | 391 | for (auto data : data_array) 392 | { 393 | value_pos = datafile.tellg(); 394 | // datafile.write((char *)(&data),sizeof(data)); 395 | boost::archive::binary_oarchive oa(datafile, boost::archive::no_header); 396 | oa << data; 397 | bitcask_index seaindex = read_index(data.key); 398 | seaindex.value_pos = value_pos; 399 | index[data.key] = seaindex; 400 | } 401 | datafile.close(); 402 | data_array.clear(); 403 | } 404 | /* 405 | TODO merge file 406 | */ 407 | if (_activefile >= 2) 408 | { 409 | while (_activefile > 1) 410 | { 411 | string file = fileprev + to_string(_activefile); 412 | fstream datafile; 413 | datafile.open(filepath + file, ios::binary | ios::in); 414 | if (!datafile) 415 | { 416 | _response += cmd_prompt + "the data file " + file + " open failure!\n"; 417 | } 418 | bitcask_data beans_data; 419 | bitcask_index beans_index; 420 | datafile.seekg(0, ios::beg); 421 | // TODO load to memory maybe too big? 422 | while (datafile) 423 | { 424 | boost::archive::binary_iarchive ia(datafile, boost::archive::no_header); 425 | try 426 | { 427 | ia >> beans_data; 428 | } 429 | catch (const exception &e) 430 | { 431 | goto do_merge_2; 432 | } 433 | 434 | data_array.push_back(beans_data); 435 | } 436 | 437 | do_merge_2: 438 | datafile.close(); 439 | // write to file 440 | for (int pos = 1; pos < _activefile; pos++) 441 | { 442 | string mergefile = fileprev + to_string(pos); 443 | fstream datafile; 444 | datafile.open(filepath + mergefile, ios::binary | ios::out | ios::app); 445 | if (!datafile) 446 | { 447 | _response += cmd_prompt + "the data file " + file + " open failure!\n"; 448 | } 449 | long mergefile_end = datafile.tellg(); 450 | bitcask_data merge_data = data_array.back(); 451 | bitcask_index merge_index = read_index(merge_data.key); 452 | while (mergefile_end < filemax && (filemax - mergefile_end) < sizeof(merge_data)) 453 | { 454 | // datafile.write((char *)(&merge_data), sizeof(merge_data)); 455 | boost::archive::binary_oarchive oa(datafile, boost::archive::no_header); 456 | oa << merge_data; 457 | merge_data = data_array.back(); 458 | merge_index = read_index(merge_data.key); 459 | merge_index.value_pos = mergefile_end; 460 | merge_index.file_id = mergefile; 461 | index[merge_data.key] = merge_index; 462 | mergefile_end += sizeof(merge_data); 463 | data_array.pop_back(); 464 | merge_data = data_array.back(); 465 | } 466 | datafile.close(); 467 | } 468 | if (data_array.size() == 0) 469 | { 470 | _activefile--; 471 | } 472 | else 473 | { 474 | fstream newdatafile; 475 | newdatafile.open(filepath + file, ios::binary | ios::out); 476 | if (!newdatafile) 477 | { 478 | _response += cmd_prompt + "the data file " + file + " open failure!\n"; 479 | } 480 | newdatafile.seekg(0, ios::beg); 481 | for (auto data : data_array) 482 | { 483 | // newdatafile.write((char *)(&data), sizeof(data)); 484 | boost::archive::binary_oarchive oa(newdatafile, boost::archive::no_header); 485 | oa << data; 486 | } 487 | newdatafile.close(); 488 | break; 489 | } 490 | } 491 | } 492 | } 493 | 494 | void bitcask::flush() 495 | { 496 | // write index file to index file hint.bin 497 | fstream hint; 498 | hint.open(filepath + "hint.bin", ios::binary | ios::out); 499 | if (!hint) 500 | { 501 | _response += cmd_prompt + "the file hint.bin open failure!\n"; 502 | } 503 | hint.seekg(0, ios::beg); 504 | for (auto indexinfo : index) 505 | { 506 | if (indexinfo.second.value_valid == true) 507 | { 508 | // hint.write((char *)(&indexinfo.second), sizeof(indexinfo.second)); 509 | boost::archive::binary_oarchive oa(hint, boost::archive::no_header); 510 | oa << indexinfo.second; 511 | } 512 | } 513 | // hint.close(); 514 | // write active file number to file filelog.bin 515 | fstream filelog; 516 | filelog.open(filepath + "filelog.bin", ios::binary | ios::out); 517 | if (!filelog) 518 | { 519 | _response += cmd_prompt + "the filelog.bin open failuer!\n"; 520 | } 521 | filelog.write((char *)(&_activefile), sizeof(int)); 522 | } 523 | 524 | string bitcask::Get(string key) 525 | { 526 | if (this->_start == true) 527 | { 528 | bitcask_data bc_data = read_data(key); 529 | if (bc_data.key.length() == 0) 530 | cout << key + " does not exist!" << endl; 531 | return bc_data.value; 532 | } 533 | else 534 | { 535 | cout << "please open a database first" << endl; 536 | return ""; 537 | } 538 | } 539 | 540 | void bitcask::Put(string key, string value) 541 | { 542 | if (this->_start == true) 543 | insert_data(key, value); 544 | else 545 | cout << "please open a database first" << endl; 546 | } 547 | 548 | void bitcask::Open(string path) 549 | { 550 | if (this->_start == false) 551 | init(path); 552 | else 553 | cout << "already open a database, please close first" << endl; 554 | } 555 | 556 | void bitcask::Close() 557 | { 558 | if (this->_start) 559 | { 560 | // merge(); 561 | flush(); 562 | _start = false; 563 | _activefile = 0; 564 | _finish = false; 565 | _response = ""; 566 | filepath = ""; 567 | } 568 | else 569 | cout << "please open a database first" << endl; 570 | } 571 | -------------------------------------------------------------------------------- /work/task6/bitcask/bitcask.h: -------------------------------------------------------------------------------- 1 | #ifndef BITCASK_H_ 2 | #define BITCASK_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | using namespace std; 19 | 20 | #define filemax 1024 * 4096 // 4MB 21 | 22 | const string fileprev = "bitcask_data"; 23 | const string cmd_prompt = ">>> bitcask : "; 24 | const string cmd = ">>> "; 25 | const int number = 0; 26 | // data 27 | struct bitcask_data 28 | { 29 | string key; 30 | int key_len; 31 | string value; 32 | int value_len; 33 | boost::posix_time::ptime timestamp; 34 | uint32_t crc; 35 | // crc 36 | template 37 | void serialize(Archive &ar, const unsigned int version) 38 | { 39 | ar &key_len; 40 | ar &key; 41 | ar &value_len; 42 | ar &value; 43 | ar &crc; 44 | ar ×tamp; 45 | } 46 | }; 47 | 48 | // index 49 | struct bitcask_index 50 | { 51 | string key; 52 | string file_id; 53 | int value_pos; 54 | int value_len; 55 | boost::posix_time::ptime timestamp; 56 | bool value_valid; 57 | 58 | template 59 | void serialize(Archive &ar, const unsigned int version) 60 | { 61 | ar &key; 62 | ar &file_id; 63 | ar &value_pos; 64 | ar &value_len; 65 | ar ×tamp; 66 | ar &value_valid; 67 | } 68 | 69 | bitcask_index() 70 | { 71 | value_valid = false; 72 | } 73 | }; 74 | 75 | // bitcask 76 | class bitcask 77 | { 78 | private: 79 | unordered_map index; 80 | int _activefile; 81 | bool _start; 82 | bool _finish; 83 | string _response; 84 | string filepath; 85 | 86 | private: 87 | void init(string path); 88 | uint32_t crc32(string value); 89 | void insert_data(string key, string value); 90 | void write_data(bitcask_data newdata); 91 | void write_index(bitcask_index newindex); 92 | bitcask_data read_data(string key); 93 | void read_datainfo(string key); 94 | bitcask_index read_index(string key); 95 | void delete_data(string key); 96 | void update_data(string key, string value); 97 | void update_index(bitcask_index upindex, string key); 98 | void merge(); 99 | void flush(); // flush index :hint.bin 100 | 101 | public: 102 | bitcask(); 103 | string Get(string key); 104 | void Put(string key, string value); 105 | void Open(string path); 106 | void Close(); 107 | ~bitcask(); 108 | 109 | }; 110 | 111 | #endif /* BITCASK_H_ */ 112 | -------------------------------------------------------------------------------- /work/task6/bitcask/main.cpp: -------------------------------------------------------------------------------- 1 | #include "bitcask.h" 2 | 3 | #define db_path "/home/miracle/work/task6/bitcask/db/" 4 | 5 | int main() 6 | { 7 | bitcask *db = new bitcask; 8 | db->Open(db_path); 9 | 10 | db->Put("key1", "value1"); 11 | cout << db->Get("key1") << endl; 12 | db->Close(); 13 | cout << db->Get("key1") << endl; 14 | db->Open(db_path); 15 | cout << db->Get("key1") << endl; 16 | cout << db->Get("key2") << endl; 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /work/task6/计算机系统综合实验六.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiracleHYH/CS_Exp_ZNS/339f079a6805fd375e13d4f0acad8bc5bead688f/work/task6/计算机系统综合实验六.pptx --------------------------------------------------------------------------------