阿里内核月报:2017年04月

简介: Vlastimil Babka 是 kcomactd 的作者,他在 LSF&MM 2017 的内存管理讨论上提到,这个工具的一个目的是腾出更多可用的连续物理页,用于分配高阶内存,这在THP等特性中会有大用处。目前内存压缩的实现在内核中是一个按需执行的机制,他打算让这个机制稍微做点儿预测的工作。

1. Gregg: perf sched for Linux CPU scheduler analysis

随着 Linux 4.10 perf sched timehist 新特性的推出,Linux 的 perf 工具又增加了一个新的 CPU 调度器性能分析的子命令。

perf sched timehist 可以按照调度事件表示调度延迟,其中包括了重要的调度相关的时间信息:

  • 任务等待被唤醒的时间 (waiting for sleep)
  • 任务从被唤醒之后到运行态的调度延迟 (waiting on per-cpu run-queue)
  • 任务在CPU上实际执行的时间 (running on the cpu)

由于 timehist 子命令隶属于 perf sched 子命令,Brendan Gregg 同学特意为此写了一篇博客(译文) 总结了 perf sched 子命令的基本使用。

对急于了解这篇文章是否对自己有用的同学,可以快速浏览一下博客里的 perf sched 例子里的输出.

2. Proactive compaction

Vlastimil Babka 是 kcomactd 的作者,他在 LSF&MM 2017 的内存管理讨论上提到,这个工具的一个目的是腾出更多可用的连续物理页,用于分配高阶内存,这在THP等特性中会有大用处。目前内存压缩的实现在内核中是一个按需执行的机制,他打算让这个机制稍微做点儿预测的工作。

他目前的想法是,让内存压缩在后台进行,不与任何进程上下文关联。 kswapd 内核线程会在内存分配的慢路径中唤醒,然后回收一个特定数目的页面;然后它再唤醒 kcompactd 线程来压缩内存,直到给出的一个预期order的页面可用,或者扫完整个内存zone都腾不出空间为止。不过这样最后最多也就分出一个高阶的页,所以这是远远不够的。(感觉这个是已经在 upstream 里的 kcompactd 的实现呀)

他抛出一个问题,怎么才能让这个机制更牛逼一些。Michal Hocko 建议,加一个配置选项,可以设置水位(watermark)和时间周期(time period),压缩线程可以反复唤醒去确保预期的N个页面可以被腾出来。不过 Babka 反驳说,把参数暴露给系统管理员不太靠谱,THP 的 allocation rate 或者网络的吞吐量这种场景,是很难量化出要多少个可用的空闲页面的。所以最好还是让系统智能一点,自己调。

那如果是个自动调节的方案,我们对这个实现的输入是什么呢?首先应该是最近每个order的页面的需求量,当然以后靠谱了可以带点儿预测功能,预测将来各阶内存各需要多少页面,在此之前,只能以过去的需求量来预测将来。当然可以跟踪一下请求页面分配的来源的重要程度,比如说THP和SLUB分配高阶内存相比,后者对高阶可用内存的需求就更要紧。另外一个输入就是,最近的内存压缩的成功率是多少。如果成功率过低,就不用再尝试压缩了。此外,Mel Gorman 还建议,记录一下压缩线程本身在执行的时候,有多少压缩请求发过来。

Andrea Arcangeli 指出,压缩后创建的大页要重点保护一下,要不然,内核辛辛苦苦拼出一块连续大页,刚分配完,就可能被打碎去回应一些小页的分配,太不值当了。当然如果这个压缩本身就是来自一个分配请求触发的,这个问题不会出现,因为大页分出来马上就被用了。综合考虑,保护大页这个事情,还是要好好跟进一下。

Babka 介绍了一下当前这个预测特性的进度,还处于 RFC 阶段,最近刚发出一系列 RFC 补丁。他系列尝试记录用更多的 kcompactd 活动而成功分配的数字,当然这个数字是在内存不紧张的情况下得出的;另外一点,这个patch现在还没有记录前面提到的分配请求的重要程度,或许GFP标记可以用来干这事;还有一点,它现在只是一直不停运行压缩,直到足够多的高阶内存可用为止。

还有一个工作就是评估这项工作的价值:他说现有的 benchmark 显示,基本都能跑到上限,没什么性能损失。

