在FS/IO上下文使用的GFP掩码 【ChatGPT】

发布时间 2023-12-09 16:58:16作者: 摩斯电码

GFP masks used from FS/IO context

日期

2018年5月

作者

Michal Hocko mhocko@kernel.org

简介

文件系统和IO堆栈中的代码路径在分配内存时必须小心,以防止直接内存回收调用回FS或IO路径并在已持有的资源上阻塞(例如,用于事务上下文的最常见的锁)而导致递归死锁。

避免这种死锁问题的传统方法是在调用分配器时清除gfp掩码中的__GFP_FS或__GFP_IO(注意后者隐含清除前者)。可以使用GFP_NOFS或GFP_NOIO作为快捷方式。然而,事实证明上述方法导致了滥用,即“以防万一”地使用受限gfp掩码而没有深入考虑,这会导致问题,因为过度使用GFP_NOFS/GFP_NOIO可能导致内存过度回收或其他内存回收问题。

新API

自4.12以来,我们有了一个通用的范围API,用于NOFS和NOIO上下文memalloc_nofs_save、memalloc_nofs_restore和memalloc_noio_save、memalloc_noio_restore,它们允许将范围标记为从文件系统或I/O角度看是关键部分。从该范围分配的任何内存都会从给定的掩码中删除__GFP_FS或__GFP_IO,因此没有内存分配会递归回FS/IO。

unsigned int memalloc_nofs_save(void)

标记隐式的GFP_NOFS分配范围。

参数

void

无参数

描述

此函数标记了GFP_NOFS分配范围的开始。所有进一步的分配将隐式地删除__GFP_FS标志,因此从分配递归角度来看,它们对于FS关键部分是安全的。使用memalloc_nofs_restore来用由此函数返回的标志结束范围。

此函数可以安全地从任何上下文中使用。

void memalloc_nofs_restore(unsigned int flags)

结束隐式的GFP_NOFS范围。

参数

unsigned int flags

要恢复的标志。

描述

结束由memalloc_nofs_save函数开始的隐式的GFP_NOFS范围。始终确保给定的标志是与配对的memalloc_nofs_save调用返回的值。

unsigned int memalloc_noio_save(void)

标记隐式的GFP_NOIO分配范围。

参数

void

无参数

描述

此函数标记了GFP_NOIO分配范围的开始。所有进一步的分配将隐式地删除__GFP_IO标志,因此从分配递归角度来看,它们对于IO关键部分是安全的。使用memalloc_noio_restore来用由此函数返回的标志结束范围。

此函数可以安全地从任何上下文中使用。

void memalloc_noio_restore(unsigned int flags)

结束隐式的GFP_NOIO范围。

参数

unsigned int flags

要恢复的标志。

描述

结束由memalloc_noio_save函数开始的隐式的GFP_NOIO范围。始终确保给定的标志是与配对的memalloc_noio_save调用返回的值。

文件系统/IO代码在开始任何与回收相关的关键部分时简单地调用适当的保存函数,例如与回收上下文共享的锁,或者当通过回收可能存在事务上下文嵌套时。在关键部分结束时应调用恢复函数。最好还要解释一下回收上下文是什么,以便更容易进行维护。

请注意,保存/恢复函数的正确配对允许嵌套,因此可以安全地从现有的NOIO或NOFS范围中调用memalloc_noio_save或memalloc_noio_restore。

__vmalloc(GFP_NOFS)怎么办

vmalloc不支持GFP_NOFS语义,因为分配器内部有硬编码的GFP_KERNEL分配,这些分配相当复杂,不容易修复。这意味着几乎总是使用GFP_NOFS/GFP_NOIO调用vmalloc都是一个错误。好消息是,可以通过范围API实现NOFS/NOIO语义。

在理想的情况下,上层应该已经标记了危险的上下文,因此不需要特别注意,vmalloc应该可以无问题地调用。有时,如果上下文不是很清晰,或者存在层次违规,那么推荐的解决方法是通过范围API包装vmalloc,并附上解释问题的注释。