Compartilhar via


DMA de dispersão/coleta de NDIS

Cuidado

Para processadores Arm e Arm64, é altamente recomendável que os gravadores de driver NDIS usem dma WDF ou DMA WDM em vez de DMA de dispersão/coleta de NDIS.

Para obter mais informações sobre o AMD WDF, consulte Manipulando operações de DMA em drivers KMDF.

Para obter mais informações sobre o AMD do WDM, consulte os tópicos filho relacionados ao DMA de Gerenciamento de entrada/saída para drivers.

Os drivers de miniporto NDIS podem usar o método SGDMA (DMA de dispersão/coleta) para transferir dados entre uma NIC e a memória do sistema. Uma transferência de AMD bem-sucedida requer que o endereço físico dos dados esteja em um intervalo de endereços compatível com a NIC. O HAL fornece um mecanismo para que os drivers obtenham a lista de endereços físicos para uma cadeia de MDL e, se necessário, armazenarão os dados em buffer duplo em um intervalo de endereços físico.

Em versões do NDIS anteriores ao NDIS 6.0, o suporte a SGDMA em drivers de miniporto e NDIS é limitado em alguns aspectos e, em particular, não funciona bem em um cenário de envio de vários pacotes. O suporte ao SGDMA do NDIS 6.0 supera essas limitações, fornecendo uma interface simples para drivers de miniporta.

Histórico do SGDMA do NDIS

Em versões do NDIS anteriores ao NDIS 6.0, o NDIS obtém uma lista de SG (dispersão) para cada pacote antes de enviar o pacote para o driver de miniporto. O NDIS também lida com o caso em que a tentativa original de obter a lista de SGs falha devido à fragmentação excessiva. Nesse caso, o NDIS armazena o pacote em buffer duplo em um buffer contíguo e tenta novamente. HAL também pode fazer buffer duplo dos dados em um endereço físico ao qual a NIC dá suporte se, por exemplo, o endereço físico dos dados estiver acima do máximo de 32 bits e a NIC não oferecer suporte ao DMA de 64 bits.

Para evitar uma situação de deadlock, o NDIS obtém uma lista de SGs para um pacote e envia um pacote por vez. Se o NDIS tentar mapear todos os pacotes antes de enviá-los para o driver de miniporto, o sistema poderá esgotar os recursos. Nesse caso, o NDIS estaria aguardando os registros de mapa ficarem disponíveis enquanto alguns registros de mapa são bloqueados para os pacotes que não foram enviados. Os pacotes bloqueados não podem ser reutilizados.

Essa abordagem para o suporte ao SGDMA tem as seguintes limitações:

  • Como o pacote é mapeado antes de chegar ao driver de miniporto, o driver não pode otimizar para pacotes pequenos ou pacotes muito fragmentados. O driver de miniporta não pode fazer buffer duplo do pacote em um endereço físico conhecido.

  • Não há nenhuma garantia de que a matriz de endereços físicos que o NDIS passou para o driver de miniporto mapeia para o endereço virtual dos dados originais. Portanto, se o driver alterar os dados no endereço virtual na cadeia de MDL antes de enviá-los, as modificações feitas nos dados não serão refletidas nos dados nos endereços físicos. Nesse caso, a NIC envia os dados não modificados.

  • O NDIS está limitado a enviar um pacote por vez para evitar um deadlock devido a problemas de recursos. Isso não é tão eficiente quanto enviar vários pacotes.

  • Como o NDIS não pode determinar os recursos de transmissão de drivers de miniporto, ele não pode pré-alocar o armazenamento para um buffer de lista de SG. Portanto, o NDIS deve alocar o armazenamento necessário em tempo de execução. Isso não é tão eficiente quanto pré-alocar o armazenamento.

  • As funções HAL que alocam uma lista de SG devem ser chamadas em IRQL = DISPATCH_LEVEL. O NDIS não tem as informações de IRQL atuais, portanto, ele precisa definir o IRQL como DISPATCH_LEVEL mesmo que já esteja em DISPATCH_LEVEL. Isso não será eficiente se o IRQL já estiver em DISPATCH_LEVEL.

Benefícios do suporte ao SGDMA do NDIS

Na interface SGDMA NDIS 6.0 e posterior, o NDIS não mapeia o buffer de dados antes de enviá-lo para o driver de miniporta. Em vez disso, o NDIS fornece uma interface para o driver mapear os dados de rede.

