Eliyas
Fri Mar 09 11:12:41 CST 2007
1) Documentation is not completely accurate. It just describes a typical
scenario but there so many different ways you can handle the IRP.
2) Calvin logic is correct. He doesn't need to mark the pending again in the
completionRoutine because he has already marked his stack location pending
in his dispatch routine. So when the IRP reaches the completion routine of
the driver above, it will have right information (ie Irp->PendingReturned
will be set). Look at the IoCompleteRequest code at the end of this message
to understand how that happens.
Here is the simple rule I follow to figure out whether the completion
routine should mark the IRP pending or not:
RULE:
If your dispatch routine returns the status of IoCallDriver as is then you
should do the following in your completion routine before you return.
if (Irp->PendingReturned) {
IoMarkIrpPending( Irp );
}
Let us examine this rule in little bit more detail:
NTSTATUS
DispathRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine();
return IoCallDriver(TopOfDeviceStack, Irp);
}
The above dispatch routine returns the status of the IoCallDriver as is.
When you do that, you cannot defer the completion of the IRP. In other
words, when the IRP comes back to your completionRoutine, you must let it
complete. In yet another words, you cannot return
STATUS_MORE_PROCESSING_REQUIRED and decide to complete the IRP later. If you
think hard, you will understand the reason. So let us look at how the
completionRoutine for this IRP is going to look:
NTSTATUS
CompletionRoutine_1 (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
if (Irp->PendingReturned) {
IoMarkIrpPending( Irp );
}
return STATUS_CONTINUE_COMPLETION ;
}
NTSTATUS
CompletionRoutine_2 (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
if (Irp->PendingReturned) {
IoMarkIrpPending( Irp );
}
IoCompleteRequest( Irp, IO_NO_INCREMENT);
return STATUS_MORE_PROCESSING_REQUIRED;
}
RULE 2:
If you mark the IRP pending, you must return STATUS_PENDING from your
dispatch routine. In the completion routine, you don't need to mark the IRP
pending again. You can either return STATUS_CONTINUE_COMPLETION or defer the
completion by returning STATUS_MORE_PROCESSING_REQUIRED.
NTSTATUS
DispathRoutine
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
IoMarkIrpPending( Irp );
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine();
IoCallDriver(TopOfDeviceStack, Irp);
return STATUS_PENDING ;
}CompletionRoutine_1(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
return STATUS_CONTINUE_COMPLETION ;
}
NTSTATUS
CompletionRoutine_2 (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
//
// Because you are stopping the completion of the IRP by returning the
// following status, you must complete the IRP later.
//
return STATUS_MORE_PROCESSING_REQUIRED ;
}
This article (
http://support.microsoft.com/kb/320275 - it's split in to two
articles) I wrote about five few years ago describes all the possible ways
(AFAIK) of handling the IRP.It contains mostly code snippet. You have to
compare and contrast the cases to figure out the differences.
I have pasted here only the relevant code of IoCompletionRequest to help you
in understanding the insanity around marking the pending.
1) You will see how the pending flag is copied from the stack location to
the Irp->PendingReturned.
2) You willl see why the deviceobject is NULL in the completion routine.
Another often discussed issue.
IoCompleteRequest()
{
stackPointer = IoGetCurrentIrpStackLocation(Irp);
for (Irp->CurrentLocation++,
Irp->Tail.Overlay.CurrentStackLocation++;
Irp->CurrentLocation <= (CCHAR) (Irp->StackCount + 1);
stackPointer++,
Irp->CurrentLocation++,
Irp->Tail.Overlay.CurrentStackLocation++) {
Irp->PendingReturned = stackPointer->Control & SL_PENDING_RETURNED;
if ( (NT_SUCCESS( Irp->IoStatus.Status ) &&
stackPointer->Control & SL_INVOKE_ON_SUCCESS) ||
(!NT_SUCCESS( Irp->IoStatus.Status ) &&
stackPointer->Control & SL_INVOKE_ON_ERROR) ||
(Irp->Cancel &&
stackPointer->Control & SL_INVOKE_ON_CANCEL)
) {
minorFunction = stackPointer->MinorFunction;
//
// This driver has specified a completion routine. Invoke the
// routine passing it a pointer to its device object and the
// IRP that is being completed.
//
ZeroIrpStackLocation( stackPointer );
if (Irp->CurrentLocation == (CCHAR) (Irp->StackCount + 1)) {
deviceObject = NULL;
}
else {
deviceObject = IoGetCurrentIrpStackLocation(
Irp )->DeviceObject;
}
status = stackPointer->CompletionRoutine( deviceObject,
Irp,
stackPointer->Context
);
if (status == STATUS_MORE_PROCESSING_REQUIRED) {
//
// Note: Notice that if the driver has returned the above
// status value, it may have already DEALLOCATED the
// packet! Therefore, do NOT touch any part of the
// IRP in the following code.
//
return;
}
} else {
if (Irp->PendingReturned && Irp->CurrentLocation <=
Irp->StackCount) {
IoMarkIrpPending( Irp );
}
ZeroIrpStackLocation( stackPointer );
}
} // End of for loop
}