← Documents Documentation/block/biovecs.rst GitHub 원문 ↗

Linux 6.18.37 · Block

Immutable biovecs and biovec iterators

불변 biovec과 bvec_iter의 상태 분리, driver 전환 규칙, segment helper 제약을 설명합니다.

Source pathDocumentation/block/biovecs.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

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

1. 요약·해설

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

요약과 해설

biovecs.rst:1-151

Linux block layer는 submit된 `bio_vec`를 직접 변경하지 않고 진행 상태를 `struct bvec_iter`에 보관합니다. 이 분리는 부분 완료, clone, split, biovec 공유를 안전하고 단순하게 만듭니다.

driver는 raw biovec이나 `bi_idx`, `bi_vcnt`에 의존하지 말고 iterator-aware helper를 사용해야 합니다. 특히 `_all` helper는 `BIO_CLONED`가 아닌 `bio`에만 허용되므로 일반 driver 순회에는 맞지 않습니다.

2. 영어 원문 전체

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

원문 전체 펼치기
1 ======================================
2 Immutable biovecs and biovec iterators
3 ======================================
4
5 Kent Overstreet <[email protected]>
6
7 As of 3.13, biovecs should never be modified after a bio has been submitted.
8 Instead, we have a new struct bvec_iter which represents a range of a biovec -
9 the iterator will be modified as the bio is completed, not the biovec.
10
11 More specifically, old code that needed to partially complete a bio would
12 update bi_sector and bi_size, and advance bi_idx to the next biovec. If it
13 ended up partway through a biovec, it would increment bv_offset and decrement
14 bv_len by the number of bytes completed in that biovec.
15
16 In the new scheme of things, everything that must be mutated in order to
17 partially complete a bio is segregated into struct bvec_iter: bi_sector,
18 bi_size and bi_idx have been moved there; and instead of modifying bv_offset
19 and bv_len, struct bvec_iter has bi_bvec_done, which represents the number of
20 bytes completed in the current bvec.
21
22 There are a bunch of new helper macros for hiding the gory details - in
23 particular, presenting the illusion of partially completed biovecs so that
24 normal code doesn't have to deal with bi_bvec_done.
25
26 * Driver code should no longer refer to biovecs directly; we now have
27 bio_iovec() and bio_iter_iovec() macros that return literal struct biovecs,
28 constructed from the raw biovecs but taking into account bi_bvec_done and
29 bi_size.
30
31 bio_for_each_segment() has been updated to take a bvec_iter argument
32 instead of an integer (that corresponded to bi_idx); for a lot of code the
33 conversion just required changing the types of the arguments to
34 bio_for_each_segment().
35
36 * Advancing a bvec_iter is done with bio_advance_iter(); bio_advance() is a
37 wrapper around bio_advance_iter() that operates on bio->bi_iter, and also
38 advances the bio integrity's iter if present.
39
40 There is a lower level advance function - bvec_iter_advance() - which takes
41 a pointer to a biovec, not a bio; this is used by the bio integrity code.
42
43 As of 5.12 bvec segments with zero bv_len are not supported.
44
45 What's all this get us?
46 =======================
47
48 Having a real iterator, and making biovecs immutable, has a number of
49 advantages:
50
51 * Before, iterating over bios was very awkward when you weren't processing
52 exactly one bvec at a time - for example, bio_copy_data() in block/bio.c,
53 which copies the contents of one bio into another. Because the biovecs
54 wouldn't necessarily be the same size, the old code was tricky convoluted -
55 it had to walk two different bios at the same time, keeping both bi_idx and
56 and offset into the current biovec for each.
57
58 The new code is much more straightforward - have a look. This sort of
59 pattern comes up in a lot of places; a lot of drivers were essentially open
60 coding bvec iterators before, and having common implementation considerably
61 simplifies a lot of code.
62
63 * Before, any code that might need to use the biovec after the bio had been
64 completed (perhaps to copy the data somewhere else, or perhaps to resubmit
65 it somewhere else if there was an error) had to save the entire bvec array
66 - again, this was being done in a fair number of places.
67
68 * Biovecs can be shared between multiple bios - a bvec iter can represent an
69 arbitrary range of an existing biovec, both starting and ending midway
70 through biovecs. This is what enables efficient splitting of arbitrary
71 bios. Note that this means we _only_ use bi_size to determine when we've
72 reached the end of a bio, not bi_vcnt - and the bio_iovec() macro takes
73 bi_size into account when constructing biovecs.
74
75 * Splitting bios is now much simpler. The old bio_split() didn't even work on
76 bios with more than a single bvec! Now, we can efficiently split arbitrary
77 size bios - because the new bio can share the old bio's biovec.
78
79 Care must be taken to ensure the biovec isn't freed while the split bio is
80 still using it, in case the original bio completes first, though. Using
81 bio_chain() when splitting bios helps with this.
82
83 * Submitting partially completed bios is now perfectly fine - this comes up
84 occasionally in stacking block drivers and various code (e.g. md and
85 bcache) had some ugly workarounds for this.
86
87 It used to be the case that submitting a partially completed bio would work
88 fine to _most_ devices, but since accessing the raw bvec array was the
89 norm, not all drivers would respect bi_idx and those would break. Now,
90 since all drivers _must_ go through the bvec iterator - and have been
91 audited to make sure they are - submitting partially completed bios is
92 perfectly fine.
93
94 Other implications:
95 ===================
96
97 * Almost all usage of bi_idx is now incorrect and has been removed; instead,
98 where previously you would have used bi_idx you'd now use a bvec_iter,
99 probably passing it to one of the helper macros.
100
101 I.e. instead of using bio_iovec_idx() (or bio->bi_iovec[bio->bi_idx]), you
102 now use bio_iter_iovec(), which takes a bvec_iter and returns a
103 literal struct bio_vec - constructed on the fly from the raw biovec but
104 taking into account bi_bvec_done (and bi_size).
105
106 * bi_vcnt can't be trusted or relied upon by driver code - i.e. anything that
107 doesn't actually own the bio. The reason is twofold: firstly, it's not
108 actually needed for iterating over the bio anymore - we only use bi_size.
109 Secondly, when cloning a bio and reusing (a portion of) the original bio's
110 biovec, in order to calculate bi_vcnt for the new bio we'd have to iterate
111 over all the biovecs in the new bio - which is silly as it's not needed.
112
113 So, don't use bi_vcnt anymore.
114
115 * The current interface allows the block layer to split bios as needed, so we
116 could eliminate a lot of complexity particularly in stacked drivers. Code
117 that creates bios can then create whatever size bios are convenient, and
118 more importantly stacked drivers don't have to deal with both their own bio
119 size limitations and the limitations of the underlying devices. Thus
120 there's no need to define ->merge_bvec_fn() callbacks for individual block
121 drivers.
122
123 Usage of helpers:
124 =================
125
126 * The following helpers whose names have the suffix of `_all` can only be used
127 on non-BIO_CLONED bio. They are usually used by filesystem code. Drivers
128 shouldn't use them because the bio may have been split before it reached the
129 driver.
130
131 ::
132
133 bio_for_each_segment_all()
134 bio_for_each_bvec_all()
135 bio_first_bvec_all()
136 bio_first_page_all()
137 bio_first_folio_all()
138 bio_last_bvec_all()
139
140 * The following helpers iterate over single-page segment. The passed 'struct
141 bio_vec' will contain a single-page IO vector during the iteration::
142
143 bio_for_each_segment()
144 bio_for_each_segment_all()
145
146 * The following helpers iterate over multi-page bvec. The passed 'struct
147 bio_vec' will contain a multi-page IO vector during the iteration::
148
149 bio_for_each_bvec()
150 bio_for_each_bvec_all()
151 rq_for_each_bvec()
152

