Documentation/driver-api/nvdimm/btt.rst GitHub 원문 ↗

Linux 6.18.37 · Driver API

BTT - Block Translation Table

영구 메모리에 원자적 섹터 갱신을 제공하는 BTT의 배치, map, flog, RTT와 복구 절차의 전문 번역입니다.

Source pathDocumentation/driver-api/nvdimm/btt.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

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

1. 요약·해설

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

요약과 해설

btt.rst:1-285

BTT는 모든 쓰기를 새 free block에 기록하고 map과 flog를 순서대로 갱신해 전원 장애에도 이전 또는 새 섹터 중 하나만 노출합니다. Arena별 metadata, lane·RTT·map lock과 시작 시 free-list 복구 규칙이 이 보장을 구성합니다.

문서 구성
원문 줄내용
1-25목적과 원자성
26-70Arena 정적 배치
71-152Map과 Flog
153-220Lane, RTT, map lock, 복구
221-251Read와 Write 흐름
252-285오류 처리와 ndctl 사용

2. 영어 원문 전체

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

원문 전체 펼치기
1 =============================
2 BTT - Block Translation Table
3 =============================
4
5
6 1. Introduction
7 ===============
8
9 Persistent memory based storage is able to perform IO at byte (or more
10 accurately, cache line) granularity. However, we often want to expose such
11 storage as traditional block devices. The block drivers for persistent memory
12 will do exactly this. However, they do not provide any atomicity guarantees.
13 Traditional SSDs typically provide protection against torn sectors in hardware,
14 using stored energy in capacitors to complete in-flight block writes, or perhaps
15 in firmware. We don't have this luxury with persistent memory - if a write is in
16 progress, and we experience a power failure, the block will contain a mix of old
17 and new data. Applications may not be prepared to handle such a scenario.
18
19 The Block Translation Table (BTT) provides atomic sector update semantics for
20 persistent memory devices, so that applications that rely on sector writes not
21 being torn can continue to do so. The BTT manifests itself as a stacked block
22 device, and reserves a portion of the underlying storage for its metadata. At
23 the heart of it, is an indirection table that re-maps all the blocks on the
24 volume. It can be thought of as an extremely simple file system that only
25 provides atomic sector updates.
26
27
28 2. Static Layout
29 ================
30
31 The underlying storage on which a BTT can be laid out is not limited in any way.
32 The BTT, however, splits the available space into chunks of up to 512 GiB,
33 called "Arenas".
34
35 Each arena follows the same layout for its metadata, and all references in an
36 arena are internal to it (with the exception of one field that points to the
37 next arena). The following depicts the "On-disk" metadata layout::
38
39
40 Backing Store +-------> Arena
41 +---------------+ | +------------------+
42 | | | | Arena info block |
43 | Arena 0 +---+ | 4K |
44 | 512G | +------------------+
45 | | | |
46 +---------------+ | |
47 | | | |
48 | Arena 1 | | Data Blocks |
49 | 512G | | |
50 | | | |
51 +---------------+ | |
52 | . | | |
53 | . | | |
54 | . | | |
55 | | | |
56 | | | |
57 +---------------+ +------------------+
58 | |
59 | BTT Map |
60 | |
61 | |
62 +------------------+
63 | |
64 | BTT Flog |
65 | |
66 +------------------+
67 | Info block copy |
68 | 4K |
69 +------------------+
70
71
72 3. Theory of Operation
73 ======================
74
75
76 a. The BTT Map
77 --------------
78
79 The map is a simple lookup/indirection table that maps an LBA to an internal
80 block. Each map entry is 32 bits. The two most significant bits are special
81 flags, and the remaining form the internal block number.
82
83 ======== =============================================================
84 Bit Description
85 ======== =============================================================
86 31 - 30 Error and Zero flags - Used in the following way::
87
88 == == ====================================================
89 31 30 Description
90 == == ====================================================
91 0 0 Initial state. Reads return zeroes; Premap = Postmap
92 0 1 Zero state: Reads return zeroes
93 1 0 Error state: Reads fail; Writes clear 'E' bit
94 1 1 Normal Block – has valid postmap
95 == == ====================================================
96
97 29 - 0 Mappings to internal 'postmap' blocks
98 ======== =============================================================
99
100
101 Some of the terminology that will be subsequently used:
102
103 ============ ================================================================
104 External LBA LBA as made visible to upper layers.
105 ABA Arena Block Address - Block offset/number within an arena
106 Premap ABA The block offset into an arena, which was decided upon by range
107 checking the External LBA
108 Postmap ABA The block number in the "Data Blocks" area obtained after
109 indirection from the map
110 nfree The number of free blocks that are maintained at any given time.
111 This is the number of concurrent writes that can happen to the
112 arena.
113 ============ ================================================================
114
115
116 For example, after adding a BTT, we surface a disk of 1024G. We get a read for
117 the external LBA at 768G. This falls into the second arena, and of the 512G
118 worth of blocks that this arena contributes, this block is at 256G. Thus, the
119 premap ABA is 256G. We now refer to the map, and find out the mapping for block
120 'X' (256G) points to block 'Y', say '64'. Thus the postmap ABA is 64.
121
122
123 b. The BTT Flog
124 ---------------
125
126 The BTT provides sector atomicity by making every write an "allocating write",
127 i.e. Every write goes to a "free" block. A running list of free blocks is
128 maintained in the form of the BTT flog. 'Flog' is a combination of the words
129 "free list" and "log". The flog contains 'nfree' entries, and an entry contains:
130
131 ======== =====================================================================
132 lba The premap ABA that is being written to
133 old_map The old postmap ABA - after 'this' write completes, this will be a
134 free block.
135 new_map The new postmap ABA. The map will up updated to reflect this
136 lba->postmap_aba mapping, but we log it here in case we have to
137 recover.
138 seq Sequence number to mark which of the 2 sections of this flog entry is
139 valid/newest. It cycles between 01->10->11->01 (binary) under normal
140 operation, with 00 indicating an uninitialized state.
141 lba' alternate lba entry
142 old_map' alternate old postmap entry
143 new_map' alternate new postmap entry
144 seq' alternate sequence number.
145 ======== =====================================================================
146
147 Each of the above fields is 32-bit, making one entry 32 bytes. Entries are also
148 padded to 64 bytes to avoid cache line sharing or aliasing. Flog updates are
149 done such that for any entry being written, it:
150 a. overwrites the 'old' section in the entry based on sequence numbers
151 b. writes the 'new' section such that the sequence number is written last.
152
153
154 c. The concept of lanes
155 -----------------------
156
157 While 'nfree' describes the number of concurrent IOs an arena can process
158 concurrently, 'nlanes' is the number of IOs the BTT device as a whole can
159 process::
160
161 nlanes = min(nfree, num_cpus)
162
163 A lane number is obtained at the start of any IO, and is used for indexing into
164 all the on-disk and in-memory data structures for the duration of the IO. If
165 there are more CPUs than the max number of available lanes, than lanes are
166 protected by spinlocks.
167
168
169 d. In-memory data structure: Read Tracking Table (RTT)
170 ------------------------------------------------------
171
172 Consider a case where we have two threads, one doing reads and the other,
173 writes. We can hit a condition where the writer thread grabs a free block to do
174 a new IO, but the (slow) reader thread is still reading from it. In other words,
175 the reader consulted a map entry, and started reading the corresponding block. A
176 writer started writing to the same external LBA, and finished the write updating
177 the map for that external LBA to point to its new postmap ABA. At this point the
178 internal, postmap block that the reader is (still) reading has been inserted
179 into the list of free blocks. If another write comes in for the same LBA, it can
180 grab this free block, and start writing to it, causing the reader to read
181 incorrect data. To prevent this, we introduce the RTT.
182
183 The RTT is a simple, per arena table with 'nfree' entries. Every reader inserts
184 into rtt[lane_number], the postmap ABA it is reading, and clears it after the
185 read is complete. Every writer thread, after grabbing a free block, checks the
186 RTT for its presence. If the postmap free block is in the RTT, it waits till the
187 reader clears the RTT entry, and only then starts writing to it.
188
189
190 e. In-memory data structure: map locks
191 --------------------------------------
192
193 Consider a case where two writer threads are writing to the same LBA. There can
194 be a race in the following sequence of steps::
195
196 free[lane] = map[premap_aba]
197 map[premap_aba] = postmap_aba
198
199 Both threads can update their respective free[lane] with the same old, freed
200 postmap_aba. This has made the layout inconsistent by losing a free entry, and
201 at the same time, duplicating another free entry for two lanes.
202
203 To solve this, we could have a single map lock (per arena) that has to be taken
204 before performing the above sequence, but we feel that could be too contentious.
205 Instead we use an array of (nfree) map_locks that is indexed by
206 (premap_aba modulo nfree).
207
208
209 f. Reconstruction from the Flog
210 -------------------------------
211
212 On startup, we analyze the BTT flog to create our list of free blocks. We walk
213 through all the entries, and for each lane, of the set of two possible
214 'sections', we always look at the most recent one only (based on the sequence
215 number). The reconstruction rules/steps are simple:
216
217 - Read map[log_entry.lba].
218 - If log_entry.new matches the map entry, then log_entry.old is free.
219 - If log_entry.new does not match the map entry, then log_entry.new is free.
220 (This case can only be caused by power-fails/unsafe shutdowns)
221
222
223 g. Summarizing - Read and Write flows
224 -------------------------------------
225
226 Read:
227
228 1. Convert external LBA to arena number + pre-map ABA
229 2. Get a lane (and take lane_lock)
230 3. Read map to get the entry for this pre-map ABA
231 4. Enter post-map ABA into RTT[lane]
232 5. If TRIM flag set in map, return zeroes, and end IO (go to step 8)
233 6. If ERROR flag set in map, end IO with EIO (go to step 8)
234 7. Read data from this block
235 8. Remove post-map ABA entry from RTT[lane]
236 9. Release lane (and lane_lock)
237
238 Write:
239
240 1. Convert external LBA to Arena number + pre-map ABA
241 2. Get a lane (and take lane_lock)
242 3. Use lane to index into in-memory free list and obtain a new block, next flog
243 index, next sequence number
244 4. Scan the RTT to check if free block is present, and spin/wait if it is.
245 5. Write data to this free block
246 6. Read map to get the existing post-map ABA entry for this pre-map ABA
247 7. Write flog entry: [premap_aba / old postmap_aba / new postmap_aba / seq_num]
248 8. Write new post-map ABA into map.
249 9. Write old post-map entry into the free list
250 10. Calculate next sequence number and write into the free list entry
251 11. Release lane (and lane_lock)
252
253
254 4. Error Handling
255 =================
256
257 An arena would be in an error state if any of the metadata is corrupted
258 irrecoverably, either due to a bug or a media error. The following conditions
259 indicate an error:
260
261 - Info block checksum does not match (and recovering from the copy also fails)
262 - All internal available blocks are not uniquely and entirely addressed by the
263 sum of mapped blocks and free blocks (from the BTT flog).
264 - Rebuilding free list from the flog reveals missing/duplicate/impossible
265 entries
266 - A map entry is out of bounds
267
268 If any of these error conditions are encountered, the arena is put into a read
269 only state using a flag in the info block.
270
271
272 5. Usage
273 ========
274
275 The BTT can be set up on any disk (namespace) exposed by the libnvdimm subsystem
276 (pmem, or blk mode). The easiest way to set up such a namespace is using the
277 'ndctl' utility [1]:
278
279 For example, the ndctl command line to setup a btt with a 4k sector size is::
280
281 ndctl create-namespace -f -e namespace0.0 -m sector -l 4k
282
283 See ndctl create-namespace --help for more options.
284
285 [1]: https://github.com/pmem/ndctl
286

