Documentation/driver-api/driver-model/devres.rst GitHub 원문 ↗

Linux 6.18.37 · Driver API

Devres - Managed Device Resource

devres의 자동 release, group rollback, lifetime·overhead와 전체 managed API catalog를 설명합니다.

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

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

1. 요약·해설

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

요약과 해설

devres.rst:1-468

devres는 struct device에 release callback이 있는 resource entry를 연결해 detach와 probe failure의 cleanup을 공통화합니다. group은 여러 acquisition을 transaction처럼 rollback하고, managed devm/dmam/pcim API는 cleanup만 대신하므로 allocation·operation failure 검사는 driver가 계속 수행해야 합니다.

2. 영어 원문 전체

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

원문 전체 펼치기
1 ================================
2 Devres - Managed Device Resource
3 ================================
4
5 Tejun Heo <teheo@suse.de>
6
7 First draft 10 January 2007
8
9 .. contents
10
11 1. Intro : Huh? Devres?
12 2. Devres : Devres in a nutshell
13 3. Devres Group : Group devres'es and release them together
14 4. Details : Life time rules, calling context, ...
15 5. Overhead : How much do we have to pay for this?
16 6. List of managed interfaces: Currently implemented managed interfaces
17
18
19 1. Intro
20 --------
21
22 devres came up while trying to convert libata to use iomap. Each
23 iomapped address should be kept and unmapped on driver detach. For
24 example, a plain SFF ATA controller (that is, good old PCI IDE) in
25 native mode makes use of 5 PCI BARs and all of them should be
26 maintained.
27
28 As with many other device drivers, libata low level drivers have
29 sufficient bugs in ->remove and ->probe failure path. Well, yes,
30 that's probably because libata low level driver developers are lazy
31 bunch, but aren't all low level driver developers? After spending a
32 day fiddling with braindamaged hardware with no document or
33 braindamaged document, if it's finally working, well, it's working.
34
35 For one reason or another, low level drivers don't receive as much
36 attention or testing as core code, and bugs on driver detach or
37 initialization failure don't happen often enough to be noticeable.
38 Init failure path is worse because it's much less travelled while
39 needs to handle multiple entry points.
40
41 So, many low level drivers end up leaking resources on driver detach
42 and having half broken failure path implementation in ->probe() which
43 would leak resources or even cause oops when failure occurs. iomap
44 adds more to this mix. So do msi and msix.
45
46
47 2. Devres
48 ---------
49
50 devres is basically linked list of arbitrarily sized memory areas
51 associated with a struct device. Each devres entry is associated with
52 a release function. A devres can be released in several ways. No
53 matter what, all devres entries are released on driver detach. On
54 release, the associated release function is invoked and then the
55 devres entry is freed.
56
57 Managed interface is created for resources commonly used by device
58 drivers using devres. For example, coherent DMA memory is acquired
59 using dma_alloc_coherent(). The managed version is called
60 dmam_alloc_coherent(). It is identical to dma_alloc_coherent() except
61 for the DMA memory allocated using it is managed and will be
62 automatically released on driver detach. Implementation looks like
63 the following::
64
65 struct dma_devres {
66 size_t size;
67 void *vaddr;
68 dma_addr_t dma_handle;
69 };
70
71 static void dmam_coherent_release(struct device *dev, void *res)
72 {
73 struct dma_devres *this = res;
74
75 dma_free_coherent(dev, this->size, this->vaddr, this->dma_handle);
76 }
77
78 dmam_alloc_coherent(dev, size, dma_handle, gfp)
79 {
80 struct dma_devres *dr;
81 void *vaddr;
82
83 dr = devres_alloc(dmam_coherent_release, sizeof(*dr), gfp);
84 ...
85
86 /* alloc DMA memory as usual */
87 vaddr = dma_alloc_coherent(...);
88 ...
89
90 /* record size, vaddr, dma_handle in dr */
91 dr->vaddr = vaddr;
92 ...
93
94 devres_add(dev, dr);
95
96 return vaddr;
97 }
98
99 If a driver uses dmam_alloc_coherent(), the area is guaranteed to be
100 freed whether initialization fails half-way or the device gets
101 detached. If most resources are acquired using managed interface, a
102 driver can have much simpler init and exit code. Init path basically
103 looks like the following::
104
105 my_init_one()
106 {
107 struct mydev *d;
108
109 d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL);
110 if (!d)
111 return -ENOMEM;
112
113 d->ring = dmam_alloc_coherent(...);
114 if (!d->ring)
115 return -ENOMEM;
116
117 if (check something)
118 return -EINVAL;
119 ...
120
121 return register_to_upper_layer(d);
122 }
123
124 And exit path::
125
126 my_remove_one()
127 {
128 unregister_from_upper_layer(d);
129 shutdown_my_hardware();
130 }
131
132 As shown above, low level drivers can be simplified a lot by using
133 devres. Complexity is shifted from less maintained low level drivers
134 to better maintained higher layer. Also, as init failure path is
135 shared with exit path, both can get more testing.
136
137 Note though that when converting current calls or assignments to
138 managed devm_* versions it is up to you to check if internal operations
139 like allocating memory, have failed. Managed resources pertains to the
140 freeing of these resources *only* - all other checks needed are still
141 on you. In some cases this may mean introducing checks that were not
142 necessary before moving to the managed devm_* calls.
143
144
145 3. Devres group
146 ---------------
147
148 Devres entries can be grouped using devres group. When a group is
149 released, all contained normal devres entries and properly nested
150 groups are released. One usage is to rollback series of acquired
151 resources on failure. For example::
152
153 if (!devres_open_group(dev, NULL, GFP_KERNEL))
154 return -ENOMEM;
155
156 acquire A;
157 if (failed)
158 goto err;
159
160 acquire B;
161 if (failed)
162 goto err;
163 ...
164
165 devres_remove_group(dev, NULL);
166 return 0;
167
168 err:
169 devres_release_group(dev, NULL);
170 return err_code;
171
172 As resource acquisition failure usually means probe failure, constructs
173 like above are usually useful in midlayer driver (e.g. libata core
174 layer) where interface function shouldn't have side effect on failure.
175 For LLDs, just returning error code suffices in most cases.
176
177 Each group is identified by `void *id`. It can either be explicitly
178 specified by @id argument to devres_open_group() or automatically
179 created by passing NULL as @id as in the above example. In both
180 cases, devres_open_group() returns the group's id. The returned id
181 can be passed to other devres functions to select the target group.
182 If NULL is given to those functions, the latest open group is
183 selected.
184
185 For example, you can do something like the following::
186
187 int my_midlayer_create_something()
188 {
189 if (!devres_open_group(dev, my_midlayer_create_something, GFP_KERNEL))
190 return -ENOMEM;
191
192 ...
193
194 devres_close_group(dev, my_midlayer_create_something);
195 return 0;
196 }
197
198 void my_midlayer_destroy_something()
199 {
200 devres_release_group(dev, my_midlayer_create_something);
201 }
202
203
204 4. Details
205 ----------
206
207 Lifetime of a devres entry begins on devres allocation and finishes
208 when it is released or destroyed (removed and freed) - no reference
209 counting.
210
211 devres core guarantees atomicity to all basic devres operations and
212 has support for single-instance devres types (atomic
213 lookup-and-add-if-not-found). Other than that, synchronizing
214 concurrent accesses to allocated devres data is caller's
215 responsibility. This is usually non-issue because bus ops and
216 resource allocations already do the job.
217
218 For an example of single-instance devres type, read pcim_iomap_table()
219 in lib/devres.c.
220
221 All devres interface functions can be called without context if the
222 right gfp mask is given.
223
224
225 5. Overhead
226 -----------
227
228 Each devres bookkeeping info is allocated together with requested data
229 area. With debug option turned off, bookkeeping info occupies 16
230 bytes on 32bit machines and 24 bytes on 64bit (three pointers rounded
231 up to ull alignment). If singly linked list is used, it can be
232 reduced to two pointers (8 bytes on 32bit, 16 bytes on 64bit).
233
234 Each devres group occupies 8 pointers. It can be reduced to 6 if
235 singly linked list is used.
236
237 Memory space overhead on ahci controller with two ports is between 300
238 and 400 bytes on 32bit machine after naive conversion (we can
239 certainly invest a bit more effort into libata core layer).
240
241
242 6. List of managed interfaces
243 -----------------------------
244
245 CLOCK
246 devm_clk_get()
247 devm_clk_get_optional()
248 devm_clk_put()
249 devm_clk_bulk_get()
250 devm_clk_bulk_get_all()
251 devm_clk_bulk_get_optional()
252 devm_get_clk_from_child()
253 devm_clk_hw_register()
254 devm_of_clk_add_hw_provider()
255 devm_clk_hw_register_clkdev()
256
257 DMA
258 dmaenginem_async_device_register()
259 dmam_alloc_coherent()
260 dmam_alloc_attrs()
261 dmam_free_coherent()
262 dmam_pool_create()
263 dmam_pool_destroy()
264
265 DRM
266 devm_drm_dev_alloc()
267
268 GPIO
269 devm_gpiod_get()
270 devm_gpiod_get_array()
271 devm_gpiod_get_array_optional()
272 devm_gpiod_get_index()
273 devm_gpiod_get_index_optional()
274 devm_gpiod_get_optional()
275 devm_gpiod_put()
276 devm_gpiod_unhinge()
277 devm_gpiochip_add_data()
278 devm_gpio_request_one()
279
280 I2C
281 devm_i2c_add_adapter()
282 devm_i2c_new_dummy_device()
283
284 IIO
285 devm_iio_device_alloc()
286 devm_iio_device_register()
287 devm_iio_dmaengine_buffer_setup()
288 devm_iio_kfifo_buffer_setup()
289 devm_iio_kfifo_buffer_setup_ext()
290 devm_iio_map_array_register()
291 devm_iio_triggered_buffer_setup()
292 devm_iio_triggered_buffer_setup_ext()
293 devm_iio_trigger_alloc()
294 devm_iio_trigger_register()
295 devm_iio_channel_get()
296 devm_iio_channel_get_all()
297 devm_iio_hw_consumer_alloc()
298 devm_fwnode_iio_channel_get_by_name()
299
300 INPUT
301 devm_input_allocate_device()
302
303 IO region
304 devm_release_mem_region()
305 devm_release_region()
306 devm_release_resource()
307 devm_request_mem_region()
308 devm_request_free_mem_region()
309 devm_request_region()
310 devm_request_resource()
311
312 IOMAP
313 devm_ioport_map()
314 devm_ioport_unmap()
315 devm_ioremap()
316 devm_ioremap_uc()
317 devm_ioremap_wc()
318 devm_ioremap_resource() : checks resource, requests memory region, ioremaps
319 devm_ioremap_resource_wc()
320 devm_platform_ioremap_resource() : calls devm_ioremap_resource() for platform device
321 devm_platform_ioremap_resource_byname()
322 devm_platform_get_and_ioremap_resource()
323 devm_iounmap()
324
325 Note: For the PCI devices the specific pcim_*() functions may be used, see below.
326
327 IRQ
328 devm_free_irq()
329 devm_request_any_context_irq()
330 devm_request_irq()
331 devm_request_threaded_irq()
332 devm_irq_alloc_descs()
333 devm_irq_alloc_desc()
334 devm_irq_alloc_desc_at()
335 devm_irq_alloc_desc_from()
336 devm_irq_alloc_descs_from()
337 devm_irq_alloc_generic_chip()
338 devm_irq_setup_generic_chip()
339 devm_irq_domain_create_sim()
340
341 LED
342 devm_led_classdev_register()
343 devm_led_classdev_register_ext()
344 devm_led_classdev_unregister()
345 devm_led_trigger_register()
346 devm_of_led_get()
347
348 MDIO
349 devm_mdiobus_alloc()
350 devm_mdiobus_alloc_size()
351 devm_mdiobus_register()
352 devm_of_mdiobus_register()
353
354 MEM
355 devm_free_pages()
356 devm_get_free_pages()
357 devm_kasprintf()
358 devm_kcalloc()
359 devm_kfree()
360 devm_kmalloc()
361 devm_kmalloc_array()
362 devm_kmemdup()
363 devm_krealloc()
364 devm_krealloc_array()
365 devm_kstrdup()
366 devm_kstrdup_const()
367 devm_kvasprintf()
368 devm_kzalloc()
369
370 MFD
371 devm_mfd_add_devices()
372
373 MUX
374 devm_mux_chip_alloc()
375 devm_mux_chip_register()
376 devm_mux_control_get()
377 devm_mux_state_get()
378
379 NET
380 devm_alloc_etherdev()
381 devm_alloc_etherdev_mqs()
382 devm_register_netdev()
383
384 PER-CPU MEM
385 devm_alloc_percpu()
386 devm_free_percpu()
387
388 PCI
389 devm_pci_alloc_host_bridge() : managed PCI host bridge allocation
390 devm_pci_remap_cfgspace() : ioremap PCI configuration space
391 devm_pci_remap_cfg_resource() : ioremap PCI configuration space resource
392
393 pcim_enable_device() : after success, the PCI device gets disabled automatically on driver detach
394 pcim_iomap() : do iomap() on a single BAR
395 pcim_iomap_regions() : do request_region() and iomap() on multiple BARs
396 pcim_iomap_table() : array of mapped addresses indexed by BAR
397 pcim_iounmap() : do iounmap() on a single BAR
398 pcim_pin_device() : keep PCI device enabled after release
399 pcim_set_mwi() : enable Memory-Write-Invalidate PCI transaction
400
401 PHY
402 devm_usb_get_phy()
403 devm_usb_get_phy_by_node()
404 devm_usb_get_phy_by_phandle()
405
406 PINCTRL
407 devm_pinctrl_get()
408 devm_pinctrl_put()
409 devm_pinctrl_get_select()
410 devm_pinctrl_register()
411 devm_pinctrl_register_and_init()
412 devm_pinctrl_unregister()
413
414 POWER
415 devm_reboot_mode_register()
416 devm_reboot_mode_unregister()
417
418 PWM
419 devm_pwmchip_alloc()
420 devm_pwmchip_add()
421 devm_pwm_get()
422 devm_fwnode_pwm_get()
423
424 REGULATOR
425 devm_regulator_bulk_register_supply_alias()
426 devm_regulator_bulk_get()
427 devm_regulator_bulk_get_const()
428 devm_regulator_bulk_get_enable()
429 devm_regulator_bulk_put()
430 devm_regulator_get()
431 devm_regulator_get_enable()
432 devm_regulator_get_enable_read_voltage()
433 devm_regulator_get_enable_optional()
434 devm_regulator_get_exclusive()
435 devm_regulator_get_optional()
436 devm_regulator_irq_helper()
437 devm_regulator_put()
438 devm_regulator_register()
439 devm_regulator_register_notifier()
440 devm_regulator_register_supply_alias()
441 devm_regulator_unregister_notifier()
442
443 RESET
444 devm_reset_control_get()
445 devm_reset_controller_register()
446
447 RTC
448 devm_rtc_device_register()
449 devm_rtc_allocate_device()
450 devm_rtc_register_device()
451 devm_rtc_nvmem_register()
452
453 SERDEV
454 devm_serdev_device_open()
455
456 SLAVE DMA ENGINE
457 devm_acpi_dma_controller_register()
458
459 SPI
460 devm_spi_alloc_host()
461 devm_spi_alloc_target()
462 devm_spi_optimize_message()
463 devm_spi_register_controller()
464 devm_spi_register_host()
465 devm_spi_register_target()
466
467 WATCHDOG
468 devm_watchdog_register_device()
469

