KMDF Driver Timeout Functionality

Hello All,

I?m writing a KMDF function driver for a PCI device. This device allows DMA transfers so for a read/write I setup a DMA transfer and then write the appropriate registers on the device to kick off the DMA. The device then asserts an interrupt when the DMA completes, so the driver clears the interrupt and does the rest of the processing in a DPC. However, when the device fails (my team designed it so that isn?t uncommon), my app locks up forever because it?s waiting for an interrupt that never happens. Therefore, I want a timer to timeout the operation if this happens.
To make this happen, I set up a timer in my EventDeviceAdd callback to fire a DPC periodically to complete any pending requests that have hung for about a second. Here?s how I set up the timer:

KeInitializeDpc(&devContext->TimerDPC, &TimerDPC, devContext);
KeInitializeTimer(&devContext->TimeoutTimer);
LARGE_INTEGER Delay;
Delay.QuadPart = -5000000LL;
Success = KeSetTimerEx(&devContext->TimeoutTimer, Delay, 5000000, &devContext->TimerDPC)

However, the system bug checks the first time my DPC callback is called. I?m guessing this is because I?m setting up the timer in PASSIVE_LEVEL. However, it the MSDN article ?Using Timer Objects? in the DDK documentation, it says ?Driver routines that run at IRQL >= DISPATCH_LEVEL can time out requests by using a timer object with an associated DPC object to queue a driver-supplied CustomTimerDpc routine.? This is probably my problem, right?

So how would I start the timer to queue a DPC since I can?t really do it from another DPC?

Or should I be using the WdfTimer instead (if so, are there any good examples out there)? I?m using the KeTimer because it seems simpler, but maybe it?s not the correct route? Or am I going the entirely wrong route to timeout these operations?

Thanks!!
Ryan

xxxxx@lmco.com wrote:

I?m writing a KMDF function driver for a PCI device. This device allows DMA transfers so for a read/write I setup a DMA transfer and then write the appropriate registers on the device to kick off the DMA. The device then asserts an interrupt when the DMA completes, so the driver clears the interrupt and does the rest of the processing in a DPC. However, when the device fails (my team designed it so that isn?t uncommon), my app locks up forever because it?s waiting for an interrupt that never happens. Therefore, I want a timer to timeout the operation if this happens.
To make this happen, I set up a timer in my EventDeviceAdd callback to fire a DPC periodically to complete any pending requests that have hung for about a second. Here?s how I set up the timer:

KeInitializeDpc(&devContext->TimerDPC, &TimerDPC, devContext);
KeInitializeTimer(&devContext->TimeoutTimer);
LARGE_INTEGER Delay;
Delay.QuadPart = -5000000LL;
Success = KeSetTimerEx(&devContext->TimeoutTimer, Delay, 5000000, &devContext->TimerDPC)

However, the system bug checks the first time my DPC callback is called. I?m guessing this is because I?m setting up the timer in PASSIVE_LEVEL.

You’re guessing? What does the windbg !analyze -v output say?

Or should I be using the WdfTimer instead (if so, are there any good examples out there)? I?m using the KeTimer because it seems simpler, but maybe it?s not the correct route? Or am I going the entirely wrong route to timeout these operations?

You should be using a WDFTIMER instead. The documentation for each of
the WDF calls has an example of how to use that call. Check WdfTimerCreate.


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

Use a WDFTIMER, cleanup will be much easier. Not sure why you need a sample, the API surface for the object is very very small. Also, you need to start the timer only after you enabled the interrupt, starting it in adddevice is too early.

What is the output of !analyze -v when you bugcheck?

d

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@lmco.com
Sent: Monday, February 14, 2011 10:34 AM
To: Windows System Software Devs Interest List
Subject: [ntdev] KMDF Driver Timeout Functionality

Hello All,

