By Jonathan Corbet
June 14, 2011
翻译:王鑫/李潇
关于分配大块儿连续物理内存的问题经常在 LWN 讨论。虚拟内存,由其性质决定了它总会将页面分散到整个系统;内核长时间运行后,可供分配的页面极少会出现一个挨一个的情况。多年来,内核开发者们处理这个问题的方式就是尽可能地避免对大范围连续内存分配的依赖。内核代码很少会试图分配超过两个的连续物理页面。
近来,关于大范围连续内存分配的需求越来越多。需求之一是超大页面数,尤其是transparent huge pages feature。另一个则是老树开新花了:硬件不能执行分散/收集DMA。任何只能对连续物理内存区域做DMA的设备(缺少I/O内存管理单元)都要求一个物理上连续的缓冲区来协同工作。这个要求通常是相对低端(stupid)的硬件的一个标志;你可以祈求这样的硬件随着时间越来越少。而我们所关注的是获得一定的能力的同时仍然保留着连续DMA要求的那些设备。比如,视频采集引擎,它能够采集完全高清的数据,然后在其上进行一系列的转换,但是它仍然会需要一个连续的缓冲区用来保存结果。高清视频的出现加剧了这个问题,那些物理连续的缓冲区现在比原来需要分配得更多,而且更难被分配了。
大概一年前,LWN 就开始关注解决这个问题的连续内存分配补丁。这个补丁集遵循着在启动时保留一大块内存的神圣而又庄严的传统,而这个的唯一目的就是为了满足大范围的分配需求。多年以来,这个技术一直被“bigphysarea”补丁所使用,或者简单地在启动内核时使用一个mem=的参数,用来留下一段未使用的物理内存。Android pmem driver也是从保留区域中分配大块内存的。这个方法当然有效,将近20年的经验足以证明它。保留内存的不利之处则在于它对其他任何想使用它的东西都是无效的;如果设备没用使用这块内存,它就简单地被闲置起来。这种内存的浪费在内核开发者和用户之间变得越来越不得心。
基于上述及更多的原因,CMA补丁从未被加入进去。尽管问题并没有被解决而且也没有开发者在其上工作,最新版的CMA补丁集看起来十分不同,虽然仍然有些问题需要解决,但是看起来本补丁集有较大的机会加入到主线中。
这个CMA分配器仍然能够同保留内存区域的方式一起工作,但那很明显不是想要的操作模式。相反,新的CMA试图维护当需要时能够创建连续大块内存的内存区域。为了那个目的,CMA依靠“migration type”机制被深度构建进内存管理代码中。
在每一个区段内,成块的页面被标记为被那些可移动的或可回收的页面使用。可移动的页面主要是那些缓存页或者匿名内存页,它们通过页表和page cache radix tree来访问。这些页的内容可以随着页表和树的更新而相应地被移动到其它地方。可回收页,则是在需要时返回给内核;它们像inode cache一样保存数据结构。不可移动页通常是那些内核拥有直接指针的页面,比如,通过kmalloc()申请的内存不能在不破坏任何东西的情况下被移动。
内存管理子系统试图将可活动页面保持在一起。如果要通过移动页面来获得掉一整块空闲内存,一个单独的固定页面就可破坏整个过程。通过将可活动页面分组,内核希望能不用遇到这个障碍就释放更多的页面,内存压缩代码目前就是依靠这些可活动页面才能工作。
CMA通过增加一个新的“CMA”迁移类型来延伸这个机制;它很像”可活动“类型,但有几点不一样。“CMA”类型比较棘手,内核不能改变被标志为CMA的页面的迁移类型。内存分配器不会从CMA区域分配固定页面,对于其他任何用途,只有当没有其他选择的时候才会分配CMA页面。如果一切顺利的话,被标志为CMA用途的内存区域只会包含可活动页面,并且有数量可观的空闲页面。
换句话说,被标志为CMA用途的内存对系统其余地方保持可用,只有一条限制,那就是它只包含可活动页面。当驱动需要一片连续的内存时,CMA分配器就在它的一个特定范围挤出足够的页面创建一个适当大小的连续缓冲区。如果在这里面的页面真正是可活动,分配器就提供驱动所需要的缓冲区。当缓冲区不再需要的时候,内存也可以被用作其他用途。
有人可能好奇为什么需要这样的机制,因为内存压缩机制就已经能够给transparent huge page创建大片物理上连续的块。那样的代码是可用的:我自己的 Linux 系统,在写这篇文章前,有大约25%被分配为大页面。这个问题的答案就是DMA缓冲区相对大页面有一些不同的要求。DMA缓冲区可能更大,例如,transparent huge page几乎在所有架构里都是2MB大小,而DMA缓冲区能有10M或者更多。如果底层的硬件足够“奇怪”的话,可能有必要将DMA缓冲区放在特定内存范围内,而CMA开发者 Marek Szyprowski看起来似乎就有这种“奇怪”硬件的需求。最终,2MB的大页面必须也有2MB的对齐,而对DMA缓冲区的对齐要求就宽松的多。CMA分配器能取得被请求数量的内存而不用过度担心严格的对齐要求。
CMA补丁集为建立内存区域和创建特定范围的“上下文”提供了一个函数集。有简单的cm_alloc()和cm_free()函数来获取和释放缓冲区。虽然如此,但预期设备驱动将不会直接调用CMA,相反,对CMA的意识将被固定在DMA支持函数里。当驱动调用函数如dma_alloc_coherent()时,CMA将自动被使用来满足要求。在大多数情况下,它应该能“正常工作”。
另外一个CMA的关注点是关于特定区域是如何设置的。目前的补丁期望在系统的board file里面做一些特殊的调用。这是一个很ARM式的方法。接下来的目的是去掉board file,这样就会发现其他东西。正如 Arnd Bergmann所指出的那样,将该信息移到设备树也不是一个选择。这其实是一个策略决策。Arnd正努力争取一些能在大部分系统工作的合理默认设置,其它一些少数系统将在后续被加进来。
最终的结果可能是,patch set至少得经过一个迭代才能进入主线。但CMA满足了以前从未遇到过的一个需求。这部分代码有潜在的可能性能够在减小对其余系统的影响的同时,让分配物理地址连续的内存变得更加可靠。看起来它似乎值得拥有。
最新评论