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

时间:2015-08-10 17:58:57

标签: linux memory-management linux-kernel linux-device-driver

我正在开发一个涉及一些自定义硬件和我为硬件编写的自定义Linux设备驱动程序的系统。系统偶尔需要非常快速地移动大量数据,因此我的驱动程序动态地(即在需要时)分配大的(1 GB)DMA缓冲区,这些缓冲区被使用,然后在不再需要时释放。为了分配这么大的缓冲区,我实际上使用dma_alloc_coherent分配了一堆较小的缓冲区(256 X 4MB),然后使用remap_pfn_range将它们连续映射到用户空间。这在大多数情况下非常有效。

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

例如,在我捕获页面缓存的最后一次失败中,在我的系统上填充了27 GB的32 GB RAM。我怀疑页面缓存“丰满”导致dma_alloc_coherent调用失败。为了测试这个理论,我使用以下方法手动清空页面缓存:

# echo 1 > /proc/sys/vm/drop_caches

这将缓存的大小从27 GB减少到94 MB,并且我能够分配20 + 1 GB DMA缓冲区而没有任何问题。

显然,页面缓存是一件有益的事情,所以我不希望每次在分配DMA缓冲区时空间不足时都要完全清空它。我的问题是:如何动态缩小内核空间中的页面缓存,这样如果对dma_alloc_coherent的调用失败,我可以恢复足够的空间,以便我可以重试该调用并使其成功?

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

我发现了一些模糊的引用,表明我正在尝试的可能,例如“这些对象是自动的 在系统的其他地方需要内存时由内核回收。“(来自:https://www.kernel.org/doc/Documentation/sysctl/vm.txt)。但我还没有找到任何指明内存如何回收的细节。

对此的任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:7)

TL; DR :扫描活动的超级块并删除对非脏的超级块的引用,直到您根据需要回收了尽可能多的系统内存。 (或者你最终用尽了对活动超级块的引用。)

  

如何编写内核代码以动态缩小 fs页面缓存
  恢复足够的空间,以便后续调用dma_alloc_coherent()成功?

要回答这个问题,让我们看一下“drop_caches操作”在您的系统上将fs页面缓存从27GB减少到94MB的作用。

  1. echo 1 > /proc/sys/vm/drop_caches
    调用
    drop_caches_sysctl_handler()

  2. 反过来调用iterate_supers()
    将指针传递给函数drop_pagecache_sb()

  3. 接下来发生的事情是iterate_supers()扫描活动超级块,每次找到活动超级块时,它会调用drop_pagecache_sb(),并向其传递对活动超级块的引用。

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

      

    由于您有兴趣运行此流程以回收有限/已知数量的内存,即即将使用dma_alloc_coherent()请求的内容,您只需要在最后执行上述功能并进行额外检查每次迭代时,一旦自由系统内存量超过所需级别,就立即中止超级块扫描。

    要记住要进一步优化此过程的几点:

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

    • iterate_supers_type()似乎很有趣
      它允许人们迭代特定file_system_type

    • 的超级块

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

答案 1 :(得分:0)

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

这里有两个选项:

1)使用诸如dirty_ratio,dirty_background_ratio等VM设置进行游戏以保持自由ram。见:https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Performance_Tuning_Guide/s-memory-tunables.html

2)编写一个内核守护进程,它调用处理drop_cache的内核函数(因为drop_cache migh sleep)。