3. 한국어 전문 번역

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

BTT의 목적과 원자적 섹터 갱신

1-25

영구 메모리 기반 저장 장치는 바이트 단위, 더 정확히는 캐시 라인 단위로 I/O를 수행할 수 있지만, 흔히 이를 전통적인 블록 장치로 노출해야 합니다. 영구 메모리 블록 드라이버가 이 역할을 하지만 자체적으로 원자성을 보장하지는 않습니다.

전통적인 SSD는 보통 커패시터에 저장된 에너지로 진행 중인 블록 쓰기를 마치거나 펌웨어에서 처리해 torn sector를 방지합니다. 영구 메모리에서는 쓰기 도중 전원이 끊기면 블록에 이전 데이터와 새 데이터가 섞일 수 있고, 응용 프로그램은 이런 상태를 처리하지 못할 수 있습니다.

Block Translation Table, BTT는 영구 메모리 장치에 원자적 섹터 갱신 의미론을 제공합니다. 섹터 쓰기가 찢어지지 않는다고 가정하는 응용 프로그램을 그대로 지원하기 위해 stacked block device로 나타나며, 하위 저장 공간 일부를 메타데이터용으로 예약합니다.

핵심은 볼륨의 모든 블록을 다시 매핑하는 indirection table입니다. BTT는 원자적 섹터 갱신 기능만 제공하는 매우 단순한 파일 시스템으로 생각할 수 있습니다.

