Page Table Entry leak on bulk read transfer

Hi,

I am in the process of debugging a usb driver that has a System Page Table Entry leak. The driver is perfectly functional (until it runs out of PTEs which takes about 1/2 day). I have traced the problem to be inside a Windows function, so I suspect I am setting something up slightly wrong. Hopefully someone can offer me advice on where to go from here.

BTW to examine the allocated System Page Table Entries I use “!sysptes 1” .

The problem starts in the DispatchReadWrite() routine (for reads). What happens is that DispatchReadWrite() routine calls a completion routine (ReadWriteCompletion()) via IoCallDriver(). Just before IoCallDriver() is executed no PTEs have been consumed. When IoCallDriver() is stepped into it leads to ReadWriteCompletion() but it consumes a PTE *before* reaching ReadWriteCompletion().

When the code gets into the completion routine, because the transfer size is greater than the MAX_TRANSFER_SIZE, the completion routine makes a recursive call to itself (again using IOCallDriver() ). Again as before the call to IOCallDriver() causes a PTE to be consumed.

On the second pass through ReadWriteCompletion() the number of bytes left to transfer is equal to the MAX_TRANSFER_SIZE, so this is the final pass through this function. On this second (and final) pass through, ReadWriteCompletion() follows a path that includes the function IoFreeMdl(). When this function is executed, it frees up one PTE. (Although when the IoAllocateMDL() function was called at the start of DispatchReadWrite(), no PTEs were consumed)

And that pretty much is it. Two PTEs are consumed, and only one is freed each time through the dispatch/completion routines. Below I have included the code of the two functions - with error checking on paths that are never exected taken out.

Any ideas on what I am doing wrong??

NTSTATUS BulkUsb_DispatchReadWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PMDL mdl;
PURB urb;
ULONG totalLength;
ULONG stageLength;
ULONG urbFlags;
BOOLEAN read;
NTSTATUS ntStatus;
ULONG_PTR virtualAddress;
PFILE_OBJECT fileObject;
PDEVICE_EXTENSION deviceExtension;
PIO_STACK_LOCATION irpStack;
PIO_STACK_LOCATION nextStack;
PBULKUSB_RW_CONTEXT rwContext;
PUSBD_PIPE_INFORMATION pipeInformation;

//
// initialize variables
//
urb = NULL;
mdl = NULL;
rwContext = NULL;
totalLength = 0;
irpStack = IoGetCurrentIrpStackLocation(Irp);
fileObject = irpStack->FileObject;
read = (irpStack->MajorFunction == IRP_MJ_READ) ? TRUE : FALSE;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

if(deviceExtension->SSEnable) {

KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent,
Executive,
KernelMode,
FALSE,
NULL);
}

pipeInformation = fileObject->FsContext;

rwContext = (PBULKUSB_RW_CONTEXT)
ExAllocatePoolWithTag(NonPagedPool,
sizeof(BULKUSB_RW_CONTEXT), 0x11111111);

if(Irp->MdlAddress) {

totalLength = MmGetMdlByteCount(Irp->MdlAddress);
}

urbFlags = USBD_SHORT_TRANSFER_OK;
virtualAddress = (ULONG_PTR) MmGetMdlVirtualAddress(Irp->MdlAddress);

if(read) {
urbFlags |= USBD_TRANSFER_DIRECTION_IN;
}
else {
urbFlags |= USBD_TRANSFER_DIRECTION_OUT;
}

//
// the transfer request is for totalLength.
// we can perform a max of BULKUSB_MAX_TRANSFER_SIZE
// in each stage.
//
if(totalLength > BULKUSB_MAX_TRANSFER_SIZE) {
stageLength = BULKUSB_MAX_TRANSFER_SIZE;
}
else {
stageLength = totalLength;
}

mdl = IoAllocateMdl((PVOID) virtualAddress,
totalLength,
FALSE,
FALSE,
NULL);

//
// map the portion of user-buffer described by an mdl to another mdl
//
IoBuildPartialMdl(Irp->MdlAddress,
mdl,
(PVOID) virtualAddress,
stageLength);