3. 한국어 전문 번역

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

문서 정보와 목차

1-18

문서 제목은 `Devres - Managed Device Resource`이며 저자는 Tejun Heo `<teheo@suse.de>`입니다. 첫 초안은 2007년 1월 10일(`10 January 2007`)에 작성되었습니다.

  • Intro: Devres가 필요한 이유
  • Devres: 핵심 구조와 동작
  • Devres Group: resource를 묶어 함께 release하기
  • Details: lifetime rule과 calling context
  • Overhead: 관리에 필요한 비용
  • List of managed interfaces: 현재 구현된 managed interface

Devres 도입 배경

19-46

devres는 libata를 iomap 사용 방식으로 전환하는 과정에서 등장했습니다. iomap된 각 address는 driver detach 때 unmap하기 위해 보관해야 합니다. native mode의 일반 SFF ATA controller, 즉 전통적인 PCI IDE는 PCI BAR 5개를 사용하므로 모두 추적해야 합니다.

다른 많은 device driver와 마찬가지로 libata low-level driver의 `->remove`와 `->probe` failure path에도 버그가 충분히 있었습니다. 문서가 없거나 잘못된 hardware를 하루 종일 다뤄 간신히 동작시키고 나면 정리 경로까지 세심히 다루기 어렵다는 현실을 지적합니다.

