Problems with WDF SpinLocks

Hi all,

I have encountered another puzzling problem in writing a driver. The basic idea of my driver was to synchronize use of a DMA engine using WorkItems and spinlocks. I am relying on the FIFO like behavior of WorkItems while using the spinlock to make sure that only the current work item will have access to the DMA engine.

I perform the following steps:

  1. Created a SpinLock object that is kept in my device extension.

  2. ISR occurs->DPC called-> DPC creates a WorkItem and executes it for each service request
    * The DPC also uses events to tell currently executing WOrkItems that a DMA operation has completed

  3. The WorkItem will try and obtain the spinlock in the device context
    * All other subsequent workitems should wait for the lock until it is release.
    * The DMA Workitem will wait for notification that the DMA completed.
    * When the DMA operation is completed the workItem will release the spinlock and delete itself.

  4. The next WorkItem should be able to get the spinlock and go on its merry way.

The problem I am seeing is that each WorkItem that is created seems to be able to obtain the spinlock as soon as it tries and kills the synchronization I thought I had. I have so far assumed that I am always dealing the one and only Device context each time I obtained the context but now I am staring to question this. Could I not be using “my” device context somehow?

What could be wrong?

Here is the general code snippets----

NTSTATUS
ddcInitializeDMA(
IN PDEVICE_EXTENSION devExt
)

{
NTSTATUS status = STATUS_SUCCESS;
WDF_DMA_ENABLER_CONFIG dmaConfig;
WDF_OBJECT_ATTRIBUTES attributes;
int i=0;
PAGED_CODE();

//Initialize DMA Lock
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.ParentObject = devExt->Device;
status = WdfSpinLockCreate(&attributes,&devExt->DmaLock);
if(!NT_SUCCESS(status)){
KdPrint(( “e2mapci : ddcInitializeDeviceExtension : WdfSpinLockCreate : DmaLock failed!”));
}

//
// Initialize MTI DMA Event
//
KeInitializeEvent(&devExt -> MtiDMAEvent, NotificationEvent, FALSE);
return status;
}

NTSTATUS
ddcInitializeDMA(
IN PDEVICE_EXTENSION devExt
)

{
NTSTATUS status = STATUS_SUCCESS;
WDF_DMA_ENABLER_CONFIG dmaConfig;
WDF_OBJECT_ATTRIBUTES attributes;
int i=0;
PAGED_CODE();

//Initialize DMA Lock
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.ParentObject = devExt->Device;
status = WdfSpinLockCreate(&attributes,&devExt->DmaLock);
if(!NT_SUCCESS(status)){
KdPrint(( “e2mapci : ddcInitializeDeviceExtension : WdfSpinLockCreate : DmaLock failed!”));
}

//
// Initialize MTI DMA Event
//
KeInitializeEvent(&devExt -> MtiDMAEvent, NotificationEvent, FALSE);
return status;
}

VOID
ddcEvtInterruptDpc(
IN WDFINTERRUPT Interrupt,
IN WDFDEVICE Device
)

–*/
{
NTSTATUS status= STATUS_SUCCESS;

devExt = ddcGetDeviceContext(Device);

//
// Evaluate MSR
//

// read improvements iterrupt status
ISR1 = ddcReadReg32(ISRReg);

if(ISR1)
{

WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(
&attributes,
MTI_DATA_WORKITEM_CONTEXT
);
attributes.ParentObject = Device;

WDF_WORKITEM_CONFIG_INIT(
&workitemConfig,
ddcDMAWorkItem
);

workitemConfig.AutomaticSerialization = FALSE;

status = WdfWorkItemCreate(
&workitemConfig,
&attributes,
&DMAWorkItem
);

if (!NT_SUCCESS(status)) {
KdPrint((“Failed to create workitem %x\n”, status));
return;

}

//get context
MTIWorkitemContext = GetMTIDataWorkItemContext(DMAWorkItem);

//set device in workitem context
MTIWorkitemContext->Device = Device;

//
// Execute this work item.
//
WdfWorkItemEnqueue(DMAWorkItem);
}

}

return;
}