I?m writing a KMDF function driver for a PCI device. This device allows DMA transfers so for a read/write I setup a DMA transfer and then write the appropriate registers on the device to kick off the DMA. The device then asserts an interrupt when the DMA completes, so the driver clears the interrupt and does the rest of the processing in a DPC. However, when the device fails (my team designed it so that isn?t uncommon), my app locks up forever because it?s waiting for an interrupt that never happens. Therefore, I want a timer to timeout the operation if this happens.
To make this happen, I set up a timer in my EventDeviceAdd callback to fire a DPC periodically to complete any pending requests that have hung for about a second. Here?s how I set up the timer:

KeInitializeDpc(&devContext->TimerDPC, &TimerDPC, devContext);
KeInitializeTimer(&devContext->TimeoutTimer);
LARGE_INTEGER Delay;
Delay.QuadPart = -5000000LL;
Success = KeSetTimerEx(&devContext->TimeoutTimer, Delay, 5000000, &devContext->TimerDPC)

However, the system bug checks the first time my DPC callback is called. I?m guessing this is because I?m setting up the timer in PASSIVE_LEVEL. However, it the MSDN article ?Using Timer Objects? in the DDK documentation, it says ?Driver routines that run at IRQL >= DISPATCH_LEVEL can time out requests by using a timer object with an associated DPC object to queue a driver-supplied CustomTimerDpc routine.? This is probably my problem, right?

So how would I start the timer to queue a DPC since I can?t really do it from another DPC?

Or should I be using the WdfTimer instead (if so, are there any good examples out there)? I?m using the KeTimer because it seems simpler, but maybe it?s not the correct route? Or am I going the entirely wrong route to timeout these operations?

Thanks!!
Ryan


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

You need to give us the !analyze -v from Windbg for the crash. There is
no problem with doing the setup at PASSIVE_LEVEL. You can actually call
KeSetTimerEx from a DPC routine.

You should be using WdfTimer take a look at the
general\echo\kmdf\AutoSync example.

Don Burn (MVP, Windows DKD)
Windows Filesystem and Driver Consulting
Website: http://www.windrvr.com
Blog: http://msmvps.com/blogs/WinDrvr

xxxxx@lmco.com” wrote in message
news:xxxxx@ntdev:

> Hello All,
>
> I?m writing a KMDF function driver for a PCI device. This device allows DMA transfers so for a read/write I setup a DMA transfer and then write the appropriate registers on the device to kick off the DMA. The device then asserts an interrupt when the DMA completes, so the driver clears the interrupt and does the rest of the processing in a DPC. However, when the device fails (my team designed it so that isn?t uncommon), my app locks up forever because it?s waiting for an interrupt that never happens. Therefore, I want a timer to timeout the operation if this happens.
> To make this happen, I set up a timer in my EventDeviceAdd callback to fire a DPC periodically to complete any pending requests that have hung for about a second. Here?s how I set up the timer:
>
> KeInitializeDpc(&devContext->TimerDPC, &TimerDPC, devContext);
> KeInitializeTimer(&devContext->TimeoutTimer);
> LARGE_INTEGER Delay;
> Delay.QuadPart = -5000000LL;
> Success = KeSetTimerEx(&devContext->TimeoutTimer, Delay, 5000000, &devContext->TimerDPC)
>
> However, the system bug checks the first time my DPC callback is called. I?m guessing this is because I?m setting up the timer in PASSIVE_LEVEL. However, it the MSDN article ?Using Timer Objects? in the DDK documentation, it says ?Driver routines that run at IRQL >= DISPATCH_LEVEL can time out requests by using a timer object with an associated DPC object to queue a driver-supplied CustomTimerDpc routine.? This is probably my problem, right?
>
> So how would I start the timer to queue a DPC since I can?t really do it from another DPC?
>
> Or should I be using the WdfTimer instead (if so, are there any good examples out there)? I?m using the KeTimer because it seems simpler, but maybe it?s not the correct route? Or am I going the entirely wrong route to timeout these operations?
>
> Thanks!!
> Ryan