BTT가 보호하는 쓰기 경로
Application sector writeBTT stacked block deviceIndirection tablePersistent-memory block
Power failure during writeOld or new sector mappingNo torn sector exposed

블록 계층과 영구 메모리 사이에서 간접 매핑을 사용해 섹터 단위 원자성을 제공합니다.

=============================
BTT - Block Translation Table
=============================


1. Introduction
===============

Persistent memory based storage is able to perform IO at byte (or more
accurately, cache line) granularity. However, we often want to expose such
storage as traditional block devices. The block drivers for persistent memory
will do exactly this. However, they do not provide any atomicity guarantees.
Traditional SSDs typically provide protection against torn sectors in hardware,
using stored energy in capacitors to complete in-flight block writes, or perhaps
in firmware. We don't have this luxury with persistent memory - if a write is in
progress, and we experience a power failure, the block will contain a mix of old
and new data. Applications may not be prepared to handle such a scenario.

The Block Translation Table (BTT) provides atomic sector update semantics for
persistent memory devices, so that applications that rely on sector writes not
being torn can continue to do so. The BTT manifests itself as a stacked block
device, and reserves a portion of the underlying storage for its metadata. At
the heart of it, is an indirection table that re-maps all the blocks on the
volume. It can be thought of as an extremely simple file system that only
provides atomic sector updates.

