Target
与传输就绪后,对端是谁接管队列,决定了延迟路径落在用户态还是内核。SPDK NVMe
Driver 把远端 NVMe-oF 子系统接进同一套
spdk_nvme_* API;Linux 则提供
nvme-rdma / nvme-tcp 与
nvme-cli。官方 Getting Started
写明:与内核 target/host
做过互通测试。本文钉住三条边界:SPDK host
怎么连、和内核 initiator
如何分工、多路径能做什么却不该神话什么。
本文是「SPDK / 用户态存储栈」系列第 10 篇(共 16 篇)。→ 系列目录
篇目 核心内容 第 9 篇 · 传输层 RDMA / TCP 与 interrupt 第 10 篇 · Initiator host 路径、内核互通、multipath 第 11 篇 · vhost 对 VMM 的块后端边界 上一篇:传输层 · 下一篇:vhost / virtio 边界
版本锚定:SPDK v26.01;NVMe over Fabrics Target(Configuring the Linux NVMe-oF Host)、NVMe Driver(NVMe over Fabrics Host Support)、NVMe-oF Multipath HOWTO;内核侧指
nvme-rdma/nvme-tcp与nvme-cli常规用法。无伪造 IOPS、无臆造 failover 毫秒数。
本篇不展开:完整 ANA 状态机百科、厂商多路径栈、把 NVMe-oF 盘给虚拟机的 vhost 路径(第 11 篇)。
一、两条 host 路径
flowchart TB
subgraph spdk_host ["SPDK host path"]
app["app / bdevperf / spdk_tgt"]
libnvme["lib/nvme fabrics"]
bdevnvme["bdev_nvme"]
app --> libnvme
libnvme --> bdevnvme
end
subgraph k_host ["Linux kernel host path"]
cli["nvme-cli"]
kmod["nvme-rdma / nvme-tcp"]
block["/dev/nvmeNnM"]
cli --> kmod
kmod --> block
end
tgt["SPDK or kernel NVMe-oF target"]
bdevnvme --> tgt
block --> tgt
| 路径 | 入口 | 上层消费者 | 完成模型 |
|---|---|---|---|
| SPDK host | spdk_nvme_probe() /
bdev_nvme_attach_controller |
SPDK 应用、bdev 栈、用户态轮询 | 默认 qpair 轮询;可选 interrupt 相关 API(见驱动文档 / changelog) |
| 内核 host | modprobe nvme-rdma 或 nvme-tcp
+ nvme connect |
文件系统、O_DIRECT、内核块客户端 |
内核 NVMe 驱动与块层 |
官方术语:规范称连接方为 host;文档也承认民间称 initiator。下文按语境混用,对象都是「发起方」。
二、SPDK host:同一套 NVMe API,换 trid
NVMe Driver · NVMe over Fabrics Host Support:
- 远端连接与本地 PCIe 枚举共用
spdk_nvme_probe()思路;trid指向 discovery 服务或某个 NVM subsystem。 - 可用
spdk_nvme_transport_id_parse()解析人类可读 transport 字符串。 - Discovery 地址会回调每个发现的子系统;也可直连已知 subsystem,跳过 discovery。
- API 在附着之后与本地盘相同:分配
qpair、
nvme_ns_cmd_read/write、轮询完成。这是用户态栈把「远端盘」当成「又一个 NVMe 控制器」的关键设计。
在 bdev 层,Block Device User Guide 用同一条 RPC 覆盖两种叶子:
# 语义示例:本机 PCIe
scripts/rpc.py bdev_nvme_attach_controller -b NVMe1 -t PCIe -a 0000:01:00.0
# 语义示例:远端 NVMe-oF(RDMA)
scripts/rpc.py bdev_nvme_attach_controller -b Nvme0 -t RDMA -a 192.168.100.1 \
-f IPv4 -s 4420 -n nqn.2016-06.io.spdk:cnode1(地址与 NQN 换成你的环境;本篇不附真实输出。)
限制:RDMA 内存注册等问题与 target 侧同类,官方指向 target 文档的 RDMA Limitations。Host 进程同样吃大页与核 mask;与 target 共机时要避免抢同一组专用核(第 3、15 篇)。
三、内核 initiator:nvme-cli 与驱动模块
Getting Started · Configuring the Linux NVMe over Fabrics Host:
modprobe nvme-rdma # 或 nvme-tcp
nvme discover -t rdma -a 192.168.100.8 -s 4420
nvme connect -t rdma -n "nqn.2016-06.io.spdk:cnode1" -a 192.168.100.8 -s 4420
nvme disconnect -n "nqn.2016-06.io.spdk:cnode1"把 rdma 换成 tcp
即切换传输。成功后出现内核 NVMe 块设备,其后路径与普通 nvme
盘相同——可接文件系统,或再经 storage/79
的 O_DIRECT+io_uring,而不是
SPDK reactor。
互通矩阵(机制级)
官方断言测试过 SPDK 与 Linux 实现的互通。工程上仍建议按格子验证,而不是假设「任意版本组合永远兼容」:
| Initiator ↓ Target → | SPDK nvmf_tgt |
内核 nvmet |
|---|---|---|
SPDK bdev_nvme / libnvme |
同栈主路径 | 应测;注意特性位与对齐 |
内核 nvme-rdma / nvme-tcp |
官方主线示例 | 内核经典路径 |
版本钉:本系列 target 侧钉 SPDK v26.01;内核 initiator 的特性(TLS、认证、ANA)随内核版本变化,正文不声称某一发行版内核已具备与 v26.01 对等的全部实验特性。升级任一侧后应重跑 discover/connect 与数据路径冒烟。
四、何时选哪条 host 路径
| 目标 | 更合适的 host | 原因 |
|---|---|---|
| 用户态数据面继续用 bdev(再导出 vhost/nvmf、做用户态流水线) | SPDK | 远端盘直接进 bdev 树 |
| 把远端盘交给内核 FS / 传统应用 | 内核 initiator | 块设备节点与 VFS 集成现成 |
| 同机已为 SPDK 独占 CPU/大页,又想少进程 | SPDK | 避免再维护一套内核连接与 udev |
运维只熟 nvme-cli,且应用不肯链 SPDK |
内核 | 工具链与可观测在 sysfs/内核侧 |
混合可以存在(部分盘内核、部分盘 SPDK),但观测与故障域会分裂——排障坐标系见第 14 篇。
4.1 互操作时的「特性位」意识
Target 在 v26.01 宣称支持 NVMe 2.0 所需特性,并允许用
transport 选项掩码关掉部分 optional 命令。Initiator
若假设「凡是 NVMe-oF 都能 fused compare-and-write / 某
Identify CNS」,可能在掩码或旧内核上踩坑。changelog
历史里也曾记载 RDMA 上 fused 操作对 inline data
的对称性要求——同栈最省心,跨实现要按特性单测,不要只靠一次
nvme connect 成功。
站内 storage/03 讲清协议对象;本篇强调实现边界:协议兼容 ≠ 运维兼容。超时默认值、重连节奏、discovery 刷新,都是实现与发行版策略,必须写进联调清单。
五、多路径:能做,但不要神话
5.1 官方能力
NVMe-oF Multipath HOWTO 演示:
- target 上同一 subsystem 配 多个 listener(不同 IP:port);
nvmf_create_subsystem ... -r打开 ANA reporting;- initiator(示例用
bdevperf)对同一 controller 名 二次bdev_nvme_attach_controller,第二次带 multipath 相关参数,把第二条 path 加进去; - 用
nvmf_subsystem_listener_set_ana_state把某 listener 标为non_optimized等,观察流量切到另一 path; bdev_nvme_set_multipath_policy可设active_active(round-robin 类)等策略。
Block Device User Guide 也指向 NVMe bdev 的 multipath 文档。结论:SPDK target 与 initiator 都支持到同一 subsystem 的多独立路径,并可用 ANA 表达优选。
5.2 谨慎清单(无伪造数字)
多路径解决的是 可达性与负载分布,不自动提供:
- 跨故障域的数据冗余——那是 RAID/复制/上层集群的事;两条网线连同一 malloc bdev 只是双通道。
- 零中断切换——ANA 状态变更、路径下线、重连与超时如何咬合,取决于配置与内核/用户态策略;本篇不编造 RTO。
- 与内核 dm-multipath 叙事混用——内核 NVMe
multipath 与 SPDK
bdev_nvmemultipath 是不同控制面;混部时要分清「谁在选 path」。 - 观测——HOWTO 用
bdev_nvme_get_io_paths、主机ss -t等查看路径;生产必须把 path 状态纳入第 13–14 篇的坐标系,而不是只看聚合 IOPS。
争论:有人主张「块存储多路径应完全像 FC SCSI 年代一样对应用透明」;另一侧认为 NVMe ANA + 显式策略更可控。SPDK 暴露 ANA state 与 multipath policy RPC,偏向 显式可控。透明性若要做,应在编排层,而不是假装 RPC 不存在。
5.3 推荐的最小验证剧本(无数字)
在声称「生产可切路径」之前,至少按官方 HOWTO 的逻辑跑通一次(环境允许时):
- 两 listener 都标
optimized,确认双路径可见(
bdev_nvme_get_io_paths或内核侧等价信息); - 将一路标
non_optimized,确认流量迁到另一路(可用ss等观察连接活跃,而不是只看应用是否还活着); - 再测一路 listener 进程级故障或链路 down,记录应用是否只读错误、是否自动恢复——把现象写进台账,不把「应该会」写进对外承诺;
- 若启用
active_active,确认这是负载分布策略,而不是额外的耐久性保证。
没有上述剧本就写进 SLA,属于流程事故,不是传输层事故。
六、安全与实验特性边界
Getting Started 另述:TCP TLS 1.3 PSK(experimental)、DH-HMAC-CHAP 带内认证等。要点:
- TLS secure-channel listener 与
allow-any-host互斥; - 文档写明规范允许的「带内认证 + 安全通道」组合
当前未支持,认证需走非
--secure-channel的 listener; - 密钥经 keyring RPC 注入;initiator 在
bdev_nvme_attach_controller上传--psk/ DHCHAP 参数。
这些改变的是连接建立与信任,不改变「附着成功后 bdev/内核盘如何提交 I/O」的主模型。生产启用前按官方实验标签与内核支持矩阵单独评估。
把安全开关与多路径开关同时打开时,更要把联调矩阵写全:每条 path 是否都走 secure-channel、PSK/密钥轮换是否覆盖所有 listener、认证失败是「连不上」还是「连上后第一条 I/O 失败」。症状不同,值班手册不能共用同一段「检查网线」。
还有一类边界来自「谁拥有超时时钟」。SPDK host
上超时与重试更多落在应用或 bdev_nvme 选项;内核 initiator
上则与 nvme 驱动参数、sysfs
与发行版默认值纠缠。两端同时改超时却不记录,会出现「同一次链路抖动,一侧快速失败、一侧长时间挂起」的分裂症状。联调清单里应单列超时与重连参数来源,并在故障演练中只改一侧、观察另一侧,形成可复核的因果,而不是两侧一起拧。
若 SPDK host 与内核 host 同时连接同一 subsystem,要确认 target 的 max qpairs、host 允许列表与资源配额是否按「双 initiator」规划。两边抢同一套队列与 ANA 状态时,现象可能是间歇性 connect 失败或路径抖动,而不是清晰的权限错误。除非有明确的迁移窗口需求,生产上应避免无文档的双栈并行挂载。
七、开放问题
- 跨实现 multipath 行为差异(SPDK ANA RPC vs 内核 NVMe multipath)在双厂商路径下的一致性,仍依赖集成测试而非单一规范段落。
- Host 侧 interrupt mode 与内核 initiator 共置:用户态可睡、内核路径仍中断驱动时,同机延迟谱如何建模,缺少标准方法。
- 发现服务与多租户过滤(v26.01 custom discovery filter)在 initiator 大规模自动附着时的策略语言,仍属产品空白。
- 内核块设备节点上的上层(FS、LVM、dm)与 SPDK 多路径并发:一旦远端盘进了 VFS,故障切换语义可能被文件系统日志与挂起策略放大——这超出 NVMe-oF 本身,却是真实部署的主要风险源。
从系列问题清单回看:第 4 个关键问题(NVMe-oF 如何落到 bdev、传输差在哪)在 08–10 收束到「对象映射 + 传输模型 + host 选择」;多路径与安全是同一问题上的运维外环,不是性能彩蛋。下一篇离开 fabrics host,改谈把 bdev 喂给虚拟机的 vhost 边界。
八、参考资料
规范 / 官方文档(A)
- SPDK v26.01,NVMe Driver · NVMe over Fabrics Host Support。
- SPDK v26.01,NVMe over Fabrics Target · Linux host 配置、TLS/认证边界、与内核互通声明。
- SPDK v26.01,NVMe-oF Multipath
HOWTO(多
listener、ANA、
active_active)。 - SPDK v26.01,Block Device User Guide · NVMe bdev attach 示例。
源码(A)
spdk/spdktag v26.01:lib/nvme/(fabrics)、module/bdev/nvme/。
站内
实验台账
- 本篇无
nvme discover/bdev_nvme_get_io_paths真实输出;无 failover 耗时数字。
Host 侧一旦选定,观测工具也要跟着换:SPDK 看 RPC 与
reactor,内核看 sysfs 与 nvme
日志,不要混用两套口径硬比。
把「能 ping 通网卡」当成「多路径已验证」是不够的:必须看到 path 列表与流量迁移,才算演练完成。
九、小结
- SPDK host 用同一 NVMe API +
bdev_nvme吃远端盘;内核 host 用nvme-rdma/nvme-tcp得到/dev/nvme*。 - 官方做过互通;版本组合仍要冒烟,实验安全特性单独评估。
- 多路径与 ANA 可用,解决可达与选路,不替代冗余,也不该用未测数字吹「无感切换」。
- 下一步:把 bdev 送给虚拟机——见 第 11 篇 vhost。
附:选型一句话
- 远端盘还要进 SPDK 流水线(再导出、再计算、再用户态处理)→ SPDK host。
- 远端盘只要变成「这台 Linux 上的一块盘」给 FS/应用 → 内核 initiator。
- 需要双活路径 → 先完成第 5.3 节验证剧本,再谈 SLA;路径数 ≠ 副本数。
这三句盖不住所有拓扑,但能挡住「先上多路径和 TLS,再决定 initiator 在哪」的常见倒置。机制上,initiator 选择决定了完成通知落在 reactor 还是内核块层——与第 1 篇坐标系里的「另一格」直接对应。
→ 系列目录 · 上一篇:传输层 · 下一篇:vhost / virtio 边界
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【SPDK 用户态存储】排障坐标系:绑盘、空转、队列满、传输超时与多路径
把 SPDK 常见症状映射到绑盘/环境、reactor CPU、bdev 队列、传输超时、多路径五轴;给出可核对清单与观测入口,不虚构延迟数字与 RPC dump。
【SPDK 用户态存储】用户态存储全景:从 Reactor 到 NVMe-oF 的坐标系
定位 SPDK 相对内核块层、O_DIRECT+io_uring 与 NVMe 协议百科的生态位;钉住 I/O 路径、线程模型、设备独占、块抽象、远端传输五条坐标系,并给出 16 篇阅读路线。
【SPDK 用户态存储】NVMe-oF Target:subsystem、listener 与 namespace→bdev
拆解 SPDK NVMe-oF target:lib/nvmf 中 tgt/subsystem/listener/namespace 与 bdev 的映射;说明 poll group 如何驱动 I/O,以及 app/nvmf_tgt 相对库的角色。
【SPDK 用户态存储】NVMe-oF 传输层:RDMA vs TCP,轮询与 v26.01 中断模式
对比 SPDK NVMe-oF 的 RDMA 与 TCP 传输约束;说明默认轮询模型,并依据 v26.01 changelog 与官方 Interrupt Mode 文档钉住低负载 RDMA/TCP 中断驱动模式的适用边界。