urb = ExAllocatePoolWithTag(NonPagedPool,
sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), 0x22222222);

UsbBuildInterruptOrBulkTransferRequest(
urb,
sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
pipeInformation->PipeHandle,
NULL,
mdl,
stageLength,
urbFlags,
NULL);

rwContext->Urb = urb;
rwContext->Mdl = mdl;
rwContext->Length = totalLength - stageLength;
rwContext->Numxfer = 0;
rwContext->VirtualAddress = virtualAddress + stageLength;
rwContext->DeviceExtension = deviceExtension;

//
// use the original read/write irp as an internal device control irp
//

nextStack = IoGetNextIrpStackLocation(Irp);
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack->Parameters.Others.Argument1 = (PVOID) urb;
nextStack->Parameters.DeviceIoControl.IoControlCode =
IOCTL_INTERNAL_USB_SUBMIT_URB;

IoSetCompletionRoutine(Irp,
(PIO_COMPLETION_ROUTINE)BulkUsb_ReadWriteCompletion,
rwContext,
TRUE,
TRUE,
TRUE);

//
// since we return STATUS_PENDING call IoMarkIrpPending.
// This is the boiler plate code.
// This may cause extra overhead of an APC for the Irp completion
// but this is the correct thing to do.
//

IoMarkIrpPending(Irp);

BulkUsb_IoIncrement(deviceExtension);
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject,
Irp); <------------------ consumes 1 PTE

//
// we return STATUS_PENDING and not the status returned by the lower layer.
//
return STATUS_PENDING;
}

NTSTATUS BulkUsb_ReadWriteCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++

Routine Description:

This is the completion routine for reads/writes
If the irp completes with success, we check if we
need to recirculate this irp for another stage of
transfer. In this case return STATUS_MORE_PROCESSING_REQUIRED.
if the irp completes in error, free all memory allocs and
return the status.

–*/
{
ULONG stageLength;
NTSTATUS ntStatus;
PIO_STACK_LOCATION nextStack;
PBULKUSB_RW_CONTEXT rwContext;

//
// initialize variables
//
rwContext = (PBULKUSB_RW_CONTEXT) Context;
ntStatus = Irp->IoStatus.Status;

UNREFERENCED_PARAMETER(DeviceObject);
//
// successfully performed a stageLength of transfer.
// check if we need to recirculate the irp.
//
if(NT_SUCCESS(ntStatus)) {

if(rwContext) {

rwContext->Numxfer +=
rwContext->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;

if(rwContext->Length) {

//
// another stage transfer
//
if(rwContext->Length > BULKUSB_MAX_TRANSFER_SIZE) {
stageLength = BULKUSB_MAX_TRANSFER_SIZE;
}
else {
stageLength = rwContext->Length;
}

IoBuildPartialMdl(Irp->MdlAddress,
rwContext->Mdl,
(PVOID) rwContext->VirtualAddress,
stageLength);

//
// reinitialize the urb
//
rwContext->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength
= stageLength;
rwContext->VirtualAddress += stageLength;
rwContext->Length -= stageLength;

nextStack = IoGetNextIrpStackLocation(Irp);
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack->Parameters.Others.Argument1 = rwContext->Urb;
nextStack->Parameters.DeviceIoControl.IoControlCode =
IOCTL_INTERNAL_USB_SUBMIT_URB;

IoSetCompletionRoutine(Irp,
BulkUsb_ReadWriteCompletion,
rwContext,
TRUE,
TRUE,
TRUE);

IoCallDriver(rwContext->DeviceExtension->TopOfStackDeviceObject,
Irp); <------------------ consumes 1 PTE

return STATUS_MORE_PROCESSING_REQUIRED;
}
else {
//
// this is the last transfer
//
Irp->IoStatus.Information = rwContext->Numxfer;
}
}
}
else {

BulkUsb_DbgPrint(1, (“ReadWriteCompletion - failed with status = %X\n”, ntStatus));
}

if(rwContext) {
BulkUsb_IoDecrement(rwContext->DeviceExtension);
ExFreePoolWithTag(rwContext->Urb, 0x22222222);
IoFreeMdl(rwContext->Mdl); <------------------ frees 1 PTE
ExFreePoolWithTag(rwContext, 0x11111111);
}

return ntStatus;
}

