俗话说,龙生龙,凤生凤,老鼠的儿子会打洞,但我没想到这句话也有能套用在NVMe协议上的一天。不知道大家在了解NVMe 协议的时候,有没有注意过以下几个概念:Write Zeroes,Write Uncorrectable,Dataset Management,TRIM,Sanitize,Deallocate?它们都能够让一定范围的LBA在读取时返回全0结果或不可读,但你知道它们都有哪些差异,又有哪些共性?别着急,今天我们先来详细介绍一下Write Zeroes Command,然后再一起盘一下它的几个“远亲近邻”们。
Write Zeroes,顾名思义,是用来对一段连续的LBA写0h数据的一个命令,通过将这些LBA释放出来,供上层应用使用。它可以通过具体的参数配置,来决定是否要真的往物理地址写0h数据,还是仅仅在被读取时返回数据全0h的内容。虽然在Host端读取时看到的表现是一样的,但实际的处理方式不尽相同。
首先,使用这个命令有几个需要着重关注的点:
1. Identify中的相关字段表明是否可以支持Write Zeroes Command
a) ONCS(Optional NVM Command Support)的Bit3为1b,此时WZSL(Write Zeroes Size Limit)为建议值,超过WZSL大小的Write Zeroes Command会造成一些延迟;
b) ONCS的Bit3为0b,WZSL为0,说明控制器不支持Write Zeroes命令;
c) ONCS的Bit3为0b,WZSL为非零值,此时WZSL为限制值,超过WZSL大小的Write Zero Command会被终止。
2. Identify中的相关字段可以支持Deallocate后清零的相关设置
Host在读取一个成功做完Write Zeroes命令的LBA时,除了PI(Protect Information)内容外,其余无论是User Data还是Meta Data一定要确保读到的值是0h,这是此命令的意义所在。因此,Identify Namespace中的DLFEAT(Deallocate Logical Block Features)需要被注意:
a) 只有DLFEAT的Bit3为1,Host才能主动要求Write Zeroes使用Deallocate实现 (CDW12.DEAC),不然只能遵从控制器自己的处理方式。
b) 只有DLFEAT的Bit2:0为001b时,才可以确保Deallocate以后的LBA读到的值一定为0 h(PI除外),否则这个命令不会允许使用Deallocate实现,所有被作用的LBA都会实际写0h数据来处理。
虽然无论哪种实现方式都能达到Write Zeroes的目的,但显然Deallocate的方式会更节省时间。
3. Identify中关于Deallocated LBA错误处理的设置
当NSFEAT(Namespace Features)中的Bit2(DEA)被置1时,表示支持Deallocated LBA错误处理的功能。此时相应Feature设置若要求使能错误处理,控制器会终止一切试图读取此LBA的命令;反之若不要求错误处理,或者DEA为0,则按照前述的设定返回全0h/全FFh数据。
对Write Zeroes命令来说,Host决定其行为方式的关键参数,在于CDW12.DEAC。我们假设上述的Identify设置都支持Write Zeroes Command,以及读访问Deallocated LBA不会报错,并支持返回全0h值:
1. 如果设置CDW12.DEAC为1,则控制器的行为是:
a) 应当Deallocate此LBA;
b) 应当在这个LBA被读取时,除了PI外,所有数据都返回为0h;
c) PI(若有)的处理方式应该遵循协议中Deallocated LBA的PI规则:
i. Guard字段为全FFh值,或者是根据此LBA的非PI字段数据算出的CRC值。
ii. Application Tag字段和Storage Tag字段(若有),以及Logical Block Reference Tag都应当为全FFh值(表示PI不需要被验证)。
2. 如果设置CDW12.DEAC为0,则控制器的行为是:
a) 可能会Deallocated此LBA(取决于FW行为和Identify设定);
b) 应当在这个LBA被读取时,除了PI外,所有数据都返回为0h;
c) 应当基于Command中的CDW12.PRINFO设定来返回该LBA的PI值:
i. PRACT=0时,没有PI,所有数据全返回0h。
ii. PRACT=1时,除PI外所有数据全返回0h,PI则由Host自己指定。
iii. PRCHK应始终为000b。
接下来,我们来看看命令的主要字段设定:
我们选择其中比较重要的几个来介绍下:
1. SLBA(Starting LBA):CDW[11:10],Write Zeroes命令需要写0h的起始LBA地址, CDW10涵盖地址Bit[31:00],CDW11涵盖地址Bit[63:32]。
2. NLB(Number of Logical Blocks):从SLBA起,Write Zeroes命令需要在多少个连续的LBA上生效。这是个0 Base的值(即0值为起始,代表数量1)。
3. PRINFO(Protection Information Field):作用如前述,用来指定PI的行为以及检查字段。在此命令中,作为检查字段的PRCHK(Protection Information Check)应当为000b。
4. DEAC(Deallocate):作用如前述,决定命令Deallocate行为的重要参数。
命令完成时的返回值字段:
1. Namespace is Write Protected:该命名空间处于写保护状态,命令禁止使用;
2. Invalid Protection Information:命令中指定的PI设定和所涉及的命名空间PI规则不符,或者ILBRT字段不合法;
3. Attempted Write to Read Only Range:指定的LBA范围内包含了只读块区域,如果该只读块由所处命名空间写保护导致,则应返回Namespace is Write Protected结果。
至此,关于Write Zeroes的相关知识基本介绍完毕了,不过这篇文章的内容还没有结束。现在,我们回到文章开头时提到的,那些“远亲近邻”们的故事吧。
首先我们来说说TRIM,这个术语对于熟悉SSD的读者们肯定不陌生。然而,在NVMe协议中,我们并不能找到TRIM的相关定义,NVMe的TRIM,哪去了?
TRIM源自SATA SSD使用的ATA 指令集,在ATA8-ACS2中定义了TRIM的概念,它是当时Dataset Management命令中唯一的功能。与之相对应的还有SAS SSD使用的SCSI指令集,在SBC-3中定义了UNMAP命令。同样/类似的功能在不同的协议中有不同的名字,而SATA的TRIM最先出现也最被大家所熟知,并一直沿用至今。某种意义上可以说,TRIM是我们今天讨论的这些命令的“祖宗“。
而NVMe自己的TRIM,对应的是Deallocate这个概念。不过NVMe似乎更倾向定义它为一种状态,而不是某个功能或命令,它代表着一个LBA被要求与PBA解除关系。对于Deallocated LBA的读访问效果在我们今天的文章内已经做了较多介绍了,可以说,Deallocate就像TRIM在NVMe指令集中的“孪生兄弟”。
那在NVMe SSD中要想对某个LBA做Deallocate操作,该如何进行呢?这个答案,细心的读者可能已经知道了。作为多个NVMe命令的可选效果之一,Deallocate分别被嵌入在Dataset Management,Sanitize,以及Write Zeroes之中。它们就像Deallocate的三个孩子,各自都有着TRIM/Deallocate的基因,但也添加了一些属于自己的特点。
从概念上说,Dataset Management是正统的TRIM/Deallocate“继承人”,因为如前所述,它最早出现于ATA8-ACS2指令集,并携带唯一的TRIM功能。等到了NVMe时代,除了可以选择对LBA范围进行Deallocate操作以外,还可以描述LBA的读/写特性(延迟,频率,单次操作数据量大小,优化类型等等)。它可以在一个命令中指定多个操作实例,每个实例可以对一定范围的LBA进行不同的操作。因此,我们可以称这个命令为Deallocate的“长子”。
Sanitize同样是一个功能强大的命令,它的主要目的是用来对全盘进行数据擦除,而Deallocate只是作为数据擦除后LBA的一种可选状态,通过具体的参数配置是否使用。Sanitize支持普通擦除,覆盖写擦除,以及安全擦除三种执行方式,在执行过程中以及执行失败的状态下,NVMe SSD都会自动中断一些不被允许执行的命令,如所有的I/O请求,即使Sanitize期间遇到异常掉电,NVMe在上电后也会自动恢复Sanitize操作,你甚至还能通过它的特殊设定来退出某些异常的错误模式。Sanitize就像是一个优秀的“次子”,更多相关介绍,可参考《增强数据安全擦除 Sanitize》一文。
作为“末子”——本文着重论述的Write Zeroes,其实它的定义在ATA8-ACS2中也有迹可循。在ATA8-ACS2的TRIM功能中,其实就已经定义了其中一种TRIM后的效果为“deterministic read after trim behavior with data cleared to zero”,即TRIM后读取的Data数据要保证为0h。而在NVMe指令集中,直接把这个效果单独分拆出来并稍微做了些拓展,便有了Write Zeroes命令。在实际的Host应用中,Write Zeroes会用作新开辟整块的全0h区域,或者删除指定区域的数据使其保证为0h值。
至于Write Uncorrectable,它只是Write Zeroes的“好邻居”,名字虽然相近,但做的事情却完全不同。Write Uncorrectable的作用就是直接定义一段LBA数据为失效数据,既不Deallocate,也不覆盖或擦除,甚至有点“摆烂”。对该区域的所有读访问操作都会被拒绝,直到新的数据被写到这块LBA区域,这个命令带来的失效状态才会解除。
至此,我们介绍完了NVMe协议中,Write Zeroes的各种“远亲近邻”们,希望对于大家理解这些概念能有所帮助。在PBlaze7 7940系列企业级SSD中,我们在提供TRIM、Sanitize等重要功能的基础上,增加了Write Zeroes Command、Write Uncorrectable等命令的支持,进一步降低用户数据泄露的风险,也让产品的部署使用更加灵活。而作为一家企业级SSD产品和解决方案供应商,忆恒创源也将和广大生态伙伴一同,不断完善这些新技术的使用体验,持续为客户打造更高价值努力前行。