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

Linux 6.18.37 · Core API

Folio Queue

I/O buffer용 folio segment 목록의 초기화, 추가·제거, 조회, mark, ITER_FOLIOQ 순회와 무잠금 생산·소비 규칙을 설명합니다.

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

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

1. 요약·해설

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

요약과 해설

folio_queue.rst:1-209

`folio_queue`는 folio 목록을 여러 segment로 나누어 I/O buffer를 구성합니다. 각 segment는 비순환 이중 연결 구조이며 `next`와 `prev`로 이어지고, folio pointer와 size 및 mark를 내부에 보관합니다.

append 함수는 다음 빈 slot을 채우지만 capacity 초과를 검사하거나 목록을 확장하지 않습니다. clear 함수는 slot과 mark만 지우고 count를 줄이지 않으므로, 소비 과정에서는 초기화된 slot 범위와 실제 점유 상태를 구분해야 합니다.

`ITER_FOLIOQ` iov_iter는 segment 사이를 전후로 이동하며 필요한 memory barrier를 제공합니다. producer와 consumer가 lock 없이 양 끝을 동시에 갱신하려면 iterator가 참조하는 마지막 segment를 너무 일찍 free하지 않고 queue에 최소 한 segment를 남겨야 합니다.

2. 영어 원문 전체

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

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0+
2
3 ===========
4 Folio Queue
5 ===========
6
7 :Author: David Howells <dhowells@redhat.com>
8
9 .. Contents:
10
11 * Overview
12 * Initialisation
13 * Adding and removing folios
14 * Querying information about a folio
15 * Querying information about a folio_queue
16 * Folio queue iteration
17 * Folio marks
18 * Lockless simultaneous production/consumption issues
19
20
21 Overview
22 ========
23
24 The folio_queue struct forms a single segment in a segmented list of folios
25 that can be used to form an I/O buffer. As such, the list can be iterated over
26 using the ITER_FOLIOQ iov_iter type.
27
28 The publicly accessible members of the structure are::
29
30 struct folio_queue {
31 struct folio_queue *next;
32 struct folio_queue *prev;
33 ...
34 };
35
36 A pair of pointers are provided, ``next`` and ``prev``, that point to the
37 segments on either side of the segment being accessed. Whilst this is a
38 doubly-linked list, it is intentionally not a circular list; the outward
39 sibling pointers in terminal segments should be NULL.
40
41 Each segment in the list also stores:
42
43 * an ordered sequence of folio pointers,
44 * the size of each folio and
45 * three 1-bit marks per folio,
46
47 but these should not be accessed directly as the underlying data structure may
48 change, but rather the access functions outlined below should be used.
49
50 The facility can be made accessible by::
51
52 #include <linux/folio_queue.h>
53
54 and to use the iterator::
55
56 #include <linux/uio.h>
57
58
59 Initialisation
60 ==============
61
62 A segment should be initialised by calling::
63
64 void folioq_init(struct folio_queue *folioq);
65
66 with a pointer to the segment to be initialised. Note that this will not
67 necessarily initialise all the folio pointers, so care must be taken to check
68 the number of folios added.
69
70
71 Adding and removing folios
72 ==========================
73
74 Folios can be set in the next unused slot in a segment struct by calling one
75 of::
76
77 unsigned int folioq_append(struct folio_queue *folioq,
78 struct folio *folio);
79
80 unsigned int folioq_append_mark(struct folio_queue *folioq,
81 struct folio *folio);
82
83 Both functions update the stored folio count, store the folio and note its
84 size. The second function also sets the first mark for the folio added. Both
85 functions return the number of the slot used. [!] Note that no attempt is made
86 to check that the capacity wasn't overrun and the list will not be extended
87 automatically.
88
89 A folio can be excised by calling::
90
91 void folioq_clear(struct folio_queue *folioq, unsigned int slot);
92
93 This clears the slot in the array and also clears all the marks for that folio,
94 but doesn't change the folio count - so future accesses of that slot must check
95 if the slot is occupied.
96
97
98 Querying information about a folio
99 ==================================
100
101 Information about the folio in a particular slot may be queried by the
102 following function::
103
104 struct folio *folioq_folio(const struct folio_queue *folioq,
105 unsigned int slot);
106
107 If a folio has not yet been set in that slot, this may yield an undefined
108 pointer. The size of the folio in a slot may be queried with either of::
109
110 unsigned int folioq_folio_order(const struct folio_queue *folioq,
111 unsigned int slot);
112
113 size_t folioq_folio_size(const struct folio_queue *folioq,
114 unsigned int slot);
115
116 The first function returns the size as an order and the second as a number of
117 bytes.
118
119
120 Querying information about a folio_queue
121 ========================================
122
123 Information may be retrieved about a particular segment with the following
124 functions::
125
126 unsigned int folioq_nr_slots(const struct folio_queue *folioq);
127
128 unsigned int folioq_count(struct folio_queue *folioq);
129
130 bool folioq_full(struct folio_queue *folioq);
131
132 The first function returns the maximum capacity of a segment. It must not be
133 assumed that this won't vary between segments. The second returns the number
134 of folios added to a segments and the third is a shorthand to indicate if the
135 segment has been filled to capacity.
136
137 Not that the count and fullness are not affected by clearing folios from the
138 segment. These are more about indicating how many slots in the array have been
139 initialised, and it assumed that slots won't get reused, but rather the segment
140 will get discarded as the queue is consumed.
141
142
143 Folio marks
144 ===========
145
146 Folios within a queue can also have marks assigned to them. These marks can be
147 used to note information such as if a folio needs folio_put() calling upon it.
148 There are three marks available to be set for each folio.
149
150 The marks can be set by::
151
152 void folioq_mark(struct folio_queue *folioq, unsigned int slot);
153 void folioq_mark2(struct folio_queue *folioq, unsigned int slot);
154
155 Cleared by::
156
157 void folioq_unmark(struct folio_queue *folioq, unsigned int slot);
158 void folioq_unmark2(struct folio_queue *folioq, unsigned int slot);
159
160 And the marks can be queried by::
161
162 bool folioq_is_marked(const struct folio_queue *folioq, unsigned int slot);
163 bool folioq_is_marked2(const struct folio_queue *folioq, unsigned int slot);
164
165 The marks can be used for any purpose and are not interpreted by this API.
166
167
168 Folio queue iteration
169 =====================
170
171 A list of segments may be iterated over using the I/O iterator facility using
172 an ``iov_iter`` iterator of ``ITER_FOLIOQ`` type. The iterator may be
173 initialised with::
174
175 void iov_iter_folio_queue(struct iov_iter *i, unsigned int direction,
176 const struct folio_queue *folioq,
177 unsigned int first_slot, unsigned int offset,
178 size_t count);
179
180 This may be told to start at a particular segment, slot and offset within a
181 queue. The iov iterator functions will follow the next pointers when advancing
182 and prev pointers when reverting when needed.
183
184
185 Lockless simultaneous production/consumption issues
186 ===================================================
187
188 If properly managed, the list can be extended by the producer at the head end
189 and shortened by the consumer at the tail end simultaneously without the need
190 to take locks. The ITER_FOLIOQ iterator inserts appropriate barriers to aid
191 with this.
192
193 Care must be taken when simultaneously producing and consuming a list. If the
194 last segment is reached and the folios it refers to are entirely consumed by
195 the IOV iterators, an iov_iter struct will be left pointing to the last segment
196 with a slot number equal to the capacity of that segment. The iterator will
197 try to continue on from this if there's another segment available when it is
198 used again, but care must be taken lest the segment got removed and freed by
199 the consumer before the iterator was advanced.
200
201 It is recommended that the queue always contain at least one segment, even if
202 that segment has never been filled or is entirely spent. This prevents the
203 head and tail pointers from collapsing.
204
205
206 API Function Reference
207 ======================
208
209 .. kernel-doc:: include/linux/folio_queue.h
210

