Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Os drivers de rede do Windows usam solicitações OID para enviar mensagens de controlo através da camada de ligação 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 OID eram enviadas de duas maneiras: Regular e Direta.
Este tópico apresenta um terceiro estilo de chamada OID: Síncrono. Uma chamada síncrona destina-se a ser de baixa latência, sem bloqueio, escalá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 com solicitações OID regulares e diretas
Com solicitações OID síncronas, a carga útil da chamada (o próprio OID) é exatamente a mesma que com solicitações OID regulares e diretas. A única diferença está na própria chamada. Portanto, o o que é o mesmo em todos os três tipos de OIDs; apenas o 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 |
|---|---|---|---|
| Carga útil | 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 preenchido por | Miniportas, filtros | Miniportas, filtros | Miniportas, filtros |
| Os filtros podem modificar | Sim | Sim | Sim |
| O NDIS aloca memória | Para cada filtro (clone OID) | Para cada filtro (clone OID) | Apenas se houver um número anormalmente grande de filtros (contexto de chamada) |
| Pode ficar pendente | Sim | Sim | Não |
| Pode bloquear | Sim | Não | Não |
| IRQL | == PASSIVO | <= EXPEDIÇÃO | <= EXPEDIÇÃO |
| serializado por NDIS | Sim | Não | Não |
| Os filtros são invocados | Recursivamente | Recursivamente | Iterativamente |
| Os filtros clonam o OID | Sim | Sim | Não |
Filtragem
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, intercetar, modificar e emitir OIDs síncronos. No entanto, para eficiência, a mecânica de um OID síncrono é um pouco diferente.
Transmissão, intercetação e originação
Conceitualmente, todas as solicitações OID são emitidas por um driver superior e são concluídas por um driver inferior. Ao longo do caminho, a solicitação OID pode passar por qualquer número de 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ções. A figura a seguir ilustra esse cenário comum.
No entanto, qualquer módulo de filtro tem permissão para intercetar a solicitação OID e concluí-la. Nesse caso, a solicitação não é transmitida para os drivers de nível inferior, como mostrado no diagrama a seguir.
Em alguns casos, um módulo de filtro pode decidir originar sua própria solicitação OID. Esta solicitação começa ao nível do módulo de filtro e apenas percorre drivers de nível inferior, como mostra o diagrama a seguir.
Todas as solicitações OID têm esse fluxo básico: um driver superior (um driver de protocolo ou filtro) emite uma solicitação e um driver inferior (uma miniporta ou driver de filtro) a completa.
Como funcionam as solicitações OID regulares e diretas
As solicitações OID regulares ou diretas são enviadas recursivamente. O diagrama a seguir mostra a sequência de chamada de função. Observe que a sequência em si é muito parecida com a 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 recursão mais profundamente.
O NDIS considera uma estrutura NDIS_OID_REQUEST válida para apenas um único salto ao longo da pilha. Se um driver de filtro quiser passar a solicitação para o próximo driver inferior (que é o caso para a grande maioria dos OIDs), o driver de filtro deve inserir várias dúzias 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. Aceder ao pool de memória é lento e torna impossível avançar na solicitação OID.
- O design da estrutura OID deve permanecer inalterado ao longo do tempo porque todos os drivers de filtro codificam rigidamente a mecânica de copiar o conteúdo de um NDIS_OID_REQUEST para outro.
- Exigir tanto 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.
Gestores 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 problema do filtro pode impedir que o OID continue para baixo e completar o OID com algum código de status. Se nenhum filtro decidir intercetar o OID, o OID alcançará o driver da NIC, que deve concluir o OID de forma síncrona.
Depois que um OID é concluído, chamadas completas são invocadas para cada driver de filtro, começando de onde quer que na pilha o OID tenha sido concluído, 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 intercetam 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 intercetar um OID síncrono em seu manipulador de problemas, o OID não será fornecido aos filtros inferiores ou ao driver da NIC. No entanto, manipuladores completos para filtros superiores ainda são invocados, conforme mostrado no diagrama a seguir:
Alocações mínimas de memória
Solicitações OID regulares e diretas requerem um driver de filtro para clonar um NDIS_OID_REQUEST. Por outro lado, as solicitações OID síncronas não podem ser clonadas. A vantagem desse design é que os OIDs síncronos têm latência mais baixa – a solicitação OID não é repetidamente clonada à medida que viaja pela pilha de filtros – e há menos oportunidades de falha.
No entanto, isso levanta um novo problema. Se o OID não puder ser clonado, onde um driver de filtro armazena seu estado por solicitação? Por exemplo, suponha que um driver de filtro traduza um OID para outro. No caminho para baixo da pilha, o filtro precisa salvar o OID antigo. No caminho de volta para cima da 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 voo. O NDIS preserva esse slot na chamada do manipulador Issue de um filtro para seu manipulador Complete. Isso permite que o manipulador de problemas salve o estado que é posteriormente consumido pelo manipulador Complete. O trecho 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 stack, garantindo que não haja alocações de pool no caso comum. Normalmente, não são mais do que sete filtros. Se o usuário configurar um caso patológico, o NDIS recorre a uma alocação de pool.
Redução do clichê
Considere o clichê em Exemplo de clichê para lidar com solicitações OID regulares ou diretas. Esse código é o custo de entrada apenas para registar um gestor de OID. Se você quiser emitir seus próprios OIDs, você tem que adicionar mais uma dúzia de linhas de clichê. Com OIDs síncronos, não há necessidade da complexidade adicional de lidar com a conclusão assíncrona. Portanto, você pode cortar muito desse clichê.
Aqui está um processador de problemas mínimo com OIDs síncronos:
NDIS_STATUS
MyFilterSynchronousOidRequest(
NDIS_HANDLE FilterModuleContext,
NDIS_OID_REQUEST *OidRequest,
PVOID *CallContext)
{
return NDIS_STATUS_SUCCESS;
}
Se você quiser intercetar ou modificar um OID específico, você pode fazê-lo adicionando apenas algumas linhas de código. O manipulador Complete 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 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íncrona e implementar algum código adicional para distinguir suas próprias conclusões de OID das conclusões de OIDs que clonou recentemente. Um exemplo deste modelo é mostrado em modelo de exemplo para emitir uma solicitação 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 manipulador na miniporta. Além disso, alguns OIDs não podem ser utilizados em algumas das linhas de processamento. Por exemplo, OID_PNP_SET_POWER requer uma sincronização cuidadosa e muitas vezes força a miniporta a fazer chamadas de bloqueio. Isso torna difícil o manuseio num retorno de chamada Direct OID, e impede seu uso num retorno de chamada OID síncrono.
Portanto, assim como nas solicitações Direct OID, as chamadas OID síncronas só podem ser usadas com um subconjunto de OIDs. No Windows 10, versão 1709, apenas o OID_GEN_RSS_SET_INDIRECTION_TABLE_ENTRIES OID usado no RSSv2 (Receive Side Scaling Versão 2) é suportado no caminho OID síncrono.
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:
- Solicitações OID do adaptador Miniport
- Solicitações OID do módulo de filtro
- Pedidos de OID do driver de protocolo