3. 한국어 전문 번역

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

불변 biovec과 iterator 모델

1-44

`Immutable biovecs and biovec iterators` 문서는 Kent Overstreet `<[email protected]>`가 작성했습니다. Linux 3.13부터 `bio`가 submit된 뒤에는 biovec을 절대 수정하지 않아야 합니다. 대신 biovec의 범위를 나타내는 새 `struct bvec_iter`를 사용하며, `bio`가 완료되어 갈 때 biovec 자체가 아니라 iterator를 수정합니다.

이전 code에서 `bio`를 부분 완료하려면 `bi_sector`와 `bi_size`를 갱신하고 `bi_idx`를 다음 biovec으로 전진시켰습니다. biovec 중간에서 끝났다면 그 biovec에서 완료한 byte 수만큼 `bv_offset`을 늘리고 `bv_len`을 줄였습니다.

새 방식에서는 `bio`의 부분 완료를 위해 바뀌어야 하는 모든 상태를 `struct bvec_iter`로 분리합니다. `bi_sector`, `bi_size`, `bi_idx`를 이 구조체로 옮겼고, `bv_offset`과 `bv_len`을 수정하는 대신 현재 bvec에서 완료한 byte 수를 나타내는 `bi_bvec_done`을 둡니다.

새 helper macro들은 이런 내부 처리를 감춥니다. 특히 부분 완료된 biovec처럼 보이는 view를 제공하므로 일반 code가 `bi_bvec_done`을 직접 다룰 필요가 없습니다.

  • driver code는 이제 biovec을 직접 참조하지 않아야 합니다. `bio_iovec()`과 `bio_iter_iovec()` macro는 raw biovec에서 literal `struct biovecs`를 구성해 반환하면서 `bi_bvec_done`과 `bi_size`를 반영합니다. `bio_for_each_segment()`는 `bi_idx`에 해당하던 integer 대신 `bvec_iter` argument를 받도록 변경되었습니다. 많은 code는 `bio_for_each_segment()` argument type만 바꾸면 전환할 수 있습니다.
  • `bvec_iter`를 전진시킬 때는 `bio_advance_iter()`를 사용합니다. `bio_advance()`는 `bio->bi_iter`에 작동하는 `bio_advance_iter()` wrapper이며, bio integrity iterator가 있으면 그것도 함께 전진시킵니다. 더 낮은 수준의 `bvec_iter_advance()`는 `bio`가 아니라 biovec pointer를 받고 bio integrity code에서 사용합니다.

