IoMarkIRPPending and the IRP->PendingReturned flag

Hi,
I was discussing with my teammate about IRPs completion mechanism, and he
raised a question regarding IoMarkIRPPending the the PendingReturned flag.
As I understand, the entire IoMarkIRPPending mechanism is used as an
optimization for the I/O manager in cases in which the IRP sycnrhonously
completes. In these cases, IoCompleteRequest
doesn’t need to “wake” the original caller of the IRP. IoCompleteRequest
determines whether to wake-up the caller (I think by using an APC) by
checking the SL_PENDING_RETURNED flag in the top IRP stack.
The question is why do we need the SL_PENDING_RETURNED flag in the stack
location, and the relatively cumbersome mechanism of propagating it up the
IRP stack when the stack is being unwinded during completion? Another more
simple implementation could be having IoMarkIRPPending just set the
PendingReturned flag in the IRP itself. IoCompleteRequest will check the
flag and determine whether it needs to wake up the caller or not.
With this implementation, Completion routine can avoid doing the notorious
piece of code:
If (pIRP->PendingReturned){
IoMarkIrpPending(pIRP);
}
Not only that this piece of code seems like LINUX black magic, it may lead
to various bugs as you are allowed to do it only if you have a stack
location of your own in the IRP. If the IRP is yours, doing it will usually
blue screen the machine.

Thinking about the Pending flag, brought another issue: If a completion
routine decides to defer the completion(By using Work items for example) and
return STATUS_MORE_PROCESSING_REQUIRED, it must unconditionally call
IoMarkIRPPending on the IRP(Obviously only if the IRP wasn’t generated by
the driver setting the completion routine).

Any commments on those two issues will greatly help.

Thanks,
Eran.

Hi,

The question is why do we need the SL_PENDING_RETURNED flag in the stack
location, and the relatively cumbersome mechanism of propagating it up the
IRP stack when the stack is being unwinded during completion?

Imagine this case:

////////////////////////
// DRIVER A //
////////////////////////
DriverAReadDispatch(){
IoSetCompletionRoutine(&event);
status = IoCallDriver(DriverB);
if (status == PENDING) {
WaitForSingleObject(&event);
status = Irp->IoStatus.Status;
}
//
// Restart completion processing
//
IoCompleteRequest();
return status;
}

DriverACompRoutine() {
KeSetEvent();
return STATUS_M_P_R;
}

////////////////////////
// DRIVER B //
////////////////////////
DriverBReadDispatch() {
IoMarkIrpPending();
return PENDING;
}

In this case, DriverA waited synchronously for the IRP to complete, so it
would be incorrect to propogate the SL_PENDING_RETURNED flag up to the I/O
manager (imagine DriverA being a highest level driver and it should be
clear). This is why it’s a per-stack location concept and not a per-IRP one.

If the IRP is yours, doing it will usually blue screen the machine.

If you’re lucky and running Verifier. If you’re UNlucky you’ve just set a
random bit in a memory location you don’t own, good luck finding that one!
Moral of the story? Don’t do that…

Thinking about the Pending flag, brought another issue: If a completion
routine decides to defer the completion(By using Work items for example)
and return STATUS_MORE_PROCESSING_REQUIRED, it must unconditionally call
IoMarkIRPPending on the IRP(Obviously only if the IRP wasn’t generated by
the driver setting the completion routine).

It’s fair to say that this is a true statement, since if you’re going to be
doing lots of processing on the IRPs then you’ll want to handle them
asynchronously (and his means calling IoMarkIrpPending and returning
PENDING).

Regards,

-scott

Scott Noone
Software Engineer
OSR Open Systems Resources, Inc.
http://www.osronline.com

“Eran Borovik” wrote in message news:xxxxx@ntdev…
> Hi,
> I was discussing with my teammate about IRPs completion mechanism, and he
> raised a question regarding IoMarkIRPPending the the PendingReturned flag.
> As I understand, the entire IoMarkIRPPending mechanism is used as an
> optimization for the I/O manager in cases in which the IRP sycnrhonously
> completes. In these cases, IoCompleteRequest
> doesn’t need to “wake” the original caller of the IRP. IoCompleteRequest
> determines whether to wake-up the caller (I think by using an APC) by
> checking the SL_PENDING_RETURNED flag in the top IRP stack.
> The question is why do we need the SL_PENDING_RETURNED flag in the stack
> location, and the relatively cumbersome mechanism of propagating it up the
> IRP stack when the stack is being unwinded during completion? Another more
> simple implementation could be having IoMarkIRPPending just set the
> PendingReturned flag in the IRP itself. IoCompleteRequest will check the
> flag and determine whether it needs to wake up the caller or not.
> With this implementation, Completion routine can avoid doing the notorious
> piece of code:
> If (pIRP->PendingReturned){
> IoMarkIrpPending(pIRP);
> }
> Not only that this piece of code seems like LINUX black magic, it may lead
> to various bugs as you are allowed to do it only if you have a stack
> location of your own in the IRP. If the IRP is yours, doing it will
> usually blue screen the machine.
>
> Thinking about the Pending flag, brought another issue: If a completion
> routine decides to defer the completion(By using Work items for example)
> and return STATUS_MORE_PROCESSING_REQUIRED, it must unconditionally call
> IoMarkIRPPending on the IRP(Obviously only if the IRP wasn’t generated by
> the driver setting the completion routine).
>
>
> Any commments on those two issues will greatly help.
>
> Thanks,
> Eran.
>
>
>
>

Thanks for the reply.
This issue annoyed me for quite some time and I didn’t think about the case
you mentioned about waiting for IRP to complete synchronously.

Regards,
Eran.

“Scott Noone” wrote in message news:xxxxx@ntdev…
> Hi,
>
>>
>> The question is why do we need the SL_PENDING_RETURNED flag in the stack
>> location, and the relatively cumbersome mechanism of propagating it up
>> the IRP stack when the stack is being unwinded during completion?
>>
>
> Imagine this case:
>
> ////////////////////////
> // DRIVER A //
> ////////////////////////
> DriverAReadDispatch(){
> IoSetCompletionRoutine(&event);
> status = IoCallDriver(DriverB);
> if (status == PENDING) {
> WaitForSingleObject(&event);
> status = Irp->IoStatus.Status;
> }
> //
> // Restart completion processing
> //
> IoCompleteRequest();
> return status;
> }
>
> DriverACompRoutine() {
> KeSetEvent();
> return STATUS_M_P_R;
> }
>
> ////////////////////////
> // DRIVER B //
> ////////////////////////
> DriverBReadDispatch() {
> IoMarkIrpPending();
> return PENDING;
> }
>
>
> In this case, DriverA waited synchronously for the IRP to complete, so it
> would be incorrect to propogate the SL_PENDING_RETURNED flag up to the I/O
> manager (imagine DriverA being a highest level driver and it should be
> clear). This is why it’s a per-stack location concept and not a per-IRP
> one.
>
>>
>> If the IRP is yours, doing it will usually blue screen the machine.
>>
>
> If you’re lucky and running Verifier. If you’re UNlucky you’ve just set a
> random bit in a memory location you don’t own, good luck finding that one!
> Moral of the story? Don’t do that…
>
>>
>> Thinking about the Pending flag, brought another issue: If a completion
>> routine decides to defer the completion(By using Work items for example)
>> and return STATUS_MORE_PROCESSING_REQUIRED, it must unconditionally call
>> IoMarkIRPPending on the IRP(Obviously only if the IRP wasn’t generated by
>> the driver setting the completion routine).
>>
>
> It’s fair to say that this is a true statement, since if you’re going to
> be doing lots of processing on the IRPs then you’ll want to handle them
> asynchronously (and his means calling IoMarkIrpPending and returning
> PENDING).
>
> Regards,
>
> -scott
> –
> Scott Noone
> Software Engineer
> OSR Open Systems Resources, Inc.
> http://www.osronline.com
>
>
> “Eran Borovik” wrote in message news:xxxxx@ntdev…
>> Hi,
>> I was discussing with my teammate about IRPs completion mechanism, and he
>> raised a question regarding IoMarkIRPPending the the PendingReturned
>> flag.
>> As I understand, the entire IoMarkIRPPending mechanism is used as an
>> optimization for the I/O manager in cases in which the IRP sycnrhonously
>> completes. In these cases, IoCompleteRequest
>> doesn’t need to “wake” the original caller of the IRP. IoCompleteRequest
>> determines whether to wake-up the caller (I think by using an APC) by
>> checking the SL_PENDING_RETURNED flag in the top IRP stack.
>> The question is why do we need the SL_PENDING_RETURNED flag in the stack
>> location, and the relatively cumbersome mechanism of propagating it up
>> the IRP stack when the stack is being unwinded during completion? Another
>> more simple implementation could be having IoMarkIRPPending just set the
>> PendingReturned flag in the IRP itself. IoCompleteRequest will check the
>> flag and determine whether it needs to wake up the caller or not.
>> With this implementation, Completion routine can avoid doing the
>> notorious piece of code:
>> If (pIRP->PendingReturned){
>> IoMarkIrpPending(pIRP);
>> }
>> Not only that this piece of code seems like LINUX black magic, it may
>> lead to various bugs as you are allowed to do it only if you have a stack
>> location of your own in the IRP. If the IRP is yours, doing it will
>> usually blue screen the machine.
>>
>> Thinking about the Pending flag, brought another issue: If a completion
>> routine decides to defer the completion(By using Work items for example)
>> and return STATUS_MORE_PROCESSING_REQUIRED, it must unconditionally call
>> IoMarkIRPPending on the IRP(Obviously only if the IRP wasn’t generated by
>> the driver setting the completion routine).
>>
>>
>> Any commments on those two issues will greatly help.
>>
>> Thanks,
>> Eran.
>>
>>
>>
>>
>
>
>