Arena와 정적 저장 배치

26-70

BTT를 배치할 수 있는 하위 저장 장치 자체에는 별도 제한이 없습니다. 다만 BTT는 사용 가능한 공간을 최대 512 GiB 크기의 청크인 Arena로 나눕니다.

모든 Arena는 같은 메타데이터 배치를 따르며, 다음 Arena를 가리키는 한 필드를 제외하면 Arena 안의 모든 참조는 그 Arena 내부에서 끝납니다.

Backing Store에는 Arena 0, Arena 1과 이후 Arena가 연속해 놓입니다. 각 Arena 내부에는 선두의 4K Arena info block, 실제 Data Blocks, BTT Map, BTT Flog, 그리고 끝의 4K info block 복사본이 차례로 배치됩니다.

BTT on-disk 배치
Backing StoreArena 0, up to 512 GiBArena 1, up to 512 GiBAdditional Arenas
Each ArenaArena info block, 4KData BlocksBTT MapBTT FlogInfo block copy, 4K

원문의 ASCII 그림을 backing store와 Arena 내부 순서로 구조화했습니다.



2. Static Layout
================

The underlying storage on which a BTT can be laid out is not limited in any way.
The BTT, however, splits the available space into chunks of up to 512 GiB,
called "Arenas".

Each arena follows the same layout for its metadata, and all references in an
arena are internal to it (with the exception of one field that points to the
next arena). The following depicts the "On-disk" metadata layout::


    Backing Store     +------->  Arena
  +---------------+   |   +------------------+
  |               |   |   | Arena info block |
  |    Arena 0    +---+   |       4K         |
  |     512G      |       +------------------+
  |               |       |                  |
  +---------------+       |                  |
  |               |       |                  |
  |    Arena 1    |       |   Data Blocks    |
  |     512G      |       |                  |
  |               |       |                  |
  +---------------+       |                  |
  |       .       |       |                  |
  |       .       |       |                  |
  |       .       |       |                  |
  |               |       |                  |
  |               |       |                  |
  +---------------+       +------------------+
                          |                  |
                          |     BTT Map      |
                          |                  |
                          |                  |
                          +------------------+
                          |                  |
                          |     BTT Flog     |
                          |                  |
                          +------------------+
                          | Info block copy  |
                          |       4K         |
                          +------------------+

BTT Map과 주소 용어

71-120

BTT Map은 외부 LBA를 내부 블록으로 연결하는 단순 lookup/indirection table입니다. 각 map entry는 32비트이며, 최상위 2비트는 Error와 Zero 플래그이고 나머지 30비트는 내부 postmap block 번호입니다.

플래그가 `00`이면 초기 상태로 읽기는 0을 반환하고 Premap과 Postmap이 같습니다. `01`은 Zero 상태로 읽기가 0을 반환합니다. `10`은 Error 상태로 읽기가 실패하며 쓰기가 `E` 비트를 지웁니다. `11`은 유효한 postmap을 가진 정상 블록입니다.

External LBA는 상위 계층에 보이는 LBA입니다. ABA는 Arena Block Address, 즉 Arena 안의 블록 오프셋 또는 번호입니다. Premap ABA는 External LBA 범위를 검사해 정한 Arena 내부 오프셋이고, Postmap ABA는 map 간접 참조 뒤 Data Blocks 영역에서 얻은 블록 번호입니다.

