一、问题概述
1.1 故障现象
在双路服务器上,同PCIe交换芯片下的两块NVMe硬盘分别绑定近端和远端NUMA节点进行并发随机读测试时,绑定远端NUMA节点的硬盘性能出现严重下降。其中Solidigm盘性能下降尤为显著,降幅达 79.8%(885K → 179K IOPS)。
1.2 测试环境
项目
配置
机型
操作系统
RedHat 9.2
CPU
Hygon C86-4G(2 Sockets × 64 Cores × 2 Threads = 256 vCPUs)
NUMA节点
8个(每节点32 vCPUs)
测试盘1
SOLIDIGM(PCIe 4.0)
测试盘2
Samsung(PCIe 4.0)
中断均衡
irqbalance服务已开启
1.3 测试方法
numactl --cpunodebind=<node> --membind=<node> \
fio --ioengine=libaio --direct=1 --iodepth=32 --time_based \
--bs=4k --runtime=60s --rw=randread --filename=/dev/nvmeXn1 \
--numjobs=16 --thread --group_reporting \
--norandommap --randrepeat=0
二、性能测试结果
2.1 跨NUMA vs 本地NUMA性能对比
硬盘型号
本地NUMA IOPS(K)
跨NUMA IOPS(K)
性能下降
Samsung MZQL23T8HCLS-00B7C
940
702
25.3%
SOLIDIGM SSDPF2KX038T1
885
179
79.8% ⚠️
2.2 关键观察
-
三星盘跨NUMA性能下降约 25%(符合预期,跨NUMA内存访问本身有延迟开销)
-
Solidigm盘跨NUMA性能下降约 80%(异常,远超正常跨NUMA开销)
-
结论:Solidigm盘存在额外的跨NUMA性能瓶颈
三、内核函数级性能分析(ftrace)
3.1 测试方法
使用内核ftrace追踪NVMe驱动关键函数耗时:
echo function_graph > /sys/kernel/debug/tracing/current_tracer
echo 'nvme_queue_rq' > /sys/kernel/debug/tracing/set_ftrace_filter
echo 'nvme_setup_cmd' >> /sys/kernel/debug/tracing/set_ftrace_filter
echo 'nvme_pci_complete_batch' >> /sys/kernel/debug/tracing/set_ftrace_filter
echo 'nvme_pci_complete_rq' >> /sys/kernel/debug/tracing/set_ftrace_filter
echo 'nvme_irq' >> /sys/kernel/debug/tracing/set_ftrace_filter
3.2 提交路径耗时对比
函数
本地NUMA (μs)
跨NUMA (μs)
异常分析
nvme_setup_cmd
0.19 - 2.09 (~0.95)
0.24 - 1.57 (~0.8)
✅ 正常,无明显差异
nvme_queue_rq
4.23 - 36.49 (~8.5)
20.44 - 486.79 (~110)
⚠️ 异常:存在185μs/454μs/486μs长尾
3.3 完成路径耗时对比
函数
本地NUMA (μs)
跨NUMA (μs)
分析
nvme_pci_complete_batch
1.21 - 4.90 (~2.3)
1.10 - 4.36 (~2.2)
✅ 正常
nvme_irq
2.71 - 9.16 (~6.1)
4.62 - 12.03 (~6.9)
✅ 正常
3.4 ftrace分析结论
观察点
结论
PCIe完成路径(nvme_irq/nvme_pci_complete_batch)
无明显差异,中断处理不是瓶颈
NVMe命令提交路径(nvme_queue_rq)
存在严重异常长尾,问题出在提交阶段
命令设置阶段(nvme_setup_cmd)
无差异,问题不在命令构造
锁定方向:问题出在 nvme_queue_rq() 内部,介于命令构造完成与门铃写入之间。
四、硬件级深入排查
4.1 门铃寄存器写入测试
硬盘
跨NUMA门铃写入延迟
相对速度
Solidigm
18.09 ns/write
更快
Samsung
57.69 ns/write
较慢
关键发现:Solidigm门铃写入更快,理论上IOPS应更高,但实际性能更差。排除门铃延迟为根因。
4.2 关键硬件参数对比(nvme id-ctrl)
参数
Samsung
Solidigm
差异分析
mdts
9
5
Samsung是Solidigm的 16倍
单次最大DMA传输
2 MB
128 KB
Samsung一次搬运16倍数据
sgls
0
0
均不支持SGL(使用PRP)
maxcmd
256
0
控制器命令限制
4.3 mdts对DMA能力的影响
mdts = Maximum Data Transfer Size
单次最大DMA = 2^(mdts) × 4096 字节
Samsung (mdts=9): 2^9 × 4096 = 512 × 4KB = 2,048 KB = 2 MB
Solidigm (mdts=5): 2^5 × 4096 = 32 × 4KB = 128 KB
在4K随机读场景下:
-
Samsung:单次DMA可聚合 512个 4K请求
-
Solidigm:单次DMA仅能聚合 32个 4K请求
-
Solidigm需要16倍的DMA事务数完成同样IOPS ⚠️
五、根因分析
5.1 PRP(Physical Region Page)机制
NVMe协议使用PRP列表描述数据在内存中的物理地址。每次DMA传输需要:
-
从
dma_pool分配PRP列表内存 -
填写PRP Entry(每个Entry指向一个4KB物理页)
-
传输完成后释放PRP列表
关键瓶颈:dma_pool_alloc() 和 dma_pool_free() 需要获取全局自旋锁。
5.2 问题传导链
Solidigm mdts=5 (128KB)
↓
4K随机读需要32个PRP Entry/IO
↓
DMA事务数是Samsung的16倍
↓
dma_pool_alloc/free调用频率是Samsung的16倍
↓
dma_pool全局锁竞争加剧
↓
跨NUMA访问时锁竞争 + 远端内存访问延迟叠加
↓
导致nvme_queue_rq()出现数百微秒长尾
↓
IOPS从885K暴跌至179K (79.8%性能损失)
5.3 内核社区已知问题
该问题已被内核社区识别并正在解决:
Patch: [PATCH v5 0/3] nvme/pci: PRP list DMA pool partitioning
作者: Caleb Sander Mateos
核心方案: 将全局PRP DMA池拆分为per-NUMA-node独立池
text
优化前:所有CPU共享一个dma_pool(一把全局锁)
优化后:每个NUMA节点拥有独立的dma_pool(多把锁并行)
效果:跨NUMA场景下锁竞争减少约50%
六、结论与建议
6.1 根本原因
Solidigm盘 mdts=5 限制了单次DMA传输大小为128KB,导致在4K随机读测试中需要16倍于Samsung(mdts=9)的DMA事务数。大量PRP列表的频繁分配/释放操作,与dma_pool全局锁竞争相互叠加,在跨NUMA访问场景下形成严重性能瓶颈。
6.2 推荐解决方案
方案
措施
适用场景
短期规避
使用 numactl --cpunodebind=<盘所在node> --membind=<盘所在node> 强制绑定本地NUMA
生产环境立即生效
中期优化
升级内核至包含 nvme/pci: make PRP list DMA pools per-NUMA-node patch的版本
彻底解决锁竞争问题
长期选型
将 mdts >= 7(≥512KB)纳入NVMe盘选型评估指标
降低跨NUMA场景性能风险
监控预警
监控 perf lock contention 中 dma_pool_alloc 的锁竞争时间
提前发现类似问题
6.3 预期效果
-
实施短期规避后,Solidigm盘跨NUMA性能预计可恢复至 本地NUMA的70-80%(与三星类似水平)
-
实施per-NUMA-node PRP池优化后,跨NUMA锁竞争时间预计 减少50%以上
附录:关键源码路径
模块
文件路径
相关函数
NVMe驱动主逻辑
drivers/nvme/host/pci.c
nvme_queue_rq(), nvme_setup_cmd()
PRP内存分配
drivers/nvme/host/pci.c
nvme_alloc_prp_pool()
DMA池管理
mm/dmapool.c
dma_pool_alloc(), dma_pool_free()
跟踪点定义
include/trace/events/nvme.h
trace_nvme_*
报告日期: 2026-06-21
分析人: 内核性能分析团队