Using IoBuildAsynchronousFsdRequest in a layered driver

I am writing a device driver that creates a disk device whose reads and
writes are redirected to reads and writes to a disk device.

In my read dispatch routine I am calling IoBuildAsynchronousFsdRequest() to
create a new request. I then set a completion routine passing the original
IRP as the context parameter. Then I mark the original IRP pending by
calling IoMarkIrpPending(). I then call IoCallDriver and return its status
which, I think, should either be STATUS_PENDING or a failure.

PUCHAR currentAddress;

ASSERT ( Irp->MdlAddress != NULL );
currentAddress = MmGetSystemAddressForMdlSafe(Irp->MdlAddress,
NormalPagePriority );

.
.
.

case IRP_MJ_READ:
// Get the length and offset
length = MmGetMdlByteCount(Irp->MdlAddress) ;
// liAdd is a wrapper around the way-too-long RtlLargIntegerAdd() call
byteOffset = liAdd (devExt->StartByte,
irpStack->Parameters.Read.ByteOffset) ;
irp0 = IoBuildAsynchronousFsdRequest (IRP_MJ_READ,
devExt->DiskDevice,
currentAddress,
length,
&byteOffset,
NULL) ;

if (irp0 == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES ;
COMPLETE_REQUEST( Irp, status, information );
IoReleaseRemoveLock(&devExt->RemoveLock, Irp);
return status;
}

IoSetCompletionRoutine (irp0,
VfDiskReadWriteCompletionRoutine,
Irp, // Pass in the original IRP as the context for the completion
routine.
TRUE,
TRUE,
TRUE) ;

// Mark the original IRP as pending
IoMarkIrpPending (Irp) ;

// Call the disk driver
status = IoCallDriver (devExt->DiskDevice, irp0) ;

return status ;

I’m not quite sure what to do in my completion routine. Should I complete
the original IRP? How do I know what status to return?

NTSTATUS VfDiskReadWriteCompletionRoutine (IN PDEVICE_OBJECT deviceObject,
IN PIRP irp,
IN PVOID context)
{
NTSTATUS status ;
PIRP masterIrp ;
ULONG information = 0 ;

// We passed in the master IRP as the context ;
masterIrp = (PIRP)context ;

if( irp->PendingReturned )
{
IoMarkIrpPending(irp);
status = STATUS_MORE_PROCESSING_REQUIRED ;
return status ;
}

status = STATUS_SUCCESS ;
COMPLETE_REQUEST ( masterIrp, status, information );

IoFreeIrp (irp) ;

return status ;
}

Thanks,

Jonathan


You are currently subscribed to ntfsd as: $subst(‘Recip.EmailAddr’)
To unsubscribe send a blank email to leave-ntfsd-$subst(‘Recip.MemberIDChar’)@lists.osr.com

A number of things are wrong with this code:
1.) You should be returning STATUS_PENDING from the dispatch
2.) Completion routines can only return 1 of 2 values:
STATUS_MORE_PROCESSING_REQUIRED or STATUS_SUCCESS. Any other status is
meaningless. In your case, you should free the IRP that you created &
abort completion processing.
3.) You are always completing the original IRP successfully
regardless of the status of IRP that did the real work.
4.) You are always completing the IRP with an information value
of 0. This field is used to indicate length of actual transfer.
5.) You are using a buffer that is seems to be already mapped &
locked to pass into IoBuildAsynchronousFsdRequest. I/O may be remapping
them again - given your situation, I would hand-roll the IRP & avoid
remap.
6.) Regardless - your completion routine should free up any
MDL’s allocated by IoBuildAsynchronousFsdRequest

So you should rephrase your completion routine so (you don’t need to
propagate pending flag for an irp that’s going to be freed before the
completion routine returns)

status = irp->IoStatus.Status;
information = irp->IoStatus.Information;

//
// unlock & free mdls
//
while (irp->MdlAddress != NULL) {
PMDL nextMdl;
nextMdl = Irp->MdlAddress->Next;
MmUnlockPages(Irp->MdlAddress);
IoFreeMdl(Irp->MdlAddress);
Irp->MdlAddress = nextMdl;
}

IoFreeIrp (irp) ;

COMPLETE_REQUEST ( masterIrp, status, information );

return STATUS_MORE_PROCESSING_REQUIRED;