对于这项工作,大家也有一些顾虑。比如说,后台压缩会不会增加系统的电源消耗。Hocko 说,正因为有这种风险,所以要让SA自主配置。Babka 说,这不是大问题,内存压缩是按需启动的,平时空着的时候基本上不会增加系统电源消耗。

最后,Ancangeli 建议那些需要大页场景的子系统可以注册到压缩代码中,标明他们需要多少大页以方便后续压缩。Babka 说他不想加那么多可调的参数。Johannes Weiner 说可以添加一个开关,因为任何预测系统都有可能带来资源浪费的风险,当然其他接口就不用加了。大体上,大家认为这个工作是有价值的,不过特性刚开始的实现可以尽可能简单,以后再来按需复杂化这个特性。

3. Cpusets and memory policies

“cpusets”控制着给定进程运行哪些处理器上。而内存的(或“mempolicy”)机制控制了如何在NUMA节点之间分配内存。正如Vlastimil Babka在2017年LSFMM上解释的那样,这两种机制一起使用配合的不好,会出现一些想不到的问题。

cpusets是一个特权控制机制;非特权进程通常不能更改其CPU分配。相反,Mempolicies是进程本身控制。这两种机制一起使用时希望可以在cpuset和mempolicy的交集定义的节点上分配内存。而如果这个交集的节点为空的话,则对系统不会产生影响,但实际上并不是这样。
Babka介绍了可能发生以下情况:想象一个进程运行在4个节点的系统上;首先将cpuset和mempolicy都设置为节点0和1。在这种情况下,内存将会在这两个节点中分配。如果cpuset更改为节点1和2,同时内存分配也会到这两个节点。但是,如果cpuset修改为单个节点(比如节点2),然后再恢复到原来的0和1,则结果是进程将仅仅从节点0上分配了;内核将失去mempolicy配置的两个节点的信息。

这个问题在2.6.26内核通过在set_mempolicy()系统调用中添加几个标志解决掉了。如果进程设置mempolicy时使用MPOL_F_STATIC_NODES标志,则在更改cpuset时内存策略不会更改。而MPOL_F_RELATIVE_NODES会使得内存策略与cpuset更改保持一致,同时会记住原始策略,因此这样不会出现上述单节点分配的情况。但如果cpuset和mempolicy之间没有交集会怎样,特别是有MPOL_F_STATIC_NODES时?答案是会从cpuset节点分配内存。Kirill Shutemov认为这样可能会存在内存分配失败,但是如果不这样做则会破坏ABI。最好方式是能在其他不同节点上分配内存,而不是杀死一个工作的任务,特别是如果该任务在运行在老内核上。一般如果这个处理方式能确定,set_mempolicy()接口ABI会被破坏

当前这种实现中问题是cpuset被改变时mempolicy也被改变,如果出现一个空节点列表时会导致内核OOM,这时OOM killer会被调用,对用户来说这是不公平的。要解决这个问题显得必要而紧迫。mempolicy随着cpusets更改而更新这种方式已经确定。在这种情况下,对于静态情况,解决方法简单,因为节点不会改变。相反,动态情况下重新映射需要在运行时完成; 这是要解决的起来比较复杂,默认情况下可能没有可行的修复方法。

这次讨论的重点是如何修复动态的情况。它可能涉及将可以使用内存区域列表移到cpusets中,这是多年前mempolicies的一个实现方式,计划看看这个补丁在当时是怎么解决的。

4. Slab reclaim

”回收“是一个寻找系统内存的过程,它会将当前不在使用且能在被回收后恢复的内存回收。Michal Hocko 在 2017 Linux Storage, Filesystem, and Memory-Management 峰会提及:从slab分配器中回收对象的机制还远远不够完美。他同Christoph Lameter 一起浏览几个改善现状的途径。

Slab 回收时会调用”收缩回调“;当系统需要更多内存时,收缩器被调用并开始释放一些对象。使用者希望收缩器在这个过程中能提供更多有用的功能,但这里最大的问题是,不存在对象与页表的对应关系。如果内核要释放一个页表,那么页表内的所有对象都将被释放,并没有一种机制可以让收缩器对指定页表中的对象进行收缩操作。