3. 한국어 전문 번역

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

문서 정보와 목차

1-20

`SPDX-License-Identifier: GPL-2.0+`

Folio Queue

저자: David Howells <dhowells@redhat.com>

Contents

  • Overview
  • Initialisation
  • Adding and removing folios
  • Querying information about a folio
  • Querying information about a folio_queue
  • Folio queue iteration
  • Folio marks
  • Lockless simultaneous production/consumption issues

folio_queue 구조와 세그먼트 연결

21-58

Overview

`folio_queue` struct는 I/O buffer를 구성할 수 있는 folio 분할 목록에서 단일 segment를 나타냅니다. 따라서 `ITER_FOLIOQ` iov_iter type을 사용해 목록을 순회할 수 있습니다.

구조체에서 공개적으로 접근할 수 있는 member는 다음과 같습니다.

struct folio_queue {
        struct folio_queue *next;
        struct folio_queue *prev;
        ...
};

`next`와 `prev` 두 pointer는 현재 접근 중인 segment 양쪽의 segment를 가리킵니다. doubly-linked list이지만 의도적으로 circular list가 아닙니다. 양 끝 terminal segment의 바깥쪽 sibling pointer는 NULL이어야 합니다.

목록의 각 segment에는 다음 정보도 저장됩니다.

  • 순서가 있는 folio pointer sequence
  • 각 folio의 size
  • folio마다 세 개의 1-bit mark