VOID
ddcDMAWorkItem(
IN WDFWORKITEM WorkItem
)
{
PMTI_DATA_WORKITEM_CONTEXT pItemContext;
NTSTATUS status= STATUS_SUCCESS;
PDEVICE_EXTENSION devExt;

KdPrint((“ddcDMAWorkItem: called!\n”));

// get work item
pItemContext = GetMTIDataWorkItemContext(WorkItem);

devExt = ddcGetDeviceContext(pItemContext->Device);

// acquire DMA spinlock
KdPrint((“ddcDMAWorkItem: waiting for spinlock!\n”));
WdfSpinLockAcquire(devExt->DmaLock);
KdPrint((“ddcDMAWorkItem: acquired spinlock!\n”));

// send out DMA

//
// Create a new DmaTransaction.
//

//
// Initialize this new DmaTransaction.
//

// preform DMA

//wait for signal event to sgnal transaction complete process buffer & release spinlock
KeWaitForSingleObject(&devExt -> MtiDMAEvent, Executive, KernelMode, FALSE,NULL);
KdPrint((“ddcDMAWorkItem: Received DMA event!\n”));

//

// release SpinLock
WdfSpinLockRelease(devExt->DmaLock);
KdPrint((“ddcDMAWorkItem: released spinlock!\n”));

// delete work item
WdfObjectDelete(WorkItem);

return;
}

Dump the value of the WDFSPINLOCK handle value (via DbgPrint) in each
work item call and see if you are using the same value. What happens if
you use a KSPIN_LOCK?

Also, there is a big flaw in your design. You cannot infinitely wait on
the event while holding the spin lock, or more succinctly, you can do an
infinite wait at dispatch level. If you do not need to sync state with
the DPC, you can use a WDFWAITLOCK

d

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of
xxxxx@ddc-web.com
Sent: Thursday, August 09, 2007 4:41 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] Problems with WDF SpinLocks

Hi all,

I have encountered another puzzling problem in writing a driver. The
basic idea of my driver was to synchronize use of a DMA engine using
WorkItems and spinlocks. I am relying on the FIFO like behavior of
WorkItems while using the spinlock to make sure that only the current
work item will have access to the DMA engine.

I perform the following steps:

  1. Created a SpinLock object that is kept in my device extension.

  2. ISR occurs->DPC called-> DPC creates a WorkItem and executes it for
    each service request
    * The DPC also uses events to tell currently executing WOrkItems that a
    DMA operation has completed

  3. The WorkItem will try and obtain the spinlock in the device context
    * All other subsequent workitems should wait for the lock until it is
    release.
    * The DMA Workitem will wait for notification that the DMA completed.
    * When the DMA operation is completed the workItem will release the
    spinlock and delete itself.

  4. The next WorkItem should be able to get the spinlock and go on its
    merry way.

The problem I am seeing is that each WorkItem that is created seems to
be able to obtain the spinlock as soon as it tries and kills the
synchronization I thought I had. I have so far assumed that I am always
dealing the one and only Device context each time I obtained the context
but now I am staring to question this. Could I not be using “my” device
context somehow?

What could be wrong?

Here is the general code snippets----

NTSTATUS
ddcInitializeDMA(
IN PDEVICE_EXTENSION devExt
)

{
NTSTATUS status = STATUS_SUCCESS;
WDF_DMA_ENABLER_CONFIG dmaConfig;
WDF_OBJECT_ATTRIBUTES attributes;
int i=0;
PAGED_CODE();

//Initialize DMA Lock
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.ParentObject = devExt->Device;
status = WdfSpinLockCreate(&attributes,&devExt->DmaLock);
if(!NT_SUCCESS(status)){
KdPrint(( “e2mapci : ddcInitializeDeviceExtension :
WdfSpinLockCreate : DmaLock failed!”));
}

//
// Initialize MTI DMA Event
//
KeInitializeEvent(&devExt -> MtiDMAEvent, NotificationEvent,
FALSE);
return status;
}

NTSTATUS
ddcInitializeDMA(
IN PDEVICE_EXTENSION devExt
)

{
NTSTATUS status = STATUS_SUCCESS;
WDF_DMA_ENABLER_CONFIG dmaConfig;
WDF_OBJECT_ATTRIBUTES attributes;
int i=0;
PAGED_CODE();

//Initialize DMA Lock
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.ParentObject = devExt->Device;
status = WdfSpinLockCreate(&attributes,&devExt->DmaLock);
if(!NT_SUCCESS(status)){
KdPrint(( “e2mapci : ddcInitializeDeviceExtension :
WdfSpinLockCreate : DmaLock failed!”));
}

//
// Initialize MTI DMA Event
//
KeInitializeEvent(&devExt -> MtiDMAEvent, NotificationEvent,
FALSE);
return status;
}

VOID
ddcEvtInterruptDpc(
IN WDFINTERRUPT Interrupt,
IN WDFDEVICE Device
)