Hocko 说道,几年前,Dave Chinner 提出了一个有趣的想法:可以依赖slab分配器来加强回收。如果分配器维护一个LRU(最近使用过)列表,他们就可以更好地回收这些对象。但是,因为没有人关心实现那个提议,所以至今这个提议仍然是一个想法而已

其后,Lameter 介绍了一个截然不同的思路,并且他为此努力了一段时间。这个思路通过在slab分配器上添加几个回调,这些回调可以在页被释放时向子系统要求重新定位对象。第一个回调类似如下

void *isolate_object(struct kmem_cache *cache, void **objs, int nr, int node);

这个方法准备重新分配**objs指向的对象;除此之外,它会保证这些对象直到整个操作完成前是稳定不可变。一旦这个操作完成,第二个回调将被调用

void migrate_objects(struct kmem_cache *cache, void **objs, int nr, int node, void *private);

这个回调将尝试把objs移动一个新的内存位置;当然也可以释放objs。一旦一个slab页里面的对象都完成移动操作,就可以释放这个页了。

上述机制的第一个实现版本已经在2007年完成,Lameter 暗示合并该实现并开始使用它的实际可能马上就到了。随着内存发展,容量越来越大,对更好的slab分配器的需求也会更加紧迫
Andrea Arcangeli 提议了一个不同的方法:从虚拟映射的页中分配slab对象,这样一来,如果这个页需要被重新定位时,只要修改该页的映射。通过这个方式,能方便在节点之间移动已分配的slab对象,却又能避免追踪该对象指针。这避免了上述Lameter方法中的一个主要缺陷————需要针对每一个slab类型增加移动接口

Rik van Riel 指出这个方法的问题在于不能将slab对象移动到碎片的页表中。而这种需求恰恰是最重要的使用场景,他看到过许多系统用在slab的内存有95%是空的。 Arcangeli 回应道,这个机制有三种使用场景:内存热插入,内存整理和避免内存不足。他说,他的虚拟映射方案解决了最重要的这三个问题,而且可以很好的兼容搭配kmalloc()工作,否则兼容上也是一个问题

本次会话就此到已段落,并没有达成一个最终的决议。会话会在邮件列表中继续,也许基于特定的补丁继续讨论

5. Huge pages in the ext4 filesystem

透明大页特性加入内核时仅支持匿名内存(非文件后端)。2016年,页缓存支持大页功能加入进来,但仅支持tmpfs。由于在某些负载下大页将显著地提升性能,这使得扩展支持其他文件系统受到欢迎。LSF/MM 2017会议上,Kirill Shutemov领导了仅有的一个文件系统和内存管理联合议题,讨论在ext4文件系统中支持大页。

他开始提到tmpfs目前支持得很好,是时候进入下一阶段支持一个真正的文件系统了。复合页用于在系统内存映射中表示大页,开始的一系列小页组成大页头页,余下的则是尾页。大多数重要的元数据存储在头页中。使用复合页允许大页在LRU链表中以单个条目表示,同时所有缓冲区头部与头页绑定。不同于DAX的是,透明大页没有任何文件磁盘布局的限制。

使用tmpfs,创建一个大页导致基树中额外增加512个条目,这将不能在ext4中工作。同样需要添加DAX支持以保持一致。还存在一些其他的问题,例如,预读当前不能在大页下工作。最大的预读窗口是128KB,远小于大页的大小。他不确定这是否是个大问题,但如果是则需要解决。大页也导致任何页缓存中的影子条目被忽略,这将使得系统的页回收决策变得越来越糟。

他强调大页需要避免打破现有的语义,这意味着有时需要回退到小页。页迁移是可能发生的一种场景。一个相关的问题是,很多系统调用提供4KB精度,这将妨碍大页的使用。使用ext4加密同样将强制回退到小页。

鉴于以上所述,他问到是否有理由不再继续ext4的大页支持?他的补丁已经有一段时间了,目前计划变更基线到当前的页缓存工作上并重新发出。

Jan Kara问到是否有必要每个文件系统都感知大页,这会增加复杂度,或者说文件系统是否可能一直使用小页。Shutemov回复这不会永远都是个选择。比如整个复合页的单个最新标识。更清晰的抽象并在任何可能时候隐藏差异是有意义的,他一直在致力这方面的工作,但解决方案并非总是显著。

