zvfs: hook db_bench thread 1 complete
This commit is contained in:
98
plan/baseline_commands.md
Normal file
98
plan/baseline_commands.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# Phase 0 - 基线命令与验证记录
|
||||
|
||||
- 日期: 2026-03-02
|
||||
- 主机: `ubuntu`(本地开发机)
|
||||
- db_bench 路径: `/home/lian/env/rocksdb-test/db_bench`
|
||||
- LD_PRELOAD 库: `/home/lian/share/10.1-spdk/zvfs/zvfs/libzvfs.so`
|
||||
|
||||
## 1) 成功基线(不启用 LD_PRELOAD)
|
||||
|
||||
### 命令
|
||||
```bash
|
||||
rm -rf /tmp/rdb_phase0_plain && \
|
||||
strace -f -qq -o /tmp/phase0_plain.strace \
|
||||
-e trace=%file,%desc,fsync,fdatasync,ftruncate,fallocate,pread64,pwrite64,lseek,rename,renameat,renameat2 \
|
||||
/home/lian/env/rocksdb-test/db_bench \
|
||||
--benchmarks=fillseq \
|
||||
--db=/tmp/rdb_phase0_plain \
|
||||
--num=5000 \
|
||||
--value_size=128 \
|
||||
--threads=1 \
|
||||
--compression_type=none \
|
||||
--stats_interval_seconds=0
|
||||
```
|
||||
|
||||
### 结果摘要
|
||||
- 命令退出码: `0`
|
||||
- db_bench 输出: `fillseq ... 5000 operations`(成功)
|
||||
- errno 分布(trace 全局):
|
||||
- `ENOENT x11`
|
||||
- `EEXIST x9`
|
||||
- 关键 syscall(全局计数):
|
||||
- `openat 91`, `fcntl 82`, `pread64 8`, `fsync 10`, `fdatasync 10`, `ftruncate 5`, `fallocate 5`, `rename 9`
|
||||
- DB 路径相关 syscall(`/tmp/rdb_phase0_plain`):
|
||||
- `openat 61`, `mkdir 12`, `access 10`, `rename 9`, `unlink 12`, `rmdir 2`
|
||||
|
||||
## 2) 失败分支(启用 LD_PRELOAD,/zvfs 路径)
|
||||
|
||||
### 命令
|
||||
```bash
|
||||
strace -f -qq -o /tmp/phase0_preload.strace \
|
||||
-e trace=%file,%desc,fsync,fdatasync,ftruncate,fallocate,pread64,pwrite64,lseek,rename,renameat,renameat2 \
|
||||
env LD_PRELOAD=/home/lian/share/10.1-spdk/zvfs/zvfs/libzvfs.so \
|
||||
/home/lian/env/rocksdb-test/db_bench \
|
||||
--benchmarks=fillseq \
|
||||
--db=/zvfs/rdb_phase0_preload \
|
||||
--num=1000 \
|
||||
--value_size=128 \
|
||||
--threads=1 \
|
||||
--compression_type=none \
|
||||
--stats_interval_seconds=0
|
||||
```
|
||||
|
||||
### 结果摘要
|
||||
- 命令退出码: `1`
|
||||
- 关键报错:
|
||||
- `Cannot use IOVA as 'PA'`
|
||||
- `Failed to initialize DPDK`
|
||||
- `open error: ... While mkdir if missing: /zvfs/rdb_phase0_preload: No such file or directory`
|
||||
- `/zvfs/rdb_phase0_preload` 相关错误码:
|
||||
- `ENOENT x4`
|
||||
- 直接证据(trace):
|
||||
- `openat(..., "/zvfs/rdb_phase0_preload", ... O_DIRECTORY) = -1 ENOENT`
|
||||
- `mkdir("/zvfs/rdb_phase0_preload", 0755) = -1 ENOENT`
|
||||
|
||||
## 3) 提取统计的复用命令
|
||||
|
||||
### 3.1 syscall 频次(全局)
|
||||
```bash
|
||||
sed -E 's/^\[pid +[0-9]+\] +//; s/^[0-9]+ +//' /tmp/phase0_plain.strace \
|
||||
| grep -oP '^[a-zA-Z_][a-zA-Z0-9_]*(?=\()' \
|
||||
| sort | uniq -c | sort -nr
|
||||
```
|
||||
|
||||
### 3.2 errno 分布
|
||||
```bash
|
||||
grep -oP '= -1 [A-Z0-9]+' /tmp/phase0_plain.strace | awk '{print $3}' | sort | uniq -c | sort -nr
|
||||
grep -oP '= -1 [A-Z0-9]+' /tmp/phase0_preload.strace | awk '{print $3}' | sort | uniq -c | sort -nr
|
||||
```
|
||||
|
||||
### 3.3 DB 路径相关 syscall
|
||||
```bash
|
||||
grep '/tmp/rdb_phase0_plain' /tmp/phase0_plain.strace \
|
||||
| sed -E 's/^\[pid +[0-9]+\] +//; s/^[0-9]+ +//' \
|
||||
| grep -oP '^[a-zA-Z_][a-zA-Z0-9_]*(?=\()' \
|
||||
| sort | uniq -c | sort -nr
|
||||
```
|
||||
|
||||
## 4) Phase0 正确性验证清单(执行记录)
|
||||
|
||||
- [x] 每个“phase2 必须实现 syscall”都在矩阵中给出复现证据或来源。
|
||||
- [x] 基线命令可重复执行(成功路径与失败路径各 1 组)。
|
||||
- [x] 输出包含 syscall 统计与错误码分布。
|
||||
- [x] 验证记录已写入 `plan/baseline_commands.md`。
|
||||
|
||||
## 5) 已知限制(phase1 前需处理)
|
||||
|
||||
1. 当前环境中 SPDK 初始化失败(IOVA/PA),导致无法在本机直接完成完整 `/zvfs` I/O 链路验证。
|
||||
2. phase0 主要覆盖 `fillseq`;phase1/phase2 开始前应补 `readrandom/overwrite` 的 syscall 采样。
|
||||
168
plan/phase1_validation.md
Normal file
168
plan/phase1_validation.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# Phase 1 验证方案(用户执行)
|
||||
|
||||
> 背景:本轮已完成 phase1 代码改造,但当前代理环境无法在 root + SPDK 运行条件下完成端到端验证。下面步骤请你在本机执行。
|
||||
|
||||
## 0) 目标
|
||||
|
||||
验证 3 件事:
|
||||
1. `zvfs_io_req` 解耦后,`read/write` 旧路径行为不回退。
|
||||
2. 新增 `zvfs_pread/zvfs_pwrite` API 能正确处理 offset I/O。
|
||||
3. 构建产物 `libzvfs.so` 可正常编译链接。
|
||||
|
||||
---
|
||||
|
||||
## 1) 构建验证
|
||||
|
||||
```bash
|
||||
cd /home/lian/share/10.1-spdk/zvfs
|
||||
make -C zvfs -j
|
||||
```
|
||||
|
||||
期望:
|
||||
- 编译成功,无报错。
|
||||
- 生成 `/home/lian/share/10.1-spdk/zvfs/zvfs/libzvfs.so`。
|
||||
|
||||
---
|
||||
|
||||
## 2) Phase1 新 API 验证(不依赖 LD_PRELOAD hook)
|
||||
|
||||
### 2.1 生成临时测试程序
|
||||
|
||||
```bash
|
||||
cat >/tmp/phase1_api_check.c <<'EOF'
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "zvfs.h"
|
||||
|
||||
static int expect_eq(const char *name, const void *got, const void *exp, size_t n) {
|
||||
if (memcmp(got, exp, n) != 0) {
|
||||
fprintf(stderr, "[FAIL] %s mismatch\n", name);
|
||||
return -1;
|
||||
}
|
||||
printf("[OK] %s\n", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int rc = 1;
|
||||
int mounted = 0;
|
||||
int created = 0;
|
||||
if (zvfs_env_setup() != 0) {
|
||||
fprintf(stderr, "zvfs_env_setup failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
zvfs_t *fs = calloc(1, sizeof(*fs));
|
||||
zvfs_file_t *file = calloc(1, sizeof(*file));
|
||||
zvfs_dirent_t *dirent = calloc(1, sizeof(*dirent));
|
||||
if (!fs || !file || !dirent) {
|
||||
rc = 2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!zvfs_mount(fs)) {
|
||||
fprintf(stderr, "zvfs_mount failed\n");
|
||||
rc = 3;
|
||||
goto out;
|
||||
}
|
||||
mounted = 1;
|
||||
|
||||
file->fs = fs;
|
||||
file->dirent = dirent;
|
||||
if (!zvfs_create(file)) {
|
||||
fprintf(stderr, "zvfs_create failed\n");
|
||||
rc = 4;
|
||||
goto out;
|
||||
}
|
||||
created = 1;
|
||||
|
||||
/* 验证 pwrite + pread 的 offset 语义 */
|
||||
const char *a = "AAAA";
|
||||
const char *b = "BBBB";
|
||||
if (zvfs_pwrite(file, (const uint8_t *)a, 4, 0) != 4) { rc = 5; goto out; }
|
||||
if (zvfs_pwrite(file, (const uint8_t *)b, 4, 8) != 4) { rc = 6; goto out; }
|
||||
|
||||
uint8_t got[12] = {0};
|
||||
uint8_t exp[12] = {'A','A','A','A',0,0,0,0,'B','B','B','B'};
|
||||
if (zvfs_pread(file, got, sizeof(got), 0) != 12) { rc = 7; goto out; }
|
||||
if (expect_eq("pread/pwrite-hole", got, exp, sizeof(exp)) != 0) { rc = 8; goto out; }
|
||||
|
||||
/* 验证旧 read/write 顺序语义未回退 */
|
||||
file->current_offset = 0;
|
||||
const char *c = "CCCC";
|
||||
if (zvfs_write(file, (const uint8_t *)c, 4) != 4) { rc = 9; goto out; }
|
||||
file->current_offset = 0;
|
||||
uint8_t got2[4] = {0};
|
||||
if (zvfs_read(file, got2, sizeof(got2)) != 4) { rc = 10; goto out; }
|
||||
if (expect_eq("read/write-seq", got2, c, 4) != 0) { rc = 11; goto out; }
|
||||
|
||||
rc = 0;
|
||||
printf("[PASS] phase1_api_check\n");
|
||||
|
||||
out:
|
||||
if (created) {
|
||||
(void)zvfs_close(file);
|
||||
(void)zvfs_delete(file);
|
||||
}
|
||||
if (mounted) {
|
||||
(void)zvfs_umount(fs);
|
||||
}
|
||||
|
||||
free(dirent);
|
||||
free(file);
|
||||
free(fs);
|
||||
return rc;
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
### 2.2 编译并运行
|
||||
|
||||
```bash
|
||||
gcc -O2 -Wall -Wextra -std=gnu11 \
|
||||
-I/home/lian/share/10.1-spdk/zvfs/zvfs \
|
||||
-I/home/lian/share/10.1-spdk/zvfs/spdk/include \
|
||||
-o /tmp/phase1_api_check /tmp/phase1_api_check.c \
|
||||
/home/lian/share/10.1-spdk/zvfs/zvfs/libzvfs.so -ldl
|
||||
|
||||
sudo /tmp/phase1_api_check
|
||||
```
|
||||
|
||||
期望:
|
||||
- 输出包含:
|
||||
- `[OK] pread/pwrite-hole`
|
||||
- `[OK] read/write-seq`
|
||||
- `[PASS] phase1_api_check`
|
||||
|
||||
---
|
||||
|
||||
## 3) 旧 POSIX 路径回归(LD_PRELOAD)
|
||||
|
||||
```bash
|
||||
cd /home/lian/share/10.1-spdk/zvfs
|
||||
make -C test -j
|
||||
|
||||
sudo env LD_PRELOAD=/home/lian/share/10.1-spdk/zvfs/zvfs/libzvfs.so ./test/bin/test_basic /zvfs
|
||||
sudo env LD_PRELOAD=/home/lian/share/10.1-spdk/zvfs/zvfs/libzvfs.so ./test/bin/test_lseek /zvfs
|
||||
sudo env LD_PRELOAD=/home/lian/share/10.1-spdk/zvfs/zvfs/libzvfs.so ./test/bin/test_two_files /zvfs
|
||||
```
|
||||
|
||||
期望:
|
||||
- `test_basic`、`test_lseek`、`test_two_files` 通过。
|
||||
- 无崩溃、无明显数据错乱。
|
||||
|
||||
若出现一次性 `zvfs_mount failed`,建议先确保没有残留测试进程,再重跑一次验证命令。
|
||||
|
||||
---
|
||||
|
||||
## 4) 验证完成后回填
|
||||
|
||||
请把验证结果回填到 `plan/plan.md` 的 phase1 区域:
|
||||
- `### 阶段验收`
|
||||
- `### 正确性验证方案`
|
||||
|
||||
若任何一项失败,请附上:
|
||||
1. 命令;
|
||||
2. 错误输出;
|
||||
3. 是否可稳定复现。
|
||||
82
plan/phase2_validation.md
Normal file
82
plan/phase2_validation.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Phase 2 验证方案(用户执行)
|
||||
|
||||
目标:验证 phase2 新增 hook 与 POSIX 语义(openat/pread/pwrite/fsync/ftruncate/stat/rename 等)。
|
||||
|
||||
## 1) 构建
|
||||
|
||||
```bash
|
||||
cd /home/lian/share/10.1-spdk/zvfs
|
||||
make -C zvfs -j
|
||||
make -C test -j
|
||||
```
|
||||
|
||||
## 2) 关键回归(LD_PRELOAD)
|
||||
|
||||
```bash
|
||||
cd /home/lian/share/10.1-spdk/zvfs
|
||||
sudo env LD_PRELOAD=/home/lian/share/10.1-spdk/zvfs/zvfs/libzvfs.so ./test/bin/test_basic /zvfs
|
||||
sudo env LD_PRELOAD=/home/lian/share/10.1-spdk/zvfs/zvfs/libzvfs.so ./test/bin/test_lseek /zvfs
|
||||
sudo env LD_PRELOAD=/home/lian/share/10.1-spdk/zvfs/zvfs/libzvfs.so ./test/bin/test_two_files /zvfs
|
||||
sudo env LD_PRELOAD=/home/lian/share/10.1-spdk/zvfs/zvfs/libzvfs.so ./test/bin/test_phase2_posix /zvfs
|
||||
```
|
||||
|
||||
期望:
|
||||
- 以上 4 个测试全部 PASSED。
|
||||
- `test_phase2_posix` 会覆盖:
|
||||
- `mkdir/openat/close/rmdir`
|
||||
- `pread/pwrite` + 稀疏洞校验
|
||||
- `fsync/fdatasync`
|
||||
- `ftruncate`
|
||||
- `fstat/stat/access`
|
||||
- `rename`
|
||||
- `O_EXCL` 与只读 fd 上 `write` 的 errno
|
||||
|
||||
## 3) db_bench 最小验证
|
||||
|
||||
```bash
|
||||
cd /home/lian/share/10.1-spdk/zvfs
|
||||
sudo env LD_PRELOAD=/home/lian/share/10.1-spdk/zvfs/zvfs/libzvfs.so \
|
||||
/home/lian/env/rocksdb-test/db_bench \
|
||||
--benchmarks=fillseq \
|
||||
--db=/zvfs/rdb_phase2 \
|
||||
--num=200000 \
|
||||
--value_size=128 \
|
||||
--threads=1 \
|
||||
--compression_type=none \
|
||||
--stats_interval_seconds=5
|
||||
|
||||
|
||||
sudo /home/lian/env/rocksdb-test/db_bench \
|
||||
--benchmarks=fillseq \
|
||||
--db=/tmp/rdb_native \
|
||||
--num=200000 \
|
||||
--value_size=128 \
|
||||
--threads=1 \
|
||||
--compression_type=none \
|
||||
--stats_interval_seconds=5
|
||||
```
|
||||
|
||||
期望:
|
||||
- 不出现 `Function not implemented`
|
||||
- 不出现 `Bad file descriptor`
|
||||
- 不出现 `While mkdir if missing` 相关 `ENOENT`
|
||||
|
||||
## 4) 失败场景建议(可选)
|
||||
|
||||
用 `strace` 辅助确认关键 syscall 已被接管:
|
||||
|
||||
```bash
|
||||
sudo strace -f -qq -o /tmp/phase2_check.strace \
|
||||
-e trace=%file,%desc,fsync,fdatasync,ftruncate,pread64,pwrite64,rename,fcntl \
|
||||
env LD_PRELOAD=/home/lian/share/10.1-spdk/zvfs/zvfs/libzvfs.so ./test/bin/test_phase2_posix /zvfs
|
||||
```
|
||||
|
||||
看点:
|
||||
- `openat("/zvfs/...")` 不再因目录缺失直接失败
|
||||
- `rename`/`ftruncate`/`pread64`/`fdatasync` 调用链完整
|
||||
|
||||
## 5) 回填要求
|
||||
|
||||
执行后请把结果回填到 `plan/plan.md` 的 phase2 区域:
|
||||
- `### 阶段验收`
|
||||
- `### 正确性验证方案`
|
||||
249
plan/plan.md
Normal file
249
plan/plan.md
Normal file
@@ -0,0 +1,249 @@
|
||||
# ZVFS -> RocksDB (LD_PRELOAD) 分阶段改造计划(可重入)
|
||||
|
||||
本文档是给后续编码代理(我)使用的执行计划。目标是:在不破坏现有功能的前提下,逐步把 `zvfs.c + zvfs_hook.c` 改造成可支撑 RocksDB 的 LD_PRELOAD 存储层。
|
||||
|
||||
---
|
||||
|
||||
## 0. 使用规则(可重入协议)
|
||||
|
||||
每次开始新一轮开发时,严格按以下顺序执行:
|
||||
|
||||
1. 读取本文件,定位“当前阶段”和“未完成任务”。
|
||||
2. 先做“状态核对”:
|
||||
- 代码是否已存在同名结构/函数;
|
||||
- 测试是否已覆盖该阶段验收项;
|
||||
- 若已完成则跳过,不重复改动(幂等)。
|
||||
3. 只推进一个阶段内的最小闭环,不跨阶段大改。
|
||||
4. 每完成一个任务,立即更新本文件:
|
||||
- 勾选完成项;
|
||||
- 记录关键变更文件;
|
||||
- 记录剩余风险;
|
||||
- 记录本阶段“正确性验证”执行结果。
|
||||
5. 若发现与计划冲突的现实约束(SPDK限制/接口差异/语义冲突),先在“变更记录”追加说明,再调整后续子任务,不直接删原计划。
|
||||
6. 非代码文档统一写入 `plan/` 目录(用户约束)。
|
||||
|
||||
---
|
||||
|
||||
## 1. 总目标与约束
|
||||
|
||||
### 1.1 总目标
|
||||
- 支持 RocksDB 关键 I/O 路径(优先 db_bench 可运行并稳定)。
|
||||
- 提供正确 POSIX 语义(至少覆盖 RocksDB 依赖子集)。
|
||||
- 从单线程串行模型演进到可并行数据面。
|
||||
- 保证崩溃后一致性(元数据持久化可恢复)。
|
||||
|
||||
### 1.2 当前主要问题(已确认)
|
||||
- 单线程 + busy wait + 全局串行,队列深度近似 1。
|
||||
- hook 覆盖不足,缺少 pread/pwrite/fsync/ftruncate/openat/stat 等。
|
||||
- I/O 参数耦合在 `zvfs_file_t`,不利于 pread/pwrite 和并发。
|
||||
- 全局状态无并发保护(`g_fs/fd_table/dirents/open_count` 等)。
|
||||
- open/write/close/unlink errno 与 POSIX 语义不完整。
|
||||
- 元数据依赖宿主文本文件,容量小、非原子、不可恢复。
|
||||
- 配置路径硬编码,部署迁移性差。
|
||||
|
||||
### 1.3 强制要求(新增)
|
||||
- 每个阶段都必须定义“正确性验证方案”,且在阶段结束前执行并记录结果。
|
||||
- 未完成正确性验证,不得将该阶段标记为完成。
|
||||
|
||||
---
|
||||
|
||||
## 2. 阶段总览
|
||||
|
||||
- 阶段0:基线与兼容清单(先知道 RocksDB 真实需要什么)
|
||||
- 阶段1:架构解耦(file vs io_req)
|
||||
- 阶段2:补齐关键 hook 与 POSIX 语义
|
||||
- 阶段3:并发模型升级(控制面/数据面分离)
|
||||
- 阶段4:元数据持久化重构(super blob / 日志)
|
||||
- 阶段5:性能与稳定性验收
|
||||
|
||||
---
|
||||
|
||||
## 3. 详细阶段计划
|
||||
|
||||
## 阶段0:基线与兼容清单
|
||||
|
||||
### 目标
|
||||
- 建立“RocksDB syscall 最小集合”和“当前实现缺口表”。
|
||||
|
||||
### 任务
|
||||
- [x] 用 `strace` 或等效方式采集 `db_bench` syscall(含失败分支)。
|
||||
- [x] 形成兼容矩阵:必须实现 / 可降级透传 / 暂不支持。
|
||||
- [x] 固化第一批验收用例(最小 db_bench 参数 + 现有单测集合)。
|
||||
|
||||
### 交付物
|
||||
- [x] `plan/rocksdb_syscall_matrix.md`
|
||||
- [x] `plan/baseline_commands.md`
|
||||
|
||||
### 阶段验收
|
||||
- [x] 能明确列出阶段2必须完成的 hook 列表。
|
||||
|
||||
### 正确性验证方案
|
||||
- [x] 验证矩阵中的每个“必须实现 syscall”都有最小复现样例(命令或小程序)。
|
||||
- [x] 基线命令可重复执行,输出包含 syscall 统计与错误码分布。
|
||||
- [x] 验证结果记录到 `plan/baseline_commands.md`(含日期和环境信息)。
|
||||
|
||||
---
|
||||
|
||||
## 阶段1:架构解耦(关键)
|
||||
|
||||
### 目标
|
||||
- 把 I/O 请求参数从 `zvfs_file_t` 中拆出,建立请求对象,先打通 pread/pwrite 内核路径。
|
||||
|
||||
### 任务
|
||||
- [x] 新增 `zvfs_io_req`(字段至少:op/buf/len/offset/flags/result/errno/finished)。
|
||||
- [x] 重构 `zvfs_read/zvfs_write` 为基于 `zvfs_io_req` 的通用入口。
|
||||
- [x] 实现内部 `zvfs_pread_internal/zvfs_pwrite_internal`,不改 hook 先可调用。
|
||||
- [x] 移除 `zvfs_file_t` 中仅一次请求有效的临时字段(或标记弃用)。
|
||||
|
||||
### 交付物
|
||||
- [x] `zvfs/zvfs.h` 新结构与接口
|
||||
- [x] `zvfs/zvfs.c` 读写路径重构完成
|
||||
- [x] `plan/phase1_validation.md`(用户侧验证步骤)
|
||||
|
||||
### 阶段验收
|
||||
- [x] 现有 read/write/lseek 测试全通过。
|
||||
- [x] 新增 pread/pwrite 单测通过(可先直连接口,不经 hook)。
|
||||
|
||||
### 正确性验证方案
|
||||
- [x] 新旧接口结果一致性校验:同一输入下 `read/write` 与 `pread/pwrite` 数据一致。
|
||||
- [x] 边界测试:offset=0、EOF、跨 page、非对齐、空写入。
|
||||
- [x] 失败路径测试:非法 fd/参数时返回值与 `errno` 符合预期。
|
||||
|
||||
---
|
||||
|
||||
## 阶段2:补齐 hook 与 POSIX 语义
|
||||
|
||||
### 目标
|
||||
- 满足 RocksDB 最小兼容 API 集,保证语义与 errno 正确。
|
||||
|
||||
### 首批必须实现
|
||||
- [x] `pread/pwrite`(及 `pread64/pwrite64` 视平台符号而定)
|
||||
- [x] `open/open64/openat`
|
||||
- [x] `fsync/fdatasync`
|
||||
- [x] `ftruncate`
|
||||
- [x] `fstat/stat/lstat`(至少满足 RocksDB 元数据查询)
|
||||
- [x] `rename`(原子替换语义)
|
||||
|
||||
### 语义修复
|
||||
- [x] `open` 支持 `O_TRUNC/O_APPEND/O_EXCL`
|
||||
- [x] 权限检查(`O_RDONLY/O_WRONLY/O_RDWR`)
|
||||
- [x] 返回值与 `errno` 映射统一(失败路径不可吞错)
|
||||
|
||||
### 交付物
|
||||
- [x] `zvfs/zvfs_hook.c`(phase2 hook 覆盖与语义修复)
|
||||
- [x] `test/test_phase2_posix.c`(phase2 POSIX 回归用例)
|
||||
- [x] `plan/phase2_validation.md`(用户侧验证步骤)
|
||||
|
||||
### 阶段验收
|
||||
- [ ] db_bench 基础 workload 可跑通(单线程先行)。
|
||||
- [ ] 不支持的 syscall 必须明确透传且行为可解释。
|
||||
|
||||
### 正确性验证方案
|
||||
- [ ] 每个新增 hook 至少 1 个正例 + 1 个反例(权限/参数/不存在文件)。
|
||||
- [ ] `open` 语义验证:`O_TRUNC/O_APPEND/O_EXCL` 行为与本地文件系统对齐。
|
||||
- [ ] `errno` 对照验证:对关键失败场景做预期值断言。
|
||||
- [ ] 跑最小 `db_bench`,确认无 “Function not implemented/Bad file descriptor” 类错误。
|
||||
|
||||
---
|
||||
|
||||
## 阶段3:并发模型升级
|
||||
|
||||
### 目标
|
||||
- 保持 hook 层阻塞语义,但底层可并行提交处理。
|
||||
|
||||
### 任务
|
||||
- [ ] 设计并实现 `控制面线程 + N个数据面worker`。
|
||||
- [ ] 每 worker 使用独立 io_channel。
|
||||
- [ ] 引入并发保护:fd_table、dirents、open_count、全局挂载状态。
|
||||
- [ ] 修复生命周期竞态(close/unlink 并发、延迟删除)。
|
||||
|
||||
### 阶段验收
|
||||
- [ ] 多线程压测下无崩溃/无明显数据错乱。
|
||||
- [ ] QD>1 场景吞吐显著高于阶段2。
|
||||
|
||||
### 正确性验证方案
|
||||
- [ ] 多线程读写一致性校验(文件内容 hash 或区块比对)。
|
||||
- [ ] 并发场景稳定性:长时间压测无崩溃、无死锁、无句柄泄漏。
|
||||
- [ ] 竞态回归:`close/unlink`、双开同文件、并发 append 场景正确。
|
||||
|
||||
---
|
||||
|
||||
## 阶段4:元数据持久化重构
|
||||
|
||||
### 目标
|
||||
- 去掉宿主文本元数据文件,转向 blobstore 内部可恢复元数据。
|
||||
|
||||
### 任务
|
||||
- [ ] 使用 super blob(或单独 metadata blob)管理目录与 inode-like 信息。
|
||||
- [ ] 建立日志或 copy-on-write 更新流程,支持崩溃恢复。
|
||||
- [ ] 对 `create/unlink/rename/truncate` 实现原子更新策略。
|
||||
|
||||
### 阶段验收
|
||||
- [ ] 强制中断后重启可恢复一致目录和文件大小信息。
|
||||
- [ ] 不再依赖固定绝对路径元数据文件。
|
||||
|
||||
### 正确性验证方案
|
||||
- [ ] 故障注入:在 create/write/rename/truncate 中间点中断后重启验证一致性。
|
||||
- [ ] 元数据回放验证:目录项数量、文件大小、blob_id 映射正确。
|
||||
- [ ] 对比验证:与中断前快照(或日志)比对差异可解释。
|
||||
|
||||
---
|
||||
|
||||
## 阶段5:性能与稳定性验收
|
||||
|
||||
### 目标
|
||||
- 形成“可用 + 可解释 + 可回归”的最终版本。
|
||||
|
||||
### 任务
|
||||
- [ ] buffer 管理优化(池化、减少拷贝、减少重复 preread)。
|
||||
- [ ] 完整回归:现有单测 + 新增 hook 语义测试 + db_bench 组合。
|
||||
- [ ] 输出性能报告(吞吐、延迟、CPU、错误率)。
|
||||
|
||||
### 阶段验收
|
||||
- [ ] 关键 workload 稳定运行,结果可复现。
|
||||
|
||||
### 正确性验证方案
|
||||
- [ ] 全量回归连续执行至少 3 轮,结果一致且无新增失败。
|
||||
- [ ] 性能结果包含波动范围(平均值与离散度),可复现实验命令。
|
||||
- [ ] 最终发布前执行一次“从空盘到回归完成”的冷启动验证流程。
|
||||
|
||||
---
|
||||
|
||||
## 4. 当前阶段状态
|
||||
|
||||
- 当前阶段:`阶段2`
|
||||
- 阶段状态:`pending_user_validation`
|
||||
- 本轮目标:用户按 `plan/phase2_validation.md` 执行 phase2 正确性验证
|
||||
|
||||
---
|
||||
|
||||
## 5. 每轮执行后必须更新的记录
|
||||
|
||||
## 5.1 变更记录(按时间追加)
|
||||
- [x] 2026-03-02: 完成 phase0(db_bench syscall 基线 + 失败分支 + 兼容矩阵);文件:`plan/plan.md`、`plan/rocksdb_syscall_matrix.md`、`plan/baseline_commands.md`;风险:当前环境 SPDK 初始化失败(IOVA PA 不可用),需在 phase1 前确定运行环境策略
|
||||
- [x] 2026-03-02: 完成 phase1 代码改造(file/io_req 解耦 + 新增 pread/pwrite API);文件:`zvfs/zvfs.h`、`zvfs/zvfs.c`、`plan/plan.md`、`plan/phase1_validation.md`;风险:本轮未在 root+LD_PRELOAD 环境完成端到端验证
|
||||
- [x] 2026-03-02: 根据用户反馈修复 phase1 边界问题(preread 失败时将临时缓冲区清零,避免洞区读到脏数据);文件:`zvfs/zvfs.c`、`plan/phase1_validation.md`;风险:仍需用户侧端到端复测确认
|
||||
- [x] 2026-03-02: 根据用户复测继续修复 sparse hole 问题(offset>EOF 时清零本次覆盖页内 gap 区间);文件:`zvfs/zvfs.c`、`plan/plan.md`;风险:跨多页大洞写入语义仍需在 phase2 做专项覆盖
|
||||
- [x] 2026-03-02: 完成 phase2 代码改造(补齐 openat/pread/pwrite/fsync/ftruncate/stat/rename/fcntl/mkdir/rmdir 等 hook 与关键语义);文件:`zvfs/zvfs_hook.c`、`zvfs/zvfs.h`、`test/test_phase2_posix.c`、`test/Makefile`、`plan/phase2_validation.md`、`plan/plan.md`;风险:尚未在用户环境完成 db_bench 端到端验收
|
||||
- [x] 2026-03-02: 根据用户 db_bench 反馈修复目录打开语义(目录允许 O_RDONLY 打开,不再强制要求 O_DIRECTORY);文件:`zvfs/zvfs_hook.c`;风险:仍需用户复测 db_bench
|
||||
- [x] 2026-03-02: 根据用户 db_bench 反馈补齐 fadvise/fallocate/sync_file_range hook,避免 pseudo-fd 被内核路径误判为 EBADF;文件:`zvfs/zvfs_hook.c`、`zvfs/zvfs.h`;风险:仍需用户复测 db_bench
|
||||
|
||||
## 5.2 风险清单(持续维护)
|
||||
- [ ] 线程模型改造可能引入 SPDK thread affinity 问题
|
||||
- [ ] rename/truncate 语义与 blobstore 能力映射复杂
|
||||
- [ ] LD_PRELOAD 多符号拦截顺序可能受 libc/应用实现影响
|
||||
|
||||
## 5.3 决策记录(ADR-lite)
|
||||
- [ ] D-001: 为什么选择控制面/数据面分离
|
||||
- [ ] D-002: 为什么选择 super blob 元数据格式
|
||||
- [ ] D-003: 不支持 syscall 的透传策略与边界
|
||||
|
||||
---
|
||||
|
||||
## 6. 完成定义(DoD)
|
||||
|
||||
满足以下条件才可标记“计划完成”:
|
||||
- [ ] 阶段0~5全部验收项勾选完成
|
||||
- [ ] RocksDB db_bench 至少 3 类 workload 稳定通过
|
||||
- [ ] 关键 POSIX 语义测试通过并有失败用例说明
|
||||
- [ ] 文档包含部署方式、限制项、回归命令
|
||||
87
plan/rocksdb_syscall_matrix.md
Normal file
87
plan/rocksdb_syscall_matrix.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# Phase 0 - RocksDB syscall 兼容矩阵
|
||||
|
||||
- 日期: 2026-03-02
|
||||
- 采样对象: `/home/lian/env/rocksdb-test/db_bench`
|
||||
- 当前 LD_PRELOAD: `/home/lian/share/10.1-spdk/zvfs/zvfs/libzvfs.so`
|
||||
- 说明: 本矩阵基于 phase0 的 `fillseq` 最小工作负载和失败分支采样;用于定义 phase2 必做 hook 范围。
|
||||
|
||||
## 1) 采样结论(关键)
|
||||
|
||||
### 1.1 成功基线(不启用 LD_PRELOAD,`--db=/tmp/rdb_phase0_plain`)
|
||||
关键 syscall 计数(来自 strace 汇总):
|
||||
|
||||
| syscall | count |
|
||||
|---|---:|
|
||||
| openat | 91 |
|
||||
| fcntl | 82 |
|
||||
| read | 56 |
|
||||
| fstat | 56 |
|
||||
| getdents64 | 36 |
|
||||
| stat | 14 |
|
||||
| mkdir | 12 |
|
||||
| unlink | 12 |
|
||||
| access | 11 |
|
||||
| fsync | 10 |
|
||||
| fdatasync | 10 |
|
||||
| rename | 9 |
|
||||
| pread64 | 8 |
|
||||
| ftruncate | 5 |
|
||||
| fallocate | 5 |
|
||||
| lseek | 1 |
|
||||
|
||||
路径级(仅匹配 DB 路径)高频调用:
|
||||
- `openat`, `mkdir`, `access`, `rename`, `unlink`, `rmdir`
|
||||
|
||||
### 1.2 失败分支(启用 LD_PRELOAD,`--db=/zvfs/rdb_phase0_preload`)
|
||||
观察到的关键失败:
|
||||
- SPDK 初始化失败:`Cannot use IOVA as PA`,随后 `spdk_env_init` 失败。
|
||||
- RocksDB 目录创建路径使用 `openat + mkdir`,对 `/zvfs/rdb_phase0_preload` 返回 `ENOENT`。
|
||||
- 与 DB 路径相关错误码分布:`ENOENT x4`。
|
||||
|
||||
结论:仅 hook `open/read/write/close/unlink/lseek` 无法支撑 RocksDB;至少要覆盖目录/元数据/同步/随机读写相关 syscall。
|
||||
|
||||
## 2) 当前实现 vs RocksDB 需求
|
||||
|
||||
当前已实现 hook(`zvfs_hook.c`):
|
||||
- `open`, `read`, `write`, `close`, `unlink`, `lseek`
|
||||
|
||||
当前缺失但在 phase0 中已观测到(或由 RocksDB 常规路径强依赖):
|
||||
- `openat/openat64`, `mkdir`, `rmdir`
|
||||
- `pread64/pwrite64`(`pwrite` 在 fillseq 未显式出现,但 RocksDB 通常依赖)
|
||||
- `fsync/fdatasync`
|
||||
- `ftruncate/fallocate`
|
||||
- `fcntl`(文件锁)
|
||||
- `stat/fstat/lstat/newfstatat`(含 `statx` 兼容策略)
|
||||
- `rename`(CURRENT / OPTIONS / MANIFEST 原子更新)
|
||||
- `access/faccessat`, `unlinkat`
|
||||
|
||||
## 3) Phase 2 必做清单(来自 phase0 证据)
|
||||
|
||||
> 这是“必须实现或必须可正确透传”的最小集合。
|
||||
|
||||
| API/syscall | 证据来源 | 当前状态 | Phase2 目标 |
|
||||
|---|---|---|---|
|
||||
| open/open64 | 现有 hook + RocksDB 文件创建 | 部分支持 | 补齐 flag 语义(`O_TRUNC/O_APPEND/O_EXCL`) |
|
||||
| openat/openat2 | phase0 strace 高频 | 缺失 | 必须支持 `/zvfs` 路径 |
|
||||
| close | 已实现 | 支持 | 保持并修复错误传播 |
|
||||
| read/write | 已实现 | 支持 | 保持 |
|
||||
| pread64/pwrite64 | phase0 观测到 pread64 | 缺失 | 必须支持 offset I/O |
|
||||
| fsync/fdatasync | phase0 高频 | 缺失 | 必须支持(至少语义正确) |
|
||||
| ftruncate | phase0 观测到 | 缺失 | 必须支持 |
|
||||
| fallocate | phase0 观测到 | 缺失 | 必须支持或明确降级策略 |
|
||||
| fcntl(F_SETLK等) | phase0 高频 | 缺失 | 必须支持最小锁语义 |
|
||||
| stat/fstat/lstat/newfstatat | phase0 高频 | 缺失 | 必须支持最小元数据语义 |
|
||||
| mkdir/rmdir | phase0 路径级高频 | 缺失 | 必须支持 `/zvfs` 目录层 |
|
||||
| rename | phase0 路径级高频 | 缺失 | 必须支持原子替换语义 |
|
||||
| unlink/unlinkat | unlink 已有,unlinkat 缺失 | 部分支持 | 补齐 unlinkat |
|
||||
| access/faccessat | phase0 路径级高频 | 缺失 | 必须支持或一致透传 |
|
||||
|
||||
## 4) 可降级透传(phase2 可先不接管)
|
||||
|
||||
- `getdents64`, `readlink`, `statfs/fstatfs` 可先透传(前提:不作用于 `/zvfs` 或语义可接受)。
|
||||
- 若作用于 `/zvfs` 路径,必须在 phase2 给出明确行为(支持或返回可解释错误)。
|
||||
|
||||
## 5) 风险与备注
|
||||
|
||||
1. 当前机器环境下 SPDK 初始化存在 IOVA/PA 限制,影响“真实 I/O 路径”验证,需要在 phase1 前先明确运行环境策略。
|
||||
2. 仅凭 `fillseq` 不能覆盖全部 syscall;phase2 开始前建议补 `readrandom/overwrite` 采样以确认 `pwrite64` 等调用比例。
|
||||
Reference in New Issue
Block a user