Обеспечение прямого хода выполнения операций ввода-вывода
Некоторые драйверы, например драйверы хранилища для устройства подкачки системы, должны выполнять по крайней мере некоторые из поддерживаемых операций ввода-вывода без сбоев, чтобы избежать потери критически важных системных данных. Одной из потенциальных причин сбоя драйвера является нехватка памяти. Если платформа или драйвер не могут выделить достаточно памяти для обработки запроса ввода-вывода, может потребоваться выполнить запрос ввода-вывода, выполнив его со значением состояния ошибки.
В версиях KMDF до версии 1.9 платформа всегда завершается ошибкой запроса ввода-вывода, если она не может выделить объект запроса платформы для пакета запросов ввода-вывода (IRP), отправленного диспетчером ввода-вывода драйверу. Чтобы предоставить драйверам возможность обрабатывать запросы ввода-вывода в ситуациях с нехваткой памяти, версии 1.9 и более поздние версии платформы предоставляют гарантированную возможность прогресса для очередей ввода-вывода.
Эта возможность позволяет платформе и драйверу предварительно выделять память для наборов объектов запросов и связанных с запросами буферов контекста драйвера соответственно. Платформа и драйвер используют эту предварительно выделенную память только при низком объеме системной памяти.
Функции гарантированного прогресса
С помощью гарантированного прогресса платформы для очередей ввода-вывода драйвер может:
Попросите платформу предварительно выделить набор объектов запросов для использования с определенной очередью ввода-вывода во время нехватки памяти.
Предоставьте функцию обратного вызова, которая предварительно выделяет ресурсы для конкретных запросов, которые драйвер может использовать при получении предварительно выделенных объектов запросов от платформы в ситуациях нехватки памяти.
Предоставьте другую функцию обратного вызова, которая выделяет ресурсы для конкретного драйвера для запроса ввода-вывода, если не обнаружена ситуация с нехваткой памяти. Если выделение этой функции обратного вызова завершается сбоем из-за нехватки памяти, это может указать, должна ли платформа использовать один из своих предварительно выделенных объектов запроса.
Укажите, какие запросы ввода-вывода требуют использования предварительно выделенных объектов запросов. Возможные варианты включают использование предварительно выделенных объектов для всех irP, их использование только в том случае, если выполняется операция ввода-вывода подкачки, или дополнительная функция обратного вызова драйвера проверяет каждый IRP, чтобы определить, следует ли использовать предварительно выделенный объект.
Если драйвер реализует гарантированный прогресс для одной или нескольких своих очередей ввода-вывода, драйвер будет лучше обрабатывать запросы ввода-вывода в ситуациях с нехваткой памяти. Вы можете реализовать гарантированный прогресс вперед для очереди ввода-вывода устройства по умолчанию и для любой очереди ввода-вывода, настроенной драйвером, путем вызова WdfDeviceConfigureRequestDispatching.
Гарантированная возможность прогресса платформы работает для вашего драйвера, только если и драйвер, и целевые объекты ввода-вывода драйвера реализуют гарантированный прогресс вперед. Иными словами, если драйвер реализует гарантированный прогресс вперед для устройства, все драйверы более низкого уровня в стеке драйверов устройства также должны реализовывать гарантированный прогресс вперед.
Включение гарантированного прогресса вперед для очереди ввода-вывода
Чтобы обеспечить гарантированный прогресс для очереди ввода-вывода, драйвер инициализирует структуру WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY , а затем вызывает метод WdfIoQueueAssignForwardProgressPolicy . Если драйвер вызывает WdfDeviceConfigureRequestDispatching для настройки очереди ввода-вывода, он должен сделать это перед вызовом WdfIoQueueAssignForwardProgressPolicy.
Когда драйвер вызывает WdfIoQueueAssignForwardProgressPolicy, он может указать следующие три функции обратного вызова событий, все из которых являются необязательными:
EvtIoAllocateResourcesForReservedRequest
Функция обратного вызова EvtIoAllocateResourcesForReservedRequest драйвера выделяет и сохраняет ресурсы, относящиеся к запросу, для объектов запросов, резервируемых платформой в ситуациях с нехваткой памяти.
Платформа вызывает эту функцию обратного вызова при каждом создании зарезервированного объекта запроса. Драйвер должен выделить ресурсы, относящиеся к запросу, для одного запроса ввода-вывода, как правило, с помощью контекстного пространства зарезервированного объекта запроса.
EvtIoAllocateRequestResources
Функция обратного вызова EvtIoAllocateRequestResources драйвера выделяет ресурсы, относящиеся к запросу, для немедленного использования. Он вызывается сразу после того, как платформа получит IRP и создаст объект запроса для IRP.
Если попытка функции обратного вызова выделить ресурсы завершается сбоем, функция обратного вызова возвращает значение состояния ошибки. Затем платформа удаляет только что созданный объект запроса и использует один из зарезервированных объектов запроса. В свою очередь , обработчик запросов драйвера использует ресурсы для конкретных запросов, которые ранее были выделены функцией обратного вызова EvtIoAllocateRequestResources .
EvtIoWdmIrpForForwardProgress
Функция обратного вызова EvtIoWdmIrpForForwardProgress драйвера проверяет IRP и сообщает платформе, следует ли использовать зарезервированный объект запроса для IRP или завершить запрос ввода-вывода, завершив его значением состояния ошибки.
Платформа вызывает эту функцию обратного вызова только в том случае, если платформе не удается создать новый объект запроса и вы указали (установив флаг в структуре WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY драйвера), что требуется, чтобы драйвер проверял irPs во время нехватки памяти. Другими словами, ваш драйвер может оценить каждое IRP и решить, нужно ли обрабатывать его даже в ситуациях с нехваткой памяти.
Когда драйвер вызывает WdfIoQueueAssignForwardProgressPolicy, он также указывает количество зарезервированных объектов запроса, которые платформа будет предварительно выделять для ситуаций с нехваткой памяти. Вы можете выбрать количество объектов запроса, подходящих для вашего устройства и драйвера. Чтобы избежать снижения производительности, драйвер обычно должен указывать число, которое приблизительно соответствует количеству запросов ввода-вывода, которые драйвер и устройство могут обрабатывать параллельно.
Однако если вызов драйвера к WdfIoQueueAssignForwardProgressPolicy и функция обратного вызова EvtIoAllocateResourcesForReservedRequest предварительно выделяют слишком много зарезервированных объектов запросов или слишком много памяти ресурсов, относящихся к запросу, ваш драйвер может на самом деле способствовать ситуации нехватки памяти, которые вы пытаетесь обработать. Необходимо протестировать производительность драйвера и устройства, а также включить имитации с низким объемом памяти, чтобы определить оптимальные показатели.
Перед возвратом WdfIoQueueAssignForwardProgressPolicy платформа создает и резервирует количество объектов запроса, указанное драйвером. Каждый раз, когда она резервирует объект запроса, платформа немедленно вызывает функцию обратного вызова EvtIoAllocateResourcesForReservedRequest драйвера, чтобы драйвер мог выделить и сохранить ресурсы для конкретных запросов, если платформа фактически использует зарезервированные объекты запроса.
Когда один из обработчиков запросов драйвера получает запрос ввода-вывода из очереди ввода-вывода, он может вызвать метод WdfRequestIsReserved , чтобы определить, является ли объект запроса объектом запроса, предварительно выделенным платформой для ситуаций с нехваткой памяти. Если этот метод возвращает значение TRUE, драйвер должен использовать ресурсы, зарезервированные функцией обратного вызова EvtIoAllocateResourcesForReservedRequest .
Если платформа использует один из своих зарезервированных объектов запроса, она возвращает объект в свой набор зарезервированных объектов после того, как драйвер завершит запрос. Платформа сохраняет объект запроса и все контекстное пространство, созданное драйвером путем вызова WdfDeviceInitSetRequestAttributes или WdfObjectAllocateContext, для повторного использования в случае другой ситуации с нехваткой памяти.
Как платформа и драйвер поддерживают гарантированный прогресс вперед
Ниже приведены шаги, которые выполняются драйвером и платформой для поддержки гарантированного прогресса вперед для очереди ввода-вывода.
Драйвер вызывает WdfIoQueueAssignForwardProgressPolicy.
В ответ платформа выделяет и сохраняет количество объектов запроса, указанное драйвером. Если драйвер ранее назывался WdfDeviceInitSetRequestAttributes, каждое выделение включает в себя контекстное пространство, указанное WdfDeviceInitSetRequestAttributes .
Кроме того, если драйвер предоставил функцию обратного вызова EvtIoAllocateResourcesForReservedRequest , платформа вызывает функцию обратного вызова каждый раз, когда выделяет и сохраняет объект запроса.
Платформа получает пакет запроса ввода-вывода (IRP), который диспетчер ввода-вывода отправляет драйверу.
Платформа пытается выделить объект запроса для IRP. Если очередь ввода-вывода, созданная драйвером для типа запроса, поддерживает гарантированное продвижение вперед, следующий шаг зависит от того, будет ли выделение успешным или сбоем.
Выделение объекта запроса выполнено успешно.
Если драйвер предоставил функцию обратного вызова EvtIoAllocateRequestResources , платформа вызывает ее. Если функция обратного вызова возвращает STATUS_SUCCESS, платформа добавляет запрос в очередь ввода-вывода. Если функция обратного вызова возвращает значение состояния ошибки, платформа удаляет только что созданный объект запроса и использует один из предварительно выделенных объектов запроса. Когда обработчик запросов драйвера получает объект запроса, он определяет, был ли объект запроса предварительно выделен и, следовательно, должен ли он использовать предварительно выделенные ресурсы драйвера.
Если драйвер не предоставил функцию обратного вызова EvtIoAllocateRequestRequestResources , платформа добавляет запрос в очередь ввода-вывода так же, как если бы драйвер не включил гарантированный прогресс вперед.
Сбой выделения объекта запроса.
Дальнейшие действия платформы зависят от значения, предоставленного драйвером для элемента ForwardProgressReservedPolicyструктуры WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY . Этот член информирует платформу о том, когда следует использовать зарезервированный запрос: всегда, только если запрос ввода-вывода является операцией ввода-вывода по страницам или только в том случае, если функция обратного вызова EvtIoWdmIrpForwardProgress указывает, что следует использовать зарезервированный запрос.
Во всех случаях обработчики запросов драйвера могут вызывать WdfRequestIsReserved , чтобы определить, использовала ли платформа зарезервированный объект запроса. В этом случае драйвер должен использовать ресурсы запроса, выделенные его функцией обратного вызова EvtIoAllocateResourcesForReservedRequest .
Сценарий гарантированного переадресации
Вы пишете драйвер для запоминающего устройства, который может содержать файл подкачки системы. Важно, чтобы операции чтения и записи в файл подкачки были успешными.
Вы решили создать отдельные очереди ввода-вывода для операций чтения и записи, а также обеспечить гарантированный прогресс для обеих этих очередей ввода-вывода. Вы решили создать третью очередь ввода-вывода для всех остальных типов запросов без включения гарантированного прогресса вперед.
Стек драйверов и устройство могут обрабатывать четыре операции записи параллельно, поэтому перед вызовом WdfIoQueueAssignForwardProgressRequests структуры WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY задайте значение 4 перед вызовом WdfIoQueueAssignForwardProgressPolicy.
Вы решаете, что обеспечение прогресса важно только в том случае, если устройство драйвера является устройством разбиения по страницам, поэтому драйвер задает член ForwardProgressReservedPolicy структуры WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY wdfIoForwardProgressReservedPolicyPagingIO.
Так как драйверу требуется объект памяти платформы для каждого запроса на чтение и запись, вы решаете, что драйвер должен предварительно выделить некоторые объекты памяти для использования для вызовов WdfIoTargetFormatRequestForRead и WdfIoTargetFormatRequestForWrite в ситуациях с нехваткой памяти.
Таким образом, драйвер предоставляет функцию обратного вызова EvtIoAllocateResourcesForReservedRequest для очереди чтения и еще одну функцию для очереди записи. Каждый раз, когда платформа вызывает одну из этих функций обратного вызова, функция обратного вызова вызывает WdfMemoryCreate и сохраняет возвращенный дескриптор объекта в ситуациях с нехваткой памяти. Так как функция обратного вызова получает дескриптор предварительно выделенного объекта запроса, она может родительский объект памяти для объекта запроса. (Драйвер для устройства DMA также может предварительно выделить объекты DMA платформы.)
Обработчики запросов для очередей чтения и записи должны определить, является ли каждый полученный объект запроса тем, что платформа зарезервирована для ситуаций с нехваткой памяти. Обработчик запроса может вызывать WdfRequestIsReserved или сравнивать дескриптор объекта запроса с дескриптором, полученным ранее функцией обратного вызова EvtIoAllocateResourcesForReservedRequest .
Драйвер также предоставляет функцию обратного вызова EvtIoAllocateRequestResources для очереди чтения и еще одну функцию для очереди записи. Платформа вызывает одну из этих функций обратного вызова, когда получает запрос на чтение или запись от диспетчера ввода-вывода и успешно создает объект запроса. Каждая из этих функций обратного вызова вызывает WdfMemoryCreate , чтобы выделить объект памяти для запроса. Если выделение не удается, функция обратного вызова возвращает значение состояния ошибки, чтобы уведомить платформу о ситуации с нехваткой памяти. Платформа, обнаруживая возвращаемое значение ошибки, удаляет только что созданный объект запроса и использует один из предварительно выделенных объектов.
Этот драйвер не предоставляет функцию обратного вызова EvtIoWdmIrpForForwardProgress , так как ему не нужно проверять отдельные irP для чтения или записи, прежде чем платформа добавит их в очередь ввода-вывода.
Помните, что когда драйвер реализует гарантированный прогресс вперед для устройства, все драйверы нижнего уровня в стеке драйверов устройства также должны реализовывать гарантированный прогресс вперед.