-----Original Message-----
From: xxxxx@powerquest.com
[mailto:xxxxx@powerquest.com]
Sent: Monday, June 25, 2001 6:34 AM
To: File Systems Developers
Subject: [ntfsd] Using IoBuildAsynchronousFsdRequest in a layered driver

I am writing a device driver that creates a disk device whose reads and
writes are redirected to reads and writes to a disk device.

In my read dispatch routine I am calling IoBuildAsynchronousFsdRequest()
to
create a new request. I then set a completion routine passing the
original
IRP as the context parameter. Then I mark the original IRP pending by
calling IoMarkIrpPending(). I then call IoCallDriver and return its
status
which, I think, should either be STATUS_PENDING or a failure.

PUCHAR currentAddress;

ASSERT ( Irp->MdlAddress != NULL );
currentAddress = MmGetSystemAddressForMdlSafe(Irp->MdlAddress,
NormalPagePriority );

.
.
.

case IRP_MJ_READ:
// Get the length and offset
length = MmGetMdlByteCount(Irp->MdlAddress) ;
// liAdd is a wrapper around the way-too-long RtlLargIntegerAdd() call
byteOffset = liAdd (devExt->StartByte,
irpStack->Parameters.Read.ByteOffset) ;
irp0 = IoBuildAsynchronousFsdRequest (IRP_MJ_READ,

devExt->DiskDevice,
currentAddress,
length,
&byteOffset,
NULL) ;

if (irp0 == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES ;
COMPLETE_REQUEST( Irp, status, information );
IoReleaseRemoveLock(&devExt->RemoveLock, Irp);
return status;
}

IoSetCompletionRoutine (irp0,
VfDiskReadWriteCompletionRoutine,
Irp, // Pass in the original IRP as
the context for the completion
routine.
TRUE,
TRUE,
TRUE) ;

// Mark the original IRP as pending
IoMarkIrpPending (Irp) ;

// Call the disk driver
status = IoCallDriver (devExt->DiskDevice, irp0) ;

return status ;

I’m not quite sure what to do in my completion routine. Should I
complete
the original IRP? How do I know what status to return?

NTSTATUS VfDiskReadWriteCompletionRoutine (IN PDEVICE_OBJECT
deviceObject,

IN PIRP irp,

IN PVOID context)
{
NTSTATUS status ;
PIRP masterIrp ;
ULONG information = 0 ;

// We passed in the master IRP as the context ;
masterIrp = (PIRP)context ;

if( irp->PendingReturned )
{
IoMarkIrpPending(irp);
status = STATUS_MORE_PROCESSING_REQUIRED ;
return status ;
}

status = STATUS_SUCCESS ;
COMPLETE_REQUEST ( masterIrp, status, information );

IoFreeIrp (irp) ;

return status ;
}

Thanks,

Jonathan


You are currently subscribed to ntfsd as: xxxxx@windows.microsoft.com
To unsubscribe send a blank email to leave-ntfsd-$subst(‘Recip.MemberIDChar’)@lists.osr.com


You are currently subscribed to ntfsd as: $subst(‘Recip.EmailAddr’)
To unsubscribe send a blank email to leave-ntfsd-$subst(‘Recip.MemberIDChar’)@lists.osr.com

Thanks for the input.

I guess I don’t understand STATUS_MORE_PROCESSING_REQUIRED. When do I
return STATUS_MORE_PROCESSING_REQUIRED from my completion routine and when
do I return STATTUS_SUCCESS?

You said:

5.) You are using a buffer that is seems to be already mapped &
locked to pass into IoBuildAsynchronousFsdRequest. I/O may be remapping
them again - given your situation, I would hand-roll the IRP & avoid
remap.

What does that imply? I was trying to create my own MDLs, but I had
problems with that. I was doing the following:

irp0 = IoAllocateIrp (devExt->DiskDevice->StackSize + 1,
FALSE) ;

// Get the start virtual address and byte count
va = (ULONG_PTR)MmGetMdlVirtualAddress (Irp->MdlAddress) ;
length = MmGetMdlByteCount(Irp->MdlAddress) ;

// Allocate a new MDL
mdl = IoAllocateMdl (va,
length,
FALSE,
FALSE,
irp0) ;

if (mdl == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES ;
COMPLETE_REQUEST( Irp, status, information );
IoReleaseRemoveLock(&devExt->RemoveLock, Irp);
return status;
}