`nfree`는 어느 시점이든 유지하는 free block 수이며, 동시에 해당 Arena에 실행할 수 있는 쓰기 수이기도 합니다.

예를 들어 BTT가 1,024G 디스크를 노출하고 768G의 External LBA를 읽으면 두 번째 512G Arena 안의 256G 지점이므로 Premap ABA는 256G입니다. map에서 이 블록 X가 블록 Y, 예를 들어 64를 가리키면 Postmap ABA는 64입니다.

BTT Map 해석
항목의미
Bits 31-30 = `00`Initial, read zero, Premap = Postmap
Bits 31-30 = `01`Zero state, read zero
Bits 31-30 = `10`Error state, read fails, write clears E
Bits 31-30 = `11`Normal block with valid Postmap
Bits 29-0Internal Postmap block number
External LBA상위 계층에 노출한 LBA
Premap ABA범위 검사로 얻은 Arena 내부 위치
Postmap ABAMap 간접 참조 뒤의 Data Blocks 위치
`nfree`Free block 수이자 Arena 동시 쓰기 수


3. Theory of Operation
======================


a. The BTT Map
--------------

The map is a simple lookup/indirection table that maps an LBA to an internal
block. Each map entry is 32 bits. The two most significant bits are special
flags, and the remaining form the internal block number.

======== =============================================================
Bit      Description
======== =============================================================
31 - 30         Error and Zero flags - Used in the following way::

           == ==  ====================================================
           31 30  Description
           == ==  ====================================================
           0  0          Initial state. Reads return zeroes; Premap = Postmap
           0  1          Zero state: Reads return zeroes
           1  0          Error state: Reads fail; Writes clear 'E' bit
           1  1          Normal Block – has valid postmap
           == ==  ====================================================

29 - 0         Mappings to internal 'postmap' blocks
======== =============================================================


Some of the terminology that will be subsequently used:

============        ================================================================
External LBA        LBA as made visible to upper layers.
ABA                Arena Block Address - Block offset/number within an arena
Premap ABA        The block offset into an arena, which was decided upon by range
                checking the External LBA
Postmap ABA        The block number in the "Data Blocks" area obtained after
                indirection from the map
nfree                The number of free blocks that are maintained at any given time.
                This is the number of concurrent writes that can happen to the
                arena.
============        ================================================================


For example, after adding a BTT, we surface a disk of 1024G. We get a read for
the external LBA at 768G. This falls into the second arena, and of the 512G
worth of blocks that this arena contributes, this block is at 256G. Thus, the
premap ABA is 256G. We now refer to the map, and find out the mapping for block
'X' (256G) points to block 'Y', say '64'. Thus the postmap ABA is 64.

BTT Flog와 allocating write

121-152

BTT는 모든 쓰기를 free block에 수행하는 allocating write로 만들어 섹터 원자성을 제공합니다. Free block의 실행 목록은 BTT flog로 관리하며, `flog`라는 이름은 free list와 log를 합친 것입니다.

Flog에는 `nfree`개의 entry가 있습니다. `lba`는 쓰는 Premap ABA, `old_map`은 쓰기가 끝나면 free가 될 기존 Postmap ABA, `new_map`은 map에 반영할 새 Postmap ABA입니다. 복구가 필요할 때를 위해 새 `lba -> postmap_aba` 관계를 flog에도 남깁니다.

`seq`는 flog entry의 두 section 중 어느 쪽이 최신이고 유효한지 표시합니다. 정상 동작 중 이진수 `01 -> 10 -> 11 -> 01`로 순환하며 `00`은 초기화되지 않은 상태입니다. 프라임이 붙은 `lba'`, `old_map'`, `new_map'`, `seq'`는 대체 section입니다.

모든 필드는 32비트이므로 한 entry의 실제 필드 크기는 32바이트이며, 캐시 라인 공유나 aliasing을 피하려고 64바이트로 padding합니다. 갱신할 때 sequence number로 오래된 section을 골라 덮어쓰고, 새 section에서는 sequence number를 마지막에 기록합니다.

Flog entry
필드역할
`lba`쓰는 Premap ABA
`old_map`완료 뒤 free가 될 기존 Postmap ABA
`new_map`새 Postmap ABA와 복구 정보
`seq`두 section 중 최신 쪽 표시
Primed fields대체 lba, old_map, new_map, sequence
크기32-byte fields, padded to 64 bytes
갱신 순서Old section overwrite, sequence number last



