從 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_restorememalloc_noio_savememalloc_noio_restore。這些 API 允許將某個作用域標記為檔案系統或 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 程式碼隨後只需在任何與回收相關的關鍵區開始之前呼叫相應的 save 函式——例如,與回收上下文共享的鎖,或者當事務上下文可能透過回收巢狀時。在關鍵區結束時應呼叫 restore 函式。所有這些最好伴隨著對回收上下文的解釋,以便於維護。

請注意,save/restore 函式的正確配對允許巢狀,因此從現有的 NOIO 或 NOFS 作用域中分別呼叫 memalloc_noio_savememalloc_noio_restore 是安全的。

__vmalloc(GFP_NOFS) 如何?

自 v5.17 版本,特別是在 commit 451769ebb7e79 (“mm/vmalloc: alloc GFP_NO{FS,IO} for vmalloc”) 之後,[k]vmalloc 現在透過隱式使用作用域 API 來支援 GFP_NOFS/GFP_NOIO。

在早期核心中,vmalloc 不支援 GFP_NOFS 語義,因為分配器內部深處有硬編碼的 GFP_KERNEL 分配。這意味著使用 GFP_NOFS/GFP_NOIO 呼叫 vmalloc 幾乎總是一個錯誤。

在理想情況下,上層應該已經標記了危險上下文,因此不需要特殊處理,vmalloc 應該可以毫無問題地被呼叫。有時,如果上下文不明確或存在分層違規,那麼(在 v5.17 之前的核心上)推薦的方法是用作用域 API 包裝 vmalloc,並附帶解釋問題的註釋。