Storport miniport: Win8 does not respect "MAXIMUM TRANSFER LENGTH" in Block Limits VPD

I have found that Windows (at least version 8) issues a Block Limits VPD inquiry to a Storport virtual miniport, but ignores the value returned in “MAXIMUM TRANSFER LENGTH”. I regularly get READ/WRITE SRB’s with DataTransferLength’s greater than the value I advertise. (Please note that I am aware that “MAXIMUM TRANSFER LENGTH” is measured in blocks and I set it correctly.)

I have also found out that Windows does respect the value set in PPORT_CONFIGURATION_INFORMATION::MaximumTransferLength during HwFindAdapter. Because my virtual adapter purports to support multiple different virtual targets I set this value to SP_UNINITIALIZED_VALUE and was hoping to limit the DataTransferLength per target via the Block Limits VPD. Unfortunately this does not seem possible.

It looks like that if I want to limit data transfer lengths to different targets, I will have to break up I/O in my own miniport. I am wondering if any of the experts in this forum have any input on this.

I usually fail all VPD inquiry requests with zero bytes transferred, and
a SCSISTAT_CHECK_CONDITION. I don’t quite remember why I had to do this,
but I did have some issues between OS versions. So, I only return the
standard inquiry data requests:

Properly implementing READ_CAPACITY and READ_CAPACITY16 seemed to solve
issues for me with getting out of bound read/write requests. It’s been a
while since i’ve worked on virtual storport drivers, so I am reaching back
a few years.

On Thu, Jan 10, 2019 at 4:23 PM Bill_Zissimopoulos
wrote:

> OSR https://community.osr.com/
> Bill_Zissimopoulos started a new discussion: Storport miniport: Win8 does
> not respect “MAXIMUM TRANSFER LENGTH” in Block Limits VPD
>
> I have found that Windows (at least version 8) issues a Block Limits VPD
> inquiry to a Storport virtual miniport, but ignores the value returned in
> “MAXIMUM TRANSFER LENGTH”. I regularly get READ/WRITE SRB’s with
> DataTransferLength’s greater than the value I advertise. (Please note that
> I am aware that “MAXIMUM TRANSFER LENGTH” is measured in blocks and I set
> it correctly.)
>
> I have also found out that Windows does respect the value set in
> PPORT_CONFIGURATION_INFORMATION::MaximumTransferLength during
> HwFindAdapter. Because my virtual adapter purports to support multiple
> different virtual targets I set this value to SP_UNINITIALIZED_VALUE and
> was hoping to limit the DataTransferLength per target via the Block Limits
> VPD. Unfortunately this does not seem possible.
>
> It looks like that if I want to limit data transfer lengths to different
> targets, I will have to break up I/O in my own miniport. I am wondering if
> any of the experts in this forum have any input on this.
>
> –
> Reply to this email directly or follow the link below to check it out:
>
> https://community.osr.com/discussion/290929/storport-miniport-win8-does-not-respect-maximum-transfer-length-in-block-limits-vpd
>
> Check it out:
> https://community.osr.com/discussion/290929/storport-miniport-win8-does-not-respect-maximum-transfer-length-in-block-limits-vpd
>

Hmmmm…

The maximum transfer limit is enforced by the Disk Class Driver. You can see the code in ClassPnP’s UTILS.C module to build the request for VPD page 0xB0 (if BlockLimits is supported, of course). Look at ClasspDeviceGetBlockLimitsVPDPage. The code there shows how the request for the Block Limits page is sent and interpreted. Likewise, look in CLASS.C at the function ServiceTransferRequest and XFERPKT.C for InitializeTransferPackets where you’ll see the Maximum Transfer Length enforced and the value returned by your Miniport taken into account.

You should be able to debug this live on the system. The code for the Disk Class Driver is available in the WDK. Build the Disk.sys code, set some breakpoints and get to the root of the problem!

Peter

Peter Viscarola wrote:

You can see the code in ClassPnP’s UTILS.C module to build the request for VPD page 0xB0…

Peter, these are great pointers. Thank you. I will study the relevant source code and see if I can figure out the problem.

Jamie Kirby wrote:

Properly implementing READ_CAPACITY and READ_CAPACITY16 seemed to solve
issues for me with getting out of bound read/write requests.

To clarify the issue is not getting LBA’s out of bounds, but getting DataTransferLength’s greater that what I have advertised (using the Block Limits VPD).

Bill

My analysis of the source code in ServiceTransferRequest indicates that the routine uses the Fdo->DeviceExtension->PrivateFdoData->HwMaxXferLen field to determine the Maximum Transfer Length and whether to split the request.

This field is initialized in a few places:

  • InitializeTransferPackets in WDK8
  • InitializeTransferPackets and ClassReadDriveCapacity in current GitHub release

In all cases the value returned by “Block Limits VPD” is ignored. So unless I am missing something it looks like I have to split I/O in my own miniport.

Source code pointers to GitHub:

Hmmm… Not so sure:

    hwMaxPages = adapterDesc->MaximumPhysicalPages ? adapterDesc->MaximumPhysicalPages-1 : 0;

    fdoData->HwMaxXferLen = MIN(adapterDesc->MaximumTransferLength, hwMaxPages << PAGE_SHIFT);

adapterDesc->MaximumTransferLength is from the VPD… unless I’m mistaken.

But, regardless, you’ll see what’s up for sure in the debugger. Take you 30 minutes and you’ll have your answer.

Peter

adapterDesc->MaximumTransferLength is from the VPD… unless I’m mistaken.

I believe this comes from HwFindAdapter and PORT_CONFIGURATION_INFORMATION.

Here is evidence from WinDbg that HwMaxXferLen is not related to the BlockLimitsVPD:

kd> ?? ((_FUNCTIONAL_DEVICE_EXTENSION *)0xfffffa80032b4890)->PrivateFdoData->HwMaxXferLen
unsigned long 0xffffe000
kd> ?? ((_FUNCTIONAL_DEVICE_EXTENSION *)0xfffffa80032b4890)->FunctionSupportInfo->BlockLimitsData.MaximumTransferLength
unsigned long 0x80

(My miniport sets PORT_CONFIGURATION_INFORMATION:: MaximumTransferLength = SP_UNINITIALIZED_VALUE.)

My head hurts.

But the code doesn’t lie, and you’re looking right at it in the debugger. This is definitely not what I would have expected.

OTOH, I’ve never returned SP_UNINITIALIZED_VALUE to StorPort either. And this VPD page is “relatively new” (you know, like… what… only 15 years old or something).

Thanks for tracing along, AND getting back to us here on the forum. Sad, but it seems that you’re going to have to do exactly as you proposed at the outset: Handle the max transfer length yourself, in your miniport. Yuck! That’s something you’re specifically not supposed to have to handle.

Peter

Peter, thanks for confirming my understanding.

Thanks for tracing along, AND getting back to us here on the forum.

No problem, the NTDEV and NTFSD forums have been extremely helpful to me, so it is the least I can do.

Sad, but it seems that you’re going to have to do exactly as you proposed at the outset: Handle the max transfer length yourself, in your miniport. Yuck! That’s something you’re specifically not supposed to have to handle.

I ended up doing exactly this and somehow it ended up not being very hairy. (Of course I may change my mind on this when I get some weird data corruption 2 years from now when I will have completely forgotten this discussion…)

Thanks for your help!