요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. _gfp_mask_from_fs_io:
=================================
GFP masks used from FS/IO context
=================================
:Date: May, 2018
:Author: Michal Hocko <mhocko@kernel.org>
Introduction
============
Code paths in the filesystem and IO stacks must be careful when
allocating memory to prevent recursion deadlocks caused by direct
memory reclaim calling back into the FS or IO paths and blocking on
already held resources (e.g. locks - most commonly those used for the
transaction context).
The traditional way to avoid this deadlock problem is to clear __GFP_FS
respectively __GFP_IO (note the latter implies clearing the first as well) in
the gfp mask when calling an allocator. GFP_NOFS respectively GFP_NOIO can be
used as shortcut. It turned out though that above approach has led to
abuses when the restricted gfp mask is used "just in case" without a
deeper consideration which leads to problems because an excessive use
of GFP_NOFS/GFP_NOIO can lead to memory over-reclaim or other memory
reclaim issues.
New API
========
Since 4.12 we do have a generic scope API for both NOFS and NOIO context
``memalloc_nofs_save``, ``memalloc_nofs_restore`` respectively ``memalloc_noio_save``,
``memalloc_noio_restore`` which allow to mark a scope to be a critical
section from a filesystem or I/O point of view. Any allocation from that
scope will inherently drop __GFP_FS respectively __GFP_IO from the given
mask so no memory allocation can recurse back in the FS/IO.
.. kernel-doc:: include/linux/sched/mm.h
:functions: memalloc_nofs_save memalloc_nofs_restore
.. kernel-doc:: include/linux/sched/mm.h
:functions: memalloc_noio_save memalloc_noio_restore
FS/IO code then simply calls the appropriate save function before
any critical section with respect to the reclaim is started - e.g.
lock shared with the reclaim context or when a transaction context
nesting would be possible via reclaim. The restore function should be
called when the critical section ends. All that ideally along with an
explanation what is the reclaim context for easier maintenance.
Please note that the proper pairing of save/restore functions
allows nesting so it is safe to call ``memalloc_noio_save`` or
``memalloc_noio_restore`` respectively from an existing NOIO or NOFS
scope.
What about __vmalloc(GFP_NOFS)
==============================
Since v5.17, and specifically after the commit 451769ebb7e79 ("mm/vmalloc:
alloc GFP_NO{FS,IO} for vmalloc"), GFP_NOFS/GFP_NOIO are now supported in
``[k]vmalloc`` by implicitly using scope API.
In earlier kernels ``vmalloc`` didn't support GFP_NOFS semantic because there
were hardcoded GFP_KERNEL allocations deep inside the allocator. That means
that calling ``vmalloc`` with GFP_NOFS/GFP_NOIO was almost always a bug.
In the ideal world, upper layers should already mark dangerous contexts
and so no special care is required and ``vmalloc`` should be called without any
problems. Sometimes if the context is not really clear or there are layering
violations then the recommended way around that (on pre-v5.17 kernels) is to
wrap ``vmalloc`` by the scope API with a comment explaining the problem.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
문서 정보
1-9`.. _gfp_mask_from_fs_io:`
GFP masks used from FS/IO context
날짜: May, 2018
저자: Michal Hocko <mhocko@kernel.org>
FS/IO reclaim 재귀와 기존 회피법
10-27Introduction
filesystem과 IO stack의 code path는 memory를 할당할 때 주의해야 합니다. direct memory reclaim이 FS 또는 IO path를 다시 호출하고, 이미 보유한 resource에 block되면 recursion deadlock이 발생할 수 있습니다. 가장 흔한 예는 transaction context에 쓰는 lock입니다.
전통적인 회피 방법은 allocator를 호출할 때 gfp mask에서 각각 `__GFP_FS` 또는 `__GFP_IO`를 지우는 것입니다. `__GFP_IO`를 지우면 `__GFP_FS`도 함께 지워야 합니다. 단축 표현으로 각각 `GFP_NOFS`와 `GFP_NOIO`를 사용할 수 있습니다.
하지만 restricted gfp mask가 깊은 검토 없이 만일을 대비한다는 이유로 남용되었습니다. `GFP_NOFS`와 `GFP_NOIO`를 과도하게 쓰면 memory over-reclaim이나 그 밖의 memory reclaim 문제가 생길 수 있습니다.
NOFS/NOIO scope API
28-54New API
4.12부터 NOFS와 NOIO context 모두에 generic scope API가 있습니다. 각각 `memalloc_nofs_save`, `memalloc_nofs_restore`, `memalloc_noio_save`, `memalloc_noio_restore`입니다.
이 API는 filesystem 또는 I/O 관점에서 특정 scope를 critical section으로 표시합니다. 그 scope 안의 모든 allocation은 주어진 mask에서 각각 `__GFP_FS` 또는 `__GFP_IO`를 자동으로 제거하므로 memory allocation이 FS/IO로 재귀할 수 없습니다.
.. kernel-doc:: include/linux/sched/mm.h
:functions: memalloc_nofs_save memalloc_nofs_restore
.. kernel-doc:: include/linux/sched/mm.h
:functions: memalloc_noio_save memalloc_noio_restore
FS/IO code는 reclaim과 관련된 critical section을 시작하기 전에 적절한 save function을 호출합니다. 예를 들면 reclaim context와 공유하는 lock을 얻거나 reclaim을 통해 transaction context가 중첩될 수 있는 구간입니다. critical section이 끝나면 restore function을 호출해야 합니다.
유지보수를 쉽게 하려면 어떤 reclaim context를 차단하는지 설명도 함께 남기는 것이 좋습니다.
save/restore function을 올바르게 쌍으로 사용하면 중첩을 지원합니다. 따라서 이미 NOIO 또는 NOFS scope 안에서도 각각 `memalloc_noio_save`와 `memalloc_noio_restore`를 안전하게 호출할 수 있습니다.
__vmalloc(GFP_NOFS) 지원
55-70What about __vmalloc(GFP_NOFS)
v5.17부터, 구체적으로 commit `451769ebb7e79`("mm/vmalloc: alloc GFP_NO{FS,IO} for vmalloc") 이후에는 `[k]vmalloc`이 scope API를 암묵적으로 사용하므로 `GFP_NOFS`와 `GFP_NOIO`를 지원합니다.
이전 커널의 `vmalloc` allocator 내부에는 `GFP_KERNEL` allocation이 hardcode되어 있어 `GFP_NOFS` semantic을 지원하지 않았습니다. 따라서 당시 `vmalloc`을 `GFP_NOFS` 또는 `GFP_NOIO`와 함께 호출하는 것은 거의 항상 bug였습니다.
이상적으로는 upper layer가 이미 위험한 context를 표시해야 하므로 특별한 조치 없이 `vmalloc`을 호출할 수 있어야 합니다. context가 명확하지 않거나 layering violation이 있다면 pre-v5.17 kernel에서는 문제를 설명하는 comment와 함께 `vmalloc`을 scope API로 감싸는 방법을 권장합니다.
요약과 해설
gfp_mask-from-fs-io.rst:1-70FS/IO code가 resource를 보유한 채 memory reclaim에 들어가면 reclaim이 같은 path로 재귀하여 deadlock할 수 있습니다. 개별 allocation마다 `GFP_NOFS`나 `GFP_NOIO`를 붙이는 방식은 범위를 놓치거나 지나치게 제한하기 쉽습니다.
`memalloc_nofs_save/restore`와 `memalloc_noio_save/restore`는 위험한 전체 scope를 표시합니다. 중첩 가능한 save/restore 쌍을 사용하면 해당 구간의 모든 allocation에서 필요한 GFP bit가 자동으로 제거됩니다.
v5.17 이후 `[k]vmalloc`은 scope API를 통해 NOFS/NOIO를 지원합니다. 이전 커널에서는 위험한 context를 upper layer에서 표시하고, 불가피할 때만 이유를 기록한 scope로 `vmalloc`을 감싸야 합니다.