underlying data structure가 바뀔 수 있으므로 이 정보에 직접 접근하면 안 됩니다. 대신 아래에 설명한 access function을 사용해야 합니다.

이 기능을 사용하려면 다음 header를 include합니다.

#include <linux/folio_queue.h>

iterator를 사용하려면 다음 header도 include합니다.

#include <linux/uio.h>

세그먼트 초기화

59-70

Initialisation

segment는 다음 함수를 호출해 초기화해야 합니다.

void folioq_init(struct folio_queue *folioq);

초기화할 segment의 pointer를 전달합니다. 이 함수가 모든 folio pointer를 반드시 초기화하는 것은 아니므로, 접근할 때 추가된 folio 수를 주의해서 확인해야 합니다.

folio 추가와 제거

71-97

Adding and removing folios

segment struct에서 다음번 비어 있는 slot에 folio를 설정하려면 다음 함수 중 하나를 호출합니다.

unsigned int folioq_append(struct folio_queue *folioq,
                           struct folio *folio);

unsigned int folioq_append_mark(struct folio_queue *folioq,
                                struct folio *folio);

두 함수 모두 저장된 folio count를 갱신하고 folio를 저장하며 size를 기록합니다. 두 번째 함수는 추가된 folio의 첫 번째 mark도 설정합니다. 두 함수는 사용한 slot number를 반환합니다.

[!] capacity 초과 여부를 검사하지 않으며 목록도 자동으로 확장되지 않는다는 점에 유의해야 합니다.

folio를 잘라내려면 다음 함수를 호출합니다.

void folioq_clear(struct folio_queue *folioq, unsigned int slot);

이 함수는 array의 slot과 해당 folio의 모든 mark를 지우지만 folio count는 바꾸지 않습니다. 따라서 이후 이 slot에 접근할 때는 slot이 사용 중인지 확인해야 합니다.

개별 folio 정보 조회

98-119

Querying information about a folio

특정 slot의 folio 정보는 다음 함수로 조회할 수 있습니다.

struct folio *folioq_folio(const struct folio_queue *folioq,
                           unsigned int slot);

해당 slot에 folio가 아직 설정되지 않았다면 undefined pointer가 반환될 수 있습니다. slot에 있는 folio의 size는 다음 두 함수 중 하나로 조회합니다.

unsigned int folioq_folio_order(const struct folio_queue *folioq,
                                unsigned int slot);

size_t folioq_folio_size(const struct folio_queue *folioq,
                         unsigned int slot);

첫 번째 함수는 size를 order로 반환하고 두 번째 함수는 byte 수로 반환합니다.

folio_queue 세그먼트 정보 조회

120-142

Querying information about a folio_queue