Kara继续说,这需要某些合适的数据结构以追踪子页状态。目前内核使用缓冲区头部链表,但可能需要修改。更细粒度的追踪将会有优势。但他重申他看不到文件系统需要知道页缓存中页大小的理由,且让每个文件系统了解可变大小的页缓存将会是个重大的工作。Shutemov同意这个担忧,但他说正确的途径是先实现单个文件系统,使之工作后,再尝试抽象。

Matthew Wilcox则抱怨到当前的工作仅支持两种页大小,而他更想能够处理任何复合页大小。抽象出代码将使得整个事情更为清晰。一开始代码无需真正能处理每个页大小,但应当为此做好准备。

Trond Myklebust说想要在页缓存中合适地支持大页。NFS代码中,他需要很多的循环和聚集以迎合合理的块大小。Ted Ts'o问到是否已经是时候分离也大小(PAGE_SIZE)概念和页缓存中存储数据大小(PAGE_CACHE_SIZE)。过去内核曾区别对待这两者,但不久前该区分又被移除了,导致更清晰的代码。Wilcox回复过去从来没有明确定义过PAGE_CACHE_SIZE的含义,同时抽象处理页缓存大小不是代码清理,而是性能更好。他建议ext4支持多种块大小将使得该工作变得更容易,但Shutemov很快补充到他无法对此承诺。

Ts’o说大块的问题在于,当一个进程遇到4KB页缺页时,文件系统需要引入一个大块,这从来都不是一件容易的事情。文件系统的人认为这是内存管理的问题,而内存管理的人则指向文件系统,这种状况持续了很长时间。Wilcox认为这是内存管理的问题,他在页缓存中支持可变页大小的工作应当能处理其中的大部分。

Andrea Arcangeli说当大页无法分配时将发生问题。透明大页代码不需要这种分配,而是总回退到小页。他不想看到这种修改。相反,真正的解决方案是增加页大小基数。Rik van Riel回答到,如果页缓存包含多个大页,将可用于回收,同时应当比现在更容易分配。

议题结束时,Ts'o注意到相比ext4,内存管理将需要更大的修改。如果大家乐意这个工作,可能是时候合并这些想法,相关遗留问题可以接下来再解决。或者,尝试先进一步衍生出接口。他说,这更大程度是内存管理的决策,因此他将服从内存管理组。Shutemov说页缓存接口是最难的部分,他将着眼于使得该接口和文件系统更加清晰。但是,他提醒到,一开始尝试抽象一切毫无意义。

6. Fast memory allocation for networking

在2016年Linux存储、文件系统和内存管理(LSFMM)峰会上,Jesper Dangaard Brouer提出用一种新的内存管理API来满足内核网络协议栈对性能越来越高的要求。2017年,他重回LSFMM峰会内存管理组分会场像社区介绍他最近在这一方向上的已经做的以及还需要做的工作。

网络数据包处理速度惊人,一个10GB的以太网链路每秒可以处理近1百50万个包。也就意味着在目前的硬件环境上,每个数据包留给操作系统的处理时间只有大约200个 CPU时钟周期,而且随着链路带宽的提升留着操作系统的CPU时钟周期变得更少。

解决这个问题主要的方法在于对多个数据包尽量批处理。但批处理并不是什么灵丹妙药,批量处理10个包并不意味着会有10倍的性能提升。但是它仍然会有很大的帮助,因此必须这么做。多种多样的kernel-bypass方案(如DPDK)已经显示以如此高的速率处理包是可能的。他们采用批处理和特殊的内存分配器;另外,他们同时还采用polling的技术,通过浪费CPU来减少唤醒。Brouer认为内核可以做得比那个更好。

目前已经在尝试的一个技术是express data path(XDP),大约在4.9内核的开发周期中。通过使用XDP,并且避免使用内核的内存管理层,就有可能可以在内核实现线速。这就意味着需要把数据包放在缓存中,并且保持他们为DMA操作连续映射。当这些操作完成后,一个使用XDP的"drop every packet"的简单应用可以达到17Mpps,而另一个重传每个到达这个设备接口的每一个数据包的应用可以达到10Mpps的处理速率。这些benchmarks可能看起来太理想化,但是他们却可以解决现实世界中的问题:譬如防范DOS攻击以及做负载均衡。Facebook目前正在使用XDP做这些任务。