low-level driver는 core code만큼 주목이나 test를 받지 못하고 detach·initialization failure는 눈에 띌 만큼 자주 일어나지 않습니다. 특히 init failure path는 실행 빈도가 더 낮으면서 여러 entry point를 처리해야 하므로 더 취약합니다.

그 결과 detach 때 resource를 leak하거나 `->probe()` 실패 시 resource leak 또는 oops를 일으키는 불완전한 failure path가 남습니다. iomap뿐 아니라 MSI와 MSI-X도 관리해야 할 resource를 늘립니다.

Devres 핵심과 managed DMA 예제

47-98

devres는 `struct device`와 연결된 임의 크기 memory area의 linked list입니다. 각 devres entry에는 release function이 연결됩니다. 여러 방법으로 개별 release할 수 있지만 driver detach 때는 모든 entry를 반드시 release합니다. release 시 연결된 function을 호출한 뒤 devres entry를 free합니다.

device driver가 자주 쓰는 resource에는 devres 기반 managed interface가 제공됩니다. coherent DMA memory의 일반 allocator는 `dma_alloc_coherent()`이고 managed version은 `dmam_alloc_coherent()`입니다. 동작은 같지만 할당한 DMA memory가 관리되어 driver detach 때 자동 release됩니다. 구현 개념은 다음과 같습니다.