Linux 5.12부터 `bv_len`이 0인 bvec segment는 지원하지 않습니다.

불변 biovec이 주는 이점

45-93

실제 iterator를 두고 biovec을 immutable로 만들면 다음과 같은 이점이 있습니다.

  • 예전에는 정확히 한 번에 bvec 하나씩 처리하지 않을 때 `bio` 순회가 매우 까다로웠습니다. 예를 들어 `block/bio.c`의 `bio_copy_data()`는 한 `bio`의 내용을 다른 `bio`로 복사합니다. 두 biovec의 크기가 반드시 같지 않으므로 이전 code는 두 `bio`를 동시에 순회하면서 각각의 `bi_idx`와 현재 biovec 안의 offset을 모두 유지해야 했습니다. 새 code는 훨씬 직관적입니다. 이 pattern은 여러 곳에서 나타나며, 과거 많은 driver가 bvec iterator를 직접 구현했습니다. 공통 구현을 사용하면 많은 code가 크게 단순해집니다.
  • 예전에는 `bio` 완료 후 biovec을 다시 써야 할 수 있는 code가 전체 bvec array를 저장해야 했습니다. data를 다른 곳에 복사하거나 error가 발생했을 때 다른 곳으로 다시 submit하는 경우가 이에 해당하며, 실제로 여러 곳에서 이 작업을 수행했습니다.
  • biovec을 여러 `bio`가 공유할 수 있습니다. bvec iterator는 기존 biovec의 임의 범위를 나타낼 수 있고, biovec 중간에서 시작하거나 끝날 수도 있습니다. 이 특성 덕분에 임의 `bio`를 효율적으로 split할 수 있습니다. 따라서 `bio`의 끝에 도달했는지는 `bi_vcnt`가 아니라 오직 `bi_size`로 판단하며, `bio_iovec()` macro도 biovec을 구성할 때 `bi_size`를 반영합니다.
  • `bio` split이 훨씬 단순해졌습니다. 이전 `bio_split()`은 bvec가 하나보다 많은 `bio`에서는 동작하지도 않았습니다. 이제 새 `bio`가 기존 `bio`의 biovec을 공유할 수 있어 임의 크기의 `bio`를 효율적으로 split할 수 있습니다. 다만 원본 `bio`가 먼저 완료될 수 있으므로 split된 `bio`가 사용하는 동안 biovec이 free되지 않게 주의해야 합니다. split할 때 `bio_chain()`을 사용하면 이를 처리하는 데 도움이 됩니다.
  • 부분 완료된 `bio`를 submit해도 완전히 안전합니다. 이 요구는 stacking block driver에서 간혹 생겼고 `md`, `bcache` 같은 여러 code가 보기 좋지 않은 workaround를 사용했습니다. 예전에는 대부분의 device에서 부분 완료된 `bio`를 submit해도 동작했지만 raw bvec array 접근이 일반적이어서 모든 driver가 `bi_idx`를 준수하지는 않았고, 그런 driver는 실패했습니다. 이제 모든 driver가 반드시 bvec iterator를 거치며 이를 확인하는 audit도 마쳤으므로 부분 완료된 `bio`를 안전하게 submit할 수 있습니다.

