文章目录
- [1 Controller Properties](#1 Controller Properties)
- [1.1 如何访问](#1.1 如何访问)
- [1.2 Controller 初始化流程](#1.2 Controller 初始化流程)
- [1.3 CAP - Controller Capabilities (Offset 00h, 64-bit)](#1.3 CAP - Controller Capabilities (Offset 00h, 64-bit))
- [X 面试场景问题](#X 面试场景问题)
- [1 为什么Properties Host必须通过BAR访问,不能通过DMA?](#1 为什么Properties Host必须通过BAR访问,不能通过DMA?)
- [2 如果Host按dword访问qword的CAP会怎样?](#2 如果Host按dword访问qword的CAP会怎样?)
- [3 CC.EN从1→0关闭Controller时,CSTS.RDY多久变0?](#3 CC.EN从1→0关闭Controller时,CSTS.RDY多久变0?)
- [4 IO SQ/CQ的创建细节?](#4 IO SQ/CQ的创建细节?)
1 Controller Properties
NVMe 协议章节 3.1.4 Controller Properties
Controller Properties(如CAP、CC、CSTS) 这些关键的寄存器,和SSD Controller初始化流程相关。
画出初始化状态机流程图?
FTL关注点:
Controller Properties(如CAP、CC、CSTS)通常通过PCIe BAR空间访问
必须按指定宽度(dword/qword)访问,跨Property访问不支持
这意味着:如果你要读取64-bit的CAP(Controller Capabilities),必须一次性读qword,不能分两次读dword
1.1 如何访问
| 层级 | 存储位置 | 说明 |
|---|---|---|
| PCIe BAR空间 | SSD控制器内部的寄存器(Registers) | 物理上在SSD主控芯片内 |
| Host视角 | 映射到Host内存地址空间的MMIO区域 | Host通过内存读写访问 |
Host CPU → PCIe TLP (Memory Read/Write) → SSD Controller BAR → 内部寄存器
关键:Host看到的"内存地址" = PCIe配置空间分配的BAR基地址 + Property Offset
1.2 Controller 初始化流程
Host要把SSD(Controller) 初始化,好让Controller能够开始接收命令
完整的Host初始化Controller的流程状态机如下:
c
┌─────────────────┐
│ 1. 读取CAP │ ← 确认Controller能力
│ (Offset 00h) │
└────────┬────────┘
▼
┌─────────────────┐
│ 2. 配置CC │ ← 设置Command Set, Page Size
│ (Offset 14h) │ 但不Enable (EN=0)
└────────┬────────┘
▼
┌─────────────────┐
│ 3. 配置AQA │ ← Admin Queue深度
│ (Offset 24h) │
└────────┬────────┘
▼
┌─────────────────┐
│ 4. 配置ASQ │ ← Admin SQ基址 (64-bit)
│ (Offset 28h) │
└────────┬────────┘
▼
┌─────────────────┐
│ 5. 配置ACQ │ ← Admin CQ基址 (64-bit)
│ (Offset 30h) │
└────────┬────────┘
▼
┌─────────────────┐
│ 6. 写CC.EN=1 │ ← Enable Controller
│ (Offset 14h) │
└────────┬────────┘
▼
┌─────────────────┐
│ 7. 轮询CSTS │ ← Host轮询寄存器,等待RDY=1
│ (Offset 1Ch) │ 检查CFS=0
└────────┬────────┘
▼
┌─────────────────┐
│ 8. Identify │ ← 发送Admin命令
│ Controller │ 获取设备信息
└────────┬────────┘
▼
┌─────────────────┐
│ 9. Create I/O │ ← Host发送 创建I/O Queue 请求
│ Queues │
└────────┬────────┘
▼
┌─────────────────┐
│ 10. 开始I/O │ ← 发送Read/Write命令
└─────────────────┘
FTL需关注的Properties如下
| 关注点 | 为什么重要 |
|---|---|
| CAP.MPS | 决定PRP List结构,影响数据传输效率 |
| CAP.MQES | 决定Queue深度,影响并发I/O性能 |
| CC.MPS配置 | 必须与Host内存页大小匹配 |
| CSTS.RDY轮询 | 超时处理,防止初始化死锁 |
| Doorbell机制 | Tail Doorbell通知有新命令,Head Doorbell通知已取走完成 |
1.3 CAP - Controller Capabilities (Offset 00h, 64-bit)
CAP是只读寄存器
| 字段 | 位 | 作用 | FTL关注点 |
|---|---|---|---|
| MQES | 15:00 | Max Queue Entries Supported | 决定SQ/CQ最大深度(0-based) |
| CQR | 16 | Contiguous Queues Required | 1=必须用物理连续队列 |
| AMS | 18:17 | Arbitration Mechanism Supported | 0=Round Robin, 1=Weighted RR |
| TO | 23:20 | Timeout | 500ms × (TO+1),初始化超时计算 |
| DSTRD | 31:24 | Doorbell Stride | 每个Doorbell寄存器间距 = 4 << DSTRD |
| NSSRS | 33 | NVM Subsystem Reset Supported | 是否支持NSSR |
| CSS | 44:37 | Command Sets Supported | 支持哪些I/O Command Set |
| MPSMAX/MPSMIN | 52:32 | Memory Page Size Max/Min | 决定PRP/SGL页大小 |
X 面试场景问题
1 为什么Properties Host必须通过BAR访问,不能通过DMA?
Properties是控制器状态/配置寄存器,需要CPU直接访问(低延迟)。DMA用于数据传输(高吞吐)。BAR映射到MMIO空间,CPU可用普通load/store指令访问。
2 如果Host按dword访问qword的CAP会怎样?
未定义行为。Spec要求必须按定义宽度访问。实际可能导致读取错误值或触发错误。
3 CC.EN从1→0关闭Controller时,CSTS.RDY多久变0?
取决于CAP.TO定义的Timeout。Host必须轮询等待,或超时后强制Reset。
疑问,CC.EN从1→0 该行为意味着什么?Controller内部做什么后会把RDY置为0?
4 IO SQ/CQ的创建细节?
在Controller 准备就绪后,Host会给Controller发消息,来创建 IO SQ/CQ。
创建SQ/CQ的 队列数量 和 深度,如何确定的?
Host会通过Identify命令,来获取Controller 支持的 队列数量上限。
Host会通过Set Features的Number Of Queues 属性,来向Controller传递想要的 IO SQ/CQ 数量。
Number Of Queues 是 SSD的一个Feature
NSQ (Number of Submission Queues):主机希望创建的I/O提交队列数量(0值表示1个队列)。
NCQ (Number of Completion Queues):主机希望创建的I/O完成队列数量(0值表示1个队列)。
控制器收到请求后,会评估自己的能力。它不一定能满足Host的所有请求,可能会返回一个它实际能支持的、接近请求值的数量。这个协商结果会通过Set Features命令的完成队列条目返回给Host。之后,Host会读出该结果作为实际分配数量的参考。
获得最终数量后,Host便开始按这个参考结果进行实际的队列创建操作,主要有两种Admin命令:
Create I/O Completion Queue命令:用于逐个创建I/O CQ。命令参数包括指定CQ ID (qid) 和大小 (qsize) 等。
Create I/O Submission Queue命令:用于逐个创建I/O SQ。创建时需指定归属的CQ ID,实现多SQ对一CQ的关联。
典型协商流程
初始匹配阶段:主机首次写入 Set Features (FID=07h),假设要申请 NCQ=63 和
NSQ=63。若控制器最大支持32个I/O队列(0-based返回31),协商后主机会读取 Result 值: Result = (31
<< 16) | 31 (即0x001F001F)。
逐个创建阶段:最终按 31 的返回值,通过 Create I/O Completion Queue 和 Create I/O
Submission Queue 命令逐个创建这些队列。