struct dma_devres {
      size_t                size;
      void                *vaddr;
      dma_addr_t        dma_handle;
};

static void dmam_coherent_release(struct device *dev, void *res)
{
      struct dma_devres *this = res;

      dma_free_coherent(dev, this->size, this->vaddr, this->dma_handle);
}

dmam_alloc_coherent(dev, size, dma_handle, gfp)
{
      struct dma_devres *dr;
      void *vaddr;

      dr = devres_alloc(dmam_coherent_release, sizeof(*dr), gfp);
      ...

      /* alloc DMA memory as usual */
      vaddr = dma_alloc_coherent(...);
      ...

      /* record size, vaddr, dma_handle in dr */
      dr->vaddr = vaddr;
      ...

      devres_add(dev, dr);

      return vaddr;
}
Managed DMA resource lifecycle
devres_alloc(release callback)dma_alloc_coherentrecord size/vaddr/dma_handledevres_add(device, entry)driver detachrelease callback + devres free

allocation과 devres registration에서 detach cleanup까지의 순서입니다.

단순해진 init·exit path와 책임

99-144

driver가 `dmam_alloc_coherent()`를 사용하면 initialization이 중간에 실패하거나 device가 detach되어도 area가 반드시 free됩니다. 대부분의 resource를 managed interface로 얻으면 init과 exit code가 훨씬 단순해집니다. init path는 대략 다음과 같습니다.

