📜 ⬆️ ⬇️

Making the code cleaner: A few words about the managed resources in the Linux kernel for device drivers

Watching the emerging drivers in the Linux kernel, I can not help but note that developers are not well aware of the kernel infrastructure, or rather the internal API, which greatly simplifies life when writing device drivers. Today I will touch on the topic of managed resources. In particular, I will explain how they work and how they simplify the development of drivers.

Conditionally break the provided API into:


In each of the categories, I will provide a list of available functions and, if necessary, additional information that is worth paying attention to. But first I will briefly tell you the principle of how these resources are managed.
')

Resource Management for Devices


Back in 2007, Tejun Heo proposed to simplify device drivers using managed device resources, creating the so-called devres API.

The case in 2007
commit 9ac7849e35f705830f7b016ff272b0ff1f7ff759
Author: Tejun Heo <htejun@gmail.com>

devres: device resource management

The idea is as follows. Each device corresponds to its representation, a struct device , in the Linux kernel device tree. The devres_head field was added to the devres_head (in fact, another one for access synchronization), which indicates the list of managed resources, if such were used at the stage ->probe() . A managed resource is a dedicated memory on the heap associated with a data structure, in the case of kmalloc() , for example, it is simply a void * pointer.

At the stage ->probe() , as I have already said, the list of managed resources is being replenished, at the stage ->remove() basic part of the driver maintenance will take care of freeing all occupied resources exactly in the reverse order of adding them.

For exceptional cases, it is possible to cause an appropriate release earlier and clearly. Here lies the problem , which we will examine with an example in the next section.

Memory, including allocated for DMA and I / O


The following basic functions can be used for memory allocation:
devm_kasprintf ()
devm_kcalloc ()
devm_kmalloc ()
devm_kmemdup ()
devm_kstrdup ()
devm_kzalloc ()

The purpose of each in my opinion is obvious, I will not dwell on this. Let us turn to the example of use.

Suppose we allocate memory for our structure and want it to be automatically released when the driver completes.
 struct my_cool_device { struct device *dev; void *memregion; }; int mydev_probe(...) { struct device *dev = ...; struct my_cool_device *cool; cool = devm_kzalloc(dev, ...); if (!cool) return -ENOMEM; cool->memregion = devm_kmalloc(dev, ...); if (!cool->memregion) return -ENOMEM; cool->dev = dev; dev_info(dev, "Found my cool device\n"); return 0; } void mydev_remove(...) { /* Nothing! */ } 


Notice how beautifully the exit path looks like when an error occurs in the middle of a function? And how wonderful it looks ->remove() !

Now there are examples of how to free a resource, if for some reason you need to do this explicitly.
Right:
 void mydev_remove(...) { struct device *dev = ...; struct my_cool_device *cool = ...; devm_kfree(dev, cool->memregion); } 

Wrong:
 void mydev_remove(...) { struct my_cool_device *cool = ...; kfree(cool->memregion); } 


To allocate memory suitable for DMA use
dmam_alloc_coherent ()
dmam_alloc_noncoherent ()
dmam_pool_create ()

I / O resources can be allocated using the following functions.
devm_ioport_map ()
devm_ioremap ()
devm_ioremap_resource (): checks a resource, requests a region of memory, projects it onto the physical addresses of the device
pcim_iomap ()
pcim_iomap_regions (): queries the region and projects the physical addresses specified in the required BARs onto it
pcim_iomap_table (): an array of projected addresses indexed by the BAR number

Note that the bottom section refers to devices on the PCI bus, but it is in the I / O category.

Interruptions


devm_request_any_context_irq ()
devm_request_irq ()
devm_request_threaded_irq ()


Voltage regulators


devm_regulator_bulk_get ()
devm_regulator_get ()
devm_regulator_register ()


For drivers on tires (PCI, SPI, IIO)


Bus IIO:
devm_iio_device_alloc ()
devm_iio_device_register ()
devm_iio_kfifo_allocate ()
devm_iio_trigger_alloc ()

MDIO Bus:
devm_mdiobus_alloc ()
devm_mdiobus_alloc_size ()

PCI bus:
pcim_enable_device (): in case of successful completion all operations are assumed to be managed
pcim_pin_device (): leave the device enabled after release

Pay particular attention to pcim_enable_device() . Once applied, it is necessary to clean out the release of regions, even if their selection and projection is carried out in the old way. For example, you can see the details in commit 5618955c4269 .

SPI Bus:
devm_spi_register_master ()


GPIO, Pin control


GPIO:
devm_gpiod_get ()
devm_gpiod_get_index ()
devm_gpiod_get_index_optional ()
devm_gpiod_get_optional ()

Pin control:
devm_pinctrl_get ()

Rest


I have touched on only the most frequently used and helpful resource management functions. The rest can be read in the relevant documentation on the kernel API: devres.txt .

Source: https://habr.com/ru/post/255459/


All Articles