Dr David Braendler

You need to get the latest DDK, this is a bug in the sample. In the
completion routine it should be:

// the source MDL is not mapped and so when the lower
driver
// calls MmGetSystemAddressForMdl(Safe) on Urb->Mdl
(target Mdl),
// system PTEs are used.
// IoFreeMdl calls MmPrepareMdlForReuse to release PTEs
(unlock
// VA address before freeing any Mdl
// Rather than calling IoFreeMdl and IoAllocateMdl each
time,
// just call MmPrepareMdlForReuse
// Not calling MmPrepareMdlForReuse will leak system
PTEs
//
MmPrepareMdlForReuse(rwContext->Mdl);

IoBuildPartialMdl(Irp->MdlAddress,
rwContext->Mdl,
(PVOID) rwContext->VirtualAddress,
stageLength);

the call to MmPrepareMdlForReuse() is missing in previous versions of
the sample

d

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of David Braendler
Sent: Wednesday, July 13, 2005 4:05 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] Page Table Entry leak on bulk read transfer

Hi,

I am in the process of debugging a usb driver that has a System Page
Table Entry leak. The driver is perfectly functional (until it runs out
of PTEs which takes about 1/2 day). I have traced the problem to be
inside a Windows function, so I suspect I am setting something up
slightly wrong. Hopefully someone can offer me advice on where to go
from here.

BTW to examine the allocated System Page Table Entries I use “!sysptes
1” .

The problem starts in the DispatchReadWrite() routine (for reads). What
happens is that DispatchReadWrite() routine calls a completion routine
(ReadWriteCompletion()) via IoCallDriver(). Just before IoCallDriver()
is executed no PTEs have been consumed. When IoCallDriver() is stepped
into it leads to ReadWriteCompletion() but it consumes a PTE *before*
reaching ReadWriteCompletion().

When the code gets into the completion routine, because the transfer
size is greater than the MAX_TRANSFER_SIZE, the completion routine makes
a recursive call to itself (again using IOCallDriver() ). Again as
before the call to IOCallDriver() causes a PTE to be consumed.

On the second pass through ReadWriteCompletion() the number of bytes
left to transfer is equal to the MAX_TRANSFER_SIZE, so this is the final
pass through this function. On this second (and final) pass through,
ReadWriteCompletion() follows a path that includes the function
IoFreeMdl(). When this function is executed, it frees up one PTE.
(Although when the IoAllocateMDL() function was called at the start of
DispatchReadWrite(), no PTEs were consumed)

And that pretty much is it. Two PTEs are consumed, and only one is freed
each time through the dispatch/completion routines. Below I have
included the code of the two functions - with error checking on paths
that are never exected taken out.

Any ideas on what I am doing wrong??

NTSTATUS BulkUsb_DispatchReadWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PMDL mdl;
PURB urb;
ULONG totalLength;
ULONG stageLength;
ULONG urbFlags;
BOOLEAN read;
NTSTATUS ntStatus;
ULONG_PTR virtualAddress;
PFILE_OBJECT fileObject;
PDEVICE_EXTENSION deviceExtension;
PIO_STACK_LOCATION irpStack;
PIO_STACK_LOCATION nextStack;
PBULKUSB_RW_CONTEXT rwContext;
PUSBD_PIPE_INFORMATION pipeInformation;

//
// initialize variables
//
urb = NULL;
mdl = NULL;
rwContext = NULL;
totalLength = 0;
irpStack = IoGetCurrentIrpStackLocation(Irp);
fileObject = irpStack->FileObject;
read = (irpStack->MajorFunction == IRP_MJ_READ) ? TRUE : FALSE;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

if(deviceExtension->SSEnable) {

KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent,
Executive,
KernelMode,
FALSE,
NULL);
}

pipeInformation = fileObject->FsContext;

rwContext = (PBULKUSB_RW_CONTEXT)
ExAllocatePoolWithTag(NonPagedPool,
sizeof(BULKUSB_RW_CONTEXT), 0x11111111);

if(Irp->MdlAddress) {

totalLength = MmGetMdlByteCount(Irp->MdlAddress);
}

urbFlags = USBD_SHORT_TRANSFER_OK;
virtualAddress = (ULONG_PTR) MmGetMdlVirtualAddress(Irp->MdlAddress);

if(read) {
urbFlags |= USBD_TRANSFER_DIRECTION_IN;
}
else {
urbFlags |= USBD_TRANSFER_DIRECTION_OUT;
}

//
// the transfer request is for totalLength.
// we can perform a max of BULKUSB_MAX_TRANSFER_SIZE
// in each stage.
//
if(totalLength > BULKUSB_MAX_TRANSFER_SIZE) {
stageLength = BULKUSB_MAX_TRANSFER_SIZE;
}
else {
stageLength = totalLength;
}

mdl = IoAllocateMdl((PVOID) virtualAddress,
totalLength,
FALSE,
FALSE,
NULL);

//
// map the portion of user-buffer described by an mdl to another mdl
//
IoBuildPartialMdl(Irp->MdlAddress,
mdl,
(PVOID) virtualAddress,
stageLength);

urb = ExAllocatePoolWithTag(NonPagedPool,
sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), 0x22222222);

UsbBuildInterruptOrBulkTransferRequest(
urb,
sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
pipeInformation->PipeHandle,
NULL,
mdl,
stageLength,
urbFlags,
NULL);

rwContext->Urb = urb;
rwContext->Mdl = mdl;
rwContext->Length = totalLength - stageLength;
rwContext->Numxfer = 0;
rwContext->VirtualAddress = virtualAddress + stageLength;
rwContext->DeviceExtension = deviceExtension;

//
// use the original read/write irp as an internal device control irp
//

nextStack = IoGetNextIrpStackLocation(Irp);
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack->Parameters.Others.Argument1 = (PVOID) urb;
nextStack->Parameters.DeviceIoControl.IoControlCode =
IOCTL_INTERNAL_USB_SUBMIT_URB;

IoSetCompletionRoutine(Irp,
(PIO_COMPLETION_ROUTINE)BulkUsb_ReadWriteCompletion,
rwContext,
TRUE,
TRUE,
TRUE);

//
// since we return STATUS_PENDING call IoMarkIrpPending.
// This is the boiler plate code.
// This may cause extra overhead of an APC for the Irp completion
// but this is the correct thing to do.
//

IoMarkIrpPending(Irp);

BulkUsb_IoIncrement(deviceExtension);
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject,
Irp); <------------------ consumes 1 PTE

//
// we return STATUS_PENDING and not the status returned by the lower
layer.
//
return STATUS_PENDING;
}

NTSTATUS BulkUsb_ReadWriteCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++

Routine Description:

This is the completion routine for reads/writes
If the irp completes with success, we check if we
need to recirculate this irp for another stage of
transfer. In this case return STATUS_MORE_PROCESSING_REQUIRED.
if the irp completes in error, free all memory allocs and
return the status.