Sounds like there’s a solid consensus to use the WdfTimer instead. I should have taken that route to begin with - I’ll add it to my long list of lessons learned.

For your interest, here’s my WinDbf Analyze dump (the first ASSERT_DATA is what led me to believe that my problem relates to setting the timer at the passive level).

1: kd> !analyze -v
*******************************************************************************
* *
* Bugcheck Analysis *
* *
*******************************************************************************

TIMER_OR_DPC_INVALID (c7)
Kernel timer or DPC used incorrectly.
Arguments:
Arg1: 00000000, Timer object found in memory which must not contain such items.
Arg2: 8a596d98, Address of the timer object.
Arg3: 8a596b88, Start of memory range being checked.
Arg4: 8a596df8, End of memory range being checked.

Debugging Details:

*** No owner thread found for resource 80af15c0
*** No owner thread found for resource 80af15c0
*** No owner thread found for resource 80af15c0
*** No owner thread found for resource 80af15c0

INVALID_DPC_FOUND: ffffffffb95cc2d0

FAULTING_IP:
HIBDrvr!TimerDPC+0 [d:\hib_pci_driver\hibdrvr.cpp @ 1302]
b95cc2d0 8bff mov edi,edi

DEFAULT_BUCKET_ID: DRIVER_FAULT

BUGCHECK_STR: 0xC7

PROCESS_NAME: System

LOCK_ADDRESS: 80af1640 – (!locks 80af1640)

Resource @ nt!IopDeviceTreeLock (0x80af1640) Shared 1 owning threads
Threads: 8abf1020-01<*>
1 total locks, 1 locks currently held

PNP_TRIAGE:
Lock address : 0x80af1640
Thread Count : 1
Thread address: 0x8abf1020
Thread wait : 0x81a9

ASSERT_DATA: KeGetCurrentIrql() <= DISPATCH_LEVEL

ASSERT_FILE_LOCATION: d:\xpsp\base\ntos\ke\timerobj.c at Line 196

LAST_CONTROL_TRANSFER: from 80abadee to 80ab912c

