存储类驱动程序的 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 功能的子缓冲区。 对于每个此类 IRP, SplitTransferRequest 例程

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

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

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

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

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

此类 IoCompletion 例程必须释放驱动程序已分配的任何 IRP 和/或 SRB,并且必须在传输所有请求的数据或类驱动程序已用尽 IRP 重试后完成原始 IRP,并且必须因设备传输错误而失败。