–*/
{
ULONG stageLength;
NTSTATUS ntStatus;
PIO_STACK_LOCATION nextStack;
PBULKUSB_RW_CONTEXT rwContext;

//
// initialize variables
//
rwContext = (PBULKUSB_RW_CONTEXT) Context;
ntStatus = Irp->IoStatus.Status;

UNREFERENCED_PARAMETER(DeviceObject);
//
// successfully performed a stageLength of transfer.
// check if we need to recirculate the irp.
//
if(NT_SUCCESS(ntStatus)) {

if(rwContext) {

rwContext->Numxfer +=
rwContext->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;

if(rwContext->Length) {

//
// another stage transfer
//
if(rwContext->Length > BULKUSB_MAX_TRANSFER_SIZE) {
stageLength = BULKUSB_MAX_TRANSFER_SIZE;
}
else {
stageLength = rwContext->Length;
}

IoBuildPartialMdl(Irp->MdlAddress,
rwContext->Mdl,
(PVOID) rwContext->VirtualAddress,
stageLength);

//
// reinitialize the urb
//
rwContext->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength
= stageLength;
rwContext->VirtualAddress += stageLength;
rwContext->Length -= stageLength;

nextStack = IoGetNextIrpStackLocation(Irp);
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack->Parameters.Others.Argument1 = rwContext->Urb;
nextStack->Parameters.DeviceIoControl.IoControlCode =
IOCTL_INTERNAL_USB_SUBMIT_URB;

IoSetCompletionRoutine(Irp,
BulkUsb_ReadWriteCompletion,
rwContext,
TRUE,
TRUE,
TRUE);

IoCallDriver(rwContext->DeviceExtension->TopOfStackDeviceObject,
Irp); <------------------ consumes 1 PTE

return STATUS_MORE_PROCESSING_REQUIRED;
}
else {
//
// this is the last transfer
//
Irp->IoStatus.Information = rwContext->Numxfer;
}
}
}
else {

BulkUsb_DbgPrint(1, (“ReadWriteCompletion - failed with status = %X\n”,
ntStatus));
}

if(rwContext) {
BulkUsb_IoDecrement(rwContext->DeviceExtension);
ExFreePoolWithTag(rwContext->Urb, 0x22222222);
IoFreeMdl(rwContext->Mdl); <------------------ frees 1 PTE
ExFreePoolWithTag(rwContext, 0x11111111);
}

return ntStatus;
}

Dr David Braendler


Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256

You are currently subscribed to ntdev as: unknown lmsubst tag argument:
‘’
To unsubscribe send a blank email to xxxxx@lists.osr.com

It seems you’re using old version of BulkUsb sample (code is rather unreadable without formatting). This problem should be fixed in w2k3 DDK, maybe even with XP SP1 DDK. It is a good idea to install the newest available DDK and compare samples code for changes.

Copied from newer source (completion routine):

// the source MDL is not mapped and so when the lower driver
// calls MmGetSystemAddressForMdl(Safe) on Urb->Mdl (target Mdl),
// system PTEs are used.
// IoFreeMdl calls MmPrepareMdlForReuse to release PTEs (unlock
// VA address before freeing any Mdl
// Rather than calling IoFreeMdl and IoAllocateMdl each time,
// just call MmPrepareMdlForReuse
// Not calling MmPrepareMdlForReuse will leak system PTEs
//
MmPrepareMdlForReuse(rwContext->Mdl);

IoBuildPartialMdl(Irp->MdlAddress,
rwContext->Mdl,
(PVOID) rwContext->VirtualAddress,
stageLength);

Best regards,

