Compartilhar via


Dividindo solicitações de transferência de DMA

Qualquer driver pode precisar dividir uma solicitação de transferência e executar mais de uma operação de transferência de DMA para atender a um determinado IRP, dependendo do seguinte:

  • O número de registros de mapa retornados por IoGetDmaAdapter

  • Os bytes de dados a serem transferidos, contidos no membro Length do local da pilha de E/S do driver para o IRP

  • O número de limites de página, na memória física do sistema, para o buffer no qual ou do qual o driver deve transferir dados

  • Restrições específicas do dispositivo nas operações de DMA do driver. Por exemplo, o driver de disco "AT" do sistema deve dividir as solicitações de transferência para mais de 256 setores devido às limitações do controlador de disco.

Um driver pode determinar o número de registros de mapa necessários para transferir todos os dados especificados por um IRP da seguinte maneira:

  1. Chame MmGetMdlVirtualAddress, passando um ponteiro para o MDL em Irp-MdlAddress>, para obter o endereço virtual inicial do buffer. Observe que um driver não deve tentar acessar a memória usando esse endereço virtual. O valor retornado por MmGetMdlVirtualAddress é um índice no MDL, não necessariamente um endereço válido.

  2. Passe o índice retornado e o valor de Length no local da pilha de E/S do driver do IRP para a macro ADDRESS_AND_SIZE_TO_SPAN_PAGES .

Se o valor retornado por ADDRESS_AND_SIZE_TO_SPAN_PAGES for maior que o valor NumberOfMapRegisters retornado por IoGetDmaAdapter, o driver não poderá transferir todos os dados solicitados para esse IRP em uma única operação de DMA. Em vez disso, ele deve fazer o seguinte:

  1. Divida o buffer em partes dimensionadas para se adequar ao número de registros de mapa disponíveis (e quaisquer restrições de DMA específicas do dispositivo).

  2. Execute quantas operações de AMD forem necessárias para atender à solicitação de transferência.

Por exemplo, suponha que ADDRESS_AND_SIZE_TO_SPAN_PAGES indica que doze registros de mapa são necessários para atender a uma solicitação de transferência, mas o valor NumberOfMapRegisters retornado por IoGetDmaAdapter é de apenas cinco. (Suponha que não haja restrições de DMA específicas do dispositivo.) Nesse caso, o driver deve executar três operações de transferência de AMD, chamando MapTransfer três vezes para transferir todos os dados solicitados pelo IRP.

Os drivers de dispositivo DMA do sistema usam várias técnicas para dividir uma transferência de DMA quando não há registros de mapa suficientes para atender a um IRP com uma única operação de E/S. Uma técnica a ser usada é a seguinte:

  1. Chame IoAllocateMdl para alocar um MDL que descreve uma parte do buffer do usuário.

  2. Chame MmProbeAndLockPages para bloquear essa parte do buffer do usuário.

  3. Transfira os dados para essa parte do buffer.

  4. Chame MmUnlockPages e siga um destes procedimentos:

    • Se o MDL alocado pelo driver na etapa 1 for grande o suficiente para a próxima parte da transferência, chame MmPrepareMdlForReuse e repita as etapas 2 a 4.
    • Caso contrário, chame IoFreeMdl e repita as etapas de 1 a 4.
  5. Chame MmUnlockPages e IoFreeMdl quando todos os dados forem transferidos.

Se um driver de nível mais alto não puder bloquear todo o buffer de usuário com MmProbeAndLockPages em um computador com memória limitada, ele poderá fazer o seguinte:

  1. Chame IoBuildSynchronousFsdRequest para alocar um IRP de transferência parcial e bloquear uma parte do buffer do usuário. A área bloqueada geralmente é um múltiplo de PAGE_SIZE ou é dimensionada para atender à capacidade de transferência do dispositivo subjacente.

  2. Chame IoCallDriver para o IRP de transferência parcial e chame KeWaitForSingleObject para aguardar um objeto de evento que o driver configurou para ser associado ao IRP de transferência parcial, se os drivers inferiores retornarem STATUS_PENDING.

  3. Quando ele recuperar o controle, repita as etapas 1 e 2 até que todos os dados sejam transferidos e, em seguida, conclua o IRP original.

Quando um driver de classe de armazenamento divide grandes solicitações de transferência para drivers de porta/miniport scsi subjacentes, ele aloca um IRP adicional para cada parte da solicitação de transferência. Ele registra uma rotina IoCompletion para cada IRP alocado por driver, para acompanhar o status da solicitação de transferência completa e liberar os IRPs alocados pelo driver. Em seguida, ele envia esses IRPs para o driver de porta usando IoCallDriver.

Outros drivers de classe/porta só poderão usar essa técnica se o driver de classe puder determinar quantos registros de mapa estão disponíveis para o driver de porta. O driver de porta deve armazenar essas informações de configuração no registro para o driver de classe emparelhado ou os drivers emparelhados devem definir uma interface privada, usando solicitações de controle de E/S internas do dispositivo, para passar informações de configuração sobre o número de registros de mapa disponíveis do driver de porta para o driver de classe.

Um driver monolítico (ou seja, um driver que não faz parte de um par de classes/portas) para um dispositivo DMA deve dividir grandes solicitações de transferência para si mesmo. Esses drivers geralmente dividem uma solicitação grande em partes e executam uma sequência de operações de AMD para atender ao IRP.

Se uma solicitação de transferência for muito grande para o driver de dispositivo subjacente manipular, um driver de nível superior poderá chamar MmGetMdlVirtualAddress e IoBuildPartialMdl e configurar uma sequência de IRPs de transferência parcial para drivers de dispositivo subjacentes.