Modo de sondagem do NDIS
Visão geral do modo de pesquisa NDIS
O Modo de Sondagem NDIS é um modelo de execução de sondagem controlado pelo sistema operacional que orienta o caminho de dados da interface de rede.
Anteriormente, o NDIS não tinha uma definição formal de um contexto de execução de caminho de dados. Os drivers NDIS normalmente dependiam de DPCs (Chamadas de Procedimento Diferido) para implementar seu modelo de execução. No entanto, o uso de DPCs pode sobrecarregar o sistema quando longas cadeias de indicação são feitas e evitar esse problema requer muito código que é difícil de acertar. O modo de pesquisa NDIS oferece uma alternativa aos DPCs e ferramentas de execução semelhantes.
O Modo de Pesquisa NDIS move a complexidade das decisões de agendamento dos drivers NIC para o NDIS, onde o NDIS define limites de trabalho por iteração. Para conseguir isso, o Modo de Enquete fornece:
Um mecanismo para o sistema operacional exercer pressão de retorno na placa de rede.
Um mecanismo para o sistema operacional controlar com precisão as interrupções.
O Modo de Sondagem NDIS está disponível para drivers de miniporto NDIS 6.85 e posteriores.
Problemas com o modelo DPC
O diagrama de sequência a seguir ilustra um exemplo típico de como um driver de miniporto NDIS lida com uma intermitência de pacotes Rx usando um DPC. Neste exemplo, o hardware é padrão em termos de NICs PCIe. Ele tem uma fila de hardware de recepção e uma máscara de interrupção para essa fila.
Quando não há atividade de rede, o hardware tem a interrupção Rx habilitada. Quando um pacote Rx chega:
O hardware gera uma interrupção e o NDIS chama a função MiniportInterrupt (ISR) do driver.
O driver faz muito pouco trabalho no ISR porque eles são executados em um IRQL muito alto. O driver desabilita a interrupção do ISR e adia o processamento de hardware para uma função MiniportInterruptDPC (DPC).
O NDIS eventualmente chama o DPC do driver e o driver drena todas as conclusões da fila de hardware e as indica para o sistema operacional.
Dois pontos problemáticos podem afetar a pilha de rede quando o driver adia as operações de E/S para um DPC:
O driver não sabe se o sistema é capaz de processar todos os dados que estão sendo indicados, portanto, o driver não tem escolha a não ser drenar o máximo possível de elementos de sua fila de hardware e indicá-los na pilha.
Como o driver está usando um DPC para adiar o trabalho de seu ISR, todas as indicações são feitas em DISPATCH_LEVEL. Isso pode sobrecarregar o sistema quando longas cadeias de indicação são feitas e causar a Verificação de Bugs 0x133 DPC_WATCHDOG_VIOLATION.
Evitar esses pontos problemáticos requer muito código complicado em seu driver. Embora você possa verificar se o watchdog DPC está próximo do limite com a função KeQueryDpcWatchdogInformation e sair do DPC, você ainda precisa criar uma infraestrutura em torno disso em seu driver: você precisa de alguma maneira de pausar um pouco, continuar a indicar os pacotes e, ao mesmo tempo, sincronizar tudo isso com o tempo de vida do caminho de dados.
Introdução aos objetos Poll
O Modo de Sondagem NDIS introduz o objeto Sondagem para resolver os pontos problemáticos associados aos DPCs. Um objeto Poll é uma construção de contexto de execução. Os drivers de miniporto podem usar um objeto Poll no lugar de um DPC ao lidar com operações de caminho de dados.
Um objeto Poll oferece o seguinte:
Ele fornece uma maneira para o NDIS definir limites de trabalho por iteração.
Está intimamente ligado a um mecanismo de notificação. Isso mantém o sistema operacional e a NIC sincronizados em relação a quando o trabalho precisa ser processado.
Ele tem um conceito de iteração e interrupções integrado. Ao usar DPCs, os drivers são forçados a reabilitar a interrupção toda vez que concluem um DPC. Ao usar objetos Poll, os drivers não precisam reabilitar a interrupção a cada iteração de sondagem porque o Modo de Sondagem permitirá que o driver saiba quando terminar a sondagem e é hora de reabilitar a interrupção novamente.
Ao tomar decisões de agendamento, o sistema pode ser inteligente sobre se deve ser executado em DISPATCH_LEVEL ou PASSIVE_LEVEL. Isso pode permitir a priorização ajustada do tráfego de diferentes NICs e levar a uma distribuição de carga de trabalho mais justa na máquina.
Possui garantias de serialização. Depois de executar o código de dentro do contexto de execução de um objeto Poll, você tem a garantia de que nenhum outro código relacionado ao mesmo contexto de execução será executado. Isso permite que um driver NIC tenha uma implementação livre de bloqueio de seu caminho de dados.
O modelo do Modo de Sondagem NDIS
O diagrama de sequência a seguir ilustra como o mesmo driver de NIC PCIe hipotético lida com uma intermitência de pacotes Rx usando um objeto Poll em vez de um DPC.
Assim como o modelo DPC, quando um pacote Rx chega, o hardware gera uma interrupção, o NDIS chama o ISR do driver e o driver desabilita a interrupção do ISR. Neste ponto, o modelo do Modo de Pesquisa diverge:
Em vez de enfileirar um DPC, o driver enfileira um objeto Poll (que ele criou anteriormente) do ISR para notificar o NDIS de que o novo trabalho está pronto para ser processado.
Em algum momento no futuro, o NDIS chama o manipulador de iteração de sondagem do driver para processar o trabalho. Ao contrário de um DPC, o driver não tem permissão para indicar tantos Rx NBLs quanto há elementos prontos em sua fila de hardware. Em vez disso, o driver deve verificar o parâmetro de dados de pesquisa do manipulador para obter o número máximo de NBLs que ele pode indicar.
Depois que o driver buscar até o número máximo de pacotes Rx, ele deverá inicializar NBLs, adicioná-los à fila NBL fornecida pelo manipulador de pesquisa e sair do retorno de chamada. O driver não deve habilitar a interrupção antes de sair.
O NDIS continua a pesquisar o driver até avaliar que o driver não está mais progredindo. Neste ponto, o NDIS interromperá a sondagem e solicitará que o driver reabilite a interrupção.
Palavra-chave INF padronizada para o modo de pesquisa NDIS
A seguinte palavra-chave deve ser usada para habilitar ou desabilitar o suporte para o Modo de Sondagem NDIS:
*As palavras-chave INF padronizadas da Enumeração NdisPoll têm os seguintes atributos:
SubkeyName
O nome da palavra-chave que você deve especificar no arquivo INF e que aparece no registro.
ParamDesc
O texto de exibição associado a SubkeyName.
Valor
O valor inteiro de enumeração associado a cada opção na lista. Esse valor é armazenado em NDI\params\ SubkeyName\Value.
EnumDesc
O texto de exibição associado a cada valor exibido no menu.
Padrão
O valor padrão do menu.
SubkeyName | ParamDesc | Valor | EnumDesc |
---|---|---|---|
*NdisPoll | Modo de enquete Ndis | 0 | Desabilitadas |
1 (Padrão) | Enabled |
Para obter mais informações sobre como usar palavras-chave de enumeração, confira Palavras-chave de enumeração.
Criando um objeto Poll
Para criar um objeto Poll, o driver de miniporto faz o seguinte em sua função de retorno de chamada MiniportInitializeEx:
- Aloca um contexto de miniporto privado.
- Aloca uma estrutura NDIS_POLL_CHARACTERISTICS para especificar pontos de entrada para as funções de retorno de chamada NdisPoll e NdisSetPollNotification .
- Chama NdisRegisterPoll para criar o objeto Poll e armazená-lo no contexto de miniport.
O exemplo a seguir mostra como um driver de miniporto pode criar um objeto Poll para um fluxo de fila de recebimento. O tratamento de erros é omitido para simplificar.
NDIS_SET_POLL_NOTIFICATION NdisSetPollNotification;
NDIS_POLL NdisPoll;
NDIS_STATUS
MiniportInitialize(
_In_ NDIS_HANDLE NdisAdapterHandle,
_In_ NDIS_HANDLE MiniportDriverContext,
_In_ NDIS_MINIPORT_INIT_PARAMETERS * MiniportInitParameters
)
{
// Allocate a private miniport context
MINIPORT_CONTEXT * miniportContext = ...;
NDIS_POLL_CHARACTERISTICS pollCharacteristics;
pollCharacteristics.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
pollCharacteristics.Header.Revision = NDIS_POLL_CHARACTERISTICS_REVISION_1;
pollCharacteristics.Header.Size = NDIS_SIZEOF_NDIS_POLL_CHARACTERISTICS_REVISION_1;
pollCharacteristics.SetPollNotificationHandler = NdisSetPollNotification;
pollCharacteristics.PollHandler = NdisPoll;
// Create a Poll object and store it in the miniport context
NdisRegisterPoll(
NdisAdapterHandle,
miniportContext,
&pollCharacteristics,
&miniportContext->RxPoll);
return NDIS_STATUS_SUCCESS;
}
Enfileirando um objeto Poll para execução
De um ISR, os drivers de miniporto chamam NdisRequestPoll para enfileirar um objeto Poll para execução. O exemplo a seguir mostra o tratamento de recebimento, mas ignora o compartilhamento de linhas de interrupção para simplificar.
BOOLEAN
MiniportIsr(
KINTERRUPT * Interrupt,
void * Context
)
{
auto miniportContext = static_cast<MINIPORT_CONTEXT *>(Context);
auto hardwareContext = miniportContext->HardwareContext;
// Check if this interrupt is due to a received packet
if (hardwareContext->ISR & RX_OK)
{
// Disable the receive interrupt and queue the Poll
hardwareContext->IMR &= ~RX_OK;
NdisRequestPoll(miniportContext->RxPoll, nullptr);
}
return TRUE;
}
Implementando o manipulador de iteração de sondagem
O NDIS invoca o retorno de chamada NdisPoll do driver de miniporto para sondar indicações de recebimento e enviar conclusões. O NDIS invoca NdisPoll pela primeira vez quando o driver chama NdisRequestPoll para enfileirar um objeto Poll. O NDIS continuará invocando o NdisPoll enquanto o driver estiver avançando nas indicações de recebimento ou nas conclusões de transmissão.
Para receber indicações, o driver deve fazer o seguinte em NdisPoll:
- Verifique o parâmetro de recebimento da estrutura NDIS_POLL_DATA para obter o número máximo de NBLs que ele pode indicar.
- Busque até o número máximo de pacotes Rx.
- Inicialize os NBLs.
- Adicione-os à fila NBL fornecida pela estrutura NDIS_POLL_RECEIVE_DATA (localizada na estrutura NDIS_POLL_DATA do parâmetro NdisPoll PollData).
- Saia do retorno de chamada.
Para conclusões de transmissão, o driver deve fazer o seguinte em NdisPoll:
- Verifique o parâmetro de transmissão da estrutura NDIS_POLL_DATA para obter o número máximo de NBLs que ela pode concluir.
- Busque até o número máximo de pacotes Tx.
- Complete os NBLs.
- Adicione-os à fila NBL fornecida pela estrutura NDIS_POLL_TRANSMIT_DATA (localizada na estrutura NDIS_POLL_DATA do parâmetro NdisPoll PollData).
- Saia do retorno de chamada.
O driver não deve habilitar a interrupção do objeto Poll antes de sair da função NdisPoll . O NDIS continuará pesquisando o motorista até avaliar que nenhum progresso está sendo feito. Neste ponto, o NDIS interromperá a sondagem e solicitará que o driver reabilite a interrupção.
Veja como um driver pode implementar NdisPoll para um fluxo de fila de recebimento.
_Use_decl_annotations_
void
NdisPoll(
void * Context,
NDIS_POLL_DATA * PollData
)
{
auto miniportContext = static_cast<MINIPORT_CONTEXT *>(Context);
auto hardwareContext = miniportContext->HardwareContext;
// Drain received frames
auto & receive = PollData->Receive;
receive.NumberOfRemainingNbls = NDIS_ANY_NUMBER_OF_NBLS;
receive.Flags = NDIS_RECEIVE_FLAGS_SHARED_MEMORY_VALID;
while (receive.NumberOfIndicatedNbls < receive.MaxNblsToIndicate)
{
auto rxDescriptor = HardwareQueueGetNextDescriptorToCheck(hardwareContext->RxQueue);
// If this descriptor is still owned by hardware stop draining packets
if ((rxDescriptor->Status & HW_OWN) != 0)
break;
auto nbl = MakeNblFromRxDescriptor(miniportContext->NblPool, rxDescriptor);
AppendNbl(&receive.IndicatedNblChain, nbl);
receive.NumberOfIndicatedNbls++;
// Move to next descriptor
HardwareQueueAdvanceNextDescriptorToCheck(hardwareContext->RxQueue);
}
}
Gerenciando interrupções
Os drivers de miniport implementam o retorno de chamada NdisSetPollNotification para habilitar ou desabilitar a interrupção associada a um objeto Poll. O NDIS normalmente invoca o retorno de chamada NdisSetPollNotification quando detecta que o driver de miniporto não está progredindo no NdisPoll. O NDIS usa NdisSetPollNotification para informar ao driver que ele deixará de invocar NdisPoll. O driver deve invocar NdisRequestPoll quando o novo trabalho estiver pronto para ser processado.
Veja como um driver pode implementar NdisSetPollNotification para um fluxo de fila de recebimento.
_Use_decl_annotations_
void
NdisSetPollNotification(
void * Context,
NDIS_POLL_NOTIFICATION * Notification
)
{
auto miniportContext = static_cast<MINIPORT_CONTEXT *>(Context);
auto hardwareContext = miniportContext->HardwareContext;
if (Notification->Enabled)
{
hardwareContext->IMR |= RX_OK;
}
else
{
hardwareContext->IMR &= ~RX_OK;
}
}