24 持久内存 #
本章包含有关使用配备非易失性主内存的 SUSE Linux Enterprise Server 的附加信息。此类内存称为持久内存,由一个或多个 NVDIMM 构成。
24.1 简介 #
持久内存是一种新型的计算机储存,其速度接近普通的动态 RAM (DRAM),同时兼具 RAM 的按字节寻址能力以及固态硬盘 (SSD) 的性能。
与传统的 RAM 一样,持久内存直接安装在主板上的内存插槽中。因此,它的物理外形规格与 RAM 相同,以 DIMM 的形式提供。这些内存称为 NVDIMM:非易失性双列直插式内存模块。
不过,与 RAM 不同,持久内存在多个方面类似于基于闪存的 SSD。后两者采用固态内存电路的形式,但除此之外,两者都提供非易失性储存:系统断电或者重启动后,内存中的内容会得到保留。使用这两种媒体时,写入数据的速度比读取数据要慢;两者都支持有限的重新写入周期数。最后,与 SSD 一样,如果在特定的应用方案中更适合对持久内存进行扇区级别的访问,则也可以这样做。
不同的型号使用不同形式的电子储存媒体,例如 Intel 3D XPoint,或者将 NAND 闪存与 DRAM 结合使用。另外,行业正在开发新形式的非易失性 RAM。这意味着,不同的供应商和 NVDIMM 型号会提供不同的性能和持久性特征。
由于涉及的储存技术处于早期开发阶段,不同供应商的硬件可能会施加不同的限制。因此,以下叙述适用于一般性的场合。
持久内存的速度最多比 DRAM 要慢 10 倍,但比闪存要快大约 1000 倍。可在其中按字节重新写入数据,而不像在闪存中一样,需要擦除整个扇区,然后重新写入数据。最后,尽管重新写入周期数有限,但大部分形式的持久内存可以应对数百万次重新写入,相比之下,闪存只能应对数千个周期。
这会产生两种重要后果:
使用最新的技术无法运行仅包含持久内存的系统,因此无法实现完全非易失性的主内存。必须混合使用传统的 RAM 和 NVDIMM。操作系统和应用程序将在传统的 RAM 中执行,而 NVDIMM 可提供极速的补充性储存。
由于不同供应商的持久内存的性能特征不同,程序员可能需要考虑到特定服务器中 NVDIMM 的硬件规格,包括 NVDIMM 的数量,以及它们可以装入到哪些内存插槽。显然,这会对超级管理程序的使用、不同主机之间的软件迁移等造成影响。
ACPI 标准版本 6 中定义了此新型储存子系统。但是,libnvdimm
支持该标准颁布之前的 NVDIMM,可以相同的方式使用这些内存。
24.2 术语 #
- 区域
区域是持久内存中可划分为一个或多个名称空间的块。如果不事先将某个区域分配到名称空间,则您无法访问该区域的持久内存。
- 名称空间
非易失性储存的单个连续寻址范围,相当于 NVM Express SSD 的名称空间,或 SCSI 逻辑单元 (LUN)。名称空间作为单独的块设备显示在服务器的
/dev
目录中。根据所需的访问方法,名称空间可将多个 NVDIMM 中的储存合并成较大的卷,或者允许将这些储存分区成较小的卷。- 模式
每个名称空间都有相应的模式,用于定义要为该名称空间启用的 NVDIMM 功能。同一父区域的同级名称空间始终具有相同的类型,但可将其配置为使用不同的模式。名称空间模式包括:
- raw
一个内存磁盘。不支持 DAX。与其他操作系统兼容。
- 扇区
适用于不执行元数据校验和计算的传统文件系统。适用于小型引导卷。与其他操作系统兼容。
- fsdax
文件系统 DAX 模式。如果未指定其他模式,则使用默认值。创建块设备 (
/dev/pmemX [.Y]
),支持将 DAX 用于ext4
或XFS
。- devdax
设备 DAX 模式。创建单字符设备文件 (
/dev/daxX.Y
)。不需要创建文件系统。
- 类型
每个名称空间和区域都有一种类型,该类型定义如何访问与该名称空间或区域关联的持久内存。名称空间的类型始终与其父区域的类型相同。类型分为以下两种:持久内存和块模式。
- 持久内存 (PMEM)
与 RAM 一样,PMEM 储存提供字节级别的访问。这会启用直接访问 (DAX),表示访问内存时会绕过内核的页面超速缓存,直接进入到媒体中。此外,使用 PMEM 时,单个名称空间可以包含多个交错式 NVDIMM,因此这些 NVDIMM 都可作为单个设备供系统访问。
- 块模式 (BLK)
BLK 访问发生在扇区(通常为 512 字节)中,通过定义的访问窗口 aperture 进行。此行为更像是传统的磁盘驱动器。也就是说,读取和写入都是由内核进行超速缓存的。使用 BLK 访问模式时,每个 NVDIMM 可作为一个单独的名称空间供系统访问。
有些设备支持 PMEM 和 BLK 两种模式。此外,有些设备允许将储存区分割成多个单独的名称空间,以便能够使用 PMEM 访问其中一些名称空间,使用 BLK 访问另一些名称空间。
除了
devdax
名称空间以外,必须将所有其他类型格式化为ext2
、ext4
或XFS
等文件系统,就像对传统的驱动器进行格式化一样。- Direct Access (DAX)
DAX 允许将持久内存直接映射成进程的地址空间(例如,使用
mmap
系统调用进行映射)。此功能适合用于在在未使用任何额外 RAM 的情况下直接访问大量 PMEM、注册多个适用于 RDMA 的 PMEM 块,或者直接将其指派给虚拟机。- DIMM 物理地址 (DPA)
在单个 DIMM 的内存中充当偏移量的内存地址;即,在该 DIMM 中充当最低可寻址字节的从零开始的地址。
- 标签
储存在 NVDIMM 中的元数据,例如名称空间定义。可以使用 DSM 访问这些数据。
- 设备特定的方法 (DSM)
用于访问 NVDIMM 中的固件的 ACPI 方法。
24.3 使用案例 #
24.3.1 将 PMEM 与 DAX 搭配使用 #
必须注意,这种形式的内存访问不是事务性的。如果发生断电或其他系统故障,数据可能不会完全写入到储存中。仅当应用程序可以处理部分写入数据的情况时,PMEM 储存才适用。
24.3.1.1 可受益于较大字节可寻址储存量的应用程序 #
如果服务器托管的某个应用程序可按字节直接使用较大的快速储存量,则程序员可以使用 mmap
系统调用,将持久内存块直接放入该应用程序的地址空间,而无需使用任何附加的系统 RAM。
24.3.1.2 避免使用内核页面超速缓存 #
您可以节省用于页面超速缓存的 RAM,而不要将 RAM 分配给应用程序。例如,可以专门使用非易失性内存来保存虚拟机 (VM) 映像。由于这些映像不会超速缓存,因此可以减少主机上的超速缓存使用量,从而可在每台主机上配置更多的 VM。
24.3.2 将 PMEM 与 BTT 搭配使用 #
当您想要使用一组 NVDIMM 中的持久内存作为类似于磁盘的极速储存池时,此方法非常有用。
对于应用程序而言,此类设备仅显示为极速 SSD,并可像其他任何储存设备一样供您使用。例如,LVM 可以分布在非易失性储存的顶层,像往常一样正常工作。
BTT 的优点在于可以保证扇区写入的原子性,因此,即使是依赖于数据完整性的复杂应用程序也能保持正常工作。可通过标准的错误报告通道来运行媒体错误报告。
24.3.3 BLK 储存 #
尽管它拥有更强大的功能,可以防范单设备故障,但需要进行额外的管理,因为每个 NVDIMM 都显示为一个单独的设备。因此,通常首选带有 BTT 的 PMEM。
在更高版本的 SUSE Linux Enterprise Server 中将弃用并不再支持 BLK 储存。
24.4 用于管理持久内存的工具 #
要管理持久内存,必须安装 ndctl
包。安装此包也会安装 libndctl
包,后者提供一组用户空间库用于配置 NVDIMM。
这些工具通过 libnvdimm
库运行。该库支持三种类型的 NVDIMM:
PMEM
BLK
同步 PMEM 和 BLK。
ndctl
实用程序提供一系列有用的手册
页。可使用以下命令访问这些页面:
ndctl help subcommand
要查看可用子命令的列表,请使用:
ndctl --list-cmds
可用的子命令包括:
- version
显示 NVDIMM 支持工具的当前版本。
- enable-namespace
使指定的名称空间可供使用。
- disable-namespace
阻止使用指定的名称空间。
- create-namespace
从指定的储存设备创建新的名称空间。
- destroy-namespace
去除指定的名称空间。
- enable-region
使指定的区域可供使用。
- disable-region
阻止使用指定的区域。
- zero-labels
擦除设备中的元数据。
- read-labels
检索指定设备的元数据。
- list
显示可用的设备。
- help
显示有关工具用法的信息。
24.5 设置持久内存 #
24.5.1 查看可用的 NVDIMM 储存 #
可以使用 ndctl
list
命令列出系统中所有可用的 NVDIMM。
在以下示例中,系统包含三个 NVDIMM,这些 NVDIMM 位于单个三通道交错集内。
root #
ndctl list --dimms
[ { "dev":"nmem2", "id":"8089-00-0000-12325476" }, { "dev":"nmem1", "id":"8089-00-0000-11325476" }, { "dev":"nmem0", "id":"8089-00-0000-10325476" } ]
如果结合不同的参数,ndctl
list
还可以列出可用的区域。
区域可能不会按数字顺序显示。
请注意,尽管只有三个 NVDIMM,但它们却显示为四个区域。
root #
ndctl list --regions
[ { "dev":"region1", "size":68182605824, "available_size":68182605824, "type":"blk" }, { "dev":"region3", "size":202937204736, "available_size":202937204736, "type":"pmem", "iset_id":5903239628671731251 }, { "dev":"region0", "size":68182605824, "available_size":68182605824, "type":"blk" }, { "dev":"region2", "size":68182605824, "available_size":68182605824, "type":"blk" } ]
空间以两种不同的形式显示:三个 BLK 类型的独立 64 GB 区域,或者一个 PMEM 类型的合并 189 GB 区域,后者将三个交错式 NVDIMM 中的所有空间表示为单个卷。
请注意,available_size
的显示值与 size
的显示值相同。这意味着尚未分配任何空间。
24.5.2 将储存配置为使用 DAX 的单个 PMEM 名称空间 #
第一个示例将三个 NVDIMM 配置成使用 Direct Access (DAX) 的单个 PMEM 名称空间。
第一个步骤是创建新的名称空间。
root #
ndctl create-namespace --type=pmem --mode=fsdax --map=memory
{ "dev":"namespace3.0", "mode":"memory", "size":199764213760, "uuid":"dc8ebb84-c564-4248-9e8d-e18543c39b69", "blockdev":"pmem3" }
这会创建支持 DAX 的块设备 /dev/pmem3
。设备名称中的 3
继承自父区域编号(在本例中为 region3
)。
--map=memory
选项从 NVDIMM 中设置出一部分 PMEM 储存空间,以便可以使用这些空间来分配称作结构页面
的内部内核数据结构。这样,便可以将新的 PMEM 名称空间与 O_DIRECT I/O
和 RDMA
等功能搭配使用。
最终 PMEM 名称空间的容量之所以小于父 PMEM 区域,是因为有一部分持久内存预留给了内核数据结构。
接下来,我们校验新的块设备是否可用于操作系统:
root #
fdisk -l /dev/pmem3
Disk /dev/pmem3: 186 GiB, 199764213760 bytes, 390164480 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 4096 bytes I/O size (minimum/optimal): 4096 bytes / 4096 bytes
与其他任何驱动器一样,在使用该设备之前,必须先将其格式化。在本示例中,我们使用 XFS 将其格式化:
root #
mkfs.xfs /dev/pmem3
meta-data=/dev/pmem3 isize=256 agcount=4, agsize=12192640 blks = sectsz=4096 attr=2, projid32bit=1 = crc=0 finobt=0, sparse=0 data = bsize=4096 blocks=48770560, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0 ftype=1 log =internal log bsize=4096 blocks=23813, version=2 = sectsz=4096 sunit=1 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0
接下来,可将新的驱动器装入到某个目录:
root #
mount -o dax /dev/pmem3 /mnt/pmem3
然后,可以校验是否获得了一个支持 DAX 的设备:
root #
mount | grep dax
/dev/pmem3 on /mnt/pmem3 type xfs (rw,relatime,attr2,dax,inode64,noquota)
结果是,我们已获得一个使用 XFS 文件系统格式化的,且装有 DAX 的 PMEM 名称空间。
对该文件系统中的文件进行任何 mmap()
调用都会返回直接映射到 NVDIMM 上的持久内存的虚拟地址,并且会完全绕过页面超速缓存。
对该文件系统中的文件进行任何 fsync
或 msync
调用仍可确保将修改后的数据完全写入到 NVDIMM。这些调用会刷新通过 mmap
映射在用户空间中修改的任何页面的关联处理器超速缓存行。
24.5.2.1 去除名称空间 #
在创建使用相同储存的其他任何类型的卷之前,我们必须卸载此 PMEM 卷,然后将其去除。
首先卸载该卷:
root #
umount /mnt/pmem3
然后禁用名称空间:
root #
ndctl disable-namespace namespace3.0
disabled 1 namespace
然后删除该卷:
root #
ndctl destroy-namespace namespace3.0
destroyed 1 namespace
24.5.3 创建使用 BTT 的 PMEM 名称空间 #
在下一个示例中,我们将创建使用 BTT 的 PMEM 名称空间。
root #
ndctl create-namespace --type=pmem --mode=sector
{ "dev":"namespace3.0", "mode":"sector", "uuid":"51ab652d-7f20-44ea-b51d-5670454f8b9b", "sector_size":4096, "blockdev":"pmem3s" }
接下来,校验新设备是否存在:
root #
fdisk -l /dev/pmem3s
Disk /dev/pmem3s: 188.8 GiB, 202738135040 bytes, 49496615 sectors Units: sectors of 1 * 4096 = 4096 bytes Sector size (logical/physical): 4096 bytes / 4096 bytes I/O size (minimum/optimal): 4096 bytes / 4096 bytes
与前面配置的支持 DAX 的 PMEM 名称空间一样,这个支持 BTT 的 PMEM 名称空间也会占用 NVDIMM 中的所有可用储存。
设备名称 (/dev/
pmem3s
) 尾部的 s
表示扇区,可用于轻松辨别配置为使用 BTT 的 PMEM 和 BLK 名称空间。
可按前一示例中所述格式化和装入卷。
此处显示的 PMEM 名称空间不能使用 DAX。它会使用 BTT 来提供扇区写入原子性。每次通过 PMEM 块驱动程序进行扇区写入时,BTT 都会分配一个新的扇区来接收新数据。完全写入新数据后,BTT 将以原子方式更新其内部映射结构,使新写入的数据可供应用程序使用。如果在此过程中的任意时间点发生电源故障,则写入内容将会完全丢失,在这种情况下,应用程序可以访问其旧数据,而这些数据仍旧保持不变。这可以防止出现所谓“扇区撕裂”的情况。
与其他任何标准块设备一样,可以使用某个文件系统格式化这个支持 BTT 的 PMEM 名称空间,并在该文件系统中使用它。无法将该名称空间与 DAX 搭配使用。但是,此块设备中的文件的 mmap
映射将使用页面超速缓存。
在上面两个示例中,所有 NVDIMM 提供的空间都合并到一个卷中。与非冗余磁盘阵列相同,这表示如果任何单个 NVDIMM 发生错误,整个卷的内容都可能会丢失。卷中包含的 NVDIMM 越多,发生类似错误的几率就越高。
24.5.3.1 去除 PMEM 卷 #
如上例所述,在重新分配空间之前,必须先去除卷和名称空间:
root #
ndctl disable-namespace namespace3.0
disabled 1 namespaceroot #
ndctl destroy-namespace namespace3.0
destroyed 1 namespace
24.5.4 创建 BLK 名称空间 #
在此示例中,我们将创建三个单独的 BLK 设备:每个 NVDIMM 各创建一个。
这种方法的其中一个优势是,如果任何单个 NVDIMM 发生故障,其他卷将不会受到影响。
必须针对每个名称空间重复以下命令:
root #
ndctl create-namespace --type=blk --mode=sector
{ "dev":"namespace1.0", "mode":"sector", "uuid":"fed466bd-90f6-460b-ac81-ad1f08716602", "sector_size":4096, "blockdev":"ndblk1.0s" }root #
ndctl create-namespace --type=blk --mode=sector { "dev":"namespace0.0", "mode":"sector", "uuid":"12a29b6f-b951-4d08-8dbc-8dea1a2bb32d", "sector_size":4096, "blockdev":"ndblk0.0s" }root #
ndctl create-namespace --type=blk --mode=sector
{ "dev":"namespace2.0", "mode":"sector", "uuid":"7c84dab5-cc08-452a-b18d-53e430bf8833", "sector_size":4096, "blockdev":"ndblk2.0s" }
然后,我们可以校验新设备是否存在:
root #
fdisk -l /dev/ndblk*
Disk /dev/ndblk0.0s: 63.4 GiB, 68115001344 bytes, 16629639 sectors
Units: sectors of 1 * 4096 = 4096 bytes
Sector size (logical/physical): 4096 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disk /dev/ndblk1.0s: 63.4 GiB, 68115001344 bytes, 16629639 sectors
Units: sectors of 1 * 4096 = 4096 bytes
Sector size (logical/physical): 4096 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disk /dev/ndblk2.0s: 63.4 GiB, 68115001344 bytes, 16629639 sectors
Units: sectors of 1 * 4096 = 4096 bytes
Sector size (logical/physical): 4096 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
针对 BLK 名称空间生成的块设备命名为 /dev/ndblkX.Y
,其中 X 为父区域编号,Y 为该区域内唯一的名称空间编号。因此,/dev/ndblk2.0s
为区域 2 的子名称空间编号 0。
如上例所述,末尾的 s
表示该名称空间配置为使用 BTT,也就是说,它用于基于扇区的访问。由于它们是通过块窗口
进行访问的,程序无法使用 DAX,但会对访问进行超速缓存。
与以往一样,这些设备必须格式化并装入系统后才能使用。
24.6 查错 #
持久内存的耐用性优于 SSD 储存,但也可能会用完。如果某个 NVDIMM 发生故障,就需要隔离出现问题的单个模块,以便能够恢复剩余数据并更换硬件。必须确定以下三条信息:
发生故障的是哪个 NVDIMM 模块:有缺陷模块的物理位置。
现在包含坏块的是哪个名称空间 (
/dev/pmemX
)。还有其他哪些名称空间或区域也在使用该物理模块。
确定故障模块以及使用该模块的所有名称空间和区域之后,便可以备份其他未受影响的名称空间中的数据,然后关闭服务器并更换 NVDIMM。
24.6.1 查找故障模块 #
服务器主板上的 DIMM 插槽中有一组 NVDIMM。
在产生的空间中,操作系统会创建一个或多个名称空间,例如 region0
。
在这些区域内定义了某些名称空间,例如 /dev/pmem1
或 /dev/dax0
。
例如,假设有一个区域由来自三个 NVDIMM 的空间构成,该区域已配置为以下三个名称空间:
NVDIMM 0 |
region0 |
/dev/pmem1 | |
NVDIMM 1 |
[X] |
/dev/pmem2s | |
NVDIMM 2 |
/dev/dax0 |
在我们的示例中,标记为 [X] 的 region0
部分已损坏或有缺陷。
必须:
确定包含受影响区域的 NVDIMM 模块。
如果该区域跨多个 NVDIMM 交错,此步骤尤为重要。
备份受影响 NVDIMM 上任何其他名称空间的内容。
在此示例中,必须备份
/dev/pmem2s
的内容。确定名称空间与 NVDIMM 物理位置(位于主板的哪个内存插槽中)之间的关系。
必须关闭服务器并取下机箱盖,找到、去除并更换有缺陷模块。
24.6.2 测试持久内存 #
要进行测试,需要有 nfit_test
内核模块。
ndctl
命令所对应 GitHub 页面中的单元测试
部分的步骤 1 - 4 中详细介绍了测试过程。请参见本章末尾的第 24.7 节 “更多信息”。
执行带有参数
list -RM
的ndctl
命令。此操作将显示坏块列表。
tux >
sudo
ndctl list -RM : : { "dev":"region5", "size":33554432, "available_size":33554432, "type":"pmem", "iset_id":4676476994879183020, "badblock_count":8, "badblocks":[ { "offset":32768, "length":8, "dimms":[ "nmem1" 1 ] } ] }, :此处标识了这个特定的 NVDIMM。
执行带有参数
list -Du
的ndctl
命令。此操作将显示 DIMM 的句柄。
tux >
sudo
ndctl list -Du { "dev":"nmem1", "id":"cdab-0a-07e0-feffffff", "handle":"0x1", 1 "phys_id":"0x1" }, : :这是 NVDIMM 的句柄。
执行带有参数
list --d DIMM name
的ndctl
命令。tux >
sudo
ndctl list -R -d nmem1 [ { "dev":"region5", "size":33554432, "available_size":33554432, "type":"pmem", "iset_id":4676476994879183020, "badblock_count":8 }, : :
24.7 更多信息 #
可以在以下列表中找到关于此主题的更多信息:
包含有关配置 NVDIMM 系统的指导、有关测试的信息,以及有关启用 NVDIMM 的规范的链接。随着 Linux 中 NVDIMM 支持功能的不断发展,此站点的内容也会不断扩充。
有关在 Linux 和其他操作系统中配置、使用非易失性内存,以及为使用此类内存的系统编程的信息。其中介绍了 NVM 库 (NVML)。该库旨在提供有用的 API 用于在用户空间中进行持久内存编程。
此文档面向内核开发人员,包含在当前 Linux 内核树的“文档”文件夹中。其中探讨了涉及启用 NVDIMM 的不同内核模块,列出了有关内核实现的一些技术细节,并介绍了
ndctl
工具使用的sysfs
内核接口。用于管理 Linux 内核中的
libnvdimm
子系统的实用程序库。另外还包含用户空间库,以及单元测试和文档。