Реализация процедуры IoCompletion
При входе подпрограмма IoCompletion получает указатель контекста . Когда подпрограмма диспетчеризации вызывает IoSetCompletionRoutine, она может предоставить указатель контекста . Этот указатель может ссылаться на любые определяемые драйвером сведения о контексте, необходимые подпрограмме IoCompletion для обработки IRP. Обратите внимание, что область контекста не может быть страничной, так как подпрограмму IoCompletion можно вызвать в IRQL = DISPATCH_LEVEL.
Рассмотрите следующие рекомендации по реализации процедур IoCompletion:
Подпрограмма IoCompletion может проверка блок состояния ввода-вывода IRP, чтобы определить результат операции ввода-вывода.
Если входной IRP был выделен подпрограммой диспетчеризации с помощью IoAllocateIrp или IoBuildAsynchronousFsdRequest, подпрограмма IoCompletion должна вызвать IoFreeIrp , чтобы освободить эту IRP, предпочтительно до завершения исходного IRP.
Подпрограмма IoCompletion должна освободить все ресурсы на уровне IRP, выделенные подпрограммой диспетчеризации, выделенной для IRP, выделенной драйвером, предпочтительно до того, как она освободит соответствующую IRP.
Например, если подпрограмма диспетчеризации выделяет MDL с IoAllocateMdl и вызывает IoBuildPartialMdl для частичной передачи IRP, которую она выделяет, подпрограмма IoCompletion должна освободить MDL с IoFreeMdl. Если он выделяет ресурсы для поддержания состояния исходного IRP, он должен освободить эти ресурсы, предпочтительно перед вызовом IoCompleteRequest с исходным IRP и определенно перед возвратом управления.
Как правило, перед освобождением или завершением IRP подпрограмма IoCompletion должна освободить все ресурсы для каждого IRP, выделенные подпрограммой Dispatch. В противном случае драйвер должен поддерживать состояние освобождаемых ресурсов, прежде чем его подпрограмма IoCompletion вернет управление выполнением исходного запроса.
Если подпрограмме IoCompletion не удается завершить исходный IRP с помощью STATUS_SUCCESS, она должна задать для блока состояния ввода-вывода в исходной IRP значение, возвращенное в выделенном драйвере IRP, что привело к сбою процедуры IoCompletion при выполнении исходного запроса.
Если подпрограмма IoCompletion завершает исходный запрос с помощью STATUS_PENDING, она должна вызвать IoMarkIrpPending с исходным IRP, прежде чем вызывать IoCompleteRequest.
Если подпрограмма IoCompletion должна завершиться сбоем исходного IRP с ошибкой STATUS_XXX, она может заносить ошибку в журнал. Однако основной драйвер устройства несет ответственность за то, чтобы регистрировать все возникающие ошибки ввода-вывода устройства, поэтому процедуры IoCompletion обычно не регистрируют ошибки.
Когда подпрограмма IoCompletion обработала и освободила IRP, выделенную драйвером, она должна вернуть управление с помощью STATUS_MORE_PROCESSING_REQUIRED.
Возврат STATUS_MORE_PROCESSING_REQUIRED из процедуры IoCompletion предотвратит обработку завершения диспетчера ввода-вывода для выделенного драйвером и освобожденного IRP. Второй вызов IoCompleteRequest приводит к тому, что диспетчер операций ввода-вывода возобновляет вызов подпрограмм завершения IRP, начиная с подпрограммы завершения непосредственно над подпрограммой, которая вернула STATUS_MORE_PROCESSING_REQUIRED.
Если подпрограмма IoCompletion повторно использует входящую IRP для отправки одного или нескольких запросов к более низким драйверам или если подпрограмма повторяет неудачные операции, она должна обновить любой контекст, который подпрограмма IoCompletion поддерживает о каждом повторном использовании или повторных попытках IRP. Затем он может снова настроить расположение стека ввода-вывода следующего драйвера ниже, вызвать IoSetCompletionRoutine с собственной точкой входа и вызвать IoCallDriver для IRP.
Подпрограмма IoCompletion не должна вызывать IoMarkIrpPending при каждом повторном использованию или повторной попытке IRP.
Подпрограмма отправки уже помечает исходный IRP как ожидающий. Пока все драйверы в цепочке не завершают исходный IRP с помощью IoCompleteRequest, он остается незавершенным.
Перед повторным выполнением запроса подпрограмма IoCompletion должна сбросить блок состояния ввода-вывода с STATUS_SUCCESS для параметра Состояние и ноль в поле Сведения, возможно, после сохранения возвращенных сведений об ошибке.
Для каждой попытки подпрограмма IoCompletion обычно уменьшает число повторных попыток, настроенное подпрограммой Dispatch. Как правило, подпрограмма IoCompletion должна вызывать IoCompleteRequest для сбоя IRP при сбое некоторого ограниченного числа повторных попыток.
Подпрограмма IoCompletion должна возвращать STATUS_MORE_PROCESSING_REQUIRED после вызова IoSetCompletionRoutine и IoCallDriver с IRP, который используется повторно или повторно используется.
Возврат STATUS_MORE_PROCESSING_REQUIRED из процедуры IoCompletion предотвратит обработку диспетчером операций ввода-вывода повторно использованного или повторно использованного IRP.
Если подпрограмме IoCompletion не удается завершить исходный IRP с помощью STATUS_SUCCESS, она должна оставить блок состояния ввода-вывода как возвращенный более низкими драйверами для повторного использования или повторной операции, которая приводит к сбою процедуры IoCompletion .
Если подпрограмма IoCompletion завершит исходный запрос с STATUS_PENDING, она должна вызвать IoMarkIrpPending с исходным IRP, прежде чем вызывать IoCompleteRequest.
Если подпрограмма IoCompletion должна завершиться сбоем исходного IRP с ошибкой STATUS_XXX, она может заносить ошибку в журнал. Однако основной драйвер устройства несет ответственность за то, чтобы регистрировать все возникающие ошибки ввода-вывода устройства, поэтому процедуры IoCompletion обычно не регистрируют ошибки.
Любой драйвер, который задает подпрограмму IoCompletion в IRP, а затем передает IRP вниз драйверу более низкого уровня, должен проверка флаг IRP-PendingReturned> в процедуре IoCompletion. Если флаг установлен, подпрограмма IoCompletion должна вызывать IoMarkIrpPending с IRP. Обратите внимание, однако, что драйвер, который передает IRP и затем ожидает события, не должен помечать IRP ожидающим. Вместо этого ее подпрограмма IoCompletion должна сигнализировать о событии и возвращать STATUS_MORE_PROCESSING_REQUIRED.
Подпрограмма IoCompletion должна освободить все ресурсы, выделенные подпрограммой диспетчеризации для обработки исходной IRP, предпочтительно до того, как подпрограмма IoCompletion вызовет IoCompleteRequest с исходным IRP и определенно до того, как подпрограмма IoCompletion вернет управление от завершения исходного IRP.
Если какой-либо драйвер более высокого уровня установил свою подпрограмму IoCompletion в исходном IRP, процедура IoCompletion этого драйвера не вызывается до тех пор, пока не будут вызваны подпрограммы IoCompletion всех драйверов более низкого уровня.
Обеспечение повышения приоритета в вызовах IoCompleteRequest
Если драйвер устройства самого низкого уровня может завершить IRP в своей процедуре диспетчеризации, он вызывает IoCompleteRequest с PriorityBoost IO_NO_INCREMENT. Увеличение приоритета во время выполнения не требуется, так как драйвер может предположить, что исходный инициатор запроса не ждал завершения операции ввода-вывода.
В противном случае драйвер самого низкого уровня предоставляет определяемое системой значение и значение типа устройства, которое повышает приоритет времени выполнения запрашивающей стороны, чтобы компенсировать время ожидания запроса на ввод-вывод устройства. Сведения о значениях повышения см. в разделе Wdm.h или Ntddk.h.
Драйверы более высокого уровня применяют тот же PriorityBoost , что и соответствующие базовые драйверы устройств при вызове IoCompleteRequest.
Эффект вызова IoCompleteRequest
Когда драйвер вызывает IoCompleteRequest, диспетчер ввода-вывода заполняет расположение стека ввода-вывода этого драйвера нулями перед вызовом следующего драйвера более высокого уровня, если таковой имеется, который настроил подпрограмму IoCompletion для вызова IRP.
Подпрограмма IoCompletion более высокого уровня драйвера может проверка только блок состояния IRP, чтобы определить, как все драйверы с более низким уровнем обрабатывали запрос.
Вызывающий объект IoCompleteRequest не должен пытаться получить доступ к только что завершенной IRP. Такая попытка является ошибкой программирования, которая вызывает сбой системы.