Pending Irps

Two things I’m still not sure about 1. When the lowest driver decides to pend a request , how will it’s dispatch routine look? Mark IRP pending, call IoCompleteRequest and return status pending ? What is the responsiblity of the asynchronous code ? Only set the UserEvent? 2. Is there ever a case where a filter driver might return status pending directly ? Or can we rely on the assumption that only the lowest driver, that is responsible to complete the request can pend the completion of the request?

1.If your driver wants to pend request, Mark an IRP as pending and return status_pending. Once the processing is done at later time(you are pending the request because you need to process it later , not on the dispatch routine) you need to do call Iocompleterequest.
2. Any driver in the stack can mark irp has pending and return status_pending .

> @Shashi140 said: > 1.If your driver wants to pend request, Mark an IRP as pending and return status_pending. Once the processing is done at later time(you are pending the request because you need to process it later , not on the dispatch routine) you need to do call Iocompleterequest. > 2. Any driver in the stack can mark irp has pending and return status_pending . But then if I just return status pending from the middle of the device stack, and then say later on - complete the request The driver below me never get the chance to process the IRP?

The driver below me never get the chance to process the IRP?

Exactly. Thus, you would only DO that in a case where you know the lower driver does not need to participate, or where you do not want the driver to participate. I’ve written USB filters that pretended to offer services that the hardware did not provide. Thus, I intercepted and completed those IRPs without ever passing them down.

Is there ever a case where a filter driver might return status pending directly?

Sure. You need to think about an IRP as having an “owner”. When the IRP arrives in your driver, you own it. You can mark it as pending, but since you are the owner, it is YOUR responsibility to complete it at a later date. You remain the owner until you either complete the IRP or pass it down.

What is the responsibility of the asynchronous code? Only set the UserEvent?

Drivers don’t touch the UserEvent, and drivers don’t know or care whether the user mode code is asynchronous. Drivers operate under the assumption that EVERY request is asynchronous. The kernel I/O subsystem will handle the notifications to the upper levels.

> @Tim_Roberts said: > Exactly. Thus, you would only DO that in a case where you know the lower driver does not need to participate, or where you do not want the driver to participate. I’ve written USB filters that pretended to offer services that the hardware did not provide. Thus, I intercepted and completed those IRPs without ever passing them down. > > > Sure. You need to think about an IRP as having an “owner”. When the IRP arrives in your driver, you own it. You can mark it as pending, but since you are the owner, it is YOUR responsibility to complete it at a later date. You remain the owner until you either complete the IRP or pass it down. > > > Drivers don’t touch the UserEvent, and drivers don’t know or care whether the user mode code is asynchronous. Drivers operate under the assumption that EVERY request is asynchronous. The kernel I/O subsystem will handle the notifications to the upper levels. Makes more sense now , the last thing I’m still finding confusing is the PendingReturned flag, I thought it’s purpose is to indicate to the I/O manager that a driver decided to pend the request and it shouldn’t invoke second stage processing yet - if so , why would do we need to mark the IRP as pending in the completion routine ? At the time the completion routine is called it should be , well… - completed ? is there a case where IoCompleteRequest is called and the operation is still pending ?

In addition to Tim’s remarks, remember that in the general case, there isn’t one lower driver. That might be the most common, but a driver might need to request services from many lower drivers (new IRPs) as well as a work item to calculate a million digits of pi before deciding to send the IRP down or complete it directly

With respect to UserEvent, there often is none. And drivers certainly don’t care. The UM IO completion could be indicated by many methods and the threading model in use in UM can’t be known or considered by the KM code.

IIRC the reason for both marking and returning pending is something Petar has posted about before. It seems to be just a rule to follow

why would do we need to mark the IRP as pending in the completion routine ? At the time the completion routine is called it should be , well… - completed ?

In a real sense, it is the IRP stack location that gets completed, not the whole IRP. It happens one step at a time. Every driver in the stack that has set up a completion routine gets its chance to acknowledge that the request is completed as far as IT is concerned. It can also alter the status code. It is even possible for a driver to modify the IRP in its completion routine and send it back down. This is sometimes done if the original buffer is larger than what the lower driver can handle.

Also remember that this mechanism, like most kernel concepts, dates back to the mists of antiquity, essentially unchanged since the original Windows NT 3.1 release. Some of this would be done differently if it were being designed today.

*It is even possible for a driver to modify the IRP in its completion routine and send it back down.
This Looks very interesting… I have never seen any code on completion routine like this. Thanks for the info.

Reading your comments and diving a bit further , could anyone clarify if my understanding is correct? a driver might (perhaps should) return status pending from its dispatch routine whenever it needs to perform an operation that takes long time to complete . what practically happens is that the I/O manager will check if status pending has been returned and if so wait on an event - this delays the second stage processing for now , until IoCompleteRequest is called and eventually signals the said event But there’s another consequence to performing asynchronous (pending) I/O : the completion is executed in an arbitrary thread context , so the I/O manager needs to queue an apc , but it wouldn’t want to always queue an apc when there’s no need to (synchronous completion ) - so for that, we have PendingReturned (which is set when needed due to the rule we implement in completion routines ) . when a driver below us pended the IRP we have to make sure it’s being reflected upwards all the way to the I/O manager , so the event is signalled and the I/O manager can proceed freeing the IRP and copying necessary information back to the caller thread . I’m still not really sure why we do need that DESIGN wise , but at least now I (hopefully , unless someone corrects me) understand why we factually have to do this

Any request that cannot be completed immediately should be pended. That allows a user-mode app doing overlapped I/O to continue to do other work. If the app didn’t use overlapped I/O, then the thread blocks waiting for the event.

> @Tim_Roberts said: > Any request that cannot be completed immediately should be pended. That allows a user-mode app doing overlapped I/O to continue to do other work. If the app didn’t use overlapped I/O, then the thread blocks waiting for the event. Hmm , there’s this post https://www-user.tu-chemnitz.de/~heha/oney_wdm/ch05d.htm Specifically it mentions the following : To maximize system throughput, the I/O Manager expects drivers to defer the completion of IRPs that take a long time to complete. A driver indicates that completion will be deferred by calling IoMarkIrpPending and returning STATUS_PENDING from the dispatch routine. Often, though, the original caller of the I/O Manager wants to wait until the operation finishes before proceeding. The I/O Manager will therefore have logic similar to this (not the actual source code of any particular Microsoft Windows�NT function) to deal with the deferred completion: Irp->UserEvent�=�pEvent;�//��don’t�do�this�yourself status�=�IoCallDriver(…); if�(status�==�STATUS_PENDING) ��KeWaitForSingleObject(pEvent,�…); In other words, if IoCallDriver returns STATUS_PENDING, this code will wait on a kernel event. IoCompleteRequest is responsible for setting this event when the IRP finally completes. The address of the event (UserEvent ) is in one of the opaque fields of the IRP so that IoCompleteRequest can find it. “ that looks like waiting on the event solely depends on the status returned by IofCallDriver? tho, it indeed would make sense it waits only if the caller asked for synchronous I/O … So waiting on the event is not aimed to delay freeing the IRP but to ensure synchronous operation in case of asynchronous handling in kernel mode?

that looks like waiting on the event solely depends on the status returned by IofCallDriver?

If IoCallDriver returns any other code, then the IRP is complete. No need to wait for anything.

So waiting on the event is not aimed to delay freeing the IRP but to ensure synchronous operation in case of asynchronous handling in kernel mode?

Well, it’s both, right? The IRP can’t be freed until it is completed.