my_init_one()
{
      struct mydev *d;

      d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL);
      if (!d)
              return -ENOMEM;

      d->ring = dmam_alloc_coherent(...);
      if (!d->ring)
              return -ENOMEM;

      if (check something)
              return -EINVAL;
      ...

      return register_to_upper_layer(d);
}

exit path는 upper layer 등록을 해제하고 hardware를 shutdown하는 작업만 남습니다.

my_remove_one()
{
      unregister_from_upper_layer(d);
      shutdown_my_hardware();
}

devres는 low-level driver의 복잡성을 잘 유지되는 higher layer로 옮깁니다. init failure path와 exit path가 공유되므로 두 경로 모두 더 많은 test를 받을 수 있습니다.

다만 기존 호출이나 assignment를 managed `devm_*` version으로 바꿀 때 내부 memory allocation 같은 operation이 실패했는지는 driver가 직접 확인해야 합니다. managed resource가 대신하는 것은 resource의 free뿐이며 다른 모든 error check는 여전히 caller 책임입니다. 이전에는 필요 없던 check를 managed 호출 전환 후 새로 넣어야 하는 경우도 있습니다.

Managed resource가 줄이는 것과 남기는 것
ConcernDevres responsibilityDriver responsibility
Detach cleanupAutomatic releaseHardware/upper-layer shutdown
Probe failure cleanupRelease acquired managed entriesReturn correct error
Allocation failureNoCheck NULL/ERR and handle
Operation validationNoAll semantic/error checks

automatic release와 driver가 계속 책임져야 할 검사를 구분했습니다.

Devres group과 rollback

145-176

devres entry는 devres group으로 묶을 수 있습니다. group을 release하면 포함된 일반 devres entry와 올바르게 nested된 group을 모두 release합니다. 대표 용도는 resource 획득 sequence가 실패했을 때 rollback하는 것입니다.

 if (!devres_open_group(dev, NULL, GFP_KERNEL))
       return -ENOMEM;

 acquire A;
 if (failed)
       goto err;

 acquire B;
 if (failed)
       goto err;
 ...

 devres_remove_group(dev, NULL);
 return 0;

err:
 devres_release_group(dev, NULL);
 return err_code;

resource 획득 실패는 보통 probe 실패를 뜻합니다. 이런 construct는 실패 시 side effect를 남기면 안 되는 interface function을 제공하는 midlayer driver, 예를 들어 libata core layer에 특히 유용합니다. low-level driver는 대부분 error code를 바로 반환하는 것으로 충분합니다.

Devres group transaction
open groupacquire Aacquire Bsuccess: remove group marker and keep resourcesfailure: release group and rollback allreturn result

group을 transaction처럼 사용하는 success와 failure 경로입니다.

Group ID와 midlayer lifecycle

177-203

각 group은 `void *id`로 식별합니다. `devres_open_group()`의 `@id` argument로 명시하거나 예제처럼 NULL을 전달해 자동 생성할 수 있습니다. 두 경우 모두 함수는 group ID를 반환하고, 이를 다른 devres function에 전달해 target group을 선택합니다. 그 함수들에 NULL을 주면 가장 최근에 open한 group을 선택합니다.

midlayer는 create function 자체를 ID로 사용해 다음처럼 대칭적인 create/destroy interface를 만들 수 있습니다.

int my_midlayer_create_something()
{
      if (!devres_open_group(dev, my_midlayer_create_something, GFP_KERNEL))
              return -ENOMEM;

      ...

      devres_close_group(dev, my_midlayer_create_something);
      return 0;
}

void my_midlayer_destroy_something()
{
      devres_release_group(dev, my_midlayer_create_something);
}

Lifetime, atomicity, calling context

204-224

devres entry의 lifetime은 devres allocation 때 시작해 release되거나 destroy, 즉 제거되고 free될 때 끝납니다. reference counting은 없습니다.

devres core는 모든 basic devres operation의 atomicity를 보장하고 single-instance devres type을 위한 atomic lookup-and-add-if-not-found를 지원합니다. 그 밖에 할당된 devres data에 대한 concurrent access 동기화는 caller 책임입니다. 보통 bus operation과 resource allocation 자체가 동기화를 수행하므로 문제가 되지 않습니다.

single-instance devres type의 예는 `lib/devres.c`의 `pcim_iomap_table()`에서 볼 수 있습니다. 올바른 GFP mask를 지정하면 모든 devres interface function은 특별한 context 제약 없이 호출할 수 있습니다.

Memory overhead

225-241

각 devres bookkeeping 정보는 요청한 data area와 함께 할당됩니다. debug option을 끄면 bookkeeping은 32-bit machine에서 16 bytes, 64-bit에서 24 bytes를 차지합니다. 이는 pointer 세 개를 `ull` alignment로 올림한 크기입니다. singly linked list를 쓰면 pointer 두 개, 즉 32-bit 8 bytes와 64-bit 16 bytes로 줄일 수 있습니다.

devres group 하나는 pointer 8개를 사용하며 singly linked list에서는 6개로 줄일 수 있습니다. port 두 개를 가진 AHCI controller를 단순 변환했을 때 32-bit machine의 memory overhead는 300~400 bytes입니다. libata core에 추가 최적화 노력을 들이면 더 줄일 수 있습니다.

Devres bookkeeping overhead
ItemDefaultSingly linked alternative
Entry on 32-bit16 bytes8 bytes
Entry on 64-bit24 bytes16 bytes
Group8 pointers6 pointers
2-port AHCI on 32-bit300-400 bytesFurther core optimization possible

원문의 architecture별 크기와 group 비용을 정리했습니다.

Managed interface 전체 목록

242-468

현재 구현된 managed interface를 category별로 원문 그대로 보존합니다. function·symbol 이름 자체가 API이므로 번역하지 않습니다.

CLOCK
  devm_clk_get()
  devm_clk_get_optional()
  devm_clk_put()
  devm_clk_bulk_get()
  devm_clk_bulk_get_all()
  devm_clk_bulk_get_optional()
  devm_get_clk_from_child()
  devm_clk_hw_register()
  devm_of_clk_add_hw_provider()
  devm_clk_hw_register_clkdev()

