英文

从 FS/IO 上下文中使用的 GFP 掩码

日期:

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 起,我们为 NOFS 和 NOIO 上下文提供了通用的作用域 API memalloc_nofs_savememalloc_nofs_restore 分别是 memalloc_noio_savememalloc_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 以使用此函数返回的标志结束作用域。

上下文

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

返回

要传递给 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 以使用此函数返回的标志结束作用域。

上下文

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

返回

要传递给 memalloc_noio_restore 的保存标志。

void memalloc_noio_restore(unsigned int flags)

结束隐式 GFP_NOIO 范围。

参数

unsigned int flags

要还原的标志。

描述

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

然后,FS/IO 代码只需在任何与回收相关的关键部分开始之前调用适当的保存函数 - 例如,与回收上下文共享的锁,或者当可以通过回收来实现事务上下文嵌套时。当关键部分结束时,应调用还原函数。所有这些最好都附有关于回收上下文的解释,以便于维护。

请注意,保存/还原函数的正确配对允许嵌套,因此从现有的 NOIO 或 NOFS 范围调用 memalloc_noio_savememalloc_noio_restore 是安全的。

__vmalloc(GFP_NOFS) 怎么样?

自 v5.17 起,特别是在commit 451769ebb7e79 (“mm/vmalloc: alloc GFP_NO{FS,IO} for vmalloc”) 之后,现在通过隐式使用作用域 API 在 [k]vmalloc 中支持 GFP_NOFS/GFP_NOIO。

在较早的内核中,vmalloc 不支持 GFP_NOFS 语义,因为分配器深处有硬编码的 GFP_KERNEL 分配。这意味着使用 GFP_NOFS/GFP_NOIO 调用 vmalloc 几乎总是一个错误。

在理想的世界中,上层应该已经标记了危险的上下文,因此不需要特别注意,并且应该可以毫无问题地调用 vmalloc。有时,如果上下文不是很清楚或者存在分层违规,那么建议的解决方法(在 pre-v5.17 内核上)是用作用域 API 包装 vmalloc,并附带评论解释问题。