Linux Device Drivers 3 / Драйверы устройств Линукс (третье издание): 16.1. Регистрация

Оглавление
← →
Английский оригинал Перевод на русский

Сожалеем, но текст оригинала доступен только зарегистрированным пользователям.

Регистрация

hardorsoft 25.10.10 в 13:07

0

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

hardorsoft 25.10.10 в 13:07

0

Регистрация блочного устройства

hardorsoft 25.10.10 в 13:07

0

Первым делом большинство драйверов блочных устройств
регистрируют себя в ядре. Для этого используется функция
register_blkdev (которая декларируется в <linux></linux>fs.h>).

hardorsoft 25.10.10 в 13:07

0

int register_blkdev(unsigned int major, const char *name);

hardorsoft 25.10.10 в 13:08

0

Аргументами являются старший номер устройства, и
ассоциируемое имя, которое ядро изображает в /proc/devices. Если
старший номер устройства задан равным нулю, то он динамически
выделяется ядром и функция возвращает его значение. Как всегда,
возврат отрицательного значения сигнализирует об ошибке.

hardorsoft 25.10.10 в 13:08

0

Для отмены регистрации используется функция

hardorsoft 25.10.10 в 13:08

0

int unregister_blkdev(unsigned int major, const char *name);

hardorsoft 25.10.10 в 13:09

0

Аргументы функции должны быть такими же, как и в
register_blkdev, иначе функция не отменит регистрацию и возвратит
значение -EINVAL.

hardorsoft 25.10.10 в 13:09

0

В ядре 2.6 вызов register_blkdev - опционален. Со временем,
функций выполняющих этот вызов становится меньше. Функция
выполняет динамическое выделение старшего номера, если требуется, и
создает точку входа в /proc/devices. В будущих версиях ядра функция
register_blkdev может быть убрана. Но традиционно многие драйверы
вызывают ее.

hardorsoft 25.10.10 в 13:10

0

Регистрация диска
Функция register_blkdev используется для получения старшего
номера, но не делает диск доступным в системе. Происходит разделение
регистрационного интерфейса, которое должно вами использоваться для
индивидуального управления драйверами. Использование этого
интерфейса подразумевает знание пары новых структур, с них мы и
начнем.

hardorsoft 25.10.10 в 13:10

0

Операции блочного устройства

hardorsoft 25.10.10 в 13:10

0

Символьные устройства используют структуру file_operations,
чтобы сообщить системе о доступных операциях. Аналогичная структура
используется и для блочных устройств. Она называется
block_device_operations, и описана в <linux></linux>fs.h>. Ниже приведено
краткое описание полей структуры. Позже, при подробном рассмотрении драйвера sbull, мы вернемся к описанию этой структуры.

hardorsoft 25.10.10 в 13:11

0

int (*open)(struct inode *inode, struct file *filp);
int (*release)(struct inode *inode, struct file *filp);
Функции работают подобно их эквивалентам для символьных
устройств. Вызываются для открытия и закрытия устройства. При
открытии драйвер может раскручивать устройство, блокировать
лоток (для сменных носителей). Если при открытии выполняется
блокирование устройства, то при закрытии необходимо его
разблокировать.

hardorsoft 25.10.10 в 13:12

0

int (*ioctl)(struct inode *inode, struct file *filp, unsigned int
cmd, unsigned long arg);
Метод реализует системный вызов ioctl. Блочный уровень первым
перехватывает старший номер стандартного запроса, однако метод
ioctl у многих драйверов довольно краток.

hardorsoft 25.10.10 в 13:12

0

int (*media_changed) (struct gendisk *gd);
Метод вызывается ядром при проверке, не заменил ли
пользователь носитель в устройстве. Возвращает не нулевое
значение, если заменил. Очевидно, что метод применяется только
для устройств поддерживающих сменные носители (для этого
достаточно установить флаг «сменный носитель»). В других
случаях метод можно опустить.

hardorsoft 25.10.10 в 22:11

0

Аргумент struct gendisk, информирующей ядро об одном диске,
будет рассмотрен в следующем подразделе.

hardorsoft 25.10.10 в 22:13

0

struct module *owner;
Указатель на модуль, который является владельцем этой
структуры. Обычно инициализируется в значение THIS_MODULE.

hardorsoft 25.10.10 в 13:14

0

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

hardorsoft 25.10.10 в 13:17

0

Структура gendisk

hardorsoft 25.10.10 в 22:14

0

