Inverted Call & request queue

In my driver I have a collection, say for simplicity it’s a collection of ints. There exists a callback within the driver which will be triggered when an entry from the collection is deleted. I wish to inform my user mode application every time this occurs and I’ve implemented the inverted call pattern to do so. (The driver contains a number of pended IOCTLs that are completed when the callback is called.)

The articles I’ve read indicate this is the way to go, however I can’t help but feel it’s a bit ‘racy’? Correct me if I’m wrong but it relies on the user mode application to guarantee that there is always one pended IOCTL in the request queue.

After reading through the OSR sample code the approach I was going to take for the UM side of things was :

/*
FILE_FLAG_OVERLAPPED handle
CreateIoCompletionPort() to associate completion handle
Buffer some initial requests.
*/
DeviceIoControl(MY_IOCTL,…)
DeviceIoControl(MY_IOCTL,…)
DeviceIoControl(MY_IOCTL,…)

while(true)
{
/* Wait for completion */
GetQueuedCompletionStatus(…)

/* Instantly requeue another */
DeviceIoControl(MY_IOCTL,…)

/* process completed */

}

But there is nothing stopping 4 entries being deleted from the collection in the driver before the UM application has performed it’s first requeue, causing the last one to be lost.

Am I correct in saying this is a concern? Is the solution to increase the number of initially buffered IOCTLS up to a number which is certainly enough for my domain specific problem? Or have I got the wrong end of the stick?

> But there is nothing stopping 4 entries being deleted from the collection in the driver before the UM

application has performed it’s first requeue, causing the last one to be lost.

Surely, you need to separately record such events in the driver using specially allocated structures.

Then, when the inverted call IRP arrives and this set of specially allocated structures is not empty - you detach/destroy 1 event from the set, and then fill/complete the invcall IRP inline.


Maxim S. Shatskih
Microsoft MVP on File System And Storage
xxxxx@storagecraft.com
http://www.storagecraft.com

Hmm let me see if I’m understanding you correctly. In this scenario I would have a collection (WDFQUEUE) of pended requests filling at a particular rate as UM sends them, and a collection of “removed entries” filling at their own rate as the callback removes them.

Then, when the inverted call IRP arrives and this set of specially allocated structures is not empty - you detach/destroy 1 event from the set, and then fill/complete the invcall IRP inline.

What if the invcall IRP arrives and the “removed entries” collection is empty? Won’t it pend forever? Or what would complete the request in this scenario? Would it be that be that I have a separate worker thread which is pairing items in these two collections together and completing the IRPs?

Thanks for the help

You use a queue for your pending requests and a queue for your events. Set up a worker thread to process and complete IRPs. Pend a set number of IRPs before processing so hopefully you don’t get into a situation where your completion routine is starved. But even in that case you can just requeue the event to your event queue. Use cancel safe routines and when your driver unloads just complete all pending IRPs and free any pending events.

> What if the invcall IRP arrives and the “removed entries” collection is empty? Won’t it pend forever?

No, it will pend till the next event occurance.

Or what would complete the request in this scenario?

Next occurance of your event.

Would it be that be that I have a separate worker thread which is pairing items

No need. If you can get rid of any additional threads/work items in your design - do not use them.

What you’re speaking about is very much like how afd.sys (at least pre-Vista) implements socket reads.

Data arrives there by a TDI event callback, which either immediately completes a next pending socket read IRP, or, if there is no pending read IRPs, allocates a tmp chunk of memory (up to the total size of SO_RCVBUF) and copies the data there.

The next read IRP arriving will check for these tmp chunks of memory, and will be satisfied off them if any without pending (I think even FastIo optimization is used there).

Note that, with such a design, you either have non-empty pending read IRP queue, or non empty tmp record queue, but never both.


Maxim S. Shatskih
Microsoft MVP on File System And Storage
xxxxx@storagecraft.com
http://www.storagecraft.com

> You use a queue for your pending requests and a queue for your events. Set up a worker thread to

process and complete IRPs.

No need. Event-driven solutions are always better then having a thread.

CSQ is yes, a must.


Maxim S. Shatskih
Microsoft MVP on File System And Storage
xxxxx@storagecraft.com
http://www.storagecraft.com

ah, I think I understand. An attempt is made to complete the requests in two places? Psuedocode below.

/* In EvtIoDeviceControl portion which handles inverted IOCTL code */
if(RemovedEntries.IsEmpty())
PendIOCTL();
else
CompleteImmediately(RemovedEntries.First());

/* In the callback that is executed when an entry is removed from the collection of ints */
If(PendedIRPS.Empty())
RemovedEntries.Add();
else
Complete(RemovedEntry, PendedIRPS.First())

correct?

Exactly!


Maxim S. Shatskih
Microsoft MVP on File System And Storage
xxxxx@storagecraft.com
http://www.storagecraft.com

wrote in message news:xxxxx@ntdev…
> ah, I think I understand. An attempt is made to complete the requests in two places? Psuedocode below.
>
> /* In EvtIoDeviceControl portion which handles inverted IOCTL code /
> if(RemovedEntries.IsEmpty())
> PendIOCTL();
> else
> CompleteImmediately(RemovedEntries.First());
>
> /
In the callback that is executed when an entry is removed from the collection of ints */
> If(PendedIRPS.Empty())
> RemovedEntries.Add();
> else
> Complete(RemovedEntry, PendedIRPS.First())
>
> correct?
>

Thanks for the help, will give this a go today!