Question about blocking in dispatch functions

I am a newbie in driver development. Currently, I attempt to implement a keyboard filter. My driver will create a device object and attach itself to kbdclass. Everything works fine until I write the dispatch routine for read as the following :

DispatchRead(PDEVICE…) {

IoCopyCurrentIrpStackLocationToNext( Irp );

KeInitializeEvent( &event, NotificationEvent, FALSE );

IoSetCompletionRoutine( Irp, MyCompletionRoutine,
reinterpret_cast(&event),
TRUE,
TRUE,
TRUE
);

Irp->IoStatus.Information = 0;

ntStatus = IoCallDriver( deviceExtension->LowerDeviceObject, Irp );

if (STATUS_PENDING == ntStatus)
{
KeWaitForSingleObject( &event, Eecutive,KernelMode, FALSE, NULL );

ntStatus = Irp->IoStatus.Status;
}

if (NT_SUCCESS(ntStatus))
{
// key processing logic
}

Irp->IoStatus.Status = ntStatus;
IoCompleteRequest( Irp, IO_NO_INCREMENT );

return ntStatus;
}

MyCompletionRoutine(…) {
event = reinterpret_cast(Context);
KeSetEvent( event, 0, FALSE );

if (Irp->PendingReturned)
IoMarkIrpPending( Irp );

return STATUS_MORE_PROCESSING_REQUIRED;
}

After loading the driver, the mouse can be moved. But once a key is pressed, the computer is hung. I can use VNC to connect to the computer and power off it. It seems that the input thread is suspended.

Then I placed the following assertion at the start of the function:
PAGED_CODE();
ASSERT( PsGetCurrentThread() == Irp->Tail.Overlay.Thread );
There is no bug check. Therefore I am sure that my driver is at the topmost driver stack and appropriate IRQL for blocking.

As the last resort, I move the processing logic to the completion routine and remove the block in the dispatch routine. The problem is solved.

My questions are:
1. Why the blocked version hang-up the input thread of the system ?
The lower driver will eventually complete my request and signal my event, the function can resume its execution.
2. Does blocking only apply for DriverEntry, AddDevice, PNP and USB requests?
3. What exactly is Nonarbitrary and Arbitrary context?
Does it relate to my question?

Thank you for any assistance.

  1. Don’t block the reads, keep them async. If you think you need to continue with this, all of your key logic can be done in the completion routine

Better yet

  1. don’t dynamically attach to the keyboard stack. Install yourself as a class upper filter below kbdclass and you capture the service callback. You can implement all of your key logic there. Use the kbdfiltr sample in the wdk

d

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@iname.com
Sent: Tuesday, August 10, 2010 10:51 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] Question about blocking in dispatch functions

I am a newbie in driver development. Currently, I attempt to implement a keyboard filter. My driver will create a device object and attach itself to kbdclass. Everything works fine until I write the dispatch routine for read as the following :

DispatchRead(PDEVICE…) {

IoCopyCurrentIrpStackLocationToNext( Irp );

KeInitializeEvent( &event, NotificationEvent, FALSE );

IoSetCompletionRoutine( Irp, MyCompletionRoutine,
reinterpret_cast(&event),
TRUE,
TRUE,
TRUE
);

Irp->IoStatus.Information = 0;

ntStatus = IoCallDriver( deviceExtension->LowerDeviceObject, Irp );

if (STATUS_PENDING == ntStatus)
{
KeWaitForSingleObject( &event, Eecutive,KernelMode, FALSE, NULL );

ntStatus = Irp->IoStatus.Status;
}

if (NT_SUCCESS(ntStatus))
{
// key processing logic
}

Irp->IoStatus.Status = ntStatus;
IoCompleteRequest( Irp, IO_NO_INCREMENT );

return ntStatus;
}

MyCompletionRoutine(…) {
event = reinterpret_cast(Context);
KeSetEvent( event, 0, FALSE );

if (Irp->PendingReturned)
IoMarkIrpPending( Irp );

return STATUS_MORE_PROCESSING_REQUIRED; }

After loading the driver, the mouse can be moved. But once a key is pressed, the computer is hung. I can use VNC to connect to the computer and power off it. It seems that the input thread is suspended.

Then I placed the following assertion at the start of the function:
PAGED_CODE();
ASSERT( PsGetCurrentThread() == Irp->Tail.Overlay.Thread ); There is no bug check. Therefore I am sure that my driver is at the topmost driver stack and appropriate IRQL for blocking.

As the last resort, I move the processing logic to the completion routine and remove the block in the dispatch routine. The problem is solved.

My questions are:
1. Why the blocked version hang-up the input thread of the system ?
The lower driver will eventually complete my request and signal my event, the function can resume its execution.
2. Does blocking only apply for DriverEntry, AddDevice, PNP and USB requests?
3. What exactly is Nonarbitrary and Arbitrary context?
Does it relate to my question?

Thank you for any assistance.


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

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

> 1. Why the blocked version hang-up the input thread of the system ?

Because the input thread is highly async and cannot work with blocking path in any driver.

Blocking in dispatch routines is nearly always evil (except some PnP paths where it is mandatory).


Maxim S. Shatskih
Windows DDK MVP
xxxxx@storagecraft.com
http://www.storagecraft.com

Thank you for your replies and advices. For a bit cursity, I still have some questions:

  1. For other driver’s read/write request that originated from user application, blocking path or completion routine with DPC is preferred?
  2. I am a bit confused about Nonarbitrary and Arbitrary context. For this driver, does the input thread belongs to Arbitary context ?

> 1. For other driver’s read/write request that originated from user application, blocking path or

completion routine with DPC is preferred?

Completion routine.

  1. I am a bit confused about Nonarbitrary and Arbitrary context.

Arbitrary thread context means - you know nothing about the thread context you’re in.

This only occurs if you’re called from ISR or DPC.

For instance, completion routines run in arbitrary thread context (called by DpcForIsr of the driver below).


Maxim S. Shatskih
Windows DDK MVP
xxxxx@storagecraft.com
http://www.storagecraft.com

Max said:

And Max was right.

Peter
OSR

Also note from DDK:

“A driver that passes down the IRP and then waits on an event should not mark the IRP pending. Instead, its IoCompletion routine should signal the event and return STATUS_MORE_PROCESSING_REQUIRED”

Goood pickup, Alex… the completion routine is CLEARLY wrong in the provided code.

OP: Think about this a moment. You only call IoMarkIrpPending if YOUR DRIVER returns STATUS_PENDING from its dispatch routine. Is there ANY case in the code shown where you’d return STATUS_PENDING? If not, you shouldn’t be marking the IRP pending.

I/O Completion is perhaps THE single most complicated thing about WDM drivers. This is an excellent example of why the OP (and every one) should be using WDF.

Peter
OSR

I am very confused, what is wrong with my completion routine?

From my interpretation, the lower driver will queue up my request and return STATUS_PENDING. Then my completion rotuine just propagate the result up the driver stack.

If possible, please me the direction to find the answer.