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

Linux 6.18.37 · Core API

DMA and swiotlb

DMA bounce buffering을 제공하는 swiotlb의 사용 시나리오, 크기와 alignment 제약, pool·area·slot 구조, dynamic 및 restricted pool을 설명합니다.

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

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

1. 요약·해설

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

요약과 해설

swiotlb.rst:1-321

`swiotlb`는 device가 원래 memory에 직접 DMA할 수 없을 때 제약에 맞는 bounce buffer를 제공하고 CPU copy로 원래 buffer와 data를 동기화합니다. 32-bit DMA device, CoCo VM, untrusted IOMMU mapping이 대표 사용 사례입니다.

Map, unmap, sync path는 block할 수 없으므로 기본 pool을 boot 때 연속 memory로 미리 할당합니다. 현재 단일 mapping은 보통 256 KiB로 제한되고 `min_align_mask`와 `alloc_align_mask`가 실제 최대 크기와 padding을 좌우합니다.

Pool은 area와 2 KiB slot으로 나뉘며 per-area lock으로 병렬성을 확보합니다. Dynamic pool은 부족한 공간을 보완하지만 fragmentation과 linear search 비용이 있고, restricted pool은 특정 device에 격리된 DMA memory를 제공합니다.

2. 영어 원문 전체

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

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 ===============
4 DMA and swiotlb
5 ===============
6
7 swiotlb is a memory buffer allocator used by the Linux kernel DMA layer. It is
8 typically used when a device doing DMA can't directly access the target memory
9 buffer because of hardware limitations or other requirements. In such a case,
10 the DMA layer calls swiotlb to allocate a temporary memory buffer that conforms
11 to the limitations. The DMA is done to/from this temporary memory buffer, and
12 the CPU copies the data between the temporary buffer and the original target
13 memory buffer. This approach is generically called "bounce buffering", and the
14 temporary memory buffer is called a "bounce buffer".
15
16 Device drivers don't interact directly with swiotlb. Instead, drivers inform
17 the DMA layer of the DMA attributes of the devices they are managing, and use
18 the normal DMA map, unmap, and sync APIs when programming a device to do DMA.
19 These APIs use the device DMA attributes and kernel-wide settings to determine
20 if bounce buffering is necessary. If so, the DMA layer manages the allocation,
21 freeing, and sync'ing of bounce buffers. Since the DMA attributes are per
22 device, some devices in a system may use bounce buffering while others do not.
23
24 Because the CPU copies data between the bounce buffer and the original target
25 memory buffer, doing bounce buffering is slower than doing DMA directly to the
26 original memory buffer, and it consumes more CPU resources. So it is used only
27 when necessary for providing DMA functionality.
28
29 Usage Scenarios
30 ---------------
31 swiotlb was originally created to handle DMA for devices with addressing
32 limitations. As physical memory sizes grew beyond 4 GiB, some devices could
33 only provide 32-bit DMA addresses. By allocating bounce buffer memory below
34 the 4 GiB line, these devices with addressing limitations could still work and
35 do DMA.
36
37 More recently, Confidential Computing (CoCo) VMs have the guest VM's memory
38 encrypted by default, and the memory is not accessible by the host hypervisor
39 and VMM. For the host to do I/O on behalf of the guest, the I/O must be
40 directed to guest memory that is unencrypted. CoCo VMs set a kernel-wide option
41 to force all DMA I/O to use bounce buffers, and the bounce buffer memory is set
42 up as unencrypted. The host does DMA I/O to/from the bounce buffer memory, and
43 the Linux kernel DMA layer does "sync" operations to cause the CPU to copy the
44 data to/from the original target memory buffer. The CPU copying bridges between
45 the unencrypted and the encrypted memory. This use of bounce buffers allows
46 device drivers to "just work" in a CoCo VM, with no modifications
47 needed to handle the memory encryption complexity.
48
49 Other edge case scenarios arise for bounce buffers. For example, when IOMMU
50 mappings are set up for a DMA operation to/from a device that is considered
51 "untrusted", the device should be given access only to the memory containing
52 the data being transferred. But if that memory occupies only part of an IOMMU
53 granule, other parts of the granule may contain unrelated kernel data. Since
54 IOMMU access control is per-granule, the untrusted device can gain access to
55 the unrelated kernel data. This problem is solved by bounce buffering the DMA
56 operation and ensuring that unused portions of the bounce buffers do not
57 contain any unrelated kernel data.
58
59 Core Functionality
60 ------------------
61 The primary swiotlb APIs are swiotlb_tbl_map_single() and
62 swiotlb_tbl_unmap_single(). The "map" API allocates a bounce buffer of a
63 specified size in bytes and returns the physical address of the buffer. The
64 buffer memory is physically contiguous. The expectation is that the DMA layer
65 maps the physical memory address to a DMA address, and returns the DMA address
66 to the driver for programming into the device. If a DMA operation specifies
67 multiple memory buffer segments, a separate bounce buffer must be allocated for
68 each segment. swiotlb_tbl_map_single() always does a "sync" operation (i.e., a
69 CPU copy) to initialize the bounce buffer to match the contents of the original
70 buffer.
71
72 swiotlb_tbl_unmap_single() does the reverse. If the DMA operation might have
73 updated the bounce buffer memory and DMA_ATTR_SKIP_CPU_SYNC is not set, the
74 unmap does a "sync" operation to cause a CPU copy of the data from the bounce
75 buffer back to the original buffer. Then the bounce buffer memory is freed.
76
77 swiotlb also provides "sync" APIs that correspond to the dma_sync_*() APIs that
78 a driver may use when control of a buffer transitions between the CPU and the
79 device. The swiotlb "sync" APIs cause a CPU copy of the data between the
80 original buffer and the bounce buffer. Like the dma_sync_*() APIs, the swiotlb
81 "sync" APIs support doing a partial sync, where only a subset of the bounce
82 buffer is copied to/from the original buffer.
83
84 Core Functionality Constraints
85 ------------------------------
86 The swiotlb map/unmap/sync APIs must operate without blocking, as they are
87 called by the corresponding DMA APIs which may run in contexts that cannot
88 block. Hence the default memory pool for swiotlb allocations must be
89 pre-allocated at boot time (but see Dynamic swiotlb below). Because swiotlb
90 allocations must be physically contiguous, the entire default memory pool is
91 allocated as a single contiguous block.
92
93 The need to pre-allocate the default swiotlb pool creates a boot-time tradeoff.
94 The pool should be large enough to ensure that bounce buffer requests can
95 always be satisfied, as the non-blocking requirement means requests can't wait
96 for space to become available. But a large pool potentially wastes memory, as
97 this pre-allocated memory is not available for other uses in the system. The
98 tradeoff is particularly acute in CoCo VMs that use bounce buffers for all DMA
99 I/O. These VMs use a heuristic to set the default pool size to ~6% of memory,
100 with a max of 1 GiB, which has the potential to be very wasteful of memory.
101 Conversely, the heuristic might produce a size that is insufficient, depending
102 on the I/O patterns of the workload in the VM. The dynamic swiotlb feature
103 described below can help, but has limitations. Better management of the swiotlb
104 default memory pool size remains an open issue.
105
106 A single allocation from swiotlb is limited to IO_TLB_SIZE * IO_TLB_SEGSIZE
107 bytes, which is 256 KiB with current definitions. When a device's DMA settings
108 are such that the device might use swiotlb, the maximum size of a DMA segment
109 must be limited to that 256 KiB. This value is communicated to higher-level
110 kernel code via dma_map_mapping_size() and swiotlb_max_mapping_size(). If the
111 higher-level code fails to account for this limit, it may make requests that
112 are too large for swiotlb, and get a "swiotlb full" error.
113
114 A key device DMA setting is "min_align_mask", which is a power of 2 minus 1
115 so that some number of low order bits are set, or it may be zero. swiotlb
116 allocations ensure these min_align_mask bits of the physical address of the
117 bounce buffer match the same bits in the address of the original buffer. When
118 min_align_mask is non-zero, it may produce an "alignment offset" in the address
119 of the bounce buffer that slightly reduces the maximum size of an allocation.
120 This potential alignment offset is reflected in the value returned by
121 swiotlb_max_mapping_size(), which can show up in places like
122 /sys/block/<device>/queue/max_sectors_kb. For example, if a device does not use
123 swiotlb, max_sectors_kb might be 512 KiB or larger. If a device might use
124 swiotlb, max_sectors_kb will be 256 KiB. When min_align_mask is non-zero,
125 max_sectors_kb might be even smaller, such as 252 KiB.
126
127 swiotlb_tbl_map_single() also takes an "alloc_align_mask" parameter. This
128 parameter specifies the allocation of bounce buffer space must start at a
129 physical address with the alloc_align_mask bits set to zero. But the actual
130 bounce buffer might start at a larger address if min_align_mask is non-zero.
131 Hence there may be pre-padding space that is allocated prior to the start of
132 the bounce buffer. Similarly, the end of the bounce buffer is rounded up to an
133 alloc_align_mask boundary, potentially resulting in post-padding space. Any
134 pre-padding or post-padding space is not initialized by swiotlb code. The
135 "alloc_align_mask" parameter is used by IOMMU code when mapping for untrusted
136 devices. It is set to the granule size - 1 so that the bounce buffer is
137 allocated entirely from granules that are not used for any other purpose.
138
139 Data structures concepts
140 ------------------------
141 Memory used for swiotlb bounce buffers is allocated from overall system memory
142 as one or more "pools". The default pool is allocated during system boot with a
143 default size of 64 MiB. The default pool size may be modified with the
144 "swiotlb=" kernel boot line parameter. The default size may also be adjusted
145 due to other conditions, such as running in a CoCo VM, as described above. If
146 CONFIG_SWIOTLB_DYNAMIC is enabled, additional pools may be allocated later in
147 the life of the system. Each pool must be a contiguous range of physical
148 memory. The default pool is allocated below the 4 GiB physical address line so
149 it works for devices that can only address 32-bits of physical memory (unless
150 architecture-specific code provides the SWIOTLB_ANY flag). In a CoCo VM, the
151 pool memory must be decrypted before swiotlb is used.
152
153 Each pool is divided into "slots" of size IO_TLB_SIZE, which is 2 KiB with
154 current definitions. IO_TLB_SEGSIZE contiguous slots (128 slots) constitute
155 what might be called a "slot set". When a bounce buffer is allocated, it
156 occupies one or more contiguous slots. A slot is never shared by multiple
157 bounce buffers. Furthermore, a bounce buffer must be allocated from a single
158 slot set, which leads to the maximum bounce buffer size being IO_TLB_SIZE *
159 IO_TLB_SEGSIZE. Multiple smaller bounce buffers may co-exist in a single slot
160 set if the alignment and size constraints can be met.
161
162 Slots are also grouped into "areas", with the constraint that a slot set exists
163 entirely in a single area. Each area has its own spin lock that must be held to
164 manipulate the slots in that area. The division into areas avoids contending
165 for a single global spin lock when swiotlb is heavily used, such as in a CoCo
166 VM. The number of areas defaults to the number of CPUs in the system for
167 maximum parallelism, but since an area can't be smaller than IO_TLB_SEGSIZE
168 slots, it might be necessary to assign multiple CPUs to the same area. The
169 number of areas can also be set via the "swiotlb=" kernel boot parameter.
170
171 When allocating a bounce buffer, if the area associated with the calling CPU
172 does not have enough free space, areas associated with other CPUs are tried
173 sequentially. For each area tried, the area's spin lock must be obtained before
174 trying an allocation, so contention may occur if swiotlb is relatively busy
175 overall. But an allocation request does not fail unless all areas do not have
176 enough free space.
177
178 IO_TLB_SIZE, IO_TLB_SEGSIZE, and the number of areas must all be powers of 2 as
179 the code uses shifting and bit masking to do many of the calculations. The
180 number of areas is rounded up to a power of 2 if necessary to meet this
181 requirement.
182
183 The default pool is allocated with PAGE_SIZE alignment. If an alloc_align_mask
184 argument to swiotlb_tbl_map_single() specifies a larger alignment, one or more
185 initial slots in each slot set might not meet the alloc_align_mask criterium.
186 Because a bounce buffer allocation can't cross a slot set boundary, eliminating
187 those initial slots effectively reduces the max size of a bounce buffer.
188 Currently, there's no problem because alloc_align_mask is set based on IOMMU
189 granule size, and granules cannot be larger than PAGE_SIZE. But if that were to
190 change in the future, the initial pool allocation might need to be done with
191 alignment larger than PAGE_SIZE.
192
193 Dynamic swiotlb
194 ---------------
195 When CONFIG_SWIOTLB_DYNAMIC is enabled, swiotlb can do on-demand expansion of
196 the amount of memory available for allocation as bounce buffers. If a bounce
197 buffer request fails due to lack of available space, an asynchronous background
198 task is kicked off to allocate memory from general system memory and turn it
199 into an swiotlb pool. Creating an additional pool must be done asynchronously
200 because the memory allocation may block, and as noted above, swiotlb requests
201 are not allowed to block. Once the background task is kicked off, the bounce
202 buffer request creates a "transient pool" to avoid returning an "swiotlb full"
203 error. A transient pool has the size of the bounce buffer request, and is
204 deleted when the bounce buffer is freed. Memory for this transient pool comes
205 from the general system memory atomic pool so that creation does not block.
206 Creating a transient pool has relatively high cost, particularly in a CoCo VM
207 where the memory must be decrypted, so it is done only as a stopgap until the
208 background task can add another non-transient pool.
209
210 Adding a dynamic pool has limitations. Like with the default pool, the memory
211 must be physically contiguous, so the size is limited to MAX_PAGE_ORDER pages
212 (e.g., 4 MiB on a typical x86 system). Due to memory fragmentation, a max size
213 allocation may not be available. The dynamic pool allocator tries smaller sizes
214 until it succeeds, but with a minimum size of 1 MiB. Given sufficient system
215 memory fragmentation, dynamically adding a pool might not succeed at all.
216
217 The number of areas in a dynamic pool may be different from the number of areas
218 in the default pool. Because the new pool size is typically a few MiB at most,
219 the number of areas will likely be smaller. For example, with a new pool size
220 of 4 MiB and the 256 KiB minimum area size, only 16 areas can be created. If
221 the system has more than 16 CPUs, multiple CPUs must share an area, creating
222 more lock contention.
223
224 New pools added via dynamic swiotlb are linked together in a linear list.
225 swiotlb code frequently must search for the pool containing a particular
226 swiotlb physical address, so that search is linear and not performant with a
227 large number of dynamic pools. The data structures could be improved for
228 faster searches.
229
230 Overall, dynamic swiotlb works best for small configurations with relatively
231 few CPUs. It allows the default swiotlb pool to be smaller so that memory is
232 not wasted, with dynamic pools making more space available if needed (as long
233 as fragmentation isn't an obstacle). It is less useful for large CoCo VMs.
234
235 Data Structure Details
236 ----------------------
237 swiotlb is managed with four primary data structures: io_tlb_mem, io_tlb_pool,
238 io_tlb_area, and io_tlb_slot. io_tlb_mem describes a swiotlb memory allocator,
239 which includes the default memory pool and any dynamic or transient pools
240 linked to it. Limited statistics on swiotlb usage are kept per memory allocator
241 and are stored in this data structure. These statistics are available under
242 /sys/kernel/debug/swiotlb when CONFIG_DEBUG_FS is set.
243
244 io_tlb_pool describes a memory pool, either the default pool, a dynamic pool,
245 or a transient pool. The description includes the start and end addresses of
246 the memory in the pool, a pointer to an array of io_tlb_area structures, and a
247 pointer to an array of io_tlb_slot structures that are associated with the pool.
248
249 io_tlb_area describes an area. The primary field is the spin lock used to
250 serialize access to slots in the area. The io_tlb_area array for a pool has an
251 entry for each area, and is accessed using a 0-based area index derived from the
252 calling processor ID. Areas exist solely to allow parallel access to swiotlb
253 from multiple CPUs.
254
255 io_tlb_slot describes an individual memory slot in the pool, with size
256 IO_TLB_SIZE (2 KiB currently). The io_tlb_slot array is indexed by the slot
257 index computed from the bounce buffer address relative to the starting memory
258 address of the pool. The size of struct io_tlb_slot is 24 bytes, so the
259 overhead is about 1% of the slot size.
260
261 The io_tlb_slot array is designed to meet several requirements. First, the DMA
262 APIs and the corresponding swiotlb APIs use the bounce buffer address as the
263 identifier for a bounce buffer. This address is returned by
264 swiotlb_tbl_map_single(), and then passed as an argument to
265 swiotlb_tbl_unmap_single() and the swiotlb_sync_*() functions. The original
266 memory buffer address obviously must be passed as an argument to
267 swiotlb_tbl_map_single(), but it is not passed to the other APIs. Consequently,
268 swiotlb data structures must save the original memory buffer address so that it
269 can be used when doing sync operations. This original address is saved in the
270 io_tlb_slot array.
271
272 Second, the io_tlb_slot array must handle partial sync requests. In such cases,
273 the argument to swiotlb_sync_*() is not the address of the start of the bounce
274 buffer but an address somewhere in the middle of the bounce buffer, and the
275 address of the start of the bounce buffer isn't known to swiotlb code. But
276 swiotlb code must be able to calculate the corresponding original memory buffer
277 address to do the CPU copy dictated by the "sync". So an adjusted original
278 memory buffer address is populated into the struct io_tlb_slot for each slot
279 occupied by the bounce buffer. An adjusted "alloc_size" of the bounce buffer is
280 also recorded in each struct io_tlb_slot so a sanity check can be performed on
281 the size of the "sync" operation. The "alloc_size" field is not used except for
282 the sanity check.
283
284 Third, the io_tlb_slot array is used to track available slots. The "list" field
285 in struct io_tlb_slot records how many contiguous available slots exist starting
286 at that slot. A "0" indicates that the slot is occupied. A value of "1"
287 indicates only the current slot is available. A value of "2" indicates the
288 current slot and the next slot are available, etc. The maximum value is
289 IO_TLB_SEGSIZE, which can appear in the first slot in a slot set, and indicates
290 that the entire slot set is available. These values are used when searching for
291 available slots to use for a new bounce buffer. They are updated when allocating
292 a new bounce buffer and when freeing a bounce buffer. At pool creation time, the
293 "list" field is initialized to IO_TLB_SEGSIZE down to 1 for the slots in every
294 slot set.
295
296 Fourth, the io_tlb_slot array keeps track of any "padding slots" allocated to
297 meet alloc_align_mask requirements described above. When
298 swiotlb_tbl_map_single() allocates bounce buffer space to meet alloc_align_mask
299 requirements, it may allocate pre-padding space across zero or more slots. But
300 when swiotlb_tbl_unmap_single() is called with the bounce buffer address, the
301 alloc_align_mask value that governed the allocation, and therefore the
302 allocation of any padding slots, is not known. The "pad_slots" field records
303 the number of padding slots so that swiotlb_tbl_unmap_single() can free them.
304 The "pad_slots" value is recorded only in the first non-padding slot allocated
305 to the bounce buffer.
306
307 Restricted pools
308 ----------------
309 The swiotlb machinery is also used for "restricted pools", which are pools of
310 memory separate from the default swiotlb pool, and that are dedicated for DMA
311 use by a particular device. Restricted pools provide a level of DMA memory
312 protection on systems with limited hardware protection capabilities, such as
313 those lacking an IOMMU. Such usage is specified by DeviceTree entries and
314 requires that CONFIG_DMA_RESTRICTED_POOL is set. Each restricted pool is based
315 on its own io_tlb_mem data structure that is independent of the main swiotlb
316 io_tlb_mem.
317
318 Restricted pools add swiotlb_alloc() and swiotlb_free() APIs, which are called
319 from the dma_alloc_*() and dma_free_*() APIs. The swiotlb_alloc/free() APIs
320 allocate/free slots from/to the restricted pool directly and do not go through
321 swiotlb_tbl_map/unmap_single().
322

3. 한국어 전문 번역

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

DMA와 swiotlb 개요

1-28

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

DMA와 swiotlb

`swiotlb`는 Linux kernel DMA layer가 사용하는 memory buffer allocator입니다. Hardware 제약이나 다른 요구 사항 때문에 DMA를 수행하는 device가 대상 memory buffer에 직접 접근할 수 없을 때 주로 사용합니다.

이 경우 DMA layer가 `swiotlb`를 호출해 제약에 맞는 임시 memory buffer를 할당합니다. DMA는 이 임시 buffer를 대상으로 수행하고 CPU가 임시 buffer와 원래 대상 memory buffer 사이의 data를 복사합니다. 이 방식을 일반적으로 bounce buffering이라 하고 임시 memory buffer를 bounce buffer라고 합니다.

Device driver는 `swiotlb`와 직접 상호작용하지 않습니다. Driver는 자신이 관리하는 device의 DMA attribute를 DMA layer에 알리고, device의 DMA를 programming할 때 일반 DMA map, unmap, sync API를 사용합니다.

이 API는 device DMA attribute와 kernel-wide 설정으로 bounce buffering이 필요한지 판단합니다. 필요하다면 DMA layer가 bounce buffer의 할당, 해제, sync를 관리합니다. DMA attribute는 device별이므로 같은 system에서도 일부 device만 bounce buffering을 사용할 수 있습니다.

CPU가 bounce buffer와 원래 대상 buffer 사이의 data를 복사하므로 bounce buffering은 원래 buffer에 직접 DMA하는 것보다 느리고 CPU resource도 더 많이 소비합니다. 따라서 DMA 기능을 제공하는 데 꼭 필요한 경우에만 사용합니다.

사용 시나리오

29-58

사용 시나리오

`swiotlb`는 원래 address 제한이 있는 device의 DMA를 처리하려고 만들어졌습니다. Physical memory가 4 GiB를 넘게 커져도 일부 device는 32-bit DMA address만 제공할 수 있었습니다. 4 GiB 경계 아래에 bounce buffer memory를 할당하면 이러한 device도 계속 동작하며 DMA를 수행할 수 있습니다.

최근 Confidential Computing(CoCo) VM에서는 guest VM memory를 기본으로 암호화하여 host hypervisor와 VMM이 접근하지 못하게 합니다. Host가 guest 대신 I/O를 수행하려면 암호화되지 않은 guest memory로 I/O를 보내야 합니다.

CoCo VM은 모든 DMA I/O에 bounce buffer를 강제하는 kernel-wide option을 설정하고 bounce buffer memory를 암호화되지 않은 상태로 구성합니다. Host는 bounce buffer memory를 대상으로 DMA I/O를 수행하고 Linux kernel DMA layer는 sync operation으로 CPU가 원래 대상 buffer와 data를 주고받도록 합니다.

이 CPU copy가 암호화되지 않은 memory와 암호화된 memory 사이를 연결합니다. 따라서 device driver는 memory encryption의 복잡성을 처리하도록 수정하지 않아도 CoCo VM에서 그대로 동작합니다.

Bounce buffer가 필요한 다른 edge case도 있습니다. Untrusted device의 DMA operation을 위해 IOMMU mapping을 만들 때는 전송 data가 든 memory만 device에 허용해야 합니다. 하지만 그 memory가 IOMMU granule의 일부만 차지하면 granule의 나머지 부분에 관련 없는 kernel data가 있을 수 있습니다.

IOMMU access control은 granule 단위이므로 untrusted device가 관련 없는 kernel data에 접근할 수 있습니다. DMA operation에 bounce buffering을 사용하고 bounce buffer의 미사용 부분에 관련 없는 kernel data가 없도록 하면 이 문제를 해결할 수 있습니다.

핵심 기능

59-83

핵심 기능

주요 `swiotlb` API는 `swiotlb_tbl_map_single()`과 `swiotlb_tbl_unmap_single()`입니다. Map API는 byte 단위로 지정한 크기의 bounce buffer를 할당하고 buffer의 physical address를 반환합니다. Buffer memory는 물리적으로 연속입니다.

DMA layer는 physical memory address를 DMA address로 mapping한 뒤 device에 programming하도록 driver에 DMA address를 반환해야 합니다. DMA operation이 여러 memory buffer segment를 지정하면 segment마다 별도의 bounce buffer를 할당해야 합니다.

`swiotlb_tbl_map_single()`은 항상 sync operation, 즉 CPU copy를 수행하여 bounce buffer를 원래 buffer의 내용과 같게 초기화합니다.

`swiotlb_tbl_unmap_single()`은 반대 작업을 합니다. DMA operation이 bounce buffer memory를 갱신했을 가능성이 있고 `DMA_ATTR_SKIP_CPU_SYNC`가 설정되지 않았다면 unmap이 sync operation을 수행하여 bounce buffer의 data를 원래 buffer로 CPU copy합니다. 그 뒤 bounce buffer memory를 해제합니다.

`swiotlb`는 buffer 제어권이 CPU와 device 사이에서 전환될 때 driver가 사용할 수 있는 `dma_sync_*()` API에 대응하는 sync API도 제공합니다. 이 API는 원래 buffer와 bounce buffer 사이의 data를 CPU로 복사합니다.

`dma_sync_*()`와 마찬가지로 `swiotlb` sync API도 bounce buffer 일부만 원래 buffer와 주고받는 partial sync를 지원합니다.

핵심 기능의 제약

84-138

핵심 기능의 제약

`swiotlb` map, unmap, sync API는 block할 수 없는 context에서 실행될 수 있는 대응 DMA API가 호출하므로 block 없이 동작해야 합니다. 따라서 기본 `swiotlb` allocation memory pool은 boot 때 미리 할당해야 합니다. 단, 뒤의 dynamic `swiotlb`는 예외입니다.

`swiotlb` allocation은 물리적으로 연속이어야 하므로 기본 memory pool 전체를 하나의 contiguous block으로 할당합니다.

기본 pool 사전 할당은 boot-time tradeoff를 만듭니다. Non-blocking 요구 때문에 request가 공간이 생길 때까지 기다릴 수 없으므로 bounce buffer request를 항상 만족할 만큼 pool이 커야 합니다. 하지만 큰 pool의 사전 할당 memory는 system의 다른 용도로 쓸 수 없어 낭비될 수 있습니다.

모든 DMA I/O에 bounce buffer를 쓰는 CoCo VM에서는 이 tradeoff가 특히 큽니다. 이 VM은 기본 pool 크기를 memory의 약 6%, 최대 1 GiB로 잡는 heuristic을 사용하며 memory 낭비 가능성이 큽니다. 반대로 VM workload의 I/O pattern에 따라 이 크기가 부족할 수도 있습니다.

아래의 dynamic `swiotlb` 기능이 도움이 되지만 제한이 있습니다. 기본 `swiotlb` memory pool 크기를 더 잘 관리하는 문제는 아직 해결되지 않았습니다.

단일 `swiotlb` allocation은 `IO_TLB_SIZE * IO_TLB_SEGSIZE` byte로 제한되며 현재 정의에서는 256 KiB입니다. Device DMA 설정상 `swiotlb`를 사용할 가능성이 있다면 DMA segment 최대 크기도 256 KiB로 제한해야 합니다.

이 값은 `dma_map_mapping_size()`와 `swiotlb_max_mapping_size()`를 통해 higher-level kernel code에 전달됩니다. Higher-level code가 이 제한을 고려하지 않으면 `swiotlb`가 처리하기에 너무 큰 request를 만들고 "swiotlb full" error를 받을 수 있습니다.

중요한 device DMA 설정인 `min_align_mask`는 일부 low-order bit가 설정된 2의 거듭제곱 빼기 1 값이며 0일 수도 있습니다. `swiotlb` allocation은 bounce buffer physical address의 `min_align_mask` bit가 원래 buffer address의 같은 bit와 일치하도록 보장합니다.

`min_align_mask`가 0이 아니면 bounce buffer address에 alignment offset이 생겨 allocation 최대 크기가 조금 줄어들 수 있습니다. 이 offset은 `swiotlb_max_mapping_size()` 반환값에 반영되며 `/sys/block/<device>/queue/max_sectors_kb` 같은 곳에 나타납니다.

예를 들어 `swiotlb`를 사용하지 않는 device의 `max_sectors_kb`는 512 KiB 이상일 수 있습니다. 사용할 가능성이 있으면 256 KiB가 되고, `min_align_mask`가 0이 아니면 252 KiB처럼 더 작아질 수 있습니다.

`swiotlb_tbl_map_single()`은 `alloc_align_mask` parameter도 받습니다. 이 parameter는 bounce buffer 공간 allocation이 `alloc_align_mask` bit가 0인 physical address에서 시작하도록 지정합니다. 하지만 `min_align_mask`가 0이 아니면 실제 bounce buffer는 더 큰 address에서 시작할 수 있어 앞쪽 padding 공간이 생길 수 있습니다.

마찬가지로 bounce buffer 끝을 `alloc_align_mask` boundary로 올림하여 뒤쪽 padding이 생길 수 있습니다. `swiotlb` code는 앞뒤 padding 공간을 초기화하지 않습니다.

IOMMU code는 untrusted device를 mapping할 때 `alloc_align_mask`를 사용합니다. Bounce buffer가 다른 목적으로 사용되지 않는 granule에만 완전히 할당되도록 granule size - 1로 설정합니다.

Data structure 개념

139-192

Data structure 개념

`swiotlb` bounce buffer memory는 전체 system memory에서 하나 이상의 pool로 할당합니다. 기본 pool은 system boot 중 기본 64 MiB 크기로 할당하며 `swiotlb=` kernel boot line parameter로 크기를 바꿀 수 있습니다. CoCo VM 실행 같은 다른 조건에 따라서도 기본 크기를 조정할 수 있습니다.

`CONFIG_SWIOTLB_DYNAMIC`을 활성화하면 system 실행 중 추가 pool을 할당할 수 있습니다. 각 pool은 physical memory의 contiguous range여야 합니다. Architecture별 code가 `SWIOTLB_ANY` flag를 제공하지 않는 한, 32-bit physical memory만 address할 수 있는 device를 위해 기본 pool은 4 GiB physical address 아래에 할당합니다. CoCo VM에서는 `swiotlb` 사용 전에 pool memory를 decrypt해야 합니다.

각 pool은 현재 2 KiB인 `IO_TLB_SIZE` 크기의 slot으로 나뉩니다. 연속한 `IO_TLB_SEGSIZE` slot 128개가 slot set 하나를 구성합니다. Bounce buffer는 하나 이상의 contiguous slot을 차지하며 slot 하나를 여러 bounce buffer가 공유하지 않습니다.

Bounce buffer는 하나의 slot set 안에서만 할당해야 하므로 최대 크기는 `IO_TLB_SIZE * IO_TLB_SEGSIZE`입니다. Alignment와 size 제약을 만족하면 여러 작은 bounce buffer가 한 slot set에 함께 존재할 수 있습니다.

Slot은 area로도 묶이며 slot set 전체가 한 area에 있어야 합니다. 각 area는 그 안의 slot을 조작할 때 보유해야 하는 자체 spin lock을 가집니다. Area 분할은 CoCo VM처럼 `swiotlb` 사용량이 많을 때 하나의 global spin lock을 두고 경합하는 일을 피합니다.

최대 parallelism을 위해 area 수는 기본적으로 system CPU 수와 같습니다. 하지만 area는 `IO_TLB_SEGSIZE` slot보다 작을 수 없어 여러 CPU가 같은 area를 공유해야 할 수도 있습니다. Area 수는 `swiotlb=` kernel boot parameter로 설정할 수도 있습니다.

Bounce buffer를 할당할 때 호출 CPU와 연결된 area에 free space가 부족하면 다른 CPU와 연결된 area를 순서대로 시도합니다. 각 area의 allocation을 시도하기 전에 그 area의 spin lock을 얻어야 하므로 전체 `swiotlb`가 바쁘면 경합이 발생할 수 있습니다. 그러나 모든 area의 free space가 부족할 때만 allocation request가 실패합니다.

Code가 많은 계산에 shift와 bit mask를 사용하므로 `IO_TLB_SIZE`, `IO_TLB_SEGSIZE`, area 수는 모두 2의 거듭제곱이어야 합니다. 필요하면 area 수를 2의 거듭제곱으로 올림합니다.

기본 pool은 `PAGE_SIZE` alignment로 할당합니다. `swiotlb_tbl_map_single()`의 `alloc_align_mask` argument가 더 큰 alignment를 지정하면 각 slot set의 초기 slot 일부가 기준을 만족하지 못할 수 있습니다. Bounce buffer allocation은 slot set boundary를 넘을 수 없으므로 이 slot을 제외하면 최대 bounce buffer 크기가 사실상 줄어듭니다.

현재 `alloc_align_mask`는 `PAGE_SIZE`보다 클 수 없는 IOMMU granule size를 기준으로 설정하므로 문제가 없습니다. 앞으로 이것이 바뀐다면 초기 pool allocation에 `PAGE_SIZE`보다 큰 alignment가 필요할 수 있습니다.

Dynamic swiotlb

193-234

Dynamic swiotlb

`CONFIG_SWIOTLB_DYNAMIC`을 활성화하면 bounce buffer allocation에 사용할 memory를 on-demand로 확장할 수 있습니다. Free space 부족으로 bounce buffer request가 실패하면 asynchronous background task를 시작하여 general system memory를 할당하고 `swiotlb` pool로 변환합니다.

Memory allocation은 block할 수 있지만 `swiotlb` request는 block하면 안 되므로 추가 pool 생성을 비동기로 수행해야 합니다. Background task를 시작한 뒤 bounce buffer request는 "swiotlb full" error 반환을 피하려고 transient pool을 만듭니다.

Transient pool 크기는 bounce buffer request와 같고 bounce buffer를 해제할 때 삭제합니다. 생성이 block하지 않도록 general system memory atomic pool에서 memory를 가져옵니다. 특히 memory를 decrypt해야 하는 CoCo VM에서는 transient pool 생성 비용이 비교적 높으므로 background task가 non-transient pool을 추가할 때까지만 임시방편으로 사용합니다.

Dynamic pool 추가에도 제한이 있습니다. 기본 pool처럼 memory가 물리적으로 연속이어야 하므로 크기는 `MAX_PAGE_ORDER` page로 제한되며 일반적인 x86 system에서는 예를 들어 4 MiB입니다. Memory fragmentation 때문에 최대 크기 allocation을 얻지 못할 수 있습니다.

Dynamic pool allocator는 성공할 때까지 더 작은 크기를 시도하되 최소 크기는 1 MiB입니다. System memory fragmentation이 심하면 dynamic pool 추가가 완전히 실패할 수 있습니다.

Dynamic pool의 area 수는 기본 pool과 다를 수 있습니다. 새 pool 크기는 보통 최대 몇 MiB이므로 area 수도 더 적을 가능성이 큽니다. 예를 들어 새 pool이 4 MiB이고 최소 area 크기가 256 KiB이면 area를 16개만 만들 수 있습니다. System CPU가 16개보다 많으면 여러 CPU가 area를 공유하여 lock contention이 늘어납니다.

Dynamic `swiotlb`로 추가한 새 pool은 linear list로 연결합니다. `swiotlb` code는 특정 physical address가 속한 pool을 자주 찾아야 하므로 dynamic pool이 많으면 linear search의 성능이 좋지 않습니다. 더 빠른 search를 위해 data structure를 개선할 수 있습니다.

전체적으로 dynamic `swiotlb`는 CPU가 비교적 적은 작은 configuration에 가장 잘 맞습니다. 기본 pool을 줄여 memory 낭비를 막고, fragmentation이 방해하지 않는 한 필요할 때 dynamic pool로 공간을 늘릴 수 있습니다. 큰 CoCo VM에는 효용이 낮습니다.

Data structure 상세

235-306

Data structure 상세

`swiotlb`는 `io_tlb_mem`, `io_tlb_pool`, `io_tlb_area`, `io_tlb_slot` 네 가지 주요 data structure로 관리합니다. `io_tlb_mem`은 기본 memory pool과 연결된 dynamic 또는 transient pool을 포함하는 `swiotlb` memory allocator를 설명합니다.

Memory allocator별 제한된 `swiotlb` 사용 statistic도 이 structure에 저장하며, `CONFIG_DEBUG_FS`가 설정되면 `/sys/kernel/debug/swiotlb`에서 볼 수 있습니다.

`io_tlb_pool`은 기본, dynamic 또는 transient memory pool을 설명합니다. Pool memory의 시작과 끝 address, pool에 연결된 `io_tlb_area` structure array pointer와 `io_tlb_slot` structure array pointer를 포함합니다.

`io_tlb_area`는 area를 설명합니다. 핵심 field는 area의 slot 접근을 serialize하는 spin lock입니다. Pool의 `io_tlb_area` array에는 area마다 entry가 있으며 호출 processor ID에서 유도한 0-based area index로 접근합니다. Area는 여러 CPU가 `swiotlb`에 병렬 접근할 수 있도록 존재합니다.

`io_tlb_slot`은 현재 2 KiB인 `IO_TLB_SIZE` 크기의 개별 memory slot을 설명합니다. `io_tlb_slot` array는 pool 시작 memory address에 대한 bounce buffer address의 상대값으로 계산한 slot index를 사용합니다. `struct io_tlb_slot`은 24 byte이므로 overhead는 slot 크기의 약 1%입니다.

`io_tlb_slot` array는 여러 요구를 충족하도록 설계되었습니다. 첫째, DMA API와 대응 `swiotlb` API는 bounce buffer address를 identifier로 사용합니다. `swiotlb_tbl_map_single()`이 이 address를 반환하고 이후 `swiotlb_tbl_unmap_single()`과 `swiotlb_sync_*()` function에 argument로 전달합니다.

원래 memory buffer address는 `swiotlb_tbl_map_single()`에 전달되지만 다른 API에는 전달되지 않습니다. 따라서 sync operation에서 사용하도록 `swiotlb` data structure가 원래 address를 저장해야 하며, 이를 `io_tlb_slot` array에 저장합니다.

둘째, `io_tlb_slot` array는 partial sync request를 처리해야 합니다. 이때 `swiotlb_sync_*()` argument는 bounce buffer 시작 address가 아니라 중간 address이며 시작 address를 `swiotlb` code가 알지 못합니다.

그래도 sync가 요구하는 CPU copy를 위해 대응하는 원래 memory buffer address를 계산해야 합니다. 그래서 bounce buffer가 차지한 각 slot의 `struct io_tlb_slot`에 조정된 원래 buffer address를 채웁니다. Bounce buffer의 조정된 `alloc_size`도 각 slot에 기록하여 sync operation 크기의 sanity check에 사용하며, 이 field는 그 검사 외에는 사용하지 않습니다.

셋째, `io_tlb_slot` array는 사용 가능한 slot을 추적합니다. `struct io_tlb_slot`의 `list` field는 현재 slot에서 시작하는 연속 available slot 수를 기록합니다. 0은 occupied, 1은 현재 slot만 available, 2는 현재와 다음 slot이 available함을 뜻합니다.

최댓값은 `IO_TLB_SEGSIZE`이며 slot set의 첫 slot에 나타나 전체 slot set이 available함을 뜻합니다. 새 bounce buffer의 slot을 찾고 할당하거나 해제할 때 이 값을 사용하고 갱신합니다. Pool 생성 때 모든 slot set의 `list` field를 `IO_TLB_SEGSIZE`부터 1까지 초기화합니다.

넷째, `io_tlb_slot` array는 앞서 설명한 `alloc_align_mask` 요구를 맞추려고 할당한 padding slot을 추적합니다. `swiotlb_tbl_map_single()`은 0개 이상의 slot에 걸친 pre-padding 공간을 할당할 수 있습니다.

하지만 bounce buffer address로 `swiotlb_tbl_unmap_single()`을 호출할 때는 allocation을 지배한 `alloc_align_mask`와 padding slot 수를 알 수 없습니다. `pad_slots` field가 padding slot 수를 기록하여 unmap이 이를 해제할 수 있게 합니다. `pad_slots` 값은 bounce buffer에 할당한 첫 non-padding slot에만 기록합니다.

Restricted pool

307-321

Restricted pool

`swiotlb` machinery는 기본 `swiotlb` pool과 분리되어 특정 device의 DMA 전용으로 사용하는 memory pool인 restricted pool에도 사용됩니다. Restricted pool은 IOMMU가 없는 system처럼 hardware protection capability가 제한된 환경에서 일정 수준의 DMA memory protection을 제공합니다.

이 용도는 DeviceTree entry로 지정하며 `CONFIG_DMA_RESTRICTED_POOL` 설정이 필요합니다. 각 restricted pool은 main `swiotlb`의 `io_tlb_mem`과 독립적인 자체 `io_tlb_mem` data structure를 기반으로 합니다.

Restricted pool은 `dma_alloc_*()`와 `dma_free_*()` API에서 호출하는 `swiotlb_alloc()`과 `swiotlb_free()` API를 추가합니다. 이 API는 restricted pool에서 직접 slot을 할당하고 해제하며 `swiotlb_tbl_map/unmap_single()`을 거치지 않습니다.