摘要:NVMe SR-IOV是数据中心多租户存储的核心虚拟化技术。本文解析NVMe 2.0 Section 8.26的Primary/Secondary Controller架构、PCIe VF创建与Namespace容量隔离、virt-mgmt I/O Queue资源分配字段,结合Linux内核sriov_numvfs与vfio-pci直通源码,以及Samsung PM1735 fio实测数据,对比virtio/SPDK/Mdev/SR-IOV/SIOV五大方案性能隔离差异。


📑 目录


一、为什么需要NVMe I/O虚拟化:数据中心多租户的根本矛盾

1.1 存储虚拟化的历史困境

在数据中心和云计算环境中,一台物理服务器上通常需要运行数十甚至数千个虚拟机(VM)。每个VM都需要独立的存储资源,但物理NVMe SSD是一块"独占式"设备——传统方案中,一块NVMe SSD只能直通(passthrough)给一个VM使用。

这带来了两个核心矛盾:

┌──────────────────────────────────────────────────────────────┐
│         数据中心NVMe存储虚拟化的核心矛盾                        │
│                                                              │
│  矛盾1:资源利用率 vs 隔离需求                                 │
│  ┌──────────────┐    ┌──────────────┐                        │
│  │   物理NVMe    │    │   VM #1      │                        │
│  │   7.68TB     │───→│   用 200GB   │   ← 7.48TB 闲置!       │
│  │              │    └──────────────┘                        │
│  │              │    ┌──────────────┐                        │
│  │              │───→│   VM #2      │   ← 无盘可用!           │
│  │              │    │   需要 1TB   │                        │
│  └──────────────┘    └──────────────┘                        │
│                                                              │
│  矛盾2:性能需求 vs 虚拟化开销                                 │
│  NVMe SSD:  随机读 1M+ IOPS, 延迟 < 10μs                     │
│  传统virtio:  仅 50% 原生性能, 延迟增加 100%+                  │
│  → 高性能NVMe被虚拟化层严重"拖后腿"                            │
└──────────────────────────────────────────────────────────────┘

传统解决方案的局限性

方案资源利用率性能保持隔离强度核心缺陷
整盘直通(VFIO)❌ 极低✅ 近原生✅ 硬件级一块盘只能给一个VM
Virtio_blk✅ 高❌ 50%性能⚠️ 软件级严重性能损耗
SPDK vhost-NVMe✅ 高⚠️ 90%+⚠️ 软件级消耗大量CPU核心
共享文件系统(NFS/Ceph)✅ 高❌ 网络开销✅ 逻辑级无法利用NVMe低延迟

1.2 NVMe时代的新挑战:高性能与隔离的矛盾

NVMe SSD的性能指标已经远远超越了传统虚拟化层能够有效承载的范围:

NVMe Gen4 SSD 典型性能指标(如Samsung PM1733 3.2TB):
┌──────────────────────────────────────────────────────┐
│  随机读IOPS:        1,000,000+                       │
│  随机写IOPS:        200,000+                         │
│  顺序读带宽:        6,500 MB/s (PCIe 4.0 x4)        │
│  顺序写带宽:        4,000 MB/s                       │
│  读延迟 (P50):      80 μs                            │
│  写延迟 (P50):      12 μs                            │
│  队列深度:          最多 65535 对 I/O Queue           │
└──────────────────────────────────────────────────────┘

传统virtio_blk虚拟化后:
┌──────────────────────────────────────────────────────┐
│  随机读IOPS:        ~500,000 (50%)                   │
│  延迟 (P50):        ~210 μs (+162%)                  │
│  CPU开销:           2-4 核@100% (hypervisor侧)       │
└──────────────────────────────────────────────────────┘

关键洞察:NVMe SSD拥有多达65535对I/O Submission/Completion Queue。这意味着从硬件角度看,SSD本身就具备同时服务多个独立I/O通道的能力。问题在于如何让不同的VM各自拥有独立的队列,同时保证数据隔离

1.3 SR-IOV的引入:硬件级I/O共享

SR-IOV(Single Root I/O Virtualization) 是PCI-SIG定义的一种PCIe扩展能力,允许一个物理PCIe设备(Physical Function, PF)在硬件层面虚拟化为多个独立的虚拟设备(Virtual Functions, VFs)。每个VF拥有:

  • 独立的PCI配置空间
  • 独立的BAR(Base Address Register)映射
  • 独立的MSI-X中断向量
  • 独立的DMA能力

当SR-IOV与NVMe协议结合时,产生了强大的NVMe SR-IOV方案:

┌──────────────────────────────────────────────────────────────┐
│              NVMe SR-IOV 架构全景                              │
│                                                              │
│  ┌─────────────────────────────────────────────────────┐     │
│  │          物理 NVMe SSD (如 Samsung PM1735)           │     │
│  │                                                     │     │
│  │  ┌──────────┐  ┌────────┐ ┌────────┐ ┌────────┐   │     │
│  │  │    PF    │  │  VF #1 │ │  VF #2 │ │  VF #3 │   │     │
│  │  │(完整功能)│  │(独立   │ │(独立   │ │(独立   │   │     │
│  │  │          │  │ NVMe   │ │ NVMe   │ │ NVMe   │   │     │
│  │  │          │  │Ctrl #1)│ │Ctrl #2)│ │Ctrl #3)│   │     │
│  │  └──────────┘  └────────┘ └────────┘ └────────┘   │     │
│  │       │              │           │          │      │     │
│  │       ▼              ▼           ▼          ▼      │     │
│  │  ┌────────┐  ┌────────┐  ┌────────┐  ┌────────┐   │     │
│  │  │ NS #1  │  │ NS #2  │  │ NS #3  │  │ NS #4  │   │     │
│  │  │ 1.6TB  │  │ 1.6TB  │  │ 1.6TB  │  │ 1.6TB  │   │     │
│  │  └────────┘  └────────┘  └────────┘  └────────┘   │     │
│  │                                                     │     │
│  │  总容量: 6.4TB = 4 × 1.6TB (物理资源均分)          │     │
│  └─────────────────────────────────────────────────────┘     │
│         │              │           │          │               │
│         ▼              ▼           ▼          ▼               │
│  ┌──────────┐  ┌──────────┐ ┌──────────┐ ┌──────────┐      │
│  │  Host    │  │  VM #1   │ │  VM #2   │ │  VM #3   │      │
│  │ 管理面   │  │ /dev/    │ │ /dev/    │ │ /dev/    │      │
│  │ (PF drv) │  │ nvme0n1  │ │ nvme0n1  │ │ nvme0n1  │      │
│  └──────────┘  └──────────┘ └──────────┘ └──────────┘      │
└──────────────────────────────────────────────────────────────┘

二、PCIe SR-IOV基础架构:PF/VF/IOV机制全解析

2.1 SR-IOV规范核心概念

SR-IOV规范由PCI-SIG定义,核心文档为 PCI SR-IOV Specification Revision 1.1(2009)。其核心设计思想是:

一个物理PCIe Function可以衍生出多个"轻量级"PCIe Function,每个衍生Function都能被系统视为独立的PCI设备。

关键术语定义:

/*
 * PCIe SR-IOV 核心术语
 * 
 * PF (Physical Function):
 *   - 完整的 PCIe Function,拥有完整的配置空间和能力
 *   - 负责 SR-IOV 功能的配置和管理
 *   - 运行完整的设备驱动(如 nvme 驱动)
 *   - 可以独立完成所有 I/O 操作
 *
 * VF (Virtual Function):
 *   - 轻量级 PCIe Function
 *   - 仅有最小化的配置空间(省略了大部分 Capability)
 *   - 拥有独立的 PCI Configuration Space、BAR Space、MSI-X Table
 *   - 不能独立管理 SR-IOV 功能
 *   - 通过 PF 进行初始化和配置
 *   - 运行 VF 专用驱动(在 VM 内部可能是同一个 nvme 驱动)
 *
 * VF Enable/Disable:
 *   - 通过 PF 的 SR-IOV Extended Capability 中的 NumVFs 寄存器控制
 *   - VF 的创建/销毁是动态的(hot-plug 语义)
 *
 * TotalVFs:
 *   - 硬件支持的最大 VF 数量
 *   - 由 SR-IOV Capability 的 TotalVFs 字段定义
 *   - 对于 NVMe 设备,常见值: 32/64/128
 */

2.2 PCIe Extended Capability结构

SR-IOV Capability 是 PCIe Extended Capability 的一种,位于PCI配置空间的扩展区域(offset ≥ 256)。其结构如下:

/*
 * SR-IOV Extended Capability Structure
 * PCIe Spec: Extended Capability Header + SR-IOV Capability
 * 
 * 位于 PCI Config Space offset >= 256
 */

/* SR-IOV Extended Capability Header */
struct pcie_ext_cap_header {
    u16 cap_id;        /* 0x0010 = SR-IOV Capability ID */
    u16 version:4;     /* Capability 版本 */
    u16 next:12;       /* 下一个 Extended Capability 的偏移 */
};

/* SR-IOV Capability Structure (PCIe Spec Section 9.3) */
struct sriov_cap {
    /* --- SR-IOV 控制与状态 --- */
    u16 sriov_ctrl;           /* SR-IOV Control Register
                               * Bit 0: VFE  - VF Enable
                               * Bit 1: VFM  - VF Migration Enable
                               * Bit 2: MFM  - VF Migration FM Enable
                               */
    u16 sriov_status;         /* SR-IOV Status Register
                               * Bit 0: CMS  - Captured VF Migration
                               */

    /* --- PF 信息 --- */
    u16 initial_vfs;          /* InitialVFs: PF 默认可提供的 VF 数量 */
    
    /* --- VF 信息 --- */
    u16 total_vfs;            /* TotalVFs: 硬件支持的最大 VF 数量 */
    u16 num_vfs;              /* NumVFs: 当前已启用的 VF 数量(写此字段触发VF创建)*/
    
    /* --- Function Dependency Link --- */
    u8  function_dependency;  /* Function Dependency Link */
    u8  reserved0;
    
    /* --- VF 迁移支持 --- */
    u16 vf_migration_caps;    /* VF Migration Caps Array Size */
    
    /* --- VF Offset & Stride --- */
    u16 vf_offset;            /* FirstVF Offset: 第一个VF相对于PF的BDF偏移 */
    u16 vf_stride;            /* Subsequent VF Stride: 相邻VF之间的BDF步长 */

    /* --- VF Device ID --- */
    u16 vf_device_id;         /* VF Device ID (独立于PF的Device ID) */
    
    /* --- VF 内存区域 --- */
    u32 vf_bar[6];            /* VF BAR0-BAR5 (低位表示内存区域大小)
                               * 对于 NVMe VF: BAR0 通常是 MMIO 寄存器空间
                               */
    
    /* --- VF 迁移与部署 --- */
    u16 vf_migration_caps2;
    u16 reserved1;
};

/*
 * VF BDF 计算公式:
 *   VF_n 的 BDF = PF 的 BDF + vf_offset + n * vf_stride
 *   (其中 n = 0, 1, 2, ..., num_vfs-1)
 *
 * 示例: PF BDF = 0000:03:00.0
 *       vf_offset = 0x0080 (表示 0000:04:00.0)
 *       vf_stride = 0x0001
 *       VF0 BDF = 0000:04:00.0
 *       VF1 BDF = 0000:04:00.1  (如果 stride=1, 则增加 Function Number)
 *       ...
 *       实际计算中 Bus/Device/Function 的编码更复杂,
 *       参见 PCIe Spec Section 9.3.4
 */

2.3 VF的PCI配置空间与路由

VF的PCI配置空间是PF配置空间的一个精简子集

配置空间区域PFVF说明
Header (Type 0)✅ 完整✅ 完整包括Vendor/Device ID, Class Code等
Standard Capabilities✅ 全部⚠️ 部分VF只保留Power Mgmt, MSI-X, PCIe等必要能力
Extended Capabilities✅ 全部⚠️ 部分VF通常不含SR-IOV Capability本身
BAR Space✅ 完整✅ 独立每个VF拥有独立的BAR映射
VF Device ID-独立的Device ID,可被VM驱动匹配
VF 在 PCIe 拓扑中的路由:

  Root Complex
      │
      ├── Root Port 0
      │     └── ...
      │
      ├── Root Port 3
      │     └── Switch (可选)
      │           ├── PF: 0000:03:00.0  ← 宿主机 nvme 驱动管理
      │           ├── VF: 0000:04:00.0  ← 直通给 VM #1
      │           ├── VF: 0000:04:00.1  ← 直通给 VM #2
      │           ├── VF: 0000:04:01.0  ← 直通给 VM #3
      │           └── VF: 0000:04:01.1  ← 直通给 VM #4
      │
      └── Root Port 4
            └── ...

  每个VF对VM来说是一个"独立的PCIe设备"
  VM的NVMe驱动看到的是完整的NVMe Controller

2.4 IOMMU在SR-IOV中的关键角色

IOMMU(Input/Output Memory Management Unit) 是SR-IOV安全隔离的基石:

IOMMU (Intel VT-d / AMD-Vi) 在 NVMe SR-IOV 中的角色:

┌─────────────────────────────────────────────────────────┐
│                                                         │
│   VM #1 (GPA: 0x0000-0x3FFF)                           │
│   ┌───────────────┐                                     │
│   │ VF #1         │──────────────────┐                  │
│   │ DMA Request   │                  │                  │
│   │ GPA=0x1000    │                  ▼                  │
│   └───────────────┘          ┌──────────────┐           │
│                               │   IOMMU      │           │
│   VM #2 (GPA: 0x0000-0x3FFF) │              │           │
│   ┌───────────────┐          │ DMAR Table:  │           │
│   │ VF #2         │─────────→│ VF#1→HPA_A   │           │
│   │ DMA Request   │          │ VF#2→HPA_B   │           │
│   │ GPA=0x2000    │          │ (硬件强制隔离)│           │
│   └───────────────┘          └──────┬───────┘           │
│                                      │                   │
│   物理内存 (HPA):                    ▼                   │
│   ┌────────────┬────────────┐                           │
│   │ HPA_A      │ HPA_B      │                           │
│   │ (VM#1的    │ (VM#2的    │                           │
│   │  DMA Buffer)│  DMA Buffer)│                           │
│   └────────────┴────────────┘                           │
│                                                         │
│   关键保证:                                              │
│   1. VF#1 的 DMA 只能访问 HPA_A 范围                    │
│   2. VF#2 的 DMA 只能访问 HPA_B 范围                    │
│   3. 即使VF#1尝试恶意DMA到HPA_B, IOMMU也会拦截          │
│   4. MSI-X 中断重映射: VF#1的中断只路由到VM#1            │
└─────────────────────────────────────────────────────────┘

安全约束:如果IOMMU未启用或配置不当,SR-IOV的DMA隔离将完全失效。这意味着恶意VM可以通过VF的DMA操作访问其他VM甚至宿主的内存——这是严重的安全漏洞。


三、NVMe Virtualization Enhancements:协议层虚拟化增强

3.1 规范演进:NVMe 1.3→1.4→2.0

NVMe规范对虚拟化的支持经历了渐进式增强:

规范版本发布年份虚拟化章节关键特性
NVMe 1.0-1.22012-2015不支持虚拟化增强
NVMe 1.32017Chapter 8.5Virtualization Enhancements 首次引入
NVMe 1.42019Chapter 8.5增强Secondary Controller管理
NVMe 2.02021Chapter 8.26重构为独立Section,与NVM Set List深度集成

规范原文引用(NVMe Base Specification 2.0, Section 8.26):
“The NVMe Virtualization Management feature enables a controller to manage resources (e.g., I/O Queue resources) across multiple controllers in an NVM subsystem. This feature, combined with SR-IOV, enables a single NVMe device to present multiple independent NVMe controllers to different hosts or VMs.”

3.2 Controller架构:Primary vs Secondary Controller

NVMe虚拟化引入了多Controller概念:

/*
 * NVMe Controller 类型定义 (NVMe Spec 2.0, Section 8.26.1)
 *
 * NVM Subsystem 中可以包含多种类型的 Controller:
 */

enum nvme_controller_type {
    NVME_CTRL_ADMIN     = 0x0,  /* Admin Controller: 
                                  * 仅Admin Queue, 不处理I/O
                                  * 用于带外管理场景 */
    NVME_CTRL_IO        = 0x1,  /* I/O Controller:
                                  * 完整的 Admin + I/O Queue
                                  * 传统NVMe Controller */
    NVME_CTRL_DISCOVERY = 0x2,  /* Discovery Controller:
                                  * 仅用于NVMe-oF发现服务 */
};

/*
 * Primary Controller vs Secondary Controller:
 *
 * Primary Controller (通常 Controller ID 较大,如 0x41):
 *   - 功能完整的 NVMe Controller
 *   - 通常对应 PF (Physical Function)
 *   - 拥有完整的 Admin Command 权限
 *   - 可以创建/删除 Namespace
 *   - 可以执行 Virtualization Management 命令
 *   - 管理所有 Secondary Controller 的资源分配
 *
 * Secondary Controller (通常 Controller ID 较小,如 0x01, 0x02, ...):
 *   - 受限制的 NVMe Controller
 *   - 通常对应 VF (Virtual Function)
 *   - Admin Command 子集受限
 *   - 不能执行 Namespace Management
 *   - 只能访问被 Attach 的 Namespace
 *   - I/O Queue 资源由 Primary Controller 分配
 *
 * 查询 Secondary Controller 列表:
 *   nvme list-secondary /dev/nvme0
 *   
 * Identify (CNS=13h) - List of Controllers in NVM Subsystem
 */

/*
 * Identify Controller Data Structure 中的虚拟化相关字段
 * (NVMe Spec 2.0, Figure 278)
 */
struct nvme_id_ctrl_virt {
    /* ... 省略前面字段 ... */
    
    /* Virtualization Management 支持 */
    __u8    vqfrt;      /* VQ Resources Flexibility Total
                         * 控制器可灵活分配的虚拟I/O Queue总数(高位)*/
    __u8    vqfrsm;     /* VQ Resources Flexibility Secondary Maximum
                         * 单个Secondary Controller可分配的最大VQ数 */
    __u16   vqrfap;     /* VQ Resources Flexibility Allocated to Primary
                         * 已分配给Primary Controller的VQ数 */
    
    __u8    vifrt;      /* VI Resources Flexibility Total
                         * 虚拟中断资源总数 */
    __u8    vifrsm;     /* VI Resources Flexibility Secondary Maximum */
    __u16   vifrap;     /* VI Resources Flexibility Allocated to Primary */
    
    /* NVM Set 相关 */
    __u16   nnset;      /* Number of NVM Sets */
    __u16   dom_id;     /* Domain Identifier */
    __u16   endgid;     /* Endurance Group Identifier */
    
    /* Secondary Controller 相关 */
    __u16   primary_ctrl_id;  /* Primary Controller ID
                               * 对于 Secondary Controller:
                               * 指向其对应的 Primary Controller */
    /* ... */
};

3.3 Namespace资源管理:NS Management与NS Attachment

Namespace是NVMe SR-IOV中实现容量隔离的核心机制:

/*
 * Namespace Management Command (Opcode: 0x0D)
 * NVMe Spec 2.0, Section 5.3
 * 
 * 用于创建和删除 Namespace
 * 只能在 Primary Controller (PF) 上执行
 */

/* CDW10 - Namespace Management Command Dword 10 */
struct nvme_ns_mgmt_cmd {
    __u32 cdw10;
    /* Bits 07:00 - SEL (Select Operation):
     *   00b = Create   (创建新 Namespace)
     *   01b = Delete   (删除指定 Namespace)
     *   10b-FFb = Reserved
     *
     * Bits 31:08 - Reserved
     */
    
    /* Namespace Management 数据结构 (创建时使用): */
};

struct nvme_ns_mgmt_create {
    __le64 nsze;    /* Namespace Size: 
                     * 以LBA为单位的命名空间总大小
                     * 例: 1TB / 512B = 2,147,483,648 LBAs */
    __le64 ncap;    /* Namespace Capacity:
                     * 可寻址的最大LBA数 (≤ NSZE)
                     * 通常 NCAP = NSZE */
    __u8   flbas;   /* Formatted LBA Size:
                     * Bits 3:0 - LBA Format Index
                     *   0 = 512B
                     *   1 = 4096B (4Kn)
                     * Bit 4    - Metadata at End of Data
                     */
    __u8   dps;     /* Data Protection Settings:
                     * Bits 2:0 - Protection Type (0=none, 1=Type1, 2=Type2, 3=Type3)
                     * Bit 3    - Protection in First/Last 8 Bytes
                     */
    /* ... */
};

/*
 * Namespace Attachment Command (Opcode: 0x15)
 * NVMe Spec 2.0, Section 5.23
 *
 * 将 Namespace 关联到 Controller 或从 Controller 解除关联
 */

struct nvme_ns_attach_cmd {
    __le32 nsid;    /* Namespace ID to attach */
    __u32  cdw10;
    /* Bits 07:00 - SEL (Select Operation):
     *   00b = Attach   (将 NS 绑定到 Controller List)
     *   01b = Detach   (将 NS 从 Controller List 解绑)
     */
};

/* Namespace Attachment 数据结构 */
struct nvme_ns_attach_ctrl_list {
    __le16 ctrl_ids[2047];  /* Controller ID List
                             * 最多 2047 个 Controller ID
                             * 可以 Attach 到多个 Controller (共享NS)
                             * 或 Attach 到单个 Controller (独占NS)
                             *
                             * 对于 SR-IOV:
                             *   - 通常每个 VF 对应一个 Secondary Controller
                             *   - 每个 NS 独占 Attach 到一个 VF 对应的 Controller
                             *   - 也可多个 VF 共享一个 NS (需要 Reservation 配合)
                             */
};

/*
 * 关键约束 (NVMe Spec 2.0, Section 8.26.3):
 *
 * 1. 一个 NS 必须至少 Attach 到一个 Controller
 * 2. 未 Attach 的 NS 对 Host 不可见
 * 3. 同一 NS 可以被多个 Controller Attach (共享模式)
 * 4. NS 的总容量不能超过 NVM Subsystem 的物理容量
 *    即: Σ(所有NS的Size) ≤ 物理NAND总容量
 * 5. NS 创建后必须显式 Attach 才能使用
 */

3.4 Virtualization Management命令(Opcode 1Ch)

这是NVMe SR-IOV中最关键的管理命令,用于在Primary Controller和Secondary Controller之间分配和释放I/O资源:

/*
 * Virtualization Management Command (Opcode: 0x1C)
 * NVMe Spec 2.0, Section 5.26
 *
 * 核心功能:
 * 1. 分配/回收 I/O Queue 资源给 Secondary Controller
 * 2. 分配/回收 中断资源给 Secondary Controller
 * 3. 修改 Secondary Controller 的在线状态
 *
 * nvme-cli 命令:
 *   nvme virt-mgmt /dev/nvme0 -c <ctrl_id> -r <resource_type> -n <action> -a <count>
 */

/* Virtualization Management Command Dword 10 */
struct nvme_virt_mgmt_cmd {
    __u32 cdw10;
    /* Bits 15:00 - CNTLID (Controller ID):
     *   指定要管理的 Secondary Controller 的 ID
     *   范围: 0x0001 - 0xFFFE
     *
     * Bits 23:16 - ACT (Action):
     *   0x1 = Primary Admin Controller 资源分配
     *         将资源从 "可用池" 分配给指定的 Secondary Controller
     *   0x7 = Primary Admin Controller 资源回收
     *         将资源从 Secondary Controller 回收到 "可用池"
     *   0x8 = Secondary Controller Online
     *         将 Secondary Controller 置为 Online 状态
     *   0x9 = Secondary Controller Offline  
     *         将 Secondary Controller 置为 Offline 状态
     *
     * Bits 31:24 - Reserved
     */
    
    __u32 cdw11;
    /* Bits 07:00 - Resource Type:
     *   0x00 = I/O Queue (VQ - Virtual Queue)
     *   0x01 = Interrupt (VI - Virtual Interrupt)
     *
     * Bits 31:16 - Count:
     *   当 ACT=0x1 (分配) 时: 分配的资源数量
     *   当 ACT=0x7 (回收) 时: 回收的资源数量
     */
};

/*
 * 典型的 virt-mgmt 操作序列:
 *
 * # Step 1: 为 Secondary Controller #1 分配 8 个 I/O Queue
 * nvme virt-mgmt /dev/nvme0 -c 0x0001 -r 0 -n 1 -a 8
 * #                 ^controller  ^resource=VQ  ^action=alloc  ^count=8
 *
 * # Step 2: 为 Secondary Controller #1 分配 8 个中断
 * nvme virt-mgmt /dev/nvme0 -c 0x0001 -r 1 -n 1 -a 8
 * #                 ^controller  ^resource=VI  ^action=alloc  ^count=8
 *
 * # Step 3: 将 Secondary Controller #1 置为 Online
 * nvme virt-mgmt /dev/nvme0 -c 0x0001 -r 0 -n 8
 * #                 ^controller  ^resource=N/A  ^action=online
 *
 * # 之后 VF 对应的 NVMe Controller 就可以正常工作了
 */
资源分配的状态机:

┌─────────────────────────────────────────────────────────┐
│         NVMe SR-IOV 资源生命周期状态机                    │
│                                                         │
│   ┌─────────┐    Create VF    ┌─────────┐              │
│   │  PF     │───────────────→│  VF     │              │
│   │ Active  │                │ Created │              │
│   └─────────┘                └────┬────┘              │
│        │                          │                    │
│        │                          │ virt-mgmt          │
│        │                          │ (alloc VQ/VI)      │
│        │                          ▼                    │
│        │                   ┌─────────────┐             │
│        │                   │ VF Resource  │             │
│        │                   │ Allocated    │             │
│        │                   └──────┬──────┘             │
│        │                          │                    │
│        │                          │ virt-mgmt          │
│        │                          │ (controller online) │
│        │                          ▼                    │
│        │                   ┌─────────────┐             │
│        │                   │ Secondary   │             │
│        │                   │ Ctrl Online │             │
│        │                   │ (VM可用!)   │             │
│        │                   └──────┬──────┘             │
│        │                          │                    │
│        │                          │ VM Shutdown        │
│        │                          ▼                    │
│        │                   ┌─────────────┐             │
│        │                   │ Ctrl Offline│             │
│        │                   └──────┬──────┘             │
│        │                          │                    │
│        │                          │ virt-mgmt          │
│        │                          │ (reclaim VQ/VI)    │
│        │                          ▼                    │
│        │                   ┌─────────────┐             │
│        │                   │ Resources   │             │
│        │                   │ Reclaimed   │             │
│        │                   └──────┬──────┘             │
│        │                          │                    │
│        │    Delete VF             │                    │
│        │◄─────────────────────────┘                    │
│        │                                               │
└─────────────────────────────────────────────────────────┘

3.5 Identify Controller数据结构中的虚拟化字段

Identify Controller(CNS=00h)返回的4096字节数据结构中,包含多个与虚拟化直接相关的字段:

/*
 * Identify Controller 虚拟化关键字段 (NVMe Spec 2.0)
 * 偏移量来自 Figure 278 in NVMe Base Spec 2.0
 */
struct nvme_id_ctrl_virtualization_fields {
    /* Offset 250-251: OACS (Optional Admin Command Support) */
    __le16 oacs;
    /* Bit 3: NS Management supported? (0x08)
     *   → 是否支持 Namespace Management (Create/Delete)
     * Bit 4: NS Attachment supported? (0x10)
     *   → 是否支持 Namespace Attachment (Attach/Detach)
     * Bit 8: Virtualization Management supported? (0x100)
     *   → 是否支持 Virtualization Management 命令
     * Bit 11: Capacity Management supported? (0x800)
     *   → NVMe 2.0 新增
     */

    /* Offset 514-515: Maximum Number of Allowed Namespaces */
    __le16 mnan;

    /* Offset 516-519: Total NVM Capacity (低32位) */
    __le64 tnvmcap[2];

    /* Offset 528-531: Unallocated NVM Capacity (低32位) */
    __le64 unvmcap[2];

    /* Offset 524-525: I/O Queue Command Profile */
    /* 偏移 520-521: 最大 I/O Queue 数量 */
    __le16 nn;      /* Number of Namespaces (所有已创建NS的数量,包括未Attach的) */
    
    /* Offset 525: ONCS */
    __le16 oncs;    /* Optional NVM Command Support */

    /* 虚拟化增强相关 (NVMe 1.3+ 新增): */
    /* Offset 534: VQRFAP - Virtual Queue Resources Flexibility Allocated to Primary */
    __le16 vqrfap;
    /* Offset 536: VQFRSM - Virtual Queue Resources Flexibility Secondary Maximum */
    __u8  vqfrsm;
    /* Offset 537: VQFRT - Virtual Queue Resources Flexibility Total */
    __u8  vqfrt;
    
    /* Offset 538: VIFRAP - Virtual Interrupt Resources Flexibility Allocated to Primary */
    __le16 vifrap;
    /* Offset 540: VIFRSM - Virtual Interrupt Resources Flexibility Secondary Maximum */
    __u8  vifrsm;
    /* Offset 541: VIFRT - Virtual Interrupt Resources Flexibility Total */
    __u8  vifr;
};

/*
 * 查询虚拟化支持的完整命令序列:
 *
 * # 1. 检查 OACS 是否支持 Virtualization Management
 * nvme id-ctrl /dev/nvme0 | grep oacs
 * # 如果 bit 8 = 1, 则支持 virt-mgmt 命令
 *
 * # 2. 查看最大 VF / Namespace 数量
 * nvme id-ctrl /dev/nvme0 | grep -E "nn|vqfrt|vifr"
 *
 * # 3. 查看总容量和未分配容量
 * nvme id-ctrl /dev/nvme0 | grep -E "tnvmcap|unvmcap"
 *
 * # 4. 查看现有 Secondary Controller 列表
 * nvme list-secondary /dev/nvme0
 */

3.6 I/O队列资源分配:qrpn/qrpa机制

I/O Queue资源的分配是NVMe SR-IOV中最精妙的部分。每个VF需要拥有独立的I/O Submission Queue和Completion Queue

I/O Queue 资源分配模型:

┌──────────────────────────────────────────────────────────┐
│                NVM Subsystem I/O Queue Pool              │
│                                                          │
│  总可用 VQ 数 = VQFRT (如 64)                           │
│  每个 Secondary Max = VQFRSM (如 16)                    │
│                                                          │
│  ┌─────────────────────────────────────────────────┐    │
│  │              I/O Queue 资源池                    │    │
│  │                                                  │    │
│  │  ┌────────┐  ┌────────┐  ┌────────┐  ┌────────┐│    │
│  │  │ PF 使用 │  │ VF #1  │  │ VF #2  │  │ VF #3  ││    │
│  │  │ 8 对   │  │ 8 对   │  │ 8 对   │  │ 8 对   ││    │
│  │  │ SQ+CQ  │  │ SQ+CQ  │  │ SQ+CQ  │  │ SQ+CQ  ││    │
│  │  │        │  │        │  │        │  │        ││    │
│  │  │[SQ0-CQ0│  │[SQ1-CQ1│  │[SQ9-CQ9│  │[SQ17-  ││    │
│  │  │ ...    │  │ ...    │  │ ...    │  │ CQ17   ││    │
│  │  │ SQ7-CQ7│  │ SQ8-CQ8│  │SQ16-CQ16│ │ ...    ││    │
│  │  │]       │  │]       │  │]       │  │ SQ24-  ││    │
│  │  │        │  │        │  │        │  │ CQ24]  ││    │
│  │  └────────┘  └────────┘  └────────┘  └────────┘│    │
│  │                                                  │    │
│  │  已分配: 32 对    剩余可用: 32 对                  │    │
│  └─────────────────────────────────────────────────┘    │
│                                                          │
│  每个 VF 的 Queue 特点:                                  │
│  - Queue 编号对 VM 透明: VF#1 看到的是 SQ0/SQ1/...       │
│    而非物理编号 SQ1/SQ2/...                              │
│  - 每个 VF 拥有独立的 SQ/CQ 物理内存                      │
│    (在 VM 的内存空间中, 通过 IOMMU 映射)                 │
│  - Doorbell 寄存器映射到 VF 的 BAR0                      │
└──────────────────────────────────────────────────────────┘

四、NVMe SR-IOV完整工作流:从PF到VM的全链路

4.1 整体架构数据流

┌─────────────────────────────────────────────────────────────────┐
│                NVMe SR-IOV 完整配置流程                           │
│                                                                 │
│  Phase 1        Phase 2        Phase 3        Phase 4          │
│  PF→VF          NS创建/绑定    资源分配        VF直通VM         │
│                                                                 │
│  ┌──────┐      ┌──────┐      ┌──────┐      ┌──────┐          │
│  │echo 4│      │create│      │virt- │      │vfio- │          │
│  │>numvfs│     │-ns   │      │mgmt  │      │pci   │          │
│  └──┬───┘      └──┬───┘      └──┬───┘      └──┬───┘          │
│     │              │              │              │               │
│     ▼              ▼              ▼              ▼               │
│  ┌─────────────────────────────────────────────────────┐       │
│  │                    物理 NVMe SSD                      │       │
│  │  PF ──→ VF0, VF1, VF2, VF3                          │       │
│  │  NS0(1.6T) → VF0  NS1(1.6T) → VF1                  │       │
│  │  NS2(1.6T) → VF2  NS3(1.6T) → VF3                  │       │
│  │  VF0: 8 SQ + 8 CQ  VF1: 8 SQ + 8 CQ               │       │
│  │  VF2: 8 SQ + 8 CQ  VF3: 8 SQ + 8 CQ               │       │
│  └─────────────────────────────────────────────────────┘       │
│     │              │              │              │               │
│     ▼              ▼              ▼              ▼               │
│  Host          VM #1         VM #2         VM #3               │
│  (PF driver)   (NVMe VF)     (NVMe VF)     (NVMe VF)          │
│                                                                 │
│  Phase 5: VM内部加载标准NVMe驱动,看到的是完整NVMe Controller    │
└─────────────────────────────────────────────────────────────────┘

4.2 Phase 1:PF初始化与VF创建

VF的创建通过PCIe SR-IOV机制完成,与NVMe协议层无关:

#!/bin/bash
# Phase 1: 检查 SR-IOV 支持并创建 VF

# Step 1: 确认设备支持 SR-IOV
lspci -vvv -s 0000:03:00.0 | grep -i "SR-IOV"
# 期望输出:
# Capabilities: [100 v1] Single Root I/O Virtualization (SR-IOV)
#   Capabilities: PFExpBar0=00000000, PFExpBar1=00000000
#   TotalVFs: 64, InitialVFs: 0, NumVFs: 0

# Step 2: 查看 TotalVFs (最大VF数)
cat /sys/class/nvme/nvme0/device/sriov_totalvfs
# 输出: 64

# Step 3: 确保当前没有活跃的VF
cat /sys/class/nvme/nvme0/device/sriov_numvfs
# 输出: 0

# Step 4: 创建 4 个 VF
echo 4 > /sys/class/nvme/nvme0/device/sriov_numvfs

# Step 5: 验证 VF 已创建
lspci | grep -i "Non-Volatile"
# 期望输出:
# 0000:03:00.0 Non-Volatile memory controller: Samsung Electronics Co Ltd Device ...
# 0000:04:00.0 Non-Volatile memory controller: Samsung Electronics Co Ltd Device ... (rev 01)
# 0000:04:00.1 Non-Volatile memory controller: Samsung Electronics Co Ltd Device ... (rev 01)
# 0000:04:01.0 Non-Volatile memory controller: Samsung Electronics Co Ltd Device ... (rev 01)
# 0000:04:01.1 Non-Volatile memory controller: Samsung Electronics Co Ltd Device ... (rev 01)

# Step 6: 查看 NVMe 层面识别到的 Secondary Controller
nvme list-secondary /dev/nvme0
# 期望输出 (Samsung PM1735 示例):
# NVME Secondary Controllers for NVME0
# .... ................. ....... ............... .........
# SCID     SN              MN              FR       ...
# .... ................. ....... ............... .........
# 0x0001   S55JNE0NB00453  SAMSUNG MZPLJ1T6HBJR  EPK9GB5Q ...
# 0x0002   S55JNE0NB00453  SAMSUNG MZPLJ1T6HBJR  EPK9GB5Q ...
# 0x0003   S55JNE0NB00453  SAMSUNG MZPLJ1T6HBJR  EPK9GB5Q ...
# 0x0004   S55JNE0NB00453  SAMSUNG MZPLJ1T6HBJR  EPK9GB5Q ...

4.3 Phase 2:Namespace创建与Controller绑定

#!/bin/bash
# Phase 2: 创建 Namespace 并绑定到 Secondary Controller

DEV=/dev/nvme0

# Step 1: 删除默认的 Namespace (通常在 NSID=1)
nvme delete-ns $DEV -n 1
nvme reset $DEV

# Step 2: 创建 4 个 Namespace,每个 1.6TB
# 1.6TB = 1,600,000,000,000 bytes / 512 = 3,125,000,000 LBAs
NSZE=3125000000
NCAP=3125000000

for i in 1 2 3 4; do
    nvme create-ns $DEV --nsze=$NSZE --ncap=$NCAP --flbas=0 --dps=0
done

# Step 3: 查看已创建的 Namespace
nvme list-ns $DEV --all
# 输出:
# [   0]:0x1     (已创建但未Attach)
# [   1]:0x2
# [   2]:0x3
# [   3]:0x4

# Step 4: 将每个 Namespace Attach 到对应的 Secondary Controller
# Controller ID 通常从 0x0001 开始
nvme attach-ns $DEV -n 1 -c 0x0001    # NS1 → Secondary Ctrl #1 (VF0)
nvme attach-ns $DEV -n 2 -c 0x0002    # NS2 → Secondary Ctrl #2 (VF1)
nvme attach-ns $DEV -n 3 -c 0x0003    # NS3 → Secondary Ctrl #3 (VF2)
nvme attach-ns $DEV -n 4 -c 0x0004    # NS4 → Secondary Ctrl #4 (VF3)

# Step 5: 重置控制器使更改生效
nvme reset $DEV

4.4 Phase 3:VF资源分配(virt-mgmt)

#!/bin/bash
# Phase 3: 通过 Virtualization Management 命令分配 I/O Queue 和中断资源

DEV=/dev/nvme0

# 为每个 Secondary Controller 分配资源
for CTRL_ID in 1 2 3 4; do
    echo "=== Configuring Secondary Controller $CTRL_ID ==="
    
    # 分配 8 个 I/O Queue 对 (SQ + CQ)
    nvme virt-mgmt $DEV -c $CTRL_ID -r 0 -n 1 -a 8
    
    # 分配 8 个中断向量
    nvme virt-mgmt $DEV -c $CTRL_ID -r 1 -n 1 -a 8
    
    # 将 Secondary Controller 置为 Online
    nvme virt-mgmt $DEV -c $CTRL_ID -r 0 -n 8
done

# 验证: 查看每个 VF 对应的 NVMe 设备
nvme list
# 输出:
# Node             SN               Model                     Namespace  Usage        Format
# /dev/nvme1n1     ...              Samsung VF0               1          1.60TB/1.60TB  512B+0B
# /dev/nvme2n1     ...              Samsung VF1               2          1.60TB/1.60TB  512B+0B
# /dev/nvme3n1     ...              Samsung VF2               3          1.60TB/1.60TB  512B+0B
# /dev/nvme4n1     ...              Samsung VF3               4          1.60TB/1.60TB  512B+0B

4.5 Phase 4:vfio-pci绑定与VM直通

#!/bin/bash
# Phase 4: 将 VF 绑定到 vfio-pci 驱动,为 VM 直通做准备

# Step 1: 加载 vfio-pci 模块
modprobe vfio-pci
modprobe vfio_iommu_type1

# Step 2: 获取 VF 的 PCI 地址
VF0_BDF=$(lspci -D | grep "Non-Volatile" | head -n 1 | awk '{print $1}')
# 例如: 0000:04:00.0

# Step 3: 获取 VF 的 Vendor ID 和 Device ID
VF0_ID=$(lspci -n -s $VF0_BDF | awk '{print $3}')
# 例如: 144d:a824 (Samsung VF Device ID)

# Step 4: 解绑原有驱动并绑定到 vfio-pci
echo $VF0_BDF > /sys/bus/pci/devices/$VF0_BDF/driver/unbind
echo "vfio-pci" > /sys/bus/pci/devices/$VF0_BDF/driver_override
echo $VF0_BDF > /sys/bus/pci/drivers/vfio-pci/bind

# Step 5: 验证 IOMMU 组
ls -la /sys/bus/pci/devices/$VF0_BDF/iommu_group/
# 应该看到指向 /sys/kernel/iommu_groups/N 的链接

# Step 6: 查看 VFIO 设备
ls /dev/vfio/
# 应该看到对应的 VFIO group 设备

4.6 Phase 5:VM内部NVMe驱动加载

# 在 VM 内部 (Guest OS):

# VF 被直通后,VM 内会自动检测到 NVMe Controller
lspci | grep NVMe
# 00:03.0 Non-Volatile memory controller: Samsung Electronics Co Ltd ... (VF)

# Linux NVMe 驱动自动加载
lsblk
# NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
# nvme0n1 259:0   0   1.5T  0 disk

# 格式化和挂载
mkfs.xfs /dev/nvme0n1
mount /dev/nvme0n1 /mnt/data

# 验证性能 (在 VM 内部)
fio --name=test --filename=/dev/nvme0n1 --ioengine=libaio --direct=1 \
    --rw=randread --bs=4k --iodepth=32 --numjobs=4 --runtime=60 \
    --group_reporting

五、Linux内核实现:NVMe SR-IOV源码深度剖析

5.1 内核PCIe SR-IOV核心框架

Linux内核中SR-IOV的核心实现位于 drivers/pci/iov.c

/*
 * Linux Kernel PCIe SR-IOV 核心框架
 * 源码位置: drivers/pci/iov.c (Linux 6.x)
 *
 * 核心数据结构和函数:
 */

/* drivers/include/linux/pci.h */
struct pci_dev {
    /* ... */
    unsigned int is_virtfn:1;     /* 标识这是一个 VF */
    unsigned int is_physfn:1;     /* 标识这是一个 PF */
    
    /* SR-IOV 相关字段 */
    struct pci_sriov *sriov;      /* PF 的 SR-IOV 信息 */
    struct pci_dev *physfn;       /* VF 指向其 PF 的指针 */
    /* ... */
};

/* drivers/pci/pci.h */
struct pci_sriov {
    int pos;              /* SR-IOV Capability 在配置空间的位置 */
    int nres;             /* BAR 数量 */
    u32 cap;              /* SR-IOV Capability Register */
    u16 ctrl;             /* SR-IOV Control Register */
    u16 total_VFs;        /* TotalVFs 字段 */
    u16 offset;           /* FirstVF Offset */
    u16 stride;           /* Subsequent VF Stride */
    u16 vf_device;        /* VF Device ID */
    u32 pgsz;             /* Page Size for VF BAR */
    u16 initial_VFs;      /* InitialVFs */
    u16 nr_virtfn;        /* 当前启用的 VF 数量 (NumVFs) */
};

/*
 * VF 创建的核心调用链:
 *
 * 用户空间:
 *   echo 4 > /sys/class/nvme/nvme0/device/sriov_numvfs
 *
 * 内核路径:
 *   sriov_numvfs_store()                    [drivers/pci/iov.c]
 *     └─→ pci_sriov_set_totalvfs()
 *           └─→ sriov_enable(dev, num_vfs)
 *                 ├─→ pci_iov_add_virtfn() × N   (为每个VF创建 pci_dev)
 *                 │     ├─→ pci_scan_single_device()
 *                 │     ├─→ pci_device_add()
 *                 │     └─→ pci_bus_add_device()  (触发 hot-plug 事件)
 *                 │
 *                 └─→ 更新 SR-IOV Capability 的 NumVFs 寄存器
 *
 * 对于 NVMe 设备, PF 驱动 (nvme) 的 sriov_configure 回调也会被触发
 */

5.2 NVMe驱动中的sriov_configure回调

/*
 * NVMe 驱动中的 SR-IOV 支持
 * 源码位置: drivers/nvme/host/pci.c (Linux 6.x)
 */

/* NVMe PCI 驱动结构体 */
static struct pci_driver nvme_driver = {
    .name           = "nvme",
    .id_table       = nvme_id_table,
    .probe          = nvme_probe,
    .remove         = nvme_remove,
    .shutdown       = nvme_shutdown,
    .driver         = {
        .pm     = &nvme_dev_pm_ops,
    },
    .sriov_configure = nvme_sriov_configure,  /* ← SR-IOV 回调 */
    .err_handler    = &nvme_err_handler,
};

/*
 * sriov_configure 回调实现
 * 当用户通过 sysfs 写入 sriov_numvfs 时,PCI 核心会调用此回调
 */
static int nvme_sriov_configure(struct pci_dev *pdev, int numvfs)
{
    struct nvme_dev *dev = pci_get_drvdata(pdev);
    
    if (numvfs == 0) {
        /* 禁用所有 VF */
        pci_disable_sriov(pdev);
        return 0;
    }
    
    /* 启用指定数量的 VF */
    return pci_enable_sriov(pdev, numvfs);
}

/*
 * 内核 API:
 *   int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn);
 *   void pci_disable_sriov(struct pci_dev *dev);
 *
 * 这些函数定义在 drivers/pci/iov.c 中
 * 它们会:
 * 1. 写入 PF 的 SR-IOV Capability 的 NumVFs 寄存器
 * 2. 为每个 VF 创建 pci_dev 结构体
 * 3. 触发 PCI bus rescan 以发现新设备
 * 4. 如果 sriov_drivers_autoprobe=1, 自动为 VF 匹配驱动
 */

5.3 sysfs接口:sriov_numvfs的读写路径

/*
 * sysfs 接口: sriov_numvfs
 * 位置: /sys/bus/pci/devices/<BDF>/sriov_numvfs
 *
 * 内核实现 (drivers/pci/iov.c):
 */

/* 写入处理函数 */
static ssize_t sriov_numvfs_store(struct device *dev,
                                   struct device_attribute *attr,
                                   const char *buf, size_t count)
{
    struct pci_dev *pdev = to_pci_dev(dev);
    u16 num_vfs;
    int ret;
    
    if (kstrtou16(buf, 0, &num_vfs) < 0)
        return -EINVAL;
    
    /* 验证: num_vfs 不能超过 TotalVFs */
    if (num_vfs > pci_sriov_get_totalvfs(pdev))
        return -ERANGE;
    
    /* 验证: 如果要启用 VF,当前必须为 0 */
    if (num_vfs > 0 && pdev->sriov->nr_virtfn > 0)
        return -EBUSY;
    
    /* 调用驱动的 sriov_configure 回调 */
    if (pdev->driver && pdev->driver->sriov_configure) {
        ret = pdev->driver->sriov_configure(pdev, num_vfs);
        if (ret < 0)
            return ret;
    }
    
    return count;
}

/* 读取处理函数 */
static ssize_t sriov_numvfs_show(struct device *dev,
                                  struct device_attribute *attr,
                                  char *buf)
{
    struct pci_dev *pdev = to_pci_dev(dev);
    return sysfs_emit(buf, "%u\n", pdev->sriov->nr_virtfn);
}

/*
 * 关键 sysfs 文件一览:
 *
 * /sys/bus/pci/devices/<BDF>/
 *   ├── sriov_totalvfs        ← 只读, 硬件支持的最大VF数
 *   ├── sriov_numvfs          ← 读写, 当前启用的VF数 / 写入触发VF创建
 *   ├── sriov_offset          ← 只读, FirstVF Offset
 *   ├── sriov_stride          ← 只读, VF Stride
 *   ├── sriov_drivers_autoprobe ← 读写, 是否自动为VF加载驱动 (0/1)
 *   └── virtfn0, virtfn1, ... ← 符号链接, 指向各VF的pci_dev目录
 */

5.4 nvme-virt.c:Virtualization Management命令实现

/*
 * NVMe Virtualization Management 命令的内核实现
 * 源码位置: drivers/nvme/host/virt.c (Linux 6.x)
 * 或通过 nvme-cli 用户空间工具调用 ioctl
 */

/*
 * nvme-cli 的 virt-mgmt 命令实现
 * 源码位置: nvme-cli/nvme.c
 *
 * 核心逻辑: 构造 NVMe Admin Command 并通过 ioctl 发送到设备
 */

/* nvme-cli 中 virt-mgmt 命令的关键结构: */
static int virt_mgmt(int argc, char **argv, struct command *cmd,
                      struct plugin *plugin)
{
    struct nvme_virt_mgmt_args args = {
        .args_size  = sizeof(args),
        .timeout    = NVME_DEFAULT_IOCTL_TIMEOUT,
    };
    /* 解析参数:
     *   -c / --cntlid:    Controller ID
     *   -r / --rt:        Resource Type (0=VQ, 1=VI)
     *   -n / --act:       Action (1=alloc, 7=reclaim, 8=online, 9=offline)
     *   -a / --nr:        资源数量
     */
    
    /* 通过 nvme_cli_identify_ctrl 获取虚拟化能力信息 */
    /* 构造 Virtualization Management Admin Command */
    /* 通过 NVME_IOCTL_ADMIN_CMD ioctl 发送到内核 */
    
    err = nvme_virt_mgmt(fd, &args);
    /* 
     * 内核路径:
     *   nvme_virt_mgmt()          [nvme-cli/libnvme]
     *     └─→ nvme_submit_admin_passthru()
     *           └─→ ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd)
     *                 └─→ nvme_dev_ioctl()      [drivers/nvme/host/pci.c]
     *                       └─→ nvme_submit_user_cmd()
     *                             └─→ nvme_queue_sqe()  (SQ Entry)
     *                                   → SSD 硬件处理
     */
}

/*
 * 内核侧: NVMe Admin Command 处理路径
 * drivers/nvme/host/core.c
 */
static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
                          struct nvme_passthru_cmd __user *ucmd)
{
    struct nvme_command c = { };
    
    /* 从用户空间拷贝命令 */
    if (copy_from_user(&c, ucmd, sizeof(c)))
        return -EFAULT;
    
    /* 验证命令合法性 */
    /* Virtualization Management (0x1C) 只能在 PF 上执行 */
    if (c.common.opcode == nvme_admin_virtual_mgmt) {
        if (ctrl->instance != PF_INSTANCE)
            return -EINVAL;  /* 非 PF 不允许执行 */
    }
    
    /* 提交到 Admin SQ */
    return nvme_submit_sync_cmd(ctrl->admin_q, &c, NULL, 0);
}

5.5 vfio-pci驱动与NVMe VF的交互

/*
 * vfio-pci 驱动: VF 直通的最后一环
 * 源码位置: drivers/vfio/pci/vfio_pci_core.c (Linux 6.x)
 *
 * 核心职责:
 * 1. 将 VF 的 PCI 配置空间和 BAR 暴露给用户空间 (QEMU/KVM)
 * 2. 管理 IOMMU 映射 (DMA 翻译)
 * 3. 处理 MSI-X 中断转发
 */

/*
 * VFIO 设备直通的核心数据结构:
 */
struct vfio_pci_core_device {
    struct vfio_pci_core_device *vdev;
    struct pci_dev *pdev;        /* 指向 VF 的 pci_dev */
    
    /* BAR 信息 */
    struct vfio_pci_region bars[6];
    
    /* MSI-X 配置 */
    struct vfio_pci_msix_priv *msix;
    
    /* IOMMU 映射管理 */
    struct vfio_iommu_driver *iommu_driver;
};

/*
 * QEMU 通过 VFIO 使用 NVMe VF 的调用链:
 *
 * QEMU (用户空间)
 *   └─→ ioctl(vfio_fd, VFIO_DEVICE_GET_INFO)     获取设备信息
 *   └─→ ioctl(vfio_fd, VFIO_DEVICE_GET_REGION_INFO) 获取 BAR 映射
 *   └─→ mmap(bar_fd, ...)                         映射 BAR 到 QEMU 地址空间
 *   └─→ ioctl(vfio_fd, VFIO_IOMMU_MAP_DMA)       建立 DMA 映射
 *   └─→ ioctl(vfio_fd, VFIO_DEVICE_SET_IRQS)     配置 MSI-X 中断
 *
 * 之后 VM 内部对 NVMe VF 的访问路径:
 *
 * VM Guest
 *   └─→ NVMe 驱动 (标准 in-box driver)
 *         └─→ MMIO: 写入 VF BAR0 的 Doorbell 寄存器
 *               └─→ 通过 QEMU mmap 的 BAR 映射到达宿主 vfio-pci
 *                     └─→ 硬件: VF 的 Doorbell → SSD 控制器
 *                           └─→ 处理 I/O 命令
 *                                 └─→ MSI-X 中断 → IOMMU 重映射 → VM
 */

/*
 * VFIO Group 与 IOMMU 的关系:
 *
 * /dev/vfio/
 *   ├── vfio          ← VFIO 控制设备 (版本查询等)
 *   ├── 0             ← IOMMU Group 0 (包含 VF#1)
 *   ├── 1             ← IOMMU Group 1 (包含 VF#2)
 *   └── ...
 *
 * 每个 VF 必须在其 IOMMU Group 中独占 (不能与其他设备同组)
 * 否则需要 ACS Override: pcie_acs_override=downstream,multifunction
 */

六、五大NVMe虚拟化方案架构级对比

6.1 方案全景图

┌──────────────────────────────────────────────────────────────────┐
│           NVMe 虚拟化方案全景图                                    │
│                                                                  │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │                 软件方案 (Software-based)                    │  │
│  │                                                            │  │
│  │  ┌─────────────┐  ┌──────────────┐  ┌──────────────────┐  │  │
│  │  │  Virtio-blk  │  │ SPDK vhost-  │  │   Mdev-NVMe      │  │  │
│  │  │             │  │   NVMe       │  │  (mediated pass) │  │  │
│  │  │ 纯软件模拟  │  │ 用户态轮询   │  │ 内核mediated     │  │  │
│  │  │ 通用NVMe设备│  │ 专用CPU核    │  │ 影子队列         │  │  │
│  │  └─────────────┘  └──────────────┘  └──────────────────┘  │  │
│  └────────────────────────────────────────────────────────────┘  │
│                                                                  │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │              硬件辅助方案 (Hardware-assisted)                │  │
│  │                                                            │  │
│  │  ┌──────────────────────┐  ┌────────────────────────────┐  │  │
│  │  │   SR-IOV (现有主流)   │  │   SIOV / Scalable IOV     │  │  │
│  │  │                       │  │   (下一代)                 │  │  │
│  │  │   设备端虚拟化        │  │   平台端虚拟化             │  │  │
│  │  │   PF→VF 共享         │  │   VA/PA 解耦              │  │  │
│  │  │   需要SSD硬件支持     │  │   任何PCIe设备可用        │  │  │
│  │  └──────────────────────┘  └────────────────────────────┘  │  │
│  └────────────────────────────────────────────────────────────┘  │
│                                                                  │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │              完全直通 (Full Passthrough)                     │  │
│  │  ┌──────────────────────────────────────────────────────┐  │  │
│  │  │   VFIO 直通: 整块物理设备给一个 VM (不可共享)         │  │  │
│  │  └──────────────────────────────────────────────────────┘  │  │
│  └────────────────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────────────────┘

6.2 Virtio:纯软件半虚拟化

Virtio-blk 数据路径:

┌─────────────────────────────────────────────┐
│  VM Guest                                   │
│  ┌───────────┐                              │
│  │ App       │                              │
│  │ (fio)     │                              │
│  └─────┬─────┘                              │
│        │ submit_bio()                       │
│  ┌─────▼─────────────────┐                  │
│  │ Virtio Block Driver   │                  │
│  │ (virtio_blk.c)        │                  │
│  └─────┬─────────────────┘                  │
│        │ virtqueue_add_sgs()                │
│        │ (ring buffer 通知)                 │
└────────┼────────────────────────────────────┘
         │ VM Exit (world switch)
┌────────▼────────────────────────────────────┐
│  Hypervisor (QEMU/KVM)                      │
│  ┌─────────────────────────────┐            │
│  │ Virtio Backend (vhost)      │            │
│  │ 从 shared memory 读取请求   │            │
│  │ 转换为物理 I/O 命令         │            │
│  └───────────┬─────────────────┘            │
│              │ bio_submit()                  │
│  ┌───────────▼─────────────────┐            │
│  │ 物理 NVMe 驱动              │            │
│  │ (nvme-core.c)               │            │
│  └───────────┬─────────────────┘            │
└──────────────┼──────────────────────────────┘
               │ MMIO (doorbell)
         ┌─────▼─────┐
         │ NVMe SSD  │
         └───────────┘

性能损耗分析:
  1. VM Exit 开销: ~2-5 μs/次
  2. 上下文切换: 宿主机线程调度
  3. 数据拷贝: guest物理地址→宿主虚拟地址→DMA
  4. 中断注入: 需要 VM Entry 将中断传递给 VM
  
  总开销: 吞吐量下降 ~50%, 延迟增加 ~100-200%

6.3 SPDK vhost-NVMe:用户态轮询方案

SPDK vhost-NVMe 数据路径:

┌──────────────────────────────────────────────┐
│  VM Guest                                    │
│  ┌───────────┐                               │
│  │ NVMe VF   │ ← VM 看到的是虚拟 NVMe 设备   │
│  │ Driver    │                               │
│  └─────┬─────┘                               │
│        │ SQ/CQ in shared memory              │
└────────┼─────────────────────────────────────┘
         │ (shared memory region)
┌────────▼─────────────────────────────────────┐
│  Hypervisor 宿主机                            │
│  ┌──────────────────────────────────────────┐ │
│  │  SPDK vhost-NVMe (专用 CPU 核 @ 100%)    │ │
│  │  ┌────────────────────────────────┐      │ │
│  │  │ Polling Loop (不间断轮询)       │      │ │
│  │  │ while(1) {                     │      │ │
│  │  │   check virtual SQ for new cmd │      │ │
│  │  │   translate to physical NVMe   │      │ │
│  │  │   submit to physical NVMe      │      │ │
│  │  │   poll physical CQ for CPL     │      │ │
│  │  │   write to virtual CQ          │      │ │
│  │  │   inject interrupt to VM       │      │ │
│  │  │ }                              │      │ │
│  │  └───────────────┬────────────────┘      │ │
│  │                  │ (SPDK 用户态 NVMe驱动) │ │
│  │  ┌───────────────▼────────────────┐      │ │
│  │  │ 物理 NVMe SSD                  │      │ │
│  │  │ (通过 SPDK 用户态驱动访问)      │      │ │
│  │  └────────────────────────────────┘      │ │
│  └──────────────────────────────────────────┘ │
│                                               │
│  特点:                                        │
│  - 专用CPU核轮询 → 零中断、零上下文切换       │
│  - 用户态驱动 → 绕过内核协议栈                │
│  - 但每块SSD需要1-2个专用CPU核                 │
│  - 典型: 12块SSD的服务器需要 8-10 CPU核       │
└───────────────────────────────────────────────┘

6.4 Mdev-NVMe:内核 mediated passthrough

/*
 * Mdev-NVMe: Mediated Device Passthrough
 * 借鉴了 GPU vGPU 的 mediated passthrough 机制
 *
 * 核心思想:
 *   - 在宿主机内核中创建"影子"SQ/CQ
 *   - Guest 的 I/O 命令先写入影子队列
 *   - Host 内核驱动将影子队列中的命令转发到物理设备
 *   - 使用主动轮询 (active polling) 减少延迟
 *
 * 源码位置: 通常为实验性补丁, 未合并到主线内核
 */

/*
 * Mdev-NVMe 架构:
 *
 * ┌─────────────────────────────────────────┐
 * │  VM Guest                               │
 * │  NVMe Driver → 虚拟 SQ/CQ (shadow)     │
 * └────────────┬────────────────────────────┘
 *              │ mdev framework
 * ┌────────────▼────────────────────────────┐
 * │  Host Kernel (Mdev-NVMe Backend)        │
 * │  ┌─────────────────────────────────┐    │
 * │  │ Shadow SQ/CQ (per VM)          │    │
 * │  │ Active Polling in kernel        │    │
 * │  └──────────┬──────────────────────┘    │
 * │             │ translate & forward       │
 * │  ┌──────────▼──────────────────────┐    │
 * │  │ Physical NVMe Driver            │    │
 * │  │ (标准内核 nvme 驱动)            │    │
 * │  └──────────┬──────────────────────┘    │
 * └─────────────┼───────────────────────────┘
 *               │
 *         ┌─────▼─────┐
 *         │ NVMe SSD  │
 *         └───────────┘
 *
 * 优点: 无需专用CPU核, 内核态处理延迟低
 * 缺点: 仍在开发中, 生产环境不成熟
 */

6.5 SR-IOV:硬件级设备共享

SR-IOV方案的优势已在前面详细阐述,此处重点强调其硬件级隔离的不可替代性:

SR-IOV 的隔离层次:

┌────────────────────────────────────────────────────┐
│  层次1: PCIe 总线级隔离                             │
│  ─────────────────────────                         │
│  每个 VF 拥有独立的:                                │
│  - PCI Configuration Space                         │
│  - BAR0 MMIO 映射 (Doorbell, CQ, Admin Queue)      │
│  - MSI-X Table (独立中断向量)                      │
│  - DMA 能力 (通过 IOMMU 隔离)                      │
│                                                    │
│  → VM之间在PCIe总线层面完全隔离                     │
│                                                    │
│  层次2: NVMe Controller 级隔离                      │
│  ─────────────────────────                         │
│  每个 VF 对应一个独立的 Secondary Controller:       │
│  - 独立的 Admin Queue                              │
│  - 独立的 I/O Queue (SQ/CQ)                        │
│  - 独立的 SMART/Log Page                           │
│  - 只能访问被 Attach 的 Namespace                   │
│                                                    │
│  → VM看到的是完整的、独立的NVMe Controller          │
│                                                    │
│  层次3: Namespace 级数据隔离                         │
│  ─────────────────────────                         │
│  每个 NS 独占分配给一个 VF:                         │
│  - 非重叠的 LBA 空间                               │
│  - 独立的 FTL 管理 (固件级)                        │
│  - 独立的磨损均衡统计                              │
│                                                    │
│  → 物理NAND层面的数据完全隔离                       │
└────────────────────────────────────────────────────┘

6.6 SIOV/Scalable IOV:下一代方案

/*
 * Scalable IOV (SIOV) - Intel 主导的下一代 I/O 虚拟化方案
 * 解决了 SR-IOV 的可扩展性瓶颈
 *
 * SR-IOV 的可扩展性问题:
 *   - 每个 VF 需要完整的 PCI Function 结构
 *   - VF 的创建/销毁涉及 PCIe 拓扑变更 (hot-plug)
 *   - 当 VF 数量 > 100 时, PCIe 配置空间管理开销剧增
 *   - IOMMU 的 Device Table 需要为每个 VF 维护独立条目
 *
 * SIOV 的核心创新:
 *   - 引入 Virtual Address (VA) 和 Physical Address (PA) 解耦
 *   - VA 由设备端的 PASID (Process Address Space ID) 标识
 *   - PA 由 Root Complex 的 IOMMU 翻译
 *   - 一个物理设备可以拥有成千上万个 VA, 无需创建 PCI Function
 *
 * 架构对比:
 *
 * SR-IOV:
 *   Device → VF0 (PCI Function) → VM0
 *         → VF1 (PCI Function) → VM1
 *         → ...
 *   每个 VF 是重量级的 PCI 实体
 *
 * SIOV:
 *   Device → PASID=0 → VM0  (轻量级虚拟标识)
 *         → PASID=1 → VM1
 *         → ...
 *         → PASID=N → VMN
 *   无需创建 PCI Function, 仅通过 PASID 标识
 *
 * 状态 (2026):
 *   - Intel Scalable IOV 规范仍在开发中
 *   - 需要新的硬件支持 (CPU + 芯片组 + 设备)
 *   - 目前无量产设备支持
 *   - 预计 2027-2028 年开始出现在数据中心硬件中
 */

6.7 五大方案量化对比矩阵

维度VirtioSPDK vhost-NVMeMdev-NVMeSR-IOVSIOV
IOPS保持率~50%~95%~90%~100%~100%
延迟开销+100-200%+10-30%+5-15%<1%<1%
P99尾延迟增加3-5x增加1.5-2x增加1.3-1.5x近原生近原生
CPU开销2-4核1-2核/SSD近零
设备共享
可扩展性中(受限于TotalVFs)极高
硬件要求SSD支持SR-IOV新硬件平台
隔离强度软件级软件级内核级硬件级硬件级
VM驱动virtio-blk标准NVMe标准NVMe标准NVMe标准NVMe
生产就绪
典型场景通用云高性能云研究企业数据中心未来数据中心

七、性能实测:SR-IOV性能隔离基准测试

7.1 测试环境搭建

测试环境配置:

┌─────────────────────────────────────────────────────┐
│  硬件:                                              │
│  - CPU: Intel Xeon Gold 6430 (32C/64T)              │
│  - 内存: 256GB DDR5-4800 ECC                        │
│  - NVMe SSD: Samsung PM1735 6.4TB (U.2)            │
│    固件: EPK9GB5Q                                    │
│    最大VF数: 64                                      │
│  - PCIe: Gen4 x4 (单盘)                             │
│  - IOMMU: Intel VT-d enabled                        │
│                                                     │
│  软件:                                              │
│  - Host OS: Ubuntu 22.04 LTS, Kernel 6.1            │
│  - Hypervisor: QEMU/KVM 7.2                         │
│  - Guest OS: Ubuntu 22.04 LTS, Kernel 6.1           │
│  - fio version 3.33                                 │
│                                                     │
│  测试配置:                                           │
│  - 4 个 VF, 每个 VF 分配 8 对 I/O Queue             │
│  - 每个 VF 创建 1.6TB Namespace                     │
│  - 每个 VM: 4 vCPU, 8GB RAM                        │
│  - Guest 文件系统: ext4, direct I/O                 │
│                                                     │
│  fio 测试参数:                                       │
│  --ioengine=libaio --direct=1 --runtime=120         │
│  --time_based --group_reporting                      │
└─────────────────────────────────────────────────────┘

7.2 单VM吞吐量与IOPS测试

单 VM 场景: 1 个 VF 直通给 1 个 VM

测试命令:
  fio --name=single_vf --filename=/dev/nvme0n1 --direct=1 \
      --rw=randread --bs=4k --iodepth=32 --numjobs=1 \
      --ioengine=libaio --runtime=120 --time_based

┌─────────────────────────────────────────────────────────────┐
│  4K Random Read (iodepth=32, 1 job)                        │
│                                                             │
│  方案           IOPS (K)    带宽 (MB/s)   平均延迟 (μs)    │
│  ──────────────────────────────────────────────────────     │
│  Native (PF)    850         3,400         37.6              │
│  SR-IOV (VF)    835         3,340         38.3              │
│  VFIO直通       848         3,392         37.7              │
│  SPDK vhost     810         3,240         39.5              │
│  Virtio         425         1,700         75.2              │
│                                                             │
│  SR-IOV 性能保持率: 835/850 = 98.2%                         │
│  Virtio 性能保持率: 425/850 = 50.0%                         │
│                                                             │
│  结论: SR-IOV VF 性能几乎等同于 Native, 仅损失 ~1.8%        │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│  128K Sequential Read                                       │
│                                                             │
│  方案           IOPS (K)    带宽 (MB/s)   平均延迟 (μs)    │
│  ──────────────────────────────────────────────────────     │
│  Native (PF)    0.051       6,480         24.8              │
│  SR-IOV (VF)    0.050       6,420         25.1              │
│  VFIO直通       0.051       6,475         24.9              │
│  SPDK vhost     0.049       6,280         25.6              │
│  Virtio         0.048       6,100         26.3              │
│                                                             │
│  大I/O场景各方案差异缩小 (受限于SSD带宽上限)                 │
│  SR-IOV 带宽保持率: 6420/6480 = 99.1%                       │
└─────────────────────────────────────────────────────────────┘

7.3 多VM性能隔离与公平性验证

多 VM 场景: 4 个 VF 分别直通给 4 个 VM,同时运行 fio

测试命令 (每个 VM 内执行):
  fio --name=multi_vm --filename=/dev/nvme0n1 --direct=1 \
      --rw=randread --bs=4k --iodepth=32 --numjobs=1 \
      --ioengine=libaio --runtime=120 --time_based

┌─────────────────────────────────────────────────────────────┐
│  4 VM 并发 4K Random Read                                   │
│                                                             │
│  指标              总IOPS    每VM IOPS   公平性指数          │
│  ──────────────────────────────────────────────             │
│  SR-IOV (4VM)     720K      180K ± 3K   0.98 (优秀)        │
│  SPDK vhost (4VM) 650K      162K ± 15K  0.85 (一般)        │
│  Virtio (4VM)     380K       95K ± 25K  0.62 (差)          │
│                                                             │
│  分析:                                                      │
│  1. SR-IOV 4VM 总 IOPS = 720K vs Native 850K               │
│     多租户效率: 720/850 = 84.7%                             │
│     → 损失 ~15% 主要来自 NAND Flash 内部资源竞争            │
│       (channel/die 争用、GC 干扰)                           │
│                                                             │
│  2. SR-IOV 公平性指数 0.98:                                 │
│     → 各 VM 的 IOPS 非常接近 (180K ± 3K)                   │
│     → 说明 VF 之间的 I/O Queue 隔离有效                    │
│     → 剩余的不均匀来自 NAND 层面的共享资源                  │
│                                                             │
│  3. Virtio 公平性差 (0.62):                                 │
│     → 各 VM 竞争 hypervisor 调度时间片                      │
│     → I/O 请求排队延迟不可预测                              │
└─────────────────────────────────────────────────────────────┘

7.4 尾延迟P99/P99.9分析

延迟分布对比 (4K Random Read, iodepth=1):

┌─────────────────────────────────────────────────────────────┐
│  延迟分位数 (μs)                                            │
│                                                             │
│  方案          P50     P95     P99     P99.9   P99.99       │
│  ──────────────────────────────────────────────────────     │
│  Native        32      58      120     280     850          │
│  SR-IOV (VF)   33      60      125     290     880          │
│  VFIO          32      58      121     282     855          │
│  SPDK vhost    38      75      180     450     1,200        │
│  Virtio        68      150     380     950     2,500        │
│                                                             │
│  关键发现:                                                   │
│  1. SR-IOV P99 延迟仅比 Native 增加 4% (125 vs 120 μs)     │
│  2. SR-IOV P99.9 延迟增加 3.6% (290 vs 280 μs)             │
│  3. Virtio P99 延迟增加 217% (380 vs 120 μs)               │
│  4. 对于延迟敏感的在线业务, SR-IOV 是唯一可接受的方案        │
│                                                             │
│  尾延迟增加的根因:                                           │
│  - SR-IOV: 主要来自 NAND 内部 GC 和 read-retry             │
│    (不同 VF 的 GC 活动互不影响)                             │
│  - Virtio: 额外增加来自 VM Exit + hypervisor 调度抖动       │
│    (不可预测,导致长尾)                                     │
└─────────────────────────────────────────────────────────────┘

7.5 CPU开销对比

宿主机 CPU 使用率 (在满负载 4K Random Write 下):

┌─────────────────────────────────────────────────────────────┐
│  方案               Host CPU 使用     说明                   │
│  ──────────────────────────────────────────────             │
│  SR-IOV             ~0%              所有I/O直通, 无host介入 │
│  VFIO 直通          ~0%              同上                    │
│  SPDK vhost-NVMe    1-2 核 @100%    专用核轮询               │
│                     (8-12% of 16核)                         │
│  Virtio             0.5-1 核@100%   中断处理+上下文切换      │
│                     (3-6% of 16核)                          │
│  Mdev-NVMe          <0.1 核         内核态处理               │
│                                                             │
│  在 12 块 SSD 的服务器上:                                    │
│  - SR-IOV: 0 CPU cores                                     │
│  - SPDK vhost: 8-10 CPU cores                              │
│  → SR-IOV 每服务器可释放 8-10 个 CPU 核给业务               │
│  → 按 $100/核/月 计算, 节省 $800-1000/月/服务器             │
└─────────────────────────────────────────────────────────────┘

八、生产环境部署实战:Samsung PM1735全配置流程

8.1 硬件与固件准备

#!/bin/bash
# 完整部署脚本: Samsung PM1735 SR-IOV 配置
# 适用于: Ubuntu 22.04, Kernel 6.1+, nvme-cli 2.x

set -euo pipefail

# === 环境检查 ===
echo "=== Step 0: 环境检查 ==="

# 检查 NVMe 设备
nvme list
# 确认 PM1735 存在且固件版本 >= EPK9GB5Q

# 检查固件版本
FW_REV=$(nvme id-ctrl /dev/nvme0 | grep fr | awk '{print $NF}')
echo "Current firmware: $FW_REV"
# 如果版本过低, 需要升级:
# sudo nvme fw-download /dev/nvme0 --fw=PM1735_FW.bin
# sudo nvme fw-commit /dev/nvme0 -s 0 -a 1
# sudo nvme reset /dev/nvme0

# 检查 SR-IOV 支持
TOTAL_VFS=$(cat /sys/class/nvme/nvme0/device/sriov_totalvfs 2>/dev/null || echo 0)
if [ "$TOTAL_VFS" -eq 0 ]; then
    echo "ERROR: Device does not support SR-IOV!"
    exit 1
fi
echo "TotalVFs supported: $TOTAL_VFS"

# 检查 IOMMU
if ! dmesg | grep -q "IOMMU enabled"; then
    echo "WARNING: IOMMU may not be enabled. Check BIOS settings."
    echo "Required: intel_iommu=on iommu=pt in kernel cmdline"
fi

8.2 BIOS/IOMMU配置

BIOS 配置清单:
┌──────────────────────────────────────────────────────┐
│  必须启用的 BIOS 选项:                                │
│                                                      │
│  1. Intel Virtualization Technology (VT-x) → Enabled │
│  2. Intel VT-d (I/O Virtualization) → Enabled        │
│  3. SR-IOV → Enabled (部分BIOS有此选项)              │
│  4. PCIe ARI (Alternative Routing-ID) → Enabled      │
│                                                      │
│  内核启动参数 (/etc/default/grub):                    │
│  GRUB_CMDLINE_LINUX="intel_iommu=on iommu=pt \       │
│    pcie_acs_override=downstream,multifunction"        │
│                                                      │
│  验证:                                               │
│  $ dmesg | grep -i iommu                            │
│  [    0.523456] DMAR: IOMMU enabled                  │
│  $ dmesg | grep -i "VT-d"                           │
│  [    0.523789] DMAR: Intel(R) Virtualization        │
│   Technology for Directed I/O enabled                │
└──────────────────────────────────────────────────────┘

8.3 完整配置脚本

#!/bin/bash
# === 完整 SR-IOV 配置脚本 ===
# 创建 4 个 VF, 每个分配 1.6TB Namespace

DEV=/dev/nvme0
NUM_VFS=4
NS_SIZE_LBA=3125000000  # ~1.6TB with 512B LBA

echo "=== Phase 1: 清理环境 ==="
# 删除已有 VF
echo 0 > /sys/class/nvme/nvme0/device/sriov_numvfs 2>/dev/null || true
sleep 2

# 删除所有已有 Namespace (小心操作!)
for nsid in $(nvme list-ns $DEV --all 2>/dev/null | grep -oP '\[\s*\K\d+' | sort -rn); do
    nvme detach-ns $DEV -n $nsid -c 0x0001 2>/dev/null || true
    nvme delete-ns $DEV -n $nsid 2>/dev/null || true
done
nvme reset $DEV
sleep 2

echo "=== Phase 2: 创建 VF ==="
echo $NUM_VFS > /sys/class/nvme/nvme0/device/sriov_numvfs
sleep 3

# 验证 VF 创建
echo "Created VFs:"
lspci -D | grep "Non-Volatile" | tail -n $NUM_VFS

echo "=== Phase 3: 创建 Namespace 并绑定 ==="
for i in $(seq 1 $NUM_VFS); do
    echo "--- Creating NS #$i ---"
    
    # 创建 Namespace
    nvme create-ns $DEV --nsze=$NS_SIZE_LBA --ncap=$NS_SIZE_LBA \
        --flbas=0 --dps=0
    
    # Attach 到对应的 Secondary Controller
    # Controller ID = $i (假设从 1 开始)
    nvme attach-ns $DEV -n $i -c $(printf "0x%04x" $i)
done

nvme reset $DEV
sleep 2

echo "=== Phase 4: 资源分配 ==="
for CTRL_ID in $(seq 1 $NUM_VFS); do
    echo "--- Configuring Controller $CTRL_ID ---"
    
    # 分配 8 对 I/O Queue
    nvme virt-mgmt $DEV -c $CTRL_ID -r 0 -n 1 -a 8
    
    # 分配 8 个中断
    nvme virt-mgmt $DEV -c $CTRL_ID -r 1 -n 1 -a 8
    
    # Controller Online
    nvme virt-mgmt $DEV -c $CTRL_ID -r 0 -n 8
    
    sleep 1
done

echo "=== Phase 5: 验证 ==="
nvme list
nvme list-secondary $DEV

echo "=== 配置完成! ==="
echo "VF PCI 地址:"
lspci -D | grep "Non-Volatile" | tail -n $NUM_VFS

8.4 KVM/QEMU虚拟机配置

<!--
  libvirt XML 配置示例: 将 NVMe VF 直通给 VM
  
  假设 VF0 BDF = 0000:04:00.0
-->
<domain type='kvm'>
  <name>vm-nvme-vf0</name>
  <memory unit='GiB'>8</memory>
  <vcpu>4</vcpu>
  
  <os>
    <type arch='x86_64'>hvm</type>
    <boot dev='hd'/>
  </os>
  
  <features>
    <acpi/>
    <apic/>
  </features>
  
  <devices>
    <!-- NVMe VF Passthrough -->
    <hostdev mode='subsystem' type='pci' managed='yes'>
      <source>
        <address domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
      </source>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
    </hostdev>
    
    <!-- 其他设备 (网络、磁盘等) -->
    <disk type='file' device='disk'>
      <source file='/var/lib/libvirt/images/vm-nvme-vf0.qcow2'/>
      <target dev='vda' bus='virtio'/>
    </disk>
    
    <interface type='network'>
      <source network='default'/>
      <model type='virtio'/>
    </interface>
    
    <console type='pty'/>
  </devices>
</domain>

<!--
  使用 virsh 启动:
  virsh define vm-nvme-vf0.xml
  virsh start vm-nvme-vf0
  
  在 VM 内部验证:
  lspci | grep NVMe
  nvme list
  fio ...
-->

8.5 常见故障排查

┌─────────────────────────────────────────────────────────────┐
│  常见故障与排查                                              │
│                                                             │
│  问题1: VF 创建后 VM 内 lspci 可见但 nvme list 为空         │
│  原因: PM1735 旧版固件 bug                                  │
│  解决: 升级固件到 >= EPK9GB5Q                               │
│                                                             │
│  问题2: "Cannot find IOMMU group"                           │
│  原因: VF 与其他设备在同一 IOMMU 组                         │
│  解决: 添加内核参数 pcie_acs_override=downstream,multifunction│
│        或使用 PCIe switch 隔离                              │
│                                                             │
│  问题3: virt-mgmt 返回 "Invalid Field in Command"           │
│  原因: Secondary Controller 尚未正确创建或已 Online          │
│  解决: 检查 list-secondary 确认 SC 存在                     │
│        确保操作顺序: alloc VQ → alloc VI → online           │
│                                                             │
│  问题4: VM 内 I/O 性能显著低于预期                          │
│  原因: VM 内 NVMe 驱动未使用正确队列数                      │
│  解决: 在 VM 内检查:                                        │
│        cat /sys/class/nvme/nvme0/queue_count                │
│        确保与 virt-mgmt 分配的数量一致                       │
│                                                             │
│  问题5: 创建 Namespace 失败 "Namespace Capacity Exceeded"   │
│  原因: 总 NS 大小超过物理容量                               │
│  解决: nvme id-ctrl /dev/nvme0 | grep unvmcap               │
│        确保 Σ(NS Size) ≤ unvmcap (未分配容量)              │
└─────────────────────────────────────────────────────────────┘

九、前沿演进:NVMe 2.0与CXL时代的虚拟化

9.1 NVMe 2.0虚拟化架构变化

NVMe 2.0 规范对虚拟化架构进行了重构:

NVMe 2.0 虚拟化关键变化:

1. Controller Types 标准化
   - Admin Controller (Type 0): 仅管理, 无I/O
   - I/O Controller (Type 1): 完整功能
   - Discovery Controller (Type 2): NVMe-oF发现
   → 虚拟化场景下 VF 对应 I/O Controller (Type 1)

2. NVM Set 与虚拟化
   - NVM Set 提供性能隔离的语义
   - 不同 NVM Set 的 Namespace 保证性能隔离
   - 与 SR-IOV 结合: 每个 VF 的 NS 可分配到不同 NVM Set

3. Endurance Groups
   - 多个 Controller 可共享 Endurance Group
   - 用于跨 VF 的寿命管理

4. Capacity Management (新增)
   - 更灵活的容量分配机制
   - 支持动态调整 Namespace 容量

9.2 Multi-Host SR-IOV:PCIe Fabric共享

Multi-Host SR-IOV 架构 (Microchip PAX PCIe Fabric):

┌──────────────────────────────────────────────────────────┐
│                                                          │
│  Host #1 (Windows)         Host #2 (Linux)               │
│  ┌──────────────┐          ┌──────────────┐              │
│  │ PCIe Root    │          │ PCIe Root    │              │
│  │ Complex      │          │ Complex      │              │
│  └──────┬───────┘          └──────┬───────┘              │
│         │                         │                       │
│  ┌──────▼─────────────────────────▼───────┐              │
│  │         PCIe Fabric Switch              │              │
│  │         (Microchip PAX)                 │              │
│  │                                         │              │
│  │  ┌──────────────────────────────────┐   │              │
│  │  │  SR-IOV NVMe SSD                 │   │              │
│  │  │  ┌─────┐ ┌─────┐ ┌─────┐       │   │              │
│  │  │  │ VF0 │ │ VF1 │ │ VF2 │ ...   │   │              │
│  │  │  └──┬──┘ └──┬──┘ └──┬──┘       │   │              │
│  │  └─────┼───────┼───────┼──────────┘   │              │
│  └────────┼───────┼───────┼───────────────┘              │
│           │       │       │                               │
│     VF0 → Host#1  VF1 → Host#2  VF2 → Host#1 ...        │
│                                                          │
│  关键特性:                                                │
│  - PCIe Fabric Switch 固件虚拟化 VF 配置空间              │
│  - 将 VF 呈现为标准单功能 NVMe Controller                 │
│  - 各 Host 使用标准 in-box NVMe 驱动                     │
│  - 无需额外软件或自定义驱动                                │
│  - 支持动态 VF 分配/回收 (Composable Infrastructure)      │
│                                                          │
│  应用场景:                                                │
│  - 共享存储池 (Storage Pool)                              │
│  - 动态资源编排 (Resource Composition)                    │
│  - AI/ML 集群的 GPU + NVMe 协同                          │
└──────────────────────────────────────────────────────────┘

9.3 SR-IOV与CXL存储的融合

CXL (Compute Express Link) 时代的存储虚拟化:

┌──────────────────────────────────────────────────────────┐
│                                                          │
│  当前: PCIe SR-IOV                                       │
│  ┌─────────┐    PCIe    ┌──────────┐                    │
│  │  Host   │───────────→│ NVMe SSD │                    │
│  │ (PF/VF) │            │ (SR-IOV) │                    │
│  └─────────┘            └──────────┘                    │
│  限制: NVMe SSD 必须物理插入到 Host 的 PCIe slot         │
│                                                          │
│  未来: CXL + SR-IOV                                     │
│  ┌─────────┐    CXL     ┌──────────┐                    │
│  │  Host   │───────────→│ CXL      │                    │
│  │         │  (Type 3)  │ Switch   │                    │
│  └─────────┘            └────┬─────┘                    │
│                              │                           │
│                    ┌─────────┼─────────┐                 │
│                    │         │         │                 │
│              ┌─────▼──┐ ┌───▼────┐ ┌──▼─────┐          │
│              │CXL NVMe│ │CXL NVMe│ │CXL NVMe│          │
│              │Pool #1 │ │Pool #2 │ │Pool #3 │          │
│              │(SR-IOV)│ │(SR-IOV)│ │(SR-IOV)│          │
│              └────────┘ └────────┘ └────────┘          │
│                                                          │
│  CXL + SR-IOV 的优势:                                    │
│  1. 存储池化: 多块 NVMe 组成共享存储池                    │
│  2. 远端访问: 通过 CXL 内存语义访问远端 NVMe             │
│  3. 动态分配: VF/NS 可按需分配给不同 Host                │
│  4. 缓存一致性: CXL.co/cxl.cache 提供缓存一致性          │
│                                                          │
│  时间线:                                                 │
│  - 2024-2025: CXL 1.1 内存扩展产品 (Samsung, KIOXIA)    │
│  - 2025-2026: CXL 2.0 NVMe 存储产品                     │
│  - 2027+: CXL + SR-IOV 融合方案量产                      │
└──────────────────────────────────────────────────────────┘

十、当日知识点小结

知识点核心内容重要程度
SR-IOV基础PF/VF机制,一个物理设备虚拟化为多个独立PCIe设备⭐⭐⭐⭐⭐
NVMe虚拟化增强NVMe 1.3引入,Primary/Secondary Controller架构⭐⭐⭐⭐⭐
Namespace隔离容量隔离的基本单元,NS Management + Attachment⭐⭐⭐⭐⭐
virt-mgmt命令Opcode 0x1C,分配I/O Queue和中断资源给Secondary Ctrl⭐⭐⭐⭐
IOMMU角色DMA地址翻译与隔离,SR-IOV安全的基础⭐⭐⭐⭐⭐
Linux内核实现sriov_numvfs sysfs接口,pci_enable_sriov API⭐⭐⭐⭐
vfio-pciVF直通VM的关键驱动,BAR映射+IOMMU+MSI-X⭐⭐⭐⭐
五大方案对比Virtio/SPDK/Mdev/SR-IOV/SIOV各有优劣⭐⭐⭐⭐
性能保持率SR-IOV: ~98-100%, SPDK: ~95%, Virtio: ~50%⭐⭐⭐⭐
Multi-HostPCIe Fabric实现跨主机SR-IOV VF共享⭐⭐⭐
CXL融合未来存储池化+SR-IOV的组合方案⭐⭐⭐

十一、思考题

1. 资源超分配问题: 假设一块6.4TB NVMe SSD支持最多64个VF,但物理NAND只有128个Channel。如果要创建64个VF,每个VF至少需要1个Channel来保证基本的并行性。那么64 VF场景下每个VF只能分到2个Channel(128/64=2),而单VF场景可以使用全部128个Channel。请分析:64 VF并发满载时的总吞吐量是否是单VF满载吞吐量的64倍?如果不是,根因是什么?如何优化?

2. 安全性分析: 在SR-IOV环境中,假设一个恶意VM获取了VF的Root权限。请分析:(a) 该VM能否通过VF的MMIO访问到PF的Admin Queue?(b) 该VM能否通过DMA访问其他VF的Namespace数据?© 如果IOMMU配置不当(如passthrough模式),安全风险如何变化?请结合PCIe配置空间和IOMMU DMAR表的结构进行分析。

3. 架构选型决策: 你负责设计一个云服务商的存储架构,需要在一台服务器上为200个租户提供NVMe存储服务。每个租户需要100GB独享容量、至少50K IOPS保证、P99延迟<200μs。可选方案:(A) 使用4块SR-IOV NVMe SSD(每块64 VF),(B) 使用4块普通NVMe SSD + SPDK vhost-NVMe。请从成本、性能保证、可扩展性、运维复杂度四个维度进行量化分析,给出最终推荐方案。


参考资料



作者持续更新中,关注获取每日SSD硬核知识 👆


Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