OCSSD 文档 ============ 因为 ocssd 文档非常少,为了方便同学们完成实验,提供一些实验资料如下。 参考资料 ------------ ssdfans 可可读 ocssd 系列: #. `可可读 OpenChannelSSD 之一 简介 `_ #. `可可读 OpenChannelSSD 之二 PPA 接口 `_ #. `可可读 OpenChannelSSD 之三 LightNVM 子系统 `_ #. `可可读 OpenChannelSSD 之四 pblk `_ #. `可可读 OpenChannelSSD 之五 Virtual OCSSD 实验环境搭建英文 `_ #. `可可读 OpenChannelSSD 之六 从 OpenChannel SSD 到 ZNS `_ ocssd 标准: #. `Open-Channel Solid State Drives Specification Revision 2.0 `_ #. `Open-Channel Solid State Drives NVMe Specification Revision 1.2 (已过时) `_ ocssd 原始论文: #. `LightNVM: The Linux Open-Channel SSD Subsystem `_ #. `The case of FEMU: cheap, accurate, scalable and extensible flash emulator `_ OCSSD 结构 ------------ .. image:: /_static/4-ocssd-docs/ocssd-channel-pu.png :align: center :alt: channel 和 pu 对应关系 #. 存储通道(Channel、Group):可以并行发送数据和接受命令的通道 #. 存储芯片(PU、LUN):可以被片选的一块儿芯片,多个芯片物理上挂载到一个通道上,但同一时间一个通道 上只能片选一个芯片 #. 可清除块(Chunk):可以被清除的最小单元 #. 可编程块(Block):可以被编程或读取的最小单元 注:Chunk 和 Block 在不同的文献中定义不明确,即使是英文文献也有混淆的情况,此处的定义遵循 OCSSD 2.0 标准,和 pblk 代码保持对应。 举例而言 .. highlight:: bash :: qemu-img create -f ocssd -o num_grp=4,num_pu=1,num_chk=4096,num_sec=32 ocssd.img 这段代码生成了参数如下的 ocssd: #. 整个 SSD 有 4 个 group #. 每个 group,挂 1 个 PU #. 每个 PU 下,有 4096 个 chunk #. 每个 chunk 下,有 32 个 sector #. 每个 sector 默认为 4KiB 因此总大小为 :code:`4 * 4096 * 32 * 4 KiB = 2 GiB`,检查发现该镜像大小基本符合。 pblk 介绍 ------------ pblk 是在 LightNVM 上实现的一个主机 FTL 层,FTL 原本是 SSD 主控中的一个模块,负责完成: #. 逻辑地址到物理地址的转换,保证元数据的完整性 #. 逻辑块的分配,保证存取效率,例如将连续的逻辑块条带化到不同的通道 #. 实现缓存,对主机的逻辑块写入进行缓冲,调整落盘时机,减少不必要的写入量 #. 处理 Flush 操作,文件系统可能要求一些 IO 操作必须落盘才能继续执行(比如实现事务性) #. 处理写入均衡,SSD 中每个存储单元都有固有寿命,在若干个 PE 循环后会失效损坏,因此需要尽可能 的平均地使用物理单元。 .. image:: /_static/4-ocssd-docs/ocssd-path.png :align: center :alt: pblk 中的几个关键代码路径 pblk 中的几个代码路径: * 对于上层的读请求: #. 需要先查询是否命中写缓冲,如果命中的话,直接从内存里的写缓冲中读取刚刚写入的数据块 #. 如果未命中写缓冲,查询转换表(L2P Table),得到物理地址,发送到设备层去读取相应的物理数据块 #. 需要注意的是,操作系统在 pblk 之上实现了 buffer/cache 机制,因此在 pblk 中不需要实现读缓存 * 对于上层的写请求: #. 检查是否命中写缓冲,如果命中,更新写缓冲中的数据 #. 如果未命中写缓冲,则需要分配写缓冲,如果此时没有空闲的缓冲行,则挂起发起写请求的 OS 线程 #. 如果有空闲的缓冲行,分配并写入缓冲 * GC 线程: #. 在 pblk 中有若干 gc 线程用来选择需要被写入的缓冲行,进行 GC 的策略如下 #. 首先选择没有有效存储块的缓冲行,这些缓冲行可以被快速回收 #. 接着选择过去发生写入错误的缓冲行,这些缓冲行需要尽快被重试 #. 最后按照缓冲行中有效存储块的数量,从少到多选择缓冲行 #. 在选好了一个需要被 GC 的缓冲行之后,GC 模块会读取缓冲行中的其它存储块,补齐一个整行,接着将这个行落盘