Сделаем код чище: Пару слов об управляемых ресурсах в ядре Linux для...

Сделаем код чище: Пару слов об управляемых ресурсах в ядре Linux для драйверов устройств

317
ПОДЕЛИТЬСЯ

Следя за появляющимися драйверами в ядре Linux, не могу не отметить, что создатели недостаточно отлично знают инфраструктуру ядра, поточнее внутренний API , существенно упрощающий жизнь при написании драйверов устройств. В частности объясню каким образом они работают и как упрощают разработку драйверов. Сейчас я коснусь темы, посвящённой управляемым ресурсам.

Условно разобью предоставляемый API на:

Память, в том числе выделяемая для DMA и ввода-вывода
Прерывания
Регуляторы напряжения
Для драйверов на шинах (PCI, SPI, IIO)
GPIO, Pin control
Остальное

Но сначала коротко расскажу принцип как устроено управление этими ресурсами. В каждой из категорий я приведу перечень доступных функций и в случае необходимости доп информацию, на которую стоит направить внимание.

Управление ресурсами для устройств
В дальнем 2007 году Tejun Heo предложил упростить драйвера устройств с помощью управляемых ресурсов устройств, создав так называемое devres API.

Вариант в 2007commit 9ac7849e35f705830f7b016ff272b0ff1f7ff759
Author: Tejun Heo <htejun@gmail.com>

devres: device resource management

Каждому устройству соответствует его представление, struct device, в дереве устройств ядра Linux. Мысль заключается в последующем. В структуру было добавлено поле devres_head (на самом деле ещё одно для синхронизации доступа), которое показывает на перечень управляемых ресурсов, раз таковые были применены на стадии ->probe(). Управляемый ресурс представляет собой выделенную память на куче ассоциированную с некой структурой данных, в случае kmalloc(), к примеру, это просто указатель void *.

На шаге ->probe(), как я уже говорил, выполняется пополнение перечня управляемых ресурсов, на стадии же ->remove() базисная часть обслуживания драйверов сама позаботится о высвобождении всех занятых ресурсов ровно в порядке обратном их добавлению.

Для исключительных случаев можно вызвать соответственное высвобождение ранее и очевидно. Тут же кроется и неувязка, которую мы разберём на примере в последующей части.

Память, в том числе выделяемая для DMA и ввода-вывода
Для выделения памяти можно применять последующие главные функции:
devm_kasprintf()
devm_kcalloc()
devm_kmalloc()
devm_kmemdup()
devm_kstrdup()
devm_kzalloc()

Предназначение каждой по-моему разумеется, останавливаться не буду на этом. Перейдём к примеру использования.

Допустим, мы выделяем память для нашей структуры и желаем, чтоб она автоматом высвободилась, по окончании работы драйвера.
*/
} 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 devicen");
return 0;
}

void mydev_remove(…)
{
/* Nothing!

Направили внимание как прекрасно у нас смотрится путь выхода при появлении ошибки в середине функции? И как замечательно смотрится ->remove()!

Сейчас примеры как вызволить ресурс, раз это по какой-то причине нужно сделать очевидно.
Верно:
void mydev_remove(…)
{
struct device *dev = …;
struct my_cool_device *cool = …;
devm_kfree(dev, cool->memregion);
}
Некорректно:
void mydev_remove(…)
{
struct my_cool_device *cool = …;
kfree(cool->memregion);
}

Для выделения памяти, пригодной для DMA используйте
dmam_alloc_coherent()
dmam_alloc_noncoherent()
dmam_pool_create()

Ресурсы ввода-вывода можно выделить используя нижеследующие функции
devm_ioport_map()
devm_ioremap()
devm_ioremap_resource(): инспектирует ресурс, запрашивает регион памяти, проецирует его на физические адреса устройства
pcim_iomap()
pcim_iomap_regions(): запрашивает регион и проецирует на него физические адреса указанные в требуемых BAR-ах
pcim_iomap_table(): массив спроецированных адресов, индексируемый по номеру BAR-а

Обратите внимание, что нижняя часть относится к устройствам на шине PCI, но при этом вынесена в категорию ввода-вывода.

Прерывания
devm_request_any_context_irq()
devm_request_irq()
devm_request_threaded_irq()

Регуляторы напряжения
devm_regulator_bulk_get()
devm_regulator_get()
devm_regulator_register()

Для драйверов на шинах (PCI, SPI, IIO)
Шина IIO:
devm_iio_device_alloc()
devm_iio_device_register()
devm_iio_kfifo_allocate()
devm_iio_trigger_alloc()

Шина MDIO:
devm_mdiobus_alloc()
devm_mdiobus_alloc_size()

Шина PCI:
pcim_enable_device(): в случае удачного завершения все операции предполагаются управляемыми
pcim_pin_device(): бросить устройство разрешённым опосля высвобождения

К примеру, можно поглядеть детали в commit 5618955c4269. В особенности обратите внимание на pcim_enable_device(). В один прекрасный момент применив её нужно вычистить высвобождение регионов, даже раз их выделение и проецирование осуществляется старенькым методом.

Шина SPI:
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()

Остальное
Я затронул только более нередко встречаемые и используемые функции-ассистенты для управления ресурсами. habrahabr.ru О остальном можно почитать в соответственной документации на API ядра: devres.txt.

НЕТ КОММЕНТАРИЕВ

ОСТАВЬТЕ ОТВЕТ

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.