Waiting for lower level driver to complete IRP

I have inherited some code which does something I suspect is not the recommended approach.

It is an upper filter driver. Some of the messages it intercepts it passes on to the next lower driver, registers a completion routine and then waits for that completion routine to be called before returning. ie:

EVENT event;
KeInitializeEvent(&event, NotificationEvent, FALSE);
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) ForwardAndWaitCompletionRoutine, (PVOID) &event, TRUE, TRUE, TRUE);

NTSTATUS status = IoCallDriver(LowerDeviceObject, Irp);
if (status == STATUS_PENDING) { // wait for completion
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = Irp->IoStatus.Status;
}
return status;

Is this ok/inadvisable?

Cheers,
Ben

Nothing is fundamentally wrong with it. The only reason there can be an issue is that you are making an async io synchronous .

d

debt from my phone

-----Original Message-----
From: xxxxx@nchsoftware.com
Sent: Wednesday, August 03, 2011 10:06 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] Waiting for lower level driver to complete IRP

I have inherited some code which does something I suspect is not the recommended approach.

It is an upper filter driver. Some of the messages it intercepts it passes on to the next lower driver, registers a completion routine and then waits for that completion routine to be called before returning. ie:

EVENT event;
KeInitializeEvent(&event, NotificationEvent, FALSE);
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) ForwardAndWaitCompletionRoutine, (PVOID) &event, TRUE, TRUE, TRUE);

NTSTATUS status = IoCallDriver(LowerDeviceObject, Irp);
if (status == STATUS_PENDING) { // wait for completion
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = Irp->IoStatus.Status;
}
return status;

Is this ok/inadvisable?

Cheers,
Ben


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

Ok thanks for the reply.

One other question I have related to this issue is at what point does the IRP become invalid? ie. when is it no longer safe to access its buffers?

Cheers,
Ben

With such a code (provided you have returned STATUS_MORE_PROCESSING_REQUIRED from your completion routine) - it is save to access the IRP till return from your dispatch function.

Or, if you have created the IRP yourself, you must call IoFreeIrp yourself too.


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

wrote in message news:xxxxx@ntdev…
> Ok thanks for the reply.
>
> One other question I have related to this issue is at what point does the IRP become invalid? ie. when is it no longer safe to access its buffers?
>
> Cheers,
> Ben
>
>
>

It’s not an equivalent of just calling IoCallDriver and being done with it. We need to see your completion routine.

First, the completion function MUST return STATUS_MORE_PROCESSING_REQUIRED, otherwise IRP pointer becomes invalid as soon as KeWaitForSingleObject returns.

But if the completion routine returns STATUS_MORE_PROCESSING_REQUIRED, the IRP stays incomplete yet. You need to call IoCompleteRequest on it again.

Basic answer is that you are allowed to touch the buffers until you send the irp somewhere else . Once sent, you can touch the buffers once the irp is back in your possession. After you complete the irp, you may no longer touch the buffers

d

debt from my phone

-----Original Message-----
From: xxxxx@nchsoftware.com
Sent: Thursday, August 04, 2011 12:00 AM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Waiting for lower level driver to complete IRP

Ok thanks for the reply.

One other question I have related to this issue is at what point does the IRP become invalid? ie. when is it no longer safe to access its buffers?

Cheers,
Ben


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

xxxxx@nchsoftware.com wrote:

One other question I have related to this issue is at what point does the IRP become invalid? ie. when is it no longer safe to access its buffers?

You can think about the concept of IRP “ownership”. When the IRP is
handed to you, you “own” it. You continue to own it until you hand it
off to another driver with IoCallDriver, or complete the IRP with
IoCompleteRequest. If you install a completion routine, you regain
ownership in the completion routine until you call IoCompleteRequest.

While you own the IRP, it is safe to access its buffers.

In the example you cited, your KeWaitForSingleObject will not return
until your completion routine asserts the event and completes the IRP.
At that time, you no longer own the IRP, so you cannot touch the buffers.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Ok thanks so much for all the answers. From what I understand it is ok to do the following. This is a trimmed code snippet:

// my dispatch function
NTSTATUS AudioDevice::DispatchDeviceControl(PIRP pIrp)
{
NTSTATUS status = ForwardAndWait(pIrp);
CHAR* buffer = (CHAR*)(pIrp->UserBuffer);
… do stuff with buffer …
IoCompleteRequest(pIrp);
}

// completion routine
NTSTATUS CompRoutine(PDEVICE_OBJECT fdo, PIRP Irp, PKEVENT pev)
{
Irp->IoStatus.Status = STATUS_MORE_PROCESSING_REQUIRED;
KeSetEvent(pev, IO_NO_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}

// wait function
NTSTATUS LAudioDevice::ForwardAndWait(PIRP Irp)
{
KEVENT event;
KeInitializeEvent(&event, NotificationEvent, FALSE);
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE)CompRoutine, (PVOID) &event, TRUE, TRUE, TRUE);