b. The BTT Flog
---------------

The BTT provides sector atomicity by making every write an "allocating write",
i.e. Every write goes to a "free" block. A running list of free blocks is
maintained in the form of the BTT flog. 'Flog' is a combination of the words
"free list" and "log". The flog contains 'nfree' entries, and an entry contains:

========  =====================================================================
lba       The premap ABA that is being written to
old_map   The old postmap ABA - after 'this' write completes, this will be a
          free block.
new_map   The new postmap ABA. The map will up updated to reflect this
          lba->postmap_aba mapping, but we log it here in case we have to
          recover.
seq          Sequence number to mark which of the 2 sections of this flog entry is
          valid/newest. It cycles between 01->10->11->01 (binary) under normal
          operation, with 00 indicating an uninitialized state.
lba'          alternate lba entry
old_map'  alternate old postmap entry
new_map'  alternate new postmap entry
seq'          alternate sequence number.
========  =====================================================================

Each of the above fields is 32-bit, making one entry 32 bytes. Entries are also
padded to 64 bytes to avoid cache line sharing or aliasing. Flog updates are
done such that for any entry being written, it:
a. overwrites the 'old' section in the entry based on sequence numbers
b. writes the 'new' section such that the sequence number is written last.

Lane과 Read Tracking Table

153-187

`nfree`가 한 Arena에서 동시에 처리할 수 있는 I/O 수라면, `nlanes`는 BTT 장치 전체가 동시에 처리할 수 있는 I/O 수입니다. 값은 `min(nfree, num_cpus)`입니다.

모든 I/O는 시작할 때 lane 번호를 얻고, I/O가 끝날 때까지 온디스크와 메모리 내 자료 구조를 색인하는 데 그 번호를 사용합니다. CPU 수가 사용 가능한 최대 lane 수보다 많으면 spinlock으로 lane을 보호합니다.

RTT가 없으면 느린 reader가 map에서 얻은 기존 Postmap block을 읽는 동안 writer가 같은 External LBA에 새 쓰기를 완료해 map을 새 Postmap ABA로 바꿀 수 있습니다. 그러면 reader가 아직 읽는 이전 블록이 free list에 들어가고, 같은 LBA의 다음 writer가 그 블록을 골라 덮어써 reader가 잘못된 데이터를 읽게 됩니다.

이를 막는 Read Tracking Table, RTT는 Arena마다 `nfree`개의 entry를 둡니다. Reader는 읽는 Postmap ABA를 `rtt[lane_number]`에 넣고 읽기가 끝나면 지웁니다. Writer는 free block을 얻은 뒤 RTT에 그 블록이 있는지 확인하고, 있다면 reader가 entry를 지울 때까지 기다린 뒤 쓰기를 시작합니다.

RTT가 막는 read/write 경합
Reader maps LBA to old PostmapStore old Postmap in RTT[lane]Read dataClear RTT[lane]
Writer obtains free blockScan RTTWait while block is presentWrite after reader clears entry

Reader가 참조 중인 이전 Postmap block을 새 writer가 재사용하지 못하게 합니다.


c. The concept of lanes
-----------------------

While 'nfree' describes the number of concurrent IOs an arena can process
concurrently, 'nlanes' is the number of IOs the BTT device as a whole can
process::

        nlanes = min(nfree, num_cpus)

A lane number is obtained at the start of any IO, and is used for indexing into
all the on-disk and in-memory data structures for the duration of the IO. If
there are more CPUs than the max number of available lanes, than lanes are
protected by spinlocks.


d. In-memory data structure: Read Tracking Table (RTT)
------------------------------------------------------

Consider a case where we have two threads, one doing reads and the other,
writes. We can hit a condition where the writer thread grabs a free block to do
a new IO, but the (slow) reader thread is still reading from it. In other words,
the reader consulted a map entry, and started reading the corresponding block. A
writer started writing to the same external LBA, and finished the write updating
the map for that external LBA to point to its new postmap ABA. At this point the
internal, postmap block that the reader is (still) reading has been inserted
into the list of free blocks. If another write comes in for the same LBA, it can
grab this free block, and start writing to it, causing the reader to read
incorrect data. To prevent this, we introduce the RTT.

