InterruptSpinLock: IsrDPC in kernel driver

Hello,

I have a PCIe card which works in legacy mode.
Upon interrupt, I have to read 2 status registers. Each register contains that status of 4 channels. The card handled 8 channels.

Following is the code of the ISR:

BOOLEAN
PLxEvtInterruptIsr(
    IN WDFINTERRUPT Interrupt,
    IN ULONG        MessageID
    )
/*++
Routine Description:

    Interrupt handler for this driver. Called at DIRQL level when the
    device or another device sharing the same interrupt line asserts
    the interrupt. The driver first checks the device to make sure whether
    this interrupt is generated by its device and if so clear the interrupt
    register to disable further generation of interrupts and queue a
    DPC to do other I/O work related to interrupt - such as reading
    the device memory, starting a DMA transaction, coping it to
    the request buffer and completing the request, etc.

Arguments:

    Interupt   - Handle to WDFINTERRUPT Object for this device.
    MessageID  - MSI message ID (always 0 in this configuration)

Return Value:

     TRUE   --  This device generated the interrupt.
     FALSE  --  This device did not generated this interrupt.

--*/
{
    PDEVICE_EXTENSION   devExt;
    BOOLEAN             isRecognized = FALSE;
	UINT32				Status14, Status58;

    UNREFERENCED_PARAMETER(MessageID);

	//KdPrint(("--> PLxEvtInterruptIsr"));
	devExt = PLxGetDeviceContext(WdfInterruptGetDevice(Interrupt));

	//Read interrupt status register of channels 1..4
	Status14 = READ_REGISTER_ULONG((PVOID)((UINT64)devExt->Bar1Address + STATUS_14_OFFSET));
	//Read interrupt status register of channels 5..8
	Status58 = READ_REGISTER_ULONG((PVOID)((UINT64)devExt->Bar1Address + STATUS_58_OFFSET));

	if ((Status14 & 0x03030303) || (Status58 & 0x03030303))
	{
		//Build 64bits word that contains 8 statuses. Each status is 8 bits
		devExt->StatusAll = ((UINT64)Status58 << 32) | Status14;
	
		//Write status register to clear interrupts
		WRITE_REGISTER_ULONG((PVOID)((UINT64)devExt->Bar1Address + STATUS_14_OFFSET), Status14);
		WRITE_REGISTER_ULONG((PVOID)((UINT64)devExt->Bar1Address + STATUS_58_OFFSET), Status58);

		WdfInterruptQueueDpcForIsr(devExt->Interrupt);

		isRecognized = TRUE;
	}

    return isRecognized;
}

In the ISR I save the 64bits status in "StatusAll"
Then in DPC, I have to handle "StatusAll" and call KeSetEvent (for example) to wake a thread that is blocked untill message received in its channel.

The IsrDPC contains the following section:

//
// Acquire this device's InterruptSpinLock.
//
WdfInterruptAcquireLock( Interrupt );

???????
	
// Release our interrupt spinlock
//
WdfInterruptReleaseLock(Interrupt);

As much as I know there is 1:1 releation between Isr and DPC.
What should I do inside the locked section ?

Thank you,
Zvika

There is not a 1:1 relationship between ISR and DPC. It's quite possible for you to get multiple interrupts before your DPC gets scheduled.

Why do you need the interrupt lock in your DPC?

What should I do in the locked section ?

How can we know? You handle whatever interrupts occurred that you haven't already handled.

1 Like

Hi Tim,

Thank you very much for your reply.
I will count how many times ISR and DPC called and update soon.

Best regards,
Zvika

As was mentioned there is not a 1:1 relationship between ISR and DPC.

The way that I have always handled this is similar to what you are doing. Your problem is that StatusAll needs to be ORed with the new statuses. Since several ISR might occur before the DPC you don't want to overwrite the previously recorded interrupts before they are handled.

//Build 64bits word that contains 8 statuses. Each status is 8 bits
devExt->StatusAll |= (((UINT64)Status58 << 32) | Status14);

In DPC inside the locked section I would make a local copy of devExt->StatusAll and then reset devExt->StatusAll to 0. Then you can use your local copy of StatusAll to do any further processing outside the lock.

1 Like

Hi Grim, All,

Thank you very much for your reply.
I will try it and update soon.

Best regards,
Zvika

Hi All,

In my case the cause to the problem was a mistake in analyzing the interrupt status register.
But I also started using Grim's code.

Best Regards,
Zvika