← Documents Documentation/core-api/pin_user_pages.rst GitHub 원문 ↗

Linux 6.18.37 · Core API

pin_user_pages()와 관련 호출

FOLL_PIN, FOLL_GET, FOLL_LONGTERM의 관계와 DMA 고정 페이지 계수, 다섯 가지 사용 사례, 단위 테스트 및 진단 방법을 설명합니다.

Source pathDocumentation/core-api/pin_user_pages.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약과 해설

pin_user_pages.rst:1-286

`pin_user_pages*()`는 DMA 또는 데이터 접근을 위해 페이지를 고정할 때 사용하는 래퍼이며 내부에서 `FOLL_PIN`을 설정합니다. 일반 `get_user_pages*()` 참조와 구분하여 파일시스템 writeback 및 MM 코드가 고정 상태를 판단할 수 있게 합니다.

일반 페이지는 `GUP_PIN_COUNTING_BIAS`를 refcount에 더하는 방식으로 고정 횟수를 근사하고, 대형 folio는 별도 `pincount`를 사용합니다. False positive는 허용하지만 false negative는 허용하지 않는 설계이며 zero page는 실제 계수를 바꾸지 않습니다.

짧은 DIO 고정에는 `FOLL_PIN`, 장기 RDMA 고정에는 `FOLL_PIN | FOLL_LONGTERM`을 사용합니다. MMU notifier와 올바르게 동기화하거나 `struct page`만 조작하는 경우에는 별도 플래그가 필요 없지만, 실제 페이지 데이터에 쓰는 경우에는 DMA가 없어도 `FOLL_PIN`이 필요할 수 있습니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 ====================================================
4 pin_user_pages() and related calls
5 ====================================================
6
7 .. contents:: :local:
8
9 Overview
10 ========
11
12 This document describes the following functions::
13
14 pin_user_pages()
15 pin_user_pages_fast()
16 pin_user_pages_remote()
17
18 Basic description of FOLL_PIN
19 =============================
20
21 FOLL_PIN and FOLL_LONGTERM are flags that can be passed to the get_user_pages*()
22 ("gup") family of functions. FOLL_PIN has significant interactions and
23 interdependencies with FOLL_LONGTERM, so both are covered here.
24
25 FOLL_PIN is internal to gup, meaning that it should not appear at the gup call
26 sites. This allows the associated wrapper functions (pin_user_pages*() and
27 others) to set the correct combination of these flags, and to check for problems
28 as well.
29
30 FOLL_LONGTERM, on the other hand, *is* allowed to be set at the gup call sites.
31 This is in order to avoid creating a large number of wrapper functions to cover
32 all combinations of get*(), pin*(), FOLL_LONGTERM, and more. Also, the
33 pin_user_pages*() APIs are clearly distinct from the get_user_pages*() APIs, so
34 that's a natural dividing line, and a good point to make separate wrapper calls.
35 In other words, use pin_user_pages*() for DMA-pinned pages, and
36 get_user_pages*() for other cases. There are five cases described later on in
37 this document, to further clarify that concept.
38
39 FOLL_PIN and FOLL_GET are mutually exclusive for a given gup call. However,
40 multiple threads and call sites are free to pin the same struct pages, via both
41 FOLL_PIN and FOLL_GET. It's just the call site that needs to choose one or the
42 other, not the struct page(s).
43
44 The FOLL_PIN implementation is nearly the same as FOLL_GET, except that FOLL_PIN
45 uses a different reference counting technique.
46
47 FOLL_PIN is a prerequisite to FOLL_LONGTERM. Another way of saying that is,
48 FOLL_LONGTERM is a specific case, more restrictive case of FOLL_PIN.
49
50 Which flags are set by each wrapper
51 ===================================
52
53 For these pin_user_pages*() functions, FOLL_PIN is OR'd in with whatever gup
54 flags the caller provides. The caller is required to pass in a non-null struct
55 pages* array, and the function then pins pages by incrementing each by a special
56 value: GUP_PIN_COUNTING_BIAS.
57
58 For large folios, the GUP_PIN_COUNTING_BIAS scheme is not used. Instead,
59 the extra space available in the struct folio is used to store the
60 pincount directly.
61
62 This approach for large folios avoids the counting upper limit problems
63 that are discussed below. Those limitations would have been aggravated
64 severely by huge pages, because each tail page adds a refcount to the
65 head page. And in fact, testing revealed that, without a separate pincount
66 field, refcount overflows were seen in some huge page stress tests.
67
68 This also means that huge pages and large folios do not suffer
69 from the false positives problem that is mentioned below.::
70
71 Function
72 --------
73 pin_user_pages FOLL_PIN is always set internally by this function.
74 pin_user_pages_fast FOLL_PIN is always set internally by this function.
75 pin_user_pages_remote FOLL_PIN is always set internally by this function.
76
77 For these get_user_pages*() functions, FOLL_GET might not even be specified.
78 Behavior is a little more complex than above. If FOLL_GET was *not* specified,
79 but the caller passed in a non-null struct pages* array, then the function
80 sets FOLL_GET for you, and proceeds to pin pages by incrementing the refcount
81 of each page by +1.::
82
83 Function
84 --------
85 get_user_pages FOLL_GET is sometimes set internally by this function.
86 get_user_pages_fast FOLL_GET is sometimes set internally by this function.
87 get_user_pages_remote FOLL_GET is sometimes set internally by this function.
88
89 Tracking dma-pinned pages
90 =========================
91
92 Some of the key design constraints, and solutions, for tracking dma-pinned
93 pages:
94
95 * An actual reference count, per struct page, is required. This is because
96 multiple processes may pin and unpin a page.
97
98 * False positives (reporting that a page is dma-pinned, when in fact it is not)
99 are acceptable, but false negatives are not.
100
101 * struct page may not be increased in size for this, and all fields are already
102 used.
103
104 * Given the above, we can overload the page->_refcount field by using, sort of,
105 the upper bits in that field for a dma-pinned count. "Sort of", means that,
106 rather than dividing page->_refcount into bit fields, we simple add a medium-
107 large value (GUP_PIN_COUNTING_BIAS, initially chosen to be 1024: 10 bits) to
108 page->_refcount. This provides fuzzy behavior: if a page has get_page() called
109 on it 1024 times, then it will appear to have a single dma-pinned count.
110 And again, that's acceptable.
111
112 This also leads to limitations: there are only 31-10==21 bits available for a
113 counter that increments 10 bits at a time.
114
115 * Because of that limitation, special handling is applied to the zero pages
116 when using FOLL_PIN. We only pretend to pin a zero page - we don't alter its
117 refcount or pincount at all (it is permanent, so there's no need). The
118 unpinning functions also don't do anything to a zero page. This is
119 transparent to the caller.
120
121 * Callers must specifically request "dma-pinned tracking of pages". In other
122 words, just calling get_user_pages() will not suffice; a new set of functions,
123 pin_user_page() and related, must be used.
124
125 FOLL_PIN, FOLL_GET, FOLL_LONGTERM: when to use which flags
126 ==========================================================
127
128 Thanks to Jan Kara, Vlastimil Babka and several other -mm people, for describing
129 these categories:
130
131 CASE 1: Direct IO (DIO)
132 -----------------------
133 There are GUP references to pages that are serving
134 as DIO buffers. These buffers are needed for a relatively short time (so they
135 are not "long term"). No special synchronization with folio_mkclean() or
136 munmap() is provided. Therefore, flags to set at the call site are: ::
137
138 FOLL_PIN
139
140 ...but rather than setting FOLL_PIN directly, call sites should use one of
141 the pin_user_pages*() routines that set FOLL_PIN.
142
143 CASE 2: RDMA
144 ------------
145 There are GUP references to pages that are serving as DMA
146 buffers. These buffers are needed for a long time ("long term"). No special
147 synchronization with folio_mkclean() or munmap() is provided. Therefore, flags
148 to set at the call site are: ::
149
150 FOLL_PIN | FOLL_LONGTERM
151
152 NOTE: Some pages, such as DAX pages, cannot be pinned with longterm pins. That's
153 because DAX pages do not have a separate page cache, and so "pinning" implies
154 locking down file system blocks, which is not (yet) supported in that way.
155
156 .. _mmu-notifier-registration-case:
157
158 CASE 3: MMU notifier registration, with or without page faulting hardware
159 -------------------------------------------------------------------------
160 Device drivers can pin pages via get_user_pages*(), and register for mmu
161 notifier callbacks for the memory range. Then, upon receiving a notifier
162 "invalidate range" callback , stop the device from using the range, and unpin
163 the pages. There may be other possible schemes, such as for example explicitly
164 synchronizing against pending IO, that accomplish approximately the same thing.
165
166 Or, if the hardware supports replayable page faults, then the device driver can
167 avoid pinning entirely (this is ideal), as follows: register for mmu notifier
168 callbacks as above, but instead of stopping the device and unpinning in the
169 callback, simply remove the range from the device's page tables.
170
171 Either way, as long as the driver unpins the pages upon mmu notifier callback,
172 then there is proper synchronization with both filesystem and mm
173 (folio_mkclean(), munmap(), etc). Therefore, neither flag needs to be set.
174
175 CASE 4: Pinning for struct page manipulation only
176 -------------------------------------------------
177 If only struct page data (as opposed to the actual memory contents that a page
178 is tracking) is affected, then normal GUP calls are sufficient, and neither flag
179 needs to be set.
180
181 CASE 5: Pinning in order to write to the data within the page
182 -------------------------------------------------------------
183 Even though neither DMA nor Direct IO is involved, just a simple case of "pin,
184 write to a page's data, unpin" can cause a problem. Case 5 may be considered a
185 superset of Case 1, plus Case 2, plus anything that invokes that pattern. In
186 other words, if the code is neither Case 1 nor Case 2, it may still require
187 FOLL_PIN, for patterns like this:
188
189 Correct (uses FOLL_PIN calls):
190 pin_user_pages()
191 write to the data within the pages
192 unpin_user_pages()
193
194 INCORRECT (uses FOLL_GET calls):
195 get_user_pages()
196 write to the data within the pages
197 put_page()
198
199 folio_maybe_dma_pinned(): the whole point of pinning
200 ====================================================
201
202 The whole point of marking folios as "DMA-pinned" or "gup-pinned" is to be able
203 to query, "is this folio DMA-pinned?" That allows code such as folio_mkclean()
204 (and file system writeback code in general) to make informed decisions about
205 what to do when a folio cannot be unmapped due to such pins.
206
207 What to do in those cases is the subject of a years-long series of discussions
208 and debates (see the References at the end of this document). It's a TODO item
209 here: fill in the details once that's worked out. Meanwhile, it's safe to say
210 that having this available: ::
211
212 static inline bool folio_maybe_dma_pinned(struct folio *folio)
213
214 ...is a prerequisite to solving the long-running gup+DMA problem.
215
216 Another way of thinking about FOLL_GET, FOLL_PIN, and FOLL_LONGTERM
217 ===================================================================
218
219 Another way of thinking about these flags is as a progression of restrictions:
220 FOLL_GET is for struct page manipulation, without affecting the data that the
221 struct page refers to. FOLL_PIN is a *replacement* for FOLL_GET, and is for
222 short term pins on pages whose data *will* get accessed. As such, FOLL_PIN is
223 a "more severe" form of pinning. And finally, FOLL_LONGTERM is an even more
224 restrictive case that has FOLL_PIN as a prerequisite: this is for pages that
225 will be pinned longterm, and whose data will be accessed.
226
227 Unit testing
228 ============
229 This file::
230
231 tools/testing/selftests/mm/gup_test.c
232
233 has the following new calls to exercise the new pin*() wrapper functions:
234
235 * PIN_FAST_BENCHMARK (./gup_test -a)
236 * PIN_BASIC_TEST (./gup_test -b)
237
238 You can monitor how many total dma-pinned pages have been acquired and released
239 since the system was booted, via two new /proc/vmstat entries: ::
240
241 /proc/vmstat/nr_foll_pin_acquired
242 /proc/vmstat/nr_foll_pin_released
243
244 Under normal conditions, these two values will be equal unless there are any
245 long-term [R]DMA pins in place, or during pin/unpin transitions.
246
247 * nr_foll_pin_acquired: This is the number of logical pins that have been
248 acquired since the system was powered on. For huge pages, the head page is
249 pinned once for each page (head page and each tail page) within the huge page.
250 This follows the same sort of behavior that get_user_pages() uses for huge
251 pages: the head page is refcounted once for each tail or head page in the huge
252 page, when get_user_pages() is applied to a huge page.
253
254 * nr_foll_pin_released: The number of logical pins that have been released since
255 the system was powered on. Note that pages are released (unpinned) on a
256 PAGE_SIZE granularity, even if the original pin was applied to a huge page.
257 Becaused of the pin count behavior described above in "nr_foll_pin_acquired",
258 the accounting balances out, so that after doing this::
259
260 pin_user_pages(huge_page);
261 for (each page in huge_page)
262 unpin_user_page(page);
263
264 ...the following is expected::
265
266 nr_foll_pin_released == nr_foll_pin_acquired
267
268 (...unless it was already out of balance due to a long-term RDMA pin being in
269 place.)
270
271 Other diagnostics
272 =================
273
274 dump_page() has been enhanced slightly to handle these new counting
275 fields, and to better report on large folios in general. Specifically,
276 for large folios, the exact pincount is reported.
277
278 References
279 ==========
280
281 * `Some slow progress on get_user_pages() (Apr 2, 2019) <https://lwn.net/Articles/784574/>`_
282 * `DMA and get_user_pages() (LPC: Dec 12, 2018) <https://lwn.net/Articles/774411/>`_
283 * `The trouble with get_user_pages() (Apr 30, 2018) <https://lwn.net/Articles/753027/>`_
284 * `LWN kernel index: get_user_pages() <https://lwn.net/Kernel/Index/#Memory_management-get_user_pages>`_
285
286 John Hubbard, October, 2019
287

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

pin_user_pages() 계열 개요

1-17

SPDX 라이선스 식별자는 GPL-2.0입니다.

`pin_user_pages()`와 관련 호출

이 문서는 다음 함수들을 설명합니다.

pin_user_pages()
pin_user_pages_fast()
pin_user_pages_remote()

FOLL_PIN의 기본 설명

18-49

`FOLL_PIN`의 기본 설명 (Basic description of FOLL_PIN)

`FOLL_PIN`과 `FOLL_LONGTERM`은 `get_user_pages*()` 함수군, 즉 gup 함수에 전달할 수 있는 플래그입니다. 두 플래그 사이에는 중요한 상호작용과 의존성이 있으므로 이 문서에서는 함께 다룹니다.

`FOLL_PIN`은 gup 내부용 플래그이므로 gup 호출 지점에 직접 나타나면 안 됩니다. 관련 래퍼 함수인 `pin_user_pages*()` 등이 올바른 플래그 조합을 설정하고 문제도 검사하도록 하기 위한 설계입니다.

반면 `FOLL_LONGTERM`은 gup 호출 지점에서 설정할 수 있습니다. 이는 `get*()`, `pin*()`, `FOLL_LONGTERM` 등의 모든 조합을 처리하는 래퍼를 대량으로 만들지 않기 위해서입니다. 또한 `pin_user_pages*()` API는 `get_user_pages*()` API와 분명히 구별되므로 별도의 래퍼 호출을 두기 좋은 경계입니다.

다시 말해 DMA로 고정할 페이지에는 `pin_user_pages*()`를 사용하고, 다른 경우에는 `get_user_pages*()`를 사용합니다. 뒤에서 이 구분을 더 명확히 하는 다섯 가지 사례를 설명합니다.

한 번의 gup 호출에서는 `FOLL_PIN`과 `FOLL_GET`을 함께 사용할 수 없습니다. 그러나 여러 스레드와 호출 지점이 같은 `struct page`를 각각 `FOLL_PIN`과 `FOLL_GET`으로 참조하는 것은 허용됩니다. 둘 중 하나를 선택해야 하는 주체는 각 호출 지점이지 `struct page` 자체가 아닙니다.

`FOLL_PIN` 구현은 `FOLL_GET`과 거의 같지만 서로 다른 참조 횟수 계산 기법을 사용합니다.

`FOLL_PIN`은 `FOLL_LONGTERM`의 전제 조건입니다. 달리 말하면 `FOLL_LONGTERM`은 `FOLL_PIN`보다 더 제한적인 특수 사례입니다.

래퍼별 플래그 설정

50-88

각 래퍼가 설정하는 플래그

`pin_user_pages*()` 함수는 호출자가 제공한 gup 플래그에 `FOLL_PIN`을 OR 연산으로 추가합니다. 호출자는 NULL이 아닌 `struct page *` 배열을 전달해야 하며, 함수는 각 페이지의 참조값을 특수한 값 `GUP_PIN_COUNTING_BIAS`만큼 증가시켜 페이지를 고정합니다.

대형 folio에는 `GUP_PIN_COUNTING_BIAS` 방식을 사용하지 않습니다. 대신 `struct folio`에 남는 공간에 `pincount`를 직접 저장합니다.

대형 folio에 별도 `pincount`를 두면 뒤에서 설명할 계수 상한 문제를 피할 수 있습니다. Huge page에서는 각 tail page가 head page의 refcount를 증가시키므로 이 한계가 훨씬 심해집니다. 실제 시험에서도 별도 `pincount` 필드가 없을 때 일부 huge page 스트레스 테스트에서 refcount 오버플로가 관찰되었습니다.

이 방식 덕분에 huge page와 대형 folio는 뒤에서 언급하는 false positive 문제도 겪지 않습니다.

함수내부 플래그 동작
pin_user_pages이 함수는 내부에서 항상 FOLL_PIN을 설정합니다.
pin_user_pages_fast이 함수는 내부에서 항상 FOLL_PIN을 설정합니다.
pin_user_pages_remote이 함수는 내부에서 항상 FOLL_PIN을 설정합니다.

`get_user_pages*()` 함수에서는 `FOLL_GET`이 아예 지정되지 않을 수도 있어 동작이 조금 더 복잡합니다. `FOLL_GET`이 지정되지 않았지만 호출자가 NULL이 아닌 `struct page *` 배열을 전달하면 함수가 대신 `FOLL_GET`을 설정하고 각 페이지의 refcount를 1씩 증가시켜 페이지를 고정합니다.

함수내부 플래그 동작
get_user_pages이 함수는 경우에 따라 내부에서 FOLL_GET을 설정합니다.
get_user_pages_fast이 함수는 경우에 따라 내부에서 FOLL_GET을 설정합니다.
get_user_pages_remote이 함수는 경우에 따라 내부에서 FOLL_GET을 설정합니다.

DMA 고정 페이지 추적

89-124

DMA로 고정된 페이지 추적

DMA로 고정된 페이지를 추적할 때의 핵심 설계 제약과 해결책은 다음과 같습니다.

  • `struct page`마다 실제 참조 횟수가 필요합니다. 여러 프로세스가 같은 페이지를 고정하고 해제할 수 있기 때문입니다.
  • 실제로는 DMA로 고정되지 않은 페이지를 고정되었다고 보고하는 false positive는 허용되지만, 고정된 페이지를 아니라고 보고하는 false negative는 허용되지 않습니다.
  • 이를 위해 `struct page`의 크기를 늘릴 수 없고 기존 필드도 모두 사용 중입니다.
  • 따라서 `page->_refcount`의 상위 비트를 DMA 고정 횟수처럼 활용합니다. 실제 bitfield로 나누는 대신 중간 정도로 큰 값인 `GUP_PIN_COUNTING_BIAS`, 초기값 1024 즉 10비트를 `page->_refcount`에 더합니다. `get_page()`가 1024번 호출된 페이지가 DMA 고정 1회로 보일 수 있지만 이는 허용되는 false positive입니다.
  • 31비트 가운데 10비트 단위로 증가하는 카운터에 사용할 수 있는 비트가 `31-10==21`개뿐이라는 한계가 있습니다.
  • 이 한계 때문에 `FOLL_PIN`을 사용할 때 zero page는 특별히 처리합니다. 영구 페이지이므로 실제 refcount나 pincount를 바꾸지 않고 고정한 것처럼 취급하며, 해제 함수도 zero page에는 아무 작업을 하지 않습니다. 이 동작은 호출자에게 투명합니다.
  • 호출자는 '페이지의 DMA 고정 추적'을 명시적으로 요청해야 합니다. 단순히 `get_user_pages()`를 호출하는 것으로는 부족하며 `pin_user_page()`와 관련 함수군을 사용해야 합니다.

플래그 선택 사례

125-130

`FOLL_PIN`, `FOLL_GET`, `FOLL_LONGTERM`: 어떤 플래그를 언제 사용할 것인가

다음 분류는 Jan Kara, Vlastimil Babka와 여러 -mm 개발자가 설명한 사례를 정리한 것입니다.

사례 1: Direct IO

131-142

사례 1: Direct IO (DIO)

DIO 버퍼로 사용되는 페이지에 GUP 참조가 있는 경우입니다. 버퍼가 필요한 기간은 비교적 짧아서 long-term이 아니며 `folio_mkclean()` 또는 `munmap()`과의 특별한 동기화도 제공되지 않습니다. 호출 지점에 필요한 플래그는 다음과 같습니다.

FOLL_PIN

다만 호출 지점에서 `FOLL_PIN`을 직접 설정하지 말고, 내부에서 `FOLL_PIN`을 설정하는 `pin_user_pages*()` 함수 중 하나를 사용해야 합니다.

사례 2: RDMA

143-155

사례 2: RDMA

DMA 버퍼로 사용되는 페이지에 GUP 참조가 있는 경우입니다. 이 버퍼는 오랫동안 필요하며 `folio_mkclean()` 또는 `munmap()`과의 특별한 동기화는 제공되지 않습니다. 호출 지점에 필요한 플래그는 다음과 같습니다.

FOLL_PIN | FOLL_LONGTERM

DAX 페이지 같은 일부 페이지는 long-term pin으로 고정할 수 없습니다. DAX 페이지에는 별도 page cache가 없어서 페이지 고정이 파일시스템 블록을 잠그는 효과를 내는데, 이러한 방식은 아직 지원되지 않기 때문입니다.

사례 3: MMU notifier 등록

156-174

사례 3: Page fault 지원 하드웨어 유무와 관계없는 MMU notifier 등록

장치 드라이버는 `get_user_pages*()`로 페이지를 고정하고 해당 메모리 범위에 대한 MMU notifier 콜백을 등록할 수 있습니다. `invalidate range` 콜백을 받으면 장치가 그 범위를 사용하지 못하게 중지한 뒤 페이지 고정을 해제합니다. 보류 중인 I/O와 명시적으로 동기화하는 방식처럼 거의 같은 효과를 내는 다른 구성도 가능합니다.

하드웨어가 재실행 가능한 page fault를 지원한다면 드라이버가 페이지 고정을 완전히 피하는 것이 이상적입니다. 앞과 같이 MMU notifier 콜백을 등록하되, 콜백에서 장치를 중지하고 고정을 해제하는 대신 장치 page table에서 해당 범위만 제거합니다.

어느 방식이든 MMU notifier 콜백을 받을 때 드라이버가 페이지 고정을 해제하면 파일시스템 및 MM의 `folio_mkclean()`, `munmap()` 등과 올바르게 동기화됩니다. 따라서 어떤 플래그도 설정할 필요가 없습니다.

사례 4: struct page 조작

175-180

사례 4: `struct page` 조작만을 위한 고정

페이지가 추적하는 실제 메모리 내용이 아니라 `struct page` 데이터만 영향을 받는다면 일반 GUP 호출이면 충분하고 어떤 플래그도 설정할 필요가 없습니다.

사례 5: 페이지 데이터 쓰기

181-198

사례 5: 페이지 내부 데이터에 쓰기 위한 고정

DMA나 Direct IO가 없어도 단순히 페이지를 고정하고 데이터를 쓴 뒤 고정을 해제하는 패턴은 문제를 일으킬 수 있습니다. 사례 5는 사례 1과 사례 2, 그리고 이 패턴을 호출하는 모든 경우를 포괄하는 상위 사례로 볼 수 있습니다. 즉 코드가 사례 1이나 사례 2가 아니더라도 다음과 같은 패턴에는 `FOLL_PIN`이 필요할 수 있습니다.

Correct (uses FOLL_PIN calls):
    pin_user_pages()
    write to the data within the pages
    unpin_user_pages()

INCORRECT (uses FOLL_GET calls):
    get_user_pages()
    write to the data within the pages
    put_page()

folio_maybe_dma_pinned()의 목적

199-215

`folio_maybe_dma_pinned()`: 페이지 고정의 핵심 목적

Folio를 'DMA-pinned' 또는 'gup-pinned'로 표시하는 목적은 해당 folio가 DMA로 고정되었는지 질의할 수 있게 하는 것입니다. 그러면 `folio_mkclean()`과 일반적인 파일시스템 writeback 코드가 이런 고정 때문에 folio를 unmap할 수 없을 때 어떻게 처리할지 판단할 수 있습니다.

이 상황에서 무엇을 해야 하는지는 수년간 이어진 논의와 논쟁의 주제이며 문서 끝의 참고 자료에 연결되어 있습니다. 구체적인 처리 방식은 결정된 뒤 보충해야 할 TODO 항목입니다. 그동안 다음 함수가 제공되는 것은 오래된 gup+DMA 문제를 해결하기 위한 전제 조건입니다.

static inline bool folio_maybe_dma_pinned(struct folio *folio)

플래그의 제한 단계

216-226

`FOLL_GET`, `FOLL_PIN`, `FOLL_LONGTERM`을 이해하는 또 다른 방법

이 플래그들은 제한이 점점 강해지는 단계로 볼 수 있습니다. `FOLL_GET`은 `struct page`가 가리키는 데이터에는 영향을 주지 않고 구조체 자체만 조작할 때 사용합니다.

`FOLL_PIN`은 `FOLL_GET`을 대체하며 실제 데이터에 접근할 페이지를 짧은 기간 고정하는 데 사용하므로 더 강한 형태의 고정입니다. `FOLL_LONGTERM`은 `FOLL_PIN`을 전제로 하며, 데이터에 접근하면서 페이지를 장기간 고정하는 더 제한적인 사례입니다.

단위 테스트와 vmstat

227-246

단위 테스트 (Unit testing)

새 `pin*()` 래퍼 함수를 시험하는 코드는 다음 파일에 있습니다.

tools/testing/selftests/mm/gup_test.c

이 파일에는 다음 시험 호출이 추가되었습니다.

  • `PIN_FAST_BENCHMARK` (`./gup_test -a`)
  • `PIN_BASIC_TEST` (`./gup_test -b`)

시스템 부팅 이후 획득하고 해제한 DMA 고정 페이지의 총수를 다음 두 `/proc/vmstat` 항목으로 관찰할 수 있습니다.

/proc/vmstat/nr_foll_pin_acquired
/proc/vmstat/nr_foll_pin_released

일반적인 상태에서는 장기 [R]DMA 고정이 존재하거나 pin/unpin 전환 중인 경우를 제외하면 두 값이 같습니다.

고정 횟수 통계의 의미

247-270
  • `nr_foll_pin_acquired`는 시스템 전원을 켠 뒤 획득한 논리적 pin 수입니다. Huge page에서는 head page와 각 tail page를 포함한 huge page 내부의 페이지마다 head page를 한 번씩 고정합니다. 이는 huge page에 `get_user_pages()`를 적용할 때 각 head 또는 tail page마다 head page의 refcount를 증가시키는 동작과 같습니다.
  • `nr_foll_pin_released`는 시스템 전원을 켠 뒤 해제한 논리적 pin 수입니다. 원래 huge page 단위로 고정했더라도 페이지 해제는 `PAGE_SIZE` 단위로 수행됩니다. 앞서 설명한 획득 계수 방식 때문에 다음 예제 뒤에는 통계가 다시 균형을 이룹니다.
pin_user_pages(huge_page);
for (each page in huge_page)
    unpin_user_page(page);

따라서 다음 조건이 성립할 것으로 예상합니다.

nr_foll_pin_released == nr_foll_pin_acquired

다만 기존에 장기 RDMA 고정 때문에 이미 균형이 맞지 않았다면 예외입니다.

기타 진단

271-277

기타 진단 (Other diagnostics)

`dump_page()`는 새로운 계수 필드를 처리하고 대형 folio를 전반적으로 더 잘 보고하도록 개선되었습니다. 특히 대형 folio에 대해서는 정확한 `pincount`를 출력합니다.

참고 자료

278-286

참고 자료 (References)

John Hubbard, 2019년 10월