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


Обеспечение хода выполнения операций ввода-вывода

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

В версиях KMDF до версии 1.9 платформа всегда завершает запрос ввода-вывода, если он не может выделить объект запроса платформы для пакета запросов ввода-вывода ( IRP), который диспетчер операций ввода-вывода отправил драйверу. Чтобы предоставить драйверам возможность обрабатывать запросы ввода-вывода в режиме нехватки памяти, начиная с версии 1.9 и более поздних фреймворка, обеспечивается гарантированная возможность выполнения для очередей ввода-вывода.

Эта возможность позволяет платформе и драйверу предварительно выделить память для наборов объектов запросов и буферов контекста драйвера, связанных с запросами, соответственно. Платформа и драйвер используют эту предварительнораспределированную память только в том случае, если объем системной памяти невысок.

Особенности гарантированного продвижения работы

С помощью механизма гарантированного продвижения для очередей ввода-вывода драйвер может:

  • Попросите платформу предварительно выделить набор объектов запроса для использования с определенной очередью ввода-вывода во время ситуаций с низкой памятью.

  • Предоставьте функцию обратного вызова, которая предварительно выделяет ресурсы для конкретного запроса, которые драйвер может использовать при получении предварительно выделенных объектов запросов из платформы во время ситуаций с низкой памятью.

  • Укажите другую функцию обратного вызова, которая выделяет ресурсы драйвера для запроса ввода-вывода, когда ситуация с низким уровнем памяти не обнаружена. Если выделение функции обратного вызова завершается сбоем из-за нехватки памяти, она может указать, должен ли фреймворк использовать один из своих предварительно выделенных объектов запроса.

  • Укажите, какие запросы ввода-вывода требуют использования предварительно выделенных объектов запроса. Варианты включают использование предварительно выделенных объектов для всех irPs, используя их только в том случае, если выполняется операция ввода-вывода на страницы или дополнительная функция обратного вызова драйвера проверяет каждый 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), что требуется, чтобы драйвер проверял IRP в условиях дефицита памяти. Другими словами, ваш драйвер может оценить каждый IRP и решить, должен ли он обрабатывать его даже во время ситуаций с низкой памятью.

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

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

Прежде чем WdfIoQueueAssignForwardProgressPolicy возвращается, платформа создает и резервирует количество объектов запроса, указанных драйвером. Каждый раз, когда рамочная система резервирует объект запроса, она немедленно вызывает функцию обратного вызова драйвера EvtIoAllocateResourcesForReservedRequest. Драйвер может использовать эту функцию обратного вызова для выделения и сохранения ресурсов, относящихся к запросу, если платформа фактически использует зарезервированные объекты запроса.

Когда один из обработчиков запросов драйвера получает запрос ввода-вывода из очереди ввода-вывода, он может вызвать метод WdfRequestIsReserved, чтобы определить, предварительно ли платформа перераспределила объект запроса для ситуаций с низкой памятью. Если этот метод возвращает TRUE, драйвер должен использовать ресурсы, которые зарезервировала функция обратного вызова EvtIoAllocateResourcesForReservedRequest.

Если платформа использует один из своих зарезервированных объектов запроса, он возвращает объект в его набор зарезервированных объектов после завершения запроса драйвером. Платформа сохраняет объект запроса и любое пространство контекста, созданное драйвером, вызывая WdfDeviceInitSetRequestAttributes или WdfObjectAllocateContext, для повторного использования, если возникает другая ситуация с низкой памятью.

Как фреймворк и поддержка драйверов гарантируют прогресс