目前XDP还没有完成的工作是真实数据包的转发,因为目前这依赖内存管理层。而目前内核中页分配器对于这种场景显得太慢了,因此当前的以太网驱动的工作方式是重用他们申请的这些页,现在每个高性能的驱动都自己实现了一些这类的技术,因此,把这些技术挪到通用代码库会更好。

这个问题的实质是设备驱动程序需要获取到DMA-mapped的页并且一直保留着他们以便重复使用。内存管理层可以通过提供更快的per-CPU的页cache来帮助实现这点,但它仍然不能通过简单的在驱动中复用页来实现。因此,Brouer提出另一个方案,为DMA-mapped的页创建一个per-device的分配器。通过将这些页映射给这个设备,这种分配器可以大幅减少内存管理的开销。

Matthew Wilcox问道现有的DMA pool API是否可以用来实现这个目的。Brouer说,现有的DMA pools是为coherent DMA操作(长生命周期的缓存可以同时被CPU和设备访问)操作设计的,而网络数据包使用streaming DMA操作(短生命周期的缓存同一时刻只能被CPU或者设备访问)。

Brouer说,他真正想要的其实是,在page结构体中带一个destrutor的回调函数,当这个页的引用计数降到0时,destructor能够被调用。有了这个回调函数就可以实现“偷用”这个页了,从而使这个页对于同一个设备一直可用。其实,这种回调机制在内核里已经存在了,但只对高阶内存可用,而想把这种机制带到单个页上需要从已经相当拥挤的page结构体中再找地方,而这并不简单。带destructor回调的页可能还需要一个flag来标记他们,而这又是一个问题,因为目前这些flags空间已经紧缺。在一些讨论中,已经有一些tricky的方法来把所需的信息放到struct page结构体中,因此,看起来是可以找到一些方案来解决这个问题的。

Brouer总结中提到一些benchmark表明在4.11内核中,性能会更好,这要感谢4.11内核中Mel Gorman所做的page-caching的提升。但这些开销仍然有点大,而这些开销的大部分是维护内存区的统计信息所带来的。这些统计信息对于内存管理子系统本身的运作并不是必须的,但是有一些其他内核模块会使用这些统计信息来进行优化。

因此,这些统计信息确实需要保留着,但是在生产系统中禁用它们是可能的,这些统计的代码可以在他们不需要的地方通过宏的方式注释掉。

最后,Brouer问大家是否对DMA-page pool机制有反对意见,现场没有人马上提出反对意见,但是现场的开发者表示在提出确定的结论之前会先去看看patch。

7. The next steps for swap

在内核的内存管理子系统中, swap不是主流. 通常来说, 如果系统开始做swapping了, 性能就已经开始下降了, 所以优化swapping没多大意义. 现在ssd用得很广泛, 改变了格局, swapping又有了新的用处. 2017 linux 存储, 文件系统, 内存管理会议上, Tim Chen领导了一个议题, 看看swapping的性能是否还能提升.

以前Tim Chen就是干这个的, 最开始的swap scalability patch已经被合并了, 下一步就是提升swap预读性能. 现在机制发生了变化, 现在的预读是按照它们被换出的顺序读取页面, 要提升的是在预期需要之前预读页面. 他强调, 最有顺序和混合访问模式没必要一起用, 不然性能会很差。

最近提交的基于vma swap预读的patch尝试通过监控每个vma区域的page in的行为提升预读的性能, 如果出现某个vma区域会串行访问, 他的预读窗口就会增加, 以至于更多的页在需要之前可以被预读, 对于随机访问的情况, 预读就没什么意义, 所以预读窗口就会变小。

Rik van Riel指出现在的预读机制是为了多媒体而设计的, 那么基于vma的预读机制如何在这上面工作呢? Tim Chen尴尬了一下, 表示没做过这方面的尝试. Rik van Riel接着补充道, 对于旋转设备, 读一个block能把相邻的一堆block都顺便读出来, 所以就应该多读一些数据, 但是对ssd就不一样了, 他建议对于预读, 不同的设备应该有不同的行为来与之对应。

Matthew Wilcox指出, 相对于预读, 换出时间才是真正的问题, page是根据LRU列表被换出的, 但是LRU不能反映下次什么时候再次需要, 还有可能的是向swap写有可能被缓冲. 被交换的页面会进入victim cache, 在写入存储设备之前被排序. 这种方法的价值在座的都不太清楚, 虽然鉴于访问模式可能随时间而改变。

