From 321212826b12d36e943d447a7b7598f69c896f54 Mon Sep 17 00:00:00 2001 From: 1iaan Date: Mon, 2 Mar 2026 08:31:01 +0000 Subject: [PATCH] zvfs: fio file --- README.md | 6 +- fio/readme.md | 9 + fio/sample.fio | 14 + fio/zvfs.fio | 14 + plan.md | 414 +++++++------------- rocksdb.md | 9 + zvfs/Makefile | 8 +- zvfs/zvfs.c | 1008 ++++++++++++++++++++++++------------------------ 8 files changed, 704 insertions(+), 778 deletions(-) create mode 100644 fio/readme.md create mode 100644 fio/sample.fio create mode 100644 fio/zvfs.fio create mode 100644 rocksdb.md diff --git a/README.md b/README.md index b257a15..9a83818 100755 --- a/README.md +++ b/README.md @@ -42,8 +42,8 @@ zvfs的测试结果: ```shell cd /home/lian/share/10.1-spdk/spdk -export LD_LIBRARY_PATH=/home/lian/share/10.1-spdk/spdk/build/lib:/home/lian/share/10.1-spdk/spdk/dpdk/build/lib:$LD_LIBRARY_PATH -export PATH=/home/lian/share/10.1-spdk/spdk/build/bin:$PATH +export LD_LIBRARY_PATH=/home/lian/share/10.1-spdk/zvfs/spdk/build/lib:/home/lian/share/10.1-spdk/zvfs/spdk/dpdk/build/lib:$LD_LIBRARY_PATH +export PATH=/home/lian/share/10.1-spdk/zvfs/spdk/build/bin:$PATH ./build/bin/spdk_nvme_perf \ -r 'trtype:PCIe traddr:0000:03:00.0' \ @@ -84,8 +84,6 @@ Total : 80122.94 312.98 399.36 3 -r 'trtype:PCIe traddr:0000:03:00.0' \ -q 1 -o 131072 -w write -t 5 -root@ubuntu:/home/lian/share/10.1-spdk/spdk# export LD_LIBRARY_PATH=/home/lian/share/10.1-spdk/spdk/build/lib:/home/lian/share/10.1-spdk/spdk/dpdk/build/lib:$LD_LIBRARY_PATH -root@ubuntu:/home/lian/share/10.1-spdk/spdk# export PATH=/home/lian/share/10.1-spdk/spdk/build/bin:$PATH root@ubuntu:/home/lian/share/10.1-spdk/spdk# ./build/bin/spdk_nvme_perf -r 'trtype:PCIe traddr:0000:03:00.0' -q 1 -o 131072 -w write -t 5 Initializing NVMe Controllers Attached to NVMe Controller at 0000:03:00.0 [15ad:07f0] diff --git a/fio/readme.md b/fio/readme.md new file mode 100644 index 0000000..4759e5d --- /dev/null +++ b/fio/readme.md @@ -0,0 +1,9 @@ +```shell + +sudo apt install fio + +fio sample.fio +LD_PRELOAD=../zvfs/libzvfs.so fio zvfs.fio +``` + + diff --git a/fio/sample.fio b/fio/sample.fio new file mode 100644 index 0000000..2136b86 --- /dev/null +++ b/fio/sample.fio @@ -0,0 +1,14 @@ +[global] +ioengine=sync # 同步 I/O +direct=0 # 使用内核页缓存,测试系统调用性能 +bs=128k # 块大小 +size=1G # 每个文件大小 +numjobs=2 # 并发线程数 +runtime=60 # 测试运行时间,秒 +time_based=1 +rw=randrw # 随机读写 +rwmixread=70 # 70% 读,30% 写 +group_reporting=1 # 汇总报告 + +[test_syscall] +filename=/tmp/fio_sample_testfile \ No newline at end of file diff --git a/fio/zvfs.fio b/fio/zvfs.fio new file mode 100644 index 0000000..40f2ef8 --- /dev/null +++ b/fio/zvfs.fio @@ -0,0 +1,14 @@ +[global] +ioengine=sync +direct=0 +bs=128k +size=1G +numjobs=2 +runtime=60 +time_based=1 +rw=randrw +rwmixread=70 +group_reporting=1 + +[test_hook] +filename=/zvfs/fio_zvfs_testfile \ No newline at end of file diff --git a/plan.md b/plan.md index 749ce65..b3f8d98 100644 --- a/plan.md +++ b/plan.md @@ -1,346 +1,232 @@ -# ZVFS -> RocksDB LD_PRELOAD 可重入改造计划(给 Codex 执行) +# ZVFS LD_PRELOAD -> RocksDB 加速实施计划(给 Codex 执行) -> 目标:这是一份“可中断、可恢复、可分段提交”的实施计划。 -> 原则:每阶段都能独立编译、独立验证、独立回滚,不要求一次性改完。 +> 目标:基于当前 `zvfs/zvfs.c` + `zvfs/zvfs_hook.c`,从“单线程 + 接口不全”演进到“可支撑 RocksDB 多线程并有性能收益”。 +> +> 约束:**分阶段、可中断、可重入**;每阶段都必须有独立可验证的正确性门槛。 --- -## 0. 执行约束(必须遵守) +## 0. 执行规则(必须遵守) -1. **一次只做一个阶段**,每阶段结束后必须通过该阶段验收再进入下一阶段。 -2. **不跨阶段混改**:例如阶段 1 不改 hook 覆盖,阶段 3 不重构线程模型。 -3. **每阶段必须留下恢复锚点**: - - 文档状态更新(本文件“阶段状态”区) - - 代码中保留临时兼容开关(若有) - - 能独立提交 commit(建议) -4. **失败可回退**:阶段内失败仅回退本阶段改动,不影响已完成阶段。 -5. **默认保持旧行为可用**,新增能力通过新路径启用,逐步替换旧路径。 +1. 一次只做一个阶段;阶段内通过验收前不得进入下一阶段。 +2. 每阶段结束后必须更新本文“阶段状态”。 +3. 每阶段保留一个“恢复锚点”: + - 可编译的代码状态; + - 一组固定验证命令; + - 一条阶段 commit(建议)。 +4. 出现阻塞时只回滚当前阶段改动,不回滚已完成阶段。 +5. 非 `/zvfs` 路径行为必须始终透传到 libc,不得回归。 --- -## 1. 阶段状态(可重入) +## 1. 阶段状态(可重入入口) -- [ ] Phase 1: IO 请求结构体落地(单线程兼容模式) -- [ ] Phase 2: Worker + 多线程执行模型落地 -- [ ] Phase 3: 偏移型 IO 完整化(pread/pwrite 语义) -- [ ] Phase 4: RocksDB 核心 hook 补齐(P0) -- [ ] Phase 5: 目录/锁/截断接口补齐(P1) -- [ ] Phase 6: 元数据一致性与崩溃恢复增强 -- [ ] Phase 7: 性能增强与观测 +- [ ] Phase 1: 建立基线与 syscall 覆盖清单(RocksDB 实际需求) +- [ ] Phase 2: 修正现有 hook 语义缺口(不改线程模型) +- [ ] Phase 3: 补齐 RocksDB P0 接口集(单线程可跑通) +- [ ] Phase 4: 补齐 RocksDB P1 接口集(目录/锁/截断) +- [ ] Phase 5: 多线程执行模型改造(Worker + 请求队列) +- [ ] Phase 6: 并发正确性与崩溃恢复强化 +- [ ] Phase 7: 面向 RocksDB 的性能优化与验收 -> 重入方式:中断后先看此状态区 + 对应阶段“完成定义”,从未完成阶段继续。 +> 重入方式:中断后先看本区,继续第一个未完成阶段。 --- -## 2. 基线与分支策略 +## 2. 当前代码关键问题(来自 `zvfs.c`/`zvfs_hook.c`) -### 2.1 基线要求 -- 当前 `libzvfs.so` 可正常构建。 -- 当前 `func_test` 可运行。 -- 已确认 `/zvfs` 路径仍由现有 hook 接管。 - -### 2.2 分支与提交建议 -- 每阶段 1 个分支或 1 个 commit: - - `phase1-req-struct` - - `phase2-worker-thread` - - `phase3-offset-io` - - ... -- commit message 模板: - - `phaseX: + ` +1. 单线程瓶颈:所有请求依赖 `global_thread + waiter` 同步轮询,调用线程直接 `spdk_thread_poll`,不适合并发。 +2. hook 覆盖不足:当前仅 `open/read/write/close/unlink/lseek`,RocksDB 常用接口大量缺失(如 `pread/pwrite/fstat/fsync/fdatasync/ftruncate/rename/openat/fcntl` 等)。 +3. 语义缺口:`O_TRUNC/O_APPEND/errno` 等语义不完整;元数据保存与文件操作一致性较弱。 +4. 线程安全缺口:`g_fs`、`fd_table`、`dirent`、`open_count` 等无锁并发访问。 --- -## 3. 分阶段实施 +## 3. 分阶段计划 -## Phase 1: IO 请求结构体落地(不改线程模型) +## Phase 1: 建立基线与 syscall 覆盖清单(RocksDB 实际需求) ### 目标 -先把“每次系统调用复用 file 临时字段”的问题解开;引入可扩展请求对象,为后续多线程做准备。 -**本阶段仍允许单线程执行路径**(内部可继续使用现有 waiter)。 +明确 RocksDB 在当前环境下真实调用了哪些文件接口,得到“必须实现”的优先级列表。 -### 改动范围 -- `zvfs/zvfs.h` -- `zvfs/zvfs.c` -- 尽量不改 `zvfs/zvfs_hook.c` 的接口覆盖面(只做必要适配) +### 任务 +1. 编译基线:`make`、`make -C test`。 +2. 跑现有回归:`make run-test`(普通路径和 `/zvfs` 路径各一轮)。 +3. 用 `strace -f` 跑 `db_bench`(或最小 RocksDB workload),导出 syscall 统计。 +4. 产出 `docs/rocksdb-syscall-matrix.md`(P0/P1/P2 分类 + 是否已支持)。 -### 任务清单 -1. 新增 `zvfs_req_t`(最小字段): - - `op`, `file`, `buf`, `count`, `offset`, `ret`, `op_errno`, `done` - - 可选:`need_copy_back`, `actual_io_count` -2. 给现有读写流程加“请求入参”版本: - - 新增 `zvfs_read_req(req)` / `zvfs_write_req(req)` 内部函数 - - 旧 API `zvfs_read/zvfs_write` 变成薄包装(构造 req 后调用) -3. 去除对 `file->io_count/write_staging_buf/finished` 的强依赖: - - 保留字段可兼容,但主流程改为优先使用 req 字段 -4. 错误返回统一: - - 内部负 errno -> 外部返回值 + errno 映射规则固定 +### 验收 +- 能给出可复现命令与 syscall 清单。 +- 明确哪些接口是“阻塞 RocksDB 跑通”的 P0。 -### 完成定义(DoD) -- 编译通过。 -- 现有 `func_test` 全通过。 -- 无并发改造前提下,行为与旧版一致(回归结果一致)。 - -### 重入点 -- 代码里出现 `zvfs_req_t` 且 `zvfs_read/zvfs_write` 已封装请求对象。 -- 若中断,优先检查旧路径是否仍可用,再继续替换剩余 call path。 +### 中断/重入 +- 产物文件存在:`docs/rocksdb-syscall-matrix.md`。 +- 下一次从该清单继续,不需要重跑全量分析。 --- -## Phase 2: Worker + 多线程执行模型 +## Phase 2: 修正现有 hook 语义缺口(不改线程模型) ### 目标 -将“调用线程主动 poll SPDK”改为“专用 worker 线程 poll + 请求队列”。 -建立线程安全基础,不追求本阶段 hook 覆盖齐全。 +在保持单线程架构不变的前提下,先把已有接口的 POSIX 语义修正到可用状态。 -### 改动范围 -- `zvfs/zvfs.h` -- `zvfs/zvfs.c` -- `zvfs/zvfs_hook.c`(仅初始化、提交流程相关) +### 任务 +1. 修复 `open` 标志位语义:至少覆盖 `O_CREAT/O_EXCL/O_TRUNC/O_APPEND`。 +2. 统一返回值与 `errno`:`zvfs_*` 失败路径映射到标准 errno。 +3. 修复元数据 I/O 基础问题(如加载/保存边界、错误传播、close 使用 real 函数)。 +4. 增加小型语义回归测试(可放 `test/`)。 -### 任务清单 -1. 新增 `zvfs_worker_t`: - - `pthread_t tid` - - 请求队列(先用 mutex + list + cond,后续可换 MPSC) - - `running/stopping` 状态 -2. 新增生命周期函数: - - `zvfs_worker_start()` - - `zvfs_worker_stop()` - - 在 mount/init 时启动,在 atexit/unmount 时停止 -3. 新增提交流程: - - `zvfs_submit_and_wait(req)`:调用线程阻塞等待完成 - - worker 线程执行 SPDK 异步链并回填 req -4. 替换 `waiter()` 主路径: - - 保留 waiter 仅作为 fallback(编译开关控制) -5. 锁与并发保护(最小闭环): - - 全局状态锁:`g_fs/g_mounted/fd_map` - - 文件级锁:`offset` 更新、close 与 io 竞争 +### 验收 +- 现有 `test` 全通过。 +- 新增语义测试通过。 +- 非 `/zvfs` 路径行为无回归。 -### 完成定义(DoD) -- `db_bench --threads=4` 不崩溃、不死锁(先小规模)。 -- 旧单线程 case 仍通过。 -- 调用线程不再直接 `spdk_thread_poll`。 - -### 重入点 -- worker 生命周期已接管主流程(可在日志中看到 worker 启动/停止)。 -- 若中断,优先确保 stop 路径可达,避免进程退出挂死。 +### 中断/重入 +- 保留旧逻辑兼容开关(如宏开关)直到本阶段稳定。 +- 提交后可独立回退,不影响后续接口扩展。 --- -## Phase 3: 偏移型 IO 完整化(pread/pwrite 语义) +## Phase 3: 补齐 RocksDB P0 接口集(单线程可跑通) ### 目标 -补齐 RocksDB 高频调用语义:`pread/pwrite` 不改文件偏移。 -把 `read/write` 变成 `pread/pwrite + offset 管理` 的包装。 +先实现 RocksDB “必须有才能启动并跑基础 workload” 的接口集合。 -### 改动范围 -- `zvfs/zvfs.h` -- `zvfs/zvfs.c` -- `zvfs/zvfs_hook.c` +### P0 接口(优先) +- 打开类:`open64/openat/openat64/__open_2/__open64_2` +- 偏移 I/O:`pread/pread64/pwrite/pwrite64` +- 元数据:`stat/lstat/fstat/fstatat/access` +- 持久化:`fsync/fdatasync` +- 变更:`rename/renameat/unlinkat` -### 任务清单 -1. 新增核心接口: - - `zvfs_pread(file, buf, count, offset)` - - `zvfs_pwrite(file, buf, count, offset)` -2. 改造 read/write: - - `read`:在 file lock 下读取并推进 `current_offset` - - `write`:在 file lock 下决定 offset(含 O_APPEND) -3. 明确并发语义: - - 同一 fd 并发 pread/pwrite 允许并行(不同 offset) - - 同一 fd 的 read/write 偏移操作受 file lock 串行 +### 任务 +1. 在 `zvfs_hook.c` 增加 real 函数指针与统一初始化(建议 `pthread_once`)。 +2. 所有新 hook 必须支持“路径过滤 + 非 `/zvfs` 透传”。 +3. 对不支持的语义明确返回 `ENOTSUP/EOPNOTSUPP`,禁止静默成功。 +4. 增加 `pread/pwrite` 偏移语义测试。 -### 完成定义(DoD) -- `pread/pwrite` 回归用例通过。 -- 同一 fd 的并发 `pread` 不互相污染 offset。 +### 验收 +- `db_bench` 单线程基础项可跑:`fillseq/fillrandom/readrandom`。 +- `strace` 显示 P0 接口已被正确接管或透传。 -### 重入点 -- hook 层可区分 `read/write` 与 `pread/pwrite` 路径。 -- 若中断,先保证 `read/write` 仍有正确 fallback。 +### 中断/重入 +- 每新增一类 hook 单独 commit(open/io/meta/sync/rename)。 +- 中断后按未完成类别继续,不影响已完成类别。 --- -## Phase 4: RocksDB 核心 hook 补齐(P0) +## Phase 4: 补齐 RocksDB P1 接口集(目录/锁/截断) ### 目标 -先覆盖 RocksDB “能跑起来”最关键接口。 +支持 RocksDB 更完整运行路径,尤其是锁文件、目录操作、截断相关语义。 -### 改动范围 -- `zvfs/zvfs_hook.c` -- 必要时 `zvfs/zvfs.h` 新增声明 +### P1 接口 +- `ftruncate/truncate` +- `fcntl`(至少 `F_SETLK/F_SETLKW/F_GETLK/F_UNLCK`) +- `mkdir/rmdir/opendir/readdir/closedir` +- `link/symlink/readlink`(按 strace 结果决定) -### 任务清单(按优先级) -1. 打开类: - - `open/open64/openat/openat64/__open_2/__open64_2` -2. IO 类: - - `pread/pread64/pwrite/pwrite64` -3. 元数据类: - - `fstat/stat/lstat/fstatat/access` -4. 持久化类: - - `fsync/fdatasync` -5. 文件变更类: - - `rename/renameat/renameat2/unlink/unlinkat` +### 任务 +1. 为目录与锁引入最小可用实现(先保证正确,再优化)。 +2. 对暂不支持特性返回明确 errno,不可假成功。 +3. 补充目录/锁语义测试(多进程或多线程最小场景)。 -### 实现要求 -- 非 `/zvfs` 路径必须透传 real libc。 -- 所有失败分支必须设置 errno。 -- 统一 dlsym 初始化:`pthread_once`,避免递归。 +### 验收 +- `db_bench --threads=4` 可稳定执行基础 workload。 +- 无明显语义错误(锁冲突、目录丢失、truncate 异常)。 -### 完成定义(DoD) -- RocksDB 基础 workload(单线程)可跑通: - - `fillseq`, `fillrandom`, `readrandom` -- `strace -f` 观察 `/zvfs` 关键 syscall 已被接管。 - -### 重入点 -- 每新增一组 hook 独立提交,失败时可只回退该组。 +### 中断/重入 +- `fcntl` 与目录接口分成两个子里程碑。 +- 任一子里程碑完成即可落盘并停在该点。 --- -## Phase 5: 目录/锁/截断接口补齐(P1) +## Phase 5: 多线程执行模型改造(Worker + 请求队列) ### 目标 -补齐 RocksDB 稳定运行需要的辅助接口。 +把当前“调用线程主动 poll”的模式改为“专用 worker poll + 线程安全请求提交”,解决单线程瓶颈。 -### 改动范围 -- `zvfs/zvfs_hook.c` -- `zvfs/zvfs.c`(必要后端支撑) +### 任务 +1. 新增 `zvfs_worker`:专用线程、请求队列、完成通知(cond/futex/eventfd 均可)。 +2. 将 `waiter` 路径替换为 `submit_and_wait` 路径;调用线程不再直接 `spdk_thread_poll`。 +3. 增加并发保护: + - 全局锁:`g_fs/mount/fd_table/dirent`; + - 文件锁:`offset/blob 生命周期`; + - 明确锁顺序避免死锁。 +4. 保留回退开关(例如 `ZVFS_USE_LEGACY_WAITER`)直到压测稳定。 -### 任务清单 -1. 截断: - - `ftruncate/truncate` -2. 锁: - - `fcntl` 最少支持 `F_SETLK/F_SETLKW/F_UNLCK/F_GETLK` -3. 目录: - - `mkdir/rmdir/opendir/readdir/closedir` -4. 容量提示(可选但建议): - - `fallocate/posix_fallocate`(不支持时明确 `EOPNOTSUPP`) +### 验收 +- 线程数 4/8 下功能测试稳定,无死锁/崩溃。 +- CPU 火焰图或日志能证明调用线程不再承担 SPDK poll。 -### 完成定义(DoD) -- RocksDB 多线程基础 workload 可稳定运行(`--threads=4/8`)。 -- LOCK 文件语义满足单进程多线程正确性。 - -### 重入点 -- `fcntl` 与目录接口可拆成两个子阶段提交。 +### 中断/重入 +- 先实现 worker 生命周期,再迁移 read/write,再迁移 open/close。 +- 每迁移一类操作即可独立验证与提交。 --- -## Phase 6: 元数据一致性与崩溃恢复增强 +## Phase 6: 并发正确性与崩溃恢复强化 ### 目标 -解决 `zvfs_meta.txt` 全量覆盖、崩溃窗口、恢复不确定问题。 +在多线程基础上补齐一致性:元数据、删除/关闭竞态、异常退出后的可恢复性。 -### 改动范围 -- `zvfs/zvfs_hook.c`(meta load/save) -- `zvfs/zvfs.c`(关键操作强制 flush 时机) -- 文档与测试脚本 +### 任务 +1. 元数据持久化改为“原子写入流程”(临时文件 + fsync + rename)。 +2. 修复 `unlink/close/open` 并发竞态(引用计数与删除时机)。 +3. 建立故障注入测试:`kill -9`、中途断电模拟(最小可复现脚本)。 +4. 明确恢复策略与错误可观测日志。 -### 任务清单 -1. 元数据写入策略: - - 从“每次全量覆盖”改为“批量 + 关键点强制刷盘” -2. 元数据文件格式增强: - - 增加版本号 + CRC -3. 恢复路径: - - 加载失败时回退到最近完整版本(若实现双文件/快照) -4. 关键操作一致性点: - - `rename/unlink/truncate/fsync` 后策略明确 +### 验收 +- 故障注入后可重新挂载并读到一致元数据。 +- 并发 `open/unlink/close` 压测无崩溃无悬挂。 -### 完成定义(DoD) -- 人工崩溃注入后(kill -9)可重新打开 DB。 -- 元数据损坏能被检测并报错,不默默产生脏状态。 - -### 重入点 -- 先做“格式版本化 + 校验”,再做“批量策略”。 +### 中断/重入 +- 先落地元数据原子写入,再处理并发删除,再做故障注入。 --- -## Phase 7: 性能增强与观测 +## Phase 7: 面向 RocksDB 的性能优化与验收 ### 目标 -在正确性稳定后提性能,确保收益可量化。 +在正确性稳定后进行性能优化,并给出“确实加速 RocksDB”的证据。 -### 改动范围 -- `zvfs/zvfs.c` -- 性能脚本与统计输出 +### 任务 +1. 优化优先级: + - 对齐写 fast-path 与非对齐 RMW 优化; + - DMA buffer 复用/池化; + - 减少全局锁粒度; + - 批量/延迟元数据刷新策略。 +2. 构建统一 benchmark 脚本: + - 对照组 A:不使用 `LD_PRELOAD`; + - 对照组 B:`LD_PRELOAD=./libzvfs.so` + `/zvfs` 路径。 +3. 指标:吞吐(ops/s)、P99 延迟、CPU 使用率、失败率。 -### 任务清单 -1. 请求对象池 / DMA buffer 复用 -2. 减少全局锁竞争(热点路径细化锁) -3. 元数据 debounce 与批量刷盘参数化 -4. 增加指标: - - op 次数、失败率、平均/尾延迟、队列深度 +### 验收(最终目标) +- 在至少一个 RocksDB workload 上达到可重复的性能提升(建议目标 >= 1.3x,最终以实测为准)。 +- 提供完整报告:命令、环境、结果表、结论与剩余瓶颈。 -### 完成定义(DoD) -- 与旧版本对比,多线程 `db_bench` 吞吐显著提升(目标 >1.5x,按实测调整)。 -- 无新增数据一致性回归。 - -### 重入点 -- 每个优化项独立开关,可单独启停做 A/B。 +### 中断/重入 +- 每个优化项必须可单独开关,可单独回滚。 --- -## 4. 阶段间依赖关系(严格) +## 4. 阶段验收矩阵(执行时打勾) -1. 必须先完成 Phase 1 再做 Phase 2。 -2. 必须先完成 Phase 2 再做 Phase 3/4。 -3. Phase 4(核心 hook)完成后再做 Phase 5(辅助 hook)。 -4. Phase 6/7 必须基于 Phase 5 稳定分支。 +- [ ] P1 已产出 syscall matrix 且可复现。 +- [ ] P2 已完成现有语义修复且回归通过。 +- [ ] P3 已实现 P0 接口并单线程跑通 RocksDB。 +- [ ] P4 已实现 P1 接口并多线程稳定运行。 +- [ ] P5 已切换到 worker 并通过并发稳定性测试。 +- [ ] P6 已通过崩溃恢复与并发竞态测试。 +- [ ] P7 已完成性能对照并证明加速收益。 --- -## 5. 验收矩阵(执行时打勾) +## 5. 每阶段固定输出模板(执行时复用) -### Phase 1 -- [ ] 编译通过 -- [ ] `func_test` 通过 -- [ ] 请求对象已接管 read/write 内部主流程 - -### Phase 2 -- [ ] worker 模型接管 -- [ ] 多线程 smoke 测试通过 -- [ ] 无死锁/退出挂死 - -### Phase 3 -- [ ] `pread/pwrite` 语义正确 -- [ ] offset 并发污染问题消失 - -### Phase 4 -- [ ] P0 hook 集合已实现 -- [ ] RocksDB 单线程 workload 跑通 - -### Phase 5 -- [ ] P1 hook 集合已实现 -- [ ] RocksDB 多线程 workload 稳定 - -### Phase 6 -- [ ] 元数据校验与恢复策略生效 -- [ ] 崩溃恢复测试通过 - -### Phase 7 -- [ ] 关键指标可观测 -- [ ] 性能目标达成或给出瓶颈结论 - ---- - -## 6. 风险与应对(执行版) - -1. **接口补齐不完整** - - 应对:每阶段跑 `strace -f`,统计 `/zvfs` 相关未接管 syscall。 -2. **并发死锁** - - 应对:固定锁顺序;debug 模式打印锁获取日志。 -3. **行为回归** - - 应对:保留兼容开关(old path)直到该阶段验收通过。 -4. **性能退化** - - 应对:优化项独立开关,逐项 A/B,不一次性合并。 - ---- - -## 7. 给 Codex 的执行模板(每阶段复用) - -1. 阅读本阶段“目标/任务/DoD/重入点” -2. 只改本阶段文件与最小依赖 -3. 本地编译 + 对应测试 -4. 更新“阶段状态”勾选 -5. 输出: - - 改了什么 - - 测了什么 - - 还剩什么 -6. 等待用户确认再进入下一阶段 +1. 改动清单(文件 + 关键点)。 +2. 验证命令与结果。 +3. 风险/已知问题。 +4. 阶段状态勾选更新。 +5. 下一阶段入口条件是否满足。 diff --git a/rocksdb.md b/rocksdb.md new file mode 100644 index 0000000..65bd411 --- /dev/null +++ b/rocksdb.md @@ -0,0 +1,9 @@ +```shell +sudo apt install -y build-essential cmake git \ + libgflags-dev libsnappy-dev zlib1g-dev libbz2-dev \ + liblz4-dev libzstd-dev librocksdb-dev + + + + +``` \ No newline at end of file diff --git a/zvfs/Makefile b/zvfs/Makefile index 5844fa3..986e76a 100755 --- a/zvfs/Makefile +++ b/zvfs/Makefile @@ -9,7 +9,6 @@ include $(SPDK_ROOT_DIR)/mk/spdk.modules.mk include $(SPDK_ROOT_DIR)/mk/spdk.app_vars.mk LIBZVFS := libzvfs.so -APP := func_test C_SRCS := zvfs.c zvfs_hook.c @@ -22,16 +21,13 @@ LDFLAGS += -shared -rdynamic -Wl,-z,nodelete -Wl,--disable-new-dtags \ SYS_LIBS += -ldl -all: $(LIBZVFS) $(APP) +all: $(LIBZVFS) @: $(LIBZVFS): $(OBJS) $(SPDK_LIB_FILES) $(ENV_LIBS) $(LINK_C) -$(APP): func_test.c - $(CC) -o $@ $< - clean: - $(CLEAN_C) $(LIBZVFS) $(APP) + $(CLEAN_C) $(LIBZVFS) include $(SPDK_ROOT_DIR)/mk/spdk.deps.mk diff --git a/zvfs/zvfs.c b/zvfs/zvfs.c index ff34ac6..67b67bc 100755 --- a/zvfs/zvfs.c +++ b/zvfs/zvfs.c @@ -1,98 +1,98 @@ - + #include "zvfs.h" #include #undef SPDK_DEBUGLOG #define SPDK_DEBUGLOG(...) do {} while(0) - -#define ZVFS_BDEV "Nvme0n1" -#ifndef ZVFS_BDEV -#define ZVFS_BDEV "Malloc0" -#endif - -struct spdk_thread *global_thread = NULL; -const char *json_file = "/home/lian/share/10.1-spdk/zvfs/zvfs/zvfs.json"; - -// mount -void zvfs_do_mount(void *arg); -void zvfs_spdk_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx); -void zvfs_spdk_bs_load_cb(void *arg, struct spdk_blob_store *bs, int bserrno); -void zvfs_spdk_bs_init_cb(void *arg, struct spdk_blob_store *bs, int bserrno); - -// create -void zvfs_do_create(void *arg); -void zvfs_spdk_bs_create_blob_cb(void *arg, spdk_blob_id blobid, int bserrno); -void zvfs_spdk_bs_open_blob_cb(void *arg, struct spdk_blob *blb, int bserrno); -void zvfs_spdk_blob_resize_cb(void *arg, int bserrno); -void zvfs_spdk_blob_sync_cb(void *arg, int bserrno); -// open -void zvfs_do_open(void *arg); -void zvfs_spdk_bs_open_blob_cb2(void *arg, struct spdk_blob *blb, int bserrno); - -// read -void zvfs_do_read(void *arg); -void zvfs_spdk_blob_read_cb(void *arg, int bserrno); - -// write -void zvfs_do_write(void *arg); -void zvfs_do_write_io(zvfs_file_t *file); -void zvfs_spdk_blob_write_preread_cb(void *arg, int bserrno); -void zvfs_spdk_blob_write_resize_cb(void *arg, int bserrno); -void zvfs_spdk_blob_write_sync_cb(void *arg, int bserrno); + +#define ZVFS_BDEV "Nvme0n1" +#ifndef ZVFS_BDEV +#define ZVFS_BDEV "Malloc0" +#endif + +struct spdk_thread *global_thread = NULL; +const char *json_file = "/home/lian/share/10.1-spdk/zvfs/zvfs/zvfs.json"; + +// mount +void zvfs_do_mount(void *arg); +void zvfs_spdk_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx); +void zvfs_spdk_bs_load_cb(void *arg, struct spdk_blob_store *bs, int bserrno); +void zvfs_spdk_bs_init_cb(void *arg, struct spdk_blob_store *bs, int bserrno); + +// create +void zvfs_do_create(void *arg); +void zvfs_spdk_bs_create_blob_cb(void *arg, spdk_blob_id blobid, int bserrno); +void zvfs_spdk_bs_open_blob_cb(void *arg, struct spdk_blob *blb, int bserrno); +void zvfs_spdk_blob_resize_cb(void *arg, int bserrno); +void zvfs_spdk_blob_sync_cb(void *arg, int bserrno); +// open +void zvfs_do_open(void *arg); +void zvfs_spdk_bs_open_blob_cb2(void *arg, struct spdk_blob *blb, int bserrno); + +// read +void zvfs_do_read(void *arg); +void zvfs_spdk_blob_read_cb(void *arg, int bserrno); + +// write +void zvfs_do_write(void *arg); +void zvfs_do_write_io(zvfs_file_t *file); +void zvfs_spdk_blob_write_preread_cb(void *arg, int bserrno); +void zvfs_spdk_blob_write_resize_cb(void *arg, int bserrno); +void zvfs_spdk_blob_write_sync_cb(void *arg, int bserrno); void zvfs_spdk_blob_write_cb(void *arg, int bserrno); // close void zvfs_do_close(void *arg); void zvfs_spdk_blob_close_cb(void *arg, int bserrno); void zvfs_spdk_blob_open_fail_close_cb(void *arg, int bserrno); - -// delete -void zvfs_do_delete(void *arg); -void zvfs_spdk_blob_delete_cb(void *arg, int bserrno); - -// setup -void zvfs_json_load_fn(void *arg); -void json_app_load_done(int rc, void *ctx); - - -// unmount -void zvfs_do_umount(void *arg); -void zvfs_spdk_bs_unload_cb(void *arg, int bserrno); - -/* ================================================================== */ -/* HELPER */ -/* ================================================================== */ -static uint64_t zvfs_need_clusters(zvfs_t *fs, uint64_t end_byte) { - uint64_t cluster_size = spdk_bs_get_cluster_size(fs->bs); - return (end_byte + cluster_size - 1) / cluster_size; -} - -/* ---------- 辅助:计算本次 IO 涉及的 LBA 范围 ---------- */ -static void calc_lba_range(zvfs_file_t *file, - uint64_t *out_lba, - uint64_t *out_page_off, - uint64_t *out_lba_count) -{ - uint64_t io_unit = file->fs->io_unit_size; - uint64_t off = file->current_offset; - uint64_t cnt = file->io_count; - - *out_lba = off / io_unit; - *out_page_off = off % io_unit; - *out_lba_count = (*out_page_off + cnt + io_unit - 1) / io_unit; -} - -/* ---------- 确保 dma_buf 足够大 ---------- */ + +// delete +void zvfs_do_delete(void *arg); +void zvfs_spdk_blob_delete_cb(void *arg, int bserrno); + +// setup +void zvfs_json_load_fn(void *arg); +void json_app_load_done(int rc, void *ctx); + + +// unmount +void zvfs_do_umount(void *arg); +void zvfs_spdk_bs_unload_cb(void *arg, int bserrno); + +/* ================================================================== */ +/* HELPER */ +/* ================================================================== */ +static uint64_t zvfs_need_clusters(zvfs_t *fs, uint64_t end_byte) { + uint64_t cluster_size = spdk_bs_get_cluster_size(fs->bs); + return (end_byte + cluster_size - 1) / cluster_size; +} + +/* ---------- 辅助:计算本次 IO 涉及的 LBA 范围 ---------- */ +static void calc_lba_range(zvfs_file_t *file, + uint64_t *out_lba, + uint64_t *out_page_off, + uint64_t *out_lba_count) +{ + uint64_t io_unit = file->fs->io_unit_size; + uint64_t off = file->current_offset; + uint64_t cnt = file->io_count; + + *out_lba = off / io_unit; + *out_page_off = off % io_unit; + *out_lba_count = (*out_page_off + cnt + io_unit - 1) / io_unit; +} + +/* ---------- 确保 dma_buf 足够大 ---------- */ static int ensure_dma_buf(zvfs_file_t *file, uint64_t need_bytes) { - if (file->dma_buf && file->dma_buf_size >= need_bytes) return 0; - - if (file->dma_buf) spdk_free(file->dma_buf); - - file->dma_buf = spdk_malloc(need_bytes, 0x1000, NULL, - SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); - if (!file->dma_buf) { file->dma_buf_size = 0; return -1; } - + if (file->dma_buf && file->dma_buf_size >= need_bytes) return 0; + + if (file->dma_buf) spdk_free(file->dma_buf); + + file->dma_buf = spdk_malloc(need_bytes, 0x1000, NULL, + SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); + if (!file->dma_buf) { file->dma_buf_size = 0; return -1; } + file->dma_buf_size = need_bytes; return 0; } @@ -109,24 +109,24 @@ bool waiter(struct spdk_thread *thread, spdk_msg_fn start_fn, void *ctx, bool *f } spdk_thread_send_msg(thread, start_fn, ctx); - - int waiter_count = 0; - - do { - spdk_thread_poll(thread, 0, 0); - waiter_count ++; - } while(!(*finished) && waiter_count < WAITER_MAX_TIME); - - if (!(*finished) && waiter_count >= WAITER_MAX_TIME) { - return false; // timeout - } - - return true; -} - -/* ================================================================== */ -/* MOUNT */ -/* ================================================================== */ + + int waiter_count = 0; + + do { + spdk_thread_poll(thread, 0, 0); + waiter_count ++; + } while(!(*finished) && waiter_count < WAITER_MAX_TIME); + + if (!(*finished) && waiter_count >= WAITER_MAX_TIME) { + return false; // timeout + } + + return true; +} + +/* ================================================================== */ +/* MOUNT */ +/* ================================================================== */ void zvfs_do_mount(void *arg) { zvfs_t *fs = (zvfs_t*)arg; struct spdk_bs_dev *bs_dev = NULL; @@ -134,18 +134,18 @@ void zvfs_do_mount(void *arg) { return; } fs->op_errno = 0; - - // SPDK_DEBUGLOG("=== Listing ALL bdevs after JSON load ===\n"); - // struct spdk_bdev *bdev = spdk_bdev_first(); - // while (bdev) { - // SPDK_DEBUGLOG("Found bdev: [%s] product: %s\n", - // spdk_bdev_get_name(bdev), - // spdk_bdev_get_product_name(bdev)); - // bdev = spdk_bdev_next(bdev); - // } - // SPDK_DEBUGLOG("---------------------------------\n"); - // SPDK_DEBUGLOG("Trying to open: %s\n", ZVFS_BDEV); - + + // SPDK_DEBUGLOG("=== Listing ALL bdevs after JSON load ===\n"); + // struct spdk_bdev *bdev = spdk_bdev_first(); + // while (bdev) { + // SPDK_DEBUGLOG("Found bdev: [%s] product: %s\n", + // spdk_bdev_get_name(bdev), + // spdk_bdev_get_product_name(bdev)); + // bdev = spdk_bdev_next(bdev); + // } + // SPDK_DEBUGLOG("---------------------------------\n"); + // SPDK_DEBUGLOG("Trying to open: %s\n", ZVFS_BDEV); + int rc = spdk_bdev_create_bs_dev_ext(ZVFS_BDEV, zvfs_spdk_bdev_event_cb, NULL, &bs_dev); if (rc != 0) { SPDK_ERRLOG("=== bdev_open FAILED rc=%d (probably still not registered) ===\n", rc); @@ -159,7 +159,7 @@ void zvfs_do_mount(void *arg) { spdk_bs_load(bs_dev, NULL, zvfs_spdk_bs_load_cb, fs); fs->bs_dev_owned = false; } - + void zvfs_spdk_bs_load_cb(void *arg, struct spdk_blob_store *bs, int bserrno) { zvfs_t *fs = (zvfs_t*)arg; if (fs == NULL) { @@ -173,8 +173,8 @@ void zvfs_spdk_bs_load_cb(void *arg, struct spdk_blob_store *bs, int bserrno) { if (bserrno != 0) { SPDK_DEBUGLOG("load failed, new device, re-create bs_dev and init\n"); - - struct spdk_bs_dev *bs_dev = NULL; + + struct spdk_bs_dev *bs_dev = NULL; int rc = spdk_bdev_create_bs_dev_ext(ZVFS_BDEV, zvfs_spdk_bdev_event_cb, NULL, &bs_dev); if (rc != 0) { SPDK_ERRLOG("re-create bs_dev failed\n"); @@ -189,24 +189,24 @@ void zvfs_spdk_bs_load_cb(void *arg, struct spdk_blob_store *bs, int bserrno) { fs->bs_dev_owned = false; return; } - - uint64_t io_unit_size = spdk_bs_get_io_unit_size(bs); - SPDK_DEBUGLOG("io_unit_size : %"PRIu64"\n", io_unit_size); - SPDK_NOTICELOG("io_unit_size=%lu\n", io_unit_size); - - fs->io_unit_size = io_unit_size; - fs->bs = bs; + + uint64_t io_unit_size = spdk_bs_get_io_unit_size(bs); + SPDK_DEBUGLOG("io_unit_size : %"PRIu64"\n", io_unit_size); + SPDK_NOTICELOG("io_unit_size=%lu\n", io_unit_size); + + fs->io_unit_size = io_unit_size; + fs->bs = bs; fs->channel = spdk_bs_alloc_io_channel(fs->bs); if (fs->channel == NULL) { fs->op_errno = -ENOMEM; fs->finished = true; return ; } - - fs->finished = true; -} - - + + fs->finished = true; +} + + void zvfs_spdk_bs_init_cb(void *arg, struct spdk_blob_store *bs, int bserrno) { zvfs_t *fs = (zvfs_t*)arg; if (fs == NULL) { @@ -217,30 +217,30 @@ void zvfs_spdk_bs_init_cb(void *arg, struct spdk_blob_store *bs, int bserrno) { fs->finished = true; return; } - - uint64_t io_unit_size = spdk_bs_get_io_unit_size(bs); - SPDK_DEBUGLOG("io_unit_size : %"PRIu64"\n", io_unit_size); - SPDK_NOTICELOG("io_unit_size=%lu\n", io_unit_size); - - fs->io_unit_size = io_unit_size; - fs->bs = bs; + + uint64_t io_unit_size = spdk_bs_get_io_unit_size(bs); + SPDK_DEBUGLOG("io_unit_size : %"PRIu64"\n", io_unit_size); + SPDK_NOTICELOG("io_unit_size=%lu\n", io_unit_size); + + fs->io_unit_size = io_unit_size; + fs->bs = bs; fs->channel = spdk_bs_alloc_io_channel(fs->bs); if (fs->channel == NULL) { fs->op_errno = -ENOMEM; fs->finished = true; return ; } - - fs->finished = true; -} - -void zvfs_spdk_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, - void *event_ctx) { -} - -/* ================================================================== */ -/* CREATE */ -/* ================================================================== */ + + fs->finished = true; +} + +void zvfs_spdk_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, + void *event_ctx) { +} + +/* ================================================================== */ +/* CREATE */ +/* ================================================================== */ void zvfs_do_create(void *arg) { zvfs_file_t *file = (zvfs_file_t *)arg; if (file == NULL || file->fs == NULL || file->fs->bs == NULL) { @@ -268,10 +268,10 @@ void zvfs_spdk_bs_create_blob_cb(void *arg, spdk_blob_id blobid, int bserrno) { file->blob_id = blobid; SPDK_DEBUGLOG("create blobid : %"PRIu64"\n", blobid); - - spdk_bs_open_blob(file->fs->bs, blobid, zvfs_spdk_bs_open_blob_cb, file); -} - + + spdk_bs_open_blob(file->fs->bs, blobid, zvfs_spdk_bs_open_blob_cb, file); +} + void zvfs_spdk_bs_open_blob_cb(void *arg, struct spdk_blob *blb, int bserrno) { zvfs_file_t *file = (zvfs_file_t *)arg; if (bserrno) { @@ -285,9 +285,9 @@ void zvfs_spdk_bs_open_blob_cb(void *arg, struct spdk_blob *blb, int bserrno) { file->finished = true; return; } - - file->blob = blb; - + + file->blob = blb; + uint64_t free_cluster = spdk_bs_free_cluster_count(file->fs->bs); if(free_cluster == 0){ SPDK_ERRLOG("no free cluster: %d\n", bserrno); @@ -295,10 +295,10 @@ void zvfs_spdk_bs_open_blob_cb(void *arg, struct spdk_blob *blb, int bserrno) { file->finished = true; return ; } - - spdk_blob_resize(blb, 1, zvfs_spdk_blob_resize_cb, file); -} - + + spdk_blob_resize(blb, 1, zvfs_spdk_blob_resize_cb, file); +} + void zvfs_spdk_blob_resize_cb(void *arg, int bserrno) { zvfs_file_t *file = (zvfs_file_t *)arg; if (file == NULL) { @@ -311,15 +311,15 @@ void zvfs_spdk_blob_resize_cb(void *arg, int bserrno) { } uint64_t total = spdk_blob_get_num_clusters(file->blob); - SPDK_DEBUGLOG("resize blob :%"PRIu64"\n", total); - - if (file->dirent) { - file->dirent->allocated_clusters = total; - } - - spdk_blob_sync_md(file->blob, zvfs_spdk_blob_sync_cb, file); -} - + SPDK_DEBUGLOG("resize blob :%"PRIu64"\n", total); + + if (file->dirent) { + file->dirent->allocated_clusters = total; + } + + spdk_blob_sync_md(file->blob, zvfs_spdk_blob_sync_cb, file); +} + void zvfs_spdk_blob_sync_cb(void *arg, int bserrno) { zvfs_file_t *file = (zvfs_file_t *)arg; if (file == NULL) { @@ -338,14 +338,14 @@ void zvfs_spdk_blob_sync_cb(void *arg, int bserrno) { spdk_blob_close(file->blob, zvfs_spdk_blob_open_fail_close_cb, file); return ; } - - file->dma_buf_size = BUFFER_SIZE; - file->finished = true; -} - -/* ================================================================== */ -/* OPEN */ -/* ================================================================== */ + + file->dma_buf_size = BUFFER_SIZE; + file->finished = true; +} + +/* ================================================================== */ +/* OPEN */ +/* ================================================================== */ void zvfs_do_open(void *arg) { zvfs_file_t *file = (zvfs_file_t *)arg; if (file == NULL || file->fs == NULL || file->fs->bs == NULL) { @@ -358,10 +358,10 @@ void zvfs_do_open(void *arg) { file->op_errno = 0; spdk_bs_open_blob(file->fs->bs, file->blob_id, zvfs_spdk_bs_open_blob_cb2, file); } - + void zvfs_spdk_bs_open_blob_cb2(void *arg, struct spdk_blob *blb, int bserrno) { - zvfs_file_t *file = (zvfs_file_t *)arg; - + zvfs_file_t *file = (zvfs_file_t *)arg; + if (bserrno) { SPDK_ERRLOG("load blob error: %d\n", bserrno); file->op_errno = zvfs_err_from_bserrno(bserrno); @@ -373,9 +373,9 @@ void zvfs_spdk_bs_open_blob_cb2(void *arg, struct spdk_blob *blb, int bserrno) { file->finished = true; return; } - - file->blob = blb; - + + file->blob = blb; + file->dma_buf = spdk_malloc(BUFFER_SIZE, 0x1000, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); if (!file->dma_buf) { @@ -384,14 +384,14 @@ void zvfs_spdk_bs_open_blob_cb2(void *arg, struct spdk_blob *blb, int bserrno) { spdk_blob_close(file->blob, zvfs_spdk_blob_open_fail_close_cb, file); return; } - - file->dma_buf_size = BUFFER_SIZE; - file->finished = true; -} - -/* ================================================================== */ -/* READ */ -/* ================================================================== */ + + file->dma_buf_size = BUFFER_SIZE; + file->finished = true; +} + +/* ================================================================== */ +/* READ */ +/* ================================================================== */ void zvfs_do_read(void *arg) { zvfs_file_t *file = (zvfs_file_t *)arg; if (file == NULL || file->fs == NULL || file->blob == NULL || file->fs->channel == NULL) { @@ -413,28 +413,28 @@ void zvfs_do_read(void *arg) { return; } uint64_t offset = file->current_offset; - uint64_t file_sz = file->dirent ? file->dirent->file_size : 0; - - /* EOF 检查 */ - if (offset >= file_sz) { - SPDK_DEBUGLOG("read: EOF\n"); - file->io_count = 0; - file->actual_io_count = 0; - file->finished = true; - return; - } - - /* 截断到文件末尾 */ - if (offset + file->io_count > file_sz){ - file->io_count = file_sz - offset; - } - - file->actual_io_count = file->io_count; - - uint64_t lba, page_off, lba_count; - calc_lba_range(file, &lba, &page_off, &lba_count); - - uint64_t buf_need = lba_count * io_unit; + uint64_t file_sz = file->dirent ? file->dirent->file_size : 0; + + /* EOF 检查 */ + if (offset >= file_sz) { + SPDK_DEBUGLOG("read: EOF\n"); + file->io_count = 0; + file->actual_io_count = 0; + file->finished = true; + return; + } + + /* 截断到文件末尾 */ + if (offset + file->io_count > file_sz){ + file->io_count = file_sz - offset; + } + + file->actual_io_count = file->io_count; + + uint64_t lba, page_off, lba_count; + calc_lba_range(file, &lba, &page_off, &lba_count); + + uint64_t buf_need = lba_count * io_unit; if (ensure_dma_buf(file, buf_need) != 0) { SPDK_ERRLOG("ensure_dma_buf failed\n"); file->op_errno = -ENOMEM; @@ -448,10 +448,10 @@ void zvfs_do_read(void *arg) { lba, lba_count, zvfs_spdk_blob_read_cb, file); } - -void zvfs_spdk_blob_read_cb(void *arg, int bserrno) { - zvfs_file_t *file = (zvfs_file_t *)arg; - + +void zvfs_spdk_blob_read_cb(void *arg, int bserrno) { + zvfs_file_t *file = (zvfs_file_t *)arg; + if (bserrno) { SPDK_ERRLOG("blob_read error: %d\n", bserrno); file->op_errno = zvfs_err_from_bserrno(bserrno); @@ -460,32 +460,32 @@ void zvfs_spdk_blob_read_cb(void *arg, int bserrno) { file->finished = true; return; } - - file->current_offset += file->io_count; - SPDK_DEBUGLOG("read complete, new offset=%" PRIu64 "\n", file->current_offset); - file->finished = true; -} - -/* ================================================================== */ -/* WRITE */ -/* ================================================================== */ -/** - * 1. write 的 callback 链 - * zvfs_do_write - * └─→ 先用 spdk_blob_io_read 读出覆盖范围内的扇区 - * └─→ zvfs_spdk_blob_write_preread_cb - * (在 dma_buf 里 patch 新数据) - * ├─(需扩容)─→ spdk_blob_resize - * │ └─→ zvfs_spdk_blob_write_resize_cb - * │ └─→ spdk_blob_sync_md - * │ └─→ zvfs_spdk_blob_write_sync_cb - * │ └─→ zvfs_do_write_io - * │ └─→ zvfs_spdk_blob_write_cb - * └─(不需扩容)─→ zvfs_do_write_io - * └─→ zvfs_spdk_blob_write_cb - */ - -/* Step 1 : 进入 write,先把覆盖范围内的扇区读出来(read-modify-write) */ + + file->current_offset += file->io_count; + SPDK_DEBUGLOG("read complete, new offset=%" PRIu64 "\n", file->current_offset); + file->finished = true; +} + +/* ================================================================== */ +/* WRITE */ +/* ================================================================== */ +/** + * 1. write 的 callback 链 + * zvfs_do_write + * └─→ 先用 spdk_blob_io_read 读出覆盖范围内的扇区 + * └─→ zvfs_spdk_blob_write_preread_cb + * (在 dma_buf 里 patch 新数据) + * ├─(需扩容)─→ spdk_blob_resize + * │ └─→ zvfs_spdk_blob_write_resize_cb + * │ └─→ spdk_blob_sync_md + * │ └─→ zvfs_spdk_blob_write_sync_cb + * │ └─→ zvfs_do_write_io + * │ └─→ zvfs_spdk_blob_write_cb + * └─(不需扩容)─→ zvfs_do_write_io + * └─→ zvfs_spdk_blob_write_cb + */ + +/* Step 1 : 进入 write,先把覆盖范围内的扇区读出来(read-modify-write) */ void zvfs_do_write(void *arg) { zvfs_file_t *file = (zvfs_file_t *)arg; if (file == NULL || file->fs == NULL || file->blob == NULL || file->fs->channel == NULL) { @@ -505,7 +505,7 @@ void zvfs_do_write(void *arg) { return; } file->op_errno = 0; - + uint64_t io_unit = file->fs->io_unit_size; if (io_unit == 0) { file->op_errno = -EIO; @@ -513,51 +513,51 @@ void zvfs_do_write(void *arg) { return; } uint64_t lba, page_off, lba_count; - calc_lba_range(file, &lba, &page_off, &lba_count); - - uint64_t buf_need = lba_count * io_unit; + calc_lba_range(file, &lba, &page_off, &lba_count); + + uint64_t buf_need = lba_count * io_unit; if (ensure_dma_buf(file, buf_need) != 0) { SPDK_ERRLOG("ensure_dma_buf failed\n"); file->op_errno = -ENOMEM; file->finished = true; return; } - - file->aligned = (file->current_offset % io_unit == 0) && - (file->io_count % io_unit == 0); - - // static uint64_t aligned_count = 0; - // static uint64_t unaligned_count = 0; - // if (aligned) { - // aligned_count++; - // } else { - // unaligned_count++; - // } - // if ((aligned_count + unaligned_count) % 1000 == 0) { - // printf("aligned=%lu unaligned=%lu\n", aligned_count, unaligned_count); - // } - - if (file->aligned) { - /* 直接把用户数据拷到 dma_buf,跳过 preread */ - memcpy(file->dma_buf, file->write_staging_buf, file->io_count); - /* 直接进 preread_cb 的后半段逻辑(扩容判断+写) */ - zvfs_spdk_blob_write_preread_cb(file, 0); - } else { - /* - * 先把涉及的扇区读出,read 完成后在 preread_cb 里 patch 数据再写。 - * 注意:把用户数据暂存在 file->write_buf / write_count, - * 或者借用 file->io_count(io_count 不变)。 - * 这里我们把用户数据已经由上层调用者拷贝到了 write_staging_buf, - */ - /* 不管是否需要扩容,先 preread */ + + file->aligned = (file->current_offset % io_unit == 0) && + (file->io_count % io_unit == 0); + + // static uint64_t aligned_count = 0; + // static uint64_t unaligned_count = 0; + // if (aligned) { + // aligned_count++; + // } else { + // unaligned_count++; + // } + // if ((aligned_count + unaligned_count) % 1000 == 0) { + // printf("aligned=%lu unaligned=%lu\n", aligned_count, unaligned_count); + // } + + if (file->aligned) { + /* 直接把用户数据拷到 dma_buf,跳过 preread */ + memcpy(file->dma_buf, file->write_staging_buf, file->io_count); + /* 直接进 preread_cb 的后半段逻辑(扩容判断+写) */ + zvfs_spdk_blob_write_preread_cb(file, 0); + } else { + /* + * 先把涉及的扇区读出,read 完成后在 preread_cb 里 patch 数据再写。 + * 注意:把用户数据暂存在 file->write_buf / write_count, + * 或者借用 file->io_count(io_count 不变)。 + * 这里我们把用户数据已经由上层调用者拷贝到了 write_staging_buf, + */ + /* 不管是否需要扩容,先 preread */ spdk_blob_io_read(file->blob, file->fs->channel, file->dma_buf, lba, lba_count, zvfs_spdk_blob_write_preread_cb, file); } } - -/* Step 2 : preread 完成,patch dma_buf,然后决定是否扩容 */ + +/* Step 2 : preread 完成,patch dma_buf,然后决定是否扩容 */ void zvfs_spdk_blob_write_preread_cb(void *arg, int bserrno){ zvfs_file_t *file = (zvfs_file_t *)arg; if (file == NULL) { @@ -567,74 +567,74 @@ void zvfs_spdk_blob_write_preread_cb(void *arg, int bserrno){ /* preread 失败也没关系——如果是新分配区域全零即可, 这里仍然继续(SPDK 对未写过的区域返回全零)。*/ if (bserrno) { - SPDK_DEBUGLOG("preread error %d (may be uninitialized, continue)\n", bserrno); - } - - /* 只有非对齐情况才需要 patch,对齐情况下数据已经在 dma_buf 里了(do_write 里拷好的)*/ - uint64_t io_unit = file->fs->io_unit_size; - - if (!file->aligned) { - uint64_t page_off = file->current_offset % io_unit; - memcpy((uint8_t *)file->dma_buf + page_off, - file->write_staging_buf, - file->io_count); - } - - /* 判断是否需要扩容 */ - uint64_t end_byte = file->current_offset + file->io_count; - uint64_t need_clusters = zvfs_need_clusters(file->fs, end_byte); - uint64_t cur_clusters = file->dirent ? file->dirent->allocated_clusters - : spdk_blob_get_num_clusters(file->blob); - - if (need_clusters > cur_clusters) { - uint64_t free_clusters = spdk_bs_free_cluster_count(file->fs->bs); + SPDK_DEBUGLOG("preread error %d (may be uninitialized, continue)\n", bserrno); + } + + /* 只有非对齐情况才需要 patch,对齐情况下数据已经在 dma_buf 里了(do_write 里拷好的)*/ + uint64_t io_unit = file->fs->io_unit_size; + + if (!file->aligned) { + uint64_t page_off = file->current_offset % io_unit; + memcpy((uint8_t *)file->dma_buf + page_off, + file->write_staging_buf, + file->io_count); + } + + /* 判断是否需要扩容 */ + uint64_t end_byte = file->current_offset + file->io_count; + uint64_t need_clusters = zvfs_need_clusters(file->fs, end_byte); + uint64_t cur_clusters = file->dirent ? file->dirent->allocated_clusters + : spdk_blob_get_num_clusters(file->blob); + + if (need_clusters > cur_clusters) { + uint64_t free_clusters = spdk_bs_free_cluster_count(file->fs->bs); if (need_clusters - cur_clusters > free_clusters) { SPDK_ERRLOG("no free clusters\n"); file->op_errno = -ENOSPC; file->finished = true; return; } - spdk_blob_resize(file->blob, need_clusters, - zvfs_spdk_blob_write_resize_cb, file); - } else { - zvfs_do_write_io(file); - } -} - -/* Step 3a : resize 完成 → sync */ -void zvfs_spdk_blob_write_resize_cb(void *arg, int bserrno) { - zvfs_file_t *file = (zvfs_file_t *)arg; - + spdk_blob_resize(file->blob, need_clusters, + zvfs_spdk_blob_write_resize_cb, file); + } else { + zvfs_do_write_io(file); + } +} + +/* Step 3a : resize 完成 → sync */ +void zvfs_spdk_blob_write_resize_cb(void *arg, int bserrno) { + zvfs_file_t *file = (zvfs_file_t *)arg; + if (bserrno) { SPDK_ERRLOG("write resize error: %d\n", bserrno); file->op_errno = zvfs_err_from_bserrno(bserrno); file->finished = true; return; } - - spdk_blob_sync_md(file->blob, zvfs_spdk_blob_write_sync_cb, file); -} - -/* Step 3b : sync 完成 → 真正写 */ -void zvfs_spdk_blob_write_sync_cb(void *arg, int bserrno) { - zvfs_file_t *file = (zvfs_file_t *)arg; - + + spdk_blob_sync_md(file->blob, zvfs_spdk_blob_write_sync_cb, file); +} + +/* Step 3b : sync 完成 → 真正写 */ +void zvfs_spdk_blob_write_sync_cb(void *arg, int bserrno) { + zvfs_file_t *file = (zvfs_file_t *)arg; + if (bserrno) { SPDK_ERRLOG("write sync error: %d\n", bserrno); file->op_errno = zvfs_err_from_bserrno(bserrno); file->finished = true; return; } - - if (file->dirent) { - file->dirent->allocated_clusters = - (uint32_t)spdk_blob_get_num_clusters(file->blob); - } - - zvfs_do_write_io(file); -} - -/* Step 4 : 实际写入(dma_buf 已经是 patch 后的整扇区数据) */ + + if (file->dirent) { + file->dirent->allocated_clusters = + (uint32_t)spdk_blob_get_num_clusters(file->blob); + } + + zvfs_do_write_io(file); +} + +/* Step 4 : 实际写入(dma_buf 已经是 patch 后的整扇区数据) */ void zvfs_do_write_io(zvfs_file_t *file) { uint64_t io_unit_size = file->fs->io_unit_size; if (io_unit_size == 0) { @@ -643,40 +643,40 @@ void zvfs_do_write_io(zvfs_file_t *file) { return; } uint64_t lba = file->current_offset / io_unit_size; - uint64_t page_off = file->current_offset % io_unit_size; - uint64_t lba_count = (page_off + file->io_count + io_unit_size - 1) / io_unit_size; - + uint64_t page_off = file->current_offset % io_unit_size; + uint64_t lba_count = (page_off + file->io_count + io_unit_size - 1) / io_unit_size; + spdk_blob_io_write(file->blob, file->fs->channel, file->dma_buf, lba, lba_count, zvfs_spdk_blob_write_cb, file); } - -/* Step 5 : 写完成 */ -void zvfs_spdk_blob_write_cb(void *arg, int bserrno) { - zvfs_file_t *file = (zvfs_file_t *)arg; - + +/* Step 5 : 写完成 */ +void zvfs_spdk_blob_write_cb(void *arg, int bserrno) { + zvfs_file_t *file = (zvfs_file_t *)arg; + if (bserrno) { SPDK_ERRLOG("blob_write error: %d\n", bserrno); file->op_errno = zvfs_err_from_bserrno(bserrno); file->finished = true; return; } - - uint64_t new_end = file->current_offset + file->io_count; - if (file->dirent && new_end > file->dirent->file_size) { - file->dirent->file_size = new_end; - } - file->current_offset = new_end; - - SPDK_DEBUGLOG("write complete, new offset=%" PRIu64 "\n", file->current_offset); - file->finished = true; -} - - -/* ================================================================== */ -/* CLOSE */ -/* ================================================================== */ + + uint64_t new_end = file->current_offset + file->io_count; + if (file->dirent && new_end > file->dirent->file_size) { + file->dirent->file_size = new_end; + } + file->current_offset = new_end; + + SPDK_DEBUGLOG("write complete, new offset=%" PRIu64 "\n", file->current_offset); + file->finished = true; +} + + +/* ================================================================== */ +/* CLOSE */ +/* ================================================================== */ void zvfs_do_close(void *arg) { zvfs_file_t *file = (zvfs_file_t *)arg; if (file == NULL) { @@ -707,10 +707,10 @@ void zvfs_spdk_blob_open_fail_close_cb(void *arg, int bserrno) file->blob = NULL; file->finished = true; } - -void zvfs_spdk_blob_close_cb(void *arg, int bserrno) { - zvfs_file_t *file = (zvfs_file_t *)arg; - + +void zvfs_spdk_blob_close_cb(void *arg, int bserrno) { + zvfs_file_t *file = (zvfs_file_t *)arg; + if (bserrno) { SPDK_ERRLOG("blob_close error: %d\n", bserrno); file->op_errno = zvfs_err_from_bserrno(bserrno); @@ -719,13 +719,13 @@ void zvfs_spdk_blob_close_cb(void *arg, int bserrno) { file->dma_buf = NULL; file->blob = NULL; file->current_offset = 0; - - file->finished = true; -} - -/* ================================================================== */ -/* DELETE */ -/* ================================================================== */ + + file->finished = true; +} + +/* ================================================================== */ +/* DELETE */ +/* ================================================================== */ void zvfs_do_delete(void *arg) { zvfs_file_t *file = (zvfs_file_t *)arg; if (file == NULL || file->fs == NULL || file->fs->bs == NULL) { @@ -738,21 +738,21 @@ void zvfs_do_delete(void *arg) { file->op_errno = 0; spdk_bs_delete_blob(file->fs->bs, file->blob_id, zvfs_spdk_blob_delete_cb, file); } - -void zvfs_spdk_blob_delete_cb(void *arg, int bserrno) { - zvfs_file_t *file = (zvfs_file_t *)arg; - + +void zvfs_spdk_blob_delete_cb(void *arg, int bserrno) { + zvfs_file_t *file = (zvfs_file_t *)arg; + if (bserrno) { SPDK_ERRLOG("blob_delete error: %d\n", bserrno); file->op_errno = zvfs_err_from_bserrno(bserrno); } - - file->finished = true; -} - -/* ================================================================== */ -/* UNMOUNT */ -/* ================================================================== */ + + file->finished = true; +} + +/* ================================================================== */ +/* UNMOUNT */ +/* ================================================================== */ void zvfs_do_umount(void *arg) { zvfs_t *fs = (zvfs_t *)arg; @@ -776,7 +776,7 @@ void zvfs_do_umount(void *arg) { } fs->finished = true; } - + void zvfs_spdk_bs_unload_cb(void *arg, int bserrno) { zvfs_t *fs = (zvfs_t *)arg; @@ -791,29 +791,29 @@ void zvfs_spdk_bs_unload_cb(void *arg, int bserrno) { fs->bs_dev_owned = false; fs->finished = true; } - -// setup -// zvfs.json -int zvfs_env_setup(void) { - struct spdk_env_opts opts; - spdk_env_opts_init(&opts); - opts.name = "zvfs"; - - int rc = spdk_env_init(&opts); - if (rc != 0) { - return -1; - } - - spdk_log_set_print_level(SPDK_LOG_ERROR); - spdk_log_set_level(SPDK_LOG_NOTICE); - spdk_log_open(NULL); - - int rc2 = spdk_thread_lib_init(NULL, 0); - if (rc2 != 0) { - SPDK_ERRLOG("spdk_thread_lib_init failed\n"); - return -1; - } - + +// setup +// zvfs.json +int zvfs_env_setup(void) { + struct spdk_env_opts opts; + spdk_env_opts_init(&opts); + opts.name = "zvfs"; + + int rc = spdk_env_init(&opts); + if (rc != 0) { + return -1; + } + + spdk_log_set_print_level(SPDK_LOG_ERROR); + spdk_log_set_level(SPDK_LOG_NOTICE); + spdk_log_open(NULL); + + int rc2 = spdk_thread_lib_init(NULL, 0); + if (rc2 != 0) { + SPDK_ERRLOG("spdk_thread_lib_init failed\n"); + return -1; + } + global_thread = spdk_thread_create("global", NULL); if (global_thread == NULL) { SPDK_ERRLOG("spdk_thread_create failed\n"); @@ -826,49 +826,49 @@ int zvfs_env_setup(void) { SPDK_ERRLOG("json load waiter timeout\n"); return -1; } - - int retry = 0; - while (retry < 200) { // 最多等 20 秒 - spdk_thread_poll(global_thread, 0, 0); - if (spdk_bdev_get_by_name(ZVFS_BDEV) != NULL) { - SPDK_DEBUGLOG("bdev %s ready!\n", ZVFS_BDEV); - break; - } - usleep(100 * 1000); // 100ms - retry++; - } - - if (spdk_bdev_get_by_name(ZVFS_BDEV) == NULL) { - SPDK_ERRLOG("bdev %s not found after 20s timeout!\n", ZVFS_BDEV); - return -1; - } - - - SPDK_DEBUGLOG("zvfs_env_setup complete\n"); - return 0; -} - -void zvfs_json_load_fn(void *arg) { - spdk_subsystem_init_from_json_config(json_file, SPDK_DEFAULT_RPC_ADDR, json_app_load_done, - arg, true); - -} - -void json_app_load_done(int rc, void *ctx) { - bool *done = ctx; - if (rc != 0) { - SPDK_ERRLOG("JSON config load FAILED! rc=%d\n", rc); - } - // 不要 sleep!直接标记完成,让外部 waiter 去轮询 - *done = true; -} - - - - - -// filesystem -// load + + int retry = 0; + while (retry < 200) { // 最多等 20 秒 + spdk_thread_poll(global_thread, 0, 0); + if (spdk_bdev_get_by_name(ZVFS_BDEV) != NULL) { + SPDK_DEBUGLOG("bdev %s ready!\n", ZVFS_BDEV); + break; + } + usleep(100 * 1000); // 100ms + retry++; + } + + if (spdk_bdev_get_by_name(ZVFS_BDEV) == NULL) { + SPDK_ERRLOG("bdev %s not found after 20s timeout!\n", ZVFS_BDEV); + return -1; + } + + + SPDK_DEBUGLOG("zvfs_env_setup complete\n"); + return 0; +} + +void zvfs_json_load_fn(void *arg) { + spdk_subsystem_init_from_json_config(json_file, SPDK_DEFAULT_RPC_ADDR, json_app_load_done, + arg, true); + +} + +void json_app_load_done(int rc, void *ctx) { + bool *done = ctx; + if (rc != 0) { + SPDK_ERRLOG("JSON config load FAILED! rc=%d\n", rc); + } + // 不要 sleep!直接标记完成,让外部 waiter 去轮询 + *done = true; +} + + + + + +// filesystem +// load int zvfs_mount(struct zvfs_s *fs) { if (fs == NULL || global_thread == NULL) { return 0; @@ -930,25 +930,25 @@ int zvfs_read(struct zvfs_file_s *file, uint8_t *buffer, size_t count) { if(!ok) SPDK_ERRLOG("read result: ok=%d\n", ok); if (!ok || file->op_errno != 0) return -1; if (file->actual_io_count == 0) return 0; - - /* - * dma_buf 里存的是从 LBA 边界开始的整扇区数据, - * page_off 是 current_offset(读之前)相对于 LBA 边界的字节偏移。 - * - * current_offset 在 read_cb 里已经 += actual_io_count, - * 所以读之前的 offset = current_offset - actual_io_count。 - */ - - uint64_t pre_offset = file->current_offset - file->actual_io_count; - uint64_t page_off = pre_offset % file->fs->io_unit_size; - - memcpy(buffer, - (uint8_t *)file->dma_buf + page_off, - file->actual_io_count); - - return (int)file->actual_io_count; -} -// write + + /* + * dma_buf 里存的是从 LBA 边界开始的整扇区数据, + * page_off 是 current_offset(读之前)相对于 LBA 边界的字节偏移。 + * + * current_offset 在 read_cb 里已经 += actual_io_count, + * 所以读之前的 offset = current_offset - actual_io_count。 + */ + + uint64_t pre_offset = file->current_offset - file->actual_io_count; + uint64_t page_off = pre_offset % file->fs->io_unit_size; + + memcpy(buffer, + (uint8_t *)file->dma_buf + page_off, + file->actual_io_count); + + return (int)file->actual_io_count; +} +// write int zvfs_write(struct zvfs_file_s *file, const uint8_t *buffer, size_t count) { if (file == NULL || global_thread == NULL) { return -1; @@ -984,53 +984,53 @@ int zvfs_delete(struct zvfs_file_s *file) { if(!ok) SPDK_ERRLOG("delete result: ok=%d\n", ok); return ok && file->op_errno == 0; } - -int main(int argc, char *argv[]) { - - if (zvfs_env_setup()) { - return -1; - } - - SPDK_NOTICELOG("zvfs_env_setup success\n"); - - SPDK_NOTICELOG("\n\n zvfs mount start \n\n"); - zvfs_t *fs = calloc(1, sizeof(zvfs_t)); - zvfs_mount(fs); - - - SPDK_NOTICELOG("\n\n zvfs open start \n\n"); - zvfs_file_t *file = calloc(1, sizeof(zvfs_file_t)); - file->fs = fs; - zvfs_dirent_t *dirent = calloc(1, sizeof(zvfs_dirent_t)); - file->dirent = dirent; - zvfs_create(file); - - SPDK_NOTICELOG("\n\n zvfs write start \n\n"); - char *buffer = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - zvfs_write(file, buffer, strlen(buffer)); - char *buffer2 = "abcdefghijklmnopqrstuvwxyz"; - zvfs_write(file, buffer2, strlen(buffer2)); - - - SPDK_NOTICELOG("\n\n zvfs read start \n\n"); - file->current_offset = 0; - char rbuffer[BUFFER_SIZE] = {0}; - int n = zvfs_read(file, rbuffer, BUFFER_SIZE); - SPDK_NOTICELOG("READ BUFFER:%d, %s\n", n, rbuffer); - - SPDK_NOTICELOG("\n\n zvfs close start \n\n"); - zvfs_close(file); - - SPDK_NOTICELOG("\n\n zvfs delete start \n\n"); - zvfs_delete(file); - - free(dirent); - free(file); - - SPDK_NOTICELOG("\n\n zvfs umount start \n\n"); - zvfs_umount(fs); - free(fs); - -} - - + +// int main(int argc, char *argv[]) { + +// if (zvfs_env_setup()) { +// return -1; +// } + +// SPDK_NOTICELOG("zvfs_env_setup success\n"); + +// SPDK_NOTICELOG("\n\n zvfs mount start \n\n"); +// zvfs_t *fs = calloc(1, sizeof(zvfs_t)); +// zvfs_mount(fs); + + +// SPDK_NOTICELOG("\n\n zvfs open start \n\n"); +// zvfs_file_t *file = calloc(1, sizeof(zvfs_file_t)); +// file->fs = fs; +// zvfs_dirent_t *dirent = calloc(1, sizeof(zvfs_dirent_t)); +// file->dirent = dirent; +// zvfs_create(file); + +// SPDK_NOTICELOG("\n\n zvfs write start \n\n"); +// char *buffer = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +// zvfs_write(file, buffer, strlen(buffer)); +// char *buffer2 = "abcdefghijklmnopqrstuvwxyz"; +// zvfs_write(file, buffer2, strlen(buffer2)); + + +// SPDK_NOTICELOG("\n\n zvfs read start \n\n"); +// file->current_offset = 0; +// char rbuffer[BUFFER_SIZE] = {0}; +// int n = zvfs_read(file, rbuffer, BUFFER_SIZE); +// SPDK_NOTICELOG("READ BUFFER:%d, %s\n", n, rbuffer); + +// SPDK_NOTICELOG("\n\n zvfs close start \n\n"); +// zvfs_close(file); + +// SPDK_NOTICELOG("\n\n zvfs delete start \n\n"); +// zvfs_delete(file); + +// free(dirent); +// free(file); + +// SPDK_NOTICELOG("\n\n zvfs umount start \n\n"); +// zvfs_umount(fs); +// free(fs); + +// } + +