특정 segment의 정보는 다음 함수들로 가져올 수 있습니다.

unsigned int folioq_nr_slots(const struct folio_queue *folioq);

unsigned int folioq_count(struct folio_queue *folioq);

bool folioq_full(struct folio_queue *folioq);

첫 번째 함수는 segment의 maximum capacity를 반환합니다. 이 값이 모든 segment에서 같다고 가정하면 안 됩니다. 두 번째 함수는 segment에 추가된 folio 수를 반환하고, 세 번째 함수는 segment가 capacity까지 채워졌는지를 간단히 나타냅니다.

segment에서 folio를 clear해도 count와 fullness는 영향을 받지 않습니다. 이 값들은 array에서 초기화된 slot 수를 나타내는 성격이 강합니다. slot은 재사용하지 않고 queue가 소비되면 segment 자체를 버린다고 가정합니다.

folio mark 설정과 조회

143-167

Folio marks

queue 안의 folio에는 mark도 지정할 수 있습니다. 예를 들어 어떤 folio에 `folio_put()`을 호출해야 하는지를 기록할 수 있습니다. 각 folio마다 세 개의 mark를 사용할 수 있습니다.

mark는 다음 함수로 설정합니다.

void folioq_mark(struct folio_queue *folioq, unsigned int slot);
void folioq_mark2(struct folio_queue *folioq, unsigned int slot);

다음 함수로 mark를 지웁니다.

void folioq_unmark(struct folio_queue *folioq, unsigned int slot);
void folioq_unmark2(struct folio_queue *folioq, unsigned int slot);

다음 함수로 mark를 조회합니다.

bool folioq_is_marked(const struct folio_queue *folioq, unsigned int slot);
bool folioq_is_marked2(const struct folio_queue *folioq, unsigned int slot);

mark는 어떤 용도로든 사용할 수 있으며 이 API 자체는 mark의 의미를 해석하지 않습니다.

I/O iterator를 이용한 순회

168-184

Folio queue iteration

segment 목록은 `ITER_FOLIOQ` type의 `iov_iter` iterator를 사용하는 I/O iterator 기능으로 순회할 수 있습니다. iterator는 다음 함수로 초기화합니다.

void iov_iter_folio_queue(struct iov_iter *i, unsigned int direction,
                          const struct folio_queue *folioq,
                          unsigned int first_slot, unsigned int offset,
                          size_t count);

queue 안의 특정 segment, slot 및 offset에서 시작하도록 지정할 수 있습니다. iov iterator function은 전진할 때 `next` pointer를 따라가고, 필요에 따라 되돌릴 때 `prev` pointer를 따라갑니다.

무잠금 동시 생산과 소비

185-205

Lockless simultaneous production/consumption issues

올바르게 관리하면 lock을 잡지 않고도 producer가 head end에서 목록을 확장하는 동시에 consumer가 tail end에서 목록을 줄일 수 있습니다. `ITER_FOLIOQ` iterator는 이를 돕기 위해 적절한 barrier를 삽입합니다.

목록을 동시에 생산하고 소비할 때는 주의해야 합니다. 마지막 segment에 도달해 그 segment가 참조하는 folio를 IOV iterator가 모두 소비하면, `iov_iter` struct는 마지막 segment를 가리키면서 slot number가 그 segment의 capacity와 같은 상태로 남습니다.

iterator를 다시 사용할 때 다른 segment가 있으면 그곳에서 계속 진행하려고 합니다. 그러나 iterator가 전진하기 전에 consumer가 기존 segment를 제거하고 free하지 않도록 주의해야 합니다.

queue에는 한 번도 채워지지 않았거나 완전히 소비된 segment라도 항상 최소 하나의 segment를 유지하는 것이 좋습니다. 이렇게 하면 head와 tail pointer가 한 지점으로 붕괴하는 것을 막을 수 있습니다.

API 함수 참조

206-209

API Function Reference

함수별 kernel-doc 참조는 `include/linux/folio_queue.h`에서 생성됩니다: `.. kernel-doc:: include/linux/folio_queue.h`.