Hello,
I'm writing a PCI card driver currently. But I always run into a DPC Watchdog timeout during my DPC.
What I want to archive is the following:
I have an Windows-API which provides a function to "attach" to an interrupt. This means it just calles the driver and blocks execution. The driver takes the request and puts it into a manual queue.
The time an interrupts occurres, the ISR just confirms and disables the interrupt. It then queues the DPC.
In the DPC the next waiting API request is pulled from the manual queue, the interrupt is enabled again and the request is completed.
Additional the API and the driver exchange a counter to be able to track interrupts.
The code for ISR and DPC is the following:
BOOLEAN EvtInterruptIsr(
WDFINTERRUPT Interrupt,
ULONG MessageID) //Message ID used by MSI
{
BOOLEAN bRetVal = FALSE;
SDN_INTERRUPT_DATA idInterruptData = {0}; //(PSDN_INTERRUPT_DATA)&((psaMappedResources->pbMappedMemory)[SDN_IFPC_ADR_SCRATCHPAD]);
SDN_IFPC_INT_STATE iisInterruptState = {0};
ULONG ulCnt = 0;
ULONG ulIntMask = 1u;
WDFDEVICE wdDevice = WdfInterruptGetDevice(Interrupt);
PDEVICE_CONTEXT pDevCtx = GetDeviceContext(wdDevice);
ULONG ulRegister = 0;
ULONG ulCurrentOffset = 0;
ULONG ulCurrentOffsetInBytes = 0;
ULONG ulIpr = 0;
PBYTE pbCurrentChunkAddress = NULL;
/* Abbreviations*/
PBYTE pbMappedMem = pDevCtx->pbMappedMemory;
ulCurrentOffsetInBytes = SDN_IFPC_ADR_SCRATCHPAD * SDN_ADDRESS_WIDTH;
pbCurrentChunkAddress = &(pbMappedMem[ulCurrentOffsetInBytes]);
idInterruptData.ulCounter = READ_REGISTER_ULONG((PULONG)pbCurrentChunkAddress);
ulCurrentOffsetInBytes = (SDN_IFPC_ADR_SCRATCHPAD + 1) * SDN_ADDRESS_WIDTH;
pbCurrentChunkAddress = &(pbMappedMem[ulCurrentOffsetInBytes]);
idInterruptData.ulFlags = READ_REGISTER_ULONG((PULONG)pbCurrentChunkAddress);
ulCurrentOffsetInBytes = SDN_IFPC_ADR_INT_STATE * SDN_ADDRESS_WIDTH;
pbCurrentChunkAddress = &(pbMappedMem[ulCurrentOffsetInBytes]);
iisInterruptState.ulMagicNr = READ_REGISTER_ULONG((PULONG)pbCurrentChunkAddress);
ulCurrentOffsetInBytes = (SDN_IFPC_ADR_INT_STATE + 1) * SDN_ADDRESS_WIDTH;
pbCurrentChunkAddress = &(pbMappedMem[ulCurrentOffsetInBytes]);
iisInterruptState.ulIsr = READ_REGISTER_ULONG((PULONG)pbCurrentChunkAddress);
ulCurrentOffsetInBytes = (SDN_IFPC_ADR_INT_STATE + 2) * SDN_ADDRESS_WIDTH;
pbCurrentChunkAddress = &(pbMappedMem[ulCurrentOffsetInBytes]);
iisInterruptState.ulIpr = READ_REGISTER_ULONG((PULONG)pbCurrentChunkAddress);
ulCurrentOffsetInBytes = (SDN_IFPC_ADR_INT_STATE + 3) * SDN_ADDRESS_WIDTH;
pbCurrentChunkAddress = &(pbMappedMem[ulCurrentOffsetInBytes]);
iisInterruptState.ulIcrEna = READ_REGISTER_ULONG((PULONG)pbCurrentChunkAddress);
/* Store the interrupt reason*/
ulIpr = iisInterruptState.ulIpr;
idInterruptData.ulFlags |= ulIpr;
/* Acknowledge interrupts*/
for(ulCnt =0; ulCnt < 16; ulCnt++)
{
/* Is interrupt pending?*/
if((ulIpr & (ulIntMask << ulCnt)) > 0)
{
KdPrint((__DRIVER_NAME "\nWhohoo! Our interrupt!\n"));
bRetVal = TRUE;
/* Should interrupt be enabled*/
if((iisInterruptState.ulIcrEna & (ulIntMask << ulCnt)) > 0)
{
KdPrint(("####Interrupt %d!####\n", ulCnt));
/* Acknowledge and disabled currently. Will be enable in the DPC again.*/
ulRegister = SDN_IFPC_FLAG_ACK;// | SDN_IFPC_FLAG_ENABLE;
pDevCtx->ulInterruptsToReenable |= (1 << ulCnt);
ulCurrentOffset = SDN_IFPC_ADR_INT_CTRL + ulCnt;
ulCurrentOffsetInBytes = ulCurrentOffset * SDN_ADDRESS_WIDTH;
pbCurrentChunkAddress = &(pbMappedMem[ulCurrentOffsetInBytes]);
WRITE_REGISTER_ULONG((PULONG)pbCurrentChunkAddress, ulRegister);
} /* if((piisInterruptState->uiIcrEna & (ulIntMask << i)) > 0)*/
else
{
KdPrint((__DRIVER_NAME "####Interrupt shouldnt be enabled %d / 0x%x!!!\n", ulCnt, iisInterruptState.ulIcrEna));
/* Acknowledge and keep disabled*/
ulRegister = SDN_IFPC_FLAG_ACK | SDN_IFPC_FLAG_DISABLE;
ulCurrentOffset = SDN_IFPC_ADR_INT_CTRL + ulCnt;
ulCurrentOffsetInBytes = ulCurrentOffset * SDN_ADDRESS_WIDTH;
pbCurrentChunkAddress = &(pbMappedMem[ulCurrentOffsetInBytes]);
WRITE_REGISTER_ULONG((PULONG)pbCurrentChunkAddress, ulRegister);
}/* else [if((piisInterruptState->uiIcrEna & (ulIntMask << i)) > 0)]*/
} /* if((pidInterruptData->uiFlags & (ulIntMask << i)) > 0)*/
} /* for(ulCnt =0; ulCnt < 16; ulCnt++)*/
if(bRetVal == TRUE)
{
idInterruptData.ulCounter++;
ulCurrentOffsetInBytes = SDN_IFPC_ADR_SCRATCHPAD * SDN_ADDRESS_WIDTH;
pbCurrentChunkAddress = &(pbMappedMem[ulCurrentOffsetInBytes]);
WRITE_REGISTER_ULONG((PULONG)pbCurrentChunkAddress, idInterruptData.ulCounter);
ulCurrentOffsetInBytes = (SDN_IFPC_ADR_SCRATCHPAD + 1) * SDN_ADDRESS_WIDTH;
pbCurrentChunkAddress = &(pbMappedMem[ulCurrentOffsetInBytes]);
WRITE_REGISTER_ULONG((PULONG)pbCurrentChunkAddress, idInterruptData.ulFlags);
WdfInterruptQueueDpcForIsr(Interrupt);
} /* if(bRetVal == TRUE)*/
//KdPrint((__DRIVER_NAME "<-- EvtInterruptIsr\n"));
return bRetVal;
}
VOID EvtInterruptDpc(
WDFINTERRUPT Interrupt,
WDFDEVICE Device)
{
NTSTATUS nsStatus = STATUS_SUCCESS;
PDEVICE_CONTEXT pdcDevContext = GetDeviceContext(Device);
WDFREQUEST wrRequest;
ULONG ulCnt = 0;
ULONG ulRegister = 0;
ULONG ulCurrentOffset = 0;
ULONG ulCurrentOffsetInBytes = 0;
PBYTE pbCurrentChunkAddress = NULL;
SDN_INTERRUPT_DATA idInterruptData = {0};
ULONG ulBitShift = 0;
ULONG ulBitResult = 0;
PBYTE pbMappedMem = pdcDevContext->pbMappedMemory;
WDFMEMORY wmOutMemory;
KdPrint((__DRIVER_NAME "--> EvtInterruptDpc\n"));
pdcDevContext = GetDeviceContext(Device);
WdfInterruptAcquireLock(pdcDevContext->Interrupt);
do
{
/* Get next request*/
nsStatus = WdfIoQueueRetrieveNextRequest(pdcDevContext->ISRNotificationQueue, &wrRequest);
if(NT_SUCCESS(nsStatus))
{
nsStatus = WdfRequestRetrieveOutputMemory(wrRequest, &wmOutMemory);
ulCurrentOffsetInBytes = SDN_IFPC_ADR_SCRATCHPAD * SDN_ADDRESS_WIDTH;
pbCurrentChunkAddress = &(pbMappedMem[ulCurrentOffsetInBytes]);
idInterruptData.ulCounter = READ_REGISTER_ULONG((PULONG)pbCurrentChunkAddress);
ulCurrentOffsetInBytes = (SDN_IFPC_ADR_SCRATCHPAD + 1) * SDN_ADDRESS_WIDTH;
pbCurrentChunkAddress = &(pbMappedMem[ulCurrentOffsetInBytes]);
idInterruptData.ulFlags = READ_REGISTER_ULONG((PULONG)pbCurrentChunkAddress);
nsStatus = WdfMemoryCopyFromBuffer(
wmOutMemory,
0,
&idInterruptData,
sizeof(SDN_INTERRUPT_DATA));
ulCurrentOffsetInBytes = (SDN_IFPC_ADR_SCRATCHPAD + 1) * SDN_ADDRESS_WIDTH;
pbCurrentChunkAddress = &(pbMappedMem[ulCurrentOffsetInBytes]);
WRITE_REGISTER_ULONG((PULONG)pbCurrentChunkAddress, 0);
WdfRequestSetInformation(wrRequest, sizeof(SDN_INTERRUPT_DATA));
/* If request dequeued successfully -> complete request*/
WdfRequestComplete(wrRequest, nsStatus);
}
else if(nsStatus != STATUS_NO_MORE_ENTRIES)
{
KdPrint((__DRIVER_NAME "!!! WdfIoQueueRetrieveNextRequest with 0x%08x !!!\n", nsStatus));
}
} while(NT_SUCCESS(nsStatus));
if(nsStatus == STATUS_NO_MORE_ENTRIES)
{
nsStatus = STATUS_SUCCESS;
}
/* Reenable interrupts*/
for(ulCnt = 0; ulCnt < 16; ulCnt++)
{
KdPrint(("#### Check Interrupt %d ####\n", ulCnt));
ulBitShift = (1 << ulCnt);
ulBitResult = (pdcDevContext->ulInterruptsToReenable) & ulBitShift;
if(ulBitResult > 0)
{
pdcDevContext->ulInterruptsToReenable &= ~(1 << ulCnt);
ulCurrentOffset = SDN_IFPC_ADR_INT_CTRL + ulCnt;
ulCurrentOffsetInBytes = ulCurrentOffset * SDN_ADDRESS_WIDTH;
pbCurrentChunkAddress = &(pbMappedMem[ulCurrentOffsetInBytes]);
ulRegister = READ_REGISTER_ULONG((PULONG)pbCurrentChunkAddress);
ulRegister |= SDN_IFPC_FLAG_ENABLE;
WRITE_REGISTER_ULONG((PULONG)pbCurrentChunkAddress, ulRegister);
KdPrint(("####Interrupt %d reenabled!####\n", ulCnt));
}
else
{
KdPrint(("#### Don't need to enable Interrupt %s ####\n", ulCnt));
}
}
KdPrint(("#### All interrupts checked ####\n"));
WdfInterruptReleaseLock(pdcDevContext->Interrupt);
KdPrint((__DRIVER_NAME "<-- EvtInterruptDpc\n"));
}
The last output I see is "#### Check Interrupt 0 ####" (Note: I just enable and trigger Interrupt 0. All other interrupts are neither enabled nor triggered)
And if I debug KdPrint(("#### Check Interrupt %d ####\n", ulCnt)); is the last command executed.
Here is the call stack:
Call Site
00 nt! ?? ::FNODOBFM::string'+0x4f3a 01 nt!KeUpdateSystemTime+0x377 02 hal!HalpHpetClockInterrupt+0x8d 03 nt!KiInterruptDispatchNoLock+0x163 (TrapFrame @ fffff80000ba1f00)
04 nt!KxWaitForSpinLockAndAcquire+0x20
05 nt!KeAcquireSpinLockAtDpcLevel+0x6f
06 nt!KiScanInterruptObjectList+0x5c
07 nt!KiChainedDispatch+0x128 (TrapFrame @ fffff80000ba2160) 08 nt!KeThawExecution+0x26b 09 nt!KdExitDebugger+0x7a 0a nt! ?? ::FNODOBFM::string'+0x19d81
0b nt!KdpTrap+0x2b
0c nt!KiDispatchException+0x126
0d nt!KiExceptionDispatch+0xc2
0e nt!KiBreakpointTrap+0xf4 (TrapFrame @ fffff80000ba2cb0) 0f SiDaNetPCI!EvtInterruptDpc+0x1ef [c:\projekte\siemenshc\sidanet\driver\sidanet_interrupts.c @ 308] 10 Wdf01000!FxInterrupt::_InterruptDpcThunk+0x53 11 nt!KiRetireDpcList+0x1bc 12 nt!KyRetireDpcList+0x5 (TrapFrame @ fffff80000ba2e70)
13 nt!KiDispatchInterruptContinue
14 nt!KiDpcInterruptBypass+0x13
15 nt!KiInterruptDispatchNoLock+0x1fc (TrapFrame @ fffff88004cf2890) 16 igdkmd64!hybDriverEntry+0x2856b7 17 igdkmd64!hybDriverEntry+0x54aa0 18 igdkmd64!hybDriverEntry+0x1091a2 19 igdkmd64!hybDriverEntry+0x117306 1a igdkmd64!hybDriverEntry+0x1181cf 1b igdkmd64!hybDriverEntry+0x1185a8 1c igdkmd64!hybDriverEntry+0xfbc40 1d igdkmd64!hybDriverEntry+0xfc264 1e igdkmd64!hybDriverEntry+0x1190d 1f dxgkrnl!DXGCONTEXT::Render+0x5f9 20 dxgkrnl!DxgkRender+0x3e7 21 win32k!NtGdiDdDDIRender+0x12 22 nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff88004cf3c20)
23 0x7fefe9113fa 24 0x7fef79319dd
I have no idea why the DPC freezes right behind the KdPrint command... Can somebody help me out on this?
Thanks,
Torben