IoMarkIrpPending before IoSkipCurrentIrpStackLocation?

Hi,

I do this in one of the power dispatch routine. I call IoMarkIrpPending and after that I do a IoSkipCurrentIrpStackLocation. It seems to work fine.
However, the MSDN suggests this:

If your driver calls IoSkipCurrentIrpStackLocation, be careful not to modify the IO_STACK_LOCATION structure in a way that could inadvertently affect the lower driver or the system’s behavior with respect to that driver. Examples include modifying the IO_STACK_LOCATION structure’s Parameters union or calling IoMarkIrpPending.

Can anyone comment?

Thanks in advance.
-Pavan

Sorry if I missed this info. I am sending this IRP down using PoCallDriver(DevExt->NextLowerDriver, Irp);

> I do this in one of the power dispatch routine. I call IoMarkIrpPending and after that I do a

IoSkipCurrentIrpStackLocation. It seems to work fine.

This is because the lower driver also does IoMarkIrpPending. If the lower driver will behave another way (inline IoCompleteRequest), then you will crash.

Can anyone comment?

For me, this documentation is obvious. You must not do such patches to other driver’s stack location.


Maxim S. Shatskih
Windows DDK MVP
xxxxx@storagecraft.com
http://www.storagecraft.com

Well, I did not do that until verifier complained that I am returning STATUS_PENDING from my dispatch without marking the irp as pending. What I do is, I schedule a work item to pass the irp down and return STATUS_PENDING from dispatch. The work item eventually does the IoSkipCurrentIrpStackLocation and PoCallDriver.
Now I have added IoMarkIrpPending before I schedule the work item.
From the comment it not okay. So what is suggested in this case?

If you are just passing the request down unchanged with IoSkip/PoCall, why
are you bothering to post it to a work item?

-scott


Scott Noone
Consulting Associate and Chief System Problem Analyst
OSR Open Systems Resources, Inc.
http://www.osronline.com

Hope to see you at the next OSR kernel debugging class February 14th in
Columbia, MD!

wrote in message news:xxxxx@ntdev…

Well, I did not do that until verifier complained that I am returning
STATUS_PENDING from my dispatch without marking the irp as pending. What I
do is, I schedule a work item to pass the irp down and return STATUS_PENDING
from dispatch. The work item eventually does the
IoSkipCurrentIrpStackLocation and PoCallDriver.
Now I have added IoMarkIrpPending before I schedule the work item.
From the comment it not okay. So what is suggested in this case?

Expected this question :slight_smile:
I learn that there is a scene when POWER_STATE in the IrpStack->Parameters.Power.State is >= DevExt->DevicePowerState, caller will not at PASSIVE_LEVEL. So I need a work item in this case…

from your work item, are you not just doing:

IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(devObj, Irp);

??

If you call IoSkipCurrentIrpStackLocation, you don’t HAVE a stack location in which to set the pending bit. So, calling IoMarkIrpPending in this case is a not allowed. You’re effectively setting the pending bit for the driver you’re calling.

Peter
OSR

>I learn that there is a scene when POWER_STATE in the

IrpStack->Parameters.Power.State is >= DevExt->DevicePowerState, caller
will not >at PASSIVE_LEVEL. So I need a work item in this case…

If you are receiving power requests at IRQL DISPATCH_LEVEL it means that you
have DO_POWER_PAGABLE cleared in your device object flags. This indicates to
the system that you will not do anything in your power path that would
generate paging I/O, thus you shouldn’t need to be called at IRQL
PASSIVE_LEVEL.

And, in fact, if you are using the system worker queue package you are
breaking this contract of not generating paging I/O. The delayed and
critical system worker threads wait for work to do with a wait mode of
UserMode, meaning that their kernel stacks are pageable. Thus, given
sufficient memory pressure and *just* the right circumstances you can
deadlock the system.

So, to get back to the original problem, why do you think you need to post
this? If DO_POWER_PAGABLE is cleared in your device object it likely means
that it is cleared in your PDO’s device object, which means that the bus
driver is expecting to receive power IRPs at DISPATCH_LEVEL and you can just
IoSkip/PoCallDriver the IRP.

-scott


Scott Noone
Consulting Associate and Chief System Problem Analyst
OSR Open Systems Resources, Inc.
http://www.osronline.com

Hope to see you at the next OSR kernel debugging class February 14th in
Columbia, MD!

wrote in message news:xxxxx@ntdev…

Expected this question :slight_smile:
I learn that there is a scene when POWER_STATE in the
IrpStack->Parameters.Power.State is >= DevExt->DevicePowerState, caller will
not at PASSIVE_LEVEL. So I need a work item in this case…

>So, to get back to the original problem, why do you think you need to post
this? If DO_POWER_PAGABLE is cleared in your device object it likely means
that it is cleared in your PDO’s device object, which means that the bus
driver is expecting to receive power IRPs at DISPATCH_LEVEL and you can just
IoSkip/PoCallDriver the IRP.