下一个主题是对大页的交换, 现在的情况是, 第一步, 把大页分成单页, 然后把他们单独交换出去, 这样效率不高, Tim Chen和他的小伙伴们打算对这个过程改进一下. 第一步是推辞分割大页直到swap空间被申请出来为止, 预期的结果是申请了一个页面集合, 然后一次性的完成写入, https://lwn.net/Articles/702159/ 这个patch实现了这个修改, 测试的结果是swap-out性能又了14%的提升.

下一步是推迟分割大页, 直到换出操作完成. 这个功能的patch还在开发中, 标准测试显示对于swap out性能有了37%的提升。

Tim Chen说, 最后, 直接将大页换进是个好主意, 不过还需要再想一想, 这并不是一个绝对的优化窗口, 如果一个应用程序只需要一部分页, 没理由要把整个大页都换进来. 一种有可能的实现是如果这个页被标记了MADV_HUGEPAGE , 并且还有一个大的预读窗口, 就可以把整个大页都换进来。

还有一点剩余的讨论是如何验证这些patch是可以工作的. 标准测试的结果是最好的激励. 有一点要注意的是, 只要没有拖慢kernel的编译, Linus Torvalds是不喜欢卡着这些patch的. Michal Hocko说这些patch有意思,但是他们在优化一个不太容易发生的事情, 当前的code假设我们不想要swap. 但是Johannes Weiner说swap-out有新的合理的内容, 大页的批量换出操作方式可以提升性能.

下一个讨论是使用DAX直接访问机制来交换页面. 如果交换道持久内存阵列, 那些页仍然可以被直接访问而不需要再读回到ram中, 这个是swap永远可以工作的摸下. 这种情况下, 最难的部分是要决定什么时候把这些页读回道内存. 那就是内存被经常访问, 特使是写访问的情况下, 读回来会更好。

Wilcox说要不要读回来取决于内存和持续内存之间的性能差异, 在某些情况下, 永远都不用读回来, 有些时候, 比如, 持久内存其实就是hypervisor下的DRAM, 有一些讨论是关于使用系统PMU来追踪页访问, 但是这个思路也没下文了. 内核开发者不喜欢用PMU, 因为存在性能损失, 而且结果也并不总是有用。

在一些讨论结束之后, 最终达成的结论是内核应该预读随机大小的一些页到内存, 运气好的话, 频繁访问的页会在那里, 剩下的就回到swap空间去。

最后, 有一个简短的讨论是将来关于交换设备锁的优化, 看起来仍然有很多争论, 即使最近关于scalability提升之后。

8. HMM and CDM

2017 LSF/MM的第一个内存管理的topic是关于device memory不可寻址的问题,这里并非指CPU不可访问,而是指system memory与device memory缺乏cache-coherent语义,在使用的时候存在诸多不同以及由此引发了很多问题(比如DMA前需要pin住system memory)。目前有两个尝试的解决方案:异构内存管理(HMM) 与 cache-coherent设备(CDM)。

HMM开发已持续了几年,目前已经是v20版本,不过离真正进入主线还有一段距离,相关patch见:git://people.freedesktop.org/~glisse/linux。首先简单介绍一下HMM的功能,主要包括两类:(a). address space mirroring, 即保证CPU page table(即CPU侧mmu)与 device page table(即device侧mmu,逻辑上的概念,比如set_pte()与clear_pte()两个操作可以解释为dma_map_page()与dma_unmap_page())同步。底层同步的机制则依赖于kernel已有的mmu notifier特性,带来的好处之一就是DMA 与 page migration可以同时存在(此前需要pin住内存,然后DMA)。目前HMM实现了一套框架,update device page table具体实现则需要相应的device driver负责填充ops(参考例子:hmm_dmirror字符设备)。(b). migration to and from device memory,即在system memory与device memory间无缝地进行页迁移,流程与普通的swap类似:CPU访问内存时触发page fault,进而将数据从device memory DMA至system memory。

本次会议讨论的几个点:(1). HMM对硬件feature的依赖,目前明确的是硬件需要能设置页表的访问权限,比如允许CPU或GPU(但不能同时)拥有excute权限;(2). HMM与IOMMU的异同:IOMMU对I/O设备进行了安全隔离,保护系统,HMM则需要捕获write fault,CPU与device触发时执行的处理逻辑也不一样;(3). 基于KSM机制,正在开发的一个write protection特性允许同一个系统上的多个GPU设备访问相同的数据区域。这里引入了一个问题:传统的写异常将触发COW,而HMM则是保证写者拥有唯一的写权限。fork()系统调用基于COW来复制地址空间,在HMM里则是先触发page fault的进程(child或parent)将取得page的所有权,这将依赖于触发的时间点,引入不确定性。为了避免该问题,Gorman建议强制进程通过madavise()系统调用设置MAD_DONTFORK来避免该问题,这样所有的内存都将属于父进程。(4).Dan Williams提到HMM将GPU内存放置到ZONE_DEVICE区域,目前persistent memory也使用了该区域,二者共用逻辑的话容易导致一些bug,目前只能通过仔细review来避免错误。(5). 目前还没有具体的driver使用了HMM,NVIDIA GPU的Nouveau-based驱动有希望在4.12的合并窗口中提交,HMM的相关patch仍然暂时保留在-mm分支。

CDM是IBM提出的另一个思路,主要通过硬件来解决目前所遇到的问题。在一些嵌入式系统中,device memory可以看成是一个没有CPU的NUMA节点,且提供cache-coherent语义。目前仍在开发中:https://lwn.net/Articles/713035/ 。本次会议讨论的几个点:(1). 如何在Linux中使用这些设备?预期是提供给用户可选择的内存分配机制:用户决定申请CDM内存或者普通内存。NUMA balance需要关闭,因为直接在CDM内存与普通内存间进行页迁移存在问题。如果一定要页迁移,可通过DMA来加速。(2). 已有patch通过cpuset机制来隔离CDM;整个系统由于缺乏对完整的内存信息,无法实现内存均衡。zone链表将CDM隔离开来。THP页迁移的功能还有待进一步讨论。

9. Stack and driver testing

LSF/MM 2017会议上一个存储和文件系统联合议题中,Chaitanya Kulkarni领导了一个关于测试存储设备驱动和存储栈其他部分的讨论。他想以一个内核紧耦合的测试驱动框架作为起步。目前已有相关的零散测试套件,他想收集一些关于统一测试框架的想法和反馈。

Hannes Reinecke问到Kulkarni想要测试目标类型是什么?功能,性能,亦或其他?Kulkarni说,第一步是功能测试,但最终目标将转向更复杂的测试集。

James Bottomley说硬件和内核是否都正确实现刷缓存并没有得到有效验证。确保驱动在这方面正确是最难测试的。但Jens Axboe提出存在诸如fio等工具可以用于记录写入设备的数据,然后断电再验证所有记录的写是否已真正地写入到设备。但Chris Mason指出将需要大量的系统来实施这种测试。

Kulkarni想知道任何时候块层有新的提交时都能测试整个块层是否可行。他问到:是否存在这种方法,且这种类型的测试是否有必要?Bottomley引导Dave Chinner说,如果存在该类型的测试,应该被加到xfstests。这样做的原因是,文件系统开发者定期运行xfstests,测试比较充分(pool of willing guinea pigs)。另外,参会者建议不同的测试/测试套可以作为统一测试的一部分运行。

但是Mason建议将事情细分。虽然文件系统开发者一直运行xfstests,但是他们并没有Kulkarni感兴趣的硬件(Kulkarni在WD工作)。文件系统开发者可以使用device mapper创建硬件模拟器来运行xfstests。因此问题将变成需要确保模拟器能真实反映硬件。

Ted Ts'o同意该观点,指出SMR设备是一个需要测试的领域,但对文件系统开发者来说并不容易。如果device mapper模拟器提供写指针和其他SMR功能,将使xfstests和其他测试得以运行。这对打开文件系统和SMR的交互局面将起到很大作用。“我们可以后续再考虑边缘案例”。
一个参会者提出:测试device mapper模拟器固然对文件系统来说很好,但诸如块层IO调度器同样需要被测试。Axboe说,这种类型的测试可以训练多队列调度器,但仍然需要块层的测试。类似于xfstests,块层开发者可以运行和测试块层和存储层部分栈。它可包含不同类型的设备如SMR,也可包含内核特性如热插拔。如果有人构建框架,测试将会随之而来。

