Поделиться через


Реализация процедуры IoCompletion

При входе подпрограмма IoCompletion получает указатель Контекста. Когда подпрограмма отправки вызывает IoSetCompletionRoutine, она может предоставить указатель Context. Этот указатель может ссылаться на любые сведения контекста, которые требуются подпрограмме IoCompletion для обработки IRP. Область контекста не может быть разделяемой на страницы, так как подпрограмма IoCompletion может вызываться на уровне IRQL = DISPATCH_LEVEL.

Рассмотрим следующие рекомендации по реализации для подпрограмм IoCompletion:

  • Подпрограмма IoCompletion может проверить блок состояния ввода-вывода IRP, чтобы определить результат операции ввода-вывода.

  • Если подпрограмма отправки выделила входной IRP с помощью IoAllocateIrp или IoBuildAsynchronousFsdRequest, подпрограмма IoCompletion должна вызвать IoFreeIrp, чтобы освободить IRP, желательно до завершения исходного IRP.

    • Подпрограмма IoCompletion должна освободить все ресурсы, выделенные для IRP, который был выделен драйвером, предпочтительно до освобождения соответствующего IRP.

      Например, если подпрограмма отправки выделяет MDL с помощью IoAllocateMdl и вызывает IoBuildPartialMdl для частичной передачи IRP, то подпрограмма завершения IoCompletion должна освободить MDL с помощью IoFreeMdl. Если система выделяет ресурсы для поддержания состояния исходного IRP, она должна освободить эти ресурсы желательно перед вызовом IoCompleteRequest с исходным IRP и обязательно до возвращения контроля.

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

    • Если подпрограмма IoCompletion не может завершить исходный IRP с STATUS_SUCCESS, он должен задать блок состояния ввода-вывода в исходном IRP значение, возвращаемое в выделенном драйвером IRP, которое привело к сбою исходного запроса.

    • Если подпрограмма 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 обычно уменьшает количество повторных попыток, настроенных подпрограммой отправки. Как правило, подпрограмма IoCompletion должна вызывать IoCompleteRequest, чтобы завершить IRP с ошибкой, когда ограниченное число повторных попыток завершились неудачно.

    • Подпрограмма IoCompletion должна возвращать STATUS_MORE_PROCESSING_REQUIRED после вызова IoSetCompletionRoutine и IoCallDriver с IRP, которая повторно используется или повторяется.

      Возвращение STATUS_MORE_PROCESSING_REQUIRED из процедуры IoCompletion приостанавливает выполнение обработчиком ввода-вывода завершение обработки повторно использованного или возобновленного IRP.

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