DDK sample suggests we should have a work item and handle it at DISPATCH level.
Which is exactly what i was doing. As i said, I was returning STATUS_PENDING from dispatch and the work item would take care of the rest. [work item would just do a IoSkip…/PoCall…]
I was ok with this but not the verifier. Verifier says I am returning PENDING without marking irp pending…

this is getting a bit tedious. you are not actually answering the questions being asked, you are just describing the problem over and over. what device stack are you in? what ddk sample is leading you supposedly in the wrong direction?

d

Let me try a different tack and avoid the question of why you want to do
whatever it is that you’re doing in your larger scheme of things.
Concentrate instead just on understanding the mechanics of an IRP going down
the I/O stack.

If you receive an IRP in your dispatch routine and you want just to pass the
IRP down the stack immediately and without changing any parameters, the
general way is to copy the contents of your stack location to the next
device’s, set a completion routine and call the next device: thus,
IoCopyCurrentIrpStackLocationToNext, IoSetCompletionRoutine and
IoCallDriver.

In the special case where you don’t want to see the IRP’s completion, you
have a choice of ways. You can use the general way and just not specify a
completion routine. But there is also a shortcut way which gets an
efficiency by recognising that the lower device can be called as if you had
never been involved with this IRP. There is no need to copy the stack
location or to set a NULL completion routine: thus,
IoSkipCurrentIrpStackLocation and IoCallDriver.
If you receive an IRP in your dispatch routine but you want to hold on to
the IRP, thinking to pass it down later, you must have your dispatch routine
mark the IRP as pending. This means both that you call IoMarkIrpPending and
return STATUS_PENDING (and don’t forget the IRP’s IoStatus). If you were not
calling IoMarkIrpPending from your dispatch routine when returning
STATUS_PENDING, then the Driver Verifier was entirely correct to complain.

When you get round to passing the IRP down, however it is that you get round
to it, you are no longer in the dispatch routine. Even if you will not
change any parameters and don’t want to see the IRP complete, the lower
device is not going to be called exactly as if you had never been involved.
You therefore lose the right to the shortcut way of passing the IRP down.
You must use the longer sequence of IoCopyCurrentIrpStackLocation,
IoSetCompletionRoutine and IoCallDriver even though you are not changing the
request parameters and don’t want to be called on completion.

Geoff.

AND go re-read what SNoone said:

SNoone isn’t guessing. He’s TELLING you how it is.

Peter
OSR

(not particularly related to the OPs question)

ahhhhh… SORT of correct and, I’d argue, a bit misleading. Sorry.

There really is no difference, in terms of IoSkipCurrentIrpStackLocation, as to whether you’re passing the IRP from your dispatch routine or from a worker thread. That’s a bit of an artificial distinction.

The KEY is “WHAT am I returning from my dispatch routine and WHEN?” – That’s ALL that matters.

If the dispatch routine is going to send the request off to a worker thread for processing, consider what the dispatch routine does immediately after sending that request off to the worker thread.

If the dispatch routine returns a status other than STATUS_PENDING (immediately after sending the IRP to a worker thread), that’s an obvious error… The IRP will be completed by the I/O Manager, and you’ll get a blue screen for multiple IRP completions.

If the dispatch routine returns STATUS_PENDING (immediately after sending the IRP to a worker thread), then… well… the driver has decided that it DOES want to take some independent action with this IRP. It’s PENDED it. So the driver *is* consuming a stack location in this case. So, the driver cannot subsequently use IoSkipCurrentStackLocation to pass the IRP down the stack. This is the case to which Mr. Chappel was referring above.

But there’s one more option: Suppose the driver BLOCKS (such as by using KeWaitForSingleObject) immediately after sending the IRP to the worker thread? And suppose the driver contrives to unblock, and then return the status returned by the target driver, from its dispatch routine. In THAT case, the driver is NOT consuming an I/O Stack Location, and worker thread CAN call IoSkipCurrentIrpStackLocation, and all is well. Now, I admit this is a weird case… a rare one… but it IS possible. And this case demonstrates that the issue isn’t the context in which the target driver is called, but rather what’s returned from the dispatch routine and when.

There’s a reason that when we describe I/O Completion in class, we say it’s one of the two most complex topics in Windows driver development :slight_smile:

Peter
OSR

>IoSkipCurrentIrpStackLocation and PoCallDriver.

If you call IoSkip, you must return the result of IoCallDriver and not STATUS_PENDING.


Maxim S. Shatskih
Windows DDK MVP
xxxxx@storagecraft.com
http://www.storagecraft.com

>cannot subsequently use IoSkipCurrentStackLocation to pass the IRP down the stack.

Are you sure?

Imagine:

DriverDispatch:
IoMarkIrpPending
IoQueueWorkItem
return STATUS_PENDING

work item routine:
IoSkipXxx
(VOID)IoCallDriver(LowerDO, Irp);

For me, this is OK.