实际上,他旨在帮助将框架组合在一起。Josef Bacik同样推荐将其加入xfstests,Axboe对此并不反对。Bacik说xfstests框架已经有许多需要的测试,但Bottomley提醒将需要将xfstests改造成不再依赖文件系统。

一位观众提出,测试到的代码路径也需要展示出来,但另外一位指出内核支持gcov,可以用来查看测试代码覆盖。

一位参会者关心目前没有热插拔的测试,但Bacik说部分Btrfs测试使用到了。他担心xfstests不支持设备级测试,因此需要新增。Ts‘o想确保开发者可以在无需访问特殊硬件的前提下运行各种测试,因此需要创建device mapper模拟器。

Bottomley说一旦xfstests修改成不再需要挂载文件系统来作为测试的一部分,一些子系统特定的测试可以加入进来,如SCSI,NVMe,RDMA等。Bacik同意该观点,xfstests目前工作良好,除非你想测试块层。该讨论额外得出,块层测试应当加入到不再依赖文件系统的xfstests,然后新增子系统特定的测试。

Mason补充到无需持久化的测试应当成为该努力的一部分,如设备可用性和模拟。Axboe说,当新bug出现,检测该问题的测试应当同样新增进来。他不想对假定测试用例是什么样。可能存在特定类型硬件的测试用例。任何人做该工作将选择测试框架运行的形式。

Kulkarni想知道一些存储子系统特定的测试是否可以在诸如SCSI,NVMe等之间共享。Bottomley说可能,但重要的是持续聚焦于内核关心的特性。多样的硬件设备可以确保和提供Linux不使用的特性,因此测试这些对内核社区并没有价值。覆盖地图将指导哪些地方需要更多的测试。议题时间耗尽,但看来已经在正确的道路上达成了较强的一致。

相关实践学习
基于阿里云DeepGPU实例,用AI画唯美国风少女
本实验基于阿里云DeepGPU实例,使用aiacctorch加速stable-diffusion-webui,用AI画唯美国风少女,可提升性能至高至原性能的2.6倍。
相关文章
|
5月前
|
网络协议 Linux Android开发
|
5月前
|
存储 安全 Linux
探索eBPF:Linux内核的黑科技(上)
探索eBPF:Linux内核的黑科技
|
3月前
|
JSON Linux C语言
内核雏形
内核雏形
26 0
|
Linux
《从 Linux 系统内核层面来解决实际问题的实战经验》电子版地址
从 Linux 系统内核层面来解决实际问题的实战经验
67 0
《从 Linux 系统内核层面来解决实际问题的实战经验》电子版地址
|
Linux 调度 开发工具
Plugsched 实战解读:如何在不中断业务时对 Linux 内核调度器热升级? | 龙蜥技术
plugsched 如何使用?每一步的操作如何进行以及背后的工作是什么?
Plugsched 实战解读:如何在不中断业务时对 Linux 内核调度器热升级? | 龙蜥技术
|
存储 弹性计算 运维
龙蜥开源 Plugsched:首次实现 Linux kernel 调度器热升级
Plugsched 是 Linux 内核调度器子系统热升级的 SDK,它可以实现在不重启系统、应用的情况下动态替换调度器子系统,毫秒级 downtime。Plugsched 可以对生产环境中的内核调度特性动态地进行增、删、改,以满足不同场景或应用的需求,且支持回滚。
294 0
龙蜥开源 Plugsched:首次实现 Linux kernel 调度器热升级
|
弹性计算 运维 安全
龙蜥开源Plugsched:首次实现 Linux kernel 调度器热升级 | 龙蜥技术
对于plugsched而言,无论是 bugfix,还是性能优化,甚至是特性的增、删、改,都可胜任。
龙蜥开源Plugsched:首次实现 Linux kernel 调度器热升级 | 龙蜥技术
|
监控 Linux 数据中心
|
Rust 算法 安全
直播回顾:如何基于Linux内核构建起商用密码基础设施?| 龙蜥技术
Anolis商密版OS是国内⾸家从操作系统层⾯提供商⽤密码的OS解决⽅案。
直播回顾:如何基于Linux内核构建起商用密码基础设施?| 龙蜥技术