Tech Support > Microsoft Windows > Drivers > asynchronous I/O problem with KMDF
asynchronous I/O problem with KMDF
Posted by lorenzoff on September 25th, 2007


Hello to all,
I'm developing a device driver for a gaming usb scanner and, normally,
it works quite well (thanks
Eliyas Yakub). There is something that I can't understand with the
asynchronous i/o; the scanner may block several time due to its
internal workflow or due to known firmware bugs.
I've used this approach (can someone evaluate it?).
The device supports two kind of ioctl (no read nor write): requests
that do not require a firmware work (versioning and so on) and
requests that require firmware work.
The deviceadd callback creates the default queue as parallel and an
ausiliary queue as sequential. The sequential queue is used to manage
these request that need firmware work because that firmware can manage
only a command once.

WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&_def_queue _config,
WdfIoQueueDispatchParallel);
_def_queue_config.EvtIoDeviceControl = OnIoDeviceControl;
_status = WdfIoQueueCreate(_device, &_def_queue_config,
WDF_NO_OBJECT_ATTRIBUTES, &_def_queue);

WDF_IO_QUEUE_CONFIG_INIT(&_fwcmd_queue_config,
WdfIoQueueDispatchSequential);
_fwcmd_queue_config.EvtIoDeviceControl = OnFWCommandIoDeviceControl;
_status = WdfIoQueueCreate(_device, &_fwcmd_queue_config,
WDF_NO_OBJECT_ATTRIBUTES, &(_dev_context->FwCommandQueue));

When the framework deliver a new request to my default
EvtIoDeviceControl (OnIoDeviceControl on the snippet above), it is
immediately completed if it belongs to a ioctl that not requires
firmware; it is requeued to the sequential queue otherwise. In this
second case, my default EvtIoDeviceControl returns immediately after
an WdfRequestForwardToIoQueue.
At the beginning of my ausiliary EvtIoDeviceControl
(OnFWCommandIoDeviceControl on the snippet above) I use
WdfRequestMarkCancelable to be notified about request cancellation.
After, i have to write the firmware command using
WdfUsbTargetPipeWriteSynchronously, and receive the response by
WdfUsbTargetPipeReadSynchronously before complete the user ioctl
request.
When the firmware works, all this appear to work fine. One of the
known firmware bugs is that it miss the response in certain
circumstances. When this happen, WdfUsbTargetPipeReadSynchronously
block undefinitely (I known about timeout, but I prefer that is the
application to choose its timeout).
And, finally, there is the problem.
When I reproduce the situation due to the firmware blocks, my test
consolle application cannot use CancelIo beacuse it bloks into the
DeviceIoControl call (I've verified using same printf).
Application side I use this code:

HANDLE Hnd=CreateFile("\\\\.\\MyDevice", GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

OVERLAPPED _overlapped;
BOOL _result=FALSE;
HANDLE _event;

memset(&_overlapped, 0, sizeof(OVERLAPPED));
_event = CreateEvent(NULL,TRUE,FALSE,NULL);
_overlapped.hEvent = _event;

_result=DeviceIoControl(Hnd, Code, InBuffer, InBufferSize, OutBuffer,
OutBufferSize, BytesReturned, &_overlapped);
if (!_result) {
if (GetLastError() == ERROR_IO_PENDING) {
switch (WaitForSingleObject(_event, 5000)){
....
....

The most strange (for me) thing is that if I open a new process of my
test application, it works as expected; DeviceIoControl fails and
WaitForSingleObject wait until the timeout expires.
But, why the first DeviceIoControl does not return? Using the debugger
I can see that the second request is delivered to my default
EvtIoDeviceControl; it is forwarded to the sequential queue and its
callback is NOT invocked (this is right). User mode DeviceIoControl
exit immediately. Why the first call blocks?

Posted by lorenzoff on September 25th, 2007


May be due to WdfUsbTargetPipeReadSynchronously that is, ehm,
synchronous? It is called for the first request but not for the
sencond? But why? This call is not on the thread context of my default
EvtIoDeviceControl; it would have to be free, infact it is invoked
correctly by the request of the second user mode application process.

Posted by Eliyas Yakub [MSFT] on September 25th, 2007


The first call to WdfUsbTargetPipeReadSynchronously is made in the context
of the calling thread. Since the call is waiting in an non alertable mode in
kernel-mode, you cannot cancel the I/O. The usermode DeviceIoControl call
doesn't return because the thread is stuck in the framework. You should
avoid making synchronous call in the context of the user thread.

When you send another ioctl control request from another app, that request
gets queued in the second sequential queue because you have one already
outstanding, and the thread returns back to the usermode. When you exit the
app, the I/O gets cancelled because the request is sitting in the queue.

This doesn't sound right to me. You are either forwarding the request
another sequential queue which in turn sends the request down, or sending
the request down directly from the first queue. In both the cases, you don't
own the request, so how can you mark the request cancelable. Turn on driver
verifier and framework verifier and it will complain.

-Eliyas


Posted by lorenzoff on September 26th, 2007


I will make as you say.
I would have to send you a gift, but I'm working for free and so...
only a thanks ;-)