Sinalizando um evento de CPU de um driver no modo kernel
Há casos em que o KMD (driver do modo kernel) precisa sinalizar um evento de CPU para notificar o DRIVER do modo de usuário (UMD) sobre algo; por exemplo:
- Quando o KMD detecta que um de seus objetos está em um estado inválido e precisa notificar a UMD.
- Durante a depuração de GPU em que o KMD precisa se comunicar com a UMD de que algum evento aconteceu. Para IHVs com Painéis de Controle para GPU, a sinalização de eventos de CPU por KMD permite que o KMD notifique o aplicativo Painéis de Controle sobre eventos internos.
Normalmente, a UMD pode criar um evento de CPU e passar seu identificador NT para KMD em um escape de dados privados. Esse método não funciona no cenário de paravirtualização de GPU (GPU-PV), pois os identificadores NT não podem ser usados entre os limites da máquina virtual.
A partir Windows 11 versão 21H2 (WDDM 3.0), a API do WDDM foi estendida para permitir que a UMD criasse um objeto de evento de CPU que pode ser sinalizado pelo KMD. Esse recurso funciona quando o UMD está em execução no host ou em uma máquina virtual usando GPU-PV.
Fluxo de recursos
A UMD cria um evento de CPU.
A UMD cria um objeto de sincronização de GPU com o tipo D3DDDI_CPU_NOTIFICATION . O objeto criado fica visível para KMD definindo o sinalizador SignalByKmd ao chamar D3DKMTCreateSynchronizationObject.
O Dxgkrnl chama DXGKDDI_CREATECPUEVENT para permitir que o KMD crie seu próprio objeto.
A UMD chama D3DKMTEscape com o D3DDDI_DRIVERESCAPETYPE_CPUEVENTUSAGE tipo de escape conhecido para notificar o KMD sobre o uso pretendido do objeto de sincronização.
Dxgkrnl chama DXGKDDI_ESCAPE para passar os dados privados para KMD.
Em algum momento, as chamadas KMD DXGKCB_SIGNALEVENT com o sinalizador CpuEventObject para sinalizar o objeto de evento da CPU.
A UMD chama D3DKMTDestroySynchronizationObject para destruir o objeto de evento da CPU.
Dxgkrnl chama DXGKDDI_DESTROYCPUEVENT para destruir o objeto de evento da CPU. DXGKCB_SIGNALEVENT não deve ser chamado após esse ponto.
O objeto de sincronização não pode ser inserido em uma fila de contexto. Ele só pode ser sinalizado pelo KMD usando DXGKCB_SIGNALEVENT.
APIs do modo de usuário para lidar com objetos de sincronização de eventos da CPU
Criação do objeto de evento de CPU KMD
O objeto de evento da CPU KMD é criado como um objeto de sincronização de GPU chamando D3D12DDICB_CREATESYNCHRONIZATIONOBJECT2 com:
Digite definido como D3DDDI_CPU_NOTIFICATION.
Sinalizadores definidos como SignalByKmd para especificar que o objeto será sinalizado pelo KMD. Esse sinalizador só pode ser definido quando o membro Type doD3DDDI_SYNCHRONIZATIONOBJECTINFO2 é D3DDDI_CPU_NOTIFICATION.
Quando o sinalizador SignalByKmd for definido, DXGKDDI_CREATECPUEVENT será chamado para criar o objeto de evento da CPU KMD. Observe que o identificador do dispositivo deve ser especificado ao criar o objeto de sincronização.
O objeto de sincronização não pode ser usado em APIs de sinal e espera (D3DKMTSignalSynchronizationObject, D3DKMTWaitForSynchronizatioObject). Ele só pode ser sinalizado pelo KMD e o UMD pode aguardar o evento de CPU correspondente.
Escape umd para definir o uso de um objeto de sincronização de eventos de CPU KMD
Uma fuga conhecida foi adicionada a D3DDDI_DRIVERESCAPETYPE. D3DDDI_DRIVERESCAPETYPE_CPUEVENTUSAGE é usado para notificar o KMD sobre o uso pretendido de um objeto de evento de CPU KMD. Um escape conhecido é definido pela configuração DXGKARG_ESCAPE::Flags.DriverKnownEscape = 1. Escapes conhecidos são enviados para o host mesmo de máquinas virtuais seguras.
O snippet a seguir é um exemplo de uso.
D3DDDI_DRIVERESCAPE_CPUEVENTUSAGE Command = {};
Command.EscapeType = D3DDDI_DRIVERESCAPETYPE_CPUEVENTUSAGE;
Command.hSyncObject = SyncObjectHandle;
Command.Usage[0] = 1;
D3DKMT_ESCAPE Args = {};
Args.hAdapter = AdapterHandle;
Args.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
Args.Flags.DriverKnownEscape = 1;
Args.Flags.NoAdapterSynchronization = 1; // Prevent waking up the device from D3
Args.pPrivateDriverData = &Command;
Args.PrivateDriverDataSize = sizeof(Command);
NTSTATUS Status = D3DKMTEscape(&Args);
Dxgkrnl chamará DXGKDDI_ESCAPE com o seguinte:
- hDevice definido como o identificador de dispositivo de miniport que foi usado para criar o objeto de sincronização
- pPrivateDriverData apontando para uma estrutura de D3DDDI_DRIVERESCAPE_CPUEVENTUSAGE
- PrivateDriverDataSize definido como
sizeof(D3DDDI_DRIVERESCAPE_CPUEVENTUSAGE)
Criando e destruindo um objeto de evento de CPU KMD
Os seguintes DDIs são usados para criar e destruir objetos de sincronização de eventos de CPU KMD:
Sinalizando um objeto de evento de CPU do KMD
Para sinalizar um objeto de evento de CPU, o KMD chama DXGKCB_SIGNALEVENT em IRQL <= DISPATCH_LEVEL e com os valores de estrutura DXGKARGCB_SIGNALEVENT definidos da seguinte maneira:
hDxgkProcess é igual a 0.
hEvent igual ao identificador de objeto de evento da CPU Dxgkrnl passado para DXGKDDI_CREATECPUEVENT.
CpuEventObject deve ser 1.
Reservado deve ser 0.