Dav*_*ave 4 linux memory-management linux-device-driver linux-kernel
我正在研究一个涉及一些自定义硬件和我为硬件编写的自定义Linux设备驱动程序的系统.系统偶尔需要非常快速地移动大量数据,因此我的驱动程序动态地(即在需要时)分配大的(1 GB)DMA缓冲区,这些缓冲区被使用,然后在不再需要时释放.为了分配这么大的缓冲区,我实际上分配了一堆较小的缓冲区(256 X 4MB)dma_alloc_coherent,然后使用它们将它们连续映射到用户空间remap_pfn_range.这在大多数情况下非常有效.
在测试期间,在系统长时间运行测试用例之后,我有时会看到DMA分配失败,其中dma_alloc_coherent我的驱动程序中的一个调用失败,导致我的应用层软件崩溃.我终于能够找到这个问题,我发现当我看到DMA分配失败时,Linux内核页面缓存非常充分.
例如,在我捕获页面缓存的最后一次失败中,在我的系统上填充了27 GB的32 GB RAM.我怀疑页面缓存"丰满"导致dma_alloc_coherent调用失败.为了测试这个理论,我使用以下方法手动清空页面缓存:
# echo 1 > /proc/sys/vm/drop_caches
Run Code Online (Sandbox Code Playgroud)
这将缓存的大小从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).但是我还没有找到任何表明内存如何回收的细节.
对此的任何帮助将不胜感激!
TL; DR:扫描活动超级块并删除对非脏块的引用,直到您根据需要回收了尽可能多的系统内存.(或者你最终用尽了对活动超级块的引用.)
如何编写内核代码来动态缩小fs页面缓存,
恢复足够的空间,以便后续调用dma_alloc_coherent()成功?
要回答这个问题,让我们看一下" drop_caches操作"在您的系统上将fs页面缓存从27GB减少到94MB所做的工作.
echo 1 > /proc/sys/vm/drop_caches
所调用
drop_caches_sysctl_handler()
然后调用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页面缓存内存.