Locking in completion routines (KMDF)

I am developing a Windows USB driver using KMDF 1.1.

I am using WdfSynchronizationScopeDevice so my device-based lock is acquired by the framework for the duration of my EvtIoDEviceControl callback.
From my EvtIoDeviceControl I can call WdfRequestSend to request a control transfer to be sent and for me to be notified of its completion (normally asynchronously) via my completion routine.

Trouble is, how do I know whether, in the completion routine, I need to acquire the device-lock or not (assuming I need to protect some device-based context).

Normally, when the completion routine is called, the lock has not been acquired by the framework and I need to explicitly acquire it myself in the completion routine. However, in the example below, the completion routine is being called indirectly from the EvtIoDeviceControl routine itself so the lock has already been acquired!

How should I determine whether, in my completion routine, I need to acquire the lock or not?

bab8776c b7f63c3b 0000010d 00000002 8609538c nt!KeBugCheckEx+0x1b
bab87788 b7f6d9df 862f5de0 00000002 8609538c Wdf01000!FxVerifierBugCheck+0x24
bab877a8 b7f52f53 bab877cb 864792fc 864225a8 Wdf01000!FxCallbackSpinLock::Lock+0x63
bab877c0 b7fb2e26 862f5e98 86095338 bab8784c Wdf01000!imp_WdfObjectAcquireLock+0x95
bab877d0 b7fb4e62 79f6acc0 864792fc 864225a8 xyz!WdfObjectAcquireLock+0x16 [d:\winddk\wdf\kmdf10\inc\wdfsync.h @ 53]
bab8784c b7f6e70d 79bdda50 79cbfbe0 864792fc xyz!ControlTransferCompletionRoutine+0x82
bab87874 b7f471dc 85fa3f2b 86340418 00000000 Wdf01000!FxRequestBase::CompleteSubmitted+0x89
bab87890 b7f47296 014225a8 85f5b5a8 bab878bc Wdf01000!FxIoTarget::RequestCompletionRoutine+0x195
bab878a0 8050f5b1 00000000 85fa3e70 864225a8 Wdf01000!FxIoTarget::_RequestCompletionRoutine+0x35
bab878bc 804e3d38 00000000 85fa3e70 85f5b5a8 nt!IopUnloadSafeCompletion+0x1d
bab878ec f7a1f4e8 bab87914 f7a232c7 85fa3e70 nt!IopfCompleteRequest+0xa2
bab878f4 f7a232c7 85fa3e70 c000000d 85fa3e70 usbhub!USBH_CompleteIrp+0x16
bab87914 f7a23afa 8638c380 85fa3e70 85fa3e70 usbhub!USBH_PdoUrbFilter+0xab
bab87930 f7a211d8 86479344 85fa3e70 bab87964 usbhub!USBH_PdoDispatch+0x202
bab87940 804e37f7 85fdbde8 85fa3e70 b7f9e618 usbhub!USBH_HubDispatch+0x48
bab87950 b7f55572 85fcc048 85fcc134 860b0048 nt!IopfCallDriver+0x31
bab87964 b7fb571d 00000005 864225a8 86340418 Wdf01000!imp_WdfRequestSend+0x39f
bab8797c b7fb55b7 79bdda50 79cbfbe0 bab87994 xyz!WdfRequestSend+0x1d [d:\winddk\wdf\kmdf10\inc\wdfrequest.h @ 570]
bab879fc b7fb70b7 79cbfbe0 79bdda50 79f4ffb0 xyz!SendControlRequest+0x1c7
bab87ab8 b7f77bde 7a033fb0 79f4ffb0 0000003c xyz!XYZEvtIoDeviceControl+0x807

Thanks

Will Barker

Will Barker wrote:

Normally, when the completion routine is called, the lock has not
been acquired by the framework and I need to explicitly acquire
it myself in the completion routine. However, in the example below,
the completion routine is being called indirectly from the
EvtIoDeviceControl routine itself so the lock has already been acquired!

Will,

If you look back a few days, you will see that I had exactly this same problem. My recommendation is twofold:

  1. Create a second lock around the stuff you want to protect in your device extension. Don’t use the device-wide lock.

  2. After you do 1), never complete a WDFREQUEST with any lock held. Otherwise, control might come around to your dispatch handler in the same stack context and the lock will be double-acquired (as you’re seeing above).

-Chris

Correct. This is one of the restrictions of device wide locking. It
does not play well with async i/o or acquiring the lock in unknown
thread contexts. In this case, you must use your own lock. By the fact
that you must use your own lock, you should reevaluate what device wide
locking buys for you and see if it is something you should remove in the
future.

d

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of
xxxxx@gmail.com
Sent: Friday, May 11, 2007 7:50 AM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Locking in completion routines (KMDF)

Will Barker wrote:

Normally, when the completion routine is called, the lock has not
been acquired by the framework and I need to explicitly acquire
it myself in the completion routine. However, in the example below,
the completion routine is being called indirectly from the
EvtIoDeviceControl routine itself so the lock has already been
acquired!

Will,

If you look back a few days, you will see that I had exactly this same
problem. My recommendation is twofold:

  1. Create a second lock around the stuff you want to protect in your
    device extension. Don’t use the device-wide lock.

  2. After you do 1), never complete a WDFREQUEST with any lock held.
    Otherwise, control might come around to your dispatch handler in the
    same stack context and the lock will be double-acquired (as you’re
    seeing above).

-Chris


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

Doron Holan wrote:

Correct. This is one of the restrictions of device wide locking. It
does not play well with async i/o or acquiring the lock in unknown
thread contexts. In this case, you must use your own lock. By the fact
that you must use your own lock, you should reevaluate what device wide
locking buys for you and see if it is something you should remove in the
future.

This is an important issue to understand with KMDF. I consider myself a
bright guy, but KMDF makes it so easy to do automatic locking that I
fell into the trap of thinking “synchronization is a good thing, and if
a little is good, then a lot should be GREAT”, and ended up producing a
driver that excelled at unexpected deadlocks, always at the most
inopportune time.

It is really necessary to take the time to understand what is being
locked, and for how long.


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