Michal Vodicka
UPEK, Inc.
[xxxxx@upek.com, http://www.upek.com]


From: xxxxx@lists.osr.com[SMTP:xxxxx@lists.osr.com] on behalf of David Braendler[SMTP:xxxxx@cvs.com.au]
Reply To: Windows System Software Devs Interest List
Sent: Thursday, July 14, 2005 1:04 AM
To: Windows System Software Devs Interest List
Subject: [ntdev] Page Table Entry leak on bulk read transfer

Hi,

I am in the process of debugging a usb driver that has a System Page Table Entry leak. The driver is perfectly functional (until it runs out of PTEs which takes about 1/2 day). I have traced the problem to be inside a Windows function, so I suspect I am setting something up slightly wrong. Hopefully someone can offer me advice on where to go from here.

BTW to examine the allocated System Page Table Entries I use “!sysptes 1” .

The problem starts in the DispatchReadWrite() routine (for reads). What happens is that DispatchReadWrite() routine calls a completion routine (ReadWriteCompletion()) via IoCallDriver(). Just before IoCallDriver() is executed no PTEs have been consumed. When IoCallDriver() is stepped into it leads to ReadWriteCompletion() but it consumes a PTE *before* reaching ReadWriteCompletion().

When the code gets into the completion routine, because the transfer size is greater than the MAX_TRANSFER_SIZE, the completion routine makes a recursive call to itself (again using IOCallDriver() ). Again as before the call to IOCallDriver() causes a PTE to be consumed.

On the second pass through ReadWriteCompletion() the number of bytes left to transfer is equal to the MAX_TRANSFER_SIZE, so this is the final pass through this function. On this second (and final) pass through, ReadWriteCompletion() follows a path that includes the function IoFreeMdl(). When this function is executed, it frees up one PTE. (Although when the IoAllocateMDL() function was called at the start of DispatchReadWrite(), no PTEs were consumed)

And that pretty much is it. Two PTEs are consumed, and only one is freed each time through the dispatch/completion routines. Below I have included the code of the two functions - with error checking on paths that are never exected taken out.

Any ideas on what I am doing wrong??

NTSTATUS BulkUsb_DispatchReadWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PMDL mdl;
PURB urb;
ULONG totalLength;
ULONG stageLength;
ULONG urbFlags;
BOOLEAN read;
NTSTATUS ntStatus;
ULONG_PTR virtualAddress;
PFILE_OBJECT fileObject;
PDEVICE_EXTENSION deviceExtension;
PIO_STACK_LOCATION irpStack;
PIO_STACK_LOCATION nextStack;
PBULKUSB_RW_CONTEXT rwContext;>
PUSBD_PIPE_INFORMATION pipeInformation;

//
// initialize variables
//
urb = NULL;
mdl = NULL;
rwContext = NULL;
totalLength = 0;
irpStack = IoGetCurrentIrpStackLocation(Irp);
fileObject = irpStack->FileObject;
read = (irpStack->MajorFunction == IRP_MJ_READ) ? TRUE : FALSE;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

if(deviceExtension->SSEnable) {

KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent,
Executive,
KernelMode,
FALSE,
NULL);
}

pipeInformation = fileObject->FsContext;

rwContext = (PBULKUSB_RW_CONTEXT)
ExAllocatePoolWithTag(NonPagedPool,
sizeof(BULKUSB_RW_CONTEXT), 0x11111111);

if(Irp->MdlAddress) {

totalLength = MmGetMdlByteCount(Irp->MdlAddress);
}

urbFlags = USBD_SHORT_TRANSFER_OK;
virtualAddress = (ULONG_PTR) MmGetMdlVirtualAddress(Irp->MdlAddress);

if(read) {
urbFlags |= USBD_TRANSFER_DIRECTION_IN;
}
else {
urbFlags |= USBD_TRANSFER_DIRECTION_OUT;
}

//
// the transfer request is for totalLength.
// we can perform a max of BULKUSB_MAX_TRANSFER_SIZE
// in each stage.
//
if(totalLength > BULKUSB_MAX_TRANSFER_SIZE) {
stageLength = BULKUSB_MAX_TRANSFER_SIZE;
}
else {
stageLength = totalLength;
}

mdl = IoAllocateMdl((PVOID) virtualAddress,
totalLength,
FALSE,
FALSE,
NULL);

//
// map the portion of user-buffer described by an mdl to another mdl
//
IoBuildPartialMdl(Irp->MdlAddress,
mdl,
(PVOID) virtualAddress,
stageLength);

urb = ExAllocatePoolWithTag(NonPagedPool,
sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), 0x22222222);

UsbBuildInterruptOrBulkTransferRequest(
urb,
sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
pipeInformation->PipeHandle,
NULL,
mdl,
stageLength,
urbFlags,
NULL);

rwContext->Urb = urb;
rwContext->Mdl = mdl;
rwContext->Length = totalLength - stageLength;
rwContext->Numxfer = 0;
rwContext->VirtualAddress = virtualAddress + stageLength;
rwContext->DeviceExtension = deviceExtension;

//
// use the original read/write irp as an internal device control irp
//