STACK_TEXT:
f78e9e7c 80abadee 8a720000 8a72ca30 00000000 nt!DbgBreakPoint
f78ea16c 80abae30 80a32254 80a32234 000000c4 nt!RtlAssert2+0x104
f78ea188 80a322c8 80a32254 80a32234 000000c4 nt!RtlAssert+0x18
f78ea1a8 f747451f 8a72ca30 8a72ca08 f78ea1d0 nt!KeCancelTimer+0x4a
f78ea1b8 b97a2722 8a72ca30 f78ea1cf 8a7299a8 NDIS!NdisMCancelTimer+0x24
WARNING: Stack unwind information not available. Following frames may be wrong.
f78ea1d0 b97b1814 00000000 8a720000 8aae7200 e1q5132+0xf722
f78ea200 b97b1a3c 8a720000 00000020 8a720000 e1q5132+0x1e814
f78ea22c b97acc90 00000000 f745a5ef f78ea248 e1q5132+0x1ea3c
f78ea23c f745a60f 8a720000 f78ea288 80a3112f e1q5132+0x19c90
f78ea248 80a3112f 8ab0a0f0 00000048 f7717408 NDIS!ndisBugcheckHandler+0x20
f78ea288 80a31ece 8a43b128 8a596b88 8a596df8 nt!KiScanBugCheckCallbackList+0x7d
f78ea664 80a31f3d 000000c7 00000000 8a596d98 nt!KeBugCheck2+0xa96
f78ea684 80a32169 000000c7 00000000 8a596d98 nt!KeBugCheckEx+0x1b
f78ea6ac 80adfab2 00596b88 00000648 00000000 nt!KeCheckForTimer+0x9f
f78ea6f4 a8307658 8a596b90 00000000 8a596b98 nt!ExFreePoolWithTag+0x128
f78ea710 a8305eb8 8a596b98 f78ea72c a82e0e83 wdf01000!FxPoolFree+0x16e
f78ea71c a82e0e83 8a596b98 8a596b98 f78ea74c wdf01000!FxObject::operator delete+0x30
f78ea72c a82b8258 00000001 a83079a6 00000000 wdf01000!FxDevice::`scalar deleting destructor’+0x19
f78ea734 a83079a6 00000000 00000000 00000000 wdf01000!FxObject::SelfDestruct+0xb
f78ea74c a82b82a0 8a596bac 8a596b98 f78ea77c wdf01000!FxObject::ProcessDestroy+0xa6
f78ea75c a8307b2f 00000000 000004b1 a8328f40 wdf01000!FxObject::Release+0x42
f78ea77c a8308826 8a596b00 00000001 8a596b98 wdf01000!FxObject::DeletedAndDisposedWorkerLocked+0xca
f78ea79c a82e0cab c000009a 8a596b98 c000009a wdf01000!FxObject::DeleteObject+0x190
f78ea7bc a82e0ada a8324c90 8a48fc88 f78ea9d8 wdf01000!FxDevice::DeleteObject+0x1c4
f78ea7cc a82e93dd c000009a 00000001 8abc1680 wdf01000!FxDevice::DeleteDeviceFromFailedCreate+0x1d
f78ea9d8 a82e945c 8abc1680 f78ea9fc 80a2e809 wdf01000!FxDriver::AddDevice+0x18b
f78ea9e4 80a2e809 8a58bac0 8abc1680 00000000 wdf01000!FxDriver::AddDevice+0x1b
f78ea9fc 80b4bdad a82e9441 00000004 00000001 nt!PpvUtilCallAddDevice+0x19
f78eaac0 80b4e2a8 80000514 02000001 00000000 nt!PipCallDriverAddDevice+0x617
f78ead20 80b4ee56 8abc11e0 00000001 00000000 nt!PipProcessDevNodeTree+0x1f8
f78ead58 80a2d071 8a4c51a8 8abf1020 80b0737c nt!PiRestartDevice+0x116
f78ead80 80acaae3 00000000 00000000 8abf1020 nt!PipDeviceActionWorker+0x17f
f78eadac 80bc761c 00000000 00000000 00000000 nt!ExpWorkerThread+0x10f
f78eaddc 80ad9b52 80aca9d4 00000001 00000000 nt!PspSystemThreadStartup+0x34
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

STACK_COMMAND: kb

FOLLOWUP_IP:
nt!KeCancelTimer+4a
80a322c8 33c9 xor ecx,ecx

SYMBOL_STACK_INDEX: 3

SYMBOL_NAME: nt!KeCancelTimer+4a

FOLLOWUP_NAME: MachineOwner

MODULE_NAME: nt

IMAGE_NAME: ntkrnlmp.exe

DEBUG_FLR_IMAGE_TIMESTAMP: 4802b3ce

FAILURE_BUCKET_ID: 0xC7_nt!KeCancelTimer+4a

BUCKET_ID: 0xC7_nt!KeCancelTimer+4a

Followup: MachineOwner

Another update - switched to use the WdfTimer instead, and it works like a charm. I’m still curious about the exact cause of my bug before (it’s clear that WdfTimer is just a wrapper for the KeTimer anyways), but I’m happy to be up and running now. Thanks for the inputs.

For the previous !analyze -v you needed to fix your symbols. From the parameters to the bugcheck, it looked like you were using a timer that was not a part of a valid memory range

d

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@lmco.com
Sent: Monday, February 14, 2011 3:32 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] KMDF Driver Timeout Functionality

Another update - switched to use the WdfTimer instead, and it works like a charm. I’m still curious about the exact cause of my bug before (it’s clear that WdfTimer is just a wrapper for the KeTimer anyways), but I’m happy to be up and running now. Thanks for the inputs.


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