–*/
{
NTSTATUS status= STATUS_SUCCESS;

devExt = ddcGetDeviceContext(Device);

//
// Evaluate MSR
//

// read improvements iterrupt status
ISR1 = ddcReadReg32(ISRReg);

if(ISR1)
{

WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(

&attributes,

MTI_DATA_WORKITEM_CONTEXT
);
attributes.ParentObject = Device;

WDF_WORKITEM_CONFIG_INIT(

&workitemConfig,

ddcDMAWorkItem
);

workitemConfig.AutomaticSerialization = FALSE;

status = WdfWorkItemCreate(

&workitemConfig,

&attributes,

&DMAWorkItem

);

if (!NT_SUCCESS(status)) {
KdPrint((“Failed to create workitem %x\n”,
status));
return;

}

//get context
MTIWorkitemContext =
GetMTIDataWorkItemContext(DMAWorkItem);

//set device in workitem context
MTIWorkitemContext->Device = Device;

//
// Execute this work item.
//
WdfWorkItemEnqueue(DMAWorkItem);
}

}

return;
}

VOID
ddcDMAWorkItem(
IN WDFWORKITEM WorkItem
)
{
PMTI_DATA_WORKITEM_CONTEXT pItemContext;
NTSTATUS status= STATUS_SUCCESS;
PDEVICE_EXTENSION devExt;

KdPrint((“ddcDMAWorkItem: called!\n”));

// get work item
pItemContext = GetMTIDataWorkItemContext(WorkItem);

devExt = ddcGetDeviceContext(pItemContext->Device);

// acquire DMA spinlock
KdPrint((“ddcDMAWorkItem: waiting for spinlock!\n”));
WdfSpinLockAcquire(devExt->DmaLock);
KdPrint((“ddcDMAWorkItem: acquired spinlock!\n”));

// send out DMA

//
// Create a new DmaTransaction.
//

//
// Initialize this new DmaTransaction.
//

// preform DMA

//wait for signal event to sgnal transaction
complete process buffer & release spinlock
KeWaitForSingleObject(&devExt -> MtiDMAEvent,
Executive, KernelMode, FALSE,NULL);
KdPrint((“ddcDMAWorkItem: Received DMA
event!\n”));

//

// release SpinLock
WdfSpinLockRelease(devExt->DmaLock);
KdPrint((“ddcDMAWorkItem: released spinlock!\n”));

// delete work item
WdfObjectDelete(WorkItem);

return;
}


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

Eric,

In ddcDMAWorkItem function, KeWaitForSingleObject() is called (with no timeout specified) holding a spinlock. That is not allowed. Please read the WDK documentation for KeWaitForSingleObject().

Manoj Babulal

> In ddcDMAWorkItem function, KeWaitForSingleObject() is called (with no timeout

specified) holding a spinlock.

I just wonder how come that a driver that does the above “poses some problem”, instead of crashing the system right on the spot…

Anton Bassov

Why not use the KMDF queue instead and consume the IRPs from it one by one?


Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
xxxxx@storagecraft.com
http://www.storagecraft.com