Ниже описано, как драйвер и фреймворк поддерживают гарантированный прогресс для очереди ввода-вывода.

  1. Драйвер вызывает WdfIoQueueAssignForwardProgressPolicy.

    В ответ платформа выделяет и сохраняет количество объектов запроса, указанного драйвером. Если драйвер ранее вызывал WdfDeviceInitSetRequestAttributes, каждое выделение включает пространство контекста, которое указано в WdfDeviceInitSetRequestAttributes.

    Если драйвер предоставил функцию обратного вызова EvtIoAllocateResourcesForReservedRequest, фреймворк вызывает ее каждый раз, когда выделяет и сохраняет объект запроса.

  2. Платформа получает пакет запроса ввода-вывода (IRP), который диспетчер ввода-вывода отправляет драйверу.

    Платформа пытается выделить объект запроса для IRP. Если очередь ввода-вывода, созданная драйвером для типа запроса, поддерживает гарантированное продвижение вперед, следующий шаг зависит от того, успешно или неудачно прошло выделение:

    • Выделение объекта запроса успешно выполнено.

      Если драйвер предоставил функцию обратного вызова EvtIoAllocateRequestResources, платформа вызывает ее. Если функция обратного вызова возвращает STATUS_SUCCESS, платформа добавляет запрос в очередь ввода-вывода. Если функция обратного вызова возвращает значение состояния ошибки, платформа удаляет объект запроса, который он только что создал и использует один из его предварительно выделенных объектов запроса. Когда обработчик запроса драйвера получает объект запроса, он определяет, был ли объект запроса предварительно размещен и поэтому должен ли он использовать предварительнораспределированные ресурсы драйвера.

      Если драйвер не предоставил функцию обратного вызова EvtIoAllocateRequestResources, фреймворк добавляет запрос в очередь ввода-вывода, так же, как если бы драйвер не обеспечил гарантированного продвижения вперед.

    • Ошибка выделения объекта запроса.

      Что фреймворк делает далее, зависит от значения, предоставленного драйвером для элемента ForwardProgressReservedPolicy структуры WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY. Этот член сообщает фреймворку, когда следует использовать зарезервированный запрос: всегда, только если запрос ввода-вывода является операцией постраничного ввода-вывода, или только в том случае, если функция обратного вызова EvtIoWdmIrpForForwardProgress указывает, что зарезервированный запрос должен использоваться.

    Во всех случаях обработчики запросов драйвера могут вызывать WdfRequestIsReserved, чтобы определить, использовала ли платформа зарезервированный объект запроса. Если платформа использовала зарезервированный объект запроса, драйвер должен использовать ресурсы запроса, которые были выделены функцией обратного вызова EvtIoAllocateResourcesForReservedRequest.

Сценарий гарантированного продвижения вперёд

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

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

Стек драйверов и устройство могут параллельно обрабатывать четыре операции записи, поэтому перед вызовом WdfIoQueueAssignForwardProgressPolicyустановите член TotalForwardProgressRequests структуры WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY равным 4.

Вы решаете, что обеспечение поступательного прогресса важно только в том случае, если устройство вашего драйвера - это пейджинговое устройство, поэтому драйвер устанавливает член ForwardProgressReservedPolicy структуры WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY на WdfIoForwardProgressReservedPolicyPagingIO.

Так как драйверу требуется объект памяти платформы для каждого запроса на чтение и запись, вы решите, что драйвер должен предварительно выделить некоторые объекты памяти для вызова WdfIoTargetFormatRequestForRead и WdfIoTargetFormatRequestForWrite в ситуациях с низкой памятью.

Поэтому драйвер предоставляет функцию обратного вызова EvtIoAllocateResourcesForReservedRequest для очереди чтения и другой для очереди записи. Каждый раз, когда платформа вызывает одну из этих функций обратного вызова, функция обратного вызова вызывает WdfMemoryCreate и сохраняет возвращенный дескриптор объекта для ситуаций с низкой памятью. Поскольку функция обратного вызова получает дескриптор предварительно выделенного объекта запроса, она может сделать объект памяти дочерним объектом запроса. (Драйвер для устройства DMA также может предварительно выделить объекты DMA платформы .)

Обработчики запросов для очередей чтения и записи должны определить, является ли каждый полученный объект запроса таким, который фреймворк зарезервировал для случаев нехватки памяти. Обработчик запросов может вызывать WdfRequestIsReservedили сравнить дескриптор объекта запроса с теми, которые ранее полученные EvtIoAllocateResourcesForReservedRequest функции обратного вызова.

Драйвер также предоставляет EvtIoAllocateRequestResources функцию обратного вызова для очереди чтения и другую для очереди записи. Платформа вызывает одну из этих функций обратного вызова при получении запроса на чтение или запись из диспетчера операций ввода-вывода и успешно создает объект запроса. Каждая из этих функций обратного вызова вызывает WdfMemoryCreate, чтобы выделить объект памяти для запроса. Если выделение завершается ошибкой, функция обратного вызова возвращает значение состояния ошибки, чтобы уведомить фреймворк о возникновении события нехватки памяти. Платформа, обнаруживая возвращаемое значение ошибки, удаляет объект запроса, который он только что создал и использует один из его предварительно выделенных объектов.

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

Когда драйвер реализует гарантированное продвижение вперёд для устройства, все драйверы нижнего уровня в стеке драйверов устройства также должны реализовать гарантированное продвижение вперёд.