存储类驱动程序的 SplitTransferRequest 例程

返回到 GetDescriptor 例程的 STORAGE_ADAPTER_DESCRIPTOR 数据表示给定 HBA 对类驱动程序的传输能力。 具体而言,此数据指示 MaximumTransferLength (以字节为单位)和 MaximumPhysicalPages:也就是说,HBA 可以在支持系统缓冲区的物理内存中管理多少个非连续页(即其散点/收集支持的程度)。

大多数类驱动程序将指向此配置数据的指针存储在每个设备对象的设备扩展中,因为存储类驱动程序负责拆分超出 HBA 传输数据功能的所有传输请求。 换句话说,类驱动程序的 DispatchReadWrite 例程必须确定每个 IRP 是否请求一个超出 HBA 在单个传输操作中能够处理的传输。

例如,此类 DispatchReadWrite 例程可能具有类似于以下内容的代码:

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; 
} 
    :        : 

类驱动程序无法判断缓冲区在映射后将具有多少个物理中断,因此它必须假定传输中的每个页面都是不相容的,并将页数与允许的物理中断数进行比较。

请注意,此类驱动程序的 DispatchReadWrite 例程会调用 IoMarkIrpPending,然后在用原始IRP调用其 SplitTransferRequest 例程后立即返回 STATUS_PENDING。

为了执行原始传输请求,驱动程序的 SplitTransferRequest 例程创建一个或多个 IRP 来处理大小以适应 HBA 功能的子缓冲区。 对于每个此类IRPSplitTransferRequest例程:

  • 设置 SRB,通常通过调用内部 BuildRequest 例程(请参阅 存储类驱动程序的 BuildRequest 例程

  • 将 MDL 地址从原始 IRP 复制到新的 IRP

  • 将 SRB 中的 DataBuffer 设置为此传输片段的 MDL 中的偏移量(以字节为单位)

  • 在使用 IoCallDriver 将 IRP 发送到端口驱动程序之前,先设置其 IoCompletion 例程。

为了跟踪传输的每个部分,SplitTransferRequest 为每个发送到下一个较低驱动程序的驱动程序分配的 IRP 注册了一个 IoCompletion 例程。 IoCompletion 例程使用 InterlockedIncrementInterlockedDecrement 维护原始 IRP 中已完成的部分传输请求计数,以确保计数准确。

此类 IoCompletion 例程必须释放驱动程序已分配的任何 IRP 和/或 SRB,并且必须在所有请求的数据已被传输或类驱动程序已耗尽其对 IRP 的重试次数时,完成原始 IRP;若由于设备传输错误而失败,则需放弃该 IRP。