作者简介
刘长鹏,资深存储工程师, 主要研究方向为虚拟化,新存储介质应用,负责SPDK开发和社区维护。
2018年某公有云厂商因为硬盘固件版本bug引起的硬盘静默错误致使文件系统元数据损坏,导致用户数据丢失,一度引起了热烈的讨论。
静默错误就是因为硬盘的潜在故障而导致的数据写入和读出不一致,在这个过程中,用户无法从任何一个方面得到通知或者异常报告。如果用户层(例如文件系统) 没有实现读写数据校验就会很难发现这样的错误。
既然硬盘因为硬件错误,固件bug,供电温度等原因的影响会导致静默错误的出现,那么对用户来讲有没有一种可以主动的检测到这样的静默错误的手段呢?
T10标准组织就定义了 DIF (Data Integrity Field) 和DIX (Data Integrity Extensions) 这两种标准,可以给用户提供一种端到端的数据保护方式,下面我们先来看一下DIF:
图1:512 + 8 DIF格式
可以看到在硬盘512字节的数据区后面跟了一个连续的8字节数据,该8字节数据被定义为PI (Protection Information):
图2:Protection Information定义
Guard:2 Bytes,保存数据块CRC16校验值,按照图1例子就是512字节数据区的CRC16校验值,适用所有的Type;
Reference Tag:4 Bytes,主要用来保护Misdirected Writes,Misdirected Write指的是目标LBA和实际写入的LBA不一致。Type1时保存目标Logical Block Address的低32Bit;当使用Type2时按照数据块单位递增,例如一个4KB的写命令包含8个扇区和8个PI,每个PI区域的Reference Tag按照初始LBA递增;
Application Tag:2 Bytes,顾名思义由应用程序自己填写;
标准组织还定义了三种数据保护Type:
Type1:Guard + Reference Tag,reference tag是该命令所指向的LBA低32地址;
Type2:Guard + Reference Tag,reference tag单位递增;
Type3:Guard;
这里在介绍下DIF和DIX的区别,DIF的PI信息是直接跟到数据后面,用户数据和PI共享一个数据buffer,而DIX的PI是一个单独的buffer,也就是说用户需要提供2个独立的buffer,一个是数据一个存放PI信息,假设硬盘block size是512字节,在PI使能时,存取8个block时,DIF需要申请(512+8) * 8字节的Buffer,而DIX需要申请512 * 8的数据Buffer以及 8 * 8的PI Buffer。
图3:DIF vs DIX
SPDK目前提供了比较全面的针对DIF的软件支持,包括DIF/DIX Library以及iSCSI target和NVMoF target端到端的DIF数据保护。这里提到了软件支持,因为在硬盘层面,比如Intel NVMe SSD都对DIF提供了全面的硬件支持,用户也可以直接利用该硬件特性保护自己的数据,如果需要直接利用NVMe硬件特性支持DIF,可以使用SPDK提供的NVMe存取API,如:spdk_nvme_ns_cmd_write/read_with_md,
详细信息请参见(https://spdk.io/doc/nvme.html#nvme_interface)。
下面我们看下SPDK提供的软件API如何实现数据的端到端保护的,下面看下常用的使用方法:
1. 用户无感知 :使用SPDK block API无论读写都可以按照之前的512 Bytes对齐的方式组织数据,SPDK自动在写操作(Write_Insert)时添加8字节Protection Information,并且在读操作(Read_Strip_Verify)时丢弃掉多余的8字节信息;当在读取之前写入的数据时出现CRC校验失败或者Reference Tag失效时,该读命令会返回给用户IOERR错误码,并提供详细的错误信息;
图4:PI Write_Insert和Read_Strip_Verify
2. 用户感知:数据和额外的Protection Information区域都由用户提供,SPDK只负责填充PI和校验PI;同样的当检测出数据不一致时,SPDK会返回IOERR给用户;
图5:PI Write_Fill和Read_Verify
有了以上两种方式的数据保护,硬盘的任何静默错误都会被通知到用户。当然需要明确的无论是DIF还是DIX仅是检测手段,这两种方法并不能用来恢复数据,用户收到IOERR的错误码需要自行添加对该错误的容错机制,例如软件的RAID或者用户本身就是分布式文件系统。
SPDK 端到端数据保护模块:
DIF Library: lib/util/dif.c,提供基本的DIF/DIX API调用,包括DIF/DIX Insert/Strip/Verify,并且支持Scatter-Gather List数据Buffer;
Intel ISA-L:提供高效的CRC16指令优化算法;
BDEV Layer:块层API抽象,iSCSI/NVMoF Target可以直接使用;
NVMe Bdev Module:数据存储层,用户也可以直接使用NVMe接口提供的DIF/DIX API;
iSCSI Target和NVMeoF Target:调用Bdev提供的API可以实现Write Insert以及Read Strip以及Read Verify功能;
Util:SPDK Perf tool和SPDK Fio plugin,用户可以用来进行DIF/DIX功能和性能评估;
下面我们看下在SPDK Full Stack下使用端到端数据保护DIF的示例,iSCSI的示例中在远端的Host测是不感知DIF存在的,而在NVMoF的示例中,Host端是感知的,但是Host端可以选择不填充任何数据到DIF字段,由SPDK Target测进行计算和校验。
图6:SPDK NVMoF和iSCSI Target使能DIF使用示例
SPDK本身作为软件层并不会保存数据的PI信息,这里要求后端的硬盘能够提供支持类似512+8或者4096+64等格式的硬件特性,Intel NVMe全系企业级硬盘均可支持到这种可变长度的数据存储格式。