Fractionnement des demandes de transfert DMA
Tout pilote peut avoir besoin de fractionner une demande de transfert et d’effectuer plusieurs opérations de transfert DMA pour satisfaire un IRP donné, en fonction des éléments suivants :
Nombre de registres de carte retournés par IoGetDmaAdapter
Octets de données à transférer, contenus dans le membre Length de l’emplacement de pile d’E/S du pilote pour l’IRP
Nombre de limites de page, dans la mémoire physique système, pour la mémoire tampon vers laquelle ou à partir de laquelle le pilote doit transférer des données
Contraintes spécifiques à l’appareil sur les opérations DMA du pilote. Par exemple, le pilote de disque « AT » du système doit fractionner les demandes de transfert pour plus de 256 secteurs en raison des limitations du contrôleur de disque.
Un pilote peut déterminer le nombre de registres de carte nécessaires pour transférer toutes les données spécifiées par un IRP comme suit :
Appelez MmGetMdlVirtualAddress, en passant un pointeur vers le MDL à Irp-MdlAddress>, pour obtenir l’adresse virtuelle de départ de la mémoire tampon. Notez qu’un pilote ne doit pas tenter d’accéder à la mémoire à l’aide de cette adresse virtuelle. La valeur retournée par MmGetMdlVirtualAddress est un index dans le MDL, pas nécessairement une adresse valide.
Transmettez l’index retourné et la valeur Length dans l’emplacement de la pile d’E/S du pilote de l’IRP à la macro ADDRESS_AND_SIZE_TO_SPAN_PAGES .
Si la valeur retournée par ADDRESS_AND_SIZE_TO_SPAN_PAGES est supérieure à la valeur NumberOfMapRegisters retournée par IoGetDmaAdapter, le pilote ne peut pas transférer toutes les données demandées pour cette IRP en une seule opération DMA. Au lieu de cela, il doit effectuer les opérations suivantes :
Fractionnez la mémoire tampon en morceaux dimensionnés en fonction du nombre de registres de carte disponibles (et de toutes les contraintes DMA spécifiques à l’appareil).
Effectuez autant d’opérations DMA que nécessaire pour répondre à la demande de transfert.
Par exemple, supposons que ADDRESS_AND_SIZE_TO_SPAN_PAGES indique que douze registres de carte sont nécessaires pour répondre à une demande de transfert, mais que la valeur NumberOfMapRegisters retournée par IoGetDmaAdapter n’est que de cinq. (Supposons qu’aucune contrainte DMA spécifique à l’appareil.) Dans ce cas, le pilote doit effectuer trois opérations de transfert DMA, en appelant MapTransfer trois fois pour transférer toutes les données demandées par l’IRP.
Les pilotes de périphérique DMA du système utilisent différentes techniques pour fractionner un transfert DMA lorsqu’il n’y a pas suffisamment de registres de carte pour satisfaire un IRP avec une seule opération d’E/S. Une technique à utiliser est la suivante :
Appelez IoAllocateMdl pour allouer un MDL décrivant une partie de la mémoire tampon utilisateur.
Appelez MmProbeAndLockPages pour verrouiller cette partie de la mémoire tampon utilisateur.
Transférez les données pour cette partie de la mémoire tampon.
Appelez MmUnlockPages et effectuez l’une des opérations suivantes :
- Si le MDL alloué par le pilote à l’étape 1 est suffisamment grand pour la partie suivante du transfert, appelez MmPrepareMdlForReuse et répétez les étapes 2 à 4.
- Sinon, appelez IoFreeMdl et répétez les étapes 1 à 4.
Appelez MmUnlockPages et IoFreeMdl lorsque toutes les données ont été transférées.
Si un pilote de niveau supérieur ne peut pas verrouiller la mémoire tampon utilisateur entière avec MmProbeAndLockPages dans une machine avec une mémoire limitée, il peut effectuer les opérations suivantes :
Appelez IoBuildSynchronousFsdRequest pour allouer un IRP de transfert partiel et verrouiller une partie de la mémoire tampon utilisateur. La zone verrouillée est généralement un multiple de PAGE_SIZE ou est dimensionnée en fonction de la capacité de transfert de l’appareil sous-jacent.
Appelez IoCallDriver pour l’IRP de transfert partiel, puis appelez KeWaitForSingleObject pour attendre qu’un objet d’événement que le pilote a configuré soit associé à son IRP de transfert partiel, si les pilotes inférieurs retournent STATUS_PENDING.
Lorsqu’il reprend le contrôle, répétez les étapes 1 et 2 jusqu’à ce que toutes les données soient transférées, puis terminez l’IRP d’origine.
Lorsqu’un pilote de classe de stockage fractionne les demandes de transfert volumineuses pour les pilotes de port/miniport SCSI sous-jacents, il alloue un IRP supplémentaire pour chaque élément de la demande de transfert. Il enregistre une routine IoCompletion pour chaque IRP alloué au pilote, afin de suivre les status de la demande de transfert complète et de libérer les IRP alloués par le pilote. Ensuite, il envoie ces irps au pilote de port à l’aide d’IoCallDriver.
D’autres pilotes de classe/port peuvent utiliser cette technique uniquement si le pilote de classe peut déterminer le nombre de registres de carte disponibles pour le pilote de port. Le pilote de port doit stocker ces informations de configuration dans le Registre du pilote de classe jumelé, ou les pilotes associés doivent définir une interface privée, à l’aide de demandes de contrôle d’E/S internes d’appareil, pour transmettre les informations de configuration sur le nombre de registres de carte disponibles du pilote de port au pilote de classe.
Un pilote monolithique (c’est-à-dire un pilote qui ne fait pas partie d’une paire classe/port) pour un appareil DMA doit fractionner les demandes de transfert volumineuses pour lui-même. Ces pilotes divisent généralement une demande volumineuse en morceaux et effectuent une séquence d’opérations DMA afin de satisfaire l’IRP.
Si une demande de transfert est trop volumineuse pour que le pilote de périphérique sous-jacent puisse être géré, un pilote de niveau supérieur peut appeler MmGetMdlVirtualAddress et IoBuildPartialMdl, puis configurer une séquence d’irps de transfert partiel pour les pilotes de périphérique sous-jacents.