Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

677 lines
18 KiB

/*****************************************************************************
* sync.cpp - synchronization
*****************************************************************************
* Copyright (c) 1997-2000 Microsoft Corporation. All rights reserved.
*/
#include "private.h"
/*****************************************************************************
* IInterruptSyncInit
*****************************************************************************
* Interface for interrupt synchronizer with Init.
*/
DECLARE_INTERFACE_(IInterruptSyncInit,IInterruptSync)
{
DEFINE_ABSTRACT_UNKNOWN() // For IUnknown
// For IInterruptSync
STDMETHOD_(NTSTATUS,CallSynchronizedRoutine)
( THIS_
IN PINTERRUPTSYNCROUTINE Routine,
IN PVOID DynamicContext
) PURE;
STDMETHOD_(PKINTERRUPT,GetKInterrupt)
( THIS
) PURE;
STDMETHOD_(NTSTATUS,Connect)
( THIS
) PURE;
STDMETHOD_(void,Disconnect)
( THIS
) PURE;
STDMETHOD_(NTSTATUS,RegisterServiceRoutine)
( THIS_
IN PINTERRUPTSYNCROUTINE Routine,
IN PVOID DynamicContext,
IN BOOLEAN First
) PURE;
// For IInterruptSyncInit
STDMETHOD_(NTSTATUS,Init)
( THIS_
IN PRESOURCELIST ResourceList,
IN ULONG ResourceIndex,
IN INTERRUPTSYNCMODE Mode
) PURE;
};
typedef IInterruptSyncInit *PINTERRUPTSYNCINIT;
/*****************************************************************************
* CInterruptSync
*****************************************************************************
* Interrupt synchronizer implementation.
*/
class CInterruptSync
: public IInterruptSyncInit,
public CUnknown
{
private:
CM_PARTIAL_RESOURCE_DESCRIPTOR m_descriptor;
INTERRUPTSYNCMODE m_mode;
PKINTERRUPT m_pKInterrupt;
LIST_ENTRY m_listEntry;
KSPIN_LOCK m_kSpinLock;
KIRQL m_kIrql;
public:
DECLARE_STD_UNKNOWN();
CInterruptSync(PUNKNOWN pUnknownOuter);
~CInterruptSync();
STDMETHODIMP_(NTSTATUS) Init
(
IN PRESOURCELIST ResourceList,
IN ULONG ResourceIndex,
IN INTERRUPTSYNCMODE Mode
);
IMP_IInterruptSync;
friend
BOOLEAN
CInterruptSyncServiceRoutine
(
IN PKINTERRUPT Interrupt,
IN PVOID PVoidContext
);
friend
BOOLEAN
CInterruptSyncWrapperRoutine
(
IN PVOID PVoidContext
);
};
/*****************************************************************************
* ISRLISTENTRY
*****************************************************************************
* Entry in the list of ISRs.
*/
typedef struct
{
LIST_ENTRY ListEntry;
PINTERRUPTSYNCROUTINE Routine;
PVOID DynamicContext;
}
ISRLISTENTRY, *PISRLISTENTRY;
/*****************************************************************************
* WRAPPERROUTINECONTEXT
*****************************************************************************
* Context for synchronized routine wrapper function.
*/
typedef struct
{
PINTERRUPTSYNCROUTINE Routine;
PVOID DynamicContext;
CInterruptSync * InterruptSync;
NTSTATUS NtStatus;
}
WRAPPERROUTINECONTEXT, *PWRAPPERROUTINECONTEXT;
/*****************************************************************************
* Factory
*/
#pragma code_seg("PAGE")
/*****************************************************************************
* CreateInterruptSync()
*****************************************************************************
* Creates an interrupt synchronization object.
*/
NTSTATUS
CreateInterruptSync
(
OUT PUNKNOWN * Unknown,
IN REFCLSID,
IN PUNKNOWN UnknownOuter OPTIONAL,
IN POOL_TYPE PoolType
)
{
PAGED_CODE();
ASSERT(Unknown);
_DbgPrintF(DEBUGLVL_LIFETIME,("Creating INTERRUPTSYNC"));
STD_CREATE_BODY_
(
CInterruptSync,
Unknown,
UnknownOuter,
PoolType,
PINTERRUPTSYNC
);
}
/*****************************************************************************
* PcNewInterruptSync()
*****************************************************************************
* Creates and initializes an interrupt-level synchronization object.
*/
PORTCLASSAPI
NTSTATUS
NTAPI
PcNewInterruptSync
(
OUT PINTERRUPTSYNC * OutInterruptSync,
IN PUNKNOWN OuterUnknown OPTIONAL,
IN PRESOURCELIST ResourceList,
IN ULONG ResourceIndex,
IN INTERRUPTSYNCMODE Mode
)
{
PAGED_CODE();
ASSERT(OutInterruptSync);
ASSERT(ResourceList);
//
// Invalidate Parameters.
//
if (NULL == OutInterruptSync ||
NULL == ResourceList)
{
_DbgPrintF(DEBUGLVL_TERSE, ("PcInterruptSync : Invalid Parameter"));
return STATUS_INVALID_PARAMETER;
}
PUNKNOWN unknown;
NTSTATUS ntStatus = CreateInterruptSync( &unknown,
GUID_NULL,
OuterUnknown,
NonPagedPool );
if (NT_SUCCESS(ntStatus))
{
PINTERRUPTSYNCINIT interruptSync;
ntStatus = unknown->QueryInterface( IID_IInterruptSync,
(PVOID *) &interruptSync );
if (NT_SUCCESS(ntStatus))
{
ntStatus = interruptSync->Init( ResourceList,
ResourceIndex,
Mode );
if(NT_SUCCESS(ntStatus))
{
*OutInterruptSync = interruptSync;
}
else
{
interruptSync->Release();
}
}
unknown->Release();
}
return ntStatus;
}
#pragma code_seg("PAGE")
/*****************************************************************************
* CInterruptSync member functions
*/
/*****************************************************************************
* CInterruptSync::CInterruptSync()
*****************************************************************************
* Constructor.
*/
CInterruptSync::
CInterruptSync
( IN PUNKNOWN pUnknownOuter
)
: CUnknown(pUnknownOuter)
{
PAGED_CODE();
KeInitializeSpinLock(&m_kSpinLock);
InitializeListHead(&m_listEntry);
}
#pragma code_seg()
/*****************************************************************************
* CInterruptSync::~CInterruptSync()
*****************************************************************************
* Destructor.
*/
CInterruptSync::~CInterruptSync()
{
_DbgPrintF(DEBUGLVL_LIFETIME,("Destroying INTERRUPTSYNC (0x%08x)",this));
//
// Make sure we're disconnected.
//
Disconnect();
//
// Delete the list of ISRs.
//
if (! IsListEmpty(&m_listEntry))
{
KIRQL kIrqlOld;
KeAcquireSpinLock(&m_kSpinLock,&kIrqlOld);
//
// Get rid of any remaining members.
//
while (! IsListEmpty(&m_listEntry))
{
PLIST_ENTRY pListEntry = RemoveHeadList(&m_listEntry);
delete PISRLISTENTRY(pListEntry);
}
KeReleaseSpinLock(&m_kSpinLock,kIrqlOld);
}
}
#pragma code_seg("PAGE")
/*****************************************************************************
* CDmaChannel::NonDelegatingQueryInterface()
*****************************************************************************
* Obtains an interface.
*/
STDMETHODIMP_(NTSTATUS)
CInterruptSync::
NonDelegatingQueryInterface
(
REFIID Interface,
PVOID * Object
)
{
PAGED_CODE();
ASSERT(Object);
if (IsEqualGUIDAligned(Interface,IID_IUnknown))
{
*Object = PVOID(PUNKNOWN(this));
}
else if (IsEqualGUIDAligned(Interface,IID_IInterruptSync))
{
*Object = PVOID(PINTERRUPTSYNCINIT(this));
}
else
{
*Object = NULL;
}
if (*Object)
{
PUNKNOWN(*Object)->AddRef();
return STATUS_SUCCESS;
}
return STATUS_INVALID_PARAMETER;
}
/*****************************************************************************
* CInterruptSync::Init()
*****************************************************************************
* Initializes the synchronization object.
*/
STDMETHODIMP_(NTSTATUS)
CInterruptSync::
Init
(
IN PRESOURCELIST ResourceList,
IN ULONG ResourceIndex,
IN INTERRUPTSYNCMODE Mode
)
{
PAGED_CODE();
_DbgPrintF(DEBUGLVL_LIFETIME,("Initializing INTERRUPTSYNC (0x%08x)",this));
ASSERT(ResourceList);
m_mode = Mode;
PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor = ResourceList->FindTranslatedInterrupt(ResourceIndex);
NTSTATUS ntStatus = ( pDescriptor ? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES );
if (NT_SUCCESS(ntStatus))
{
m_descriptor = *pDescriptor;
m_pKInterrupt = NULL;
m_kIrql = KIRQL(m_descriptor.u.Interrupt.Level);
}
return ntStatus;
}
#pragma code_seg()
/*****************************************************************************
* CInterruptSyncServiceRoutine()
*****************************************************************************
* Wrapper for service routine.
*/
static
BOOLEAN
CInterruptSyncServiceRoutine
(
IN PKINTERRUPT Interrupt,
IN PVOID PVoidContext
)
{
CInterruptSync *pCInterruptSync = (CInterruptSync *)(PVoidContext);
BOOLEAN bResult = FALSE;
//
// Call ISRs as indicated by mode.
//
while (1)
{
BOOLEAN bResultThisPass = FALSE;
for
( PLIST_ENTRY pListEntry = pCInterruptSync->m_listEntry.Flink;
pListEntry != &pCInterruptSync->m_listEntry;
pListEntry = pListEntry->Flink
)
{
PISRLISTENTRY pIsrListEntry = PISRLISTENTRY(pListEntry);
ASSERT(pIsrListEntry->Routine);
if( NT_SUCCESS( pIsrListEntry->Routine( PINTERRUPTSYNC(pCInterruptSync),
pIsrListEntry->DynamicContext ) ) )
{
bResult = TRUE;
bResultThisPass = TRUE;
if (pCInterruptSync->m_mode == InterruptSyncModeNormal)
{
break;
}
}
}
if( (pCInterruptSync->m_mode != InterruptSyncModeRepeat) ||
(! bResultThisPass) )
{
break;
}
}
return bResult;
}
/*****************************************************************************
* CInterruptSync::Connect()
*****************************************************************************
* Initializes the synchronization object.
*/
STDMETHODIMP_(NTSTATUS)
CInterruptSync::
Connect
( void
)
{
_DbgPrintF(DEBUGLVL_BLAB,("CInterruptSync::Connect"));
NTSTATUS ntStatus = STATUS_SUCCESS;
KAFFINITY InterruptAffinity;
//
// Don't even think about connecting if we don't have any
// ISR's in our list
//
KIRQL oldIrql;
KeAcquireSpinLock( &m_kSpinLock, &oldIrql );
if( IsListEmpty( &m_listEntry ) )
{
ntStatus = STATUS_UNSUCCESSFUL;
}
KeReleaseSpinLock( &m_kSpinLock, oldIrql );
//
// Connect if not already connected
//
if ( (NT_SUCCESS(ntStatus)) && (!m_pKInterrupt) )
{
InterruptAffinity = m_descriptor.u.Interrupt.Affinity;
//
// If an interrupt affinity override was specified in the registry, and
// we are running on an MP machine, and at least one active processor
// will be able to handle our devices interrupt if only processors
// in both the device interrupt affinity specification AND the registry
// interrupt affinity are allowed to handle the interrupts, then
// limit the device interrupt affinity to the subset of both affinity
// masks.
//
if (gInterruptAffinity &&
KeNumberProcessors > 1 &&
(InterruptAffinity&gInterruptAffinity&KeQueryActiveProcessors()) ) {
InterruptAffinity &= gInterruptAffinity;
}
ntStatus = IoConnectInterrupt( &m_pKInterrupt,
CInterruptSyncServiceRoutine,
PVOID(this),
&m_kSpinLock, // TODO: Spin lock sharing?
m_descriptor.u.Interrupt.Vector,
m_kIrql,
m_kIrql, // TODO: Different for >1 interrupt?
((m_descriptor.Flags & CM_RESOURCE_INTERRUPT_LATCHED) ?
Latched : LevelSensitive),
(m_descriptor.ShareDisposition != CmResourceShareDeviceExclusive),
InterruptAffinity,
FALSE );
if(NT_SUCCESS(ntStatus))
{
ASSERT(m_pKInterrupt);
}
}
return ntStatus;
}
#pragma code_seg("PAGE")
/*****************************************************************************
* CInterruptSync::Disconnect()
*****************************************************************************
* Disconnect from the interrupt.
*/
STDMETHODIMP_(void)
CInterruptSync::
Disconnect
( void
)
{
_DbgPrintF(DEBUGLVL_BLAB,("CInterruptSync::Disconnect"));
PAGED_CODE();
if (m_pKInterrupt)
{
IoDisconnectInterrupt(m_pKInterrupt);
m_pKInterrupt = NULL;
}
}
#pragma code_seg()
/*****************************************************************************
* CServiceGroup::RegisterServiceRoutine()
*****************************************************************************
* Add a service routine.
*/
STDMETHODIMP_(NTSTATUS)
CInterruptSync::
RegisterServiceRoutine
(
IN PINTERRUPTSYNCROUTINE Routine,
IN PVOID DynamicContext,
IN BOOLEAN First
)
{
_DbgPrintF(DEBUGLVL_BLAB,("CInterruptSync::RegisterServiceRoutine"));
ASSERT(Routine);
NTSTATUS ntStatus = STATUS_SUCCESS;
//
// ensure we really have a routine
//
if( !Routine )
{
ntStatus = STATUS_INVALID_PARAMETER;
}
if( NT_SUCCESS(ntStatus) )
{
//
// Create a new member.
//
PISRLISTENTRY pIsrListEntry = new(NonPagedPool,'lIcP') ISRLISTENTRY;
if (pIsrListEntry)
{
pIsrListEntry->Routine = Routine;
pIsrListEntry->DynamicContext = DynamicContext;
//
// Add the member to the list.
//
KIRQL kIrqlOld;
KeAcquireSpinLock(&m_kSpinLock,&kIrqlOld);
if (First)
{
InsertHeadList( &m_listEntry, &pIsrListEntry->ListEntry );
}
else
{
InsertTailList( &m_listEntry, &pIsrListEntry->ListEntry );
}
KeReleaseSpinLock(&m_kSpinLock,kIrqlOld);
}
else
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
}
return ntStatus;
}
/*****************************************************************************
* CInterruptSyncWrapperRoutine()
*****************************************************************************
* Wrapper for synchronized routines.
*/
static
BOOLEAN
CInterruptSyncWrapperRoutine
(
IN PVOID PVoidContext
)
{
PWRAPPERROUTINECONTEXT pContext = PWRAPPERROUTINECONTEXT(PVoidContext);
pContext->NtStatus = pContext->Routine( PINTERRUPTSYNC(pContext->InterruptSync),
pContext->DynamicContext);
return TRUE;
}
/*****************************************************************************
* CInterruptSync::CallSynchronizedRoutine()
*****************************************************************************
* Call a synchronized routine.
*/
STDMETHODIMP_(NTSTATUS)
CInterruptSync::
CallSynchronizedRoutine
(
IN PINTERRUPTSYNCROUTINE Routine,
IN PVOID DynamicContext
)
{
WRAPPERROUTINECONTEXT context;
context.Routine = Routine;
context.DynamicContext = DynamicContext;
context.InterruptSync = this;
context.NtStatus = STATUS_SUCCESS;
if (m_pKInterrupt)
{
if (!KeSynchronizeExecution(m_pKInterrupt,CInterruptSyncWrapperRoutine,&context ) )
{
context.NtStatus = STATUS_UNSUCCESSFUL;
}
}
else if (KeGetCurrentIrql() <= DISPATCH_LEVEL)
{
_DbgPrintF(DEBUGLVL_TERSE,("Interrupt not connected yet, using spinlock"));
KIRQL kIrqlOld;
KeAcquireSpinLock(&m_kSpinLock,&kIrqlOld);
// we have no interrupt yet, so synchronize the best you can
(void)CInterruptSyncWrapperRoutine(&context);
KeReleaseSpinLock(&m_kSpinLock,kIrqlOld);
}
else
{
context.NtStatus = STATUS_UNSUCCESSFUL;
_DbgPrintF(DEBUGLVL_TERSE,("Interrupt not connected yet, but IRQL > DISPATCH_LEVEL"));
}
return context.NtStatus;
}
/*****************************************************************************
* CInterruptSync::GetKInterrupt()
*****************************************************************************
* Get a WDM InterruptObject from a portcls sync object.
*/
STDMETHODIMP_(PKINTERRUPT)
CInterruptSync::
GetKInterrupt
( void
)
{
return m_pKInterrupt;
}