The RTT is a simple, per arena table with 'nfree' entries. Every reader inserts
into rtt[lane_number], the postmap ABA it is reading, and clears it after the
read is complete. Every writer thread, after grabbing a free block, checks the
RTT for its presence. If the postmap free block is in the RTT, it waits till the
reader clears the RTT entry, and only then starts writing to it.

Map lock과 Flog 복구

188-220

두 writer가 같은 LBA에 동시에 쓰면 `free[lane] = map[premap_aba]`와 `map[premap_aba] = postmap_aba` 사이에 경합이 생길 수 있습니다. 두 thread가 각자의 `free[lane]`에 같은 이전 Postmap ABA를 넣으면 free entry 하나는 사라지고 다른 하나는 두 lane에 중복되어 배치가 일관성을 잃습니다.

Arena마다 map lock 하나를 두면 이 순서를 보호할 수 있지만 경합이 지나치게 클 수 있습니다. BTT는 대신 `nfree`개의 `map_locks` 배열을 만들고 `premap_aba modulo nfree`로 색인합니다.

시작할 때 BTT flog를 분석해 free block 목록을 다시 만듭니다. 각 lane에서 가능한 두 section 중 sequence number가 나타내는 가장 최신 section만 봅니다.

복구 규칙은 단순합니다. `map[log_entry.lba]`를 읽고 `log_entry.new`가 map entry와 같으면 `log_entry.old`가 free입니다. 다르면 `log_entry.new`가 free입니다. 두 번째 경우는 전원 장애나 안전하지 않은 종료 때문에만 생길 수 있습니다.

Flog 기반 free-list 복구
Select newest section by seqRead map[log_entry.lba]new == mapold is free
Select newest section by seqRead map[log_entry.lba]new != mapnew is free after unsafe shutdown

최신 flog section과 현재 map의 일치 여부로 free block을 결정합니다.



e. In-memory data structure: map locks
--------------------------------------

Consider a case where two writer threads are writing to the same LBA. There can
be a race in the following sequence of steps::

        free[lane] = map[premap_aba]
        map[premap_aba] = postmap_aba

Both threads can update their respective free[lane] with the same old, freed
postmap_aba. This has made the layout inconsistent by losing a free entry, and
at the same time, duplicating another free entry for two lanes.

To solve this, we could have a single map lock (per arena) that has to be taken
before performing the above sequence, but we feel that could be too contentious.
Instead we use an array of (nfree) map_locks that is indexed by
(premap_aba modulo nfree).


f. Reconstruction from the Flog
-------------------------------

On startup, we analyze the BTT flog to create our list of free blocks. We walk
through all the entries, and for each lane, of the set of two possible
'sections', we always look at the most recent one only (based on the sequence
number). The reconstruction rules/steps are simple:

- Read map[log_entry.lba].
- If log_entry.new matches the map entry, then log_entry.old is free.
- If log_entry.new does not match the map entry, then log_entry.new is free.
  (This case can only be caused by power-fails/unsafe shutdowns)

Read와 Write 절차

221-251

읽기는 External LBA를 Arena 번호와 Premap ABA로 변환하고 lane과 lane lock을 얻는 것으로 시작합니다. Map entry를 읽어 Postmap ABA를 얻고 이를 `RTT[lane]`에 넣습니다.

Map의 TRIM 플래그가 설정되어 있으면 0을 반환하고, ERROR 플래그가 있으면 `EIO`로 끝냅니다. 그렇지 않으면 해당 블록의 데이터를 읽습니다. 마지막에는 `RTT[lane]`에서 Postmap ABA를 지우고 lane과 lock을 반환합니다.

쓰기도 External LBA를 Arena와 Premap ABA로 변환하고 lane을 얻습니다. Lane으로 메모리 내 free list를 색인해 새 블록, 다음 flog index, 다음 sequence number를 구합니다.

선택한 free block이 RTT에 있는지 검사해 있으면 기다리고, 비워지면 그 블록에 데이터를 씁니다. Map에서 기존 Postmap ABA를 읽고 `[premap_aba / old postmap_aba / new postmap_aba / seq_num]` flog entry를 기록합니다.

그 뒤 새 Postmap ABA를 map에 기록하고, 이전 Postmap entry를 free list에 넣습니다. 다음 sequence number를 계산해 free-list entry에 기록한 뒤 lane과 lane lock을 반환합니다.