Структура struct gendisk определена в <linux></linux>genhd.h> и служит
для предоставления ядру индивидуального дискового устройства. В
действительности, ядро использует структуру struct gendisk для
получения информации о разделах, но для разработчиков драйвера это не
важно. Вот поля, которые должны инициализироваться драйвером:

hardorsoft 25.10.10 в 22:15

0

int major;
int first_minor;
int minors;
Эти поля описывают номера устройств используемых диском. Как минимум используется один младший (minor) номер. Если ваше
устройство имеет разделы, вы можете использовать по одному
младшему номеру на раздел. Для minors обычно устанавливается
значение 16, которое позволяет представить диск целиком и 15
разделов. Некоторые дисковые драйвера используют 64 младших
номера для устройства.

hardorsoft 25.10.10 в 22:15

0

char disk_name[32];
Поле задает имя дискового
/proc/partitions и в sysfs.

hardorsoft 25.10.10 в 22:16

0

struct block_device_operations *fops;
Содержит множество операций над устройством из предыдущего
подраздела.

hardorsoft 25.10.10 в 22:16

0

struct request_queue *queue;
Структура используется ядром для управления запросами ввода-
вывода. Мы рассмотрим это в подразделе «Выполнение запросов».

hardorsoft 25.10.10 в 22:16

0

int flags;
Множество флагов, описывающих состояние устройства
(малоиспользуется). Для устройств со сменным носителем должен
устанавливаться флаг GENHD_FL_REMOVABLE. Для устройства CD-ROM
– GENHD_FL_CD. Если по каким-то соображениям вы не хотите
показывать информацию о разделах в /proc/partitions,
установите GENHD_FL_SUPPRESS_PARTITION_INFO.

hardorsoft 25.10.10 в 22:17

0

sector_t capacity;
Емкость устройства в секторах по 512 байт. sector_t может иметь
разрядность 64 бита. Драйвер не устанавливает это поле
непосредственно, для этого используется функция set_capacity.

hardorsoft 25.10.10 в 22:17

0

void *private_data;
Драйвер блочного устройства заносит в это поле указатель на
внутренние данные.

hardorsoft 25.10.10 в 22:17

0

Ядро предоставляет несколько функций для работы со структурой
gendisk. Мы приведем их здесь, а затем покажем, как sbull их
использует, чтобы сделать доступным в системе дисковое устройство.
Структура gendisk создается динамически при инициализации
драйвера, с помощью специальной функции ядра. Драйвер не должен
создавать ее по-другому. Вот эта функция:
struct gendisk *alloc_disk(int minors);

hardorsoft 25.10.10 в 22:18

0

Аргумент minors должен содержать значение младшого номера,
используемое этим диском. Обратите внимание, что впоследствии нельзя
менять это значение, и надеяться на корректную работу.

hardorsoft 25.10.10 в 22:19

0

Когда диск больше не нужен, он может быть удален вызовом:

hardorsoft 25.10.10 в 22:19

0

void del_gendisk(struct gendisk *gd);

hardorsoft 25.10.10 в 22:19

0

Структура gendisk содержит счетчик использования (kobject).
Имеются функции get_disk и put_disk для управления счетчиком, но
драйвер их не использует. Обычно вызов del_gendisk удаляет
заключительную ссылку на структуру, но не гарантирует этого. Поэтому
возможно, что структура будет продолжать существовать (и ваши методы
могут быть вызваны) и после вызова del_gendisk. Тем не менее, если вы
удалите структуру, когда она не используется (после последнего release
или функции cleanup вашего модуля), то можете быть уверены что не
услышете о ней больше.

hardorsoft 25.10.10 в 22:20

0

Выделение структуры не делает диск доступным. Для этого нужно
инициализировать структуру и вызвать функцию add_disk:

hardorsoft 25.10.10 в 22:20

0

Выделение структуры не делает диск доступным. Для этого нужно
инициализировать структуру и вызвать функцию add_disk:

hardorsoft 25.10.10 в 22:20

0

void add_disk(struct gendisk *gd);

hardorsoft 25.10.10 в 22:20

0

Наиболее важное из того, что вы должны понять здесь, это то, что
сразу после вызова add_disk диск начинает «жить» и его методы могут
быть вызваны. Фактически, первый вызов произойдет до возврата из
add_disk, Ядро прочитает несколько первых секторов, пытаясь найти
таблицу разделов. Так что не вызывайте add_disk до тех пор, пока
драйвер не закончит инициализацию и не будет готов отвечать на
запросы.