// Build a new partial MDL. For now this will descrive the entire buffer
for the IRP
IoBuildPartialMdl (Irp->MdlAddress,
mdl,
va,
length) ;

irp0->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread ;


You are currently subscribed to ntfsd as: $subst(‘Recip.EmailAddr’)
To unsubscribe send a blank email to leave-ntfsd-$subst(‘Recip.MemberIDChar’)@lists.osr.com

I still would like any help understanding those last 2 questions. That I/O
completion routine made it work though.

Thanks


You are currently subscribed to ntfsd as: $subst(‘Recip.EmailAddr’)
To unsubscribe send a blank email to leave-ntfsd-$subst(‘Recip.MemberIDChar’)@lists.osr.com

You return STATUS_MORE_PROCESSING_REQUIRED from your completion routine when
you want the I/O Manager to give you back ownership of the IRP. If you
return anything other than STATUS_MORE_PROCESSING_REQUIRED from your
completion return, the I/O Manager will still own the IRP, and it will
attempt to call any remaining completion routines and then to deallocate the
IRP.

So, if you create your own IRP using either IoAllocateIrp() or
IoBuildAsynchronousFsdRequest(), then you need to deallocate the IRP
yourself (in your completion routine) and so you don’t want the I/O manger
to try to deallocate it, therefore you MUST return
STATUS_MORE_PROCESSING_REQUIRED from your completion routine for IRPs that
you’ve created. There exception to this is IoBuildSynchronousFsdRequest -
if you use this function then you won’t specify a completion routine, and
the I/O manager WILL deallocate the IRP for you.

-----Original Message-----
From: xxxxx@powerquest.com
[mailto:xxxxx@powerquest.com]
Sent: Monday, June 25, 2001 9:54 AM
To: File Systems Developers
Subject: [ntfsd] RE: Using IoBuildAsynchronousFsdRequest in a layered
driver

Thanks for the input.

I guess I don’t understand STATUS_MORE_PROCESSING_REQUIRED. When do I
return STATUS_MORE_PROCESSING_REQUIRED from my completion routine and when
do I return STATTUS_SUCCESS?

You said:

5.) You are using a buffer that is seems to be already mapped &
locked to pass into IoBuildAsynchronousFsdRequest. I/O may be remapping
them again - given your situation, I would hand-roll the IRP & avoid
remap.

What does that imply? I was trying to create my own MDLs, but I had
problems with that. I was doing the following:

irp0 = IoAllocateIrp (devExt->DiskDevice->StackSize + 1,
FALSE) ;

// Get the start virtual address and byte count
va = (ULONG_PTR)MmGetMdlVirtualAddress (Irp->MdlAddress) ;
length = MmGetMdlByteCount(Irp->MdlAddress) ;

// Allocate a new MDL
mdl = IoAllocateMdl (va,
length,
FALSE,
FALSE,
irp0) ;

if (mdl == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES ;
COMPLETE_REQUEST( Irp, status, information );
IoReleaseRemoveLock(&devExt->RemoveLock, Irp);
return status;
}

// Build a new partial MDL. For now this will descrive the entire buffer
for the IRP
IoBuildPartialMdl (Irp->MdlAddress,
mdl,
va,
length) ;

irp0->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread ;


You are currently subscribed to ntfsd as: xxxxx@legato.com
To unsubscribe send a blank email to leave-ntfsd-$subst(‘Recip.MemberIDChar’)@lists.osr.com


You are currently subscribed to ntfsd as: $subst(‘Recip.EmailAddr’)
To unsubscribe send a blank email to leave-ntfsd-$subst(‘Recip.MemberIDChar’)@lists.osr.com

Reading the below two articles from the OSR website would answer this
question.

http://www.osr.com/ntinsider/1997/ryo.htm
http://www.osr.com/ntinsider/1997/iocomp/iocomp.htm

-----Original Message-----
From: xxxxx@powerquest.com
[mailto:xxxxx@powerquest.com]
Sent: Monday, June 25, 2001 8:54 AM
To: File Systems Developers
Subject: [ntfsd] RE: Using IoBuildAsynchronousFsdRequest in a layered
driver

Thanks for the input.

I guess I don’t understand STATUS_MORE_PROCESSING_REQUIRED. When do I
return STATUS_MORE_PROCESSING_REQUIRED from my completion routine and
when
do I return STATTUS_SUCCESS?

