[PORTCLS]

- Don't request initializing delayed service request as this is the task of the miniport driver
- Reimplement the service group object:
- Use the initialized timer object when RequestService is called
- Fix possible race conditions when adding / removing a service sink by protecting it with a lock
- Acquire the service group list lock when executing the shared dpc routine

svn path=/trunk/; revision=47197
This commit is contained in:
Johannes Anderwald 2010-05-14 15:47:00 +00:00
parent ebb491824a
commit 0801068a35
4 changed files with 116 additions and 81 deletions

View file

@ -602,7 +602,6 @@ CPortPinDMus::Init(
DPRINT("Failed to add pin to service group\n"); DPRINT("Failed to add pin to service group\n");
return Status; return Status;
} }
m_ServiceGroup->SupportDelayedService();
} }
Status = m_IrpQueue->Init(ConnectDetails, 0, 0, NULL); Status = m_IrpQueue->Init(ConnectDetails, 0, 0, NULL);

View file

@ -1213,7 +1213,6 @@ CPortPinWaveCyclic::Init(
return Status; return Status;
} }
m_ServiceGroup->SupportDelayedService();
m_Stream->SetState(KSSTATE_STOP); m_Stream->SetState(KSSTATE_STOP);
m_State = KSSTATE_STOP; m_State = KSSTATE_STOP;
m_CommonBufferOffset = 0; m_CommonBufferOffset = 0;
@ -1224,6 +1223,8 @@ CPortPinWaveCyclic::Init(
m_Delay = Int32x32To64(10, -10000); m_Delay = Int32x32To64(10, -10000);
Status = m_Stream->SetNotificationFreq(10, &m_FrameSize); Status = m_Stream->SetNotificationFreq(10, &m_FrameSize);
PC_ASSERT(NT_SUCCESS(Status));
PC_ASSERT(m_FrameSize);
SilenceBuffer = AllocateItem(NonPagedPool, m_FrameSize, TAG_PORTCLASS); SilenceBuffer = AllocateItem(NonPagedPool, m_FrameSize, TAG_PORTCLASS);
if (!SilenceBuffer) if (!SilenceBuffer)

View file

@ -815,7 +815,6 @@ CPortPinWavePci::Init(
DPRINT("Failed to add pin to service group\n"); DPRINT("Failed to add pin to service group\n");
return Status; return Status;
} }
m_ServiceGroup->SupportDelayedService();
} }
// delay of 10 milisec // delay of 10 milisec

View file

