对文件的直接访问(Direct Access)
1. 动机
page cache经常被用来被作为文件读写的buffer,也被用来提供页被mmap系统调用映射到用户态的页。
对于更像内存的块设备来说,page cache的页没必要从原始存储拷贝过来。DAX代码通过直接对存储设备的读写操作移除了额外的拷贝。对于文件映射,存储设备会被直接映射到用户态。
2. 用法
如果一个块设备支持DAX特性,你可以像以前一样格式化一个文件系统。现在DAX的实现只支持block size等于内核的PAGE_SIZE,所以当创建文件系统时你需要指定一个block size。当挂载文件系统时,用“-o dax”选项或者在/etc/fstab中加入‘dax’。
3. 给块设备驱动程序员的建议
为了在块驱动支持DAX,需要实现一个’direct_access’块设备操作函数。它被用于在扇区号(用512字节的扇区单元表示)和表示物理内存页的页框号(pfn)之间转换,最后会返回一个可以用作内存访问内核虚拟地址。
direct_access方法接收一个表示被请求字节数的‘size’参数。这个函数需要返回从那个偏移量开始可以连续访问的字节数,返回负数时表示发生了错误。
为了只会这个方法,存储设备必须随时可以被CPU字节粒度地访问。设备用paging技术让很大的内存暴露为一个较小的窗口的情况不能实现direct_access。同样了,如果你的设备有时会让CPU stall更长时间,你也不应该去实现direct_access。
以下支持DAX块设备可以为开发提供借鉴:
- brd: 以RAM为后端的块设备驱动
- dcssblk:s390 dcss块设备驱动
- pmem:NVDIMM持久性内存驱动
4. 给文件系统程序员的建议
文件系统的支持包括:
- 加入对标记inode为DAX的支持,只要在
i_flags
上设置S_DAX
这个标志。 - 借助
dax_iomap_rw()
为有S_DAX
标志的inode实现->read_iter
和->write_iter
操作。 - 为DAX文件实现文件映射操作,需要在VMA中加入
VM_MIXEDMAP
和VM_HUGEPAGE
标志,并在vm_ops
添加fault, pmd_fault, page_mkwrite, pfn_mkwrite的处理函数。这些handlers可能会调用dax_iomap_fault()
并传入fault的size和iomap的操作。 - 调用
iomap_zero_range()
,为DAX文件传入合适的iomap操作而不是block_truncate_page()
。 - 保证read, write, truncates和page fualts间有足够的锁保护。
分配块的iomap handlers必须保证分配的块被写零了并且在返回之前转换为写extents来避免未初始化数据通过mmap被暴露。
以下文件系统实现可为开发提供借鉴:
- ext2:详见Documentation/filesystems/ext2.txt
- ext4:详见Documentation/filesystems/ext4/
- xfs:详见Documentation/admin-guide/xfs.rst
5. 处理介质错误
对于每个pmem块设备,libnvdimm子系统都 (在gendisk->badblocks) 存储介质错误的位置,如果我们在坏的位置发生了缺页,或者有潜在的错误还没有解决,那么应用就会收到SIGBUS信号。Libnvdimm也允许通过简单的写受影响的扇区 (通过pmem驱动,如果底层NVDIMM支持clear_poison这一ACPI定义的DSM) 清空这些错误。
由于DAX IO一般不通过驱动/bio的路径,应用或者系统管理员从之前的备份或者冗余中恢复丢失数据的方法如下:
-
删除受影响的文件,并从备份恢复(系统管理员路径):这会释放被文件用着的文件系统块,下次他们被重新分配前,会首先通过驱动被置零和清除坏扇区。
-
截断或者打洞部分有坏块的文件(至少只需整个对齐扇区被打洞,而不是整个文件系统块)。
这两个基本的路径允许DAX文件系统在有介质错误的情况下继续操作。更健壮的错误恢复机制未来也可以在此基础上建立,比如,通过DM在块层,或者在文件系统层,支持冗余/镜像。这都需要依赖以上两个原则,错误的清除必须用通过驱动发送IO或者通过驱动清零的方法完成。
6. 缺陷
即使内核或者其模块的数据存储在支持DAX的文件系统和块设备,它们依然得拷贝到RAM上。
DAX代码现在在有虚拟映射缓存的ARM, MIPS和SPARC架构上不能正确工作。
如果没有struct page
结构去描述,在一段DAX文件映射的用户内存页上调用get_user_pages()
函数会失败。这个问题已经通过在部分设备驱动中为驱动管理的页加入可选的struct page来解决(具体是怎么做的可以参考 drivers/nvdimm中的CONFIG_NVDIMM_PFN)。在没有struct page的情况下,对非DAX文件的O_DIRECT读写会失败(注意对DAX文件的O_DIRECT读写是可以工作,被访问的内存(buf?)才是关键)。不支持struct page引起的其他问题还包括RDMA、sendfile()和splice()。