已经对相关的基础概念有一定的了解,比如裸盘,文件系统,读写相关裸盘,裸盘挂载使用,内核插入文件系统的方式,相关操作io的库或者函数(io_uring, readv,writev, mmap等),以及用户态文件系统fuse。
接下来对spdk进行了解。
0:总结
1:对spdk进行安装以及demo测试。
2:对宏观上spdk控制磁盘的架构和方案进行认识。
3:基于已经能通过spdk对磁盘进行访问的功能,后续按需按业务就得思考了(如何有效管理磁盘?)。
1:了解相关的文件系统
分布式文件系统一般提供了网络接口,对文件进行索引。
io的读写性能,硬件已经不是瓶颈,软件有一定限制,可以用spdk。
文件系统的最底层,其实是操作磁盘,各种类型的磁盘性能已经提升,性能瓶颈就在软件,有了spdk。
类似dpdk接管网卡,提升网络性能,spdk接管磁盘,提升磁盘io读写性能。
2:spdk的环境搭建。
vfio ===》/dev/vfio/vfio 内核有个vfio模块, 有个vfio设备文件 然后用户层操作vfio接口交互(libvfio-user)。
uio ====》/sys/class/uio 使用spdk启动后绑定磁盘后可以看到。 (可以深入uio的交互逻辑以及相关其他详细信息)
ubuntu@ubuntu:/dev$ ls nvme
nvme0 nvme0n1 nvme-fabrics
ubuntu@ubuntu:/dev$ su
root@ubuntu:/dev# cd /home/ubuntu/spdk/spdk/
root@ubuntu:/home/ubuntu/spdk/spdk# ./scripts/setup.sh
0000:03:00.0 (15ad 07f0): nvme -> uio_pci_generic
#查看uio设备编号和地址的关系
root@ubuntu:/home/ubuntu/spdk/spdk# ls -l /sys/class/uio/uio*/device
lrwxrwxrwx 1 root root 0 Sep 11 03:52 /sys/class/uio/uio0/device -> ../../../0000:03:00.0
root@ubuntu:/sys/class/uio/uio0# ll
...
lrwxrwxrwx 1 root root 0 Sep 11 03:52 device -> ../../../0000:03:00.0/
#可以查看已经绑定的地址 这里显示的是大页内存的情况
root@ubuntu:/home/ubuntu/spdk/spdk# ./scripts/setup.sh status
Hugepages
node hugesize free / total
node0 1048576kB 0 / 0
node0 2048kB 1024 / 1024
Type BDF Vendor Device NUMA Driver Device Block devices
NVMe 0000:03:00.0 15ad 07f0 unknown uio_pci_generic - -
3:安装spdk并支持fio模块
$ git clone https://github.com/spdk/spdk.git
$ cd spdk
$ git submodule update --init
$ ./scripts/pkgdep.sh #涉及pip相关下载 需要换源 pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
$ ./configure --with-fio=/path/to/fio/repo #fio安装可执行目录 root@ubuntu:/home/ubuntu/spdk/spdk# ./configure --with-fio=/home/ubuntu/uring/fio-fio-3.37
$ make
$ ./script/setup.sh
#遇到问题 Failed to connect to github.com port 443 after 21094 ms: Connection refused
#是代理问题导致 可以设置 首先查看网络代理端口 设置 -> 网络和Internet -> 代理
#如果开了代理 则
ubuntu@ubuntu:~/uring/fio-fio-3.37$ git config --global http.proxy 127.0.0.1:7888
ubuntu@ubuntu:~/uring/fio-fio-3.37$ git config --global https.proxy 127.0.0.1:7888
ubuntu@ubuntu:~/uring/fio-fio-3.37$ git config --list #-l
#关闭上面的代理设置
ubuntu@ubuntu:~/uring/fio-fio-3.37$ git config --global --unset http.proxy
ubuntu@ubuntu:~/uring/fio-fio-3.37$ git config --global --unset https.proxy
#如果安装过程中,有其他问题 大多数是网络不够好的原因,多试几次就好
root@ubuntu:/home/ubuntu/spdk/spdk# ./configure --with-fio=/home/ubuntu/uring/fio-fio-3.37
Using default SPDK env in /home/ubuntu/spdk/spdk/lib/env_dpdk
Using default DPDK in /home/ubuntu/spdk/spdk/dpdk/build
Configuring ISA-L (logfile: /home/ubuntu/spdk/spdk/.spdk-isal.log)...done.
Configuring ISA-L-crypto (logfile: /home/ubuntu/spdk/spdk/.spdk-isal-crypto.log)...done.
Creating mk/config.mk...done.
Creating mk/cc.flags.mk...done.
Type 'make' to build.
root@ubuntu:/home/ubuntu/spdk/spdk# make
#安装后启动 接管了虚拟机新增的nvme磁盘 这里注意这个地址
root@ubuntu:/home/ubuntu/spdk/spdk# ./scripts/setup.sh
0000:03:00.0 (15ad 07f0): nvme -> uio_pci_generic
4:了解spdk下提供的相关脚本手动管理。
应该参考官网文档进行了解,SPDK: Storage Performance Development Kit
1:需要了解 build目录下生成的相关bin可执行文件的功能。
2:了解script目录下提供的相关脚本(需要对架构,以及函数调用的流程进行了解)。
#我可以理解未vhost就是spdk中对各种设备的一个虚拟化适配层(实现零拷贝,支持多种设备)
#手动对流程进行了解 启动一个vhost
root@ubuntu:/home/ubuntu/spdk/spdk/build/bin# ./vhost -c ../examples/hello_bdev.json
[2024-09-11 04:19:13.990166] Starting SPDK v24.09-pre / DPDK 24.03.0 initialization...
[2024-09-11 04:19:13.990285] [ DPDK EAL parameters: vhost --no-shconf -c 0x1 --huge-unlink --no-telemetry --log-level=lib.eal:6 --log-level=lib.cryptodev:5 --log-level=user1:6 --iova-mode=pa --base-virtaddr=0x200000000000 --match-allocations --file-prefix=spdk_pid26150 ]
[2024-09-11 04:19:14.141607] app.c: 909:spdk_app_start: *NOTICE*: Total cores available: 1
[2024-09-11 04:19:14.209328] reactor.c: 943:reactor_run: *NOTICE*: Reactor started on core 0
#使用脚本进行观察 vhost实际上是server端 这里连接要对应 显示不一样
root@ubuntu:/home/ubuntu/spdk/spdk# ./scripts/spdkcli.py
SPDK CLI v0.1
/bdevs> ls
o- bdevs .......................................................................................... [...]
o- aio ..................................................................................... [Bdevs: 0]
o- error ................................................................................... [Bdevs: 0]
o- iscsi ................................................................................... [Bdevs: 0]
o- logical_volume .......................................................................... [Bdevs: 0]
o- malloc .................................................................................. [Bdevs: 0]
o- null .................................................................................... [Bdevs: 0]
o- nvme .................................................................................... [Bdevs: 1]
| o- Nvme0n1 .......................... [11fd19c0-3da8-2017-000c-296beb492b8c, Size=20.0G, Not claimed]
o- raid_volume ............................................................................. [Bdevs: 0]
o- rbd ..................................................................................... [Bdevs: 0]
o- split_disk .............................................................................. [Bdevs: 0]
o- uring ................................................................................... [Bdevs: 0]
o- virtioblk_disk .......................................................................... [Bdevs: 0]
o- virtioscsi_disk ......................................................................... [Bdevs: 0]
/bdevs> /bdevs> exit
#spdk 对很多的控制接口的管理都是通过rpc 可以通过脚本查看以及创建,涉及相关的模块及流程
root@ubuntu:/home/ubuntu/spdk/spdk#
#按默认启动一个新的vhost
root@ubuntu:/home/ubuntu/spdk/spdk/build/bin# ./vhost -S /var/tmp -m 0x3
[2024-09-11 04:37:39.894391] Starting SPDK v24.09-pre / DPDK 24.03.0 initialization...
[2024-09-11 04:37:39.894598] [ DPDK EAL parameters: vhost --no-shconf -c 0x3 --huge-unlink --no-telemetry --log-level=lib.eal:6 --log-level=lib.cryptodev:5 --log-level=user1:6 --iova-mode=pa --base-virtaddr=0x200000000000 --match-allocations --file-prefix=spdk_pid26210 ]
[2024-09-11 04:37:40.032618] app.c: 909:spdk_app_start: *NOTICE*: Total cores available: 2
[2024-09-11 04:37:40.079872] reactor.c: 943:reactor_run: *NOTICE*: Reactor started on core 1
[2024-09-11 04:37:40.080050] reactor.c: 943:reactor_run: *NOTICE*: Reactor started on core 0
#使用rpc.py创建一个 bdev
root@ubuntu:/home/ubuntu/spdk/spdk# ./scripts/rpc.py -h
root@ubuntu:/home/ubuntu/spdk/spdk# ./scripts/rpc.py bdev_malloc_create 64 512 -b Malloc0
Malloc0
root@ubuntu:/home/ubuntu/spdk/spdk# ./scripts/spdkcli.py ls
o- / ............................................................................... [...]
o- bdevs ......................................................................... [...]
| o- aio .................................................................... [Bdevs: 0]
| o- error .................................................................. [Bdevs: 0]
| o- iscsi .................................................................. [Bdevs: 0]
| o- logical_volume ......................................................... [Bdevs: 0]
| o- malloc ................................................................. [Bdevs: 1]
| | o- Malloc0 ......... [9fb50102-4c6c-414f-9675-336ac2408f1c, Size=64.0M, Not claimed]
| o- null ................................................................... [Bdevs: 0]
| o- nvme ................................................................... [Bdevs: 0]
| o- raid_volume ............................................................ [Bdevs: 0]
| o- rbd .................................................................... [Bdevs: 0]
| o- split_disk ............................................................. [Bdevs: 0]
| o- uring .................................................................. [Bdevs: 0]
| o- virtioblk_disk ......................................................... [Bdevs: 0]
| o- virtioscsi_disk ........................................................ [Bdevs: 0]
o- lvol_stores ........................................................ [Lvol stores: 0]
o- vhost ......................................................................... [...]
o- block ....................................................................... [...]
o- scsi ........................................................................ [...]
#继续创建一个vhost 并给vhost分配位置
#理解 vhost是一个适配层 可以适配所有的,target属于真正的目标磁盘,进行管理。
./scripts/spdkcli.py ls
./scripts/rpc.py bdev_malloc_create 64 512 -b Malloc0
./scripts/spdkcli.py ls
netstat -anop|grep vhost
./scripts/rpc.py vhost_create_scsi_controller --cpumask 0x1 vhost.0
netstat -anop|grep vhost
./scripts/spdkcli.py ls
./scripts/rpc.py vhost_scsi_controller_add_target vhost.0 1 Malloc0
./scripts/spdkcli.py ls
./scripts/rpc.py vhost_scsi_controller_add_target vhost.0 0 Malloc0
#上面的测试需要基于一个vhost启动去做连接
^Croot@ubuntu:/home/ubuntu/spdk/spdk/build/bin# ./vhost -S /var/tmp -m 0x3
[2024-09-11 04:37:39.894391] Starting SPDK v24.09-pre / DPDK 24.03.0 initialization...
[2024-09-11 04:37:39.894598] [ DPDK EAL parameters: vhost --no-shconf -c 0x3 --huge-unlink --no-telemetry --log-level=lib.eal:6 --log-level=lib.cryptodev:5 --log-level=user1:6 --iova-mode=pa --base-virtaddr=0x200000000000 --match-allocations --file-prefix=spdk_pid26210 ]
[2024-09-11 04:37:40.032618] app.c: 909:spdk_app_start: *NOTICE*: Total cores available: 2
[2024-09-11 04:37:40.079872] reactor.c: 943:reactor_run: *NOTICE*: Reactor started on core 1
[2024-09-11 04:37:40.080050] reactor.c: 943:reactor_run: *NOTICE*: Reactor started on core 0
VHOST_CONFIG: (/var/tmp/vhost.0) logging feature is disabled in async copy mode
VHOST_CONFIG: (/var/tmp/vhost.0) vhost-user server: socket created, fd: 226
VHOST_CONFIG: (/var/tmp/vhost.0) binding succeeded
创建一个vhost,可以看到已经新创建了一个socket,以及在目录层级中也能看到。 分配vhost,还未进行
vhost_scsi_controller_add_target 创建一个target,并与逻辑单元进行关联。
这里创建了两个
相关接口就属于业务流程了,待研究相关流程接口。
可以看到,接管磁盘,可以在用户层自己做磁盘的控制。
5:运行自带的example
可以看到 使用bdev的方式操作磁盘,读写已经成功。
root@ubuntu:/home/ubuntu/spdk/spdk/build/examples# ./hello_bdev --json hello_bdev.json
[2024-09-11 05:12:00.875144] Starting SPDK v24.09-pre / DPDK 24.03.0 initialization...
[2024-09-11 05:12:00.876148] [ DPDK EAL parameters: hello_bdev --no-shconf -c 0x1 --huge-unlink --no-telemetry --log-level=lib.eal:6 --log-level=lib.cryptodev:5 --log-level=user1:6 --iova-mode=pa --base-virtaddr=0x200000000000 --match-allocations --file-prefix=spdk_pid26425 ]
[2024-09-11 05:12:01.011770] app.c: 909:spdk_app_start: *NOTICE*: Total cores available: 1
[2024-09-11 05:12:01.067633] reactor.c: 943:reactor_run: *NOTICE*: Reactor started on core 0
[2024-09-11 05:12:01.222992] hello_bdev.c: 222:hello_start: *NOTICE*: Successfully started the application
[2024-09-11 05:12:01.223091] hello_bdev.c: 231:hello_start: *NOTICE*: Opening the bdev Malloc0
[2024-09-11 05:12:01.223107] bdev.c:8114:bdev_open_ext: *NOTICE*: Currently unable to find bdev with name: Malloc0
[2024-09-11 05:12:01.223118] hello_bdev.c: 235:hello_start: *ERROR*: Could not open bdev: Malloc0
[2024-09-11 05:12:01.223129] app.c:1053:spdk_app_stop: *WARNING*: spdk_app_stop'd on non-zero
[2024-09-11 05:12:01.346120] hello_bdev.c: 309:main: *ERROR*: ERROR starting application
root@ubuntu:/home/ubuntu/spdk/spdk/build/examples# ./hello_bdev --json hello_bdev.json -b Nvme0n1
[2024-09-11 05:12:55.397512] Starting SPDK v24.09-pre / DPDK 24.03.0 initialization...
[2024-09-11 05:12:55.398311] [ DPDK EAL parameters: hello_bdev --no-shconf -c 0x1 --huge-unlink --no-telemetry --log-level=lib.eal:6 --log-level=lib.cryptodev:5 --log-level=user1:6 --iova-mode=pa --base-virtaddr=0x200000000000 --match-allocations --file-prefix=spdk_pid26428 ]
[2024-09-11 05:12:55.533299] app.c: 909:spdk_app_start: *NOTICE*: Total cores available: 1
[2024-09-11 05:12:55.589472] reactor.c: 943:reactor_run: *NOTICE*: Reactor started on core 0
[2024-09-11 05:12:55.730733] hello_bdev.c: 222:hello_start: *NOTICE*: Successfully started the application
[2024-09-11 05:12:55.730872] hello_bdev.c: 231:hello_start: *NOTICE*: Opening the bdev Nvme0n1
[2024-09-11 05:12:55.730907] hello_bdev.c: 244:hello_start: *NOTICE*: Opening io channel
[2024-09-11 05:12:55.731352] hello_bdev.c: 138:hello_write: *NOTICE*: Writing to the bdev
[2024-09-11 05:12:55.760169] hello_bdev.c: 117:write_complete: *NOTICE*: bdev io write completed successfully
[2024-09-11 05:12:55.760365] hello_bdev.c: 84:hello_read: *NOTICE*: Reading io
[2024-09-11 05:12:55.760924] hello_bdev.c: 65:read_complete: *NOTICE*: Read string from bdev : Hello World!
[2024-09-11 05:12:55.760977] hello_bdev.c: 74:read_complete: *NOTICE*: Stopping app
6:spdk提供的两种测试fio性能方式。
可以这样理解吗:nvme直接与磁盘进行通信交互,bdev是基于各种磁盘类型协议进行过封装,支持nvme类型的设备。
#spdk提供两种测试方式 可以使用fio测试其他方式读写磁盘的能力
#1:bdev LD_PRELOAD hook的方式 dpdk提供了两种方式 spdk/spdk/build/fio/目录下
#使用bdev进行测试时 注意bdev.fio脚本 用到json文件 按rpc进行通信。 注意其中设置的
root@ubuntu:/home/ubuntu/spdk/fio_script# LD_PRELOAD=/home/ubuntu/spdk/spdk/build/fio/spdk_bdev /home/ubuntu/uring/fio-fio-3.37/fio bdev.fio
test: (g=0): rw=randwrite, bs=(R) 16.0KiB-16.0KiB, (W) 16.0KiB-16.0KiB, (T) 16.0KiB-16.0KiB, ioengine=spdk_bdev, iodepth=64
fio-3.37
...
Run status group 0 (all jobs):
WRITE: bw=2027MiB/s (2126MB/s), 2027MiB/s-2027MiB/s (2126MB/s-2126MB/s), io=19.8GiB (21.3GB), run=10001-10001msec
#2: nvme spdk中用于专门与nvme通信
root@ubuntu:/home/ubuntu/spdk/fio_script# LD_PRELOAD=/home/ubuntu/spdk/spdk/build/fio/spdk_nvme /home/ubuntu/uring/fio-fio-3.37/fio nvme.fio
test: (g=0): rw=randwrite, bs=(R) 16.0KiB-16.0KiB, (W) 16.0KiB-16.0KiB, (T) 16.0KiB-16.0KiB, ioengine=spdk, iodepth=64
fio-3.37
...
Run status group 0 (all jobs):
WRITE: bw=817MiB/s (857MB/s), 817MiB/s-817MiB/s (857MB/s-857MB/s), io=8175MiB (8572MB), run=10001-10001msec
对比各种测试方式下的性能差异 以及spdk_nvme 和 spdk_bdev的区别
7:实现一个example进行理解。
7.1 了解几种访问磁盘的方式
1:可以直接通过spdk 的nvme模块,直接与nvme设备进行交互管理。
2:可以通过bdev模块,实现与nvme设备的交互。
3:基于bdev之上,使用blob/blobstore实现与磁盘进行交互。
4:与nvme设备最底层的交互实际上还是PCie总线,nvme是基于pcie的协议封装,spdk管理dbev,blob/blobstore等模块的方式,采用rpc的方式。
参考如图:bdev,blob,blobstore等创建,删除,查询的管理 用的rpc。
bdev是一个适配层封装吧,blob底层实际上也用bdev再调用。
7.2 理解一个blob的demo
1:参考spdk下的example下的demo了解概念最合适,以及新增demo的方式参考example。
2:blob的整个管理使用的是rpc,所以这里的顺序流程,一直使用回调函数获取到执行结果后继续。
3:自己的demo下,可以不放在example目录下去编译,只需要修改对应makefile下的SPDK_ROOT_DIR 参数为spdk实际目录。
7.2.1 确定环境的正确,实现入口
首先环境运行起来 参考example,自己写入口,修改makefile进行测试
#include <stdio.h>
#include <spdk/event.h>
static void example_entry(void *ctx)
{
printf("example_entry ====> \n");
}
int main(int argc, char *argv[]) {
if (argc < 2) {
return -1;
}
struct spdk_app_opts opts= {0};
spdk_app_opts_init(&opts, sizeof(opts));
opts.name = "blob_example";
opts.json_config_file = argv[1];
printf("spdk_set_thread --> \n");
// zfs_ctx_t *ctx = calloc(1, sizeof(zfs_ctx_t));
//启动spdk应用程序
spdk_app_start(&opts, example_entry, NULL);
spdk_app_fini();
// free(ctx);
return 0;
}
运行显示
root@ubuntu:/home/ubuntu/storage/spdk_one_example# ls
blob_example.c hello_blob.json Makefile
root@ubuntu:/home/ubuntu/storage/spdk_one_example# make
root@ubuntu:/home/ubuntu/storage/spdk_one_example# ./blob_example hello_blob.json
spdk_set_thread -->
[2024-09-08 03:28:36.621006] Starting SPDK v24.09-pre / DPDK 24.03.0 initialization...
[2024-09-08 03:28:36.621185] [ DPDK EAL parameters: blob_example --no-shconf -c 0x1 --huge-unlink --no-telemetry --log-level=lib.eal:6 --log-level=lib.cryptodev:5 --log-level=user1:6 --iova-mode=pa --base-virtaddr=0x200000000000 --match-allocations --file-prefix=spdk_pid3536 ]
[2024-09-08 03:28:36.759565] app.c: 909:spdk_app_start: *NOTICE*: Total cores available: 1
[2024-09-08 03:28:36.807677] reactor.c: 943:reactor_run: *NOTICE*: Reactor started on core 0
example_entry ====>
7.2.2 相关接口调用测试
依次通过回到函数创建:
创建bdev =》创建blobstore=>创建真正blob ==>真正对blob的创建,删除,修改,读,写等
#include <stdio.h>
#include <spdk/event.h>
#include <spdk/env.h>
#include <spdk/blob.h>
#include <spdk/bdev.h>
#include <spdk/blob_bdev.h>
static void example_spdk_bdev_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev,
void *event_ctx) {
printf("example_spdk_bdev_cb blobstore create success\n");
}
static void example_spdk__bdev_open_blob_complete(void *arg, struct spdk_blob *blb, int bserrno) {
printf("example_spdk__bdev_open_blob_complete blobstore create success\n");
//最后就是读写操作了 对blob的真正操作 获取空闲大小 设置大小 读写操作 以及相关释放操作。
}
static void example_spdk_bdev_blob_create_cb(void *arg, spdk_blob_id blobid, int bserrno)
{
printf("example_spdk_bdev_blob_create_cb :%"PRIu64"\n", blobid);
//blob创建后
struct spdk_blob_store *bs = arg;
spdk_bs_open_blob(bs, blobid, example_spdk__bdev_open_blob_complete, NULL);
}
static void example_spdk_bdev_init_cb(void *arg, struct spdk_blob_store *bs,int bserrno)
{
printf("example_spdk_bdev_init_cb blobstore init success\n");
spdk_bs_create_blob(bs, example_spdk_bdev_blob_create_cb, bs);
}
static void example_entry(void *ctx)
{
printf("example_entry ====> \n");
struct spdk_bs_dev *bsdev = NULL;
const char * bdev_name = "Malloc0"; //这个名字和json配置文件中 bdev_malloc_create 中对应name关联
int res = spdk_bdev_create_bs_dev_ext(bdev_name, example_spdk_bdev_cb, NULL, &bsdev);
if (res != 0) {
spdk_app_stop(-1);
return ;
}
spdk_bs_init(bsdev, NULL, example_spdk_bdev_init_cb, NULL);
}
int main(int argc, char *argv[]) {
if (argc < 2) {
return -1;
}
struct spdk_app_opts opts= {0};
spdk_app_opts_init(&opts, sizeof(opts));
opts.name = "blob_example";
opts.json_config_file = argv[1];
printf("spdk_set_thread --> \n");
spdk_app_start(&opts, example_entry, NULL);
spdk_app_fini();
// free(ctx);
return 0;
}
已经可以看到代码的调用流程。通过回调函数依次调用,但是实现后发现无法进行有效管理磁盘后续读写。
7.2.3 参考example下blob模块 实现磁盘读写
运行如下,可以看到已经正常写入并读成功。
root@ubuntu:/home/ubuntu/storage/spdk_one_example# ./blob_example hello_blob.json
spdk_set_thread -->
[2024-09-08 08:08:12.630916] Starting SPDK v24.09-pre / DPDK 24.03.0 initialization...
[2024-09-08 08:08:12.631057] [ DPDK EAL parameters: blob_example --no-shconf -c 0x1 --huge-unlink --no-telemetry --log-level=lib.eal:6 --log-level=lib.cryptodev:5 --log-level=user1:6 --iova-mode=pa --base-virtaddr=0x200000000000 --match-allocations --file-prefix=spdk_pid6132 ]
[2024-09-08 08:08:12.777323] app.c: 909:spdk_app_start: *NOTICE*: Total cores available: 1
[2024-09-08 08:08:12.836624] reactor.c: 943:reactor_run: *NOTICE*: Reactor started on core 0
example_entry ====>
example_spdk_bdev_init_cb blobstore init success
example_spdk_bdev_blob_create_cb :4294967296
example_spdk__bdev_open_blob_complete blobstore create success
example_spdk__bdev_open_blob_complete free = 15
example_resize_complete blobstore create success
example_resize_complete total = 15
exp_file_write_complete
Data written successfully.
read_data from blob :ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZa]ʆV
exp_file_read_complete
Data written successfully.
测试代码如下,只关注了流程,未考虑释放等。
这里注意,发现,blob在创建成功后,需要先进行resize等操作才能进行写入成功。
#include <stdio.h>
#include <spdk/event.h>
#include <spdk/env.h>
#include <spdk/blob.h>
#include <spdk/bdev.h>
#include <spdk/blob_bdev.h>
//回调函数依次调用 有时候需要参数传递一些指针 这里定义必要的结构
struct temp_ctx_s{
struct spdk_bs_dev *s_bsdev;
struct spdk_blob_store *s_bs;
spdk_blob_id s_blobid;
struct spdk_blob * s_blob;
struct spdk_io_channel* s_channel;
uint64_t io_unit_size;
uint8_t *write_buff;
uint8_t *read_buff;
};
// struct temp_ctx_s *g_ctx;
static void example_spdk_bdev_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev,
void *event_ctx) {
printf("example_spdk_bdev_cb blobstore create success\n");
}
static void delete_complete(void *arg1, int bserrno)
{
if (bserrno) {
printf("Error in delete completion: %d\n", (-bserrno));
return;
}
// struct temp_ctx_s *m_ctx = arg1;
//进行相关的释放动作
}
static void delete_blob(void *arg1, int bserrno)
{
if (bserrno) {
printf("Error in close completion: %d\n", (-bserrno));
return;
}
struct temp_ctx_s *m_ctx = arg1;
spdk_bs_delete_blob(m_ctx->s_bs, m_ctx->s_blobid, delete_complete, m_ctx);
}
static void exp_file_read_complete(void *arg1, int bserrno)
{
printf("exp_file_read_complete \n");
if (bserrno) {
printf("Failed to read data: %d\n", (-bserrno));
// 写入失败处理
return;
} else {
printf("Data written successfully.\n");
// 写入成功处理
}
struct temp_ctx_s *m_ctx = arg1;
spdk_blob_close(m_ctx->s_blob, delete_blob, m_ctx);
}
// struct spdk_io_channel * g_channel;
// struct spdk_blob *g_blob;
static void exp_file_write_complete(void *arg1, int bserrno)
{
printf("exp_file_write_complete \n");
if (bserrno) {
printf("Failed to write data: %d\n", (-bserrno));
// 写入失败处理
return;
}
printf("Data written successfully.\n");
// 写入成功处理
struct temp_ctx_s *m_ctx = arg1;
uint8_t *read_buff = spdk_malloc(m_ctx->io_unit_size, 0x1000, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
if (read_buff == NULL)
{
printf("read_buff is null \n");
return;
}
spdk_blob_io_read(m_ctx->s_blob, m_ctx->s_channel , read_buff, 0, 1, exp_file_read_complete, m_ctx);
printf("read_data from blob :%s \n", read_buff);
}
static void sync_complete(void *arg1, int bserrno)
{
if (bserrno) {
printf("sync_complete Error in sync callback: %d\n", (-bserrno));
return;
}
struct temp_ctx_s *m_ctx = arg1;
uint8_t *write_buff = spdk_malloc(m_ctx->io_unit_size, 0x1000, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
if (write_buff == NULL)
{
printf("write_buff is null \n");
return;
}
memset(write_buff, 0x5a, m_ctx->io_unit_size);
m_ctx->s_channel = spdk_bs_alloc_io_channel(m_ctx->s_bs);
spdk_blob_io_write(m_ctx->s_blob, m_ctx->s_channel , write_buff, 0, 1, exp_file_write_complete, m_ctx);
}
static void example_resize_complete(void *cb_arg, int bserrno)
{
printf("example_resize_complete blobstore create success\n");
struct temp_ctx_s *m_ctx = cb_arg;
if (bserrno) {
printf("example_resize_complete Error in blob resize: %d\n", (-bserrno));
return;
}
uint64_t total = 0;
total = spdk_blob_get_num_clusters(m_ctx->s_blob);
printf("example_resize_complete total = %" PRIu64 "\n", total);
spdk_blob_sync_md(m_ctx->s_blob, sync_complete, m_ctx); //进行同步相关数据 直接进行
}
//最后就是读写操作了 对blob的真正管理
// char write_data[1024] = "Hello, SPDK! =======XXXXXXX"; // 写入的数据
// char read_data[1024] = {0}; // 用于存储读取的数据
static void example_spdk__bdev_open_blob_complete(void *arg, struct spdk_blob *blob, int bserrno) {
printf("example_spdk__bdev_open_blob_complete blobstore create success\n");
struct temp_ctx_s *m_ctx = arg;
if (bserrno) {
printf("example_spdk__bdev_open_blob_complete Error in open resize: %d\n", (-bserrno));
return;
}
m_ctx->s_blob = blob;
uint64_t free = 0;
free = spdk_bs_free_cluster_count(m_ctx->s_bs);
printf("example_spdk__bdev_open_blob_complete free = %" PRIu64 "\n", free);
spdk_blob_resize(blob, free, example_resize_complete, m_ctx);
// struct spdk_blob_store *bs = arg;
// uint64_t io_unit_size = spdk_bs_get_io_unit_size(bs);
// uint8_t *write_buff = spdk_malloc(io_unit_size, 0x1000, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
// if (write_buff == NULL)
// {
// printf("write_buff is null \n");
// return;
// }
// memset(write_buff, 0x5a, io_unit_size);
// // g_channel = spdk_bs_alloc_io_channel(bs);
// g_ctx.bs = bs;
// g_ctx.s_blob = blb;
// g_ctx.io_unit_size = io_unit_size;
// g_ctx.s_channel = spdk_bs_alloc_io_channel(bs);
// spdk_blob_io_write(blb, g_ctx.s_channel , write_buff, 0, 1, exp_file_write_complete, NULL);
}
static void example_spdk_bdev_blob_create_cb(void *arg, spdk_blob_id blobid, int bserrno)
{
printf("example_spdk_bdev_blob_create_cb :%"PRIu64"\n", blobid);
//blob创建后
struct temp_ctx_s *m_ctx = arg;
if (bserrno) {
printf("example_spdk_bdev_blob_create_cb Error in blob create callback: %d\n", (-bserrno));
return;
}
m_ctx->s_blobid = blobid;
spdk_bs_open_blob(m_ctx->s_bs, blobid, example_spdk__bdev_open_blob_complete, m_ctx);
}
static void example_spdk_bdev_init_cb(void *arg, struct spdk_blob_store *bs,int bserrno)
{
printf("example_spdk_bdev_init_cb blobstore init success\n");
struct temp_ctx_s *m_ctx = arg;
if (bserrno) {
printf("example_spdk_bdev_init_cb Error initing the blobstore: %d\n", (-bserrno));
return;
}
m_ctx->s_bs = bs;
m_ctx->io_unit_size = spdk_bs_get_io_unit_size(bs);
spdk_bs_create_blob(bs, example_spdk_bdev_blob_create_cb, m_ctx);
}
static void example_entry(void *ctx)
{
printf("example_entry ====> \n");
struct spdk_bs_dev *bsdev = NULL;
const char * bdev_name = "Malloc0"; //这个名字和json配置文件中 bdev_malloc_create 中对应name关联
int res = spdk_bdev_create_bs_dev_ext(bdev_name, example_spdk_bdev_cb, NULL, &bsdev);
if (res != 0) {
spdk_app_stop(-1);
return ;
}
struct temp_ctx_s *m_ctx = ctx;
spdk_bs_init(bsdev, NULL, example_spdk_bdev_init_cb, m_ctx);
}
int main(int argc, char *argv[]) {
if (argc < 2) {
return -1;
}
struct spdk_app_opts opts= {0};
spdk_app_opts_init(&opts, sizeof(opts));
opts.name = "blob_example";
opts.json_config_file = argv[1];
printf("spdk_set_thread --> \n");
//启动spdk应用程序
struct temp_ctx_s *m_ctx = calloc(1, sizeof(struct temp_ctx_s));
spdk_app_start(&opts, example_entry, m_ctx);
spdk_app_fini();
free(m_ctx);
return 0;
}
上述流程可以发现,所有的操作都是顺序的,回调函数依次调用实现,异步的方案让我们无法实际有效管控。
7.2.4 把rpc异步调用方式改为同步
spdk已经提供了对应的接口和方案,也就是把相关操作放给特定的线程,等待执行完成。
借助spdk_thread_send_msg 放入线程 和spdk_thread_poll 指定轮询进行实现。
每次调用poller接口,实际上是在新的线程中调用,返回时即已经获取到反馈结果了。(参考example逻辑实现)
static int zvfs_env_setup(void) {
struct spdk_env_opts opts;
spdk_env_opts_init(&opts);
if (spdk_env_init(&opts) != 0) {
return -1;
}
spdk_log_set_print_level(SPDK_LOG_NOTICE);
spdk_log_set_level(SPDK_LOG_NOTICE);
spdk_log_open(NULL);
spdk_thread_lib_init(NULL, 0);
global_thread = spdk_thread_create("global", NULL);
spdk_set_thread(global_thread); //设置spdk工作线程
bool done = false;
poller(global_thread, zvfs_json_load_fn, &done, &done);
return 0;
}
//相关任务异步放入执行线程中 达到同步获取到结果
static const int POLLER_MAX_TIME = 100000;
static bool poller(struct spdk_thread *thread, spdk_msg_fn start_fn, void *ctx, bool *finished) {
spdk_thread_send_msg(thread, start_fn, ctx);
int poller_count = 0;
do {
spdk_thread_poll(thread, 0, 0);
poller_count ++;
} while (!(*finished) && poller_count < POLLER_MAX_TIME);
if (!(*finished) && poller_count >= POLLER_MAX_TIME) { //timeout
return false;
}
return true;
}