如何从内核空间中收缩Linux页面缓存?

如何从内核空间中收缩Linux页面缓存?,linux,memory-management,linux-kernel,linux-device-driver,Linux,Memory Management,Linux Kernel,Linux Device Driver,我正在开发一个系统,其中包括一些定制硬件和我为该硬件编写的定制Linux设备驱动程序。系统有时需要非常快速地移动大量数据,因此我的驱动程序会动态(即在需要时)分配大量(1 GB)DMA缓冲区,这些缓冲区会被使用,然后在不再需要时释放。为了分配这样大的缓冲区,我实际上使用dma\u alloc\u coherent分配了一堆较小的缓冲区(256 X 4MB),然后使用remap\u pfn\u range将它们连续映射到用户空间。这在大多数情况下都很有效 在测试过程中,在系统运行测试用例很长时间后

我正在开发一个系统,其中包括一些定制硬件和我为该硬件编写的定制Linux设备驱动程序。系统有时需要非常快速地移动大量数据,因此我的驱动程序会动态(即在需要时)分配大量(1 GB)DMA缓冲区,这些缓冲区会被使用,然后在不再需要时释放。为了分配这样大的缓冲区,我实际上使用
dma\u alloc\u coherent
分配了一堆较小的缓冲区(256 X 4MB),然后使用
remap\u pfn\u range
将它们连续映射到用户空间。这在大多数情况下都很有效

在测试过程中,在系统运行测试用例很长时间后,我有时会看到DMA分配失败,其中驱动程序中的一个
DMA\u alloc\u相干
调用失败,导致我的应用层软件崩溃。我终于找到了这个问题,我发现当我看到DMA分配失败时,Linux内核页面缓存非常满

例如,在上一次捕获失败时,页面缓存填充了系统上32GB内存中的27GB。我怀疑页面缓存“满”导致
dma\u alloc\u相干
调用失败。为了测试这一理论,我使用以下方法手动清空页面缓存:

# echo 1 > /proc/sys/vm/drop_caches
这将缓存的大小从27 GB降至94 MB,并且我能够分配20+1 GB DMA缓冲区而没有任何问题

显然,页面缓存是一件有益的事情,因此我不希望在分配DMA缓冲区时每次空间用完时都将其完全清空。我的问题是:如何在内核空间中动态收缩页面缓存,以便在调用
dma\u alloc\u coherent
失败时,我可以恢复足够的空间,以便重试调用并使其成功

我的系统基于x86_64,运行3.16.x Linux内核

我发现一些模糊的参考资料表明我的尝试可能是可能的,例如“这些对象是自动生成的” 当系统上其他地方需要内存时,由内核回收。“(发件人:)。但我还没有找到任何具体的说明如何回收内存


任何与此相关的帮助都将不胜感激

从技术上讲,当某些分配失败时,内核将尝试释放内存。具体取决于内存故障(软故障/硬故障)。硬故障导致内核进入直接回收路径。直接回收是一项成本高昂的操作,可能需要未定义的时间才能完成,甚至在分配之后也可能失败

这里有两个选项:

1) 使用虚拟机设置,如脏内存比率、脏内存背景比率等,以保持可用内存。见:


2) 编写一个内核守护进程,它调用处理drop\u缓存的内核函数(因为drop\u缓存可能会休眠)

TL;DR:扫描活动超级块并删除对非脏超级块的引用,直到回收了所需的系统内存。(或者最终耗尽了对活动超级块的引用。)


如何编写内核代码来动态收缩fs页面缓存
要恢复足够的空间,以便后续调用
dma\u alloc\u coherent()
成功

为了回答这个问题,让我们看看“
drop\u caches
操作”是如何将系统上的fs页面缓存从27GB减少到94MB的

  • echo 1>/proc/sys/vm/drop\u缓存

    调用

  • 这反过来调用和
    将指针传递给函数

  • 接下来发生的事情是,
    iterate\u supers()
    扫描活动的超级块,每次找到一个超级块时,它都调用
    drop\u pagecache\u sb()
    ,将对活动超级块的引用传递给它

    此迭代过程将继续,直到从fs页缓存中释放对所有活动超级块的引用。这是一种非破坏性操作,只会释放完全未使用的块。脏对象将继续使用,直到写入磁盘,并且无法释放。如果您先运行
    sync
    将它们刷新到磁盘,则“
    drop\u caches
    操作”倾向于释放更多内存

    由于您有兴趣运行此过程以回收有限/已知的内存量,即使用
    dma\u alloc\u coherent()
    将要请求的内存量,您只需在每次迭代结束时通过附加检查实现上述功能,并在可用系统内存量超过所需级别时立即中止超级块扫描


    要进一步优化此程序,请记住两点:

    • 某些块设备是否优于其他设备?
      您可能希望迭代不关心的块设备的活动超级块。如果没有回收足够的内存,则扫描希望保留在fs页缓存中的块设备,除非绝对需要回收所需内存。可能会有帮助

    • 似乎很有趣
      它允许在特定的超块上进行迭代

    请注意,这是一个推测性的解决方案,完全基于对Linux内核中现有代码的分析,您已经观察到这些代码已经解决了您的问题。一旦实现了上述方法,它将只允许您控制相同的操作,即仅在满足您当前需要的范围内尝试回收fs页面缓存内存。

    我在触发内存回收的
    dma\u alloc\u相干的
    代码路径中找不到任何地方。正如我之前所说的,我更愿意使用尽可能多的页面缓存,如果不是必须的话,也不要清空它(调用drop_cache)。谢谢你的回复,你所描述的正是我所做的。它确实感觉像一个巨大的黑客