wrote in message news:xxxxx@ntdev…
> Hi all,
>
> I have encountered another puzzling problem in writing a driver. The basic
idea of my driver was to synchronize use of a DMA engine using WorkItems and
spinlocks. I am relying on the FIFO like behavior of WorkItems while using the
spinlock to make sure that only the current work item will have access to the
DMA engine.
>
> I perform the following steps:
>
> 1. Created a SpinLock object that is kept in my device extension.
>
> 2. ISR occurs->DPC called-> DPC creates a WorkItem and executes it for each
service request
> * The DPC also uses events to tell currently executing WOrkItems that a DMA
operation has completed
>
> 3. The WorkItem will try and obtain the spinlock in the device context
> * All other subsequent workitems should wait for the lock until it is
release.
> * The DMA Workitem will wait for notification that the DMA completed.
> * When the DMA operation is completed the workItem will release the spinlock
and delete itself.
>
> 4. The next WorkItem should be able to get the spinlock and go on its merry
way.
>
>
> The problem I am seeing is that each WorkItem that is created seems to be
able to obtain the spinlock as soon as it tries and kills the synchronization I
thought I had. I have so far assumed that I am always dealing the one and only
Device context each time I obtained the context but now I am staring to
question this. Could I not be using “my” device context somehow?
>
> What could be wrong?
>
>
> Here is the general code snippets----
>
>
> NTSTATUS
> ddcInitializeDMA(
> IN PDEVICE_EXTENSION devExt
> )
>
> {
> NTSTATUS status = STATUS_SUCCESS;
> WDF_DMA_ENABLER_CONFIG dmaConfig;
> WDF_OBJECT_ATTRIBUTES attributes;
> int i=0;
> PAGED_CODE();
> …
>
> //Initialize DMA Lock
> WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
> attributes.ParentObject = devExt->Device;
> status = WdfSpinLockCreate(&attributes,&devExt->DmaLock);
> if(!NT_SUCCESS(status)){
> KdPrint(( “e2mapci : ddcInitializeDeviceExtension : WdfSpinLockCreate :
DmaLock failed!”));
> }
>
> …
>
> //
> // Initialize MTI DMA Event
> //
> KeInitializeEvent(&devExt -> MtiDMAEvent, NotificationEvent, FALSE);
> return status;
> }
>
>
>
> NTSTATUS
> ddcInitializeDMA(
> IN PDEVICE_EXTENSION devExt
> )
>
> {
> NTSTATUS status = STATUS_SUCCESS;
> WDF_DMA_ENABLER_CONFIG dmaConfig;
> WDF_OBJECT_ATTRIBUTES attributes;
> int i=0;
> PAGED_CODE();
> …
>
> //Initialize DMA Lock
> WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
> attributes.ParentObject = devExt->Device;
> status = WdfSpinLockCreate(&attributes,&devExt->DmaLock);
> if(!NT_SUCCESS(status)){
> KdPrint(( “e2mapci : ddcInitializeDeviceExtension : WdfSpinLockCreate :
DmaLock failed!”));
> }
>
> …
>
> //
> // Initialize MTI DMA Event
> //
> KeInitializeEvent(&devExt -> MtiDMAEvent, NotificationEvent, FALSE);
> return status;
> }
>
>
>
> VOID
> ddcEvtInterruptDpc(
> IN WDFINTERRUPT Interrupt,
> IN WDFDEVICE Device
> )
>
> --*/
> {
> NTSTATUS status= STATUS_SUCCESS;
> …
>
> devExt = ddcGetDeviceContext(Device);
>
> …
>
> //
> // Evaluate MSR
> //
>
> // read improvements iterrupt status
> ISR1 = ddcReadReg32(ISRReg);
>
> if(ISR1)
> {
>
> WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
> WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(
> &attributes,
> MTI_DATA_WORKITEM_CONTEXT
> );
> attributes.ParentObject = Device;
>
> WDF_WORKITEM_CONFIG_INIT(
> &workitemConfig,
> ddcDMAWorkItem
> );
>
> workitemConfig.AutomaticSerialization = FALSE;
>
> status = WdfWorkItemCreate(
> &workitemConfig,
> &attributes,
> &DMAWorkItem
> );
>
> if (!NT_SUCCESS(status)) {
> KdPrint((“Failed to create workitem %x\n”, status));
> return;
>
> }
>
> //get context
> MTIWorkitemContext = GetMTIDataWorkItemContext(DMAWorkItem);
>
> //set device in workitem context
> MTIWorkitemContext->Device = Device;
>
>
> //
> // Execute this work item.
> //
> WdfWorkItemEnqueue(DMAWorkItem);
> }
>
>
>
> }
> …
> return;
> }
>
> VOID
> ddcDMAWorkItem(
> IN WDFWORKITEM WorkItem
> )
> {
> PMTI_DATA_WORKITEM_CONTEXT pItemContext;
> NTSTATUS status= STATUS_SUCCESS;
> PDEVICE_EXTENSION devExt;
> …
> KdPrint((“ddcDMAWorkItem: called!\n”));
>
> // get work item
> pItemContext = GetMTIDataWorkItemContext(WorkItem);
>
> devExt = ddcGetDeviceContext(pItemContext->Device);
>
> // acquire DMA spinlock
> KdPrint((“ddcDMAWorkItem: waiting for spinlock!\n”));
> WdfSpinLockAcquire(devExt->DmaLock);
> KdPrint((“ddcDMAWorkItem: acquired spinlock!\n”));
>
> // send out DMA
>
> //
> // Create a new DmaTransaction.
> //
> …
>
> //
> // Initialize this new DmaTransaction.
> //
>
> …
>
> // preform DMA
> …
>
> //wait for signal event to sgnal transaction complete process buffer &
release spinlock
> KeWaitForSingleObject(&devExt -> MtiDMAEvent, Executive, KernelMode,
FALSE,NULL);
> KdPrint((“ddcDMAWorkItem: Received DMA event!\n”));
>
> //
> …
> // release SpinLock
> WdfSpinLockRelease(devExt->DmaLock);
> KdPrint((“ddcDMAWorkItem: released spinlock!\n”));
>
> // delete work item
> WdfObjectDelete(WorkItem);
>
> return;
> }
>
>
>