nextStack = IoGetNextIrpStackLocation(Irp);
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack->Parameters.Others.Argument1 = (PVOID) urb;
nextStack->Parameters.DeviceIoControl.IoControlCode =
IOCTL_INTERNAL_USB_SUBMIT_URB;

IoSetCompletionRoutine(Irp,
(PIO_COMPLETION_ROUTINE)BulkUsb_ReadWriteCompletion,
rwContext,
TRUE,
TRUE,
TRUE);

//
// since we return STATUS_PENDING call IoMarkIrpPending.
// This is the boiler plate code.
// This may cause extra overhead of an APC for the Irp completion
// but this is the correct thing to do.
//

IoMarkIrpPending(Irp);

BulkUsb_IoIncrement(deviceExtension);
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject,
Irp); <------------------ consumes 1 PTE

//
// we return STATUS_PENDING and not the status returned by the lower layer.
//
return STATUS_PENDING;
}

NTSTATUS BulkUsb_ReadWriteCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++

Routine Description:

This is the completion routine for reads/writes
If the irp completes with success, we check if we
need to recirculate this irp for another stage of
transfer. In this case return STATUS_MORE_PROCESSING_REQUIRED.
if the irp completes in error, free all memory allocs and
return the status.

–*/
{
ULONG stageLength;
NTSTATUS ntStatus;
PIO_STACK_LOCATION nextStack;
PBULKUSB_RW_CONTEXT rwContext;

//
// initialize variables
//
rwContext = (PBULKUSB_RW_CONTEXT) Context;
ntStatus = Irp->IoStatus.Status;

UNREFERENCED_PARAMETER(DeviceObject);
//
// successfully performed a stageLength of transfer.
// check if we need to recirculate the irp.
//
if(NT_SUCCESS(ntStatus)) {

if(rwContext) {

rwContext->Numxfer +=
rwContext->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;>

if(rwContext->Length) {

//
// another stage transfer
//
if(rwContext->Length > BULKUSB_MAX_TRANSFER_SIZE) {
stageLength = BULKUSB_MAX_TRANSFER_SIZE;
}
else {
stageLength = rwContext->Length;
}

IoBuildPartialMdl(Irp->MdlAddress,
rwContext->Mdl,
(PVOID) rwContext->VirtualAddress,
stageLength);

//
// reinitialize the urb
//
rwContext->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength
= stageLength;
rwContext->VirtualAddress += stageLength;
rwContext->Length -= stageLength;

nextStack = IoGetNextIrpStackLocation(Irp);
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack->Parameters.Others.Argument1 = rwContext->Urb;
nextStack->Parameters.DeviceIoControl.IoControlCode =
IOCTL_INTERNAL_USB_SUBMIT_URB;

IoSetCompletionRoutine(Irp,
BulkUsb_ReadWriteCompletion,
rwContext,
TRUE,
TRUE,
TRUE);

IoCallDriver(rwContext->DeviceExtension->TopOfStackDeviceObject,
Irp); <------------------ consumes 1 PTE

return STATUS_MORE_PROCESSING_REQUIRED;
}
else {
//
// this is the last transfer
//
Irp->IoStatus.Information = rwContext->Numxfer;
}
}
}
else {

BulkUsb_DbgPrint(1, (“ReadWriteCompletion - failed with status = %X\n”, ntStatus));
}

if(rwContext) {
BulkUsb_IoDecrement(rwContext->DeviceExtension);
ExFreePoolWithTag(rwContext->Urb, 0x22222222);
IoFreeMdl(rwContext->Mdl); <------------------ frees 1 PTE
ExFreePoolWithTag(rwContext, 0x11111111);
}

return ntStatus;
}

Dr David Braendler


Questions? First check the Kernel Driver FAQ at http://www.osronline.com/article.cfm?id=256

You are currently subscribed to ntdev as: unknown lmsubst tag argument: ‘’
To unsubscribe send a blank email to xxxxx@lists.osr.com

That was it. Thankyou very much for the help Doron and Michal.

Regards

Dr David Braendler