driver code에 미치는 영향

94-122

새 interface에는 다음과 같은 추가 의미가 있습니다.

  • `bi_idx`를 사용하는 code는 이제 거의 모두 잘못된 것이며 제거되었습니다. 이전에 `bi_idx`를 썼을 곳에서는 이제 `bvec_iter`를 사용하고, 대개 helper macro 중 하나에 전달합니다. 예를 들어 `bio_iovec_idx()` 또는 `bio->bi_iovec[bio->bi_idx]` 대신 `bio_iter_iovec()`을 사용합니다. 이 함수는 `bvec_iter`를 받아 raw biovec으로부터 literal `struct bio_vec`를 즉석에서 구성해 반환하며 `bi_bvec_done`과 `bi_size`를 반영합니다.
  • `bio`를 실제로 소유하지 않는 driver code는 `bi_vcnt`를 신뢰하거나 의존할 수 없습니다. 이유는 두 가지입니다. 첫째, 이제 `bio` 순회에는 `bi_vcnt`가 필요하지 않고 `bi_size`만 사용합니다. 둘째, `bio`를 clone하면서 원본 `bio`의 biovec 일부를 재사용할 때 새 `bio`의 `bi_vcnt`를 계산하려면 새 `bio`의 모든 biovec을 순회해야 하는데, 필요하지 않은 값을 위해 이런 작업을 하는 것은 무의미합니다. 따라서 더 이상 `bi_vcnt`를 사용하지 마십시오.
  • 현재 interface에서는 block layer가 필요에 따라 `bio`를 split할 수 있으므로 특히 stacked driver의 복잡성을 크게 줄일 수 있습니다. `bio` 생성 code는 편리한 크기로 `bio`를 만들면 되고, 더 중요한 점은 stacked driver가 자체 `bio` 크기 제한과 아래쪽 device의 제한을 동시에 처리하지 않아도 된다는 것입니다. 따라서 개별 block driver에 `->merge_bvec_fn()` callback을 정의할 필요가 없습니다.

`_all` helper의 사용 제약

123-139

이름 끝에 `_all` suffix가 붙은 다음 helper는 `BIO_CLONED`가 아닌 `bio`에서만 사용할 수 있습니다. 보통 filesystem code가 사용합니다. driver에 도달하기 전에 `bio`가 split되었을 수 있으므로 driver는 이 helper를 사용하지 않아야 합니다.

bio_for_each_segment_all()
bio_for_each_bvec_all()
bio_first_bvec_all()
bio_first_page_all()
bio_first_folio_all()
bio_last_bvec_all()

single-page와 multi-page 순회

140-151

다음 helper는 single-page segment를 순회합니다. 순회 중 전달되는 `struct bio_vec`에는 single-page I/O vector가 들어 있습니다.

bio_for_each_segment()
bio_for_each_segment_all()

다음 helper는 multi-page bvec을 순회합니다. 순회 중 전달되는 `struct bio_vec`에는 multi-page I/O vector가 들어 있습니다.

bio_for_each_bvec()
bio_for_each_bvec_all()
rq_for_each_bvec()