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

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

在testing过程中,系统长时间运行testing用例后,有时会发现DMA分配失败,其中一个dma_alloc_coherent调用失败,导致我的应用层软件崩溃。 我终于find了这个问题,并发现当我看到DMA分配失败时,Linux内核页面caching非常满。

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

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

这将caching的大小从27 GB降低到了94 MB,我能够分配20 + 1 GB的DMA缓冲区而没有任何问题。

很明显,页面caching是一个有益的事情,所以我不希望在分配DMA缓冲区时每次空间不足都不得不完全清空它。 我的问题是这样的:我如何dynamic缩小内核空间的页面caching,如果调用dma_alloc_coherent失败,我可以恢复只是足够的空间,以便我可以重试通话,并取得成功?

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

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

任何援助,将不胜感激!

Solutions Collecting From Web of "如何从内核空间中缩小Linux页面caching?"

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()

接下来发生的事情是, 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)使用像dirty_ratio,dirty_background_ratio等虚拟机设置来保持免费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)。