Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Os drivers de rede do Windows usam solicitações de OID para enviar mensagens de controle através da pilha de binding do NDIS. Os drivers de protocolo, como TCPIP ou vSwitch, dependem de dezenas de OIDs para configurar cada recurso do driver NIC subjacente. Antes do Windows 10, versão 1709, as solicitações de OID eram enviadas de duas maneiras: Regular e Direct.
Este tópico apresenta um terceiro estilo de chamada OID: síncrono. Uma chamada síncrona deve ser de baixa latência, sem bloqueio, escalonável e confiável. A interface de solicitação OID síncrona está disponível a partir do NDIS 6.80, que está incluído no Windows 10, versão 1709 e posterior.
Comparação entre solicitações OID comuns e diretas
Com solicitações OID síncronas, o conteúdo da chamada (a própria OID) é exatamente o mesmo que com solicitações OID Regulares e Diretas. A única diferença está na própria chamada. Portanto, o que é o mesmo em todos os três tipos de OIDs; apenas o modo como é diferente.
A tabela a seguir descreve as diferenças entre OIDs Regulares, OIDs Diretos e OIDs Síncronos.
| Atributo | Regular OID | OID direto | OID síncrono |
|---|---|---|---|
| Conteúdo | NDIS_OID_REQUEST | NDIS_OID_REQUEST | NDIS_OID_REQUEST |
| Tipos de OID | Estatísticas, Consulta, Conjunto, Método | Estatísticas, Consulta, Conjunto, Método | Estatísticas, Consulta, Conjunto, Método |
| Pode ser emitido por | Protocolos, filtros | Protocolos, filtros | Protocolos, filtros |
| Pode ser concluído por | Miniportos, filtros | Miniportos, filtros | Miniportos, filtros |
| Os filtros podem modificar | Yes | Yes | Yes |
| NDIS aloca memória | Para cada filtro (clone de OID) | Para cada filtro (clone de OID) | Somente se número excepcionalmente grande de filtros (contexto de chamada) |
| Pode ficar pendente | Yes | Yes | Não |
| Pode bloquear | Yes | Não | Não |
| IRQL | == PASSIVA | <= DISPATCH | <= DESPACHO |
| Serializado pelo NDIS | Yes | Não | Não |
| Os filtros são invocados | Recursivamente | Recursivamente | Iterativamente |
| Filtros clonam o OID | Yes | Yes | Não |
Filtragem
Assim como os outros dois tipos de chamadas OID, os drivers de filtro têm controle total sobre a solicitação OID em uma chamada síncrona. Os drivers de filtro podem observar, interceptar, modificar e emitir OIDs síncronos. No entanto, para eficiência, a mecânica de uma OID síncrona é um pouco diferente.
Passagem, interceptação e origem
Conceitualmente, todas as solicitações de OID são emitidas de um driver superior e são concluídas por um driver inferior. Durante o processo, a solicitação OID pode passar por diversos drivers de filtro.
No caso mais comum, um driver de protocolo emite uma solicitação OID e todos os filtros simplesmente passam a solicitação OID para baixo, sem modificação. A figura a seguir ilustra esse cenário comum.
No entanto, qualquer módulo de filtro tem permissão para interceptar a solicitação OID e concluí-la. Nesse caso, a solicitação não é repassada para controladores inferiores, conforme mostrado no diagrama a seguir.
Em alguns casos, um módulo de filtro pode decidir originar sua própria solicitação de OID. Essa solicitação começa no nível do módulo de filtro e percorre apenas drivers inferiores, como mostra o diagrama a seguir.
Todas as solicitações de OID têm esse fluxo básico: um driver mais alto (um driver de protocolo ou filtro) emite uma solicitação e um driver inferior (um driver de miniporto ou filtro) o conclui.
Como as solicitações OID regulares e diretas funcionam
Solicitações OID regulares ou diretas são enviadas recursivamente. O diagrama a seguir mostra a sequência de chamadas de função. Observe que a sequência em si é muito semelhante à sequência descrita nos diagramas da seção anterior, mas é organizada para mostrar a natureza recursiva das solicitações.
Se houver filtros suficientes instalados, o NDIS será forçado a alocar uma nova pilha de threads para continuar a repetir mais profundamente.
O NDIS considera que uma estrutura NDIS_OID_REQUEST é válida para apenas um único passo ao longo da pilha. Se um driver de filtro quiser passar a solicitação para o próximo driver inferior (que é o caso da grande maioria dos OIDs), o driver de filtro deverá inserir várias dezenas de linhas de código clichê para clonar a solicitação OID. Este clichê tem vários problemas:
- Ele força uma alocação de memória para clonar o OID. Atingir o pool de memória é lento e torna impossível garantir o progresso da solicitação de OID.
- O design da estrutura OID deve permanecer o mesmo ao longo do tempo porque todos os drivers de filtro codificam diretamente a mecânica de cópia do conteúdo de uma NDIS_OID_REQUEST para outra.
- Exigir tanta clichê obscurece o que o filtro está realmente fazendo.
O modelo de filtragem para solicitações OID síncronas
O modelo de filtragem para solicitações OID síncronas aproveita a natureza síncrona da chamada para resolver os problemas discutidos na seção anterior.
Manipuladores de eventos e conclusão
Ao contrário das solicitações OID Regulares e Diretas, há dois ganchos de filtro para solicitações OID síncronas: um manipulador de problemas e um manipulador Completo. Um driver de filtro pode registrar nenhum, um ou ambos os ganchos.
As chamadas de problema são invocadas para cada driver de filtro, começando da parte superior da pilha até a parte inferior da pilha. Qualquer chamada de emissão de filtro pode impedir que o OID prossiga para níveis mais baixos e finalize o OID com algum código de status. Se nenhum filtro decidir interceptar o OID, o OID atingirá o driver NIC, que deve concluir o OID de forma síncrona.
Após a conclusão de uma OID, as chamadas de conclusão são invocadas para cada driver de filtro, começando do ponto onde a OID foi concluída, até o topo da pilha. Uma chamada completa pode inspecionar ou modificar a solicitação OID e inspecionar ou modificar o código de status de conclusão do OID.
O diagrama a seguir ilustra o caso típico, em que um protocolo emite uma solicitação OID síncrona e os filtros não interceptam a solicitação.
Observe que o modelo de chamada para OIDs síncronos é iterativo. Isso mantém o uso da pilha limitado por uma constante, eliminando a necessidade de expandir a pilha.
Se um driver de filtro interceptar uma OID síncrona em seu manipulador de requisição, a OID não será repassada às camadas inferiores ou ao driver NIC. No entanto, os manipuladores completos para filtros mais altos ainda são invocados, conforme mostrado no diagrama a seguir:
Alocações mínimas de memória
Solicitações OID regulares e diretas exigem um driver de filtro para clonar um NDIS_OID_REQUEST. Por outro lado, solicitações OID síncronas não têm permissão para serem clonadas. A vantagem desse design é que os OIDs síncronos têm menor latência – a solicitação de OID não é clonada repetidamente à medida que percorre a pilha de filtros – e há menos oportunidades de falha.
No entanto, isso gera um novo problema. Se a OID não puder ser clonada, onde um driver de filtro armazenará seu estado por solicitação? Por exemplo, suponha que um driver de filtro traduza uma OID para outra. No caminho para baixo da pilha, o filtro precisa salvar o OID antigo. Ao voltar na pilha, o filtro precisa restaurar o OID antigo.
Para resolver esse problema, o NDIS aloca um slot do tamanho de um ponteiro para cada driver de filtro, para cada solicitação OID síncrona em andamento. O NDIS preserva esse slot durante a chamada do manipulador de solicitações de um filtro para o manipulador de conclusão. Isso permite que o manipulador de problemas salve o estado que é consumido posteriormente pelo manipulador Completo. O snippet de código a seguir mostra um exemplo.
NDIS_STATUS
MyFilterSynchronousOidRequest(
_In_ NDIS_HANDLE FilterModuleContext,
_Inout_ NDIS_OID_REQUEST *OidRequest,
_Outptr_result_maybenull_ PVOID *CallContext)
{
if ( . . . should intercept this OID . . . )
{
// preserve the original buffer in the CallContext
*CallContext = OidRequest->DATA.SET_INFORMATION.InformationBuffer;
// replace the buffer with a new one
OidRequest->DATA.SET_INFORMATION.InformationBuffer = . . . something . . .;
}
return NDIS_STATUS_SUCCESS;
}
VOID
MyFilterSynchronousOidRequestComplete(
_In_ NDIS_HANDLE FilterModuleContext,
_Inout_ NDIS_OID_REQUEST *OidRequest,
_Inout_ NDIS_STATUS *Status,
_In_ PVOID CallContext)
{
// if the context is not null, we must have replaced the buffer.
if (CallContext != null)
{
// Copy the data from the miniport back into the protocol’s original buffer.
RtlCopyMemory(CallContext, OidRequest->DATA.SET_INFORMATION.InformationBuffer,...);
// restore the original buffer into the OID request
OidRequest->DATA.SET_INFORMATION.InformationBuffer = CallContext;
}
}
O NDIS salva um PVOID por filtro por chamada. O NDIS aloca, de forma heurística, um número razoável de slots na pilha, para que não haja alocações de pool na maioria dos casos. Isso geralmente não é mais do que sete filtros. Se o usuário configurar um caso patológico, o NDIS retornará a uma alocação de pool.
Clichê reduzido
Considere o modelo padrão no exemplo de modelo padrão para lidar com solicitações OID regulares ou diretas. Esse código é o custo da entrada apenas para registrar um manipulador de OID. Se você quiser emitir seus próprios OIDs, precisará adicionar mais uma dúzia de linhas de código padrão. Com OIDs síncronos, não há necessidade da complexidade adicional de lidar com a conclusão assíncrona. Portanto, você pode cortar grande parte dessa clichê.
Aqui está um gerenciador de incidentes simples com OIDs síncronos:
NDIS_STATUS
MyFilterSynchronousOidRequest(
NDIS_HANDLE FilterModuleContext,
NDIS_OID_REQUEST *OidRequest,
PVOID *CallContext)
{
return NDIS_STATUS_SUCCESS;
}
Se você quiser interceptar ou modificar uma OID específica, poderá fazer isso adicionando apenas algumas linhas de código. O manipulador completo mínimo é ainda mais simples:
VOID
MyFilterSynchronousOidRequestComplete(
NDIS_HANDLE FilterModuleContext,
NDIS_OID_REQUEST *OidRequest,
NDIS_STATUS *Status,
PVOID CallContext)
{
return;
}
Da mesma forma, um driver de filtro pode emitir uma nova solicitação OID síncrona própria usando apenas uma linha de código:
status = NdisFSynchronousOidRequest(binding->NdisBindingHandle, &oid);
Por outro lado, um driver de filtro que precisa emitir um OID Regular ou Direto deve configurar um manipulador de conclusão assíncrono e implementar algum código para distinguir suas próprias conclusões de OID das conclusões de OIDs que acabou de clonar. Um exemplo deste modelo é demonstrado no exemplo de modelo para emitir uma solicitação de OID regular.
Interoperabilidade
Embora os estilos de chamada Regular, Direto e Síncrono usem as mesmas estruturas de dados, os pipelines não vão para o mesmo handler no miniport. Além disso, alguns OIDs não podem ser usados em algumas das pipelines. Por exemplo, OID_PNP_SET_POWER requer sincronização cuidadosa e muitas vezes força o miniporto a fazer chamadas de bloqueio. Isso dificulta o tratamento em um retorno de chamada de OID direto e impede seu uso em um retorno de chamada de OID síncrono.
Portanto, assim como acontece com solicitações OID Diretas, chamadas OID síncronas só podem ser usadas com um subconjunto de OIDs. No Windows 10, versão 1709, apenas o OID OID_GEN_RSS_SET_INDIRECTION_TABLE_ENTRIES utilizado no Receive Side Scaling Version 2 (RSSv2) é suportado no caminho síncrono OID.
Implementando solicitações OID síncronas
Para obter mais informações sobre como implementar a interface de solicitação OID síncrona em drivers, consulte os seguintes tópicos: