Freigeben über


Aufteilen von DMA-Übertragungsanforderungen

Jeder Treiber muss möglicherweise eine Übertragungsanforderung aufteilen und mehr als einen DMA-Übertragungsvorgang ausführen, um einen bestimmten IRP zu erfüllen, je nach folgendem:

  • Die Anzahl von Kartenregistern, die von IoGetDmaAdapter zurückgegeben werden.

  • Die zu übertragenden Datenbytes, die im Längenelement des E/A-Stapelspeicherorts des Treibers für den IRP enthalten sind.

  • Die Anzahl der Seitenbegrenzungen im physischen Systemspeicher für den Puffer, in den oder von dem der Treiber Daten übertragen soll

  • Gerätespezifische Einschränkungen für die DMA-Vorgänge des Treibers. Beispielsweise muss der Systemdatenträger "AT" aufgrund der Einschränkungen des Datenträgercontrollers Übertragungsanforderungen für mehr als 256 Sektoren aufteilen.

Ein Treiber kann die Anzahl der Kartenregister bestimmen, die zum Übertragen aller von einem IRP angegebenen Daten erforderlich sind, wie folgt:

  1. Rufen Sie MmGetMdlVirtualAddress auf, und übergeben Sie einen Zeiger auf die MDL unter Irp-MdlAddress>, um die virtuelle Startadresse für den Puffer zu erhalten. Beachten Sie, dass ein Treiber nicht versuchen darf, mithilfe dieser virtuellen Adresse auf den Arbeitsspeicher zuzugreifen. Der von MmGetMdlVirtualAddress zurückgegebene Wert ist ein Index in der MDL, nicht unbedingt eine gültige Adresse.

  2. Übergeben Sie den zurückgegebenen Index und den Wert von Length an der E/A-Stapelposition des Treibers des IRP an das makro ADDRESS_AND_SIZE_TO_SPAN_PAGES .

Wenn der von ADDRESS_AND_SIZE_TO_SPAN_PAGES zurückgegebene Wert größer als der von IoGetDmaAdapterzurückgegebene NumberOfMapRegisters-Wert ist, kann der Treiber nicht alle angeforderten Daten für diesen IRP in einem einzelnen DMA-Vorgang übertragen. Stattdessen muss folgendes ausgeführt werden:

  1. Teilen Sie den Puffer in Teile auf, die der Anzahl der verfügbaren Kartenregister (und allen gerätespezifischen DMA-Einschränkungen) entsprechen.

  2. Führen Sie so viele DMA-Vorgänge wie benötigt aus, um die Übertragungsanforderung zu erfüllen.

Angenommen, ADDRESS_AND_SIZE_TO_SPAN_PAGES gibt an, dass zwölf Kartenregister erforderlich sind, um eine Übertragungsanforderung zu erfüllen, aber der von IoGetDmaAdapter zurückgegebene Wert NumberOfMapRegisters ist nur fünf. (Angenommen, es gibt keine gerätespezifischen DMA-Einschränkungen.) In diesem Fall muss der Treiber drei DMA-Übertragungsvorgänge ausführen und MapTransfer dreimal aufrufen, um alle vom IRP angeforderten Daten zu übertragen.

Die DMA-Gerätetreiber des Systems verwenden verschiedene Techniken, um eine DMA-Übertragung aufzuteilen, wenn nicht genügend Kartenregister vorhanden sind, um einen IRP mit einem einzelnen E/A-Vorgang zu erfüllen. Eine zu verwendende Technik ist die folgende:

  1. Rufen Sie IoAllocateMdl auf, um eine MDL zuzuweisen, die einen Teil des Benutzerpuffers beschreibt.

  2. Rufen Sie MmProbeAndLockPages auf, um diesen Teil des Benutzerpuffers zu sperren.

  3. Übertragen Sie die Daten für diesen Teil des Puffers.

  4. Rufen Sie MmUnlockPages auf, und führen Sie eine der folgenden Aktionen aus:

    • Wenn die MDL, die der Treiber in Schritt 1 zugewiesen hat, groß genug für den nächsten Teil der Übertragung ist, rufen Sie MmPrepareMdlForReuse auf, und wiederholen Sie die Schritte 2 bis 4.
    • Rufen Sie andernfalls IoFreeMdl auf, und wiederholen Sie die Schritte 1 bis 4.
  5. Rufen Sie MmUnlockPages und IoFreeMdl auf, wenn alle Daten übertragen wurden.

Wenn ein Treiber der höchsten Ebene nicht den gesamten Benutzerpuffer mit MmProbeAndLockPages auf einem Computer mit eingeschränktem Arbeitsspeicher sperren kann, kann er die folgenden Aktionen ausführen:

  1. Rufen Sie IoBuildSynchronousFsdRequest auf, um einen Teilübertragungs-IRP zuzuweisen und einen Teil des Benutzerpuffers zu sperren. Der gesperrte Bereich ist in der Regel entweder ein Vielfaches PAGE_SIZE oder ist entsprechend der Übertragungskapazität des zugrunde liegenden Geräts dimensioniert.

  2. Rufen Sie IoCallDriver für das IRP für partielle Übertragung auf, und rufen Sie KeWaitForSingleObject auf, um auf ein Ereignisobjekt zu warten, das der Treiber eingerichtet hat, um dem IRP für teilübertragungen zugeordnet zu werden, wenn niedrigere Treiber STATUS_PENDING zurückgeben.

  3. Wenn die Steuerung wieder erfolgt, wiederholen Sie die Schritte 1 und 2, bis alle Daten übertragen wurden, und schließen Sie dann das ursprüngliche IRP ab.

Wenn ein Speicherklassentreiber große Übertragungsanforderungen für zugrunde liegende SCSI-Port-/Miniporttreiber aufteilt, weist er jedem Teil der Übertragungsanforderung einen zusätzlichen IRP zu. Es registriert eine IoCompletion-Routine für jedes vom Treiber zugewiesene IRP, um die status der vollständigen Übertragungsanforderung nachzuverfolgen und die vom Treiber zugewiesenen IRPs freizusetzen. Anschließend werden diese IRPs mithilfe von IoCallDriver an den Porttreiber gesendet.

Andere Klassen-/Porttreiber können diese Technik nur verwenden, wenn der Klassentreiber bestimmen kann, wie viele Kartenregister für den Porttreiber verfügbar sind. Der Porttreiber muss diese Konfigurationsinformationen in der Registrierung für den gekoppelten Klassentreiber speichern, oder die gekoppelten Treiber müssen mithilfe interner Geräte-E/A-Steuerungsanforderungen eine private Schnittstelle definieren, um Konfigurationsinformationen über die Anzahl der verfügbaren Zuordnungsregister vom Porttreiber an den Klassentreiber zu übergeben.

Ein monolithischer Treiber (d. h. ein Treiber, der nicht Teil eines Klassen-/Portpaars ist) für ein DMA-Gerät muss große Übertragungsanforderungen für sich selbst aufteilen. Solche Treiber teilen in der Regel eine große Anforderung in Teile auf und führen eine Sequenz von DMA-Vorgängen aus, um den IRP zu erfüllen.

Wenn eine Übertragungsanforderung für den zugrunde liegenden Gerätetreiber zu groß ist, kann ein Treiber auf höherer Ebene MmGetMdlVirtualAddress und IoBuildPartialMdl aufrufen und dann eine Sequenz von Teilübertragungs-IRPs für zugrunde liegende Gerätetreiber einrichten.