hardorsoft 25.10.10 в 22:20

0

Инициализация sbull

hardorsoft 25.10.10 в 22:21

0

Наступило время привести пример. Драйвер sbull (доступен на FTP
сайта O'Reilly вместе с исходными кодами) реализует множество
виртуальных дисков. Для каждого накопителя выделяется массив памяти
(с помощью vmalloc, для упрощения), затем этот массив делается
доступным как блочное устройство. Для проверки работы драйвера sbull,
можно разбить виртуальное устройства на разделы, построить файловую
систему, и выполнить монтирование.

hardorsoft 25.10.10 в 22:21

0

Как и другие примеры наших драйверов, sbull позволяет
определить старший номер устройства и во время компиляции, и при
загрузке модуля. Если номер не определен, он выделяется динамически.
Вызов register_blkdev для динамического выделения производится так:

hardorsoft 25.10.10 в 22:21

0

sbull_major = register_blkdev(sbull_major, "sbull");
if (sbull_major &lt;= 0) {
printk(KERN_WARNING "sbull: unable to get major number\n");
return -EBUSY;
}

hardorsoft 25.10.10 в 22:21

0

Как и другие виртуальные устройства из этой книги, у sbull есть
структура для внутреннего использования:

hardorsoft 25.10.10 в 22:21

0

struct sbull_dev {
int size; /* Размер устройства в секторах */
u8 *data; /* Массив даных */
short users; /* Количество пользователей */
short media_change; /* Флаг смены носителя */
spinlock_t lock; /* Взаимо исключающая блокировка*/
struct request_queue *queue; /* Очередь запросов */
struct gendisk *gd; /* Структура gendisk */
struct timer_list timer; /* Для симуляции смены носителя */
};

hardorsoft 25.10.10 в 22:22

0

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

hardorsoft 25.10.10 в 22:22

0

memset (dev, 0, sizeof (struct sbull_dev));
dev->size = nsectors*hardsect_size;
dev->data = vmalloc(dev->size);
if (dev->data = = NULL) {
printk (KERN_NOTICE "vmalloc failure.\n");
return;
}
spin_lock_init(&dev->lock);

hardorsoft 25.10.10 в 22:23

0

Важно отметить выделение и инициализация spinlock перед
следующим шагом, на котором выделяется очередь запросов. Мы
рассмотрим этот процесс подробнее, когда дойдем до обработки запросов.
Итак, подытожив сказанное:

hardorsoft 25.10.10 в 22:23

0

dev->queue = blk_init_queue(sbull_request, &dev->lock);

hardorsoft 25.10.10 в 22:23

0

Здесь sbull_request – наша функция обработки запросов request.
Она выполняет запросы на чтение и запись. Когда мы выделили очередь
запросов, мы можем предоставить spinlock, контролирующий доступ к
очереди. Объект для блокировки предоставляется драйвером, а не ядром,
т.к. очередь запросов и другие структуры данных драйвера находятся в
одной критической секции, и часто обращение к ним происходит ко всем
вместе. Как и любая функция, выделяющая память, blk_init_queue
может потерпеть неудачу, поэтому необходимо проверять возвращаемое
значение.

hardorsoft 25.10.10 в 22:23

0

Раз мы имеем устройство хранения, и выделили очередь запросов,
мы можем выделять, инициализировать и инсталлировать
соответствующую структуру gendisk. Вот код который это производит:

hardorsoft 25.10.10 в 22:24

0

dev->gd = alloc_disk(SBULL_MINORS);
if (! dev->gd) {
printk (KERN_NOTICE "alloc_disk failure\n");
goto out_vfree;
}
dev->gd->major = sbull_major;
dev->gd->first_minor = which*SBULL_MINORS;
dev->gd->fops = &sbull_ops;
dev->gd->queue = dev->queue;
dev->gd->private_data = dev;
snprintf (dev->gd->disk_name, 32, "sbull%c", which + 'a');
set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));
add_disk(dev->gd);

hardorsoft 25.10.10 в 22:24

0

SBULL_MINORS – это младшее число устройства. При назначении
младшего номера устройству, мы должны учитывать задействованные
ранее номера. Имя диска устанавливается так: первый – sbulla, второй –
sbullb, и т.д. Затем, в пространстве пользователя, можно добавить
разделы. Так третий раздел второго устройства может быть /dev/sbullb3.

hardorsoft 25.10.10 в 22:24

0
Минутку...

Следующая страница →