Готовый перевод Linux Device Drivers 3 / Драйверы устройств Линукс (третье издание): 16.2. Операции блочного устройства
16.2 Операции блочного устройства
В предыдущем подразделе мы сделали короткое введение в
структуру block_device_operations. Теперь мы подробнее рассмотрим эти
операции, а потом поговорим об обработке запросов. В конце мы
упомянем еще одну возможность драйвера sbull: сделаем вид, что
устройство – сменный носитель. Когда бы пользователь не закрыл
устройство, устанавливается 30-секундный таймер, и если устройство не
открывается в течении этого времени, контекст очищается, и ядру
сообщается о смене носителя. 30 секунд дают пользователю время,
например на то, чтобы монтировать устройство после создания файловой
системы.
16.2.1 Методы open и release
Для реализации фиктивного съемного носителя sbull должен знать, когда последний пользователь закрыл устройство. Драйвером
поддерживается счетчик пользователей. Эта работа выполняется в
методах open и release.
Метод open выглядит очень похоже на соответствующий метод для
символьных устройств. Он получает, в качестве аргументов, указатели на
и структуру file. Когда inode ссылается на блочное устройство, поле
i_bdev->bd_disk содержит указатель на ассоциированную структуру
gendisk. Этот указатель может быть использован для получения
драйвером внутренней структуры данных устройства. Фактически это
первое что делает метод open.
static int sbull_open(struct inode *inode, struct file *filp)
{
struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data;
del_timer_sync(&dev->timer);
filp->private_data = dev;
spin_lock(&dev->lock);
if (! dev->users)
check_disk_change(inode->i_bdev);
dev->users++;
spin_unlock(&dev->lock);
return 0;
}
Получив указатель на структуру устройства, sbull_open, вызывает
del_timer_sync для удаления таймера съемного носителя, если тот
активен. Обратите внимание, что мы не блокируем устройство
посредством spinlock, пока таймер не удален. В противном случае
возможно возникновение взаимной блокировки, в том случае если
таймерная функция запустится перед ее удалением. Когда устройство
заблокировано, мы выполняем функцию ядра check_disk_change, для
проверки: удачно ли прошла смена носителя. Можно было бы утверждать,
что это должно выполнить ядро, но, следуя стандарту, обработку
выполняет драйвер при открытии.
На последнем шаге инкрементируем счетчик пользователей, и
возвращаемся из функции.
Метод release, напротив, выполняет декремент счетчика
пользователей, и, при необходимости, запускает таймер смены носителя:
static int sbull_release(struct inode *inode, struct file *filp)
{
struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data;
spin_lock(&dev->lock);
dev->users--;
if (!dev->users) {
dev->timer.expires = jiffies + INVALIDATE_DELAY;
add_timer(&dev->timer);
}
spin_unlock(&dev->lock);
return 0;
}
В драйвере, обслуживающем реальное устройство, методы open и
release выполняют соответствующую настройку устройства и драйвера.
Это может заключаться в раскручивании диска или его остановки,
блокировки лотка (для устройств со сменными носителями), выделения буфера ПДП, и т.д.
Вы можете спросить, кто же реально открывает блочное устройство?
Имеется несколько операций, которые вызывают открытие устройства,
непосредственно из пользовательского пространства. Это разбиение
диска на разделы, построение файловой системы на разделе, выполнение
проверки файловой системы, а так же монтирование раздела. В
последнем случае, имеется не пользовательский процесс, который хранит
дескриптор открытого устройства для использования его ядром. Драйвер
блочного устройства не может отличить операцию mount (которая
открывает устройство из пространства ядра) от работы утилиты mkfs
(которая работает из пространства пользователя).
16.2.2 Поддержка устройств со сменными носителями.
Структура block_device_operations содержит два метода для
поддержки устройств со сменными носителями. Если ваше устройство не
позволяет смену носителя, вы можете опустить реализацию этих методов.
Методы относительно просты.
Метод media_changed вызывается из функции check_disk_change для
указания того, что произошла смена носителя. В этом случае,
возвращается не нулевое значение. Реализация для sbull – проста.
Происходит проверка значения флага, который устанавливается, если
сработал таймер смены носителя:
int sbull_media_changed(struct gendisk *gd)
{
struct sbull_dev *dev = gd->private_data;
return dev->media_change;
}
Метод revalidate вызывается после смены носителя. Его работа
заключается в подготовке драйвера к работе с новым носителем. После
выполнения revalidate ядро повторяет чтение таблицы разделов и
начинает работать с устройством. В sbull этот метод просто сбрасывает
флаг media_change, и обнуляет память устройства, симулируя получение
пустого диска:
int sbull_revalidate(struct gendisk *gd)
{
struct sbull_dev *dev = gd->private_data;
if (dev->media_change) {
dev->media_change = 0;
memset (dev->data, 0, dev->size);
}
return 0;
}
16.2.3 Метод ioctl
Блочное устройство предоставляет метод ioctl для управления
устройством. Код высокого уровня подсистемы блочных устройств
перехватывает команды ioctl перед подачей их вашему драйверу
(смотрите файл ядра drivers/block/ioctl.c для более полной
информации). Фактически, современный драйвер блочного устройства
может вовсе не реализовывать очень многие команды ioctl.
В драйвере sbull метод ioctl выполняет только одну команду –
запрос геометрии устройства:
int sbull_ioctl (struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
long size;
struct hd_geometry geo;
struct sbull_dev *dev = filp->private_data;
switch(cmd) {
case HDIO_GETGEO:
/* получение геометрии: т.к. У нас виртуальное устройство,мы
создаем что-то правдоподобное. Мы устанавливаем 16
секторов, 4 головки и вычисляем соответствующее количество
цилиндров. Начало данных устанавливаем на 4 сектор
*/
size = dev->size*(hardsect_size/KERNEL_SECTOR_SIZE);
geo.cylinders = (size & ~0x3f) >> 6;
geo.heads = 4;
geo.sectors = 16;
geo.start = 4;
if (copy_to_user((void __user *) arg, &geo, sizeof(geo)))
return -EFAULT;
return 0;
}
return -ENOTTY; /* Не известная команда */
}
Задача предоставления геометрии может показаться странной,
поскольку наше устройство является виртуальным и ничего не делает с
дорожками и цилиндрами. Многие реальные блочные устройства
окружаются множеством сложных структур. Ядру не интересна
геометрия блочного устройства, оно рассматривается просто как
линейный массив секторов. Однако есть пользовательские утилиты,
которые по-прежнему запрашивают геометрию диска. Следует обратить
внимание на программу fdisk, которая редактирует таблицу разделов. Ей
необходима информация о цилиндрах. Без этой информации fdisk не
будет работать.
Устройство sbull может быть разбито на разделы. Мы
предоставляем в ioctl фиктивную геометрию, которая соответствует
емкости нашего устройства. Подобное делают многие дисковые драйверы.
Обратите внимание, что количество секторов в устройстве преобразуется,
учитывая тот факт, что ядро работает только с 512-байтными секторами .
Внимание! Этот перевод, возможно, ещё не готов.
Его статус: перевод редактируется
Переведено на Нотабеноиде
http://notabenoid.org/book/11832/38376
Переводчики: hardorsoft
Настройки
Готово:
