Некоторые другие подробности

Предыдущая  Содержание  Следующая V*D*V

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

Предварительная подготовка команд

Блочный уровень представляет механизм для драйверов для изучения и предварительной обработки запросов, прежде чем они вернутся из 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);

 

По умолчанию очереди запросов не имеют функции подготовки.

Очереди помеченных команд

Оборудование, которое может иметь несколько активных запросов одновременно обычно поддерживает ту или иную форму очереди помеченных команд (tagged command queueing, TCQ). TCQ - просто техника подключения целочисленной "метки" к каждому запросу, так что когда накопитель завершает один из этих запросов, он может сообщить драйверу, какой из них. В предыдущих версиях ядра блочные драйверы, который реализовывали TCQ, делали всю работу самостоятельно; в версии 2.6, в блочный уровень была добавлена поддержка инфраструктуры TCQ для использования всеми драйверами.

 

Если ваш накопитель обрабатывает очереди помеченных команд, вы должны сообщить ядру этот факт во время инициализации вызовом:

 

int blk_queue_init_tags(request_queue_t *queue, int depth, struct blk_queue_tag *tags);

 

Здесь, queue является вашей очередью запросов, а depth является числом помеченных запросов, которые ваше устройство может иметь невыполненными в любой момент времени. tags является необязательным указателем на массив структур struct blk_queue_tag; в нём должно быть depth их. Как правило, tags может быть передан как NULL и blk_queue_init_tags создаст массив. Если, однако, необходимо   совместно использовать одни и те же метки несколькими устройствами, вы можете передать указатель на массив меток (хранящийся в поле queue_tags) от другой очереди запросов. Вы никогда не должны на самом деле самостоятельно выделять массив меток; блочный уровень должен проинициализировать массив и не экспортирует для модулей функцию инициализации.

 

Так как blk_queue_init_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. Она также извлекает запрос из очереди и связывает его со своей собственной структурой для отслеживания меток, поэтому если драйвер использует метки, необходимо позаботиться о том, чтобы не выполнять удаление запроса из очереди самостоятельно. Если доступных меток больше нет, blk_queue_start_tag оставляет запрос в очереди и возвращает ненулевое значение.

 

Когда все передачи для данного запроса завершены, ваш драйвер должен вернуть эту метку с помощью:

 

void blk_queue_end_tag(request_queue_t *queue, struct request *req);

 

Ещё раз, вы должны удерживать блокировку очереди перед вызовом этой функции. Этот вызов должен быть сделан после того, как end_that_request_first возвращает 0 (что означает, что запрос завершён), но перед вызовом end_that_request_last. Помните, что запрос уже удалён из очереди, поэтому было бы ошибкой для вашего драйвера делать это на данном этапе.

 

Если вам необходимо найти запрос, связанный с данной меткой (когда накопитель сообщает о завершение, к примеру), используйте blk_queue_find_tag:

 

struct request *blk_queue_find_tag(request_queue_t *qeue, int tag);

 

Возвращаемое значение является ассоциированной структурой request, если что-то не пошло по настоящему не так.

 

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

 

void blk_queue_invalidate_tags(request_queue_t *queue);

 

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

Предыдущая  Содержание  Следующая