DMA
  dmaenginem_async_device_register()
  dmam_alloc_coherent()
  dmam_alloc_attrs()
  dmam_free_coherent()
  dmam_pool_create()
  dmam_pool_destroy()

DRM
  devm_drm_dev_alloc()

GPIO
  devm_gpiod_get()
  devm_gpiod_get_array()
  devm_gpiod_get_array_optional()
  devm_gpiod_get_index()
  devm_gpiod_get_index_optional()
  devm_gpiod_get_optional()
  devm_gpiod_put()
  devm_gpiod_unhinge()
  devm_gpiochip_add_data()
  devm_gpio_request_one()

I2C
  devm_i2c_add_adapter()
  devm_i2c_new_dummy_device()

IIO
  devm_iio_device_alloc()
  devm_iio_device_register()
  devm_iio_dmaengine_buffer_setup()
  devm_iio_kfifo_buffer_setup()
  devm_iio_kfifo_buffer_setup_ext()
  devm_iio_map_array_register()
  devm_iio_triggered_buffer_setup()
  devm_iio_triggered_buffer_setup_ext()
  devm_iio_trigger_alloc()
  devm_iio_trigger_register()
  devm_iio_channel_get()
  devm_iio_channel_get_all()
  devm_iio_hw_consumer_alloc()
  devm_fwnode_iio_channel_get_by_name()

INPUT
  devm_input_allocate_device()

IO region
  devm_release_mem_region()
  devm_release_region()
  devm_release_resource()
  devm_request_mem_region()
  devm_request_free_mem_region()
  devm_request_region()
  devm_request_resource()

IOMAP
  devm_ioport_map()
  devm_ioport_unmap()
  devm_ioremap()
  devm_ioremap_uc()
  devm_ioremap_wc()
  devm_ioremap_resource() : checks resource, requests memory region, ioremaps
  devm_ioremap_resource_wc()
  devm_platform_ioremap_resource() : calls devm_ioremap_resource() for platform device
  devm_platform_ioremap_resource_byname()
  devm_platform_get_and_ioremap_resource()
  devm_iounmap()

  Note: For the PCI devices the specific pcim_*() functions may be used, see below.

IRQ
  devm_free_irq()
  devm_request_any_context_irq()
  devm_request_irq()
  devm_request_threaded_irq()
  devm_irq_alloc_descs()
  devm_irq_alloc_desc()
  devm_irq_alloc_desc_at()
  devm_irq_alloc_desc_from()
  devm_irq_alloc_descs_from()
  devm_irq_alloc_generic_chip()
  devm_irq_setup_generic_chip()
  devm_irq_domain_create_sim()

LED
  devm_led_classdev_register()
  devm_led_classdev_register_ext()
  devm_led_classdev_unregister()
  devm_led_trigger_register()
  devm_of_led_get()

MDIO
  devm_mdiobus_alloc()
  devm_mdiobus_alloc_size()
  devm_mdiobus_register()
  devm_of_mdiobus_register()

MEM
  devm_free_pages()
  devm_get_free_pages()
  devm_kasprintf()
  devm_kcalloc()
  devm_kfree()
  devm_kmalloc()
  devm_kmalloc_array()
  devm_kmemdup()
  devm_krealloc()
  devm_krealloc_array()
  devm_kstrdup()
  devm_kstrdup_const()
  devm_kvasprintf()
  devm_kzalloc()

MFD
  devm_mfd_add_devices()

MUX
  devm_mux_chip_alloc()
  devm_mux_chip_register()
  devm_mux_control_get()
  devm_mux_state_get()

NET
  devm_alloc_etherdev()
  devm_alloc_etherdev_mqs()
  devm_register_netdev()

PER-CPU MEM
  devm_alloc_percpu()
  devm_free_percpu()

PCI
  devm_pci_alloc_host_bridge()  : managed PCI host bridge allocation
  devm_pci_remap_cfgspace()        : ioremap PCI configuration space
  devm_pci_remap_cfg_resource()        : ioremap PCI configuration space resource

  pcim_enable_device()                : after success, the PCI device gets disabled automatically on driver detach
  pcim_iomap()                        : do iomap() on a single BAR
  pcim_iomap_regions()                : do request_region() and iomap() on multiple BARs
  pcim_iomap_table()                : array of mapped addresses indexed by BAR
  pcim_iounmap()                : do iounmap() on a single BAR
  pcim_pin_device()                : keep PCI device enabled after release
  pcim_set_mwi()                : enable Memory-Write-Invalidate PCI transaction

