深入浅出SSD


深入浅出SSD

认识SSD

  • 什么是SSD:
    SSD是一种以半导体为主要存储介质、外形和数据传输接口与传统的HDD相同的存储产品。目前主流SSD使用一种叫闪存的存储介质,未来随着存储半导体芯片技术的发展,它也可以使用更快、更可靠、更省电的新介质,例如3D XPoint、MRAM等。SSD是用固态电子存储芯片阵列制成的硬盘,主要部件为控制器和存储芯片,内部构造十分简单。详细来看,SSD硬件包括几大组成部分:主控、闪存、缓存芯片DRAM(可选,有些SSD上可能只有SRAM,并没有配置DRAM)、PCB(电源芯片、电阻、电容等)、接口(SATA、SAS、PCIe等)。软件角度,SSD内部运行固件(Firmware, FW)负责调度数据从接口端到介质端的读写,还包括嵌入核心的闪存介质寿命和可靠性管理调度算法,以及其他一些SSD内部算法。
  • SSD VS HDD:
    传统的HDD是“马达+磁头+磁盘”的机械结构,SSD则变成了“闪存介质+主控”的半导体存储芯片结构,两者有完全不同的数据存储介质和读写方式。表现则为性能高,功耗低,抗震防摔,无噪音,体积小。
  • SSD发展:
    ①昂贵的RAM SSD时代
    ②机械硬盘(HDD)称霸世界
    ③闪存出现
    ④闪存SSD异军突起
    ⑤SSD革命性进步(速度与容量,市场开始繁荣)
    ⑥PCIe SSD进入消费者市场
  • SSD基本工作原理:
    从主机PC端开始,用户从操作系统应用层面对SSD发出请求,文件系统将读写请求经驱动转化为相应的符合协议的读写和其他命令,SSD收到命令执行相应操作,然后输出结果,每个命令的输入和输出经协议标准组织标准化。三大模块如下:
  • ❏ 前端接口和相关的协议模块;
    ❏ 中间的FTL层(Flash Translation Layer)模块;
    ❏ 后端和闪存通信模块。
  • SSD读写过程:
    以写为例。主机通过接口发送写命令给SSD, SSD接收到该命令后执行,并接收主机要写入的数据。数据一般会先缓存在SSD内部的RAM中,FTL会为每个逻辑数据块分配一个闪存地址,当数据凑到一定数量后,FTL便会发送写闪存请求给后端,然后后端根据写请求,把缓存中的数据写到对应的闪存空间。由于闪存不能覆盖写,闪存块需擦除才能写入。主机发来的某个数据块,它不是写在闪存固定位置,SSD可以为其分配任何可能的闪存空间写入。因此,SSD内部需要FTL这样一个东西,完成逻辑数据块到闪存物理空间的转换或者映射。举个例子,假设SSD容量为128GB,逻辑数据块大小为4KB,所以该SSD一共有128GB/4KB=32M个逻辑数据块。每个逻辑块都有一个映射,即每个逻辑块在闪存空间都有一个存储位置。闪存地址大小如果用4字节表示,那么存储32M个逻辑数据块在闪存中的地址则需要32M×4B=128MB大小的映射表。正因为SSD内部维护了一张逻辑地址到物理地址转换的映射表,当主机发来读命令时,SSD能根据需要读取的逻辑数据块查找该映射表,获取这些逻辑数据在闪存空间所在的位置,后端便能从闪存上把对应数据读到SSD内部缓存空间,然后前端负责把这些数据返回给主机。由于前端接口协议都是标准化的,后端和闪存的接口及操作也是标准化的(闪存遵循ONFI或者Toggle协议),因此,一个SSD在前端协议及闪存确定下来后,差异化就体现在FTL算法上了。FTL算法决定了性能、可靠性、功耗等SSD的核心参数。还需要实现坏块管理、读干扰处理、数据保持处理、错误处理等很多其他事情。
  • SSD基本参数:
    基本信息,性能指标,数据可靠性和寿命,功耗,兼容性。
    容量:
    以二进制为单位的容量行业内称为裸容量,以十进制为单位的容量称为用户容量。对于闪存本身,它是裸容量。那么,裸容量多出的7%容量在SSD内部做什么用呢?SSD可以利用这多出来的7%空间管理和存储内部数据,比如把这部分额外的空间用作FTL映射表存储空间、垃圾回收所需的预留交换空间、闪存坏块的替代空间等。这里的7%多余空间也可以转换为OP概念(Over Provisioning)。
    介质:
  • IOPS:
    单位IOPS,即设备每秒完成IO请求数,一般是小块数据读写命令的响应次数,比如4KB数据块尺寸。IOPS数字越大越好。
    吞吐量:
    单位MB/s,即每秒读写命令完成的数据传输量,也叫带宽(Bandwidth),一般是大块数据读写命令,比如512KB数据块尺寸。吞吐量越大越好。
    响应时间:
    也叫时延(Latency),即每个命令从发出到收到状态回复所需要的响应时间,时延指标有平均时延(Average Latency)和最大时延两项(Max Latency)。响应时间越小越好。
    性能测试:
    性能测试设计上要考虑访问模式(Access Pattern),包括以下三部分:❏ Random/Sequential:随机(Random)和连续(Sequential)数据命令请求。何为随机和连续?指的是前后两条命令LBA地址是不是连续的,连续的地址称为Sequential,不连续的地址称为Random。❏ Block Size:块大小,即单条命令传输的数据大小,性能测试从4KB~512KB不等。随机测试一般用小数据块,比如4KB;顺序测试一般用大块数据,比如512KB。❏ Read/Write Ratio:读写命令数混合的比例。

    SSD主控

  • 主控结构:
    这款主控采用ARM CPU,主要分为前端和后端两大部分。前端(Host Interface Controller,主机接口控制器)跟主机打交道,接口可以是SATA、PCIe、SAS等。后端(Flash Controller,闪存控制器)跟闪存打交道并完成数据编解码和ECC。除此之外还有缓冲(Buffer)、DRAM。模块之间通过AXI高速和APB低速总线互联互通,完成信息和数据的通信。在此基础之上,由SSD固件开发者构筑固件(Firmware)统一完成SSD产品所需要的功能,调度各个硬件模块,完成数据从主机端到闪存端的写入和读取。
  • 前端:
    前端是负责主机和SSD设备通信的接口,命令和数据传输通过前端总线流向或流出SSD设备。主机接口则是与主机进行通信(数据交互)的标准协议接口,当前主要代表为SATA、SAS和PCIe等
  • 主控CPU:
    SSD控制器SoC模块和其他嵌入式系统SoC模块并没有什么本质的不同,一般由一颗或多颗CPU核组成,同时片上有I-RAM、D-RAM、PLL、IO、UART、高低速总线等外围电路模块。CPU负责运算、系统调度,IO完成必要的输入输出,总线连接前后端模块。通常我们所说的固件就运行在CPU核上,分别有代码存储区I-RAM和数据存储区D-RAM。如果是多核CPU,需要注意的是软件可以是对称多处理(SMP)和非对称多处理(AMP)。对称多处理多核共享OS和同一份执行代码,非对称多处理是多核分别执行不同代码。前者多核共享一份I-RAM和D-RAM,资源共享;后者每核对应一份I-RAM和D-RAM,每核独立运行,没有内存抢占导致代码速度执行变慢的问题。当SSD的CPU要求计算能力更高时,除增加核数和单核CPU频率外,AMP的设计方式更加适应计算和任务独立的要求,消除了代码和数据资源抢占导致执行速度过慢的问题。固件根据CPU的核数进行设计,充分发挥多核CPU的计算能力是固件设计考虑的一方面。另外,固件会考虑任务划分,会将任务分别加载到不同CPU上执行,在达到并行处理的同时让所有CPU有着合理且均衡的负载,不至于有的CPU忙死有的CPU闲死,这是固件架构设计要考虑的重要内容,目标是让SSD输出最大的读写性能。
  • 后端:
    后端两大模块分别为ECC模块和闪存控制器。
    ECC模块是数据编解码单元,由于闪存存储天生存在误码率,为了数据的正确性,在数据写入操作时应给原数据加入ECC校验保护,这是一个编码过程。
    闪存控制器使用符合闪存ONFI 、Toggle标准的闪存命令,负责管理数据从缓存到闪存的读取和写入。从单个闪存角度看,一个Die/LUN是一个闪存命令执行的基本单元,为了性能需求需要并发多个闪存Die/LUN,通常配置有多个通道(channel)。一个通道挂多少个闪存Die/LUN,取决于SSD容量和性能需求,Die/LUN个数越多,并发的个数越多,性能越好。

    SSD存储介质:闪存

  • 闪存:
    闪存是一种非易失性存储器,也就是说,掉电了数据也不会丢失。闪存基本存储单元(Cell)是一种类NMOS的双层浮栅(Floating Gate)MOS管。
  • 闪存芯片架构:
    闪存芯片就是由成千上万这样的存储单元按照一定的组织结构组成的(STL,MLC,TLC)。
  • 一个LUN又分为若干个Plane,市面上常见的是1个或者2个Plane,现在也有4个Plane的闪存了。每个Plane都有自己独立的Cache Register和Page Register,其大小等于一个Page的大小。固态硬盘主控在写某个Page的时候,先把数据从主控传输到该Page所对应Plane的Cache Register当中,然后再把整个Cache Register当中的数据写到闪存阵列;读的时候则相反,先把这个Page的数据从闪存介质读取到Cache Register,然后再按需传给主控。这里按需是什么意思?就是我们读取数据的时候,没有必要把整个Page的数据都传给主控,而是按需选择数据传输。但要记住,无论是从闪存介质读数据到Cache Register,还是把Cache Register的数据写入闪存介质,都以Page为单位。通过两个register,multi-Plane等操作来优化闪存访问速度。
  • 闪存寻址问题:
  • 闪存问题:
    ①坏块:
    闪存块(Block)具有一定的寿命,不是长生不老的。前面提到,当一个闪存块接近或者超出其最大擦写次数时,可能导致存储单元永久性损伤,不能再使用。随着闪存工艺不断向前发展,晶体管的尺寸越来越小,擦写次数也变得越来越少。闪存中的存储单元先天就有一些是坏的,或者说是不稳定的。并且随着闪存的不断使用,坏的存储单元越来越多。所以,用户写入闪存的数据,必须要有ECC纠错码保护,这样即使其中的一些比特发生反转,读取的时候也能通过ECC纠正过来。但若出错的比特数超过纠错能力范围,数据就会丢失,对这样的闪存块,我们应该弃之不再使用。
    ②读干扰:
    从闪存读取原理来看,当你读取一个闪存页(Page)的时候,闪存块当中未被选取的闪存页的控制极都会加一个正电压,以保证未被选中的MOS管是导通的。这样问题就来了,频繁地在一个MOS管控制极加正电压,就可能导致电子被吸进浮栅极,形成轻微写,从而最终导致比特翻转。但是,这不是永久性损伤,重新擦除闪存块还能正常使用。要注意的是,读干扰影响的是同一个闪存块中的其他闪存页,而非读取的闪存页本身。
    ③写干扰:
    同上述读干扰
    ④存储单元耦合:
    浮栅极闪存存储电荷的是导体,因此存储单元之间存在耦合电容,这会使存储单元内的电荷发生意外变化,最终导致数据读取错误。
    ⑤电荷泄漏:
    存储在闪存存储单元的电荷,如果长期不使用,会发生电荷泄漏。这同样会导致非永久性损伤,擦除后闪存块还能使用。
  • 闪存寿命:
    ❏ Wear Leveling:通过磨损平衡算法,让所有的闪存块均衡擦写,避免少数闪存块先挂掉,导致固态硬盘容量下降。❏ 降低写放大:写放大越低,固态硬盘的磨损速度越慢。
    ❏ 用更好的纠错算法:纠错能力越强,容许的出错率越高,故采用更好的纠错算法可以延长硬盘使用寿命。
  • 闪存数据完整性:
    闪存的一个特性就是随着闪存的使用以及数据存储时间的变长,存储在闪存里面的数据容易发生比特翻转,出现随机性错误。这个问题随着闪存制程的变小越发严重。因此,使用闪存作为存储介质的固态硬盘,需要采用一些数据完整性的技术来确保用户数据可靠不丢失。常见的技术有:❏ ECC纠错。❏ RAID数据恢复。❏ 重读(Read Retry)。❏ 扫描重写技术(Read Scrub)。❏ 数据随机化。

    SSD核心技术:FTL

  • FTL概念:
    FTL算法的优劣与否,直接决定了SSD在性能(Performance)、可靠性(Reliability)、耐用性(Endurance)等方面的好坏,FTL可以说是SSD固件的核心组成。那么什么是FTL?FTL是Flash Translation Layer(闪存转换层)的缩写,完成主机(或者用户,Host)逻辑地址空间到闪存(Flash)物理地址空间的翻译(Translation),或者说映射(Mapping)。SSD每把一笔用户逻辑数据写入闪存地址空间,便记录下该逻辑地址到物理地址的映射关系。当主机想读取该数据时,SSD便会根据这个映射,从闪存读取这笔数据然后返回给用户。还需要做垃圾回收,磨损平衡,处理读干扰,数据保持,坏块管理,处理MLC或TLC的Lower Page corruption问题,改善SSD性能和可靠性。
  • FTL映射管理:
    存在基于页的映射,也有混合映射。
  • 映射原理:
    用户通过LBA(Logical Block Address,逻辑块地址)访问SSD,每个LBA代表着一个逻辑块(大小一般为512B/4KB/8KB……),我们把用户访问SSD的基本单元称为逻辑页(Logical Page)。而在SSD内部,SSD主控是以闪存页为基本单元读写闪存的,我们称闪存页为物理页(Physical Page)。用户每写入一个数据页,SSD主控就会找一个物理页把用户数据写入,SSD内部同时记录了这样一条映射(Map)。有了这样一个映射关系后,下次用户需要读某个逻辑页时,SSD就知道从闪存的哪个位置把数据读取上来。
    SSD内部维护了一张逻辑页到物理页地址转换的映射表(Map Table)。用户每写入一个逻辑页,就会产生一个新的映射关系,这个映射关系会加入(第一次写)或者更改(覆盖写)映射表。当读取某个逻辑页时,SSD首先查找映射表中该逻辑页对应的物理页,然后再访问闪存读取相应的用户数据。
    对于绝大多数SSD,我们可以看到上面都有板载DRAM,其主要作用就是存储这张映射表,如图4-7所示。在SSD工作时,全部或绝大部分的映射表都可以放在DRAM上,映射关系可以快速访问。
    SSD工作时,对带DRAM的SSD来说,只需要查找DRAM当中的映射表,获取到物理地址后访问闪存便会得到用户数据,这期间只需要访问一次闪存。而对不带DRAM的SSD来说,它首先会查看该逻辑页对应的映射关系是否在SRAM内:如果在,直接根据映射关系读取闪存;如果不在,那么它首先需要把映射关系从闪存中读取出来,然后再根据这个映射关系读取用户数据,这就意味着相比于有DRAM的SSD,它需要读取两次闪存才能把用户数据读取出来,底层有效带宽减小。
    映射表除了可以放在板载DRAM、SRAM和闪存中,它还可以放到主机的内存中。NVME1.2(及后续版本)有个重要的功能就是HMB(Host Memory Buffer,主机高速缓冲存储器):主机在内存中专门划出一部分空间给SSD用,SSD可以把它当成自己的DRAM使用。因此,映射表完全可以放到主机端的内存中去。
    映射表刷新:
    映射表在SSD掉电前,是需要把它写入到闪存中去的。下次上电初始化时,需要把它从闪存中部分或全部加载到SSD的缓存(DRAM或者SRAM)中。随着SSD的写入,缓存中的映射表不断增加新的映射关系,为防止异常掉电导致这些新的映射关系丢失,SSD的固件不仅仅只在正常掉电前把这些映射关系刷新到闪存中去,而是在SSD运行过程中,按照一定策略把映射表写进闪存。这样,即使发生异常掉电,丢失的也只是一小部分映射关系,上电时可以较快地重建这些映射关系。那么,什么时候会触发映射表的写入呢?一般有以下几种情况:❏ 新产生的映射关系累积到一定的阈值❏ 用户写入的数据量达到一定的阈值❏ 闪存写完闪存块的数量达到一定的阈值❏ 其他写入策略一般有:❏ 全部更新❏ 增量更新全部更新表示的是缓存中映射表(干净的和不干净的)全部写入到闪存,增量更新的意思是只把新产生的(不干净的)映射关系刷入到闪存中去。显然,相比后者,前者需要写入更多的数据量,一方面影响用户写入性能和时延(latency),另一方面增加写放大,但其好处是固件实现简单,不需要去知道哪些映射关系是干净的,哪些是不干净的。
  • 垃圾回收:
    原理:
  • 写满之后,继续写入时SSD会把新写入的逻辑页写入到所谓的OP空间。对SSD来说,不存在什么用户空间和OP空间,它只会看到闪存空间。主机端来数据,SSD就往闪存空间写。图4-17中出现了深色方块,怎么回事?因为逻辑页1~4的数据已更新,写到新的地方,那么之前那个位置上的逻辑页1~4数据就失效了,过期了,变垃圾了。用户更新数据,由于闪存不能在原位置覆盖写,固件只能另找闪存空间写入新的数据,因此导致原闪存空间数据过期,形成垃圾。
  • 等所有Die上的Block 5写满后,所有Die上的Block 0也全部变色了(这些数据都是垃圾)。现在不仅整个用户空间都写满,整个闪存空间也都满了。如果用户想继续写入后续的逻辑页(36之后的),就需要垃圾回收了。
  • 用户如果继续往SSD上写入数据,那么SSD怎么处理?当然需要做垃圾回收。不过,SSD内部状况比之前看到的复杂多了,垃圾数据分散在每个闪存块上,而不是集中在某几个闪存块上。这个时候,如何挑选需要回收的闪存块呢?答案显而易见,挑垃圾比较多的闪存块来回收,因为有效数据少,要搬移的数据少,这样腾出空闪存块的速度快。对上面每个闪存块的垃圾数(深色方块)做个统计.
  • 有了空闲的空间(白色方块),用户就可以继续写入数据了。
  • 写放大:
  • 写放大越大,意味着额外写入闪存的数据越多,一方面磨损闪存,减少SSD寿命,另一方面,写入这些额外数据会占用底层闪存带宽,影响SSD性能。因此,SSD设计的一个目标是让WA尽量小。减小写放大,可以使用前面提到的压缩办法(主控决定),顺序写也可以减小写放大(垃圾集中,但顺序写可遇不可求,取决于用户Workload),还有就是增大OP(这个可控)。
    影响写放大的因素主要有:❏ OP:OP越大,WA越小。❏ 用户写入的数据Pattern:如前文所见,如果数据都是顺序写入,GC做的量就少(最好的情况是整个闪存块都是无效数据,只需擦除,无需数据搬移),写放大小。❏ GC策略:在挑选源闪存块的时候,如果不挑选有效数据最少(垃圾数据最多)的块作为源闪存块,就会增加写放大;另外,控制后台GC产生空闲闪存块的数量,也能减小写放大。❏ 磨损平衡:为平衡每个闪存块的擦除次数,需要数据的搬移。❏ 读干扰(Read disturb)和数据保存处理(Data Retention handling):数据搬移增加写放大。❏ 主控:带压缩和不带压缩的控制器肯定会影响写放大。❏ Trim:有没有Trim,对写放大影响很大。
  • 垃圾回收实现:
    垃圾回收可以简单地分为三步:1)挑选源闪存块。2)从源闪存块中找有效数据。3)把有效数据写入到目标闪存块。
    ①挑选源闪存块,一个常见的算法就是挑选有效数据最小的块,这样需要重写的有效数据就越少,写放大自然最小,回收一个块付出的代价也最小。那么,Die中那么多闪存块,怎么就能一下子找到有效数据最小的那个块呢?这需要固件在写用户数据时做一些额外的工作,即记录和维护每个用户闪存块的有效数据量。用户每往一个新的块上写入一笔用户数据,该闪存块上的有效数据数就加1。同时还需要找到这笔数据之前所在的块(如果之前该笔数据曾写入过),由于该笔数据写入到新的块,那么在原闪存块上的数据就变无效了,因此原闪存块上的有效数据量应该减1。
    由于固件维护了每个闪存块的有效数据量,因此在GC的时候能快速找到有效数据最少的那个块。挑选有效数据最少的那个块作为源闪存块,这种BPA算法叫作Greedy算法,是绝大多数SSD采用的一种策略。除此之外,还有其他的BPA算法。比如,除了基于闪存块有效数据量,有些SSD在挑选源闪存块时,还把闪存块的擦写次数考虑进去了,这其实暗藏着磨损平衡算法(后面会详细介绍)。挑选闪存块时,一方面,我们希望挑有效数据最少的(快速得到一个新的闪存块);另一方面,我们期望挑选擦写次数最小的(分摊擦写次数到每个闪存块)。如果两者都具备,那最好不过了。但现实是,擦写次数最小的闪存块,有效数据未必最少;有效数据最少的闪存块,擦写次数未必最小。因此,需要给有效数据和擦写次数设定一个权重因子,进而得到一个最优的选择。这种方法的好处是可以把磨损平衡算法做到GC中来,可以不需要额外的磨损平衡算法;缺点是相对单纯只看有效数据策略的GC,由于挑选的闪存块可能有效数据很多,因此写放大变大,GC性能变差。
    ②第二步就是把数据从源闪存块读出来。这里也是有讲究的,怎么读才是最有效率的?全部读出来还是只读有效数据?有人说,当然只读有效数据更有效率了,毕竟我们只需重写有效数据。我赞同这个观点,但问题来了,一个闪存块有那么多逻辑页数据,如何知道哪些数据是有效,哪些又是无效的呢?如果固件不仅仅只更新和维护闪存块的有效数据量,还给闪存块一个Bitmap表,标识哪个物理页(例子中我们假设逻辑页和闪存页大小一样)是否有效,那么在做GC的时候,固件只需根据Bitmap表的信息,把有效数据读出,然后重写即可。具体做法跟前面介绍的类似,即固件把一笔逻辑页写入到某个闪存块时,该闪存块上对应位置的Bit就置成1。一个闪存块上新增一笔有效数据,就意味着该笔数据所在的前一个闪存块上数据变成无效,因此需要把前一个闪存块对应的位置的Bit清0。
  • 垃圾回收时机:
    SSD什么时候做GC?当用户写入数据时,如果可用的闪存块小于一定阈值,这时就需要做GC,以腾出空间给用户写。这时做的GC,叫作Foreground GC(前台垃圾回收)。这是被动方式,它是由于SSD没有多少可用的闪存块时,才去做的GC。与之相对应的,就是Background GC(后台垃圾回收),它是在SSD空闲(Idle)的时候,SSD主动去做的GC,这样在用户写入的时候就有充裕的可用闪存块,不需要临时抱佛脚(做Foreground GC),从而改善用户写入性能。但是,出于功耗考虑,有些SSD可能就不做后台垃圾回收了,当SSD空闲后,直接进入省电模式,或者做少量的GC,然后进入省电模式。
  • Trim:
    Trim是一个新增的ATA命令(Data Set Management),专为SSD而生。当用户删除一个文件时,操作系统(对Windows来说,它自Windows 7开始支持Trim)会发Trim命令给SSD,告诉SSD该文件对应的数据无效了。一旦SSD知道哪些数据无效之后,在做垃圾回收的时候就可以把这些删除掉的数据抛弃掉,不做无谓的数据搬移。这样不仅增强了SSD的性能,还延长了SSD寿命。
  • 磨损平衡:
    就是让SSD中的每个闪存块的磨损(擦除)都保持均衡。为什么要做磨损平衡?原因是闪存都是有寿命的,即闪存块有擦写次数限制。一个闪存块,如果其擦写次数超过一定的值,那么该块就变得不那么可靠了,甚至变成坏块不能用了。如果不做磨损平衡,则有可能出现有些闪存块频繁拿来做擦写,这些闪存块很容易就会寿终正寝。随着不断的写入,越来越多的坏块出现,最后导致SSD在保质期前就挂掉。相反,如果让所有闪存块一起来承担,则能经受更多的用户数据写入。
    首先明确四个概念:冷数据(Cold Data)和热数据(Hot Data),年老的(Old)块和年轻的(Young)块。所谓冷数据,就是用户不经常更新的数据,比如用户写入SSD的操作系统数据、只读文件数据、小电影等;相反,热数据就是用户更新频繁的数据。数据的频繁更新,会在SSD内部产生很多垃圾数据(新的数据写入导致老数据失效)。所谓年老的块,就是擦写次数比较多的闪存块;擦写次数比较少的闪存块,年纪相对小,我们叫它年轻的块。SSD很容易区分年老的块和年轻的块,看它们的EC(Erase Count,擦除次数)就可以了,大的就是老的,小的就是年轻的。
    SSD一般有动态磨损平衡(Dynamic WL)和静态磨损平衡(Static WL)两种算法。动态磨损平衡算法的基本思想是把热数据写到年轻的块上,即在拿一个新的闪存块用来写的时候,挑选擦写次数小的;静态磨损平衡算法基本思想是把冷数据写到年老的块上,即把冷数据搬到擦写次数比较多的闪存块上。
  • 掉电恢复:
    先说正常掉电。在掉电前,主机会通过命令通知SSD,比如SATA中的Idle Immediately, SSD收到该命令后,主要会做以下事情:❏ 把buffer中缓存的用户数据刷入闪存。❏ 把映射表刷入闪存。❏ 把闪存的块信息写入闪存(比如当前写的是哪个闪存块,以及写到该闪存块的哪个位置,哪些闪存块已经写过,哪些闪存块又是无效的等)。❏ 把SSD其他信息写入闪存。
    异常掉电会导致用户数据缓存丢失,以及RAM中映射表丢失。
  • 以图4-44为例,如果我们读取物理地址Pa x,就能读取到元数据x和用户数据x,而元数据是有逻辑地址La x的,因此,我们就能获得映射:La x→Pa x。映射表的恢复原理其实很简单,只要全盘扫描整个闪存空间,就能获得所有的映射关系,最终完成整个映射表的重构。全盘扫描有一个问题,就是映射表恢复很慢,所耗的时间与SSD容量成正比。现在SSD容量已达到TB级别,全盘扫描映射方式,重构映射表需要花费几分钟甚至几十分钟,这在实际使用中,用户是不能接受的。那SSD内部是如何快速恢复映射表的呢?一种办法就是SSD定期把SSD中RAM的数据(包括映射表和缓存的用户数据)和SSD相关的状态信息(诸如闪存块擦写次数、闪存块读次数、闪存块其他信息等)写入到闪存中去,与正常掉电前SSD要做的事情类似,这个操作我们称之为做Checkpoint(检查点,此处译成“快照”更合适)
  • 坏块管理:
    坏块来源主要包括:❏ 出厂坏块(Factory Bad Block):闪存从工厂出来,就或多或少的有一些坏块。❏ 增长坏块(Grown Bad Block):随着闪存的使用,一些初期好块也会变成坏块。变坏的原因,主要是擦写磨损。
  • 坏块管理策略:
    ①略过策略:
  • ②替换策略:
    与略过策略不同,当某个Die上发现坏块时,它会被该Die上的某个好块替换。用户在写数据的时候,不是跨过这个Die,而是写到替换块上面去。采用此策略,除正常用户使用的闪存块,还需额外保留一部分好的闪存块,用于替换用户空间的坏块。整个Die上闪存块就划分为两个区域:用户空间和预留空间。还是以上面的情况为例:用户写入数据时,当碰到坏块B,它不会略过Die 1不写,而是写入到Block B的替换者Block B′上面去。采用替换策略,SSD内部需维护一张重映射表(Remap Table):坏块到替换块的映射,比如图4-51的B→B′。当SSD需要访问Block B时,它需要查找重映射表,实际访问的物理Block应该是B′。
  • SLC cache:
    使用SLC Cache的出发点,主要有以下几点:1)性能考虑:SLC性能好,用户数据写到SLC比直接写到MLC或者TLC上快很多。2)防止Lower Page数据被带坏:用户数据写到SLC,不存在写Upper Page或者Extra Page带坏Lower Page数据的可能。3)解决闪存的缺陷:比如有些MLC或者TLC的闪存块,如果没有写满,然后去读的话,可能会读到ECC错误,而对SLC模式下的闪存块,就没有这个问题。4)更多的数据写入量:SLC更耐写。
  • Host Based FTL和Device Based FTL架构比较:

PCIe

NVMe介绍

  • AHCI到NVMe:
    HDD和早期的SSD绝大多数都是使用SATA接口,跑的是AHCI(Advanced Host Controller Interface),它是由Intel联合多家公司研发的系统接口标准。AHCI支持NCQ(Native Command Queuing)功能和热插拔技术。NCQ最大深度为32,即主机最多可以发32条命令给HDD或者SSD执行,跟之前硬盘只能逐条命令执行相比,硬盘性能大幅提升。在HDD时代或者SSD早期,AHCI协议和SATA接口足够满足系统性能需求,因为整个系统的性能瓶颈在硬盘端(低速,高延时),而不是在协议和接口端。然而,随着SSD技术的飞速发展,SSD盘的性能飙升,底层闪存带宽越来越宽,介质访问延时越来越低,系统性能瓶颈已经由下转移到上面的接口和协议处了。AHCI和SATA已经不能满足高性能和低延时SSD的需求,因此SSD迫切需要自己更快、更高效的协议和接口。
    何为NVMe?NVMe即Non-Volatile Memory Express,是非易失性存储器标准,是跑在PCIe接口上的协议标准。NVMe的设计之初就有充分利用了PCIe SSD的低延时以及并行性,还有当代处理器、平台与应用的并行性。相比现在的AHCI标准,NVMe标准可以带来多方面的性能提升。NVMe为SSD而生,但不局限于以闪存为媒介的SSD,它同样可以应用在高性能和低延时的3D XPoint这类新型的介质上。
    相比下来,NVMe优点在于:
    ①低时延:
    ❏ 存储介质层面,闪存(Flash)比传统机械硬盘速度快太多了。
    ❏ 控制器方面,从SATA SSD发展成PCIe SSD,原生PCIe主控与CPU直接相连,而不像传统方式,要通过南桥控制器中转再连接CPU,因此基于PCIe的SSD时延更低。
    ❏ 软件接口方面,NVMe缩短了CPU到SSD的指令路径,比如NVMe减少了对寄存器的访问次数;使用了MSI-X中断管理;并行&多线程优化——NVMe减少了各个CPU核之间的锁同步操作等。
    ②高性能(Throughput & IOPS):
    理论上,IOPS=队列深度/ IO延迟,故IOPS的性能与队列深度有较大的关系(但IOPS并不与队列深度成正比,因为实际应用中,随着队列深度的增加,IO延迟也会提高)。市面上性能不错的SATA接口SSD,在队列深度上都可以达到32,然而这也是AHCI所能做到的极限。但目前高端的企业级PCIe SSD,其队列深度可能要达到128,甚至是256才能够发挥出最高的IOPS性能。而在NVMe标准下,最大的队列深度可达64K。此外,NVMe的队列数量也从AHCI的1,提高到了64K。PCIe接口本身在性能上碾压SATA,再加上NVMe具有比AHCI更深、更宽的命令队列,NVMe SSD在性能上秒杀SATA SSD是水到渠成的事情。
    ③低功耗
  • NVMe综述:
    NVMe是一种主机(Host)与SSD之间通信的协议,它在协议栈中隶属高层。
  • NVMe在协议栈中处于应用层或者命令层,NVMe所制定的任何命令,都交由PCIe去完成。虽然NVMe的命令也可以由别的接口完成,但NVMe与PCIe合作效果最佳。
  • NVMe三宝:
    Submission Queue(SQ)、Completion Queue(CQ)和Doorbell Register(DB)。SQ和CQ位于主机的内存中,DB则位于SSD的控制器内部。
  • SSD作为一个PCIe Endpoint(EP)通过PCIe连着Root Complex(RC),然后RC连接着CPU和内存(RC是CPU的代言人)。
    SQ位于主机内存中,主机要发送命令时,先把准备好的命令放在SQ中,然后通知SSD来取;CQ也是位于主机内存中,一个命令执行完成,成功或失败,SSD总会往CQ中写入命令完成状态。DB又是干什么用的呢?主机发送命令时,不是直接往SSD中发送命令,而是把命令准备好放在自己的内存中,那怎么通知SSD来获取命令执行呢?主机就是通过写SSD端的DB来告知SSD的。
  • SQ、CQ和DB:
    SQ与CQ的关系,可以是一对一的关系,也可以是多对一的关系。有两种SQ和CQ,一种是Admin,另外一种是IO,前者放Admin命令,用以主机管理控制SSD,后者放置IO命令,用以主机与SSD之间传输数据,系统中只有1对Admin SQ/CQ,它们是一一对应的关系;IO SQ/CQ却可以有很多
  • 主机端每个CPU核(Core)为了提升性能和服务质量可以有一个或者多个SQ,但只有一个CQ。实际系统中用多少个SQ,取决于系统配置和性能需求,可灵活设置I/O SQ个数。
    作为队列,每个SQ和CQ都有一定的深度:对Admin SQ/CQ来说,其深度可以是2~4096(4K);对IO SQ/CQ,深度可以是2~65536(64K)。队列深度也是可以配置的。
    对SQ和CQ做个小结:
    ❏ SQ用以主机发命令,CQ用以SSD回命令完成状态;
    ❏ SQ/CQ可以在主机的内存中,也可以在SSD中,但一般在主机内存中;
    ❏ 两种类型的SQ/CQ:Admin和IO,前者发送Admin命令,后者发送IO命令;
    ❏ 系统中只能有一对Admin SQ/CQ,但可以有很多对IO SQ/CQ;
    ❏ IO SQ与CQ可以是一对一的关系,也可以是多对一的关系;
    ❏ IO SQ是可以赋予不同优先级的;
    ❏ IO SQ/CQ深度可达64K, Admin SQ/CQ深度可4K;
    ❏ IO SQ/CQ的广度和深度都可以灵活配置;
    ❏ 每条命令大小是64B,每条命令完成状态是16B。
  • 队列结构:
  • DB小结:
    ❏ DB在SSD控制器端,是寄存器;
    ❏ DB记录着SQ和CQ队列的头部和尾部;
    ❏ 每个SQ或者CQ有两个DB——Head DB和Tail DB;
    ❏ 主机只能写DB,不能读DB;
    ❏ 主机通过SSD往CQ中写入的命令完成状态获取其队列头部或者尾部。
  • 寻址方法:
    主机也有两种方式来告诉SSD数据所在的内存位置,一是PRP(Physical Region Page,物理区域页),二是SGL(Scatter/Gather List,分散/聚集列表)。
    ①PRP:
  • ②SGL:
    SGL是什么?SGL是一个数据结构,用以描述一段数据空间,这个空间可以是数据源所在的空间,也可以是数据目标空间。SGL(Scatter Gather List)首先是个List,是个链表,由一个或者多个SGL段(Segment)组成,而每个SGL段又由一个或者多个SGL描述符(Descriptor)组成。SGL描述符是SGL最基本的单元,它描述了一段连续的物理内存空间:起始地址+空间大小。每个SGL描述符大小是16字节。一块内存空间,可以用来放用户数据,也可以用来放SGL段,根据这段空间的不同用途,SGL描述符也分几种类型,如表6-4所示。
  • 协议栈分析:
    PCIe最直接接触的是NVMe的事务层。在NVMe层,我们能看到的是64字节的命令、16字节的命令返回状态,以及跟命令相关的数据。而在PCIe的事务层,我们能看到的是事务层数据包(Transaction Layer Packet),即TLP。
  • 端到端数据保护:
    主机与SSD之间,数据传输的最小单元是逻辑块(Logical Block, LB),每个逻辑块大小可以是512/1024/2048/4096等字节,主机在格式化SSD的时候,逻辑块大小就确定了,之后两者就按这个逻辑块大小进行数据交互。
  • 主机与SSD之间,数据在PCIe上传输的时候,由于信道噪声的存在(说白了就是存在干扰),可能导致数据出错;另外,在SSD内部,控制器与闪存之间,数据也可能发生错误。为确保主机与闪存之间数据的完整性,即主机写入闪存的数据与最初主机写的数据一致,以及主机读到的数据与最初从闪存上读上来的数据一致,NVMe提供了一个端到端的数据保护功能。除了逻辑块数据本身,NVMe还允许每个逻辑块带个助理,叫作元数据(Meta Data)。
  • ❏ Guard:16比特的CRC(Cyclic Redundancy Check),它是逻辑块数据算出来的;检测数据是否出错。
    ❏ Application Tag:这块区域对控制器不可见,为主机所用;检测数据是否张冠李戴。
    ❏ Reference Tag:将用户数据和地址(LBA)相关联,防止数据错乱。
  • 无关紧要的数据可以不必端到端的保护。
  • 总结:
    数据端到端保护是NVMe的一个特色,其本质就是在数据块中加入CRC和数据块对应的LBA等冗余信息,SSD控制器或者主机端利用这些信息进行数据校验,然后根据校验结果执行相应的操作。加入这些检错信息的好处是能让主机与SSD控制器及时发现数据错误,副作用就是:1)每个数据块需要额外的至少8字节的数据保护信息,有效带宽减少:数据块越小,带宽影响越大。2)SSD控制器需要做数据校验,影响性能。
  • Namespace:
    一个NVMe SSD主要由SSD控制器、闪存空间和PCIe接口组成。如果把闪存空间划分成若干个独立的逻辑空间,每个空间逻辑块地址(LBA)范围是0到N-1(N是逻辑空间大小),这样划分出来的每一个逻辑空间我们就叫作NS,系统就是通过NS的ID来区分不同的NS。
  • 一个NVMe命令一共64字节,其中Byte[7:4]指定了要访问的NS。
    对每个NS来说,都有一个4KB大小的数据结构来描述它。该数据结构描述了该NS的大小,整个空间已经写了多少,每个LBA的大小,端到端数据保护相关设置,以及该NS是属于某个控制器还是几个控制器可以共享等。
    NS由主机创建和管理,每个创建好的NS,从主机操作系统角度看来,就是一个独立的磁盘,用户可在每个NS做分区等操作。
  • 对一个NVMe子系统来说,除了包含若干个NS,还可以有若干个SSD控制器。注意,这里不是说一个SSD控制器有多个CPU,而是说一个SSD有几个实现了NVMe功能的控制器。
    如图6-53所示,一个NVMe子系统包含了两个控制器,分别实现不同功能(也可以是相同功能)。整个闪存空间分成3个NS,其中NS A由控制器0(左边)独享,NS C由控制器1(右边)独享,而NS B是两者共享。独享的意思是说只有与之关联的控制器才能访问该NS,别的控制器是不能对其进行访问的,图6-53中控制器0是不能对NS C进行读写操作的,同样,控制器1也不能访问NS A;共享的意思是说,该NS(这里是NS B)是可以被两个控制器共同访问的。对共享NS,由于几个控制器都可以对它进行访问,所以要求每个控制器对该NS的访问都是原子操作,从而避免同步问题。
  • 事实上,一个NVMe子系统,除了可以有若干个NS,除了可以有若干个控制器,还可以有若干个PCIe接口。与前面的架构不一样,图6-54的架构是每一个控制器都有自己的PCIe接口,而不是两者共享一个。Dual Port,双端口,在SATA SSD上没有见过吧。这两个接口往上有可能连着同一个主机,也可能连着不同的主机。

SSD测试

SSD电源管理

ECC原理


文章作者: FFFfrance
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 FFFfrance !