BTT I/O 순서
ReadWrite
1. LBA -> Arena + Premap1. LBA -> Arena + Premap
2. Lane과 lock 획득2. Lane과 lock 획득
3. Map -> Postmap3. Free block, flog index, seq 획득
4. RTT[lane] 등록4. RTT 검사와 대기
5. TRIM이면 zero5. Free block에 data write
6. ERROR이면 EIO6. 기존 Postmap 읽기
7. Data read7. Flog entry 기록
8. RTT entry 제거8. Map을 새 Postmap으로 갱신
9. Lane 반환9-10. 이전 block과 다음 seq를 free list에 기록
11. Lane 반환



g. Summarizing - Read and Write flows
-------------------------------------

Read:

1.  Convert external LBA to arena number + pre-map ABA
2.  Get a lane (and take lane_lock)
3.  Read map to get the entry for this pre-map ABA
4.  Enter post-map ABA into RTT[lane]
5.  If TRIM flag set in map, return zeroes, and end IO (go to step 8)
6.  If ERROR flag set in map, end IO with EIO (go to step 8)
7.  Read data from this block
8.  Remove post-map ABA entry from RTT[lane]
9.  Release lane (and lane_lock)

Write:

1.  Convert external LBA to Arena number + pre-map ABA
2.  Get a lane (and take lane_lock)
3.  Use lane to index into in-memory free list and obtain a new block, next flog
    index, next sequence number
4.  Scan the RTT to check if free block is present, and spin/wait if it is.
5.  Write data to this free block
6.  Read map to get the existing post-map ABA entry for this pre-map ABA
7.  Write flog entry: [premap_aba / old postmap_aba / new postmap_aba / seq_num]
8.  Write new post-map ABA into map.
9.  Write old post-map entry into the free list
10. Calculate next sequence number and write into the free list entry
11. Release lane (and lane_lock)

Arena 오류 판정과 read-only 전환

252-271

버그나 미디어 오류로 메타데이터가 복구 불가능하게 손상되면 Arena는 오류 상태가 됩니다.

오류 조건에는 info block checksum 불일치와 복사본을 통한 복구 실패, map된 블록과 BTT flog에서 얻은 free block의 합이 모든 내부 가용 블록을 유일하고 완전하게 가리키지 못하는 경우가 포함됩니다.

또한 flog로 free list를 재구성할 때 누락·중복·불가능한 entry가 나오거나 map entry가 범위를 벗어나면 오류입니다.

이 조건 중 하나라도 발견하면 info block의 플래그를 사용해 해당 Arena를 read-only 상태로 전환합니다.

Arena 오류 조건
검사오류 판정
Info block checksum복사본 복구까지 실패
Mapped + free blocks내부 블록을 유일하고 완전하게 덮지 못함
Flog reconstructionMissing, duplicate, impossible entry
Map entryOut of bounds
결과Arena read-only flag 설정



4. Error Handling
=================

An arena would be in an error state if any of the metadata is corrupted
irrecoverably, either due to a bug or a media error. The following conditions
indicate an error:

- Info block checksum does not match (and recovering from the copy also fails)
- All internal available blocks are not uniquely and entirely addressed by the
  sum of mapped blocks and free blocks (from the BTT flog).
- Rebuilding free list from the flog reveals missing/duplicate/impossible
  entries
- A map entry is out of bounds

If any of these error conditions are encountered, the arena is put into a read
only state using a flag in the info block.

ndctl로 BTT namespace 생성

272-285

BTT는 libnvdimm subsystem이 노출하는 어떤 디스크 namespace에도 설정할 수 있으며, `pmem` 모드와 `blk` 모드를 모두 지원합니다.

가장 쉬운 설정 방법은 `ndctl` 유틸리티를 사용하는 것입니다. 예제 명령은 `namespace0.0`에 4K sector size의 BTT namespace를 강제로 생성하며 mode를 `sector`로 지정합니다.

추가 선택지는 `ndctl create-namespace --help`에서 확인할 수 있고, 문서는 pmem의 `ndctl` GitHub 저장소를 참조합니다.

4K BTT namespace 생성
`namespace0.0``ndctl create-namespace -f -e namespace0.0 -m sector -l 4k`4K-sector BTT block device

`ndctl`이 기존 namespace를 sector mode의 BTT로 구성합니다.

5. Usage
========

The BTT can be set up on any disk (namespace) exposed by the libnvdimm subsystem
(pmem, or blk mode). The easiest way to set up such a namespace is using the
'ndctl' utility [1]:

For example, the ndctl command line to setup a btt with a 4k sector size is::

    ndctl create-namespace -f -e namespace0.0 -m sector -l 4k

See ndctl create-namespace --help for more options.

[1]: https://github.com/pmem/ndctl