PHY
  devm_usb_get_phy()
  devm_usb_get_phy_by_node()
  devm_usb_get_phy_by_phandle()

PINCTRL
  devm_pinctrl_get()
  devm_pinctrl_put()
  devm_pinctrl_get_select()
  devm_pinctrl_register()
  devm_pinctrl_register_and_init()
  devm_pinctrl_unregister()

POWER
  devm_reboot_mode_register()
  devm_reboot_mode_unregister()

PWM
  devm_pwmchip_alloc()
  devm_pwmchip_add()
  devm_pwm_get()
  devm_fwnode_pwm_get()

REGULATOR
  devm_regulator_bulk_register_supply_alias()
  devm_regulator_bulk_get()
  devm_regulator_bulk_get_const()
  devm_regulator_bulk_get_enable()
  devm_regulator_bulk_put()
  devm_regulator_get()
  devm_regulator_get_enable()
  devm_regulator_get_enable_read_voltage()
  devm_regulator_get_enable_optional()
  devm_regulator_get_exclusive()
  devm_regulator_get_optional()
  devm_regulator_irq_helper()
  devm_regulator_put()
  devm_regulator_register()
  devm_regulator_register_notifier()
  devm_regulator_register_supply_alias()
  devm_regulator_unregister_notifier()

RESET
  devm_reset_control_get()
  devm_reset_controller_register()

RTC
  devm_rtc_device_register()
  devm_rtc_allocate_device()
  devm_rtc_register_device()
  devm_rtc_nvmem_register()

SERDEV
  devm_serdev_device_open()

SLAVE DMA ENGINE
  devm_acpi_dma_controller_register()

SPI
  devm_spi_alloc_host()
  devm_spi_alloc_target()
  devm_spi_optimize_message()
  devm_spi_register_controller()
  devm_spi_register_host()
  devm_spi_register_target()

WATCHDOG
  devm_watchdog_register_device()
Managed interface category map
Resource domainRepresentative managed API
CLOCK / DMA / DRMdevm_clk_get, dmam_alloc_coherent, devm_drm_dev_alloc
GPIO / I2C / IIO / INPUTdevm_gpiod_get, devm_i2c_add_adapter, devm_iio_device_alloc, devm_input_allocate_device
IO region / IOMAP / IRQdevm_request_mem_region, devm_ioremap_resource, devm_request_irq
LED / MDIO / MEM / MFD / MUXdevm_led_classdev_register, devm_mdiobus_register, devm_kzalloc, devm_mfd_add_devices, devm_mux_chip_register
NET / PER-CPU / PCI / PHYdevm_register_netdev, devm_alloc_percpu, pcim_iomap, devm_usb_get_phy
PINCTRL / POWER / PWM / REGULATORdevm_pinctrl_get, devm_reboot_mode_register, devm_pwm_get, devm_regulator_get
RESET / RTC / SERDEV / DMA / SPI / WATCHDOGdevm_reset_control_get, devm_rtc_register_device, devm_serdev_device_open, devm_acpi_dma_controller_register, devm_spi_register_controller, devm_watchdog_register_device

224줄 catalog의 subsystem 범위를 빠르게 찾을 수 있도록 구조화했습니다.

IOMAP와 PCI 항목의 설명
API의미
devm_ioremap_resource()resource 확인, memory region 요청, ioremap 수행
devm_platform_ioremap_resource()platform device에 devm_ioremap_resource() 호출
pcim_enable_device()성공 뒤 driver detach 시 PCI device 자동 disable
pcim_iomap()단일 BAR iomap
pcim_iomap_regions()여러 BAR에 request_region과 iomap 수행
pcim_iomap_table()BAR index로 찾는 mapped address array
pcim_iounmap()단일 BAR iounmap
pcim_pin_device()release 뒤에도 PCI device enable 유지
pcim_set_mwi()Memory-Write-Invalidate PCI transaction enable

catalog에서 설명 문장이 붙은 API의 의미를 한국어로 옮겼습니다.

PCI device에는 위 목록의 generic IOMAP API 대신 아래 `pcim_*()` 전용 function을 사용할 수 있습니다.