You said:

5.) You are using a buffer that is seems to be already mapped & locked
to pass into IoBuildAsynchronousFsdRequest. I/O may be remapping them
again - given your situation, I would hand-roll the IRP & avoid remap.

What does that imply? I was trying to create my own MDLs, but I had
problems with that. I was doing the following:

irp0 = IoAllocateIrp (devExt->DiskDevice->StackSize + 1,
FALSE) ;

// Get the start virtual address and byte count
va = (ULONG_PTR)MmGetMdlVirtualAddress (Irp->MdlAddress) ;
length = MmGetMdlByteCount(Irp->MdlAddress) ;

// Allocate a new MDL
mdl = IoAllocateMdl (va,
length,
FALSE,
FALSE,
irp0) ;

if (mdl == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES ;
COMPLETE_REQUEST( Irp, status, information );
IoReleaseRemoveLock(&devExt->RemoveLock, Irp);
return status;
}

// Build a new partial MDL. For now this will descrive the entire
buffer
for the IRP
IoBuildPartialMdl (Irp->MdlAddress,
mdl,
va,
length) ;

irp0->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread ;


You are currently subscribed to ntfsd as: xxxxx@MICROSOFT.com To
unsubscribe send a blank email to leave-ntfsd-$subst(‘Recip.MemberIDChar’)@lists.osr.com


You are currently subscribed to ntfsd as: $subst(‘Recip.EmailAddr’)
To unsubscribe send a blank email to leave-ntfsd-$subst(‘Recip.MemberIDChar’)@lists.osr.com

> IRP as the context parameter. Then I mark the original IRP pending by

calling IoMarkIrpPending(). I then call IoCallDriver and return its
status
which, I think, should either be STATUS_PENDING or a failure.

Severe bug. See below.

// Mark the original IRP as pending
IoMarkIrpPending (Irp) ;

// Call the disk driver
status = IoCallDriver (devExt->DiskDevice, irp0) ;
return status ;

Yes. This is a bug. Must be:

IoMarkIrpPending (Irp) ;
IoCallDriver(DeviceObject, SlaveIrp);
return STATUS_PENDING;

You must return STATUS_PENDING if you have called IoMarkIrpPending (and
vice versa - you must call IoMarkIrpPending if you return STATUS_PENDING
from your dispatch routine). Otherwise, you will break the IRP completion
code. IopCompleteRequest will be called twice for the same IRP.

With your code, if the lower driver will return, say, STATUS_SUCCESS - then
you will have a hard-to-find crash.

MS also has exactly your’s bug in CLASS2 driver. This is never mind for MS
since SCSIPORT always returns STATUS_PENDING from IRP_MJ_SCSI handler. :slight_smile:

Max


You are currently subscribed to ntfsd as: $subst(‘Recip.EmailAddr’)
To unsubscribe send a blank email to leave-ntfsd-$subst(‘Recip.MemberIDChar’)@lists.osr.com

> I guess I don’t understand STATUS_MORE_PROCESSING_REQUIRED. When do I

return STATUS_MORE_PROCESSING_REQUIRED from my completion routine and when
do I return STATTUS_SUCCESS?

STATUS_MORE_PROCESSING_REQUIRED aborts IoCompleteRequest immediately.
STATUS_SUCCESS allows IoCompleteRequest to pass the IRP to the top-level IO
completion code.

IRPs which are linked to the current thread (built by
IoBuildSynchronousFsdRequest of IoBuildDeviceIoControlRequest) must be
completed by returning STATUS_SUCCESS from the completion, thus allowing the
top-level to proceed. The top-level completion code will unlink the IRP from
the thread’s list.
IoFreeIrp is a no-no for such IRPs.

On the other hand, IRPs which are NOT linked to the thread (built by
IoBuildAsynchronousFsdRequest of IoAllocateIrp) must NOT be passed to the
top-level completion code - they are not fully initialized for this. So,
STATUS_MORE_PROCESSING_REQUIRED is a must for them, and calling IoFreeIrp is
a must too.

Max


You are currently subscribed to ntfsd as: $subst(‘Recip.EmailAddr’)
To unsubscribe send a blank email to leave-ntfsd-$subst(‘Recip.MemberIDChar’)@lists.osr.com