Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Important
Certaines informations portent sur la préversion du produit qui est susceptible d’être en grande partie modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, en ce qui concerne les informations fournies ici.
Cet article décrit la fonctionnalité de soumission de travail en mode utilisateur (UM) qui est toujours en cours de développement à partir de Windows 11, version 24H2 (WDDM 3.2). La soumission de travail en mode utilisateur permet aux applications d’envoyer le travail au GPU directement à partir du mode utilisateur avec une latence minimale. L’objectif est d’améliorer les performances des applications qui envoient fréquemment de petites charges de travail au GPU. En outre, la soumission en mode utilisateur devrait bénéficier considérablement de ces applications si elles s’exécutent à l’intérieur d’un conteneur ou d’une machine virtuelle. Cet avantage est dû au fait que le pilote en mode utilisateur (UMD) exécuté dans la machine virtuelle peut envoyer directement du travail au GPU sans avoir à envoyer un message à l’hôte.
Les pilotes et le matériel IHV qui prennent en charge la soumission de travail en mode utilisateur doivent continuer à prendre en charge simultanément le modèle de soumission de travail en mode noyau traditionnel. Cette prise en charge est nécessaire pour des scénarios comme un système invité ancien qui ne prend en charge que les files d'attente classiques KM, lorsqu'il s'exécute sur un hôte plus récent.
Cet article ne traite pas de l’interopérabilité de la soumission de messagerie unifiée avec Flip/FlipEx. La soumission UM décrite dans cet article est limitée uniquement au rendu / à la classe de scénarios de calcul. Le pipeline de présentation continue d’être basé sur la soumission en mode noyau pour l’instant, car il a une dépendance sur les clôtures surveillées natives. La conception et l'implémentation d'une présentation basée sur la soumission UM peuvent être envisagées une fois que les fences surveillées natives et la soumission UM pour le calcul/rendu sont entièrement implémentées. Par conséquent, les pilotes doivent prendre en charge la soumission en mode utilisateur pour chaque file.
Sonnettes
La plupart des générations actuelles ou à venir de GPU qui prennent en charge la planification matérielle prennent également en charge le concept d’une porte d’entrée GPU. Une sonnette d'entrée est un mécanisme permettant d’indiquer à un moteur GPU que du nouveau travail est mis en attente dans sa file. Les doorbells sont généralement enregistrés dans le PCIe BAR (base address bar) ou la mémoire système. Chaque IHV GPU a sa propre architecture qui détermine le nombre de portes d’entrée, où ils se trouvent dans le système, et ainsi de suite. Le système d’exploitation Windows utilise des signaux d'alerte dans le cadre de sa conception pour implémenter la soumission de travail en mode utilisateur.
À un niveau élevé, il existe deux modèles différents de porte-sons implémentés par différents IHV et GPU :
Portes d’entrée globales
Dans le modèle Global Doorbells, toutes les files d’attente matérielles entre les contextes et les processus partagent une seule porte d’entrée globale. La valeur écrite dans la sonnette informe le planificateur GPU de la file d’attente matérielle et du moteur particuliers qui ont de nouvelles tâches. Le matériel GPU utilise une forme de mécanisme d’interrogation pour extraire le travail si plusieurs files d’attente matérielles envoient activement du travail et sonnent la même porte d’entrée globale.
Portes d’entrée dédiées
Dans le modèle de sonnette dédié, chaque file d'attente matérielle est associée à sa propre sonnette qui est activée dès qu'il y a de nouvelles tâches à soumettre au GPU. Lorsqu'une sonnette est actionnée, le planificateur GPU sait exactement quelle file d'attente matérielle a soumis du nouveau travail. Il existe des portes d’entrée limitées qui sont partagées sur toutes les files d’attente matérielles créées sur le GPU. Si le nombre de files d’attente matérielles créées dépasse le nombre de portes d’entrée disponibles, le pilote doit déconnecter la porte d’une file d’attente matérielle plus ancienne ou la moins récemment utilisée et affecter sa porte-porte à une file d’attente nouvellement créée, en effet « virtualisant » les porte-porte.
Découverte du support pour la soumission des travaux en mode utilisateur
DXGK_NODEMETADATA_FLAGS::UserModeSubmissionSupported
Pour les nœuds GPU qui prennent en charge la fonctionnalité de soumission de travail en mode utilisateur, la fonction DxgkDdiGetNodeMetadata de KMD définit l'indicateur de métadonnées de nœud UserModeSubmissionSupported qui est ajouté à DXGK_NODEMETADATA_FLAGS. Le système d’exploitation permet ensuite à l’UMD de créer des HWQueues en mode utilisateur et des portes d’envoi uniquement sur les nœuds pour lesquels cet indicateur est défini.
DXGK_QUERYADAPTERINFOTYPE::DXGKQAITYPE_USERMODESUBMISSION_CAPS
Pour interroger des informations spécifiques à la sonnette, le système d’exploitation appelle la fonction DxgkDdiQueryAdapterInfo de KMD avec le type de requête d’informations de l’adaptateur DXGKQAITYPE_USERMODESUBMISSION_CAPS. KMD répond en remplissant la structure DXGK_USERMODESUBMISSION_CAPS avec ses détails de support pour la soumission de travail en mode utilisateur.
Actuellement, le seul cap requis est la taille de mémoire de la sonnette (en octets). Dxgkrnl a besoin de la taille de la mémoire de la sonnette pour plusieurs raisons :
- Lors de la création d'une sonnette (D3DKMTCreateDoorbell), Dxgkrnl renvoie une doorbellCpuVirtualAddress à UMD. Avant cela, Dxgkrnl doit d’abord mapper en interne à une page factice, car la porte n’est pas encore affectée et connectée. La taille de la sonnette est nécessaire pour allouer la page fictive.
- Pendant la connexion doorbell (D3DKMTConnectDoorbell), Dxgkrnl doit faire pivoter DoorbellCpuVirtualAddress vers une DoorbellPhysicalAddress fournie par KMD. Là encore, Dxgkrnl doit connaître la taille de la sonnette.
D3DDDI_CREATEHWQUEUEFLAGS ::UserModeSubmission dans D3DKMTCreateHwQueue
UMD définit l’indicateur UserModeSubmission ajouté à D3DDDI_CREATEHWQUEUEFLAGS pour créer des HWQueues qui utilisent le modèle de soumission en mode utilisateur. Les HWQueues créés à l’aide de cet indicateur ne peuvent pas utiliser le chemin d’envoi de travail en mode noyau standard et doivent s’appuyer sur le mécanisme de porte d’entrée pour la soumission de travail dans la file d’attente.
API de soumission de travail en mode utilisateur
Les API en mode utilisateur suivantes sont ajoutées pour prendre en charge la soumission de travail en mode utilisateur.
D3DKMTCreateDoorbell crée une sonnette pour un D3D HWQueue pour la soumission de tâches en mode utilisateur.
D3DKMTConnectDoorbell connecte une sonnette créée précédemment à un D3D HWQueue pour la soumission de travail en mode utilisateur.
D3DKMTDestroyDoorbell détruit un doorbell créé précédemment.
D3DKMTNotifyWorkSubmission informe le KMD que de nouveaux travaux ont été soumis sur un HWQueue. L'objectif de cette fonctionnalité est d'avoir un chemin d'envoi de travail à faible latence, où KMD n'est ni impliqué ni au courant lorsque le travail est envoyé. Cette API est utile dans les scénarios où le KMD doit être averti chaque fois que le travail est envoyé sur un HWQueue. Les pilotes doivent utiliser ce mécanisme dans des scénarios spécifiques et peu fréquents, car il implique un aller-retour de UMD à KMD sur chaque soumission de travail, ce qui permet de vaincre l’objectif d’un modèle de soumission en mode utilisateur à faible latence.
Modèle de résidence des allocations de mémoire de sonnette et de mémoire tampon circulaire
- UMD est responsable de la création de la mémoire tampon en anneau et des allocations de contrôle de mémoire tampon en anneau avant de créer une porte d’entrée.
- UMD gère la durée de vie de la mémoire tampon en anneau et des allocations de contrôle de mémoire tampon en anneau. Dxgkrnl ne détruit pas implicitement ces allocations même si la porte d’entrée correspondante est détruite. L’UMD est responsable de l’allocation et de la destruction de ces allocations. Toutefois, pour empêcher un programme malveillant en mode utilisateur de détruire ces allocations pendant toute la durée de vie de la sonnette, Dxgkrnl maintient une référence sur elles.
- Le seul scénario dans lequel Dxgkrnl détruit les allocations de mémoire tampon en anneau est pendant l’arrêt de l’appareil. Dxgkrnl détruit tous les HWQueues, les portes d’entrée et les allocations de mémoire tampon en anneau associées à l’appareil.
- Tant que les allocations de mémoire tampon en anneau sont actives, le CPUVAC de la mémoire tampon en anneau est toujours valide et disponible pour l'accès de l'UMD, quel que soit l'état des connexions de doorbell. En d'autres termes, le contenu de la mémoire tampon circulaire n'est pas lié à la sonnette.
- Lorsque KMD effectue le rappel DXG pour déconnecter une sonnette (autrement dit, appelle DxgkCbDisconnectDoorbell avec l'état D3DDDI_DOORBELL_STATUS_DISCONNECTED_RETRY), Dxgkrnl fait pivoter la CPUVA de la sonnette vers une page factice. Il ne supprime ni ne désassocie les allocations du buffer circulaire.
- En cas de scénarios de perte de périphérique (TDR/GPU Stop/Page, etc.), Dxgkrnl déconnecte la sonnette et marque l’état comme D3DDDI_DOORBELL_STATUS_DISCONNECTED_ABORT. Le mode utilisateur est chargé de détruire le HWQueue, le doorbell, la mémoire tampon en anneau et de les recréer. Cette exigence est similaire à la façon dont d’autres ressources d’appareil sont détruites et recréées dans ce scénario.
Suspension du contexte matériel
Lorsque le système d’exploitation suspend un contexte matériel, Dxgkrnl conserve la connexion de porte d’entrée active et la mémoire tampon en anneau (file d’attente de travail) résidente. De cette façon, UMD peut continuer à mettre en file d’attente le travail dans le contexte. Ce dernier n’est pas planifié lorsque le contexte est suspendu. Une fois le contexte restauré et planifié, le processeur de gestion de contexte (CMP) du GPU observe le nouveau pointeur d’écriture et la soumission de travail.
Cette logique est similaire à la logique de soumission en mode noyau actuelle, où UMD peut appeler D3DKMTSubmitCommand avec un contexte suspendu. Dxgkrnl met en file d’attente cette nouvelle commande dans HwQueue, mais elle n’est pas planifiée avant un moment ultérieur.
La séquence d’événements suivante se produit pendant la suspension et la reprise du contexte matériel.
Suspension d’un contexte matériel :
- Dxgkrnl appelle DxgkddiSuspendContext.
- KMD supprime tous les HWQueues du contexte de la liste du planificateur HW.
- Les portes d’entrée sont toujours connectées et les allocations de contrôle de mémoire tampon en anneau/mémoire tampon en anneau sont toujours résidentes. L’UMD peut écrire de nouvelles commandes dans le HWQueue de ce contexte, mais le GPU ne les traite pas, ce qui est similaire à la soumission de commandes en mode noyau d’aujourd’hui à un contexte suspendu.
- Si KMD choisit de victimiser la porte d’une HWQueue suspendue, l’UMD perd sa connexion. UMD peut tenter de reconnecter la sonnette et KMD assignera une nouvelle sonnette à cette file d'attente. L’intention est de ne pas bloquer l’UMD, mais plutôt de l’autoriser à continuer à soumettre le travail que le moteur HW peut éventuellement traiter une fois le contexte repris.
Reprise d’un contexte matériel :
- Dxgkrnl appelle DxgkddiResumeContext.
- KMD ajoute tous les HWQueues du contexte à la liste du planificateur HW.
Transitions d’état F du moteur
Dans la soumission traditionnelle du travail en mode noyau, Dxgkrnl est chargé d’envoyer de nouvelles commandes à HWQueue et de surveiller les interruptions d’achèvement à partir de KMD. Pour cette raison, Dxgkrnl a une vue complète du moment où un moteur est actif et inactif.
Dans la soumission de travail en mode utilisateur, Dxgkrnl surveille si un moteur GPU progresse à l’aide de la cadence de délai d’expiration TDR. Par conséquent, s’il est utile de lancer une transition vers l’état F1 plus tôt que dans le délai d’expiration TDR à deux secondes, le KMD peut demander au système d’exploitation de le faire.
Les modifications suivantes ont été apportées pour faciliter cette approche :
Le type d’interruption DXGK_INTERRUPT_GPU_ENGINE_STATE_CHANGE est ajouté à DXGK_INTERRUPT_TYPE. KMD utilise cette interruption pour notifier Dxgkrnl des transitions d’état du moteur qui nécessitent une action d’alimentation GPU ou une récupération de délai d’attente telle que Active -> TransitionToF1 et Active -> Hung.
La structure de données d’interruption EngineStateChange est ajoutée à DXGKARGCB_NOTIFY_INTERRUPT_DATA.
L’énumération DXGK_ENGINE_STATE est ajoutée pour représenter les transitions d’état du moteur pour EngineStateChange.
Lorsque KMD déclenche une interruption DXGK_INTERRUPT_GPU_ENGINE_STATE_CHANGE avec EngineStateChange.NewState défini sur DXGK_ENGINE_STATE_TRANSITION_TO_F1, Dxgkrnl déconnecte toutes les sonneries de HWQueues sur ce moteur, puis lance une transition de composant d’alimentation F0 vers F1.
Lorsque l’UMD tente d’envoyer un nouveau travail au moteur GPU dans l’état F1, il doit reconnecter la sonnette, ce qui entraîne à son tour Dxgkrnl d’initier une transition vers l’état d’alimentation F0.
Transitions d'état D du moteur
Lors d’une transition d’état d’alimentation d’un appareil de D0 à D3, Dxgkrnl met en pause le HWQueue, déconnecte la sonnette (en faisant pivoter l'adresse virtuelle CPU de la sonnette vers une page factice) et met à jour le statut de DoorbellStatusCpuVirtualAddress à D3DDDI_DOORBELL_STATUS_DISCONNECTED_RETRY.
Si UMD appelle D3DKMTConnectDoorbell lorsque le GPU est en D3, il force Dxgkrnl à réveiller le GPU sur D0. Dxgkrnl est également responsable de la reprise de HWQueue et du mappage de la doorbell CPUVA vers un emplacement physique de doorbell.
La séquence d’événements suivante a lieu.
Une panne de gpu D0 vers D3 se produit :
- Dxgkrnl appelle DxgkddiSuspendContext pour tous les contextes HW sur le GPU. KMD supprime ces contextes de la liste du planificateur HW.
- Dxgkrnl déconnecte toutes les sonnettes.
- Dxgkrnl peut éventuellement supprimer toutes les allocations de mémoire tampon annulaire et de leur contrôle de la VRAM si nécessaire. Il le fait une fois que tous les contextes sont suspendus et supprimés de la liste du planificateur de matériel afin que le matériel ne référence aucune mémoire supprimée.
UMD écrit une nouvelle commande dans un HWQueue lorsque le GPU est dans un état D3 :
- UMD voit que la sonnette est déconnectée et appelle D3DKMTConnectDoorbell.
- Dxgkrnl lance une transition D0.
- Dxgkrnl rend toutes les allocations de mémoire tampon en anneau/de contrôle de mémoire tampon en anneau résidentes si elles ont été supprimées.
- Dxgkrnl appelle la fonction DxgkddiCreateDoorbell de KMD pour demander que KMD crée une connexion de sonnette pour ce HWQueue.
- Dxgkrnl appelle DxgkddiResumeContext pour tous les HWContexts. KMD ajoute les files d'attente correspondantes à la liste du planificateur HW.
DDIs pour la soumission de travail en mode utilisateur
DDIS implémentées par KMD
Les DDIS en mode noyau suivants sont ajoutés pour que KMD implémente la prise en charge de la soumission de travail en mode utilisateur.
DxgkDdiCreateDoorbell. Lorsque UMD appelle D3DKMTCreateDoorbell pour créer une porte d’entrée pour un HWQueue, Dxgkrnl effectue un appel correspondant à cette fonction afin que KMD puisse initialiser ses structures de porte d’entrée.
DxgkDdiConnectDoorbell. Lorsque UMD appelle D3DKMTConnectDoorbell, Dxgkrnl effectue un appel correspondant à cette fonction pour que KMD puisse fournir une adresse virtuelle CPU mappée à l'emplacement physique de la sonnette et établir également les connexions nécessaires entre l'objet HWQueue, l'objet sonnette, l'adresse physique de la sonnette, le planificateur GPU, etc.
DxgkDdiDisconnectDoorbell. Lorsque le système d’exploitation souhaite déconnecter une sonnette particulière, il appelle KMD avec cette DDI.
DxgkDdiDestroyDoorbell. Lorsque l’UMD appelle D3DKMTDestroyDoorbell, Dxgkrnl effectue un appel correspondant à cette fonction afin que le KMD puisse détruire ses structures de signalisation.
DxgkDdiNotifyWorkSubmission. Lorsque UMD appelle D3DKMTNotifyWorkSubmission, Dxgkrnl effectue un appel correspondant à cette fonction afin que KMD puisse être informé des nouvelles soumissions de travail.
Dxgkrnl implémenté par DDI
Le rappel DxgkCbDisconnectDoorbell est implémenté par Dxgkrnl. KMD peut appeler cette fonction pour notifier Dxgkrnl que KMD doit déconnecter une sonnette particulière.
Modifications de la clôture de progression de la file d’attente HW
Les files d'attente matérielles exécutées dans le modèle de soumission de travail de l'UM conservent toujours le concept d'une valeur de clôture de progression qui augmente monotoniquement, que l'UMD génère et écrit lorsqu'une mémoire tampon d'instructions est terminée. Pour que Dxgkrnl sache si une file d’attente matérielle particulière a du travail en attente, l’UMD doit mettre à jour la valeur de clôture de progression mise en file d’attente juste avant d’ajouter une nouvelle mémoire tampon de commande à la mémoire tampon d’anneau et de la rendre visible par le GPU. CreateDoorbell.HwQueueProgressFenceLastQueuedValueCPUVirtualAddress est un mappage de processus en lecture/écriture en mode utilisateur de la dernière valeur mise en file d’attente.
Il est essentiel que l’UMD vérifie que la valeur mise en file d’attente est mise à jour juste avant que la nouvelle soumission soit rendue visible par le GPU. Les étapes suivantes sont la séquence d’opérations recommandée. Ils supposent que la file d’attente HW est inactive et que la dernière mémoire tampon terminée a eu une valeur de clôture de progression de N.
- Générez une nouvelle valeur de clôture de progression N+1.
- Renseignez la mémoire tampon de commande. La dernière instruction de la mémoire tampon de commande est une écriture de la valeur de clôture de progression vers N+1.
- Informez le système d’exploitation de la valeur nouvellement mise en file d’attente en définissant *(HwQueueProgressFenceLastQueuedValueCPUVirtualAddress) égal à N+1.
- Rendez la mémoire tampon de commande visible par le GPU en l’ajoutant à la mémoire tampon en anneau.
- Sonnez à la porte.
Arrêt normal et anormal du processus
La séquence d’événements suivante a lieu pendant l’arrêt normal du processus.
Pour chaque HWQueue de l’appareil/contexte :
- Dxgkrnl appelle DxgkDdiDisconnectDoorbell pour déconnecter la porte d’entrée.
- Dxgkrnl attend que la dernière file d’attente HwQueueProgressFenceLastQueuedValueCPUVirtualAddress soit terminée sur le GPU. Les allocations de Ring Buffer/de contrôle de Ring Buffer restent résidentes.
- L’attente de Dxgkrnl est satisfaite et il peut désormais détruire les allocations de mémoire tampon en anneau et de contrôle de mémoire tampon en anneau, ainsi que les objets doorbell et HWQueue.
La séquence d’événements suivante a lieu lors de l’arrêt anormal du processus.
Dxgkrnl indique que l'appareil est en erreur.
Pour chaque contexte d’appareil, Dxgkrnl appelle DxgkddiSuspendContext pour suspendre le contexte. Les allocations de contrôle de mémoire tampon en anneau/mémoire tampon en anneau sont toujours résidentes. KMD préempt le contexte et le supprime de sa liste d’exécution HW.
Pour chaque HWQueue de contexte, Dxglrnl :
a) Appelle DxgkDdiDisconnectDoorbell pour déconnecter la porte d’entrée.
b. Détruit les allocations de contrôle de mémoire tampon en anneau/de mémoire tampon en anneau, ainsi que les objets doorbell et HWQueue.
Exemples de pseudocode
Pseudocode de soumission de travail dans UMD
Le pseudocode suivant est un exemple de base du modèle que UMD doit utiliser pour créer et soumettre du travail à HWQueues à l’aide des API doorbell. Supposons que hHWqueue1 soit le handle d’un HWQueue créé avec l’indicateur UserModeSubmission à l’aide de l’API D3DKMTCreateHwQueue existante.
// Create a doorbell for the HWQueue
D3DKMT_CREATE_DOORBELL CreateDoorbell = {};
CreateDoorbell.hHwQueue = hHwQueue1;
CreateDoorbell.hRingBuffer = hRingBufferAlloc;
CreateDoorbell.hRingBufferControl = hRingBufferControlAlloc;
CreateDoorbell.Flags.Value = 0;
NTSTATUS ApiStatus = D3DKMTCreateDoorbell(&CreateDoorbell);
if(!NT_SUCCESS(ApiStatus))
goto cleanup;
assert(CreateDoorbell.DoorbellCPUVirtualAddress!=NULL &&
CreateDoorbell.DoorbellStatusCPUVirtualAddress!=NULL);
// Get a CPUVA of Ring buffer control alloc to obtain write pointer.
// Assume the write pointer is at offset 0 in this alloc
D3DKMT_LOCK2 Lock = {};
Lock.hAllocation = hRingBufferControlAlloc;
ApiStatus = D3DKMTLock2(&Lock);
if(!NT_SUCCESS(ApiStatus))
goto cleanup;
UINT64* WritePointerCPUVirtualAddress = (UINT64*)Lock.pData;
// Doorbell created successfully. Submit command to this HWQueue
UINT64 DoorbellStatus = 0;
do
{
// first connect the doorbell and read status
ApiStatus = D3DKMTConnectDoorbell(hHwQueue1);
D3DDDI_DOORBELL_STATUS DoorbellStatus = *(UINT64*(CreateDoorbell.DoorbellStatusCPUVirtualAddress));
if(!NT_SUCCESS(ApiStatus) || DoorbellStatus == D3DDDI_DOORBELL_STATUS_DISCONNECTED_ABORT)
{
// fatal error in connecting doorbell, destroy this HWQueue and re-create using traditional kernel mode submission.
goto cleanup_fallback;
}
// update the last queue progress fence value
*(CreateDoorbell.HwQueueProgressFenceLastQueuedValueCPUVirtualAddress) = new_command_buffer_progress_fence_value;
// write command to ring buffer of this HWQueue
*(WritePointerCPUVirtualAddress) = address_location_of_command_buffer;
// Ring doorbell by writing the write pointer value into doorbell address.
*(CreateDoorbell.DoorbellCPUVirtualAddress) = *WritePointerCPUVirtualAddress;
// Check if submission succeeded by reading doorbell status
DoorbellStatus = *(UINT64*(CreateDoorbell.DoorbellStatusCPUVirtualAddress));
if(DoorbellStatus == D3DDDI_DOORBELL_STATUS_CONNECTED_NOTIFY)
{
D3DKMTNotifyWorkSubmission(CreateDoorbell.hDoorbell);
}
} while (DoorbellStatus == D3DDDI_DOORBELL_STATUS_DISCONNECTED_RETRY);
Victimisation du pseudocode de sonnette dans KMD
L’exemple suivant montre comment KMD peut avoir besoin de « virtualiser » et de partager les porte-porte disponibles entre les HWQueues sur les GPU qui utilisent des porte-porte dédiées.
Pseudocode de la VictimizeDoorbell() fonction KMD :
- KMD décide que la sonnette logique
hDoorbell1connectée doit être désactivée et déconnectée. - KMD appelle la fonction Dxgkrnl de
DxgkCbDisconnectDoorbellCB(hDoorbell1->hHwQueue).- Dxgkrnl redirige le CPUVA visible par l'UMD de cette sonnette vers une page factice et met à jour l'état sur D3DDDI_DOORBELL_STATUS_DISCONNECTED_RETRY.
- KMD reprend le contrôle et effectue la victimisation/déconnexion réelle.
- KMD victimise
hDoorbell1et le déconnecte dePhysicalDoorbell1. -
PhysicalDoorbell1est disponible pour une utilisation
- KMD victimise
À présent, tenez compte du scénario suivant :
Il existe une seule sonnette physique dans le PCI BAR avec une CPUVA en mode noyau égale à
0xfeedfeee. Un objet doorbell créé pour un HWQueue est affecté à cette valeur de porte d’entrée physique.HWQueue KMD Handle: hHwQueue1 Doorbell KMD Handle: hDoorbell1 Doorbell CPU Virtual Address: CpuVirtualAddressDoorbell1 => 0xfeedfeee // hDoorbell1 is mapped to 0xfeedfeee Doorbell Status CPU Virtual Address: StatusCpuVirtualAddressDoorbell1 => D3DDDI_DOORBELL_STATUS_CONNECTEDLe système d’exploitation appelle
DxgkDdiCreateDoorbellpour un autreHWQueue2.HWQueue KMD Handle: hHwQueue2 Doorbell KMD Handle: hDoorbell2 Doorbell CPU Virtual Address: CpuVirtualAddressDoorbell2 => 0 // this doorbell object isn't yet assigned to a physical doorbell Doorbell Status CPU Virtual Address: StatusCpuVirtualAddressDoorbell2 => D3DDDI_DOORBELL_STATUS_DISCONNECTED_RETRY // In the create doorbell DDI, KMD doesn't need to assign a physical doorbell yet, // so the 0xfeedfeee doorbell is still connected to hDoorbell1Le système d’exploitation appelle
DxgkDdiConnectDoorbellsurhDoorbell2:// KMD needs to victimize hDoorbell1 and assign 0xfeedfeee to hDoorbell2. VictimizeDoorbell(hDoorbell1); // Physical doorbell 0xfeedfeee is now free and can be used vfor hDoorbell2. // KMD makes required connections for hDoorbell2 with HW ConnectPhysicalDoorbell(hDoorbell2, 0xfeedfeee) return 0xfeedfeee // On return from this DDI, *Dxgkrnl* maps 0xfeedfeee to process address space CPUVA i.e: // CpuVirtualAddressDoorbell2 => 0xfeedfeee // *Dxgkrnl* updates hDoorbell2 status to connected i.e: // StatusCpuVirtualAddressDoorbell2 => D3DDDI_DOORBELL_STATUS_CONNECTED ``
Ce mécanisme n’est pas nécessaire si un GPU utilise des portes d’entrée globales. Au contraire, dans cet exemple, à la fois hDoorbell1 et hDoorbell2 se verraient attribuer la même 0xfeedfeee sonnette physique.