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


Разделение запросов на передачу DMA

Любому драйверу может потребоваться разделить запрос на передачу и выполнить несколько операций передачи DMA для удовлетворения заданного IRP в зависимости от следующего:

  • Число регистров карты, возвращаемых IoGetDmaAdapter

  • Байты передаваемых данных, содержащиеся в элементе Length в расположении стека ввода-вывода драйвера для IRP.

  • Количество границ страниц в физической памяти системы для буфера, в который или из которого драйвер передает данные

  • Ограничения на операции DMA драйвера, относящиеся к устройству. Например, системный драйвер диска "AT" должен разделить запросы на передачу для более чем 256 секторов из-за ограничений контроллера диска.

Драйвер может определить количество регистров карты, необходимых для передачи всех данных, заданных IRP, следующим образом:

  1. Вызовите MmGetMdlVirtualAddress, передав указатель на MDL по адресу Irp-MdlAddress>, чтобы получить начальный виртуальный адрес буфера. Обратите внимание, что драйвер не должен пытаться получить доступ к памяти с помощью этого виртуального адреса. Значение, возвращаемое MmGetMdlVirtualAddress , является индексом в MDL, а не обязательно допустимым адресом.

  2. Передайте возвращенный индекс и значение Length в расположении стека ввода-вывода драйвера ВП в макрос ADDRESS_AND_SIZE_TO_SPAN_PAGES .

Если значение, возвращаемое ADDRESS_AND_SIZE_TO_SPAN_PAGES , больше значения NumberOfMapRegisters , возвращаемого IoGetDmaAdapter, драйвер не может передать все запрошенные данные для этого IRP в одной операции DMA. Вместо этого он должен выполнять следующие действия.

  1. Разделите буфер на части, размер которых соответствует количеству доступных регистров карты (и любым ограничениям DMA, зависящим от устройства).

  2. Выполните столько операций DMA, сколько требуется для удовлетворения запроса на передачу.

Например, предположим, что ADDRESS_AND_SIZE_TO_SPAN_PAGES указывает, что для выполнения запроса на передачу требуется двенадцать регистров карты, но значение NumberOfMapRegisters , возвращаемое IoGetDmaAdapter , равно только пяти. (Предположим, нет ограничений DMA для конкретного устройства.) В этом случае драйвер должен выполнить три операции передачи DMA, три раза вызвав MapTransfer для передачи всех данных, запрошенных IRP.

Драйверы устройств DMA системы используют различные методы для разделения передачи DMA, когда регистров карты недостаточно для удовлетворения IRP с помощью одной операции ввода-вывода. Один из способов использования:

  1. Вызовите IoAllocateMdl , чтобы выделить MDL, описывающий часть буфера пользователя.

  2. Вызовите MmProbeAndLockPages , чтобы заблокировать эту часть пользовательского буфера.

  3. Передача данных для этой части буфера.

  4. Вызовите MmUnlockPages и выполните одно из следующих действий:

    • Если MDL, выделенного драйвером на шаге 1, достаточно велик для следующей части передачи, вызовите MmPrepareMdlForReuse и повторите шаги 2–4.
    • В противном случае вызовите IoFreeMdl и повторите шаги с 1 по 4.
  5. Вызовите MmUnlockPages и IoFreeMdl , когда все данные были переданы.

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

  1. Вызовите IoBuildSynchronousFsdRequest , чтобы выделить IRP с частичной передачей и заблокировать часть пользовательского буфера. Заблокированная область обычно кратна PAGE_SIZE или имеет размер в соответствии с пропускной способностью базового устройства.

  2. Вызовите IoCallDriver для частичной передачи IRP и вызовите KeWaitForSingleObject , чтобы дождаться, пока объект события, настроенный драйвером, будет связан с его частичной передачей IRP, если более низкие драйверы возвращают STATUS_PENDING.

  3. После восстановления управления повторите шаги 1 и 2, пока не будут переданы все данные, а затем завершите исходный IRP.

Когда драйвер класса хранения разделяет большие запросы на передачу для базовых драйверов портов или мини-портов SCSI, он выделяет дополнительный IRP для каждого элемента запроса на передачу. Он регистрирует подпрограмму IoCompletion для каждого выделенного драйвера IRP, чтобы отслеживать состояние полного запроса на передачу и освободить выделенные драйвером IRP. Затем они отправляются в драйвер порта с помощью IoCallDriver.

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

Монолитный драйвер (т. е. драйвер, не включающийся в пару "класс/порт") для устройства DMA должен разделять большие запросы на передачу. Такие драйверы обычно разбивают большой запрос на части и выполняют последовательность операций DMA для удовлетворения IRP.

Если запрос на передачу слишком велик для обработки базового драйвера устройства, драйвер более высокого уровня может вызвать MmGetMdlVirtualAddress и IoBuildPartialMdl, а затем настроить последовательность частичной передачи irP для базовых драйверов устройств.