Overlapped IOCTL returning ERROR_IO_PENDING (native WDM v KMDF)

I have recently ported one of our WDM drivers to use KMDF. I’m concerned about one backward-compatibility issue.

When the application issues

hPort = CreateFile (szPort,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);

followed by

bRc = DeviceIoControl(hPort,
IOCTL_X,
NULL,
0,
NULL,
0,
&dwResult,
&ol);

the driver processes the IOCTL synchronously, calling

WdfRequestCompleteWithInformation(Request, status, bytesReturned);

prior to returning from the EvtIoDeviceControl callback. This then results in bRc being set to FALSE with GetLastError() returning ERROR_IO_PENDING.

The original WDM driver too processes the request/IRP synchronously and calls IoCompleteRequest. This however results in bRc being set to TRUE (even though the port was opened in overlapped mode).

The KMDF version may well now be behaving as expected but it is nevertheless different from before and therefore presents a backward-compatibility issue. Can anyone confirm whether this is an intentional fix made by the KMDF and whether the previous behaviour can be conditionally achieved?

Thanks

Will

Yes, this is by design. All IRPs that are handled by a WDFQUEUE are marked pending and STATUS_PENDING is returned, even if it is completed synchronously in the callback (there is no other way to do it given that you can stop/start a queue at any time). Whenever the application opens a handle as overlapped it needs to handle the situation that any i/o may be pended, assuming an overlapped handle call returns synchronously is a bug in the app (IMHO).

d

xxxxx@farsite.co.uk wrote:

I have recently ported one of our WDM drivers to use KMDF. I’m concerned about one backward-compatibility issue.

When the application issues

bRc = DeviceIoControl(hPort,

the driver processes the IOCTL synchronously, calling

WdfRequestCompleteWithInformation(Request, status, bytesReturned);

prior to returning from the EvtIoDeviceControl callback. This then results in bRc being set to FALSE with GetLastError() returning ERROR_IO_PENDING.

The original WDM driver too processes the request/IRP synchronously and calls IoCompleteRequest. This however results in bRc being set to TRUE (even though the port was opened in overlapped mode).

The KMDF version may well now be behaving as expected but it is nevertheless different from before and therefore presents a backward-compatibility issue. Can anyone confirm whether this is an intentional fix made by the KMDF and whether the previous behaviour can be conditionally achieved?

“Fix” is not the right word, but KMDF does process the IRPs
differently. It is certainly the case that both approaches are valid,
and your application is definitely at fault. ANY overlapped call can
return ERROR_IO_PENDING, and it is a bug when an application does not
check for that. It only takes about 5 lines of code to do a synchronous
overlapped call correctly.

In KMDF, your callback is not called in the context of the calling
process. The documentation clearly warns about this. When the
framework puts a request into a dispatch queue, it pends the original
IRP and returns. The queue will be drained – and your callback called
– at a later point, by some system thread.

If you really, really, really cannot work around this, you can use the
EvtIoInCallerContext callback to handle this. It will be called in the
context of the caller’s process, before the request is placed in an I/O
queue. The correct procedure, however, is to fix your buggy application.


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

This came up a few months ago when someone noticed some buggy serial I/O OCX out there that does a non-overlapped ReadFile() call on its driver handle, even though the handle was opened overlapped.

This was done because a previous call to ClearCommError() indicated N bytes in the driver’s buffer, and so (presumably) the call to ReadFile() (asking for N bytes) could be completed “synchronously” inside the driver.

This “works” in WDM if you are handling IRPs manually, but KMDF was pending the IRP before it delivered it to the queue. That is, even though the request was being completed inside the queue callback, it was still too late.

The solution (for this guy), since he needed to support that OCX, was (I believe) to setup a callback to preprocess WDM IRPs and handle it outside of KMDF.

Tim, I saw your idea about EvtIoInCallerContext and looked at it for a little bit.

It looks like, by doing this, you lose all synchronization and locking that you normally get for free when requests are delivered to your I/O queues.

How would you go about synchronizing with the framework in this case? Would you call WdfObjectAcquireLock() on the WDFDEVICE, or just the WDFQUEUE where the request was headed, or…?

(Assume the WDFDEVICE was created with WdfSynchronizationScopeDevice).

The idea of these callbacks is generally to capture any information that you need from the caller’s context before the request is sent to the framework.

You could write a very simple KMDF filter driver to sit above your driver which sent all I/O down the stack in it’s EvtIoInCallerContext callback directly (using IoCallDriver) and then depending on the I/O control would either return the status to the caller or would wait for the request to complete before returning control.

This lets you turn asynchronous completion into synchronous completion to fix the poor assumptions that the app made.

-p

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@gmail.com
Sent: Thursday, January 11, 2007 1:05 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Overlapped IOCTL returning ERROR_IO_PENDING (native WDM v KMDF)

Tim, I saw your idea about EvtIoInCallerContext and looked at it for a little bit.

It looks like, by doing this, you lose all synchronization and locking that you normally get for free when requests are delivered to your I/O queues.

How would you go about synchronizing with the framework in this case? Would you call WdfObjectAcquireLock() on the WDFDEVICE, or just the WDFQUEUE where the request was headed, or…?

(Assume the WDFDEVICE was created with WdfSynchronizationScopeDevice).


Questions? First check the Kernel Driver FAQ at http://www.osronline.com/article.cfm?id=256

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

That’s an interesting idea, but for the problem I’m thinking of (synchronous completion of ReadFile when bytes are in the buffer), the filter wouldn’t have any knowledge of the function driver’s state, and so it couldn’t really determine (on its own) whether to pass it down, or wait.

xxxxx@gmail.com wrote:

Tim, I saw your idea about EvtIoInCallerContext and looked at it for a little bit.

It looks like, by doing this, you lose all synchronization and locking that you normally get for free when requests are delivered to your I/O queues.

That’s correct. You are getting a shot at the request very early on,
before it goes into a queue, so you lose the queue synchronization.

How would you go about synchronizing with the framework in this case? Would you call WdfObjectAcquireLock() on the WDFDEVICE, or just the WDFQUEUE where the request was headed, or…?

(Assume the WDFDEVICE was created with WdfSynchronizationScopeDevice).

That depends on what kind of synchronization you need. It is very easy
with KMDF (as I discovered rather painfully) to oversynchronize
yourself. If you really cannot have your InCallerContext callback run
side-by-side with of your other callbacks, then acquiring the device
lock should simulate what the framework provides. But if you just have
a couple of data structures that you need to protect, it may be better
to use a more specific lock. In my driver, I have one or two pieces of
data that are shared with the ISR that have to be protected, so I grab
the interrupt lock for those short periods. Beyond that, I use
WdfSynchronizationScopeNone.


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