Essa abordagem gera os seguintes benefícios:

  • Como o NDIS fornece a interface para HAL para mapear os dados de rede, o NDIS protege os drivers de miniporta da complexidade e detalhes do processo de mapeamento.

  • Os drivers de miniporta têm acesso aos dados antes de serem mapeados. Portanto, todas as alterações feitas nos dados originais são refletidas nos dados representados pela lista de SG, mesmo que o NDIS ou o HAL faça buffers duplos dos dados.

  • Os drivers de miniport podem otimizar a transmissão de pacotes pequenos ou altamente fragmentados copiando-os para um buffer pré-alocado com um endereço físico conhecido. Essa abordagem evita o mapeamento que não é necessário e, portanto, melhora o desempenho do sistema.

  • O NDIS pode enviar vários buffers para o driver de miniporta com segurança. Isso resulta em menos chamadas para miniportar drivers e, portanto, melhora o desempenho do sistema.

  • Os drivers de miniport podem pré-alocar a memória para uma lista de SG como parte dos blocos de descritor de transmissão. Portanto, os drivers NDIS ou miniport não são necessários para alocar memória para listas de SG em tempo de execução.

  • Como os drivers de miniporta podem estar em execução em IRQL = DISPATCH_LEVEL, os drivers de miniport podem evitar chamadas desnecessárias para elevar o IRQL para DISPATCH_LEVEL. Por exemplo, como a conclusão de um envio ocorre no contexto de um DPC de interrupção, os drivers de miniporto podem liberar a lista de SG sem gerar o IRQL.

Registrando e desregistrando canais de DMA

Um driver de miniporto NDIS chama a função NdisMRegisterScatterGatherDma de sua função MiniportInitializeEx para registrar um canal DMA com NDIS.

O driver de miniporta passa uma descrição de DMA para NdisMRegisterScatterGatherDma no parâmetro DmaDescription . NdisMRegisterScatterGatherDma retorna um tamanho para o buffer que deve ser grande o suficiente para manter a lista de dispersão/coleta. Os drivers de miniporto devem usar esse tamanho para pré-alocar o armazenamento para listas de dispersão/coleta.

O driver de miniport também passa NdisMRegisterScatterGatherDma os pontos de entrada para as funções MiniportXxx que o NDIS chama para processar a lista de dispersão/coleta. O NDIS chama a função MiniportProcessSGList do driver de miniport depois que HAL criou a lista de dispersão/coleta para um buffer. NdisMRegisterScatterGatherDma fornece um identificador no parâmetro pNdisMiniportDmaHandle , que o driver de miniporto deve usar em chamadas subsequentes para funções de DMA de dispersão/coleta de NDIS.

Um driver de miniporto NDIS chama a função NdisMDeregisterScatterGatherDma de sua função MiniportHaltEx para liberar recursos de dispersão/coleta de DMA.

Alocando e liberando listas de dispersão/coleta

Um driver de miniporto NDIS chama a função NdisMAllocateNetBufferSGList em sua função MiniportSendNetBufferLists . O driver de miniporto chama NdisMAllocateNetBufferSGList uma vez para cada estrutura NET_BUFFER que ele deve mapear. Depois que os recursos estiverem disponíveis e o HAL tiver a lista de SG pronta, o NDIS chamará a função MiniportProcessSGList do driver. O NDIS pode chamar MiniportProcessSGList antes ou depois que a chamada do driver de miniporto para NdisMAllocateNetBufferSGList retorna.

Para melhorar o desempenho do sistema, a lista de dispersão/coleta é gerada a partir dos dados de rede que começam no início do MDL especificado no membro CurrentMdl da estrutura de NET_BUFFER_DATA associada. O início dos dados de rede na lista SG é deslocado do início da lista SG pelo valor especificado no membro CurrentMdlOffset da estrutura de NET_BUFFER_DATA associada.

Ao manipular um DPC para uma interrupção de envio completo e depois que o driver de miniporto não precisar mais da lista de SG, o driver de miniporto deve chamar a função NdisMFreeNetBufferSGList para liberar a lista de SGs.

Nota Não chame NdisMFreeNetBufferSGList enquanto o driver ou hardware ainda estiver acessando a memória descrita pela estrutura NET_BUFFER associada à lista de dispersão/coleta. 

Antes de acessar os dados recebidos, os drivers de miniport devem chamar NdisMFreeNetBufferSGList para liberar o cache de memória.