16.4. Несколько других подробностей
Этот подраздел содержит дополнительную информацию об уровне
блочных устройств, которая может быть интересна тем, кто хочет достичь
более глубокого понимания. Можно написать корректный драйвер и без
нижеследующих возможностей, но, в некоторых ситуациях, они могут
быть полезны.
16.4.1. Предварительная подготовка команд
Уровень блочных устройств предоставляет механизм для драйверов,
который осуществляет проверку и предварительную обработку запросов
перед тем, как их вернет elv_next_request. Этот механизм позволяет
драйверу подготовить устройство к будущим операциям, решить могут ли
быть обработаны все запросы, или выполнить еще какие-то
вспомогательные действия.
Если вы хотите использовать это свойство, создайте функцию
предварительной подготовки, в соответствии со следующим прототипом:
typedef int (prep_rq_fn) (request_queue_t *queue, struct request *req);
Структура request содержит поле cmd, которое является массивом
из BLK_MAX_CDB байт. Этот массив может быть использован функцией
предварительной подготовки для хранения аппаратных команд или любой
другой информации. Эта функция возвращает одно из следующих
значений:
BLKPREP_OK
Подготовка прошла успешно, и запрос может быть обработан
функцией request вашего драйвера.
BLKPREP_KILL
Запрос не может быть выполнен. Сопровождается кодом ошибки.
BLKPREP_DEFER
Запрос не может быть выполнен в настоящее время. Он остается в
начале очереди, но не обрабатывается функцией request.
Функция предварительной подготовки вызывается из
elv_next_request непосредственно перед возвратом из нее в ваш драйвер.
Если возвращается значение BLKPREP_DEFER, то elv_next_request вернет
NULL. Данный режим может быть использован, например, тогда, когда
количество выполняемых запросов в вашем устройстве достигло
максимального значения.
Чтобы уровень блочных устройств мог вызывать вашу функцию,
передайте ее в:
void blk_queue_prep_rq(request_queue_t *queue, prep_rq_fn *func);
По умолчанию очередь запросов не имеет функции предварительной
подготовки.
16.4.2. Пометка команд очереди
Аппаратное обеспечение, которое может обслуживать сразу
несколько запросов, обычно поддерживает пометку команд очереди (TCQ
– tagged command queueing). TCQ – простая технология, прикрепляющая
целочисленную метку (tag) к каждому запросу, что позволяет устройству
сообщить драйверу о выполнении одного из запросов. В предыдущих
версиях ядра, драйвера реализующие TCQ, выполняли всю работу
самостоятельно. В ядре 2.6, в уровень блочных устройств добавлена
инфраструктура поддержки TCQ.
Если ваш драйвер использует TCQ, вы должны проинформировать об
этом ядро во время инициализации с помощью вызова:
int blk_queue_init_tags(request_queue_t *queue, int depth, struct
blk_queue_tag *tags);
Параметр queue – это ваша очередь запросов, depth – количество
помечаемых запросов, которое ваше устройство может обработать. Параметр tags – опциональный указатель на массив структур
blk_queue_tag, состоящий из depth элементов. Обычно параметр tags
равен NULL, в этом случае функция blk_queue_init_tags создает массив.
Однако, если вам необходимо, чтобы массив tags был общим у нескольких
устройств, вы можете передать в tags указатель из другой очереди
запросов, хранящийся в поле queue_tags. Вы не должны самостоятельно
выделять массив tags. Уровень блочных устройств должен сам
инициализировать массив, и он не экспортирует функцию инициализации
в модули.
Поскольку функция blk_queue_init_tags производит выделение
памяти, ее работа может завершиться неудачей, в этом случае
возвращается отрицательное число.
При изменении размера массива меток tags, необходимо
проинформировать об этом ядро с помощью функции:
int blk_queue_resize_tags(request_queue_t *queue, int new_depth);
При вызове этой функции блокировка очереди должена быть
захвачена. Вызов может завершиться неудачей, в этом случае
возвращается отрицательное число.
Массив меток ассоциируется со структурой request, вызовом
функции blk_queue_start_tag. При вызове этой функции блокировка
очереди так же должена быть захвачена.
int blk_queue_start_tag(request_queue_t *queue, struct request *req);
Если имеется доступная метка, эта функция использует ее для
запроса, сохраняя ее значение в req->tag и возвращает «0». Так же
функция выводит запрос из очереди и связывает его со структурой
хранения меток. Таким образом, вашему драйверу не требуется убирать
запросы из очереди, если он использует TCQ. В случае, когда нет больше
свободных меток, функция blk_queue_start_tag оставляет запрос в
очереди и возвращает не нулевое значение.
Когда данные запроса полностью переданы, ваш драйвер должен
освободить метку. Для этого используется команда:
void blk_queue_end_tag(request_queue_t *queue, struct request *req);
Перед этим вызовом вы снова должны захватить блокировку
очереди. Функция должна вызываться только после того, как вызов
blk_queue_start_tag вернул нулевое значение, и до вызова
end_that_request_last. И помните, что запрос уже выведен из очереди.
Если требуется найти запрос ассоциированный с меткой (например
когда драйвер сообщает о выполнении) используйте функцию:
struct request *blk_queue_find_tag(request_queue_t *qeue, int tag);
Возвращаемое значение является ассоциированной структурой
request, если все работает правильно.
Если действительно произошел какой-то сбой, ваш драйвер может
произвести сброс, или какое-либо еще действие над устройством. В этом
случае оставшиеся команды не будут выполнены. Предоставляется функция, которая может помочь в исправлении этой ситуации:
void blk_queue_invalidate_tags(request_queue_t *queue);
Эта функция возвращает все ожидающие исполнения запросы
обратно в очередь. При выполнении этого вызова, блокировка очереди
должна быть захвачена.
Внимание! Этот перевод, возможно, ещё не готов.
Его статус: перевод редактируется
Переведено на Нотабеноиде
http://notabenoid.org/book/11832/38378
Переводчики: hardorsoft