Am I wrong?

What is surely a bug:

a) DriverDispatch:
IoMarkIrpPending
IoSkipXxx
return IoCallDriver(LowerDO, Irp);

and

a) DriverDispatch:
IoSkipXxx
(VOID)IoCallDriver(LowerDO, Irp);
return STATUS_PENDING;


Maxim S. Shatskih
Windows DDK MVP
xxxxx@storagecraft.com
http://www.storagecraft.com

Yes, you are wrong. Even when it works, you’re still wrong architecturally, and opening yourself up for trouble.

Consider what happens when the driver below you completes the request synchronously from within his dispatch routine. SL_PENDING_RETURNED is set in HIS stack location (because you set it by calling IoMarkIrpPending and then IoSkipCurrentIrpStackLocation). This is the equivalent of him doing:

IoMarkIrpPending(…);

Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = (something);

IoCompleteRequest(Irp, 0);
return STATUS_SUCCESS;

And I’m sure you agree that THIS formulation is NOT correct.

Peter
OSR

wrote in message news:xxxxx@ntdev…
> ahhhhh… SORT of correct and, I’d argue, a bit misleading. Sorry.

You could end up arguing all sorts of things, once you rush to interpret a
paragraph independently of the context that was set up in the paragraph
immediately before.

> There really is no difference, in terms of IoSkipCurrentIrpStackLocation,
> as to whether you’re passing the IRP from your dispatch routine or from
> a worker thread. That’s a bit of an artificial distinction.

The artifice is yours. The distinction I made is only between: passing the
IRP down from a dispatch routine; and passing the IRP down from a deferred
execution that was scheduled from a dispatch routine that “pends” the IRP.
In the former, the IRP can be passed to the lower device as if you were
never involved, and you therefore have the safe use of
IoSkipCurrentIrpStackLocation. In the latter, which is the OP’s situation,
you have got involved enough that the IRP cannot safely be presented to the
lower device as if you were never involved.

True, I didn’t spell out what exactly is enough involvement to cause this. I
figured that the OP’s immediate need, if he persists with his design for
better or worse, is to know that by becoming sufficiently involved he picks
up the necessity of avoiding IoSkipCurrentIrpStackLocation. You’re correct,
of course, that this extra involvement that puts
IoSkipCurrentIrpStackLocation out of bounds doesn’t come just because you
pass the IRP from a worker thread rather than a dispatch routine. But I
didn’t say it does.

Perhaps I should be grateful you didn’t misinterpret me as saying that what
matters is whether you pass the IRP from a dispatch routine or from a mere
subroutine of the dispatch routine.

Geoff.

Ouch, Mr. Chappell… a bit touchy, are we? And on only your SECOND POST to this list?!? That’s not a good start. Yikes! Do you think I *deliberately* misinterpreted your post? I most certainly did not. What POSSIBLE point would there be in that?

In fact, the context in the “paragraph immediately before” includes the following sentence:

(capitals of MUST mine)

This is precisely the context I was using to interpret the follow-on paragraph, and what I was trying to clarify. You say “you must have your dispatch routine mark the IRP as pending”… but in fact there’s another alternative: Your dispatch routine could BLOCK. Your post did not account for this alternative.

That’s all I was seeking to clarify Mr. Chappell. Maybe YOU understand this detail. But your post certainly does not make this clear. And my gentle attempt to clarify the situation and flesh-out the various alternatives certainly does *not* warrant a sarcastic rebuke.

Peter
OSR

> “Maxim S. Shatskih” wrote in message
> news:xxxxx@ntdev…
> DriverDispatch:
> IoMarkIrpPending
> IoQueueWorkItem
> return STATUS_PENDING
>
> work item routine:
> IoSkipXxx
> (VOID)IoCallDriver(LowerDO, Irp);
>
> For me, this is OK.

This is what the OP is doing, at least as I understand his problem (putting
aside whether what he wants to do may anyway be unsound). But it is not OK.

The point to IoSkipCurrentIrpStackLocation ahead of calling the lower device
is that you have done nothing to your stack location, which may as well be
used by the lower device. Once you alter the contents of your stack
location, you have no business calling IoSkipCurrentIrpStackLocation, no
matter where you call it from.

At least, that’s what seems to be intended as the design. Sometimes, and
perhaps even often, there will be no consequence if you contravene this
design. It depends, of course, on what change you make and when or whether
anyone gets to care. In the particular case where your change is made by
calling IoMarkIrpPending, the obvious effect is that the lower device’s
dispatch routine starts executing as if it had already called
IoMarkIrpPending itself. If it would always call this itself, then no harm
is done (I suspect off the top of my head, but have not checked). But it is
intrusive, at least, and escaping ill effect doesn’t make the intrusiveness
OK.

Geoff.

Peter,

> <…> in fact there’s another alternative: Your dispatch routine could BLOCK <…>

Don’t you block an arbitrary thread which (to me) breaks the implied guarantee of forward progress?