原文网址:https://lwn.net/Articles/717601/
原文题目:HMM and CDM
原文作者:Jonathan Corbet
原文时间:2017年3月22日
异构内存管理和一致性设备内存
在2017Linux存储、文件系统和内存管理会议的内存管理分论坛上,第一个主题继续开展全体会议关于“HMM问题和相关补丁状态更新”的讨论。Glisse重点讨论了关于HMM还需要做哪些工作。Balbir Singh提出解决HMM问题的另一种方法,以便获得更多硬件支持。
HMM的推进工作
HMM的讨论从一个小组提出的问题开始:支持HMM的设备要具备哪些特征?答案是,硬件需要支持某种形式的页表结构,CPU上的驱动软件可以配置该硬件对每个内存页的访问权限。例如,根据具体内存页上代码类型,执行权限(访问权限包括:执行、读、写)要么在CPU上、要么在GPU上、要么两者都没有。HMM还需要防止两者同时写相同的内存页,因此GPU还需要能做故障处理。
Dave Hansen问,除了IOMMU(IO内存管理单元,在ARM上用于处理DMA虚拟地址翻译)还需要什么?Glisse回答说,HMM上使用IOMMU,主要是用来保护系统不受到IO设备破坏,而不是地址翻译。Mel Gorman补充说,HMM要求在CPU和GPU两端都能捕获到对指定内存页的写故障,以及提供各自的(different意思?)保护机制——IOMMU做不到。
还有一项正在进行的工作就是,用内核共享内存KSM(kernel shared memory)机制实现写保护;这些补丁很快就会提交。KSM允许同一个内存页映射到多个地址空间上(理解:这类似进程间共享内存通信机制),这点对于多GPU系统而言非常有用,这些GPUs可能访问相同数据。
Andrea Arcangeli发起了关于写故障处理的讨论。正常情况下,对共享内存页的写故障会触发写时复制(COW:copy-on-write)操作,但HMM情况有所不同;HMM机制要保证“写端对内存页的独占访问”。Gorman表达对HMM相关进程调用fork()之后进行“写操作”语义的担心。fork()函数将可写页标记为COW,但现在还不清楚的是如果将COW页同时映射给父/子进程后会发生什么后果;写故障处理最终会将内存页的访问权限按照时序规则指定给其中一个进程。(这里的时序规则是什么呢?是不是子进程优先或父进程优先呢?)
为了完全避免此类问题,Gorman建议,HMM使用的内存都用madvise()系统调用标记为MADV_DONTFORK(madvise系统调用是应用程序向内核提出的关于内存块的操作建议,内核使用这些建议信息进行优化);这样一来,fork()生成的子进程根本就不使用父进程的HMM相关内存。他强调这必须要做到。有人解释到:fork()会将所有HMM内存访问权限都给到父进程,而且GPU不能访问任何HMM内存;Gorman停歇片刻后说,对此他很高兴:HMM内存只与父进程相关,而对子进程不可见。
得到解释后,Gorman接着问,将HMM纳入内核是否还有其他障碍。Hansen说,在已经安装巨量内存的系统上HMM还存在问题;在这种系统上,已经没有多少物理地址范围留给GPU内存了。Gorman回答说,这个问题只有在实际应用才会出现,用户也不希望看到;但这属于硬件限制,并不能成为不接受HMM补丁的原因。
Dan Williams关注到,HMM补丁将GPU内存定义到ZONE_DEVICE区,这个区通常是持久内存区(持久内存:掉电后单元数据依然保持的内存)。这两种内存完全不同,虽然ZONE_DEVICE区可以用作HMM,但是如果框架开发者不理解所有访问ZONE_DEVICE区的代码行为,这些开发者所作的修改很可能破坏其中某些代码的行为。Gorman建议Williams重点从这个角度出发对HMM代码进行审查;Gorman说,代码的长期可维护性是一个根本性问题,需要谨慎考虑。Johannes Weiner建议可以用ZONE_HIGHMEM区,但是Gorman叫他回家(理解:彻底否定了这个建议)。
最后一个关注点是,HMM代码还缺少相关设备驱动;如果现在就合并进内核主线,这就是一个没有任何用户的死代码。基于Nouveau的NVIDIA GPU驱动似乎能赶在4.12合并窗口期完成。Gorman建议Andrew Morton在没有可用的设备驱动之前将HMM纳入到-mm分支(-mm分支是Linux内核关于内存管理的专向分支,而不是主线分支),Morton问道,没有设备驱动是否真的会成为阻碍代码合并到上游主分支的原因。他更希望,在合并窗口期即将结束的时候,他能够向Linus Torvalds充分解释为什么要提交这些代码。
最终的结论是,HMM还有一些小问题需要修改,但离被内核主线接纳的道路也越来越清晰了。
一致性设备内存结点
Singh接下来给出了IBM关于HMM的观点;对于IBM而言,大部分问题已经在硬件层面解决了。在正确配置的系统上,设备内存看上去就在自己的NUMA结点上,只是这个NUMA结点没有CPU组件而言。这种设备内存和系统其他部分也保持完全一样的缓存一致性。在Linux上已经开发支持一致性设备内存结点(CDMs:Coherent device memory nodes)的相关补丁。
这类硬件在Linux上该如何工作还存在很多问题。希望能另外提供一种内存分配方法:让用户空间的应用程序可以选择是在常规内存还是CDM内存中进行内存分配。因为内核对CDM内存的使用缺乏了解,因此需要谨慎处理内存回收。基于显而易见的的原因(?),需要关闭常规的NUMA均衡调度机制,否则页面在迁入或迁出CDM所在的结点时会出错。如果需要进行这样的页面迁移,应该用到DMA引擎来加速。
首先,CDM内存由CPU上的软件进行分配;然后CDM设备/处理器上的软件再执行对该CDM内存的访问。CDM设备/处理器可以通过指针既能访问CDM内存,也能访问常规内存(透明性:两者的访问在软件上仅仅是指针的区别)。基于观察到的用户访问模型,内存希望能够迁移到最合适的NUMA结点上。Hansen提醒说,内核的NUMA均衡代码虽然非常好,但是大部分用户会将其关闭;是否还需要专门为这种配置实现一个函数调用[will there really be a call for it in this setting]?。Singh回答道,NUMA均衡开启和关闭两者完全不同[it can make a big difference];来自应用的提示非常有帮助。
迄今为止,补丁还包括了利用cpuset机制将CDMs进行隔离。内核还缺乏足够的信息,因此内存均衡的效果并不理想。为了隔离CDM内存,将内存区链表进行拆分[The zone lists have been split to separate out CDM memory];这样一来,在大部分系统上CDM内存被隐藏起来了,从而避免与常规内存混淆。会议最后提到,此次讨论还没涉及“透明巨页迁移”相关影响,这个话题下次讨论。