
【SSD】FTL 闪存转换层
FTL(Flash Translation Layer,闪存转换层)用于完成主机逻辑地址空间到闪存物理地址空间的翻译,或者说是映射。SSD每把一笔用户逻辑数据写入闪存地址空间,便记录下该逻辑地址到物理地址的映射关系,下次主机想读取该数据时,固件根据这个映射便能从闪存中把这笔数据读上来然后返回给用户。事实上,现在SSD中的FTL要做的事情还有很多,比如垃圾回收、磨损均衡、异常掉电处理等(后面会有详细
1 FTL综述
FTL(Flash Translation Layer,闪存转换层)用于完成主机逻辑地址空间到闪存物理地址空间的翻译,或者说是映射。SSD每把一笔用户逻辑数据写入闪存地址空间,便记录下该逻辑地址到物理地址的映射关系,下次主机想读取该数据时,固件根据这个映射便能从闪存中把这笔数据读上来然后返回给用户。
事实上,现在SSD中的FTL要做的事情还有很多,比如垃圾回收、磨损均衡、异常掉电处理等(后面会有详细介绍)。通过实现这些算法,FTL把SSD存储介质特性隐藏起来,使用户使用基于闪存的SSD像使用传统HDD一样,不用考虑存储介质特性。
SSD使用的存储介质一般是NAND闪存,它具有如下特性。
- 闪存块需先擦除才能写入,不能覆盖写(out-of-place update)。闪存不允许覆盖写,必须写到一个新的位置。因此,SSD的固件需要维护一张逻辑地址到物理地址的映射表,以跟踪每个逻辑块最新数据存储在闪存中的位置。另外,FTL需要做垃圾回收,即把若干个闪存块上的有效数据搬到某个新的闪存块,然后把这些闪存块擦除,得到可用的闪存块。
- 闪存块都是有一定寿命的。我们期望所有闪存块都来均摊数据的写入,而不是有些闪存块累死,而有些闪存块一直闲着。FTL需要做磨损均衡(Wear Leveling),让数据的写入尽量均摊到SSD中的每个闪存块上,即让每个块磨损都差不多,从而保证SSD具有最大的数据写入量。
- 存在读干扰问题。每个闪存块可读的次数也是有限的,读得太多,上面的数据也会出错,这就是读干扰问题。FTL需要处理读干扰问题,当某个闪存块读的次数马上要达到一定阈值时,FTL需要把这些数据从该闪存块上搬走,从而避免数据出错。
- 存在数据保持问题。由于电荷的流失,存储在闪存上的数据会丢失。(在高温下,电荷流失速度会加快,数据保存的时间就更短了。)但一旦上电,FTL就可对此进行处理,比如定期扫描闪存,发现是否存在数据保持问题,如果存在,则需要刷新数据。
- 存在坏块。闪存天然就有坏块。另外,随着SSD的使用,也会产生新的坏块。坏块出现的症状是擦写失败或者读失败(ECC不能纠正数据错误)。坏块管理也是FTL的一大任务。
FTL有Host-Based和Device-Based两种。
Device-Based的意思是说FTL是在Device(设备)端实现的,用的是SSD上的控制器和RAM资源。FTL在设备固件里处于中间层,起着承上启下的作用:把前端(Front End,FE)主机的读写请求转换成对后端(Back End,BE)闪存的读写请求。目前主流SSD都是Device-Based FTL。
2 映射管理
2.1 映射的种类
根据映射粒度的不同,FTL映射可分为块映射、页映射和混合映射。
块映射有好的连续大尺寸的读写性能,但小尺寸数据的写性能是非常糟糕的,因为用户即使只更新一个逻辑页,也需要把整个物理块数据先读出来,然后改变逻辑页的数据,最后对整个块进行写入。由于闪存页远比闪存块多(一个闪存块包含几百甚至几千个物理页),因此需要更多的空间来存储映射表。但它的性能更好,尤其体现在随机写上面。为追求性能,SSD一般都采用页映射。
2.2 映射的基本原理
用户是通过LBA(Logical Block Address,逻辑块地址)访问SSD的,每个LBA代表一个逻辑块(大小一般为512B、4KB、8KB…),我们把用户访问SSD的基本单元称为逻辑页(Logical Page)。而在SSD内部,主控以闪存页为基本单元读写闪存。
SSD内部维护了一张逻辑页地址到物理页地址转换(Logical address ToPhysical address,L2P)的映射表, 写)映射表,以追踪该逻辑页最新数据所在的物理位置;当读取某个逻辑页时,SSD首先查找映射表中与该逻辑页对应的物理页,然后访问闪存并读取相应的用户数据。
由于闪存页和逻辑页大小不同,一般前者大于后者,所以在实际场景中不会是一个逻辑页对应一个物理页,而是若干个逻辑页写在一个物理页:逻辑页其实是和子物理页一一对应的。
这里假设有一个256GB的SSD,以4KB大小的逻辑页为例,那么用户空间一共有64M(256GB/4KB)个逻辑页,也就意味着SSD需要有能容纳64M条映射关系的映射表。映射表中的每个单元存储的是物理地址(Physical Address),假设用4B来表示,那么整个映射表的大小为256MB(64M×4B)。所以一般来说,映射表大小为SSD容量的千分之一。
对早期SSD以及现在的企业级SSD来说,我们可以看到上面都有板载DRAM,它的主要作用就是存储这张映射表。在SSD工作时,全部的或者绝大部分的映射表都可以缓存在DRAM中,这样固件就可以快速获得和更新映射关系。
但对消费级SSD或者移动存储设备(比如eMMC、UFS)来说,出于成本和功耗考虑,它们采用DRAM-less设计,即不带DRAM,比如经典的Sandforce主控,它并不支持板载DRAM。对这种不带DRAM的SSD,它们的映射表存在哪里呢?它们一般采用多级映射。图6-8所示是一个两级映射的例子。一级映射表常驻SRAM,二级映射表小部分缓存在SRAM,其他部分都存放在闪存上。
对不带DRAM的SSD来说,它首先要看与该逻辑页对应的映射关系是否在SRAM内,如果在,那就直接根据映射关系读取闪存;如果该映射关系不在SRAM内,那么它首先需要把映射关系(映射块)从闪存里面读取出来,然后根据这个映射关系读取用户数据。这就意味着相比有DRAM的SSD,不带DRAM的SSD需要读取两次闪存才能把用户数据读取出来,读取性能和延时都要比带DRAM的SSD差。
对顺序读来说,由于映射关系连续,一次映射块的加载就可以满足很多用户数据的读。比如当映射块大小为4KB时,加载一个映射块就可以满足4MB用户数据的连续读。这意味着DRAM-less的SSD也可以有好的顺序读性能。但对随机读来说,映射关系分散,一次映射关系的加载,可能只能满足几笔逻辑页的读,因此对随机读来说,可能需要访问若干次闪存才能完成一次随机读操作,因此随机读性能就不是那么理想了。
一种常规思路就是:设备运行时,所有映射关系都保存在闪存上面,按需加载映射关系到控制器SRAM,并把最近访问的映射关系缓存在控制器的SRAM中。根据用户访问SSD的时间和空间局部性,一部分映射表加载到SRAM中,因为接下来它们大概率会被再次使用到,因此映射表缓存对后续的访问是有帮助的。经典的不带DRAM的FTL架构是DFTL(Demand-based FTL),它应该是后续所有DRAM-less FTL的鼻祖。
2.3 HMB
映射表除了可以放到板载DRAM、片上SRAM和闪存中,还可以放到主机的内存中。NVME 1.2及后续版本有一个重要的功能——HMB(HostMemory Buffer),就是主机在内存中专门划出一部分空间供SSD使用,SSD可以把它当成自己的DRAM使用。
在性能上,HMB应该介于带DRAM(板载)和不带DRAM(映射表绝大多数存放在闪存)之间,因为SSD访问主机端DRAM的速度肯定比访问本地SSD端DRAM的速度要慢,但还是比访问闪存的速度(几十微秒)要快。
带DRAM的SSD设计的优势是性能好,映射表完全可以放在DRAM中,查找和更新迅速;劣势是,由于增加了一个DRAM,所以提高了SSD的成本,还有就是加大了SSD功耗。DRAM-less的SSD设计与之正好相反,优势是成本和功耗相对低;缺点是性能差。HMB的出现为SSD的设计提供了新的思路。SSD可以自己不带DRAM,完全用主机DRAM来缓存数据和映射表。
2.4 映射表写入
在SSD掉电前,需要把映射表写入闪存中。下次上电初始化时,需要把它从闪存中全部或者部分加载到SSD的缓存(DRAM或者SRAM)中。随着SSD的写入,缓存中的映射表不断增加新的映射关系,为防止异常掉电导致这些新的映射关系丢失,SSD的固件不仅在正常掉电前把这些映射关系写入到闪存中,还在SSD运行过程中按照一定策略把映射表写入闪存。这样即使发生异常掉电,丢失的也是一小部分映射关系,上电时可以较快地重建这些映射关系。
3 垃圾回收
3.1 垃圾回收原理
垃圾回收就是把某个闪存块上的有效数据读出来并进行重写,然后把该闪存块擦除,从而得到新的可用闪存块。如图所示,块x上面有效数据为A、B、C,块y上面有效数据为D、E、F、G,其余方块为无效数据。垃圾回收机制就是先找一个可用块z,然后把块x和块y的有效数据搬移到块z上,这样块x和块y上面就没有任何有效数据了,将它们擦除就得到两个可用的闪存块。我们用不到一个闪存块的代价,获得了两个可用的闪存块(见图6-19),这就是垃圾回收的价值。
垃圾数据随机分散在每个闪存块上,而不是集中在某几个闪存块上。这个时候,如何挑选需要回收的闪存块呢?答案是挑垃圾比较多的闪存块来回收,因为有效数据少,要搬移的数据少,这样腾出空闪存块的速度快,付出的代价也小。
SSD越写越慢是有科学依据的:可用闪存空间富裕时,SSD无须做垃圾回收,因为总有空闲的空间可写。SSD使用早期,由于没有触发垃圾回收机制,无须额外的读写操作,所以速度很快。之后SSD变慢了,主要原因是SSD需要做垃圾回收。
另外,如果用户是顺序写,垃圾比较集中,利于SSD做垃圾回收;如果用户是随机写,垃圾比较分散,SSD做垃圾回收相对来说就更慢,所以性能没有前者好。因此,SSD的垃圾回收性能跟用户写入数据的模式(顺序写还是随机写)也有关。
3.2 写放大
垃圾回收的存在会引入写放大问题:用户要写入一定量的数据,SSD为了腾出空间写这些数据,需要额外做一些数据搬移工作,也就是进行额外写,导致的后果往往就是SSD往闪存中写入的数据量比实际用户写入SSD的数据量要大。
对空盘来说(未触发垃圾回收),写放大系数一般为1,即用户写入数据量与SSD写入闪存的数据量相等(这里忽略了SSD内部数据的写入,如映射表的写入)。SandForce控制器内部具有实时数据压缩模块,它能对用户写入的数据进行实时压缩,然后再把它们写入闪存,因此写放大系数可以做到小于1。
假设一共36个方块,其中有12个有效数据方块,我们做完垃圾回收后,需对这12个有效数据方块进行写回操作;后面还可以写入24个方块的用户数据。因此,为了写这24个方块的用户数据,SSD实际写了12个方块的原有效数据,再加上该24个方块的用户数据,总共写入36个方块的数据,由写放大系数的定义可知:WAF=36/24=1.5。
写放大系数越大,意味着额外写入闪存的数据越多,这不仅会磨损闪存,减少SSD寿命,还会影响SSD的性能(写入这些额外数据时会占用底层闪存带宽)。因此,SSD设计的一个目标是让写放大系数尽量小。减小写放大系数,可以使用前面提到的压缩办法(由主控决定),另外顺序写也可以减小写放大系数。还可以增大OP来减小写放大系数(这个可控)。
假设SSD容量是180个小方块,当OP是36个小方块时,整个SSD闪存空间为216个小方块,OP比例是36/180=20%。那么180个小方块的用户数据平均分摊到216个小方块时,每个小方块的平均有效数据为180/216=0.83,一个闪存块上的有效数据为0.83×9=7.5。OP越大,每个闪存块有效数据越少,垃圾越多,需要重写的数据越少,因此写放大系数越小。同时,垃圾回收需要重写的数据越少,SSD满盘写性能也越高。
影响写放大系数的因素主要有OP大小、用户写入的数据模式(随机写与顺序写)、垃圾回收策略(在挑选源闪存块的时候,如果不是挑选有效数据最少的块作为源闪存块就会增加写放大系数)、垃圾回收的时机(理论上,越晚做垃圾回收写放大系数越小,因为闪存块上的一些数据可能被后续用户重写,从而变成垃圾数据,越到后面垃圾数据越多,需要搬移的有效数据越少,写放大系数越小)、磨损均衡(为均衡每个闪存块的擦除次数,需要搬移数据)、数据刷新(数据搬移会增加写放大系数)、主控(带压缩和不带压缩)。
4 解除映射关系
用户删除文件时,系统需要发出一些特殊命令来及时告诉SSD哪些数据已经不需要了。在SSD主机协议中有相应的命令来支持该功能,比如ATA中的Data Set Management(Trim命令)、SCSI里面的UNMAP命令等。一旦SSD知道哪些数据是用户不需要的,在做垃圾回收的时候就不会搬移主机删除的数据。
一般FTL都有3张表:FTL映射表记录与每个LBA对应的物理页位置;Valid Page Bit Map(VPBM)表记录每个物理块上哪个页上的数据是有效数据;Valid Page Count(VPC)表则记录每个物理块上的有效页个数。通常垃圾回收会使用VPC表进行排序来回收最少有效页的闪存块;VPBM表则是为了实现在垃圾回收时只读有效数据,有些FTL会省略这个表。
5 磨损均衡
一个闪存块,如果擦写次数超过一定的值,那么该块就变得不可靠了,甚至变成坏块。如果不做磨损均衡,则有可能出现某些闪存块被频繁擦写,这些闪存块很容易寿终正寝。随着数据不断写入,越来越多的坏块出现,最后导致SSD在保质期前就挂掉了。相反,如果让所有闪存块一起来承担用户数据的写入,则能经受更多的用户数据写入。
所谓冷数据,就是用户不经常更新的数据,比如用户写入SSD的操作系统数据、只读文件数据、视频文件等;相反,热数据就是用户更新频繁的数据。频繁更新会在SSD内部产生很多垃圾数据(新数据写入导致老数据失效)。所谓年老的块,就是擦写次数比较多的闪存块。擦写次数比较少的闪存块,年纪相对小,我们称其为年轻的块。
动态磨损均衡算法基本思想是把热数据写到年轻的块上;静态磨损均衡算法基本思想是把冷数据写到年老的块上,即把冷数据搬到擦写次数比较多的闪存块上。
6 掉电恢复
掉电分两种,一种是正常掉电,另一种是异常掉电。不管是哪种掉电,重新上电后,SSD都需要能从掉电中恢复过来,继续正常的工作。
先说正常掉电。在掉电前,主机会通过命令通知SSD,比如SATA中的Idle Immediately,收到该命令后,SSD主要做以下事情
- 把写缓存中的用户数据刷入闪存;
- 把映射表刷入闪存;
- 把闪存的块信息写入闪存(比如当前写的是哪个闪存块,写到该闪存块的哪个位置,哪些闪存块已经写过,哪些闪存块是无效的,等等);
- 把SSD的其他信息写入闪存。设备把掉电前该做的事情做好后,才会反馈给主机“可以断电了”的信息。
主机收到命令响应后,才会真正停止对SSD的供电。正常掉电不会导致数据的丢失。
所谓异常掉电,就是SSD在没有收到主机的掉电通知时就被断电;或者收到主机的掉电通知,但还没有来得及处理上面提到的那些事情,就被断电了。异常掉电可能导致数据的丢失。异常掉电恢复的目的一方面是尽可能恢复用户数据,把损失降到最低;另一方面是让SSD经历异常掉电后还能正常工作。
一个SSD,除了有掉电数据不丢失的闪存,还有掉电数据丢失的RAM(SRAM或者DRAM)。闪存的作用是存储数据,而RAM主要在SSD工作时缓存用户数据和存放映射表(MapTable,逻辑地址映射闪存物理地址)。所以一旦掉电,RAM的数据就会丢失。
为防止异常掉电数据丢失,一个简单的设计就是在SSD上面放电容。SSD一旦检测到掉电,就让电容开始放电来给SSD供电,然后把RAM中的数据刷到闪存中,从而避免数据丢失。企业级的SSD一般都带有电 容。带电容的SSD,还配有异常掉电处理模块,因为电容不能绝对保证SSD在掉电前把所有的信息刷入闪存。
还有一个比较前卫的想法,就是把RAM这种易失性的存储介质,用非易失性存储介质来替代,但要求这种非易失性存储介质性能上接近RAM。这样整个SSD就都是非易失性的了,也就不用担心SSD异常掉电了。英特尔开发的3D XPoint,可能就将此作为一个选择。3D XPoint兼有闪存非易失性和内存快速访问的特点。但这只能保护缓存在3D XPoint中的数据,对那些正在往闪存中写的数据,还是无法提供保护。
和RAM中用户数据丢失不同,RAM中映射表数据是有办法恢复过来的。对SSD异常掉电的恢复主要就是对映射表的恢复重建。SSD在写用户数据到闪存的时候,会额外写入元数据。如果我们读取物理地址PPA x,那么就能读取到该位置上的元数据和用户数据,而元数据是有逻辑地址LBA x的,因此,我们就能重构映射关系LBA x→PPA x。映射表的恢复原理其实很简单,只要扫描整个闪存空间,就能获得所有的映射关系,最终完成整个映射表的重构。更关键在于解决数据新旧问题和重构速度问题等。
同一逻辑地址,用户可能写过若干次,在闪存空间,与该逻辑地址对应的数据有很多是旧数据,只有一笔是新数据,那么如何甄别哪些数据是旧的,哪个数据是最新的呢?如何让逻辑地址映射到最新数据所在的物理地址呢?SSD起初把逻辑地址LBA 2的数据写在物理地址PPA 2上;后面,用户改写了这个数据,SSD把它写到了物理地址PPA 5上。我们知道,用户最后写入的数据总是最新的。在这里,TS帮上大忙了,哪个TS值大,就表示哪个数据是最后写入的。SSD可以依赖元数据中的TS来区分新旧数据。
用全盘扫描映射方式重构映射表需要花费几分钟甚至几十分钟,这在实际使用中,用户是不能接受的。一种办法就是定期把SSD中RAM的数据(包括映射表和缓存的用户数据)和SSD相关的状态信息(诸如闪存块擦写次数,闪存块读次数,闪存块其他信息等)写入闪存。这个操作被称为做Checkpoint(检查点,但笔者觉得翻译成“快照”更合适点)。重上电,SSD可以从闪存中读取最新的快照信息,即快照C。由于是异常掉电,从快照C处到X处新产生的映射关系丢失。由于之前绝大多数的映射关系都被快照C保存,因此需要重建的映射关系仅仅是快照C之后产生的映射关系,恢复这部分关系,仅需扫描局部的物理空间,因此相对全盘扫描,映射表重建速度大大加快。
7 坏块管理
- 出厂坏块:闪存从工厂出来,就或多或少有一些坏块。
- 增长坏块:随着闪存的使用,一些初期好块也会变成坏块。变坏的原因可能是闪存缺陷,也可能是擦写磨损过多。
7.1 坏块鉴别
一般来说,刚出厂的闪存都被擦除,里面的数据全是0xFF。但是对坏块来说,闪存厂商会打上不同的标记。
用户在使用闪存的时候,首先应该按照闪存规格书的建议扫描所有的闪存块,把坏块剔除出来,建立一张坏块表。
增长坏块的出现会通过读写擦等操作反映出来。读到UECC(Uncorrectable Error Correction Code,数据没有办法通过ECC纠错恢复)、擦除失败、写失败,这些都是一个增长坏块出现的症状。用户应该把增长坏块加入坏块表,不再使用。
7.2 坏块管理策略
1.略过策略
在写闪存的时候,一旦遇到表中登记的坏块就跨过它,写下一个闪存块,这就是略过策略。假设Die 1上有一个块B是坏块,若固件采取坏块略过策略,则写完块A后便会跨过块B将数据写到Die 2的块C上。
2.替换策略
当某个Die上发现坏块,替换策略会将坏块用该Die上的某个好块替换。也就是说,在替换策略下,用户在写数据的时候不是跨过这个Die,而是写到替换块上。用户写入数据时碰到块B这个坏块,它不会略过Die 1不写,而是写入块B的替换者块B′上。
略过策略的劣势在于会使性能变得不稳定,以4个Die为例,略过策略可能导致Die的并行度在1个和4个Die之间,而替换策略并行度总是4个Die,毋庸置疑,前者性能表现不如后者;略过策略会导致超级块(每个Die上取具有相同块编号组成的一个FTL管理块)组成不固定,给FTL算法带来复杂性,而替换策略能保证每个超级块的组成始终是一致的,实现更容易。
但替换策略有木桶效应,如果某个Die质量比较差,则整个SSD可用的闪存块都会受限于那个坏的Die。
更多推荐
所有评论(0)