@ -54,12 +54,10 @@ protected:
LIST_ENTRY m_ServiceSinkHead; LIST_ENTRY m_ServiceSinkHead;
BOOL m_Initialized; BOOL m_TimerInitialized;
BOOL m_TimerActive;
KTIMER m_Timer; KTIMER m_Timer;
KDPC m_Dpc; KDPC m_Dpc;
KEVENT m_Event; KSPIN_LOCK m_Lock;
LONG m_ThreadActive;
friend VOID NTAPI IServiceGroupDpc(IN struct _KDPC *Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2); friend VOID NTAPI IServiceGroupDpc(IN struct _KDPC *Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2);
@ -105,9 +103,16 @@ CServiceGroup::QueryInterface(
CServiceGroup::CServiceGroup(IUnknown * OuterUnknown) CServiceGroup::CServiceGroup(IUnknown * OuterUnknown)
{ {
// initialize dpc
KeInitializeDpc(&m_Dpc, IServiceGroupDpc, (PVOID)this); KeInitializeDpc(&m_Dpc, IServiceGroupDpc, (PVOID)this);
// set highest importance
KeSetImportanceDpc(&m_Dpc, HighImportance); KeSetImportanceDpc(&m_Dpc, HighImportance);
KeInitializeEvent(&m_Event, NotificationEvent, FALSE);
// initialize service group list lock
KeInitializeSpinLock(&m_Lock);
// initialize service group list
InitializeListHead(&m_ServiceSinkHead); InitializeListHead(&m_ServiceSinkHead);
} }
@ -119,16 +124,35 @@ CServiceGroup::RequestService()
DPRINT("CServiceGroup::RequestService() Dpc at Level %u\n", KeGetCurrentIrql()); DPRINT("CServiceGroup::RequestService() Dpc at Level %u\n", KeGetCurrentIrql());
if (m_TimerInitialized)
{
LARGE_INTEGER DueTime;
// no due time
DueTime.QuadPart = 0LL;
// delayed service requested
KeSetTimer(&m_Timer, DueTime, &m_Dpc);
}
else
{
// check curent irql
if (KeGetCurrentIrql() > DISPATCH_LEVEL) if (KeGetCurrentIrql() > DISPATCH_LEVEL)
{ {
//insert dpc to queue
KeInsertQueueDpc(&m_Dpc, NULL, NULL); KeInsertQueueDpc(&m_Dpc, NULL, NULL);
return;
} }
else
{
// raise irql to dispatch level to make dpc fire immediately
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
// insert dpc to queue
KeInsertQueueDpc(&m_Dpc, NULL, NULL); KeInsertQueueDpc(&m_Dpc, NULL, NULL);
// lower irql to old level
KeLowerIrql(OldIrql); KeLowerIrql(OldIrql);
} }
}
}
//--------------------------------------------------------------- //---------------------------------------------------------------
// IServiceGroup methods // IServiceGroup methods
@ -140,18 +164,33 @@ CServiceGroup::AddMember(
IN PSERVICESINK pServiceSink) IN PSERVICESINK pServiceSink)
{ {
PGROUP_ENTRY Entry; PGROUP_ENTRY Entry;
KIRQL OldLevel;
// sanity check
PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
// allocate service sink entry
Entry = (PGROUP_ENTRY)AllocateItem(NonPagedPool, sizeof(GROUP_ENTRY), TAG_PORTCLASS); Entry = (PGROUP_ENTRY)AllocateItem(NonPagedPool, sizeof(GROUP_ENTRY), TAG_PORTCLASS);
if (!Entry) if (!Entry)
{
// out of memory
return STATUS_INSUFFICIENT_RESOURCES; return STATUS_INSUFFICIENT_RESOURCES;
}
// initialize service sink entry
Entry->pServiceSink = pServiceSink; Entry->pServiceSink = pServiceSink;
// increment reference count
pServiceSink->AddRef(); pServiceSink->AddRef();
// acquire service group list lock
KeAcquireSpinLock(&m_Lock, &OldLevel);
// insert into service sink list
InsertTailList(&m_ServiceSinkHead, &Entry->Entry); InsertTailList(&m_ServiceSinkHead, &Entry->Entry);
// release service group list lock
KeReleaseSpinLock(&m_Lock, OldLevel);
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
@ -162,23 +201,45 @@ CServiceGroup::RemoveMember(
{ {
PLIST_ENTRY CurEntry; PLIST_ENTRY CurEntry;
PGROUP_ENTRY Entry; PGROUP_ENTRY Entry;
KIRQL OldLevel;
// sanity check
PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
// acquire service group list lock
KeAcquireSpinLock(&m_Lock, &OldLevel);
// grab first entry
CurEntry = m_ServiceSinkHead.Flink; CurEntry = m_ServiceSinkHead.Flink;
// loop list until the passed entry is found
while (CurEntry != &m_ServiceSinkHead) while (CurEntry != &m_ServiceSinkHead)
{ {
// grab entry
Entry = CONTAINING_RECORD(CurEntry, GROUP_ENTRY, Entry); Entry = CONTAINING_RECORD(CurEntry, GROUP_ENTRY, Entry);
// check if it matches the passed entry
if (Entry->pServiceSink == pServiceSink) if (Entry->pServiceSink == pServiceSink)
{ {
// remove entry from list
RemoveEntryList(&Entry->Entry); RemoveEntryList(&Entry->Entry);
// release service sink reference
pServiceSink->Release(); pServiceSink->Release();
// free service sink entry
FreeItem(Entry, TAG_PORTCLASS); FreeItem(Entry, TAG_PORTCLASS);
return;
// leave loop
break;
} }
// move to next entry
CurEntry = CurEntry->Flink; CurEntry = CurEntry->Flink;
} }
// release service group list lock
KeReleaseSpinLock(&m_Lock, OldLevel);
} }
VOID VOID
@ -194,73 +255,40 @@ IServiceGroupDpc(
PGROUP_ENTRY Entry; PGROUP_ENTRY Entry;
CServiceGroup * This = (CServiceGroup*)DeferredContext; CServiceGroup * This = (CServiceGroup*)DeferredContext;
// acquire service group list lock
KeAcquireSpinLockAtDpcLevel(&This->m_Lock);
// grab first entry
CurEntry = This->m_ServiceSinkHead.Flink; CurEntry = This->m_ServiceSinkHead.Flink;
// loop the list and call the attached service sink/group
while (CurEntry != &This->m_ServiceSinkHead) while (CurEntry != &This->m_ServiceSinkHead)
{ {
//grab current entry
Entry = (PGROUP_ENTRY)CONTAINING_RECORD(CurEntry, GROUP_ENTRY, Entry); Entry = (PGROUP_ENTRY)CONTAINING_RECORD(CurEntry, GROUP_ENTRY, Entry);
// call service sink/group
Entry->pServiceSink->RequestService(); Entry->pServiceSink->RequestService();
// move to next entry
CurEntry = CurEntry->Flink; CurEntry = CurEntry->Flink;
} }
// release service group list lock
KeReleaseSpinLockFromDpcLevel(&This->m_Lock);
} }
#if 0
VOID
NTAPI
ServiceGroupThread(IN PVOID StartContext)
{
NTSTATUS Status;
KWAIT_BLOCK WaitBlockArray[2];
PVOID WaitObjects[2];
CServiceGroup * This = (CServiceGroup*)StartContext;
// Set thread state
InterlockedIncrement(&This->m_ThreadActive);
// Setup the wait objects
WaitObjects[0] = &m_Timer;
WaitObjects[1] = &m_Event;
do
{
// Wait on our objects
Status = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, Executive, KernelMode, FALSE, NULL, WaitBlockArray);
switch(Status)
{
case STATUS_WAIT_0:
IServiceGroupDpc(&This->m_Dpc, (PVOID)This, NULL, NULL);
break;
case STATUS_WAIT_1:
PsTerminateSystemThread(STATUS_SUCCESS);
return;
}
}while(TRUE);
}
#endif
VOID VOID
NTAPI NTAPI
CServiceGroup::SupportDelayedService() CServiceGroup::SupportDelayedService()
{ {
//NTSTATUS Status;
//HANDLE ThreadHandle;
PC_ASSERT_IRQL(DISPATCH_LEVEL); PC_ASSERT_IRQL(DISPATCH_LEVEL);
if (m_Initialized) // initialize the timer
return; KeInitializeTimer(&m_Timer);
KeInitializeTimerEx(&m_Timer, NotificationTimer); // use the timer to perform service requests
m_TimerInitialized = TRUE;
#if 0
Status = PsCreateSystemThread(&ThreadHandle, THREAD_ALL_ACCESS, NULL, 0, NULL, ServiceGroupThread, (PVOID)This);
if (NT_SUCCESS(Status))
{
ZwClose(ThreadHandle);
m_Initialized = TRUE;
}
#endif
} }
VOID VOID
@ -270,17 +298,14 @@ CServiceGroup::RequestDelayedService(
{ {
LARGE_INTEGER DueTime; LARGE_INTEGER DueTime;
// sanity check
PC_ASSERT_IRQL(DISPATCH_LEVEL); PC_ASSERT_IRQL(DISPATCH_LEVEL);
PC_ASSERT(m_TimerInitialized);
DueTime.QuadPart = ullDelay; DueTime.QuadPart = ullDelay;
if (m_Initialized) // set the timer
{
if (KeGetCurrentIrql() <= DISPATCH_LEVEL)
KeSetTimer(&m_Timer, DueTime, &m_Dpc); KeSetTimer(&m_Timer, DueTime, &m_Dpc);
else
KeInsertQueueDpc(&m_Dpc, NULL, NULL);
}
} }
VOID VOID
@ -288,12 +313,11 @@ NTAPI
CServiceGroup::CancelDelayedService() CServiceGroup::CancelDelayedService()
{ {
PC_ASSERT_IRQL(DISPATCH_LEVEL); PC_ASSERT_IRQL(DISPATCH_LEVEL);
PC_ASSERT(m_TimerInitialized);
if (m_Initialized) // cancel the timer
{
KeCancelTimer(&m_Timer); KeCancelTimer(&m_Timer);
} }
}
NTSTATUS NTSTATUS
NTAPI NTAPI
@ -303,19 +327,31 @@ PcNewServiceGroup(
{ {
CServiceGroup * This; CServiceGroup * This;
NTSTATUS Status; NTSTATUS Status;
DPRINT("PcNewServiceGroup entered\n"); DPRINT("PcNewServiceGroup entered\n");
This = new(NonPagedPool, TAG_PORTCLASS)CServiceGroup(OuterUnknown); //FIXME support aggregation
if (!This) PC_ASSERT(OuterUnknown == NULL);
return STATUS_INSUFFICIENT_RESOURCES;
// allocate a service group object
This = new(NonPagedPool, TAG_PORTCLASS)CServiceGroup(OuterUnknown);
if (!This)
{
// out of memory
return STATUS_INSUFFICIENT_RESOURCES;
}
// request IServiceSink interface
Status = This->QueryInterface(IID_IServiceSink, (PVOID*)OutServiceGroup); Status = This->QueryInterface(IID_IServiceSink, (PVOID*)OutServiceGroup);
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
{ {
// failed to acquire service sink interface
delete This; delete This;
return Status; return Status;
} }
// done
return Status; return Status;
} }