DeviceIoControl & InvertedCall & WdfIoQueueDispatchParallel

Primarily to aid my understanding I was hoping someone with more knowledge than I would be able to explain the “blocking” behavior in the following pseudocode.

h = CreateFileW(L"XXX",
            GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_WRITE | FILE_SHARE_READ,
            nullptr,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            nullptr);

//On worker thread
for(;;) { DeviceIoControl(h, IOCTL_INVERTED_CALL, ...); PrintData(); }

//On Main
switch(userInput)
{
    CASE 1:
        DeviceIoControl(h, IOCTL_OTHER, ...); //BLOCKS until IOCTL_INVERTED_CALL is completed. My IoHandler in the kernel is not called.
}

I had originally thought that IOCTL_OTHER was blocking because my default IOQueue(WdfIoQueueCreate) was WdfIoQueueDispatchSequential. However, even when I change this to WdfIoQueueDispatchParallel my handler is not called. Things all work fine when the worker thread operates own it’s own handle (CreateFileW’ed seperately), but i was hoping to avoid multiple handles.

What exactly is causing the “blocking” in this circumstance? I don’t think it’s my driver’s IOQueue? Is this something to do with the mysterious OVERLAPPED structure that I’ve been able to avoid thus far?

Thanks,
Jason

You need to open the handle as OVERLAPPED. A handle without OVERLAPPED will serialize IO requests at IO manager dispatching layer before it is sent to the driver.

1 Like

I see. I’ve done some further tinkering and I’m struggling to understand something else.

//On worker thread
for(;;) 
{
h2 = CreateFileW(L"XXX",
            GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_WRITE | FILE_SHARE_READ,
            nullptr,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            nullptr);
    DeviceIoControl(h2, IOCTL_INVERTED_CALL, ...); 
    PrintData(); 
}

//On Main
h = CreateFileW(L"XXX",
            GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_WRITE | FILE_SHARE_READ,
            nullptr,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            nullptr);
            
switch(userInput)
{
    CASE 1:
        DeviceIoControl(h, IOCTL_OTHER, ...); //No blocking occurs here, even when IOCTL_INVERTED_CALL is pending
}

Why does blocking NOT occur / Why does my IOCTL_OTHER handler get called in the example above?

Since my handler for IOCTL_INVERTED_CALL will not call WdfRequestCompleteWithInformation, the queue is WdfIoQueueDispatchSequential and the docs state:

The framework does not deliver the next request until a driver has called WdfRequestComplete.

I would have assumed IOCTL_OTHER should block?

This is obvliously related to the use of two different usermode file handles, but I don’t understand how that would affect my drives IO queue?

Thanks,
Jason

It’s just dawned on me, perhaps this is because the WDFREQUEST has been forwarded to my pending request queue? (WdfRequestForwardToIoQueue?)

If the source queue’s dispatching method is sequential or parallel, the framework can deliver another request to one of the source queue’s request handlers.

Nope. Both should block until the IOCTL is complete… and I/O done on different handles is considered separately.

In general, blocking in your app has no relationship to Queuing in your driver.

Later ETA: While how your driver receives and processes I/O Requests can affect when an asynchronous I/O operation returns to your app, this isn’t related to the Queue type or dispatch method.

You need to check the return status of the calls to DeviceIoControl… if the second one is returning without waiting, I suspect that’s because it’s failing.

Peter

I/O done on different handles is considered separately.
In general, blocking in your app has no relationship to Queuing in your driver.
A handle without OVERLAPPED will serialize IO requests at IO manager dispatching layer before it is sent to the driver

Thanks, the quotes above have helped tremendously.

I was working under the misconception that if CreateFile was called to create a synchronous handle I could use this handle for IOCTL_INVERTED_CALL on ThreadA and IOCTL_OTHER on ThreadB if my DriverIO queue was WdfIoQueueDispatchParallel.

I now understand that in order for this to be possible I need to either use OverlappedIO on the single handle, or open multiple handles.

Thanks,
Jason

this to be possible I need to either use OverlappedIO on the single handle, or open multiple handles.

Yes! Exactly correct.

Peter