요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
================================
Devres - Managed Device Resource
================================
Tejun Heo <teheo@suse.de>
First draft 10 January 2007
.. contents
1. Intro : Huh? Devres?
2. Devres : Devres in a nutshell
3. Devres Group : Group devres'es and release them together
4. Details : Life time rules, calling context, ...
5. Overhead : How much do we have to pay for this?
6. List of managed interfaces: Currently implemented managed interfaces
1. Intro
--------
devres came up while trying to convert libata to use iomap. Each
iomapped address should be kept and unmapped on driver detach. For
example, a plain SFF ATA controller (that is, good old PCI IDE) in
native mode makes use of 5 PCI BARs and all of them should be
maintained.
As with many other device drivers, libata low level drivers have
sufficient bugs in ->remove and ->probe failure path. Well, yes,
that's probably because libata low level driver developers are lazy
bunch, but aren't all low level driver developers? After spending a
day fiddling with braindamaged hardware with no document or
braindamaged document, if it's finally working, well, it's working.
For one reason or another, low level drivers don't receive as much
attention or testing as core code, and bugs on driver detach or
initialization failure don't happen often enough to be noticeable.
Init failure path is worse because it's much less travelled while
needs to handle multiple entry points.
So, many low level drivers end up leaking resources on driver detach
and having half broken failure path implementation in ->probe() which
would leak resources or even cause oops when failure occurs. iomap
adds more to this mix. So do msi and msix.
2. Devres
---------
devres is basically linked list of arbitrarily sized memory areas
associated with a struct device. Each devres entry is associated with
a release function. A devres can be released in several ways. No
matter what, all devres entries are released on driver detach. On
release, the associated release function is invoked and then the
devres entry is freed.
Managed interface is created for resources commonly used by device
drivers using devres. For example, coherent DMA memory is acquired
using dma_alloc_coherent(). The managed version is called
dmam_alloc_coherent(). It is identical to dma_alloc_coherent() except
for the DMA memory allocated using it is managed and will be
automatically released on driver detach. Implementation looks like
the following::
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;
}
If a driver uses dmam_alloc_coherent(), the area is guaranteed to be
freed whether initialization fails half-way or the device gets
detached. If most resources are acquired using managed interface, a
driver can have much simpler init and exit code. Init path basically
looks like the following::
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);
}
And exit path::
my_remove_one()
{
unregister_from_upper_layer(d);
shutdown_my_hardware();
}
As shown above, low level drivers can be simplified a lot by using
devres. Complexity is shifted from less maintained low level drivers
to better maintained higher layer. Also, as init failure path is
shared with exit path, both can get more testing.
Note though that when converting current calls or assignments to
managed devm_* versions it is up to you to check if internal operations
like allocating memory, have failed. Managed resources pertains to the
freeing of these resources *only* - all other checks needed are still
on you. In some cases this may mean introducing checks that were not
necessary before moving to the managed devm_* calls.
3. Devres group
---------------
Devres entries can be grouped using devres group. When a group is
released, all contained normal devres entries and properly nested
groups are released. One usage is to rollback series of acquired
resources on failure. For example::
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;
As resource acquisition failure usually means probe failure, constructs
like above are usually useful in midlayer driver (e.g. libata core
layer) where interface function shouldn't have side effect on failure.
For LLDs, just returning error code suffices in most cases.
Each group is identified by `void *id`. It can either be explicitly
specified by @id argument to devres_open_group() or automatically
created by passing NULL as @id as in the above example. In both
cases, devres_open_group() returns the group's id. The returned id
can be passed to other devres functions to select the target group.
If NULL is given to those functions, the latest open group is
selected.
For example, you can do something like the following::
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);
}
4. Details
----------
Lifetime of a devres entry begins on devres allocation and finishes
when it is released or destroyed (removed and freed) - no reference
counting.
devres core guarantees atomicity to all basic devres operations and
has support for single-instance devres types (atomic
lookup-and-add-if-not-found). Other than that, synchronizing
concurrent accesses to allocated devres data is caller's
responsibility. This is usually non-issue because bus ops and
resource allocations already do the job.
For an example of single-instance devres type, read pcim_iomap_table()
in lib/devres.c.
All devres interface functions can be called without context if the
right gfp mask is given.
5. Overhead
-----------
Each devres bookkeeping info is allocated together with requested data
area. With debug option turned off, bookkeeping info occupies 16
bytes on 32bit machines and 24 bytes on 64bit (three pointers rounded
up to ull alignment). If singly linked list is used, it can be
reduced to two pointers (8 bytes on 32bit, 16 bytes on 64bit).
Each devres group occupies 8 pointers. It can be reduced to 6 if
singly linked list is used.
Memory space overhead on ahci controller with two ports is between 300
and 400 bytes on 32bit machine after naive conversion (we can
certainly invest a bit more effort into libata core layer).
6. List of managed interfaces
-----------------------------
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()
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-46devres는 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-98devres는 `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;
}
allocation과 devres registration에서 detach cleanup까지의 순서입니다.
단순해진 init·exit path와 책임
99-144driver가 `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 호출 전환 후 새로 넣어야 하는 경우도 있습니다.
automatic release와 driver가 계속 책임져야 할 검사를 구분했습니다.
Devres group과 rollback
145-176devres 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를 바로 반환하는 것으로 충분합니다.
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-224devres 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에 추가 최적화 노력을 들이면 더 줄일 수 있습니다.
원문의 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()
224줄 catalog의 subsystem 범위를 빠르게 찾을 수 있도록 구조화했습니다.
catalog에서 설명 문장이 붙은 API의 의미를 한국어로 옮겼습니다.
PCI device에는 위 목록의 generic IOMAP API 대신 아래 `pcim_*()` 전용 function을 사용할 수 있습니다.
요약과 해설
devres.rst:1-468devres는 struct device에 release callback이 있는 resource entry를 연결해 detach와 probe failure의 cleanup을 공통화합니다. group은 여러 acquisition을 transaction처럼 rollback하고, managed devm/dmam/pcim API는 cleanup만 대신하므로 allocation·operation failure 검사는 driver가 계속 수행해야 합니다.