NTSTATUS status = IoCallDriver(LowerDeviceObject, Irp);
if (status == STATUS_PENDING) { // wait for completion
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = Irp->IoStatus.Status;
}
return status;
}

Cheers,
Ben

The completion routine is clobbering the Irp->IoStatus.Status set by the
lower driver, replacing it with a junk.
setting event is not necessary if the lower didn’t pend. This is just a
minor thing.

Calvin

On Thu, Aug 4, 2011 at 4:42 PM, wrote:

> Ok thanks so much for all the answers. From what I understand it is ok to
> do the following. This is a trimmed code snippet:
>
> // my dispatch function
> NTSTATUS AudioDevice::DispatchDeviceControl(PIRP pIrp)
> {
> NTSTATUS status = ForwardAndWait(pIrp);
> CHAR* buffer = (CHAR*)(pIrp->UserBuffer);
> … do stuff with buffer …
> IoCompleteRequest(pIrp);
> }
>
> // completion routine
> NTSTATUS CompRoutine(PDEVICE_OBJECT fdo, PIRP Irp, PKEVENT pev)
> {
> Irp->IoStatus.Status = STATUS_MORE_PROCESSING_REQUIRED;
> KeSetEvent(pev, IO_NO_INCREMENT, FALSE);
> return STATUS_MORE_PROCESSING_REQUIRED;
> }
>
> // wait function
> NTSTATUS LAudioDevice::ForwardAndWait(PIRP Irp)
> {
> KEVENT event;
> KeInitializeEvent(&event, NotificationEvent, FALSE);
> IoCopyCurrentIrpStackLocationToNext(Irp);
> IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE)CompRoutine, (PVOID)
> &event, TRUE, TRUE, TRUE);
>
> NTSTATUS status = IoCallDriver(LowerDeviceObject, Irp);
> if (status == STATUS_PENDING) { // wait for completion
> KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
> status = Irp->IoStatus.Status;
> }
> return status;
> }
>
> Cheers,
> Ben
>
>
> —
> NTDEV is sponsored by OSR
>
> For our schedule of WDF, WDM, debugging and other seminars visit:
> http://www.osr.com/seminars
>
> To unsubscribe, visit the List Server section of OSR Online at
> http://www.osronline.com/page.cfm?name=ListServer
>

Also, you need to protect the access to pIrp->UserBuffer with __try {} __except() block. Keep in mind UserBuffer is only initialized for the devctl code with METHOD_NEITHER flag. I don’t see if you actually check the IoControlCode. You can’t assume your driver will only get well-formed requests.

in completion routine, as soon as we assert the event, then the dispatch routine’s kewaiforsingleobject become running, right?

at this point, dispatch routine own the irp, we can touch the irp-buffer, and complete irp anything we want, because we own the irp.

So I was wonder, when the next sentence in completion routine: return SMPR is excetud.

can this sentense be excuted after we complete the irp in dispatch routine?

it is any concurrent case which confuse me a long time, even I read the paper about completion routine from OSR.

> So I was wonder, when the next sentence in completion routine: return SMPR is excetud. can

this sentense be excuted after we complete the irp in dispatch routine?

In simplified terms, you can think of IoCompleteRequest() as of a loop that invokes completion routines that drivers had set, starting from the one immediately above IoCompleteRequest()'s caller on the stack and proceeding upwards. Returning STATUS_MORE_PROCESSING_REQUIRED from a completion routine simply tells IoCompleteRequest( ) to break out of this loop and return to the caller on the spot. As a result,
all remaining completion routines are not called.

This is why you have to call IoCompleteRequest() from your dispatch routine if your completion one has returned STATUS_MORE_PROCESSING_REQUIRED . Otherwise, completion routines of all drivers above yours on the stack are not going to get invoked…

Certainly, this is very simplistic view of IRP completion, but still it describes the general idea behind the whole thing…

it is any concurrent case which confuse me a long time, even I read the paper about completion
routine from OSR.

As you can see, there is no concurrency here whatsoever…

Anton Bassov

Thanks for all the responses. The question from Jeff Ekin is one I had been wondering myself, so thanks Anton for your clear answer.
Cheers,
Ben

> Also, you need to protect the access to pIrp->UserBuffer with __try {} __except() block. Keep in mind

UserBuffer is only initialized for the devctl code with METHOD_NEITHER flag.

IIRC it is always initialized for all data transfer IRPs.


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

sorry, if lower level driver call IoCompleteRequest(), then the upper level setted IoCompletionRoutine will be called, right?

What you said:
Returning STATUS_MORE_PROCESSING_REQUIRED from a completion routine
simply tells IoCompleteRequest( ) to break out of this loop and return to the
caller on the spot.

