SplitTransferRequest-Routine des Speicherklassentreibers
Die STORAGE_ADAPTER_DESCRIPTOR Daten, die an die GetDescriptor-Routine zurückgegeben werden, gibt die Übertragungsfunktionen eines bestimmten HBA an den Klassentreiber an. Diese Daten geben insbesondere die MaximumTransferLength in Bytes und die MaximumPhysicalPages an: d. h. wie viele nicht zusammenhängende Seiten der HBA im physischen Speicher verwalten kann, der einen Systempuffer unterstützt (d. h. den Umfang seiner Scatter-/Gather-Unterstützung).
Die meisten Klassentreiber speichern einen Zeiger auf diese Konfigurationsdaten in der Geräteerweiterung jedes Geräteobjekts, da Speicherklassentreiber für die Aufteilung aller Übertragungsanforderungen verantwortlich sind, die die Fähigkeit des HBA zum Übertragen von Daten überschreiten. Anders ausgedrückt: Die DispatchReadWrite-Routine eines Klassentreibers muss bestimmen, ob jede IRP eine Übertragung anfordert, die mehr ist, als der HBA in einem einzelnen Übertragungsvorgang verarbeiten kann.
Eine solche DispatchReadWrite-Routine könnte beispielsweise Code wie folgt aufweisen:
PSTORAGE_ADAPTER_DESCRIPTOR adapterDescriptor =
commonExtension->PartitionZeroExtension->AdapterDescriptor;
ULONG transferPages;
ULONG maximumTransferLength =
adapterDescriptor->MaximumTransferLength;
: :
//
// Calculate number of pages in this transfer
//
transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(
MmGetMdlVirtualAddress(Irp->MdlAddress),
currentIrpStack->Parameters.Read.Length);
//
// Check whether requested length is greater than the maximum number
// of bytes that can be transferred in a single operation
//
if (currentIrpStack->Parameters.Read.Length > maximumTransferLength ||
transferPages > adapterDescriptor->MaximumPhysicalPages) {
transferPages = adapterDescriptor->MaximumPhysicalPages - 1;
if (maximumTransferLength > transferPages << PAGE_SHIFT) {
maximumTransferLength = transferPages << PAGE_SHIFT;
}
IoMarkIrpPending(Irp);
SplitTransferRequest(DeviceObject,
Irp,
maximumTransferLength);
return STATUS_PENDING;
}
: :
Der Klassentreiber kann nicht feststellen, wie viele physische Unterbrechungen der Puffer aufweisen wird, nachdem er zugeordnet wurde. Daher muss er davon ausgehen, dass jede Seite in der Übertragung unauffällig ist, und die Anzahl der Seiten mit der Anzahl der zulässigen physischen Unterbrechungen vergleichen.
Beachten Sie, dass die DispatchReadWrite-Routine eines solchen Treibers IoMarkIrpPending aufruft und sofort nach einem Aufruf der SplitTransferRequest-Routine mit der ursprünglichen IRP STATUS_PENDING zurückgibt.
Zum Ausführen der ursprünglichen Übertragungsanforderung erstellt die SplitTransferRequest-Routine des Treibers einen oder mehrere IRPs, um Unterbuffer zu verarbeiten, die den Funktionen des HBA entsprechen. Für jede solche IRP ist die SplitTransferRequest-Routine :
Richtet einen SRB ein, in der Regel durch Aufrufen einer internen BuildRequest-Routine (siehe BuildRequest-Routine des Speicherklassentreibers).
Kopiert die MDL-Adresse vom ursprünglichen IRP in die neue IRP
Legt den DataBuffer im SRB auf einen Offset in Bytes in der MDL für diesen Teil der Übertragung fest.
Richtet die IoCompletion-Routine vor dem Senden des IRP an den Porttreiber mit IoCallDriver ein.
Um jeden Teil der Übertragung nachzuverfolgen, registriert SplitTransferRequest eine IoCompletion-Routine für jeden vom Treiber zugewiesenen IRP, den es an den nächstniedrigen Treiber sendet. Die IoCompletion-Routine verwaltet die Anzahl der abgeschlossenen Teilübertragungsanforderungen im ursprünglichen IRP mithilfe von InterlockedIncrement und InterlockedDecrement , um sicherzustellen, dass die Anzahl korrekt ist.
Eine solche IoCompletion-Routine muss alle IRPs und/oder SRBs freigeben, die der Treiber zugewiesen hat, und die ursprüngliche IRP abschließen, wenn alle angeforderten Daten übertragen wurden oder wenn der Klassentreiber wiederholungsversuche des IRP erschöpft hat und aufgrund von Geräteübertragungsfehlern fehlschlagen muss.