here the caller is the lower level driver, right?

but as we known, if lower level driver call IoCompleteRequest(), it means that he will never own this irp, then i could not under why the loop return to the caller?

In my think, as completion routine is returned, the same level dispatch routine will be the spot, right?

The lower level driver doesn’t care what happened above, so when the call returns to the lower level driver it goes off and does something else. The call has to return to the caller. When the completion routine sets the event, the dispatch routine will run, but not necessarily immediately.

d

debt from my phone

-----Original Message-----
From: workingmailing@163.com
Sent: Monday, August 08, 2011 2:22 AM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Waiting for lower level driver to complete IRP

sorry, if lower level driver call IoCompleteRequest(), then the upper level setted IoCompletionRoutine will be called, right?

What you said:
Returning STATUS_MORE_PROCESSING_REQUIRED from a completion routine
simply tells IoCompleteRequest( ) to break out of this loop and return to the
caller on the spot.

here the caller is the lower level driver, right?

but as we known, if lower level driver call IoCompleteRequest(), it means that he will never own this irp, then i could not under why the loop return to the caller?

In my think, as completion routine is returned, the same level dispatch routine will be the spot, right?


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

in completion routine: we set the event, and return SMPR.
in dispatch routine, we waiting on the event.

when the event is set, the dispatch routine should have two case:
one is immediately dispatch routine will run, and the other not.

I have question here, if it is immediately dispatch routine will run, does the return SMPR will running after it?

If the dispatch routine runs immediately after setting the event, yes the “return SMPR” will execute after iocompleterequest returns. That is fine, the completion routine loop that Anton described knows not to touch the irp at all if SMPR is returned

d

debt from my phone

-----Original Message-----
From: workingmailing@163.com
Sent: Monday, August 08, 2011 7:26 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Waiting for lower level driver to complete IRP

in completion routine: we set the event, and return SMPR.
in dispatch routine, we waiting on the event.

when the event is set, the dispatch routine should have two case:
one is immediately dispatch routine will run, and the other not.

I have question here, if it is immediately dispatch routine will run, does the return SMPR will running after it?


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

You have to look at the internal logic.

If the lower-level driver is capable of finishing the IRP immediately,
without any queueing, then it will return some kind of status, such as
STATUS_SUCCESS or STATUS_WORLD_HAS_GONE_BAD or something else. But if it
has to queue it, then it will return STATUS_PENDING. In that case, such as
in the IRP_MJ_PNP prototype handler, you do something of the form

NSTATUS status;
KEVENT event;

KeInitializeEvent(&event);

IoSetCompletionRoutine(Irp, MyCompletion, &event, TRUE, TRUE, TRUE);
status = IoCallDriver(lowerlevel, Irp);

if(status == STATUS_PENDING)
{
KeWaitForSingleObject(&event, …other parameters here…, NULL);
status = Irp.IoStatus.Status;
}

if(!NT_SUCCESS(status))
{
…deal with failure
}
else
{
…IRP has been processed, either synchronously or asynchronously
}

NTSTATUS MyCompletion(PDEVICE_OBJECT device, PIRP Irp, PVOID context)
{
PKEVENT event = (PKEVENT)context;
…stuff
KeSetEvent(event, …);
return STATUS_MORE_PROCESSING_REQUIRED;
}

If the lower level queues up the IRP, then the event becomes signaled, and
the thread which is waiting resumes, using whatever status was set in the
IoStatus.Status field by the lower-level driver.

There is nothing that says when the thread which is released starts running.
It may even be running before the KeSetEvent returns, and you must not do
anything to the IRP after that KeSetEvent. Any race conditions that might
arise in the I/O Manager will be dealt with by the I/O Manager, and from
your viewpoint this happens by Magic. But once you do the KeSetEvent, you
should assume the IRP is no longer under your control. The thread which has
resumed might call IoCompleteRequest, which may result in the I/O Manager
freeing up the IRP before the KeSetEvent returns. Therefore, it shouldn’t
matter to you what precisely happens, or what order it happens in. You have
to assume that there is potential concurrency everywhere.
joe

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of
workingmailing@163.com
Sent: Monday, August 08, 2011 10:25 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Waiting for lower level driver to complete IRP

in completion routine: we set the event, and return SMPR.
in dispatch routine, we waiting on the event.

when the event is set, the dispatch routine should have two case:
one is immediately dispatch routine will run, and the other not.

I have question here, if it is immediately dispatch routine will run, does
the return SMPR will running after it?


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

If the lower level queues up the IRP, then the event becomes signaled, and
the thread which is waiting resumes, using whatever status was set in the
IoStatus.Status field by the lower-level driver.

Sorry, does queue up the IRP lead to the event becomes signaled?
In my opinion, only when the lower level driver call IoCompleteRequest for the irp, the IoCompletion will be invoked.