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.
5940 lines
161 KiB
5940 lines
161 KiB
/*++
|
|
|
|
Copyright (c) 1998-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
rm.c
|
|
|
|
Abstract:
|
|
|
|
Implementation of the "Resource Manager" APIs.
|
|
|
|
Revision History:
|
|
|
|
Who When What
|
|
-------- -------- ----------------------------------------------
|
|
josephj 11-18-98 Created
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
#include <precomp.h>
|
|
|
|
//
|
|
// File-specific debugging defaults.
|
|
//
|
|
#define TM_CURRENT TM_RM
|
|
|
|
//=========================================================================
|
|
// U T I L I T Y M A C R O S
|
|
//=========================================================================
|
|
|
|
#define RM_ALLOC(_pp, _size, _tag) \
|
|
NdisAllocateMemoryWithTag((_pp), (_size), (_tag))
|
|
|
|
#define RM_ALLOCSTRUCT(_p, _tag) \
|
|
NdisAllocateMemoryWithTag(&(_p), sizeof(*(_p)), (_tag))
|
|
|
|
#define RM_FREE(_p) NdisFreeMemory((_p), 0, 0)
|
|
|
|
#define RM_ZEROSTRUCT(_p) \
|
|
NdisZeroMemory((_p), sizeof(*(_p)))
|
|
|
|
#define RM_PRIVATE_UNLINK_NEXT_HASH(_pHashTable, _ppLink) \
|
|
((*(_ppLink) = (*(_ppLink))->pNext), ((_pHashTable)->NumItems--))
|
|
|
|
#define SET_RM_STATE(_pHdr, _Mask, _Val) \
|
|
(((_pHdr)->RmState) = (((_pHdr)->RmState) & ~(_Mask)) | (_Val))
|
|
|
|
#define CHECK_RM_STATE(_pHdr, _Mask, _Val) \
|
|
((((_pHdr)->RmState) & (_Mask)) == (_Val))
|
|
|
|
#define RMISALLOCATED(_pHdr) \
|
|
CHECK_RM_STATE((_pHdr), RMOBJSTATE_ALLOCMASK, RMOBJSTATE_ALLOCATED)
|
|
|
|
#define SET_RM_TASK_STATE(_pTask, _pState) \
|
|
SET_RM_STATE(&(_pTask)->Hdr, RMTSKSTATE_MASK, (_pState))
|
|
|
|
#define CHECK_RM_TASK_STATE(_pTask, _pState) \
|
|
CHECK_RM_STATE(&(_pTask)->Hdr, RMTSKSTATE_MASK, (_pState))
|
|
|
|
#define GET_RM_TASK_STATE(_pTask) \
|
|
((_pTask)->Hdr.RmState & RMTSKSTATE_MASK)
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
#define RMPRIVATELOCK(_pobj, _psr) \
|
|
rmLock(&(_pobj)->RmPrivateLock, 0, rmPrivateLockVerifier, (_pobj), (_psr))
|
|
#else // !RM_EXTRA_CHECKING
|
|
#define RMPRIVATELOCK(_pobj, _psr) \
|
|
rmLock(&(_pobj)->RmPrivateLock, (_psr))
|
|
#endif // !RM_EXTRA_CHECKING
|
|
|
|
#define RMPRIVATEUNLOCK(_pobj, _psr) \
|
|
rmUnlock(&(_pobj)->RmPrivateLock, (_psr))
|
|
|
|
|
|
#if 0
|
|
#define RM_TEST_SIG 0x59dcfd36
|
|
#define RM_TEST_DEALLOC_SIG 0x21392147
|
|
#define RM_OBJECT_IS_ALLOCATED(_pobj) \
|
|
((_pobj)->Sig == RM_TEST_SIG)
|
|
#define RM_MARK_OBJECT_AS_DEALLOCATED(_pobj) \
|
|
((_pobj)->Sig = RM_TEST_DEALLOC_SIG)
|
|
#else
|
|
#define RM_OBJECT_IS_ALLOCATED(_pobj) 0x1
|
|
#define RM_MARK_OBJECT_AS_DEALLOCATED(_pobj) (0)
|
|
#endif
|
|
|
|
//=========================================================================
|
|
// L O C A L P R O T O T Y P E S
|
|
//=========================================================================
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
|
|
// The lowest AssociationID used internal to the RM API implementation.
|
|
//
|
|
#define RM_PRIVATE_ASSOC_BASE (0x1<<31)
|
|
|
|
// Association types internal to RM API impmenentation.
|
|
//
|
|
enum
|
|
{
|
|
RM_PRIVATE_ASSOC_LINK = RM_PRIVATE_ASSOC_BASE,
|
|
RM_PRIVATE_ASSOC_LINK_CHILDOF,
|
|
RM_PRIVATE_ASSOC_LINK_PARENTOF,
|
|
RM_PRIVATE_ASSOC_LINK_TASKPENDINGON,
|
|
RM_PRIVATE_ASSOC_LINK_TASKBLOCKS,
|
|
RM_PRIVATE_ASSOC_INITGROUP,
|
|
RM_PRIVATE_ASSOC_RESUME_TASK_ASYNC,
|
|
RM_PRIVATE_ASSOC_RESUME_TASK_DELAYED
|
|
};
|
|
|
|
|
|
const char *szASSOCFORMAT_LINK = " Linked to 0x%p (%s)\n";
|
|
const char *szASSOCFORMAT_LINK_CHILDOF = " Child of 0x%p (%s)\n";
|
|
const char *szASSOCFORMAT_LINK_PARENTOF = " Parent of 0x%p (%s)\n";
|
|
const char *szASSOCFORMAT_LINK_TASKPENDINGON = " Pending on 0x%p (%s)\n";
|
|
const char *szASSOCFORMAT_LINK_TASKBLOCKS = " Blocks 0x%p (%s)\n";
|
|
const char *szASSOCFORMAT_INITGROUP = " Owns group 0x%p (%s)\n";
|
|
const char *szASSOCFORMAT_RESUME_TASK_ASYNC = " Resume async (param=0x%p)\n";
|
|
const char *szASSOCFORMAT_RESUME_TASK_DELAYED = " Resume delayed (param=0x%p)\n";
|
|
|
|
// Linked to 0x098889(LocalIP)
|
|
// Parent of 0x098889(InitIPTask)
|
|
// Child of 0x098889(Interface)
|
|
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
// Private RM task to unload all objects in a group.
|
|
//
|
|
typedef struct
|
|
{
|
|
RM_TASK TskHdr; // Common task header
|
|
PRM_GROUP pGroup; // Group being unloaded
|
|
UINT uIndex; // Index of hash-table currently being
|
|
// unloaded.
|
|
NDIS_EVENT BlockEvent; // Event to optionally signal when done.
|
|
BOOLEAN fUseEvent; // TRUE IFF event is to be signaled.
|
|
PFN_RM_TASK_HANDLER pfnTaskUnloadObjectHandler; // ...
|
|
// Object's unload task.
|
|
PFN_RM_TASK_ALLOCATOR pfnUnloadTaskAllocator;
|
|
|
|
} TASK_UNLOADGROUP;
|
|
|
|
|
|
//
|
|
// RM_PRIVATE_TASK is the union of all tasks structures used intenally in rm.c.
|
|
// rmAllocateTask allocates memory of sizeof(RM_PRIVATE_TASK), which is guaranteed
|
|
// to be large enough to hold any task internal to rm.c
|
|
//
|
|
typedef union
|
|
{
|
|
RM_TASK TskHdr;
|
|
TASK_UNLOADGROUP UnloadGroup;
|
|
|
|
} RM_PRIVATE_TASK;
|
|
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
|
|
VOID
|
|
rmDbgInitializeDiagnosticInfo(
|
|
PRM_OBJECT_HEADER pObject,
|
|
PRM_STACK_RECORD pSR
|
|
);
|
|
|
|
VOID
|
|
rmDbgDeinitializeDiagnosticInfo(
|
|
PRM_OBJECT_HEADER pObject,
|
|
PRM_STACK_RECORD pSR
|
|
);
|
|
|
|
VOID
|
|
rmDbgPrintOneAssociation (
|
|
PRM_HASH_LINK pLink,
|
|
PVOID pvContext,
|
|
PRM_STACK_RECORD pSR
|
|
);
|
|
|
|
VOID
|
|
_cdecl
|
|
rmDefaultDumpEntry (
|
|
char *szFormatString,
|
|
UINT_PTR Param1,
|
|
UINT_PTR Param2,
|
|
UINT_PTR Param3,
|
|
UINT_PTR Param4
|
|
);
|
|
|
|
UINT
|
|
rmSafeAppend(
|
|
char *szBuf,
|
|
const char *szAppend,
|
|
UINT cbBuf
|
|
);
|
|
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
|
|
NDIS_STATUS
|
|
rmTaskUnloadGroup(
|
|
IN struct _RM_TASK * pTask,
|
|
IN RM_TASK_OPERATION Code,
|
|
IN UINT_PTR UserParam, // Unused
|
|
IN PRM_STACK_RECORD pSR
|
|
);
|
|
|
|
|
|
NDIS_STATUS
|
|
rmAllocatePrivateTask(
|
|
IN PRM_OBJECT_HEADER pParentObject,
|
|
IN PFN_RM_TASK_HANDLER pfnHandler,
|
|
IN UINT Timeout,
|
|
IN const char * szDescription, OPTIONAL
|
|
OUT PRM_TASK *ppTask,
|
|
IN PRM_STACK_RECORD pSR
|
|
);
|
|
|
|
VOID
|
|
rmWorkItemHandler_ResumeTaskAsync(
|
|
IN PNDIS_WORK_ITEM pWorkItem,
|
|
IN PVOID pTaskToResume
|
|
);
|
|
|
|
|
|
VOID
|
|
rmTimerHandler_ResumeTaskDelayed(
|
|
IN PVOID SystemSpecific1,
|
|
IN PVOID FunctionContext,
|
|
IN PVOID SystemSpecific2,
|
|
IN PVOID SystemSpecific3
|
|
);
|
|
|
|
VOID
|
|
rmPrivateTaskDelete (
|
|
PRM_OBJECT_HEADER pObj,
|
|
PRM_STACK_RECORD psr
|
|
);
|
|
|
|
VOID
|
|
rmDerefObject(
|
|
PRM_OBJECT_HEADER pObject,
|
|
PRM_STACK_RECORD pSR
|
|
);
|
|
|
|
VOID
|
|
rmLock(
|
|
PRM_LOCK pLock,
|
|
#if RM_EXTRA_CHECKING
|
|
UINT uLocID,
|
|
PFNLOCKVERIFIER pfnVerifier,
|
|
PVOID pVerifierContext,
|
|
#endif //RM_EXTRA_CHECKING
|
|
PRM_STACK_RECORD pSR
|
|
);
|
|
|
|
VOID
|
|
rmUnlock(
|
|
PRM_LOCK pLock,
|
|
PRM_STACK_RECORD pSR
|
|
);
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
ULONG
|
|
rmPrivateLockVerifier(
|
|
PRM_LOCK pLock,
|
|
BOOLEAN fLock,
|
|
PVOID pContext,
|
|
PRM_STACK_RECORD pSR
|
|
);
|
|
|
|
ULONG
|
|
rmVerifyObjectState(
|
|
PRM_LOCK pLock,
|
|
BOOLEAN fLock,
|
|
PVOID pContext,
|
|
PRM_STACK_RECORD pSR
|
|
);
|
|
|
|
RM_DBG_LOG_ENTRY *
|
|
rmDbgAllocateLogEntry(VOID);
|
|
|
|
VOID
|
|
rmDbgDeallocateLogEntry(
|
|
RM_DBG_LOG_ENTRY *pLogEntry
|
|
);
|
|
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
VOID
|
|
rmEndTask(
|
|
PRM_TASK pTask,
|
|
NDIS_STATUS Status,
|
|
PRM_STACK_RECORD pSR
|
|
);
|
|
|
|
|
|
VOID
|
|
rmUpdateHashTableStats(
|
|
PULONG pStats,
|
|
ULONG LinksTraversed
|
|
);
|
|
|
|
typedef struct
|
|
{
|
|
PFN_RM_GROUP_ENUMERATOR pfnObjEnumerator;
|
|
PVOID pvCallerContext;
|
|
INT fContinue;
|
|
|
|
} RM_STRONG_ENUMERATION_CONTEXT, *PRM_STRONG_ENUMERATION_CONTEXT;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
PRM_OBJECT_HEADER *ppCurrent;
|
|
PRM_OBJECT_HEADER *ppEnd;
|
|
|
|
} RM_WEAK_ENUMERATION_CONTEXT, *PRM_WEAK_ENUMERATION_CONTEXT;
|
|
|
|
|
|
VOID
|
|
rmEnumObjectInGroupHashTable (
|
|
PRM_HASH_LINK pLink,
|
|
PVOID pvContext,
|
|
PRM_STACK_RECORD pSR
|
|
);
|
|
|
|
VOID
|
|
rmConstructGroupSnapshot (
|
|
PRM_HASH_LINK pLink,
|
|
PVOID pvContext,
|
|
PRM_STACK_RECORD pSR
|
|
);
|
|
|
|
//=========================================================================
|
|
// L O C A L D A T A
|
|
//=========================================================================
|
|
|
|
// Global struture for the RM apis.
|
|
//
|
|
struct
|
|
{
|
|
// Accessed via interlocked operation.
|
|
//
|
|
ULONG Initialized;
|
|
|
|
RM_OS_LOCK GlobalOsLock;
|
|
LIST_ENTRY listGlobalLog;
|
|
UINT NumGlobalLogEntries;
|
|
|
|
} RmGlobals;
|
|
|
|
|
|
RM_STATIC_OBJECT_INFO
|
|
RmPrivateTasks_StaticInfo =
|
|
{
|
|
0, // TypeUID
|
|
0, // TypeFlags
|
|
"RM Private Task", // TypeName
|
|
0, // Timeout
|
|
|
|
NULL, // pfnCreate
|
|
rmPrivateTaskDelete, // pfnDelete
|
|
NULL, // LockVerifier
|
|
|
|
0, // length of resource table
|
|
NULL // Resource Table
|
|
};
|
|
|
|
|
|
// TODO: make constant
|
|
static
|
|
RM_STATIC_OBJECT_INFO
|
|
RmTask_StaticInfo =
|
|
{
|
|
0, // TypeUID
|
|
0, // TypeFlags
|
|
"Task", // TypeName
|
|
0, // Timeout
|
|
|
|
NULL, // Create
|
|
NULL, // Delete
|
|
NULL, // LockVerifier
|
|
|
|
0, // ResourceTable size
|
|
NULL // ResourceTable
|
|
};
|
|
|
|
|
|
//=========================================================================
|
|
// R M A P I S
|
|
//=========================================================================
|
|
|
|
|
|
#define RM_INITIALIZATION_STARTING 1
|
|
#define RM_INITIALIZATION_COMPLETE 2
|
|
|
|
VOID
|
|
RmInitializeRm(VOID)
|
|
/*++
|
|
Must be called before any RM APIs are called.
|
|
TODO: replace by registration mechanism.
|
|
See notes.txt 03/07/1999 entry "Registering root objects with RM".
|
|
--*/
|
|
{
|
|
ENTER("RmInitializeRm", 0x29f5d167)
|
|
|
|
if (InterlockedCompareExchange(
|
|
&RmGlobals.Initialized, RM_INITIALIZATION_STARTING, 0)==0)
|
|
{
|
|
TR_INFO(("Initializing RM APIs Global Info\n"));
|
|
NdisAllocateSpinLock(&RmGlobals.GlobalOsLock);
|
|
InitializeListHead(&RmGlobals.listGlobalLog);
|
|
|
|
InterlockedExchange(&RmGlobals.Initialized, RM_INITIALIZATION_COMPLETE);
|
|
}
|
|
else
|
|
{
|
|
// Spin waiting for it to get to RM_INITIALIZATION_COMPLETE (allocated).
|
|
TR_INFO(("Spinning, waiting for initialization to complete.\n"));
|
|
while (RmGlobals.Initialized != RM_INITIALIZATION_COMPLETE)
|
|
{
|
|
// spin
|
|
}
|
|
}
|
|
|
|
EXIT()
|
|
}
|
|
|
|
|
|
VOID
|
|
RmDeinitializeRm(VOID)
|
|
/*++
|
|
Must be called to deinitialze, after last RM api is called and all async
|
|
activity is over.
|
|
TODO: replace by deregistration mechanism.
|
|
See notes.txt 03/07/1999 entry "Registering root objects with RM".
|
|
--*/
|
|
{
|
|
ENTER("RmDeinitializeRm", 0x9a8407e9)
|
|
|
|
ASSERT(RmGlobals.Initialized == RM_INITIALIZATION_COMPLETE);
|
|
TR_INFO(("Deinitializing RM APIs Global Info\n"));
|
|
|
|
// Make sure global log list is empty. Acquiring the GLobalOsLock is
|
|
// not necessary here because all activity has stopped by now.
|
|
//
|
|
ASSERT(IsListEmpty(&RmGlobals.listGlobalLog));
|
|
|
|
EXIT()
|
|
}
|
|
|
|
|
|
VOID
|
|
RmInitializeHeader(
|
|
IN PRM_OBJECT_HEADER pParentObject,
|
|
IN PRM_OBJECT_HEADER pObject,
|
|
IN UINT Sig,
|
|
IN PRM_LOCK pLock,
|
|
IN PRM_STATIC_OBJECT_INFO pStaticInfo,
|
|
IN const char * szDescription,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the RM_OBJECT_HEADER portion of an object.
|
|
|
|
Arguments:
|
|
|
|
pParentObject - NULL for a root object.
|
|
pObject - Object to be initialized.
|
|
Sig - Signature of the object.
|
|
pLock - Lock used to serialize access to the object.
|
|
pStaticInfo - Static informatation about the object.
|
|
szDescription - A discriptive string (for debugging only) to be associated
|
|
with this object.
|
|
--*/
|
|
{
|
|
ENTER("RmInitializeHeader", 0x47dea382)
|
|
|
|
NdisZeroMemory(pObject, sizeof(*pObject));
|
|
|
|
if (szDescription == NULL)
|
|
{
|
|
szDescription = pStaticInfo->szTypeName;
|
|
}
|
|
|
|
TR_VERB(("Initializing header 0x%p (%s)\n", pObject, szDescription));
|
|
|
|
pObject->Sig = Sig;
|
|
pObject->pLock = pLock;
|
|
pObject->pStaticInfo = pStaticInfo;
|
|
pObject->szDescription = szDescription;
|
|
SET_RM_STATE(pObject, RMOBJSTATE_ALLOCMASK, RMOBJSTATE_ALLOCATED);
|
|
|
|
// The private lock is set to level (UINT)-1, which is the highest
|
|
// possible level.
|
|
//
|
|
RmInitializeLock(&pObject->RmPrivateLock, (UINT)-1);
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
rmDbgInitializeDiagnosticInfo(pObject, pSR);
|
|
#endif //RM_EXTRA_CHECKING
|
|
|
|
// Link to parent if non NULL.
|
|
//
|
|
|
|
if (pParentObject != NULL)
|
|
{
|
|
pObject->pParentObject = pParentObject;
|
|
pObject->pRootObject = pParentObject->pRootObject;
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
RmLinkObjectsEx(
|
|
pObject,
|
|
pParentObject,
|
|
0x11f25620,
|
|
RM_PRIVATE_ASSOC_LINK_CHILDOF,
|
|
szASSOCFORMAT_LINK_CHILDOF,
|
|
RM_PRIVATE_ASSOC_LINK_PARENTOF,
|
|
szASSOCFORMAT_LINK_PARENTOF,
|
|
pSR
|
|
);
|
|
#else // !RM_EXTRA_CHECKING
|
|
RmLinkObjects(pObject, pParentObject, pSR);
|
|
#endif // !RM_EXTRA_CHECKING
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
pObject->pRootObject = pObject;
|
|
}
|
|
|
|
|
|
// We increment the total-ref count once for the allocation. This
|
|
// reference is removed in the call to RmDeallocateObject.
|
|
// Note that this reference is in addition to the reference implicitly
|
|
// added by the call to RmLinkObjects above.
|
|
//
|
|
NdisInterlockedIncrement(&pObject->TotRefs);
|
|
|
|
#if RM_TRACK_OBJECT_TREE
|
|
|
|
// Initialize our list of children.
|
|
//
|
|
InitializeListHead(&pObject->listChildren);
|
|
|
|
if (pParentObject != NULL)
|
|
{
|
|
// Insert ourselves into our parent's list of children.
|
|
//
|
|
RMPRIVATELOCK(pParentObject, pSR);
|
|
InsertHeadList(&pParentObject->listChildren, &pObject->linkSiblings);
|
|
RMPRIVATEUNLOCK(pParentObject, pSR);
|
|
}
|
|
#endif // RM_TRACK_OBJECT_TREE
|
|
|
|
EXIT()
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
RmDeallocateObject(
|
|
IN PRM_OBJECT_HEADER pObject,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Logically deallocate the object pObject. We don't actually unlink it from
|
|
its parent or deallocate if there are non zero references to it.
|
|
|
|
--*/
|
|
{
|
|
UINT Refs;
|
|
ENTER("RmDeallocateObject", 0xa87fdf4a)
|
|
TR_INFO(("0x%p (%s)\n", pObject, pObject->szDescription));
|
|
|
|
RMPRIVATELOCK(pObject, pSR);
|
|
|
|
RETAILASSERTEX(RMISALLOCATED(pObject), pObject);
|
|
|
|
|
|
// Set state to deallocated.
|
|
//
|
|
SET_RM_STATE(pObject, RMOBJSTATE_ALLOCMASK, RMOBJSTATE_DEALLOCATED);
|
|
|
|
RMPRIVATEUNLOCK(pObject, pSR);
|
|
|
|
// Remove the ref explicitly added in RmInitializeAllocateObject.
|
|
// rmDerefObject will remove the link to the parent, if any, if the
|
|
// ref count drop to 1.
|
|
//
|
|
rmDerefObject(pObject, pSR);
|
|
|
|
EXIT()
|
|
}
|
|
|
|
|
|
VOID
|
|
RmInitializeLock(
|
|
IN PRM_LOCK pLock,
|
|
IN UINT Level
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize a lock.
|
|
|
|
Arguments:
|
|
|
|
pLock - Unitialized memory to hold a struct of type RM_LOCK.
|
|
Level - Level to be associated with this lock. Locks must be acquired
|
|
in strictly increasing order of the locks' "Level" values.
|
|
--*/
|
|
{
|
|
ASSERT(Level > 0);
|
|
NdisAllocateSpinLock(&pLock->OsLock);
|
|
pLock->Level = Level;
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
pLock->pDbgInfo = &pLock->DbgInfo;
|
|
NdisZeroMemory(&pLock->DbgInfo, sizeof(pLock->DbgInfo));
|
|
#endif // RM_EXTRA_CHECKING
|
|
}
|
|
|
|
|
|
VOID
|
|
RmDoWriteLock(
|
|
PRM_LOCK pLock,
|
|
PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Acquire (write lock) lock pLock.
|
|
|
|
--*/
|
|
{
|
|
rmLock(
|
|
pLock,
|
|
#if RM_EXTRA_CHECKING
|
|
0x16323980, // uLocID,
|
|
NULL,
|
|
NULL,
|
|
#endif //RM_EXTRA_CHECKING
|
|
pSR
|
|
);
|
|
}
|
|
|
|
|
|
VOID
|
|
RmDoUnlock(
|
|
PRM_LOCK pLock,
|
|
PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unlock lock pLock.
|
|
|
|
--*/
|
|
{
|
|
rmUnlock(
|
|
pLock,
|
|
pSR
|
|
);
|
|
}
|
|
|
|
#if TODO // Currently RmReadLockObject is a macro defined to be RmWriteLockObject.
|
|
// TODO: Verifier need to to also make sure that object hasn't changed state
|
|
// *while* the object has been read-locked.
|
|
VOID
|
|
RmReadLockObject(
|
|
IN PRM_OBJECT_HEADER pObj,
|
|
#if RM_EXTRA_CHECKING
|
|
UINT uLocID,
|
|
#endif //RM_EXTRA_CHECKING
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Acquire (read lock) lock pLock.
|
|
|
|
--*/
|
|
{
|
|
ASSERT(!"Unimplemented");
|
|
}
|
|
#endif //TODO
|
|
|
|
|
|
VOID
|
|
RmWriteLockObject(
|
|
IN PRM_OBJECT_HEADER pObj,
|
|
#if RM_EXTRA_CHECKING
|
|
UINT uLocID,
|
|
#endif //RM_EXTRA_CHECKING
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Acquire (write lock) the lock associated with object pObj.
|
|
|
|
Arguments:
|
|
|
|
pObj -- Object whose lock to acquire.
|
|
uLocID -- Arbitrary UINT identifying static location from which this
|
|
call is made.
|
|
|
|
--*/
|
|
{
|
|
ENTER("RmWriteLockObject", 0x590ed543)
|
|
TR_VERB(("Locking 0x%p (%s)\n", pObj, pObj->szDescription));
|
|
|
|
rmLock(
|
|
pObj->pLock,
|
|
#if RM_EXTRA_CHECKING
|
|
uLocID,
|
|
// pObj->pStaticInfo->pfnLockVerifier,
|
|
rmVerifyObjectState,
|
|
pObj,
|
|
#endif //RM_EXTRA_CHECKING
|
|
pSR
|
|
);
|
|
EXIT()
|
|
}
|
|
|
|
|
|
VOID
|
|
RmUnlockObject(
|
|
IN PRM_OBJECT_HEADER pObj,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Release the lock associated with object pObj.
|
|
|
|
--*/
|
|
{
|
|
ENTER("RmUnLockObject", 0x0307dd84)
|
|
TR_VERB(("Unlocking 0x%p (%s)\n", pObj, pObj->szDescription));
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
//
|
|
// Make sure that pObject is the object that is *supposed* to be freed.
|
|
//
|
|
ASSERT(pSR->LockInfo.pNextFree[-1].pVerifierContext == (PVOID) pObj);
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
rmUnlock(
|
|
pObj->pLock,
|
|
pSR
|
|
);
|
|
|
|
EXIT()
|
|
}
|
|
|
|
|
|
VOID
|
|
RmUnlockAll(
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unlocks all currently held locks as recorded in pSR.
|
|
If the locks are associated with objects, RmUnlockObject is called for
|
|
each of the held locks. Otherwise the raw unlock is performed.
|
|
|
|
--*/
|
|
{
|
|
ENTER("RmUnLockObject", 0x9878be96)
|
|
TR_VERB(("Unlocking all\n"));
|
|
|
|
while (pSR->LockInfo.CurrentLevel != 0)
|
|
{
|
|
rmUnlock(
|
|
pSR->LockInfo.pNextFree[-1].pLock,
|
|
pSR
|
|
);
|
|
}
|
|
|
|
EXIT()
|
|
}
|
|
|
|
|
|
VOID
|
|
RmDbgChangeLockScope(
|
|
IN PRM_OBJECT_HEADER pPreviouslyLockedObject,
|
|
IN PRM_OBJECT_HEADER pObject,
|
|
IN ULONG LocID,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
(Debug only)
|
|
|
|
Munge things so that the following sequence works:
|
|
Rm[Read|Write]LockObject(pPreviouslyLockedObject, pSR);
|
|
RmChangeLockScope(pPreviouslyLockedObject, pSR);
|
|
RmUnlockObject(pObject, pSR);
|
|
|
|
Of course, we require that the two objects have the same lock!
|
|
|
|
NOTE: We only support changing scope of the MOST RECENTLY
|
|
acquired lock.
|
|
|
|
|
|
Arguments:
|
|
|
|
pPreviouslyLockedObject - Currently locked object.
|
|
pObject - Object to transfer lock scope to.
|
|
LocID - Arbitrary UINT identifying static location from
|
|
which this call is made.
|
|
--*/
|
|
{
|
|
//
|
|
// This is a NOOP unless extra-checking is enabled.
|
|
// TODO: make this inline in the fre build.
|
|
//
|
|
#if RM_EXTRA_CHECKING
|
|
RM_LOCKING_INFO * pLI = pSR->LockInfo.pNextFree-1;
|
|
PRM_LOCK pLock = pPreviouslyLockedObject->pLock;
|
|
ASSERT(
|
|
pLock->Level == pSR->LockInfo.CurrentLevel
|
|
&& pLock == pObject->pLock
|
|
&& pLock == pLI->pLock
|
|
&& pLI->pVerifierContext == (PVOID) pPreviouslyLockedObject);
|
|
|
|
ASSERT(pLI->pfnVerifier == rmVerifyObjectState);
|
|
|
|
rmVerifyObjectState(pLock, FALSE, pLI->pVerifierContext, pSR);
|
|
pLI->pVerifierContext = pObject;
|
|
pLock->DbgInfo.uLocID = LocID;
|
|
rmVerifyObjectState(pLock, TRUE, pLI->pVerifierContext, pSR);
|
|
|
|
#endif // RM_EXTRA_CHECING
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
RmLinkObjects(
|
|
IN PRM_OBJECT_HEADER pObj1,
|
|
IN PRM_OBJECT_HEADER pObj2,
|
|
IN PRM_STACK_RECORD pSr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Link object pObj1 to object pObj2. Basically, this function refs both
|
|
objects.
|
|
|
|
OK to call with some locks held, including RmPrivateLock.
|
|
TODO: remove arp pSr above -- we don't need it.
|
|
--*/
|
|
{
|
|
ENTER("RmLinkObjects", 0xfe2832dd)
|
|
|
|
// Maybe we're being too harsh here -- if required, remove this...
|
|
// This could happen where a task is linked at the point where the object
|
|
// is being deallocated, so I'm changing the following to debug asserts
|
|
// (used to be retail asserts).
|
|
//
|
|
ASSERT(RMISALLOCATED(pObj1));
|
|
ASSERT(RMISALLOCATED(pObj2));
|
|
|
|
TR_INFO(("0x%p (%s) linked to 0x%p (%s)\n",
|
|
pObj1,
|
|
pObj1->szDescription,
|
|
pObj2,
|
|
pObj2->szDescription
|
|
));
|
|
|
|
NdisInterlockedIncrement(&pObj1->TotRefs);
|
|
NdisInterlockedIncrement(&pObj2->TotRefs);
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
RmLinkObjectsEx(
|
|
IN PRM_OBJECT_HEADER pObj1,
|
|
IN PRM_OBJECT_HEADER pObj2,
|
|
IN ULONG LocID,
|
|
IN ULONG AssocID,
|
|
IN const char * szAssociationFormat,
|
|
IN ULONG InvAssocID,
|
|
IN const char * szInvAssociationFormat,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Same as RmLinkObjects, execpt that (debug only) it also sets up association
|
|
(pObj2, pObj2->szDescription, AssocID)
|
|
on pObj1, and association
|
|
(pObj1, pObj1->szDescription, InvAssocID)
|
|
on object pObj2
|
|
|
|
Arguments:
|
|
|
|
pObj1 - Object whose lock to acquire.
|
|
pObj2 - Object whose lock to acquire.
|
|
LocID - Arbitrary UINT identifying static location from which
|
|
this call is made.
|
|
AssocID - ID of the association (see RmDbgAddAssociation) that
|
|
represents the link from pObj1 to pObj2.
|
|
szAssociationFormat - Format of the association (see RmDbgAddAssociation)
|
|
InvAssocId - ID of the inverse association (i.e., represents
|
|
link from pObj2 to pObj1).
|
|
szInvAssociationFormat - Format of the inverse association.
|
|
|
|
--*/
|
|
{
|
|
ENTER("RmLinkObjectsEx", 0xef50263b)
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
|
|
RmDbgAddAssociation(
|
|
LocID, // Location ID
|
|
pObj1, // pObject
|
|
(UINT_PTR) pObj2, // Instance1
|
|
(UINT_PTR) (pObj2->szDescription), // Instance2
|
|
AssocID, // AssociationID
|
|
szAssociationFormat,
|
|
pSR
|
|
);
|
|
|
|
RmDbgAddAssociation(
|
|
LocID, // Location ID
|
|
pObj2, // pObject
|
|
(UINT_PTR) pObj1, // Instance1
|
|
(UINT_PTR) (pObj1->szDescription), // Instance2
|
|
InvAssocID, // AssociationID
|
|
szInvAssociationFormat,
|
|
pSR
|
|
);
|
|
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
RmLinkObjects(
|
|
pObj1,
|
|
pObj2,
|
|
pSR
|
|
);
|
|
}
|
|
|
|
|
|
VOID
|
|
RmUnlinkObjects(
|
|
IN PRM_OBJECT_HEADER pObj1,
|
|
IN PRM_OBJECT_HEADER pObj2,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unlink objects pObj1 and pObj2 (i.e., undo the effect of
|
|
RmLinkObjects(pObj1, pObj2, pSR)).
|
|
|
|
--*/
|
|
{
|
|
ENTER("RmUnlinkObjects", 0x7c64356a)
|
|
TR_INFO(("0x%p (%s) unlinked from 0x%p (%s)\n",
|
|
pObj1,
|
|
pObj1->szDescription,
|
|
pObj2,
|
|
pObj2->szDescription
|
|
));
|
|
#if RM_EXTRA_CHECKING
|
|
//
|
|
// TODO: remove explict link
|
|
//
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
// Remove link refs.
|
|
//
|
|
rmDerefObject(pObj1, pSR);
|
|
rmDerefObject(pObj2, pSR);
|
|
}
|
|
|
|
|
|
VOID
|
|
RmUnlinkObjectsEx(
|
|
IN PRM_OBJECT_HEADER pObj1,
|
|
IN PRM_OBJECT_HEADER pObj2,
|
|
IN ULONG LocID,
|
|
IN ULONG AssocID,
|
|
IN ULONG InvAssocID,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Same as RmUnlinkObjects, execpt that it also removes the association
|
|
(pObj2, pObj2->szDescription, AssocID)
|
|
on pObj1, and association
|
|
(pObj1, pObj1->szDescription, InvAssocID)
|
|
object pObj2
|
|
|
|
Arguments:
|
|
|
|
See RmLinkObjectsEx.
|
|
|
|
--*/
|
|
{
|
|
ENTER("RmUnlinkObjectsEx", 0x65d3536c)
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
|
|
RmDbgDeleteAssociation(
|
|
LocID, // Location ID
|
|
pObj1, // pObject
|
|
(UINT_PTR) pObj2, // Instance1
|
|
(UINT_PTR) (pObj2->szDescription), // Instance2
|
|
AssocID, // AssociationID
|
|
pSR
|
|
);
|
|
|
|
RmDbgDeleteAssociation(
|
|
LocID, // Location ID
|
|
pObj2, // pObject
|
|
(UINT_PTR) pObj1, // Instance1
|
|
(UINT_PTR) (pObj1->szDescription), // Instance2
|
|
InvAssocID, // AssociationID
|
|
pSR
|
|
);
|
|
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
RmUnlinkObjects(
|
|
pObj1,
|
|
pObj2,
|
|
pSR
|
|
);
|
|
}
|
|
|
|
|
|
VOID
|
|
RmLinkToExternalEx(
|
|
IN PRM_OBJECT_HEADER pObj,
|
|
IN ULONG LocID,
|
|
IN UINT_PTR ExternalEntity,
|
|
IN ULONG AssocID,
|
|
IN const char * szAssociationFormat,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Links object pObj to external entity ExternalEntity. Basically, this function
|
|
adds a reference to pObj. In addition (debug only) this function sets up
|
|
an association on pObj that links the external entity to pObj. pObj can be
|
|
linked to ExternalEntity with the specified association ID AssocID only ONCE
|
|
at any particular point of time.
|
|
|
|
Once this link is setup, an attempt to deallocate pObj without removing the
|
|
link results in an assertion failure.
|
|
|
|
RmUnlinkFromExternalEx is the inverse function.
|
|
|
|
Arguments:
|
|
|
|
pObj - Object to be linked to an external entity.
|
|
|
|
(following are for debug only...)
|
|
|
|
LocID - Arbitrary UINT identifying static location from
|
|
which this call is made.
|
|
ExternalEntity - Opaque value representing the external entity.
|
|
AssocID - Association ID representing the linkage.
|
|
szAssociationFormat - Association format for the linkage.
|
|
|
|
--*/
|
|
{
|
|
ENTER("RmLinkToExternalEx", 0x9aeaca74)
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
|
|
RmDbgAddAssociation(
|
|
LocID, // Location ID
|
|
pObj, // pObject
|
|
(UINT_PTR) ExternalEntity, // Instance1
|
|
(UINT_PTR) 0, // Instance2 (unused)
|
|
AssocID, // AssociationID
|
|
szAssociationFormat,
|
|
pSR
|
|
);
|
|
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
RmLinkToExternalFast(pObj);
|
|
|
|
EXIT()
|
|
}
|
|
|
|
|
|
VOID
|
|
RmUnlinkFromExternalEx(
|
|
IN PRM_OBJECT_HEADER pObj,
|
|
IN ULONG LocID,
|
|
IN UINT_PTR ExternalEntity,
|
|
IN ULONG AssocID,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Inverse of RmUnlinkFromExternalEx -- removes the link set up between
|
|
pObj and ExternalEntity.
|
|
|
|
Arguments:
|
|
|
|
See RmLinkToExternalEx.
|
|
|
|
--*/
|
|
{
|
|
ENTER("RmUnlinkFromExternalEx", 0x9fb084c3)
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
|
|
RmDbgDeleteAssociation(
|
|
LocID, // Location ID
|
|
pObj, // pObject
|
|
(UINT_PTR) ExternalEntity, // Instance1
|
|
(UINT_PTR) 0, // Instance2 (unused)
|
|
AssocID, // AssociationID
|
|
pSR
|
|
);
|
|
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
RmUnlinkFromExternalFast(pObj);
|
|
|
|
EXIT()
|
|
}
|
|
|
|
|
|
VOID
|
|
RmLinkToExternalFast( // TODO make inline
|
|
IN PRM_OBJECT_HEADER pObj
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Fast version of RmLinkToExternalEx -- same behavior in retail. No associations
|
|
are setup.
|
|
|
|
Arguments:
|
|
|
|
See RmLinkToExternalEx.
|
|
|
|
--*/
|
|
{
|
|
NdisInterlockedIncrement(&pObj->TotRefs);
|
|
}
|
|
|
|
|
|
VOID
|
|
RmUnlinkFromExternalFast( // TODO make inline
|
|
IN PRM_OBJECT_HEADER pObj
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Inverse of RmUnlinkFromExternalFast -- removes the link set up between
|
|
pObj and ExternalEntity.
|
|
|
|
TODO -- we need a fast implementation for the case that the object is
|
|
not going to go away. For now we actually declare a stack record here each
|
|
time, becaues rmDerefObject wants one! Bad bad.
|
|
|
|
Arguments:
|
|
|
|
See RmLinkToExternalFast.
|
|
|
|
--*/
|
|
{
|
|
RM_DECLARE_STACK_RECORD(sr)
|
|
rmDerefObject(pObj, &sr);
|
|
}
|
|
|
|
|
|
VOID
|
|
RmTmpReferenceObject(
|
|
IN PRM_OBJECT_HEADER pObj,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add a temporary reference to object pObj.
|
|
(Debug only) Increments the count of tmprefs maintained in pSR.
|
|
|
|
--*/
|
|
{
|
|
ENTER("RmTmpReferenceObject", 0xdd981024)
|
|
TR_VERB(("0x%p (%s)\n", pObj, pObj->szDescription));
|
|
|
|
ASSERT(RM_OBJECT_IS_ALLOCATED(pObj));
|
|
|
|
pSR->TmpRefs++;
|
|
|
|
NdisInterlockedIncrement(&pObj->TotRefs);
|
|
}
|
|
|
|
|
|
VOID
|
|
RmTmpDereferenceObject(
|
|
IN PRM_OBJECT_HEADER pObj,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove a temporary reference to object pObj.
|
|
(Debug only) Decrements the count of tmprefs maintained in pSR.
|
|
|
|
--*/
|
|
{
|
|
ENTER("RmTmpDereferenceObject", 0xd1630c11)
|
|
TR_VERB(("0x%p (%s)\n", pObj, pObj->szDescription));
|
|
|
|
RETAILASSERTEX(pSR->TmpRefs>0, pSR);
|
|
pSR->TmpRefs--;
|
|
|
|
rmDerefObject(pObj, pSR);
|
|
}
|
|
|
|
|
|
VOID
|
|
RmDbgAddAssociation(
|
|
IN ULONG LocID,
|
|
IN PRM_OBJECT_HEADER pObject,
|
|
IN ULONG_PTR Entity1,
|
|
IN ULONG_PTR Entity2,
|
|
IN ULONG AssociationID,
|
|
IN const char * szFormatString, OPTIONAL
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
|
|
Routine Description:
|
|
|
|
Add an arbitrary association, for debugging purposes, under
|
|
object pObject. The association is defined by the triple
|
|
(Entity1, Entity2, AssociationID) -- only ONE such tuple may
|
|
be registered at any time with object pParentObject.
|
|
Note: It is valid for both of the following to be registered at the same
|
|
time: (a, b, 1) and (a, b, 2)
|
|
|
|
No association should exist at the point the object is deleted.
|
|
|
|
Arguments:
|
|
|
|
LocID - Arbitrary ID, typically representing the source location
|
|
- from which this function is called.
|
|
pObject - Object to add the association.
|
|
Entity1 - The 1st entity making up the association. May be NULL.
|
|
Entity2 - The 2nd entity making up the association. May be NULL.
|
|
AssociationID - ID defining the association.
|
|
NOTE: AssociationID must not have the high-bit set.
|
|
Associations with the high bit set are reserved for
|
|
internal use of the Rm API implementation.
|
|
|
|
--*/
|
|
{
|
|
#if RM_EXTRA_CHECKING
|
|
PRM_OBJECT_DIAGNOSTIC_INFO pDiagInfo = pObject->pDiagInfo;
|
|
ENTER("RmDbgAddAssociation", 0x512192eb)
|
|
|
|
if (pDiagInfo)
|
|
{
|
|
|
|
//
|
|
// Allocate an association and enter it into the hash table.
|
|
// Assert if it already exists.
|
|
//
|
|
|
|
RM_PRIVATE_DBG_ASSOCIATION *pA;
|
|
RM_ALLOCSTRUCT(pA, MTAG_DBGINFO); // TODO use lookaside lists.
|
|
|
|
if (pA == NULL)
|
|
{
|
|
//
|
|
// Allocation failed. Record this fact, so that
|
|
// RmDbgDeleteAssociation doesn't assert
|
|
// if an attempt is made to remove an assertion which doesn't exist.
|
|
//
|
|
NdisAcquireSpinLock(&pDiagInfo->OsLock);
|
|
pDiagInfo->AssociationTableAllocationFailure = TRUE;
|
|
NdisReleaseSpinLock(&pDiagInfo->OsLock);
|
|
}
|
|
else
|
|
{
|
|
BOOLEAN fFound;
|
|
PRM_HASH_LINK *ppLink;
|
|
RM_ZEROSTRUCT(pA);
|
|
|
|
pA->Entity1 = Entity1;
|
|
pA->Entity2 = Entity2;
|
|
pA->AssociationID = AssociationID;
|
|
|
|
if (szFormatString == NULL)
|
|
{
|
|
// Put in the default description format string.
|
|
//
|
|
szFormatString = " Association (E1=0x%x, E2=0x%x, T=0x%x)\n";
|
|
}
|
|
|
|
TR_VERB((" Obj:0x%p (%s)...\n", pObject, pObject->szDescription));
|
|
TRACE0(TL_INFO,((char*)szFormatString, Entity1, Entity2, AssociationID));
|
|
|
|
pA->szFormatString = szFormatString;
|
|
|
|
NdisAcquireSpinLock(&pDiagInfo->OsLock);
|
|
|
|
fFound = RmLookupHashTable(
|
|
&pDiagInfo->AssociationTable,
|
|
&ppLink,
|
|
pA // We use pA as the key.
|
|
);
|
|
|
|
if (fFound)
|
|
{
|
|
ASSERTEX(
|
|
!"Association already exists:",
|
|
CONTAINING_RECORD(*ppLink, RM_PRIVATE_DBG_ASSOCIATION, HashLink)
|
|
);
|
|
RM_FREE(pA);
|
|
pA = NULL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Enter the association into the hash table.
|
|
//
|
|
|
|
RmAddHashItem(
|
|
&pDiagInfo->AssociationTable,
|
|
ppLink,
|
|
&pA->HashLink,
|
|
pA // We use pA as the key
|
|
);
|
|
}
|
|
NdisReleaseSpinLock(&pDiagInfo->OsLock);
|
|
|
|
// Now, just for grins, make a note of this in the object's log.
|
|
// WARNING: Although pEntity1/2 may contain pointers,
|
|
// we expect the the format string is such that if there are any
|
|
// references to regular or unicode strings, those strings will
|
|
// be valid for the life of the object (typically these strings
|
|
// are statically-allocated strings).
|
|
//
|
|
// We COULD use the more conservative format string to display the
|
|
// log entry, but it's useful to have the information displayed
|
|
// properly.
|
|
//
|
|
// Note-- we could also do different things depending on the type
|
|
// of association.
|
|
//
|
|
#if 0 // conservative format
|
|
RmDbgLogToObject(
|
|
pObject,
|
|
" Add association (E1=0x%x, E2=0x%x, T=0x%x)\n",
|
|
Entity1,
|
|
Entity2,
|
|
AssociationID,
|
|
0, // Param4 // (UINT_PTR) szFormatString,
|
|
NULL,
|
|
NULL
|
|
);
|
|
#else // aggresive format
|
|
{
|
|
#define szADDASSOC " Add assoc:"
|
|
|
|
#if OBSOLETE // This doesn't work because rgMungedFormat is on the stack!
|
|
char rgMungedFormat[128];
|
|
UINT uLength;
|
|
rgMungedFormat[0]=0;
|
|
rmSafeAppend(rgMungedFormat, szADDASSOC, sizeof(rgMungedFormat));
|
|
uLength = rmSafeAppend(
|
|
rgMungedFormat,
|
|
szFormatString,
|
|
sizeof(rgMungedFormat)
|
|
);
|
|
if (uLength && rgMungedFormat[uLength-1] != '\n')
|
|
{
|
|
rgMungedFormat[uLength-1] = '\n';
|
|
}
|
|
RmDbgLogToObject(
|
|
pObject,
|
|
rgMungedFormat,
|
|
Entity1,
|
|
Entity2,
|
|
AssociationID,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
#endif // OBSOLETE
|
|
|
|
RmDbgLogToObject(
|
|
pObject,
|
|
szADDASSOC,
|
|
(char*)szFormatString,
|
|
Entity1,
|
|
Entity2,
|
|
AssociationID,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
#endif // aggressive format
|
|
}
|
|
}
|
|
|
|
EXIT()
|
|
#endif // RM_EXTRA_CHECKING
|
|
}
|
|
|
|
|
|
VOID
|
|
RmDbgDeleteAssociation(
|
|
IN ULONG LocID,
|
|
IN PRM_OBJECT_HEADER pObject,
|
|
IN ULONG_PTR Entity1,
|
|
IN ULONG_PTR Entity2,
|
|
IN ULONG AssociationID,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes the previously-added association (Entity1, Entity2, Association)
|
|
from object pObject. See the description of RmDbgAddAssociation for
|
|
details.
|
|
|
|
Arguments:
|
|
|
|
See RmDbgAddAssociation.
|
|
|
|
--*/
|
|
{
|
|
#if RM_EXTRA_CHECKING
|
|
ENTER("RmDbgDelAssociation", 0x8354559f)
|
|
PRM_OBJECT_DIAGNOSTIC_INFO pDiagInfo = pObject->pDiagInfo;
|
|
|
|
if (pDiagInfo)
|
|
{
|
|
BOOLEAN fFound;
|
|
PRM_HASH_LINK *ppLink;
|
|
RM_PRIVATE_DBG_ASSOCIATION TrueKey;
|
|
|
|
// Only the following 3 fields of TrueKey make up the key
|
|
//
|
|
TrueKey.Entity1 = Entity1;
|
|
TrueKey.Entity2 = Entity2;
|
|
TrueKey.AssociationID = AssociationID;
|
|
|
|
NdisAcquireSpinLock(&pDiagInfo->OsLock);
|
|
|
|
fFound = RmLookupHashTable(
|
|
&pDiagInfo->AssociationTable,
|
|
&ppLink,
|
|
&TrueKey
|
|
);
|
|
|
|
if (fFound)
|
|
{
|
|
RM_PRIVATE_DBG_ASSOCIATION *pA =
|
|
CONTAINING_RECORD(*ppLink, RM_PRIVATE_DBG_ASSOCIATION, HashLink);
|
|
|
|
TR_VERB((" Obj:0x%p (%s)...\n", pObject, pObject->szDescription));
|
|
TRACE0(TL_INFO,
|
|
((char*)pA->szFormatString,
|
|
pA->Entity1,
|
|
pA->Entity2,
|
|
pA->AssociationID));
|
|
|
|
//
|
|
// Now, just for grins, make a note of this in the oject's log.
|
|
// Note that of pEntity1/2 contain pointers, we can't expect them
|
|
// to be valid for as long as the object is alive, so we use
|
|
// the more conservative format string to display the log entry.
|
|
//
|
|
// TODO/BUGUG -- see comments under RmDbgAddAssociation
|
|
// about the risk of directly passing szFormat
|
|
//
|
|
|
|
#if 0 // conservative
|
|
RmDbgLogToObject(
|
|
pObject,
|
|
NULL,
|
|
" Deleted Association (E1=0x%x, E2=0x%x, T=0x%x)\n",
|
|
pA->Entity1,
|
|
pA->Entity2,
|
|
pA->AssociationID,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
#else // aggressive
|
|
#define szDELASSOC " Del assoc:"
|
|
RmDbgLogToObject(
|
|
pObject,
|
|
szDELASSOC,
|
|
(char*) pA->szFormatString,
|
|
pA->Entity1,
|
|
pA->Entity2,
|
|
pA->AssociationID,
|
|
0, // Param4 // (UINT_PTR) szFormatString,
|
|
NULL,
|
|
NULL
|
|
);
|
|
#endif // aggressive
|
|
|
|
//
|
|
// Remove the association and free it.
|
|
//
|
|
|
|
RM_PRIVATE_UNLINK_NEXT_HASH( &pDiagInfo->AssociationTable, ppLink );
|
|
|
|
RM_FREE(pA);
|
|
}
|
|
else
|
|
{
|
|
if (!pDiagInfo->AssociationTableAllocationFailure)
|
|
{
|
|
ASSERT(!"Association doesn't exist");
|
|
}
|
|
}
|
|
NdisReleaseSpinLock(&pDiagInfo->OsLock);
|
|
|
|
|
|
}
|
|
EXIT()
|
|
#endif // RM_EXTRA_CHECKING
|
|
}
|
|
|
|
|
|
VOID
|
|
RmDbgPrintAssociations(
|
|
PRM_OBJECT_HEADER pObject,
|
|
PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
(Debug only) Dumps the associations on object pObject.
|
|
|
|
--*/
|
|
{
|
|
#if RM_EXTRA_CHECKING
|
|
ENTER("RmPrintAssociations", 0x8354559f)
|
|
PRM_OBJECT_DIAGNOSTIC_INFO pDiagInfo = pObject->pDiagInfo;
|
|
|
|
if (pDiagInfo)
|
|
{
|
|
TR_INFO((
|
|
"Obj 0x%p (%s):\n",
|
|
pObject,
|
|
pObject->szDescription
|
|
));
|
|
|
|
NdisAcquireSpinLock(&pDiagInfo->OsLock);
|
|
|
|
RmEnumHashTable(
|
|
&pDiagInfo->AssociationTable,
|
|
rmDbgPrintOneAssociation, // pfnEnumerator
|
|
pObject, // context
|
|
pSR
|
|
);
|
|
|
|
NdisReleaseSpinLock(&pDiagInfo->OsLock);
|
|
}
|
|
EXIT()
|
|
#endif // RM_EXTRA_CHECKING
|
|
}
|
|
|
|
|
|
//
|
|
// Diagnostic per-object logging.
|
|
//
|
|
|
|
VOID
|
|
RmDbgLogToObject(
|
|
IN PRM_OBJECT_HEADER pObject,
|
|
IN char * szPrefix, OPTIONAL
|
|
IN char * szFormatString,
|
|
IN UINT_PTR Param1,
|
|
IN UINT_PTR Param2,
|
|
IN UINT_PTR Param3,
|
|
IN UINT_PTR Param4,
|
|
IN PFN_DBG_DUMP_LOG_ENTRY pfnDumpEntry, OPTIONAL
|
|
IN PVOID pvBuf OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Make one log entry in pObject's log.
|
|
|
|
TODO: See notes.txt entry "03/07/1999 ... Registering root objects with RM"
|
|
on how we will find the deallocator function fo pvBuf. For now we simply
|
|
use NdisFreeMemory.
|
|
|
|
TODO: need to implement trimming of log when we reach a maximum. Currently we
|
|
just stop logging.
|
|
|
|
Arguments:
|
|
pfnDumpEntry - Function to be used for dumping the log.
|
|
If NULL, a default function is used, which interprets
|
|
szFormatString as the standard printf format string.
|
|
|
|
szFormatString - Format string for log display -- 1st arg to pfnDumpEntry
|
|
|
|
Param1-4 - Remaining args to pfnDumpEntry;
|
|
|
|
pvBuf - If non-NULL, piece of memory to be freed when the log entry
|
|
is freed.
|
|
|
|
NOTE: If Param1-4 contain pointers, the memory they refer to is assumed
|
|
to be valid for as long as the object is alive. If the entities being logged
|
|
may go away before the object is deallocated, the caller should
|
|
allocate a buffer to hold a copy of the entities, and pass the pointer to
|
|
that buffer as pvBuf.
|
|
|
|
--*/
|
|
{
|
|
#if RM_EXTRA_CHECKING
|
|
ENTER("RmDbgLogToObject", 0x2b2015b5)
|
|
|
|
PRM_OBJECT_DIAGNOSTIC_INFO pDiagInfo = pObject->pDiagInfo;
|
|
|
|
if (pDiagInfo && RmGlobals.NumGlobalLogEntries < 4000)
|
|
{
|
|
RM_DBG_LOG_ENTRY *pLogEntry;
|
|
|
|
NdisAcquireSpinLock(&RmGlobals.GlobalOsLock);
|
|
|
|
pLogEntry = rmDbgAllocateLogEntry();
|
|
|
|
if (pLogEntry != NULL)
|
|
{
|
|
if (pfnDumpEntry == NULL)
|
|
{
|
|
pfnDumpEntry = rmDefaultDumpEntry;
|
|
}
|
|
|
|
pLogEntry->pObject = pObject;
|
|
pLogEntry->pfnDumpEntry = pfnDumpEntry;
|
|
pLogEntry->szPrefix = szPrefix;
|
|
pLogEntry->szFormatString = szFormatString;
|
|
pLogEntry->Param1 = Param1;
|
|
pLogEntry->Param2 = Param2;
|
|
pLogEntry->Param3 = Param3;
|
|
pLogEntry->Param4 = Param4;
|
|
pLogEntry->pvBuf = pvBuf;
|
|
|
|
// Insert item at head of object log.
|
|
//
|
|
InsertHeadList(&pDiagInfo->listObjectLog, &pLogEntry->linkObjectLog);
|
|
|
|
// Insert item at head of global log.
|
|
//
|
|
InsertHeadList(&RmGlobals.listGlobalLog, &pLogEntry->linkGlobalLog);
|
|
|
|
|
|
pDiagInfo->NumObjectLogEntries++;
|
|
RmGlobals.NumGlobalLogEntries++;
|
|
}
|
|
|
|
NdisReleaseSpinLock(&RmGlobals.GlobalOsLock);
|
|
|
|
#if 0
|
|
pfnDumpEntry(
|
|
szFormatString,
|
|
Param1,
|
|
Param2,
|
|
Param3,
|
|
Param4
|
|
);
|
|
#endif // 0
|
|
}
|
|
else
|
|
{
|
|
// TODO -- free pvBuf if NON NULL.
|
|
}
|
|
EXIT()
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
RmDbgPrintObjectLog(
|
|
IN PRM_OBJECT_HEADER pObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
(Debug only) Dumps object pObject's log.
|
|
|
|
--*/
|
|
{
|
|
#if RM_EXTRA_CHECKING
|
|
ENTER("RmPrintObjectLog", 0xe06507e5)
|
|
PRM_OBJECT_DIAGNOSTIC_INFO pDiagInfo = pObject->pDiagInfo;
|
|
|
|
TR_INFO((" pObj=0x%p (%s)\n", pObject, pObject->szDescription));
|
|
|
|
|
|
if (pDiagInfo != NULL)
|
|
{
|
|
LIST_ENTRY *pLink=NULL;
|
|
LIST_ENTRY * pObjectLog = &pDiagInfo->listObjectLog;
|
|
|
|
NdisAcquireSpinLock(&RmGlobals.GlobalOsLock);
|
|
|
|
for(
|
|
pLink = pObjectLog->Flink;
|
|
pLink != pObjectLog;
|
|
pLink = pLink->Flink)
|
|
{
|
|
RM_DBG_LOG_ENTRY *pLE;
|
|
|
|
pLE = CONTAINING_RECORD(pLink, RM_DBG_LOG_ENTRY, linkObjectLog);
|
|
|
|
if (pLE->szPrefix != NULL)
|
|
{
|
|
// Print the prefix.
|
|
DbgPrint(pLE->szPrefix);
|
|
}
|
|
|
|
// Call the dump function for this entry.
|
|
//
|
|
//
|
|
pLE->pfnDumpEntry(
|
|
pLE->szFormatString,
|
|
pLE->Param1,
|
|
pLE->Param2,
|
|
pLE->Param3,
|
|
pLE->Param4
|
|
);
|
|
|
|
}
|
|
NdisReleaseSpinLock(&RmGlobals.GlobalOsLock);
|
|
}
|
|
EXIT()
|
|
|
|
#endif // RM_EXTRA_CHECKING
|
|
}
|
|
|
|
|
|
VOID
|
|
RmDbgPrintGlobalLog(VOID)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
(Debug only) Dumps the global log (which contains entries from all object's
|
|
logs.
|
|
|
|
--*/
|
|
{
|
|
#if RM_EXTRA_CHECKING
|
|
ENTER("RmPrintGlobalLog", 0xe9915066)
|
|
LIST_ENTRY *pLink=NULL;
|
|
LIST_ENTRY *pGlobalLog = &RmGlobals.listGlobalLog;
|
|
|
|
TR_INFO(("Enter\n"));
|
|
|
|
NdisAcquireSpinLock(&RmGlobals.GlobalOsLock);
|
|
|
|
for(
|
|
pLink = pGlobalLog->Flink;
|
|
pLink != pGlobalLog;
|
|
pLink = pLink->Flink)
|
|
{
|
|
RM_DBG_LOG_ENTRY *pLE;
|
|
|
|
pLE = CONTAINING_RECORD(pLink, RM_DBG_LOG_ENTRY, linkGlobalLog);
|
|
|
|
// Print the ptr and name of the object whose entry this is...
|
|
//
|
|
DbgPrint(
|
|
"Entry for 0x%p (%s):\n",
|
|
pLE->pObject,
|
|
pLE->pObject->szDescription
|
|
);
|
|
|
|
if (pLE->szPrefix != NULL)
|
|
{
|
|
// Print the prefix.
|
|
DbgPrint(pLE->szPrefix);
|
|
}
|
|
|
|
// Call the dump function for this entry.
|
|
//
|
|
//
|
|
pLE->pfnDumpEntry(
|
|
pLE->szFormatString,
|
|
pLE->Param1,
|
|
pLE->Param2,
|
|
pLE->Param3,
|
|
pLE->Param4
|
|
);
|
|
|
|
}
|
|
NdisReleaseSpinLock(&RmGlobals.GlobalOsLock);
|
|
|
|
EXIT()
|
|
|
|
#endif // RM_EXTRA_CHECKING
|
|
}
|
|
|
|
|
|
RM_STATUS
|
|
RmLoadGenericResource(
|
|
IN PRM_OBJECT_HEADER pObj,
|
|
IN UINT GenericResourceID,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
TODO This function is going away...
|
|
|
|
--*/
|
|
{
|
|
PRM_STATIC_OBJECT_INFO pSI = pObj->pStaticInfo;
|
|
RM_STATUS Status;
|
|
|
|
// The resource ID should be less than number of bits in the ResourceMap
|
|
//
|
|
ASSERT(GenericResourceID < 8*sizeof(pObj->ResourceMap));
|
|
|
|
|
|
RMPRIVATELOCK(pObj, pSR);
|
|
|
|
do
|
|
{
|
|
UINT ResFlag = 1<<GenericResourceID;
|
|
|
|
if (!RMISALLOCATED(pObj))
|
|
{
|
|
Status = NDIS_STATUS_FAILURE;
|
|
RMPRIVATEUNLOCK(pObj, pSR);
|
|
break;
|
|
}
|
|
|
|
if (pSI->NumResourceTableEntries <= GenericResourceID)
|
|
{
|
|
ASSERTEX(!"Invalid GenericResourceID", pObj);
|
|
Status = NDIS_STATUS_FAILURE;
|
|
RMPRIVATEUNLOCK(pObj, pSR);
|
|
break;
|
|
}
|
|
|
|
// The resource entry indexed must have its ID == GenericResourceID
|
|
//
|
|
//
|
|
if (pSI->pResourceTable[GenericResourceID].ID != GenericResourceID)
|
|
{
|
|
ASSERTEX(!"Resource ID doesn't match table entry", pObj);
|
|
Status = NDIS_STATUS_FAILURE;
|
|
RMPRIVATEUNLOCK(pObj, pSR);
|
|
break;
|
|
}
|
|
|
|
if ( ResFlag & pObj->ResourceMap)
|
|
{
|
|
ASSERTEX(!"Resource already allocated", pObj);
|
|
Status = NDIS_STATUS_FAILURE;
|
|
RMPRIVATEUNLOCK(pObj, pSR);
|
|
break;
|
|
}
|
|
|
|
pObj->ResourceMap |= ResFlag;
|
|
|
|
RMPRIVATEUNLOCK(pObj, pSR);
|
|
|
|
Status = pSI->pResourceTable[GenericResourceID].pfnHandler(
|
|
pObj,
|
|
RM_RESOURCE_OP_LOAD,
|
|
NULL, // pvUserParams (unused)
|
|
pSR
|
|
);
|
|
|
|
if (FAIL(Status))
|
|
{
|
|
// Clear the resource map bit on failure.
|
|
//
|
|
RMPRIVATELOCK(pObj, pSR);
|
|
ASSERTEX(ResFlag & pObj->ResourceMap, pObj);
|
|
pObj->ResourceMap &= ~ResFlag;
|
|
RMPRIVATEUNLOCK(pObj, pSR);
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
RmUnloadGenericResource(
|
|
IN PRM_OBJECT_HEADER pObj,
|
|
IN UINT GenericResourceID,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
TODO This function is going away...
|
|
|
|
--*/
|
|
{
|
|
PRM_STATIC_OBJECT_INFO pSI = pObj->pStaticInfo;
|
|
RM_STATUS Status;
|
|
|
|
// The resource ID should be less than number of bits in the ResourceMap
|
|
//
|
|
ASSERT(GenericResourceID < 8*sizeof(pObj->ResourceMap));
|
|
|
|
RMPRIVATELOCK(pObj, pSR);
|
|
|
|
do
|
|
{
|
|
UINT ResFlag = 1<<GenericResourceID;
|
|
|
|
if (pSI->NumResourceTableEntries <= GenericResourceID)
|
|
{
|
|
ASSERTEX(!"Invalid GenericResourceID", pObj);
|
|
RMPRIVATEUNLOCK(pObj, pSR);
|
|
break;
|
|
}
|
|
|
|
if ( !(ResFlag & pObj->ResourceMap))
|
|
{
|
|
ASSERTEX(!"Resource not allocated", pObj);
|
|
RMPRIVATEUNLOCK(pObj, pSR);
|
|
break;
|
|
}
|
|
|
|
// Clear the resource flag.
|
|
//
|
|
pObj->ResourceMap &= ~ResFlag;
|
|
|
|
RMPRIVATEUNLOCK(pObj, pSR);
|
|
|
|
pSI->pResourceTable[GenericResourceID].pfnHandler(
|
|
pObj,
|
|
RM_RESOURCE_OP_UNLOAD,
|
|
NULL, // pvUserParams (unused)
|
|
pSR
|
|
);
|
|
|
|
} while (FALSE);
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
RmUnloadAllGenericResources(
|
|
IN PRM_OBJECT_HEADER pObj,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
Synchronously unload all previously loaded resources for this object,
|
|
in reverse order to which they were loaded.
|
|
|
|
TODO this function is going away...
|
|
--*/
|
|
{
|
|
PRM_STATIC_OBJECT_INFO pSI = pObj->pStaticInfo;
|
|
RM_STATUS Status;
|
|
UINT u;
|
|
|
|
RMPRIVATELOCK(pObj, pSR);
|
|
|
|
for(u = pSI->NumResourceTableEntries;
|
|
u && pObj->ResourceMap;
|
|
u--)
|
|
{
|
|
UINT ResID = u-1;
|
|
UINT ResFlag = 1<<ResID;
|
|
if ( !(ResFlag & pObj->ResourceMap))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pSI->NumResourceTableEntries <= ResID)
|
|
{
|
|
ASSERTEX(!"Corrupt ResourceMap", pObj);
|
|
RMPRIVATEUNLOCK(pObj, pSR);
|
|
break;
|
|
}
|
|
|
|
|
|
// Clear the resource flag.
|
|
//
|
|
pObj->ResourceMap &= ~ResFlag;
|
|
|
|
RMPRIVATEUNLOCK(pObj, pSR);
|
|
|
|
pSI->pResourceTable[ResID].pfnHandler(
|
|
pObj,
|
|
RM_RESOURCE_OP_UNLOAD,
|
|
NULL, // pvUserParams (unused)
|
|
pSR
|
|
);
|
|
|
|
RMPRIVATELOCK(pObj, pSR);
|
|
|
|
}
|
|
|
|
ASSERTEX(!pObj->ResourceMap, pObj);
|
|
|
|
RMPRIVATEUNLOCK(pObj, pSR);
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
RmInitializeGroup(
|
|
IN PRM_OBJECT_HEADER pOwningObject,
|
|
IN PRM_STATIC_OBJECT_INFO pStaticInfo,
|
|
IN PRM_GROUP pGroup,
|
|
IN const char* szDescription,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize a group structure.
|
|
|
|
Arguments:
|
|
|
|
pOwningObject - Object that will own the group.
|
|
pStaticInfo - Static information about objects IN the group.
|
|
pGroup - Uninitialized memory that is to hold the group structure. It
|
|
will be initialized on return from this function.
|
|
szDescription - (Debug only) descriptive name for this group.
|
|
|
|
TODO: make pStaticInfo const.
|
|
|
|
--*/
|
|
{
|
|
NdisZeroMemory(pGroup, sizeof(*pGroup));
|
|
|
|
RMPRIVATELOCK(pOwningObject, pSR);
|
|
|
|
do
|
|
{
|
|
if (!RMISALLOCATED(pOwningObject))
|
|
{
|
|
ASSERT(!"pObject not allocated");
|
|
break;
|
|
}
|
|
|
|
if (pStaticInfo->pHashInfo == NULL)
|
|
{
|
|
ASSERT(!"NULL pHashInfo");
|
|
// Static info MUST have non-NULL pHashInfo in order
|
|
// for it to be used for groups.
|
|
//
|
|
break;
|
|
}
|
|
|
|
|
|
RmInitializeHashTable(
|
|
pStaticInfo->pHashInfo,
|
|
pOwningObject, // pAllocationContext
|
|
&pGroup->HashTable
|
|
);
|
|
|
|
|
|
pGroup->pOwningObject = pOwningObject;
|
|
pGroup->pStaticInfo = pStaticInfo;
|
|
pGroup->szDescription = szDescription;
|
|
|
|
NdisAllocateSpinLock(&pGroup->OsLock);
|
|
pGroup->fEnabled = TRUE;
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
RmDbgAddAssociation(
|
|
0xc0e5362f, // Location ID
|
|
pOwningObject, // pObject
|
|
(UINT_PTR) pGroup, // Instance1
|
|
(UINT_PTR) (pGroup->szDescription), // Instance2
|
|
RM_PRIVATE_ASSOC_INITGROUP, // AssociationID
|
|
szASSOCFORMAT_INITGROUP, // szAssociationFormat
|
|
pSR
|
|
);
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
} while (FALSE);
|
|
|
|
RMPRIVATEUNLOCK(pOwningObject, pSR);
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
RmDeinitializeGroup(
|
|
IN PRM_GROUP pGroup,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deinitialize group structure pGroup. Must only be called when there are no
|
|
members in the group.
|
|
|
|
--*/
|
|
{
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
RmDbgDeleteAssociation(
|
|
0x1486def9, // Location ID
|
|
pGroup->pOwningObject, // pObject
|
|
(UINT_PTR) pGroup, // Instance1
|
|
(UINT_PTR) (pGroup->szDescription), // Instance2
|
|
RM_PRIVATE_ASSOC_INITGROUP, // AssociationID
|
|
pSR
|
|
);
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
NdisAcquireSpinLock(&pGroup->OsLock);
|
|
|
|
RmDeinitializeHashTable(&pGroup->HashTable);
|
|
|
|
NdisReleaseSpinLock(&pGroup->OsLock);
|
|
NdisFreeSpinLock(&pGroup->OsLock);
|
|
NdisZeroMemory(pGroup, sizeof(*pGroup));
|
|
|
|
}
|
|
|
|
|
|
RM_STATUS
|
|
RmLookupObjectInGroup(
|
|
IN PRM_GROUP pGroup,
|
|
IN ULONG Flags, // create, remove, lock
|
|
IN PVOID pvKey,
|
|
IN PVOID pvCreateParams,
|
|
OUT PRM_OBJECT_HEADER * ppObject,
|
|
OUT INT * pfCreated, OPTIONAL
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
TODO: split this into a pure lookup and a lookupand/orcreate function..
|
|
|
|
Lookup and/or create an object in the specified group.
|
|
|
|
|
|
#if OBSOLETE // Must allow fCreate w/o locking -- see notes.txt entry:
|
|
// 03/04/1999 JosephJ Problems with deadlock when using Groups.
|
|
MUST ONLY be NON-NULL if the fLOCKED flag is specified.
|
|
Why? Because if the lock is not held on exit, it would be possible
|
|
for someone else to pick up the object in the freshly-created state.
|
|
We want to discourage that situation.
|
|
#endif // OBSOLETE
|
|
|
|
Typically the caller specifes the
|
|
fRM_LOCKED|fRM_CREATE flags as well as non-null pfCreated. On return, if
|
|
*pfCreated is TRUE, the caller then would go on to do some more
|
|
initialization before releasing the lock.
|
|
|
|
A new important point is that if the user requests that the object be
|
|
created and locked, then the RM no longer guranteees that the create and
|
|
lock will happen in the context of holding the GroupLock
|
|
|
|
|
|
FUNDAMENTAL ASSUMPTION: The key of an object doesn't change once
|
|
it's in the group. Based on this assumption, we don't try to claim
|
|
the object's lock when looking for the object with a matching key.
|
|
|
|
Arguments:
|
|
|
|
pGroup - Group in which to lookup/create object.
|
|
Flags - One or more of fRM_LOCKED, fRM_CREATE, fRM_NEW
|
|
pvKey - Key used to lookup object.
|
|
pvCreateParams - If object is to be created, parameters to be passed to the
|
|
object's creation function.
|
|
ppObject - Place to store pointer to the found/created object.
|
|
pfCreated - If non-NULL, *pfCreated is set to TRUE IFF the object was
|
|
created.
|
|
|
|
Return Value:
|
|
|
|
NDIS_STATUS_SUCCESS If the operation succeeded.
|
|
NDIS_STATUS_RESOURCES If a new object could not be created.
|
|
NDIS_STATUS_RFAILURE If the object was not found.
|
|
|
|
--*/
|
|
{
|
|
RM_STATUS Status = NDIS_STATUS_FAILURE;
|
|
BOOLEAN fUnlockOutOfOrder = FALSE;
|
|
PRM_OBJECT_HEADER pOwningObject = pGroup->pOwningObject;
|
|
PRM_OBJECT_HEADER pObject;
|
|
|
|
#if DBG
|
|
KIRQL EntryIrql = KeGetCurrentIrql();
|
|
#endif // DBG
|
|
ENTER("RmLookupObjectInGroup", 0xd2cd6379)
|
|
|
|
ASSERT(pOwningObject!=NULL);
|
|
// OBSOLETE -- see comments above: ASSERT(pfCreated==NULL || (Flags&RM_LOCKED));
|
|
|
|
if (pfCreated != NULL) *pfCreated = FALSE;
|
|
|
|
NdisAcquireSpinLock(&pGroup->OsLock);
|
|
|
|
do
|
|
{
|
|
BOOLEAN fFound;
|
|
PRM_HASH_LINK *ppLink = NULL;
|
|
|
|
if (!RMISALLOCATED(pGroup->pOwningObject)) break;
|
|
|
|
if (pGroup->fEnabled != TRUE) break;
|
|
|
|
fFound = RmLookupHashTable(
|
|
&pGroup->HashTable,
|
|
&ppLink,
|
|
pvKey
|
|
);
|
|
|
|
if (fFound)
|
|
{
|
|
if (Flags & RM_NEW)
|
|
{
|
|
// Caller wanted us to created a new object, but the object already
|
|
// exists, so we fail...
|
|
//
|
|
// TODO: return appropriate error code.
|
|
//
|
|
break;
|
|
}
|
|
|
|
// Go from hash-link to object.
|
|
// TODO: once HashLink goes away, need some other way to get
|
|
// to the object.
|
|
//
|
|
pObject = CONTAINING_RECORD(*ppLink, RM_OBJECT_HEADER, HashLink);
|
|
ASSERT(pObject->pStaticInfo == pGroup->pStaticInfo);
|
|
|
|
}
|
|
else
|
|
{
|
|
if (!(Flags & RM_CREATE))
|
|
{
|
|
// Couldn't find it, and caller doesn't want us to create one, so
|
|
// we fail...
|
|
break;
|
|
}
|
|
|
|
// Create object...
|
|
//
|
|
ASSERTEX(pGroup->pStaticInfo->pfnCreate!=NULL, pGroup);
|
|
pObject = pGroup->pStaticInfo->pfnCreate(
|
|
pOwningObject,
|
|
pvCreateParams,
|
|
pSR
|
|
);
|
|
|
|
if (pObject == NULL)
|
|
{
|
|
Status = NDIS_STATUS_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
TR_INFO((
|
|
"Created 0x%p (%s) in Group 0x%p (%s)\n",
|
|
pObject,
|
|
pObject->szDescription,
|
|
pGroup,
|
|
pGroup->szDescription
|
|
));
|
|
|
|
ASSERTEX(RMISALLOCATED(pObject), pObject);
|
|
|
|
// Now enter it into the hash table.
|
|
//
|
|
RmAddHashItem(
|
|
&pGroup->HashTable,
|
|
ppLink,
|
|
&pObject->HashLink,
|
|
pvKey
|
|
);
|
|
if (pfCreated != NULL)
|
|
{
|
|
*pfCreated = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
RmTmpReferenceObject(pObject, pSR);
|
|
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
|
|
} while(FALSE);
|
|
|
|
NdisReleaseSpinLock(&pGroup->OsLock);
|
|
|
|
// Do we need to look the object before returning it
|
|
// to the user
|
|
if ((!FAIL(Status)) && (Flags & RM_LOCKED))
|
|
{
|
|
RmWriteLockObject(
|
|
pObject,
|
|
#if RM_EXTRA_CHECKING
|
|
0x6197fdda,
|
|
#endif //RM_EXTRA_CHECKING
|
|
pSR
|
|
);
|
|
|
|
if (!RMISALLOCATED(pObject))
|
|
{
|
|
// We don't allow this...
|
|
RmUnlockObject(
|
|
pObject,
|
|
pSR
|
|
);
|
|
}
|
|
}
|
|
|
|
if (FAIL(Status))
|
|
{
|
|
*ppObject = NULL;
|
|
}
|
|
else
|
|
{
|
|
*ppObject = pObject;
|
|
}
|
|
|
|
#if DBG
|
|
{
|
|
KIRQL ExitIrql = KeGetCurrentIrql();
|
|
TR_VERB(("Exiting. EntryIrql=%lu, ExitIrql = %lu\n", EntryIrql, ExitIrql));
|
|
}
|
|
#endif //DBG
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RM_STATUS
|
|
RmGetNextObjectInGroup(
|
|
IN PRM_GROUP pGroup,
|
|
IN PRM_OBJECT_HEADER pCurrentObject, // OPTIONAL
|
|
OUT PRM_OBJECT_HEADER * ppNextObject,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the 1st object in group (if pCurrentObject == NULL), or the object
|
|
"after" pCurrentObject (if pCurrentObject != NULL).
|
|
|
|
The definition of "after" is hidden -- the only guarantee is if this
|
|
function is 1st called with NULL pCurrentObject and subsequently with
|
|
pCurrentObject set to the value previously returned in ppNextObject, until
|
|
the function returns NDIS_STATUS_FAILURE, all objects in the group will
|
|
be returned once and only once. This guarantee is only valid if no objects
|
|
are added or removed during the enumeration process.
|
|
|
|
On success, the "next" object is tmpref'd a pointer to it is saved in
|
|
*ppNextObject.
|
|
|
|
Arguments:
|
|
|
|
pGroup - The group
|
|
pCurrentObject - (OPTIONAL) An object in the group.
|
|
ppNextObject - Place to return the the object "after" pCurrentObject
|
|
(see RoutineDescription for details.)
|
|
|
|
|
|
Return Value:
|
|
|
|
NDIS_STATUS_SUCCESS if we could find a "next" object.
|
|
NDIS_STATUS_FAILURE otherwise
|
|
|
|
--*/
|
|
{
|
|
RM_STATUS Status = NDIS_STATUS_FAILURE;
|
|
PRM_OBJECT_HEADER pOwningObject = pGroup->pOwningObject;
|
|
PRM_OBJECT_HEADER pObject;
|
|
|
|
ENTER("RmGetNextObjectInGroup", 0x11523db7)
|
|
|
|
ASSERT(pOwningObject!=NULL);
|
|
|
|
NdisAcquireSpinLock(&pGroup->OsLock);
|
|
|
|
do
|
|
{
|
|
BOOLEAN fFound;
|
|
PRM_HASH_LINK pLink = NULL;
|
|
PRM_HASH_LINK pCurrentLink = NULL;
|
|
|
|
if (!RMISALLOCATED(pGroup->pOwningObject)) break;
|
|
|
|
if (pGroup->fEnabled != TRUE) break;
|
|
|
|
if (pCurrentObject != NULL)
|
|
{
|
|
pCurrentLink = &pCurrentObject->HashLink;
|
|
}
|
|
|
|
fFound = RmNextHashTableItem(
|
|
&pGroup->HashTable,
|
|
pCurrentLink, // pCurrentLink
|
|
&pLink // pNextLink
|
|
);
|
|
|
|
if (fFound)
|
|
{
|
|
|
|
// Go from hash-link to object.
|
|
// TODO: once HashLink goes away, need some other way to get
|
|
// to the object.
|
|
//
|
|
pObject = CONTAINING_RECORD(pLink, RM_OBJECT_HEADER, HashLink);
|
|
ASSERT(pObject->pStaticInfo == pGroup->pStaticInfo);
|
|
|
|
}
|
|
else
|
|
{
|
|
// Couldn't find one.
|
|
// we fail...
|
|
break;
|
|
}
|
|
|
|
RmTmpReferenceObject(pObject, pSR);
|
|
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
|
|
} while(FALSE);
|
|
|
|
NdisReleaseSpinLock(&pGroup->OsLock);
|
|
|
|
if (FAIL(Status))
|
|
{
|
|
*ppNextObject = NULL;
|
|
}
|
|
else
|
|
{
|
|
*ppNextObject = pObject;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
RmFreeObjectInGroup(
|
|
IN PRM_GROUP pGroup,
|
|
IN PRM_OBJECT_HEADER pObject,
|
|
IN struct _RM_TASK *pTask, OPTIONAL // Unused. TODO: remove this.
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove object pObject from group pGroup and deallocate pObject.
|
|
|
|
--*/
|
|
{
|
|
ENTER("RmFreeObjectInGroup", 0xd2cd6379)
|
|
PRM_OBJECT_HEADER pOwningObject = pGroup->pOwningObject;
|
|
|
|
ASSERTEX(pOwningObject!=NULL, pGroup);
|
|
ASSERTEX(pTask==NULL, pGroup);
|
|
|
|
NdisAcquireSpinLock(&pGroup->OsLock);
|
|
|
|
// TODO: what if at this time, someone else is doing FreeAllObjects in Group?
|
|
//
|
|
TR_INFO((
|
|
"Freeing 0x%p (%s) in Group 0x%p (%s)\n",
|
|
pObject,
|
|
pObject->szDescription,
|
|
pGroup,
|
|
pGroup->szDescription
|
|
));
|
|
|
|
ASSERTEX(RMISALLOCATED(pObject), pObject);
|
|
|
|
RmRemoveHashItem(
|
|
&pGroup->HashTable,
|
|
&pObject->HashLink
|
|
);
|
|
|
|
NdisReleaseSpinLock(&pGroup->OsLock);
|
|
|
|
// Deallocate the object.
|
|
//
|
|
RmDeallocateObject(
|
|
pObject,
|
|
pSR
|
|
);
|
|
|
|
EXIT()
|
|
}
|
|
|
|
|
|
VOID
|
|
RmFreeAllObjectsInGroup(
|
|
IN PRM_GROUP pGroup,
|
|
IN struct _RM_TASK *pTask, OPTIONAL // Unused. TODO: remove this.
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove and deallocate all object in pGroup.
|
|
|
|
--*/
|
|
{
|
|
PRM_HASH_LINK *ppLink, *ppLinkEnd;
|
|
NdisAcquireSpinLock(&pGroup->OsLock);
|
|
if (pGroup->fEnabled)
|
|
{
|
|
pGroup->fEnabled = FALSE;
|
|
}
|
|
else
|
|
{
|
|
NdisReleaseSpinLock(&pGroup->OsLock);
|
|
return; // EARLY RETURN
|
|
}
|
|
|
|
//
|
|
// With fEnabled set to FALSE by us, we expect the following:
|
|
// (a) pHashTable->pTable is going to stay the same size.
|
|
// (b) No items are going to be added or removed by anyone else.
|
|
//
|
|
|
|
ppLink = pGroup->HashTable.pTable;
|
|
ppLinkEnd = ppLink + pGroup->HashTable.TableLength;
|
|
|
|
for ( ; ppLink < ppLinkEnd; ppLink++)
|
|
{
|
|
while (*ppLink != NULL)
|
|
{
|
|
PRM_HASH_LINK pLink = *ppLink;
|
|
PRM_OBJECT_HEADER pObj;
|
|
|
|
// Remove it from the bucket list.
|
|
//
|
|
*ppLink = pLink->pNext;
|
|
pLink->pNext = NULL;
|
|
pGroup->HashTable.NumItems--;
|
|
|
|
NdisReleaseSpinLock(&pGroup->OsLock);
|
|
|
|
pObj = CONTAINING_RECORD(pLink, RM_OBJECT_HEADER, HashLink);
|
|
ASSERT(pObj->pStaticInfo == pGroup->pStaticInfo);
|
|
|
|
// Deallocate the object.
|
|
//
|
|
RmDeallocateObject(
|
|
pObj,
|
|
pSR
|
|
);
|
|
|
|
NdisAcquireSpinLock(&pGroup->OsLock);
|
|
}
|
|
}
|
|
|
|
NdisReleaseSpinLock(&pGroup->OsLock);
|
|
}
|
|
|
|
|
|
VOID
|
|
RmUnloadAllObjectsInGroup(
|
|
IN PRM_GROUP pGroup,
|
|
PFN_RM_TASK_ALLOCATOR pfnUnloadTaskAllocator,
|
|
PFN_RM_TASK_HANDLER pfnUnloadTaskHandler,
|
|
PVOID pvUserParam,
|
|
IN struct _RM_TASK *pTask, OPTIONAL
|
|
IN UINT uTaskPendCode,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Stops new objects from being added and unloads(see below) all objects
|
|
currently in the group.
|
|
|
|
"Unload" consists of allocating and starting a pfnUnloadTaskHask task
|
|
on each object. The unload task is responsible for
|
|
removing and deallocating the object from the group.
|
|
|
|
If pTask if non-NULL, it will be resumed on completion of the unload.
|
|
Otherwise, this function will BLOCK until the unload is complete.
|
|
|
|
Arguments:
|
|
|
|
pGroup - Group to unload.
|
|
pfnUnloadTaskAllocator - Use to allocate the object-unload tasks.
|
|
pfnTaskAllocator - Function used to allocate the unload task.
|
|
pfnUnloadTaskHandler - The handler of the unload task
|
|
pvUserParam - Task creation user-param.
|
|
WARNING: this param must be valid for the duration
|
|
of the unload process, not just until this
|
|
function returns. Of course, if pTask is NULL,
|
|
the two cases are equivalent.
|
|
pTask - (OPTIONAL) Task to resume when unload is complete.
|
|
If NULL, this function will block until the
|
|
unload is complete.
|
|
uTaskPendCode - (OPTIONAL) PendCode to use when resuming pTask.
|
|
|
|
--*/
|
|
{
|
|
PRM_TASK pUnloadTask;
|
|
NDIS_STATUS Status;
|
|
|
|
NdisAcquireSpinLock(&pGroup->OsLock);
|
|
|
|
//
|
|
// We don't check if there is already an unload task active for this group.
|
|
// Instead we go ahead and allocate and start an unload task. This latter
|
|
// task will pend on the already running unload task if there is on.
|
|
//
|
|
|
|
// Allocate a private task to coordinate the unloading of all the objects.
|
|
//
|
|
Status = rmAllocatePrivateTask(
|
|
pGroup->pOwningObject,
|
|
rmTaskUnloadGroup,
|
|
0,
|
|
"Task:UnloadAllObjectsInGroup",
|
|
&pUnloadTask,
|
|
pSR
|
|
);
|
|
|
|
if (FAIL(Status))
|
|
{
|
|
//
|
|
// Ouch -- ugly failure...
|
|
//
|
|
ASSERT(FALSE);
|
|
|
|
NdisReleaseSpinLock(&pGroup->OsLock);
|
|
|
|
}
|
|
else
|
|
{
|
|
TASK_UNLOADGROUP *pUGTask = (TASK_UNLOADGROUP *) pUnloadTask;
|
|
|
|
pUGTask->pGroup = pGroup;
|
|
pUGTask->pfnTaskUnloadObjectHandler = pfnUnloadTaskHandler;
|
|
pUGTask->pfnUnloadTaskAllocator = pfnUnloadTaskAllocator;
|
|
|
|
if (pTask == NULL)
|
|
{
|
|
|
|
// Set up an event which we'll wait on. The event will be signaled
|
|
// by pUnloadTask when it completes.
|
|
//
|
|
NdisInitializeEvent(&pUGTask->BlockEvent);
|
|
pUGTask->fUseEvent = TRUE;
|
|
|
|
// Tmpref it so pUnloadTask will stay around even afer it's
|
|
// completed -- because we wait on the event that's actually
|
|
// located in the task memory.
|
|
//
|
|
RmTmpReferenceObject(&pUnloadTask->Hdr, pSR);
|
|
}
|
|
|
|
NdisReleaseSpinLock(&pGroup->OsLock);
|
|
|
|
if (pTask != NULL)
|
|
{
|
|
RmPendTaskOnOtherTask(
|
|
pTask,
|
|
uTaskPendCode,
|
|
pUnloadTask,
|
|
pSR
|
|
);
|
|
}
|
|
|
|
Status = RmStartTask(pUnloadTask, 0, pSR);
|
|
|
|
if (pTask == NULL)
|
|
{
|
|
NdisWaitEvent(&pUGTask->BlockEvent, 0);
|
|
RmTmpDereferenceObject(&pUnloadTask->Hdr, pSR);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
RmEnableGroup(
|
|
IN PRM_GROUP pGroup,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
TODO: need better name for this.
|
|
|
|
Routine Description:
|
|
|
|
Enables items to be added to a group.
|
|
This function is typically called with a group which has completed
|
|
RmUnloadAllObjectsFromGroup or RmFreeAllObjectsInGroup.
|
|
On return from this call items may once more be added to this group.
|
|
|
|
This call must only be called after UnloadAllObjectsInGroup or
|
|
RmFreeAllObjectsInGroup have completed (synchronously or asynchronously).
|
|
|
|
If there are items in in group or there is an unload
|
|
task associated with the group at the time this function is called,
|
|
the group is NOT reinited and the DBG version will assert.
|
|
|
|
This function and may be called with an already enabled group, provided
|
|
the condition above is met (no items in group, no unload task).
|
|
|
|
--*/
|
|
{
|
|
NdisAcquireSpinLock(&pGroup->OsLock);
|
|
if ( pGroup->pUnloadTask == NULL
|
|
&& pGroup->HashTable.NumItems == 0)
|
|
{
|
|
pGroup->fEnabled = TRUE;
|
|
}
|
|
else
|
|
{
|
|
ASSERT("invalid state.");
|
|
}
|
|
NdisReleaseSpinLock(&pGroup->OsLock);
|
|
}
|
|
|
|
|
|
VOID
|
|
RmInitializeTask(
|
|
IN PRM_TASK pTask,
|
|
IN PRM_OBJECT_HEADER pParentObject,
|
|
IN PFN_RM_TASK_HANDLER pfnHandler,
|
|
IN PRM_STATIC_OBJECT_INFO pStaticInfo, OPTIONAL
|
|
IN const char * szDescription, OPTIONAL
|
|
IN UINT Timeout,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the specified task.
|
|
|
|
The task is tempref'd. It is the responsibility of the caller to
|
|
de-ref it when done. Typically this is implicitly done by calling
|
|
RmStartTask.
|
|
|
|
Arguments:
|
|
|
|
pTask - points to unitialized memory to hold the task.
|
|
pParentObject - will be the parent of the task.
|
|
pfnHandler - task's handler function.
|
|
pStaticInfo - (OPTIONAL) Static information about the task.
|
|
szDescription - (debug only, OPTIONAL) description of the task
|
|
Timeout - unused
|
|
|
|
--*/
|
|
{
|
|
ASSERT(!Timeout); // TODO: Timeouts unimplemented.
|
|
|
|
NdisZeroMemory(pTask, sizeof(*pTask));
|
|
|
|
RmInitializeHeader(
|
|
pParentObject,
|
|
&pTask->Hdr,
|
|
MTAG_TASK,
|
|
pParentObject->pLock,
|
|
(pStaticInfo) ? pStaticInfo : &RmTask_StaticInfo,
|
|
szDescription,
|
|
pSR
|
|
);
|
|
pTask->pfnHandler = pfnHandler;
|
|
SET_RM_TASK_STATE(pTask, RMTSKSTATE_IDLE);
|
|
InitializeListHead(&pTask->listTasksPendingOnMe);
|
|
|
|
RmTmpReferenceObject(&pTask->Hdr, pSR);
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
RmAbortTask(
|
|
IN PRM_TASK pTask,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
{
|
|
ASSERT(!"Unimplemented");
|
|
}
|
|
|
|
|
|
|
|
RM_STATUS
|
|
RmStartTask(
|
|
IN PRM_TASK pTask,
|
|
IN UINT_PTR UserParam,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Start the specified task.
|
|
|
|
NO locks should be held on entry and none are held on exit.
|
|
pTask is expected to have a tmp-ref which is deref'd here.
|
|
The task is automatically deallocated on completion (either synchronous
|
|
or asynchronous completion, either successful or failed completion).
|
|
Unless the caller is explicitly added a reference to pTask before calling
|
|
this function, the caller should not assume that pTask is still valid
|
|
on return from this function.
|
|
|
|
Arguments:
|
|
|
|
pTask - points to the task to be started.
|
|
UserParam - opaque value passed to the task handler with the
|
|
RM_TASKOP_START message.
|
|
|
|
--*/
|
|
{
|
|
ENTER("RmStartTask", 0xf80502d5)
|
|
NDIS_STATUS Status;
|
|
RM_ASSERT_NOLOCKS(pSR);
|
|
|
|
RMPRIVATELOCK(&pTask->Hdr, pSR);
|
|
if (!CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_IDLE))
|
|
{
|
|
ASSERTEX(!"Invalid state", pTask);
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
Status = NDIS_STATUS_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
if (!RMISALLOCATED(pTask->Hdr.pParentObject))
|
|
{
|
|
//
|
|
// TODO: consider not calling the handler if the parent object is
|
|
// deallocated, but that may be confusing.
|
|
// Consider not allowing children to be linked to an object
|
|
// (RmInitializeHeader returns failure) if the parent object is
|
|
// deallocated.
|
|
//
|
|
TR_WARN((
|
|
"Starting task 0x%p (%s) with DEALLOCATED parent 0x%p (%s).\n",
|
|
pTask,
|
|
pTask->Hdr.szDescription,
|
|
pTask->Hdr.pParentObject,
|
|
pTask->Hdr.pParentObject->szDescription
|
|
));
|
|
}
|
|
|
|
SET_RM_TASK_STATE(pTask, RMTSKSTATE_STARTING);
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
|
|
TR_INFO((
|
|
"STARTING Task 0x%p (%s); UserParam = 0x%lx\n",
|
|
pTask,
|
|
pTask->Hdr.szDescription,
|
|
UserParam
|
|
));
|
|
|
|
Status = pTask->pfnHandler(
|
|
pTask,
|
|
RM_TASKOP_START,
|
|
UserParam,
|
|
pSR
|
|
);
|
|
|
|
RM_ASSERT_NOLOCKS(pSR);
|
|
|
|
RMPRIVATELOCK(&pTask->Hdr, pSR);
|
|
switch(GET_RM_TASK_STATE(pTask))
|
|
{
|
|
case RMTSKSTATE_STARTING:
|
|
|
|
// This task is completing synchronously.
|
|
//
|
|
ASSERT(Status != NDIS_STATUS_PENDING);
|
|
SET_RM_TASK_STATE(pTask, RMTSKSTATE_ENDING);
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
rmEndTask(pTask, Status, pSR);
|
|
RmDeallocateObject(&pTask->Hdr, pSR);
|
|
break;
|
|
|
|
case RMTSKSTATE_PENDING:
|
|
ASSERTEX(Status == NDIS_STATUS_PENDING, pTask);
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
break;
|
|
|
|
case RMTSKSTATE_ENDING:
|
|
// This task is completing synchronously and the RM_TASKOP_END
|
|
// notification has already been sent.
|
|
//
|
|
// ??? ASSERT(Status != NDIS_STATUS_PENDING);
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
// ??? RmDeallocateObject(&pTask->Hdr, pSR);
|
|
break;
|
|
|
|
default:
|
|
ASSERTEX(FALSE, pTask);
|
|
// Fall through ...
|
|
|
|
case RMTSKSTATE_ACTIVE:
|
|
// This can happen if the task is in the process of being resumed
|
|
// in the context of some other thread. Nothing to do here...
|
|
// (This actually happens sometimes on a MP machine).
|
|
//
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
// Remove the tmp ref added when the task was allocated.
|
|
//
|
|
RmTmpDereferenceObject(
|
|
&pTask->Hdr,
|
|
pSR
|
|
);
|
|
|
|
RM_ASSERT_NOLOCKS(pSR);
|
|
|
|
EXIT()
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
RmDbgDumpTask(
|
|
IN PRM_TASK pTask,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
{
|
|
}
|
|
|
|
|
|
RM_STATUS
|
|
RmSuspendTask(
|
|
IN PRM_TASK pTask,
|
|
IN UINT SuspendContext,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Suspends the specified task.
|
|
|
|
RmSuspendTask is always called in the context of a task handler.
|
|
pTask is may be locked on entry -- we don't care.
|
|
|
|
Arguments:
|
|
|
|
pTask - task to be suspended.
|
|
SuspendContext - context to be presented to the task's handler when
|
|
the task is subsequently resumed. Specifically, this
|
|
context may be accessed using the RM_PEND_CODE macro,
|
|
when the task's handler is called with code
|
|
RM_TASKOP_PENDCOMPLETE.
|
|
|
|
--*/
|
|
{
|
|
ENTER("RmSuspendTask", 0xd80fdc00)
|
|
NDIS_STATUS Status;
|
|
// RM_ASSERT_NOLOCKS(pSR);
|
|
|
|
RMPRIVATELOCK(&pTask->Hdr, pSR);
|
|
|
|
TR_INFO((
|
|
"SUSPENDING Task 0x%p (%s); SuspendContext = 0x%lx\n",
|
|
pTask,
|
|
pTask->Hdr.szDescription,
|
|
SuspendContext
|
|
));
|
|
|
|
if ( !CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_STARTING)
|
|
&& !CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_ACTIVE))
|
|
{
|
|
ASSERTEX(!"Invalid state", pTask);
|
|
Status = NDIS_STATUS_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
SET_RM_TASK_STATE(pTask, RMTSKSTATE_PENDING);
|
|
pTask->SuspendContext = SuspendContext;
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
}
|
|
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
|
|
// RM_ASSERT_NOLOCKS(pSR);
|
|
|
|
EXIT()
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
RmUnsuspendTask(
|
|
IN PRM_TASK pTask,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Undoes the effect of a previous call to RmSuspendTask.
|
|
|
|
Task MUST be in the pending state and MUST NOT be pending on another task.
|
|
Debug version will ASSERT if above conditions are not met.
|
|
|
|
RmUnsuspendTask is always called in the context of a task handler.
|
|
pTask is may be locked on entry -- we don't care.
|
|
|
|
Arguments:
|
|
|
|
pTask - task to be suspended.
|
|
|
|
--*/
|
|
{
|
|
ENTER("RmUnsuspendTask", 0xcf713639)
|
|
|
|
RMPRIVATELOCK(&pTask->Hdr, pSR);
|
|
|
|
TR_INFO((
|
|
"UN-SUSPENDING Task 0x%p (%s). SuspendContext = 0x%x\n",
|
|
pTask,
|
|
pTask->Hdr.szDescription,
|
|
pTask->SuspendContext
|
|
));
|
|
|
|
ASSERT(CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_PENDING));
|
|
ASSERT(pTask->pTaskIAmPendingOn == NULL);
|
|
SET_RM_TASK_STATE(pTask, RMTSKSTATE_ACTIVE);
|
|
pTask->SuspendContext = 0;
|
|
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
|
|
EXIT()
|
|
}
|
|
|
|
|
|
VOID
|
|
RmResumeTask(
|
|
IN PRM_TASK pTask,
|
|
IN UINT_PTR SuspendCompletionParam,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Resume a previously-suspended task.
|
|
|
|
No locks held on entry or exit.
|
|
|
|
SuspendCompletionParam is user-defined, and must be agreed upon between
|
|
the caller of RmUnpendTask and the task that's being unpended.
|
|
The Task's handler is ALWAYS called in the context of the caller of RmUnpendTask.
|
|
So it is ok for the caller to declare a structure on the stack and pass
|
|
a pointer to it as SuspendCompletionParam.
|
|
|
|
WARNING: pTask could well be invalid (deallocate) by the time we return
|
|
from this function. The caller is responsible for tmprefing pTask if it needs
|
|
to access after return from this function.
|
|
|
|
Arguments:
|
|
|
|
pTask - task to be resumed.
|
|
SuspendCompletionParam - arbitrary value that is passed on to the task's
|
|
handler as "UserParan" when the handler is called
|
|
with code RM_TASKOP_PENDCOMPLETE.
|
|
--*/
|
|
{
|
|
ENTER("RmResumeTask", 0xd261f3c6)
|
|
NDIS_STATUS Status;
|
|
RM_ASSERT_NOLOCKS(pSR);
|
|
|
|
RMPRIVATELOCK(&pTask->Hdr, pSR);
|
|
|
|
TR_INFO((
|
|
"RESUMING Task 0x%p (%s); SuspendCompletionParam = 0x%lx\n",
|
|
pTask,
|
|
pTask->Hdr.szDescription,
|
|
SuspendCompletionParam
|
|
));
|
|
|
|
if (!CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_PENDING))
|
|
{
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
ASSERTEX(!"Invalid state", pTask);
|
|
}
|
|
else
|
|
{
|
|
// Add tmp ref, because we need to look at pTask after the return
|
|
// from calling pfnHandler.
|
|
//
|
|
RmTmpReferenceObject(&pTask->Hdr, pSR);
|
|
|
|
SET_RM_TASK_STATE(pTask, RMTSKSTATE_ACTIVE);
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
Status = pTask->pfnHandler(
|
|
pTask,
|
|
RM_TASKOP_PENDCOMPLETE,
|
|
SuspendCompletionParam,
|
|
pSR
|
|
);
|
|
|
|
RM_ASSERT_NOLOCKS(pSR);
|
|
|
|
RMPRIVATELOCK(&pTask->Hdr, pSR);
|
|
switch(GET_RM_TASK_STATE(pTask))
|
|
{
|
|
case RMTSKSTATE_ACTIVE:
|
|
|
|
// This task is completing here (maybe)
|
|
//
|
|
if (Status != NDIS_STATUS_PENDING)
|
|
{
|
|
SET_RM_TASK_STATE(pTask, RMTSKSTATE_ENDING);
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
rmEndTask(pTask, Status, pSR);
|
|
RmDeallocateObject(&pTask->Hdr, pSR);
|
|
}
|
|
else
|
|
{
|
|
// It could be returning pending, but the state could
|
|
// by now be active because it was completed elsewhere.
|
|
// ASSERT(Status != NDIS_STATUS_PENDING);
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
}
|
|
break;
|
|
|
|
case RMTSKSTATE_PENDING:
|
|
ASSERTEX(Status == NDIS_STATUS_PENDING, pTask);
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
break;
|
|
|
|
case RMTSKSTATE_ENDING:
|
|
// This task is completing synchronously and the RM_TASKOP_END
|
|
// notification has already been sent.
|
|
//
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
break;
|
|
|
|
default:
|
|
ASSERTEX(FALSE, pTask);
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
}
|
|
|
|
// Remove tmpref added above. pTask may well go away now...
|
|
//
|
|
RmTmpDereferenceObject(&pTask->Hdr, pSR);
|
|
}
|
|
|
|
RM_ASSERT_NOLOCKS(pSR);
|
|
EXIT()
|
|
}
|
|
|
|
|
|
VOID
|
|
RmResumeTaskAsync(
|
|
IN PRM_TASK pTask,
|
|
IN UINT_PTR SuspendCompletionParam,
|
|
IN OS_WORK_ITEM * pOsWorkItem,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Similar to RmResumeTask, except that the task is resumed in the context
|
|
of a work-item thread.
|
|
|
|
Arguments:
|
|
pTask - see RmResumeTask
|
|
SuspendCompletionParam - see RmResumeTask
|
|
pOsWorkItem - caller supplied UNitialized work item (must stay
|
|
around until the task is resumed). Typically this
|
|
will be located within the user-specific portion
|
|
of pTask
|
|
--*/
|
|
{
|
|
NDIS_STATUS Status;
|
|
|
|
RM_ASSERT_NOLOCKS(pSR);
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
// This may seem paranoid, but is such a powerful check that it's worth it.
|
|
//
|
|
RmDbgAddAssociation(
|
|
0x33d63ece, // Location ID
|
|
&pTask->Hdr, // pObject
|
|
(UINT_PTR) SuspendCompletionParam, // Instance1
|
|
(UINT_PTR) pOsWorkItem, // Instance2
|
|
RM_PRIVATE_ASSOC_RESUME_TASK_ASYNC, // AssociationID
|
|
szASSOCFORMAT_RESUME_TASK_ASYNC, // szAssociationFormat
|
|
pSR
|
|
);
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
// We don't need to grab the private lock to set this, because only one
|
|
// entity can call RmResumeTaskAsync. Note that we also ensure things are clean
|
|
// (in the debug case) by the association added above.
|
|
//
|
|
pTask->AsyncCompletionParam = SuspendCompletionParam;
|
|
|
|
NdisInitializeWorkItem(
|
|
pOsWorkItem,
|
|
rmWorkItemHandler_ResumeTaskAsync,
|
|
pTask
|
|
);
|
|
|
|
Status = NdisScheduleWorkItem(pOsWorkItem);
|
|
if (FAIL(Status))
|
|
{
|
|
ASSERT(!"NdisStatusWorkItem failed.");
|
|
|
|
// It so happens that NdisScheudleWorkItem (current implementation
|
|
// doesn't fail. Nevertheless, we do the best we can and actually
|
|
// resume the task. If the caller was at dpc level and was expecting
|
|
// the task to resume at passive, they're out of luck.
|
|
//
|
|
RmResumeTask(pTask, SuspendCompletionParam, pSR);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
RmResumeTaskDelayed(
|
|
IN PRM_TASK pTask,
|
|
IN UINT_PTR SuspendCompletionParam,
|
|
IN ULONG MsDelay,
|
|
IN OS_TIMER * pOsTimer,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Similar to RmResumeTask, except that the task is resumed in the context
|
|
of a os timer handler which is set to fire after MsDelay milliseconds
|
|
from the time RmResumeTaskDelayed is called.
|
|
|
|
EXCEPTION: if someone has previously called RmResumeDelayedTaskNow, this
|
|
task could be resumed in the context of this function call itself.
|
|
|
|
Abort implementation notes: see notes.txt 07/14/1999 entry.
|
|
|
|
Arguments:
|
|
|
|
pTask - see RmResumeTask
|
|
SuspendCompletionParam - see RmResumeTask
|
|
pOsTimer - caller supplied UNitialized timer
|
|
(must stay around until the task is resumed).
|
|
Typically this will be located within the
|
|
user-specific portion of pTask.
|
|
--*/
|
|
{
|
|
NDIS_STATUS Status;
|
|
|
|
RM_ASSERT_NOLOCKS(pSR);
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
// This may seem paranoid, but is such a powerful check that it's worth it.
|
|
//
|
|
RmDbgAddAssociation(
|
|
0x33d63ece, // Location ID
|
|
&pTask->Hdr, // pObject
|
|
(UINT_PTR) SuspendCompletionParam, // Instance1
|
|
(UINT_PTR) NULL, // Instance2
|
|
RM_PRIVATE_ASSOC_RESUME_TASK_DELAYED, // AssociationID
|
|
szASSOCFORMAT_RESUME_TASK_DELAYED, // szAssociationFormat
|
|
pSR
|
|
);
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
// Ddk states that it's best to call this function at passive level.
|
|
//
|
|
NdisInitializeTimer(
|
|
pOsTimer,
|
|
rmTimerHandler_ResumeTaskDelayed,
|
|
pTask
|
|
);
|
|
|
|
RMPRIVATELOCK(&pTask->Hdr, pSR);
|
|
|
|
// The task-del state should NOT be "delayed"
|
|
//
|
|
ASSERT(RM_CHECK_STATE(pTask, RMTSKDELSTATE_MASK, 0));
|
|
pTask->AsyncCompletionParam = SuspendCompletionParam;
|
|
RM_SET_STATE(pTask, RMTSKDELSTATE_MASK, RMTSKDELSTATE_DELAYED);
|
|
|
|
if (RM_CHECK_STATE(pTask, RMTSKABORTSTATE_MASK, RMTSKABORTSTATE_ABORT_DELAY))
|
|
{
|
|
// Oops, the delay has been aborted -- we call the tick handler now!
|
|
//
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
|
|
rmTimerHandler_ResumeTaskDelayed(
|
|
NULL, // SystemSpecific1,
|
|
pTask, // FunctionContext,
|
|
NULL, // SystemSpecific2,
|
|
NULL // SystemSpecific3
|
|
);
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Not currently aborting, let's set the timer.
|
|
//
|
|
NdisSetTimer(pOsTimer, MsDelay);
|
|
|
|
// Very important to unlock the private lock AFTER calling set timer,
|
|
// other wise someone could call RmResumeDelayedTaskNow BEFORE we call
|
|
// NdisSetTimer, in which we would not end up aborting the delayed task.
|
|
//
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
RmResumeDelayedTaskNow(
|
|
IN PRM_TASK pTask,
|
|
IN OS_TIMER * pOsTimer,
|
|
OUT PUINT pTaskResumed,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cut's short the delay and resumes the task immediately.
|
|
|
|
Implementation notes: see notes.txt 07/14/1999 entry.
|
|
|
|
Arguments:
|
|
|
|
pTask - see RmResumeTask
|
|
pOsTimer - caller supplied initialized timer
|
|
(must stay around until the task is resumed).
|
|
Typically this will be located within the
|
|
user-specific portion of pTask.
|
|
pTaskResumed - Points to a caller-supplied variable.
|
|
RmResumeDelayedTask sets this variable to TRUE if the
|
|
task was resumed as a consequence of this call, or to
|
|
FALSE if the task was resumed due to some other reason.
|
|
--*/
|
|
{
|
|
UINT_PTR CompletionParam = pTask->AsyncCompletionParam;
|
|
|
|
*pTaskResumed = FALSE;
|
|
ASSERTEX(RMISALLOCATED(&pTask->Hdr), pTask);
|
|
|
|
RMPRIVATELOCK(&pTask->Hdr, pSR);
|
|
|
|
RM_SET_STATE(pTask, RMTSKABORTSTATE_MASK, RMTSKABORTSTATE_ABORT_DELAY);
|
|
|
|
if (RM_CHECK_STATE(pTask, RMTSKDELSTATE_MASK, RMTSKDELSTATE_DELAYED))
|
|
{
|
|
BOOLEAN TimerCanceled = FALSE;
|
|
|
|
//
|
|
// The task is actually delayed. Let's go ahead and cancel the timer
|
|
// and resume the task now (which we do indirectly by calling
|
|
// the timer handler ourselves).
|
|
//
|
|
NdisCancelTimer(pOsTimer, &TimerCanceled);
|
|
if (TimerCanceled)
|
|
{
|
|
//
|
|
// The timer was actually canceled -- so we call the timer handler
|
|
// ourselves.
|
|
//
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
|
|
rmTimerHandler_ResumeTaskDelayed(
|
|
NULL, // SystemSpecific1,
|
|
pTask, // FunctionContext,
|
|
NULL, // SystemSpecific2,
|
|
NULL // SystemSpecific3
|
|
);
|
|
*pTaskResumed = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Hmm -- the timer is not enabled. This is either because
|
|
// the timer handler has just been called (and not yet cleared
|
|
// the "DELAY" state) OR someone has previously called
|
|
// RmResumeDelayedTaskNow.
|
|
//
|
|
//
|
|
// Nothing to do.
|
|
//
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The task state is not delayed -- so we just set the abort state
|
|
// and go away.
|
|
//
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
}
|
|
}
|
|
|
|
|
|
RM_STATUS
|
|
RmPendTaskOnOtherTask(
|
|
IN PRM_TASK pTask,
|
|
IN UINT SuspendContext,
|
|
IN PRM_TASK pOtherTask,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pend task pTask on task pOtherTask.
|
|
|
|
Note: RmPendTaskOnOtherTask will cause pTask's pend operation to be
|
|
completed in the context of this call itself, if pOtherTask is already
|
|
in the completed state.
|
|
03/26/1999 -- see RmPendTaskOnOtherTaskV2, and also
|
|
03/26/1999 notes.txt entry "Some proposed ..."
|
|
|
|
Arguments:
|
|
|
|
pTask - task to be suspended.
|
|
SuspendContext - Context associated with the suspend (see
|
|
RmSuspendTask for details).
|
|
pOtherTask - task that pTask is to pend on.
|
|
|
|
Return Value:
|
|
|
|
NDIS_STATUS_SUCCESS on success.
|
|
NDIS_STATUS_FAILURE on failure (typically because pTask is not in as
|
|
position to be suspended.)
|
|
--*/
|
|
{
|
|
ENTER("RmPendTaskOnOtherTask", 0x0416873e)
|
|
NDIS_STATUS Status = NDIS_STATUS_FAILURE;
|
|
BOOLEAN fResumeTask = FALSE;
|
|
RM_ASSERT_NOLOCKS(pSR);
|
|
|
|
TR_INFO((
|
|
"PENDING Task 0x%p (%s) on Task 0x%p (%s). SuspendCompletionParam = 0x%lx\n",
|
|
pTask,
|
|
pTask->Hdr.szDescription,
|
|
pOtherTask,
|
|
pOtherTask->Hdr.szDescription,
|
|
SuspendContext
|
|
));
|
|
//
|
|
// WARNING: we break the locking rules here by getting the lock on
|
|
// both pTask and pOtherTask.
|
|
// TODO: consider acquiring them in order of increasing numerical value.
|
|
//
|
|
ASSERT(pTask != pOtherTask);
|
|
NdisAcquireSpinLock(&(pOtherTask->Hdr.RmPrivateLock.OsLock));
|
|
NdisAcquireSpinLock(&(pTask->Hdr.RmPrivateLock.OsLock));
|
|
|
|
do
|
|
{
|
|
// Break if we can't pend pTask on pOtherTask.
|
|
//
|
|
{
|
|
if ( !CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_STARTING)
|
|
&& !CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_ACTIVE))
|
|
{
|
|
ASSERTEX(!"Invalid state", pTask);
|
|
break;
|
|
}
|
|
|
|
// Non-NULL pTaskIAmPendingOn implies that pTask is already pending on
|
|
// some other task!
|
|
//
|
|
if (pTask->pTaskIAmPendingOn != NULL)
|
|
{
|
|
ASSERTEX(!"Invalid state", pTask);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
|
|
SET_RM_TASK_STATE(pTask, RMTSKSTATE_PENDING);
|
|
pTask->SuspendContext = SuspendContext;
|
|
|
|
if (CHECK_RM_TASK_STATE(pOtherTask, RMTSKSTATE_ENDING))
|
|
{
|
|
//
|
|
// Other task is done -- so we resume pTask before returning...
|
|
//
|
|
fResumeTask = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// pOtherTask is not ended -- add pTask to the list of tasks pending
|
|
// on pOtherTask.
|
|
//
|
|
pTask->pTaskIAmPendingOn = pOtherTask;
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
RmLinkObjectsEx(
|
|
&pTask->Hdr,
|
|
&pOtherTask->Hdr,
|
|
0x77c488ca,
|
|
RM_PRIVATE_ASSOC_LINK_TASKPENDINGON,
|
|
szASSOCFORMAT_LINK_TASKPENDINGON,
|
|
RM_PRIVATE_ASSOC_LINK_TASKBLOCKS,
|
|
szASSOCFORMAT_LINK_TASKBLOCKS,
|
|
pSR
|
|
);
|
|
#else // !RM_EXTRA_CHECKING
|
|
RmLinkObjects(&pTask->Hdr, &pOtherTask->Hdr, pSR);
|
|
#endif // !RM_EXTRA_CHECKING
|
|
|
|
ASSERTEX(pTask->linkFellowPendingTasks.Blink == NULL, pTask);
|
|
ASSERTEX(pTask->linkFellowPendingTasks.Flink == NULL, pTask);
|
|
InsertHeadList(
|
|
&pOtherTask->listTasksPendingOnMe,
|
|
&pTask->linkFellowPendingTasks
|
|
);
|
|
|
|
} while(FALSE);
|
|
|
|
NdisReleaseSpinLock(&(pTask->Hdr.RmPrivateLock.OsLock));
|
|
NdisReleaseSpinLock(&(pOtherTask->Hdr.RmPrivateLock.OsLock));
|
|
|
|
if (fResumeTask)
|
|
{
|
|
RmResumeTask(
|
|
pTask,
|
|
NDIS_STATUS_SUCCESS, // SuspendCompletionParam. TODO: put real code.
|
|
pSR
|
|
);
|
|
}
|
|
|
|
RM_ASSERT_NOLOCKS(pSR);
|
|
|
|
EXIT()
|
|
return Status;
|
|
}
|
|
|
|
|
|
RM_STATUS
|
|
RmPendOnOtherTaskV2(
|
|
IN PRM_TASK pTask,
|
|
IN UINT SuspendContext,
|
|
IN PRM_TASK pOtherTask,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
if pOtherTask is not complete, Pend task pTask on task pOtherTask and return
|
|
NDIS_STATUS_PENDING. However, if pOtherTask is already complete,
|
|
then don't pend and instead return NDIS_STATUS_SUCCESS.
|
|
|
|
See 03/26/1999 notes.txt entry "Some proposed ...". This function
|
|
is currently used only by rmTaskUnloadGroup, to avoid the problem describted
|
|
in the above-referenced notes.txt entry.
|
|
|
|
TODO: Eventually get rid of RmPendTaskOnOtherTask.
|
|
|
|
Arguments:
|
|
|
|
See RmPendTaskOnOtherTask
|
|
|
|
Return Value:
|
|
|
|
NDIS_STATUS_PENDING if pTask is pending on pOtherTask
|
|
NDIS_STATUS_SUCCESS if pOtherTask is complete.
|
|
NDIS_STATUS_FAILURE if there was some failure (typically pTask is not
|
|
in a position to be pended.)
|
|
--*/
|
|
{
|
|
ENTER("RmPendTaskOnOtherTaskV2", 0x0e7d1b89)
|
|
NDIS_STATUS Status = NDIS_STATUS_FAILURE;
|
|
RM_ASSERT_NOLOCKS(pSR);
|
|
|
|
TR_INFO((
|
|
"PENDING(V2) Task 0x%p (%s) on Task 0x%p (%s). SuspendCompletionParam = 0x%lx\n",
|
|
pTask,
|
|
pTask->Hdr.szDescription,
|
|
pOtherTask,
|
|
pOtherTask->Hdr.szDescription,
|
|
SuspendContext
|
|
));
|
|
|
|
// This is not a useless assert -- I'e had a bug elsewhere which caused this
|
|
// assert to get hit.
|
|
//
|
|
ASSERT(pTask != pOtherTask);
|
|
//
|
|
// WARNING: we break the locking rules here by getting the lock on
|
|
// both pTask and pOtherTask.
|
|
// TODO: consider acquiring them in order of increasing numerical value.
|
|
//
|
|
NdisAcquireSpinLock(&(pOtherTask->Hdr.RmPrivateLock.OsLock));
|
|
NdisAcquireSpinLock(&(pTask->Hdr.RmPrivateLock.OsLock));
|
|
|
|
do
|
|
{
|
|
// Break if we can't pend pTask on pOtherTask.
|
|
//
|
|
{
|
|
if ( !CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_STARTING)
|
|
&& !CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_ACTIVE))
|
|
{
|
|
ASSERTEX(!"Invalid state", pTask);
|
|
break;
|
|
}
|
|
|
|
// Non-NULL pTaskIAmPendingOn implies that pTask is already pending on
|
|
// some other task!
|
|
//
|
|
if (pTask->pTaskIAmPendingOn != NULL)
|
|
{
|
|
ASSERTEX(!"Invalid state", pTask);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if (CHECK_RM_TASK_STATE(pOtherTask, RMTSKSTATE_ENDING))
|
|
{
|
|
//
|
|
// Other task is done -- so we simply return success...
|
|
//
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// pOtherTask is not ended -- set pTask state to pending, and
|
|
// add it to the list of tasks pending on pOtherTask.
|
|
//
|
|
SET_RM_TASK_STATE(pTask, RMTSKSTATE_PENDING);
|
|
pTask->SuspendContext = SuspendContext;
|
|
pTask->pTaskIAmPendingOn = pOtherTask;
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
RmLinkObjectsEx(
|
|
&pTask->Hdr,
|
|
&pOtherTask->Hdr,
|
|
0x77c488ca,
|
|
RM_PRIVATE_ASSOC_LINK_TASKPENDINGON,
|
|
szASSOCFORMAT_LINK_TASKPENDINGON,
|
|
RM_PRIVATE_ASSOC_LINK_TASKBLOCKS,
|
|
szASSOCFORMAT_LINK_TASKBLOCKS,
|
|
pSR
|
|
);
|
|
#else // !RM_EXTRA_CHECKING
|
|
RmLinkObjects(&pTask->Hdr, &pOtherTask->Hdr, pSR);
|
|
#endif // !RM_EXTRA_CHECKING
|
|
|
|
ASSERTEX(pTask->linkFellowPendingTasks.Blink == NULL, pTask);
|
|
ASSERTEX(pTask->linkFellowPendingTasks.Flink == NULL, pTask);
|
|
InsertHeadList(
|
|
&pOtherTask->listTasksPendingOnMe,
|
|
&pTask->linkFellowPendingTasks
|
|
);
|
|
Status = NDIS_STATUS_PENDING;
|
|
|
|
} while(FALSE);
|
|
|
|
NdisReleaseSpinLock(&(pTask->Hdr.RmPrivateLock.OsLock));
|
|
NdisReleaseSpinLock(&(pOtherTask->Hdr.RmPrivateLock.OsLock));
|
|
|
|
RM_ASSERT_NOLOCKS(pSR);
|
|
|
|
EXIT()
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
RmCancelPendOnOtherTask(
|
|
IN PRM_TASK pTask,
|
|
IN PRM_TASK pOtherTask,
|
|
IN UINT_PTR UserParam,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Resume task pTask which is currently pending on pOtherTask.
|
|
|
|
Since no locks are held, pOtherTask needs to be specified, to make sure
|
|
that pTask is indeed pending on pOtherTask before canceling the pend.
|
|
|
|
If pTask is indeed pending on pOtherTask, this function will cause the
|
|
completion of the pend status with the specified user param.
|
|
|
|
Has no effect if the task is not pending.
|
|
|
|
Arguments:
|
|
|
|
pTask - Task to be "unpended"
|
|
pOtherTask - Task pTask is currently pending on.
|
|
UserParam - Passed to pTask's handler if and when pTask is resumed.
|
|
|
|
--*/
|
|
{
|
|
ENTER("RmCancelPendOnOtherTask", 0x6e113266)
|
|
NDIS_STATUS Status = NDIS_STATUS_FAILURE;
|
|
BOOLEAN fResumeTask = FALSE;
|
|
RM_ASSERT_NOLOCKS(pSR);
|
|
|
|
//
|
|
// WARNING: we break the locking rules here by getting the lock on
|
|
// both pTask and pOtherTask.
|
|
// TODO: consider acquiring them in order of increasing numerical value.
|
|
//
|
|
|
|
TR_INFO((
|
|
"CANCEL PEND of Task 0x%p (%s) on other Task 0x%p (%s); UserParam = 0x%lx\n",
|
|
pTask,
|
|
pTask->Hdr.szDescription,
|
|
pOtherTask,
|
|
pOtherTask->Hdr.szDescription,
|
|
UserParam
|
|
));
|
|
|
|
// With pTask locked, tmp ref the task it is pending on, if any...
|
|
//
|
|
{
|
|
NdisAcquireSpinLock(&(pTask->Hdr.RmPrivateLock.OsLock));
|
|
if (pOtherTask == pTask->pTaskIAmPendingOn)
|
|
{
|
|
RmTmpReferenceObject(&(pOtherTask->Hdr), pSR);
|
|
}
|
|
else
|
|
{
|
|
// Oops -- pTask is not pending on pOtherTask ...
|
|
//
|
|
pOtherTask = NULL;
|
|
}
|
|
NdisReleaseSpinLock(&(pTask->Hdr.RmPrivateLock.OsLock));
|
|
}
|
|
|
|
if (pOtherTask == NULL) return; // EARLY RETURN
|
|
|
|
|
|
NdisAcquireSpinLock(&(pOtherTask->Hdr.RmPrivateLock.OsLock));
|
|
NdisAcquireSpinLock(&(pTask->Hdr.RmPrivateLock.OsLock));
|
|
|
|
do
|
|
{
|
|
// Now that we have both task's locks, check again if pTask is pending
|
|
// on pOtherTask
|
|
//
|
|
if (pTask->pTaskIAmPendingOn != pOtherTask)
|
|
{
|
|
// Oops -- the situation is different than when we started -- quietly
|
|
// get out of here...
|
|
//
|
|
break;
|
|
}
|
|
|
|
pTask->pTaskIAmPendingOn = NULL;
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
RmUnlinkObjectsEx(
|
|
&pTask->Hdr,
|
|
&pOtherTask->Hdr,
|
|
0x6992b7a1,
|
|
RM_PRIVATE_ASSOC_LINK_TASKPENDINGON,
|
|
RM_PRIVATE_ASSOC_LINK_TASKBLOCKS,
|
|
pSR
|
|
);
|
|
#else // !RM_EXTRA_CHECKING
|
|
RmUnlinkObjects(&pTask->Hdr, &pOtherTask->Hdr, pSR);
|
|
#endif // !RM_EXTRA_CHECKING
|
|
|
|
RemoveEntryList(&pTask->linkFellowPendingTasks);
|
|
pTask->linkFellowPendingTasks.Flink = NULL;
|
|
pTask->linkFellowPendingTasks.Blink = NULL;
|
|
|
|
if (CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_PENDING))
|
|
{
|
|
fResumeTask = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We shouldn't get here -- after we are pending on another task...
|
|
//
|
|
ASSERTEX(!"Invalid state", pTask);
|
|
break;
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
NdisReleaseSpinLock(&(pTask->Hdr.RmPrivateLock.OsLock));
|
|
NdisReleaseSpinLock(&(pOtherTask->Hdr.RmPrivateLock.OsLock));
|
|
RmTmpDereferenceObject(&(pOtherTask->Hdr), pSR);
|
|
|
|
if (fResumeTask)
|
|
{
|
|
RmResumeTask(
|
|
pTask,
|
|
UserParam, // SuspendCompletionParam
|
|
pSR
|
|
);
|
|
}
|
|
|
|
RM_ASSERT_NOLOCKS(pSR);
|
|
|
|
EXIT()
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
RmInitializeHashTable(
|
|
PRM_HASH_INFO pHashInfo,
|
|
PVOID pAllocationContext,
|
|
PRM_HASH_TABLE pHashTable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize a hash table data structure.
|
|
Caller is responsible for serializing access to the hash table structure.
|
|
|
|
Arguments:
|
|
|
|
pHashInfo - Points to static information about the hash table
|
|
pAllocationContext - Passed to the allocation and deallocation functions
|
|
(pHashInfo->pfnTableAllocator and
|
|
pHashInfo0->pfnTableDeallocator) which are used to
|
|
dynamically grow /shrink the hash table.
|
|
pHashTable - Points to uninitialized memory that is to contain the
|
|
hash table.
|
|
--*/
|
|
{
|
|
|
|
NdisZeroMemory(pHashTable, sizeof(*pHashTable));
|
|
|
|
pHashTable->pHashInfo = pHashInfo;
|
|
pHashTable->pAllocationContext = pAllocationContext;
|
|
pHashTable->pTable = pHashTable->InitialTable;
|
|
pHashTable->TableLength = sizeof(pHashTable->InitialTable)
|
|
/sizeof(pHashTable->InitialTable[0]);
|
|
}
|
|
|
|
VOID
|
|
RmDeinitializeHashTable(
|
|
PRM_HASH_TABLE pHashTable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deinitialize a previously-initialized a hash table data structure.
|
|
There must be no items in the hash table when this function is called.
|
|
|
|
Caller is responsible for serializing access to the hash table structure.
|
|
|
|
Arguments:
|
|
|
|
pHashTable - Hash table to be deinitialized.
|
|
|
|
--*/
|
|
{
|
|
PRM_HASH_LINK *pTable = pHashTable->pTable;
|
|
|
|
ASSERTEX(pHashTable->NumItems == 0, pHashTable);
|
|
|
|
if (pTable != pHashTable->InitialTable)
|
|
{
|
|
NdisZeroMemory(pTable, pHashTable->TableLength*sizeof(*pTable));
|
|
|
|
pHashTable->pHashInfo->pfnTableDeallocator(
|
|
pTable,
|
|
pHashTable->pAllocationContext
|
|
);
|
|
}
|
|
|
|
NdisZeroMemory(pHashTable, sizeof(*pHashTable));
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
RmLookupHashTable(
|
|
PRM_HASH_TABLE pHashTable,
|
|
PRM_HASH_LINK ** pppLink,
|
|
PVOID pvRealKey
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Lookup an item in the hash table and/or find the place where the item
|
|
is to be inserted.
|
|
|
|
Caller is expected to serialize access to the hash table.
|
|
OK to use read-locks to serialize access.
|
|
|
|
Return value: TRUE if item found; false otherwise.
|
|
On return, *pppLink is set to a the location containing a pointer to
|
|
a RM_HASH_LINK. If the return value is TRUE, the latter pointer points
|
|
to the found RM_HASH_LINK. If the return value is FALSE, the location
|
|
is where the item is to be inserted, if required.
|
|
|
|
Arguments:
|
|
|
|
pHashTable - Hash table to look up
|
|
pppLink - place to store a pointer to a link which points
|
|
to an item (see above for details).
|
|
pvRealKey - Key used to lookup item.
|
|
|
|
Return Value:
|
|
|
|
TRUE if item is found
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
PRM_HASH_LINK *ppLink, pLink;
|
|
UINT LinksTraversed = 0;
|
|
UINT TableLength = pHashTable->TableLength;
|
|
PFN_RM_COMPARISON_FUNCTION pfnCompare = pHashTable->pHashInfo->pfnCompare;
|
|
BOOLEAN fRet = FALSE;
|
|
ULONG uHash = pHashTable->pHashInfo->pfnHash(pvRealKey);
|
|
|
|
for (
|
|
ppLink = pHashTable->pTable + (uHash%TableLength);
|
|
(pLink = *ppLink) != NULL;
|
|
ppLink = &(pLink->pNext), LinksTraversed++)
|
|
{
|
|
if (pLink->uHash == uHash
|
|
&& pfnCompare(pvRealKey, pLink))
|
|
{
|
|
// found it
|
|
//
|
|
fRet = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Update stats
|
|
//
|
|
rmUpdateHashTableStats(&pHashTable->Stats, LinksTraversed);
|
|
|
|
*pppLink = ppLink;
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
RmNextHashTableItem(
|
|
PRM_HASH_TABLE pHashTable,
|
|
PRM_HASH_LINK pCurrentLink, // OPTIONAL
|
|
PRM_HASH_LINK * ppNextLink
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find the first (if pCurrentLink is NULL) or "next" (if pCurrentLink is not NULL)
|
|
item in the hash table.
|
|
|
|
Caller is expected to serialize access to the hash table.
|
|
OK to use read-locks to serialize access.
|
|
|
|
NOTE: The "next" item returned is in no particular order.
|
|
|
|
Arguments:
|
|
|
|
pHashTable - Hash table to look up
|
|
pCurrentLink - if non-NULL, points to an existing hash link in the
|
|
hash table.
|
|
ppLinkLink - place to store a pointer to the link "after"
|
|
pCurrentLink, or the first link (if pCurrentLink is NULL).
|
|
|
|
Return Value:
|
|
|
|
TRUE if there is a "next" item.
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
PRM_HASH_LINK pLink, *ppLink, *ppLinkEnd;
|
|
UINT TableLength;
|
|
|
|
ppLink = pHashTable->pTable;
|
|
TableLength = pHashTable->TableLength;
|
|
ppLinkEnd = ppLink + TableLength;
|
|
|
|
if (pCurrentLink != NULL)
|
|
{
|
|
|
|
#if DBG
|
|
{
|
|
// Make sure this link is valid!
|
|
pLink = *(ppLink + (pCurrentLink->uHash % TableLength));
|
|
while (pLink != NULL && pLink != pCurrentLink)
|
|
{
|
|
pLink = pLink->pNext;
|
|
}
|
|
if (pLink != pCurrentLink)
|
|
{
|
|
ASSERTEX(!"Invalid pCurrentLink", pCurrentLink);
|
|
*ppNextLink = NULL;
|
|
return FALSE; // EARLY RETURN
|
|
}
|
|
}
|
|
#endif // DBG
|
|
|
|
if (pCurrentLink->pNext != NULL)
|
|
{
|
|
// Found a next link.
|
|
//
|
|
*ppNextLink = pCurrentLink->pNext;
|
|
return TRUE; // EARLY RETURN
|
|
}
|
|
else
|
|
{
|
|
// End of current bucket, move to next one.
|
|
// We check later if we've gone past the end of the table.
|
|
//
|
|
ppLink += (pCurrentLink->uHash % TableLength) + 1;
|
|
}
|
|
}
|
|
|
|
|
|
// Look for next non-null item.
|
|
//
|
|
for ( ; ppLink < ppLinkEnd; ppLink++)
|
|
{
|
|
pLink = *ppLink;
|
|
if (pLink != NULL)
|
|
{
|
|
*ppNextLink = pLink;
|
|
return TRUE; // EARLY RETURN
|
|
}
|
|
}
|
|
|
|
*ppNextLink = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VOID
|
|
RmAddHashItem(
|
|
PRM_HASH_TABLE pHashTable,
|
|
PRM_HASH_LINK * ppLink,
|
|
PRM_HASH_LINK pLink,
|
|
PVOID pvKey
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add an item to the hash table at the specified location.
|
|
Caller is expected to serialize access to the hash table.
|
|
|
|
Arguments:
|
|
|
|
pHashTable - Hash table in which to add item.
|
|
ppLink - Points to place within table to add new item.
|
|
pLink - New item to add.
|
|
pvKey - key associated with the item.
|
|
|
|
TODO: pvKey is only used to compute uHash -- consider passing in uHash directly.
|
|
|
|
--*/
|
|
{
|
|
pLink->uHash = pHashTable->pHashInfo->pfnHash(pvKey);
|
|
pLink->pNext = *ppLink;
|
|
*ppLink = pLink;
|
|
|
|
pHashTable->NumItems++;
|
|
|
|
// TODO: if required, resize
|
|
}
|
|
|
|
VOID
|
|
RmRemoveHashItem(
|
|
PRM_HASH_TABLE pHashTable,
|
|
PRM_HASH_LINK pLinkToRemove
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove an item from the hash table.
|
|
Caller is expected to serialize access to the hash table.
|
|
|
|
(debug only): Asserts if pLinkToRemove is no in the hash table.
|
|
|
|
Arguments:
|
|
|
|
pHashTable - Hash table in which to add item.
|
|
pLinkToRemove - Link to remove.
|
|
|
|
--*/
|
|
{
|
|
PRM_HASH_LINK *ppLink, pLink;
|
|
UINT TableLength = pHashTable->TableLength;
|
|
ULONG uHash = pLinkToRemove->uHash;
|
|
BOOLEAN fFound = FALSE;
|
|
|
|
for (
|
|
ppLink = pHashTable->pTable + (uHash%TableLength);
|
|
(pLink = *ppLink) != NULL;
|
|
ppLink = &(pLink->pNext))
|
|
{
|
|
if (pLink == pLinkToRemove)
|
|
{
|
|
// found it -- remove it and get out.
|
|
//
|
|
RM_PRIVATE_UNLINK_NEXT_HASH(pHashTable, ppLink);
|
|
pLink->pNext = NULL; // Important, so that enumeration works.
|
|
fFound=TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// TODO: if required, resize
|
|
|
|
ASSERT(fFound);
|
|
}
|
|
|
|
|
|
VOID
|
|
RmEnumHashTable(
|
|
PRM_HASH_TABLE pHashTable,
|
|
PFN_ENUM_HASH_TABLE pfnEnumerator,
|
|
PVOID pvContext,
|
|
PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Calls function pfnEnumerator for each item in the hash table.
|
|
Caller is expected to serialize access to the hash table.
|
|
|
|
Arguments:
|
|
|
|
pHashTable - Hash table to enumerate.
|
|
pfnEnumerator - Enumerator function.
|
|
pvContext - Opaque context passed to enumerator function.
|
|
|
|
--*/
|
|
{
|
|
PRM_HASH_LINK *ppLink, *ppLinkEnd;
|
|
|
|
ppLink = pHashTable->pTable;
|
|
ppLinkEnd = ppLink + pHashTable->TableLength;
|
|
|
|
for ( ; ppLink < ppLinkEnd; ppLink++)
|
|
{
|
|
PRM_HASH_LINK pLink = *ppLink;
|
|
while (pLink != NULL)
|
|
{
|
|
|
|
pfnEnumerator(
|
|
pLink,
|
|
pvContext,
|
|
pSR
|
|
);
|
|
|
|
pLink = pLink->pNext;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
RmEnumerateObjectsInGroup(
|
|
PRM_GROUP pGroup,
|
|
PFN_RM_GROUP_ENUMERATOR pfnEnumerator,
|
|
PVOID pvContext,
|
|
INT fStrong,
|
|
PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Calls function pfnEnumerator for each item in the group, until
|
|
the funcition return FALSE.
|
|
|
|
WARNING: Enumeration is "STRONG" -- the group lock
|
|
is held during the whole enumeration process. The
|
|
enumerator function is therefore called at DPR level, and more importantly,
|
|
the enumerator function avoid locking anything to avoid risk of deadlock.
|
|
Specifically, the enumerator function MUST NOT lock the object -- if any other
|
|
thread has called a group-related RM function with the object's lock held,
|
|
we WILL deadlock.
|
|
|
|
This function should only be used to access parts of the object that do
|
|
not need to be protected by the objects lock.
|
|
|
|
If locking needs to be performed, use RmWeakEnumerateObjectsInGroup.
|
|
|
|
Arguments:
|
|
|
|
pGroup - Hash table to enumerate.
|
|
pfnEnumerator - Enumerator function.
|
|
pvContext - Opaque context passed to enumerator function.
|
|
fStrong - MUST be TRUE.
|
|
--*/
|
|
{
|
|
|
|
if (fStrong)
|
|
{
|
|
RM_STRONG_ENUMERATION_CONTEXT Ctxt;
|
|
Ctxt.pfnObjEnumerator = pfnEnumerator;
|
|
Ctxt.pvCallerContext = pvContext;
|
|
Ctxt.fContinue = TRUE;
|
|
|
|
NdisAcquireSpinLock(&pGroup->OsLock);
|
|
|
|
RmEnumHashTable(
|
|
&pGroup->HashTable,
|
|
rmEnumObjectInGroupHashTable, // pfnEnumerator
|
|
&Ctxt, // context
|
|
pSR
|
|
);
|
|
|
|
NdisReleaseSpinLock(&pGroup->OsLock);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(!"Unimplemented");
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
RmWeakEnumerateObjectsInGroup(
|
|
PRM_GROUP pGroup,
|
|
PFN_RM_GROUP_ENUMERATOR pfnEnumerator,
|
|
PVOID pvContext,
|
|
PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Calls function pfnEnumerator for each item in the group, until
|
|
the funcition return FALSE.
|
|
|
|
Enumeration is "WEAK" -- the group lock is
|
|
NOT held the whole time, and is not held when the enumerator
|
|
function is called.
|
|
|
|
A snapshot of the entire group is first taken with the group lock held,
|
|
and each object is tempref'd. The group lock is then released and the
|
|
enumerator function is called for each object in the snapshot.
|
|
The objects are then derefd.
|
|
|
|
NOTE: It is possible that when the enumeration function is called for an
|
|
object, the object is no longer in the group. The enumeration function can
|
|
lock the object and check its internal state to determine if it is still
|
|
relevant to process the object.
|
|
|
|
Arguments:
|
|
|
|
pGroup - Hash table to enumerate.
|
|
pfnEnumerator - Enumerator function.
|
|
pvContext - Opaque context passed to enumerator function.
|
|
|
|
|
|
--*/
|
|
{
|
|
#define RM_SMALL_GROUP_SIZE 10
|
|
#define RM_MAX_ENUM_GROUP_SIZE 100000
|
|
PRM_OBJECT_HEADER *ppSnapshot;
|
|
PRM_OBJECT_HEADER SmallSnapshot[RM_SMALL_GROUP_SIZE];
|
|
UINT NumItems = pGroup->HashTable.NumItems;
|
|
|
|
do
|
|
{
|
|
RM_WEAK_ENUMERATION_CONTEXT Ctxt;
|
|
|
|
if (NumItems <= RM_SMALL_GROUP_SIZE)
|
|
{
|
|
if (NumItems == 0) break;
|
|
ppSnapshot = SmallSnapshot;
|
|
}
|
|
else if (NumItems > RM_MAX_ENUM_GROUP_SIZE)
|
|
{
|
|
// TODO: LOG_RETAIL_ERROR
|
|
ASSERT(FALSE);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
RM_ALLOC(
|
|
&(void* )ppSnapshot,
|
|
NumItems*sizeof(PRM_OBJECT_HEADER),
|
|
MTAG_RMINTERNAL
|
|
);
|
|
|
|
if (ppSnapshot == NULL)
|
|
{
|
|
ASSERT(FALSE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Ctxt.ppCurrent = ppSnapshot;
|
|
Ctxt.ppEnd = ppSnapshot+NumItems;
|
|
|
|
NdisAcquireSpinLock(&pGroup->OsLock);
|
|
|
|
RmEnumHashTable(
|
|
&pGroup->HashTable,
|
|
rmConstructGroupSnapshot, // pfnEnumerator
|
|
&Ctxt, // context
|
|
pSR
|
|
);
|
|
|
|
NdisReleaseSpinLock(&pGroup->OsLock);
|
|
|
|
ASSERT(Ctxt.ppCurrent >= ppSnapshot);
|
|
ASSERT(Ctxt.ppCurrent <= Ctxt.ppEnd);
|
|
|
|
// Fix up ppEnd to point to the last actually-filled pointer.
|
|
//
|
|
Ctxt.ppEnd = Ctxt.ppCurrent;
|
|
Ctxt.ppCurrent = ppSnapshot;
|
|
|
|
for (;Ctxt.ppCurrent < Ctxt.ppEnd; Ctxt.ppCurrent++)
|
|
{
|
|
pfnEnumerator(
|
|
*Ctxt.ppCurrent,
|
|
pvContext,
|
|
pSR
|
|
);
|
|
RmTmpDereferenceObject(*Ctxt.ppCurrent, pSR);
|
|
}
|
|
|
|
if (ppSnapshot != SmallSnapshot)
|
|
{
|
|
RM_FREE(ppSnapshot);
|
|
ppSnapshot = NULL;
|
|
}
|
|
|
|
} while (FALSE);
|
|
}
|
|
|
|
|
|
//=========================================================================
|
|
// L O C A L F U N C T I O N S
|
|
//=========================================================================
|
|
|
|
|
|
VOID
|
|
rmDerefObject(
|
|
PRM_OBJECT_HEADER pObject,
|
|
PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dereference object pObject. Deallocate it if the reference count goes to zero.
|
|
|
|
--*/
|
|
{
|
|
ULONG Refs;
|
|
ENTER("rmDerefObject", 0x5f9d81dd)
|
|
|
|
ASSERT(RM_OBJECT_IS_ALLOCATED(pObject));
|
|
|
|
//
|
|
// On entry, the ref count should be at-least 2 -- one the
|
|
// explicit ref added in RmAllocateObject, and the 2nd the ref due to
|
|
// the link to the parent.
|
|
//
|
|
// Exception to the above: if the object has no parent, the refcount should be
|
|
// at-least 1.
|
|
//
|
|
|
|
// Deref the ref added in RmAllocateObject, and if the ref count is now <=1,
|
|
// we actually unlink and free the object.
|
|
//
|
|
Refs = NdisInterlockedDecrement(&pObject->TotRefs);
|
|
|
|
if (Refs <= 1)
|
|
{
|
|
PRM_OBJECT_HEADER pParentObject;
|
|
RMPRIVATELOCK(pObject, pSR);
|
|
|
|
//
|
|
// Unlink from parent, if there is one...
|
|
//
|
|
|
|
pParentObject = pObject->pParentObject;
|
|
pObject->pParentObject = NULL;
|
|
|
|
#if RM_TRACK_OBJECT_TREE
|
|
// Verify that there are no siblings...
|
|
//
|
|
RETAILASSERTEX(IsListEmpty(&pObject->listChildren), pObject);
|
|
#endif // RM_TRACK_OBJECT_TREE
|
|
|
|
RMPRIVATEUNLOCK(pObject, pSR);
|
|
|
|
if (pParentObject != NULL)
|
|
{
|
|
ASSERTEX(!RMISALLOCATED(pObject), pObject);
|
|
|
|
ASSERTEX(Refs == 1, pObject);
|
|
|
|
#if RM_TRACK_OBJECT_TREE
|
|
RMPRIVATELOCK(pParentObject, pSR);
|
|
|
|
// Remove object from parent's list of children.
|
|
//
|
|
RETAILASSERTEX(
|
|
!IsListEmpty(&pParentObject->listChildren),
|
|
pObject);
|
|
RemoveEntryList(&pObject->linkSiblings);
|
|
|
|
RMPRIVATEUNLOCK(pParentObject, pSR);
|
|
#endif // RM_TRACK_OBJECT_TREE
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
RmUnlinkObjectsEx(
|
|
pObject,
|
|
pParentObject,
|
|
0xac73e169,
|
|
RM_PRIVATE_ASSOC_LINK_CHILDOF,
|
|
RM_PRIVATE_ASSOC_LINK_PARENTOF,
|
|
pSR
|
|
);
|
|
#else // !RM_EXTRA_CHECKING
|
|
RmUnlinkObjects(pObject, pParentObject, pSR);
|
|
#endif // !RM_EXTRA_CHECKING
|
|
|
|
}
|
|
else if (Refs == 0)
|
|
{
|
|
//
|
|
// Free to deallocate this thing...
|
|
//
|
|
|
|
ASSERTEX(!RMISALLOCATED(pObject), pObject);
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
rmDbgDeinitializeDiagnosticInfo(pObject, pSR);
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
RM_MARK_OBJECT_AS_DEALLOCATED(pObject);
|
|
|
|
if (pObject->pStaticInfo->pfnDelete!= NULL)
|
|
{
|
|
|
|
TR_INFO((
|
|
"Actually freeing 0x%p (%s)\n",
|
|
pObject,
|
|
pObject->szDescription
|
|
));
|
|
|
|
pObject->pStaticInfo->pfnDelete(pObject, pSR);
|
|
}
|
|
}
|
|
}
|
|
|
|
EXIT()
|
|
}
|
|
|
|
VOID
|
|
rmLock(
|
|
PRM_LOCK pLock,
|
|
#if RM_EXTRA_CHECKING
|
|
UINT uLocID,
|
|
PFNLOCKVERIFIER pfnVerifier,
|
|
PVOID pVerifierContext,
|
|
#endif //RM_EXTRA_CHECKING
|
|
PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Lock pLock.
|
|
|
|
Arguments:
|
|
|
|
pLock - Lock to lock.
|
|
LocID - Arbitrary ID, typically representing the source location
|
|
from which this function is called.
|
|
|
|
Following are for debug only:
|
|
|
|
pfnVerifier - Optional function that is called just after locking
|
|
pfnVerifierContext - Passed in call to pfnVerifier
|
|
|
|
--*/
|
|
{
|
|
//UINT Level = pSR->LockInfo.CurrentLevel;
|
|
RM_LOCKING_INFO li;
|
|
|
|
RETAILASSERTEX(pLock->Level > pSR->LockInfo.CurrentLevel, pLock);
|
|
RETAILASSERTEX(pSR->LockInfo.pNextFree < pSR->LockInfo.pLast, pLock);
|
|
|
|
pSR->LockInfo.CurrentLevel = pLock->Level;
|
|
|
|
// Save information about this lock in the stack record.
|
|
//
|
|
li.pLock = pLock;
|
|
#if RM_EXTRA_CHECKING
|
|
li.pfnVerifier = pfnVerifier;
|
|
li.pVerifierContext = pVerifierContext;
|
|
#endif //RM_EXTRA_CHECKING
|
|
*(pSR->LockInfo.pNextFree++) = li; // struct copy.
|
|
|
|
// Get the lock.
|
|
// TODO: uncomment the following optimization...
|
|
//if (Level)
|
|
//{
|
|
// NdisDprAcquireSpinLock(&pLock->OsLock);
|
|
//}
|
|
//else
|
|
//{
|
|
NdisAcquireSpinLock(&pLock->OsLock);
|
|
//}
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
|
|
ASSERTEX(pLock->DbgInfo.uLocID == 0, pLock);
|
|
ASSERTEX(pLock->DbgInfo.pSR == NULL, pLock);
|
|
pLock->DbgInfo.uLocID = uLocID;
|
|
pLock->DbgInfo.pSR = pSR;
|
|
// Call the verifier routine if there is one.
|
|
//
|
|
if (pfnVerifier)
|
|
{
|
|
pfnVerifier(pLock, TRUE, pVerifierContext, pSR);
|
|
}
|
|
#endif //RM_EXTRA_CHECKING
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
rmUnlock(
|
|
PRM_LOCK pLock,
|
|
PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unlock pLock.
|
|
|
|
Debug only: if there is a verifier function associated with this lock
|
|
we call it just before unlocking pLock.
|
|
|
|
Arguments:
|
|
|
|
pLock - Lock to unlock.
|
|
|
|
--*/
|
|
{
|
|
RM_LOCKING_INFO * pLI;
|
|
pSR->LockInfo.pNextFree--;
|
|
pLI = pSR->LockInfo.pNextFree;
|
|
RETAILASSERTEX(pLI->pLock == pLock, pLock);
|
|
|
|
ASSERTEX(pLock->DbgInfo.pSR == pSR, pLock);
|
|
ASSERTEX(pLock->Level == pSR->LockInfo.CurrentLevel, pLock);
|
|
|
|
pLI->pLock = NULL;
|
|
|
|
if (pLI > pSR->LockInfo.pFirst)
|
|
{
|
|
PRM_LOCK pPrevLock = (pLI-1)->pLock;
|
|
pSR->LockInfo.CurrentLevel = pPrevLock->Level;
|
|
ASSERTEX(pPrevLock->DbgInfo.pSR == pSR, pPrevLock);
|
|
}
|
|
else
|
|
{
|
|
pSR->LockInfo.CurrentLevel = 0;
|
|
}
|
|
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
|
|
// Call the verifier routine if there is one.
|
|
//
|
|
if (pLI->pfnVerifier)
|
|
{
|
|
pLI->pfnVerifier(pLock, FALSE, pLI->pVerifierContext, pSR);
|
|
pLI->pfnVerifier = NULL;
|
|
pLI->pVerifierContext = NULL;
|
|
}
|
|
pLock->DbgInfo.uLocID = 0;
|
|
pLock->DbgInfo.pSR = NULL;
|
|
|
|
#endif //RM_EXTRA_CHECKING
|
|
|
|
|
|
// Release the lock.
|
|
//
|
|
NdisReleaseSpinLock(&pLock->OsLock);
|
|
}
|
|
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
ULONG
|
|
rmPrivateLockVerifier(
|
|
PRM_LOCK pLock,
|
|
BOOLEAN fLock,
|
|
PVOID pContext,
|
|
PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
(Debug only)
|
|
|
|
The Verifier function for an object's RmPrivateLock.
|
|
|
|
Arguments:
|
|
|
|
pLock - Lock being locked/unlocked
|
|
fLock - TRUE if lock has just been locked.
|
|
FALSE if lock is about to be unlocked.
|
|
Return Value:
|
|
|
|
Unused: TODO make return value VOID.
|
|
|
|
--*/
|
|
{
|
|
ENTER("rmPrivateLockVerifier", 0xc3b63ac5)
|
|
TR_VERB(("Called with pLock=0x%p, fLock=%lu, pContext=%p\n",
|
|
pLock, fLock, pContext, pSR));
|
|
EXIT()
|
|
|
|
return 0;
|
|
}
|
|
|
|
ULONG
|
|
rmVerifyObjectState(
|
|
PRM_LOCK pLock,
|
|
BOOLEAN fLock,
|
|
PVOID pContext,
|
|
PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
(Debug only)
|
|
|
|
Uses the object's verification function (if there is one) to
|
|
compute a signature that is checked each time the object is locked,
|
|
and is updated each time the object is unlocked. Assert if this signature
|
|
has changed while the object was supposedly unlocked.
|
|
|
|
Also: Update RM_OBJECT_HEADER.pDiagInfo->PrevState if there is been a
|
|
change of state while the object was locked.
|
|
|
|
Arguments:
|
|
|
|
pLock - Lock being locked/unlocked
|
|
fLock - TRUE if lock has just been locked.
|
|
FALSE if lock is about to be unlocked.
|
|
pContext - Actually pointer to object being locked.
|
|
|
|
Return Value:
|
|
|
|
Unused: TODO make return value VOID.
|
|
|
|
--*/
|
|
{
|
|
PRM_OBJECT_HEADER pObj = (PRM_OBJECT_HEADER) pContext;
|
|
PRM_OBJECT_DIAGNOSTIC_INFO pDiagInfo = pObj->pDiagInfo;
|
|
ULONG NewChecksum;
|
|
ENTER("rmVerifyObjectState", 0xb8ff7a67)
|
|
TR_VERB(("Called with pLock=0x%p, fLock=%lu, pObj=%p\n",
|
|
pLock, fLock, pObj, pSR));
|
|
|
|
if (pDiagInfo != NULL
|
|
&& !(pDiagInfo->DiagState & fRM_PRIVATE_DISABLE_LOCK_CHECKING))
|
|
|
|
{
|
|
|
|
// Compute the new checksum and as part of that call the
|
|
// object-specific verifier if there is one....
|
|
//
|
|
{
|
|
PFNLOCKVERIFIER pfnVerifier;
|
|
|
|
// We verify that the objset-specific state was not modified
|
|
// without the lock held. This is done by including the object-specific
|
|
// state in the checksum computation.
|
|
//
|
|
NewChecksum = pObj->State;
|
|
|
|
// Then, if the object has a verifier function, we call it, and
|
|
// fold in the return value into the checkum.
|
|
//
|
|
pfnVerifier = pObj->pStaticInfo->pfnLockVerifier;
|
|
if (pfnVerifier != NULL)
|
|
{
|
|
NewChecksum ^= pfnVerifier(pLock, fLock, pObj, pSR);
|
|
|
|
}
|
|
}
|
|
|
|
if (fLock) // We've just locked the object.
|
|
{
|
|
|
|
// First thing we do is to save the current value of pObj->State in
|
|
// the TmpState location -- we'll look at it again on unlocking.
|
|
//
|
|
pDiagInfo->TmpState = pObj->State;
|
|
|
|
|
|
// Now we compare the new checksum value with the value that wase
|
|
// saved the last time this object was locked...
|
|
// Special case: old Checksum was 0 -- as it is on initialization.
|
|
//
|
|
if (NewChecksum != pDiagInfo->Checksum && pDiagInfo->Checksum)
|
|
{
|
|
TR_WARN((
|
|
"Object 0x%p (%s) possibly modified without lock held!\n",
|
|
pObj,
|
|
pObj->szDescription
|
|
));
|
|
|
|
// Unfortunately we hit this assert because there are places where
|
|
// the same lock is shared by many objects and
|
|
#if 0
|
|
// Give users the option to ignore further validation on this
|
|
// object.
|
|
//
|
|
TR_FATAL((
|
|
"To skip this assert, type \"ed 0x%p %lx; g\"\n",
|
|
&pDiagInfo->DiagState,
|
|
pDiagInfo->DiagState | fRM_PRIVATE_DISABLE_LOCK_CHECKING
|
|
));
|
|
ASSERTEX(!"Object was modified without lock held!", pObj);
|
|
#endif // 0
|
|
}
|
|
}
|
|
else // We're just about to unlock the object....
|
|
{
|
|
// Update the signature...
|
|
//
|
|
pDiagInfo->Checksum = NewChecksum;
|
|
|
|
// If there has been a change in state between locking and unlockng
|
|
// this object, save the previous state.
|
|
//
|
|
if (pDiagInfo->TmpState != pObj->State)
|
|
{
|
|
pDiagInfo->PrevState = pDiagInfo->TmpState;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
EXIT()
|
|
|
|
return 0;
|
|
}
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
VOID
|
|
rmEndTask(
|
|
PRM_TASK pTask,
|
|
NDIS_STATUS Status,
|
|
PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send the RM_TASKOP_END to the task handler, and resume any tasks pending on
|
|
pTask.
|
|
|
|
Arguments:
|
|
|
|
pTask - Task to end.
|
|
Status - Completion status -- passed on to the task handler.
|
|
|
|
--*/
|
|
{
|
|
ENTER("rmEndtask", 0x5060d952)
|
|
PRM_TASK pPendingTask;
|
|
RM_ASSERT_NOLOCKS(pSR);
|
|
|
|
TR_INFO((
|
|
"ENDING Task 0x%p (%s); Status = 0x%lx\n",
|
|
pTask,
|
|
pTask->Hdr.szDescription,
|
|
Status
|
|
));
|
|
|
|
// TODO: could change behavior so that we use the return value, but
|
|
// currently we ignore it...
|
|
//
|
|
pTask->pfnHandler(
|
|
pTask,
|
|
RM_TASKOP_END,
|
|
Status, // UserParam is overloaded here.
|
|
pSR
|
|
);
|
|
|
|
RM_ASSERT_NOLOCKS(pSR);
|
|
|
|
do
|
|
{
|
|
pPendingTask = NULL;
|
|
|
|
RMPRIVATELOCK(&pTask->Hdr, pSR);
|
|
if (!IsListEmpty(&pTask->listTasksPendingOnMe))
|
|
{
|
|
pPendingTask = CONTAINING_RECORD(
|
|
(pTask->listTasksPendingOnMe.Flink),
|
|
RM_TASK,
|
|
linkFellowPendingTasks
|
|
);
|
|
RmTmpReferenceObject(&pPendingTask->Hdr, pSR);
|
|
}
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
|
|
|
|
if (pPendingTask != NULL)
|
|
{
|
|
|
|
RmCancelPendOnOtherTask(
|
|
pPendingTask,
|
|
pTask,
|
|
Status,
|
|
pSR
|
|
);
|
|
RmTmpDereferenceObject(&pPendingTask->Hdr, pSR);
|
|
}
|
|
|
|
}
|
|
while(pPendingTask != NULL);
|
|
|
|
RM_ASSERT_NOLOCKS(pSR);
|
|
|
|
EXIT()
|
|
}
|
|
|
|
|
|
NDIS_STATUS
|
|
rmAllocatePrivateTask(
|
|
IN PRM_OBJECT_HEADER pParentObject,
|
|
IN PFN_RM_TASK_HANDLER pfnHandler,
|
|
IN UINT Timeout,
|
|
IN const char * szDescription, OPTIONAL
|
|
OUT PRM_TASK *ppTask,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate and initialize a task of subtype RM_PRIVATE_TASK.
|
|
|
|
Arguments:
|
|
|
|
pParentObject - Object that is to be the parent of the allocated task.
|
|
pfnHandler - The task handler for the task.
|
|
Timeout - Unused.
|
|
szDescription - Text describing this task.
|
|
ppTask - Place to store pointer to the new task.
|
|
|
|
Return Value:
|
|
|
|
NDIS_STATUS_SUCCESS if we could allocate and initialize the task.
|
|
NDIS_STATUS_RESOURCES otherwise
|
|
|
|
--*/
|
|
{
|
|
RM_PRIVATE_TASK *pRmTask;
|
|
NDIS_STATUS Status;
|
|
BOOLEAN fBackupTask = FALSE;
|
|
|
|
RM_ALLOCSTRUCT(pRmTask, MTAG_TASK); // TODO use lookaside lists.
|
|
|
|
*ppTask = NULL;
|
|
|
|
|
|
if (pRmTask == NULL)
|
|
{
|
|
pRmTask = (RM_PRIVATE_TASK*) arpGetBackupTask(&ArpGlobals);
|
|
fBackupTask = TRUE;
|
|
}
|
|
|
|
|
|
if (pRmTask != NULL)
|
|
{
|
|
RM_ZEROSTRUCT(pRmTask);
|
|
|
|
RmInitializeTask(
|
|
&(pRmTask->TskHdr),
|
|
pParentObject,
|
|
pfnHandler,
|
|
&RmPrivateTasks_StaticInfo,
|
|
szDescription,
|
|
Timeout,
|
|
pSR
|
|
);
|
|
*ppTask = &(pRmTask->TskHdr);
|
|
|
|
if (fBackupTask == TRUE)
|
|
{
|
|
MARK_TASK_AS_BACKUP(&pRmTask->TskHdr);
|
|
}
|
|
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
Status = NDIS_STATUS_RESOURCES;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NDIS_STATUS
|
|
rmTaskUnloadGroup(
|
|
IN struct _RM_TASK * pTask,
|
|
IN RM_TASK_OPERATION Code,
|
|
IN UINT_PTR UserParam,
|
|
IN PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This task is responsible for unloading all the objects in the group.
|
|
|
|
pTask is a pointer to TASK_UNLOADGROUP, and that structure is expected
|
|
to be initialized, including containing the pGroup to unload.
|
|
|
|
Arguments:
|
|
|
|
UserParam for (Code == RM_TASKOP_START) : unused
|
|
|
|
--*/
|
|
{
|
|
NDIS_STATUS Status = NDIS_STATUS_FAILURE;
|
|
TASK_UNLOADGROUP *pMyTask = (TASK_UNLOADGROUP*) pTask;
|
|
PRM_GROUP pGroup = pMyTask->pGroup;
|
|
BOOLEAN fContinueUnload = FALSE;
|
|
ENTER("TaskUnloadGroup", 0x964ee422)
|
|
|
|
|
|
enum
|
|
{
|
|
PEND_WaitOnOtherTask,
|
|
PEND_UnloadObject
|
|
};
|
|
|
|
switch(Code)
|
|
{
|
|
|
|
case RM_TASKOP_START:
|
|
{
|
|
// If there is already an unload task bound to pGroup, we
|
|
// pend on it.
|
|
//
|
|
NdisAcquireSpinLock(&pGroup->OsLock);
|
|
if (pGroup->pUnloadTask != NULL)
|
|
{
|
|
PRM_TASK pOtherTask = pGroup->pUnloadTask;
|
|
TR_WARN(("unload task 0x%p already bound to pGroup 0x%p; pending on it.\n",
|
|
pOtherTask, pGroup));
|
|
|
|
RmTmpReferenceObject(&pOtherTask->Hdr, pSR);
|
|
NdisReleaseSpinLock(&pGroup->OsLock);
|
|
RmPendTaskOnOtherTask(
|
|
pTask,
|
|
PEND_WaitOnOtherTask,
|
|
pOtherTask,
|
|
pSR
|
|
);
|
|
RmTmpDereferenceObject(&pOtherTask->Hdr, pSR);
|
|
Status = NDIS_STATUS_PENDING;
|
|
break;
|
|
}
|
|
else if (!pGroup->fEnabled)
|
|
{
|
|
//
|
|
// Presumably this group has already been unloaded of all objects
|
|
// and is simply sitting around. We complete right away.
|
|
//
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We're the 1st ones here -- continue on to unloading objects, if
|
|
// any...
|
|
//
|
|
pGroup->pUnloadTask = pTask;
|
|
pGroup->fEnabled = FALSE; // This will prevent new objects from
|
|
// being added and from the hash table
|
|
// itself from changing size.
|
|
pMyTask->uIndex = 0; // This keeps track of where we are in the
|
|
// hash table.
|
|
|
|
fContinueUnload = TRUE;
|
|
}
|
|
NdisReleaseSpinLock(&pGroup->OsLock);
|
|
}
|
|
break;
|
|
|
|
case RM_TASKOP_PENDCOMPLETE:
|
|
{
|
|
|
|
switch(RM_PEND_CODE(pTask))
|
|
{
|
|
case PEND_WaitOnOtherTask:
|
|
{
|
|
//
|
|
// Nothing to do -- finish task.
|
|
//
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case PEND_UnloadObject:
|
|
{
|
|
//
|
|
// Just done unloading an object; unload another if required.
|
|
//
|
|
fContinueUnload = TRUE;
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERTEX(!"Unknown pend code!", pTask);
|
|
break;
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
|
|
case RM_TASKOP_END:
|
|
{
|
|
BOOLEAN fSignal;
|
|
NdisAcquireSpinLock(&pGroup->OsLock);
|
|
|
|
// Clear ourselves from pGroup, if we're there.
|
|
//
|
|
if (pGroup->pUnloadTask == pTask)
|
|
{
|
|
pGroup->pUnloadTask = NULL;
|
|
}
|
|
fSignal = pMyTask->fUseEvent;
|
|
pMyTask->fUseEvent = FALSE;
|
|
NdisReleaseSpinLock(&pGroup->OsLock);
|
|
|
|
if (fSignal)
|
|
{
|
|
NdisSetEvent(&pMyTask->BlockEvent);
|
|
}
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERTEX(!"Unknown task op", pTask);
|
|
break;
|
|
|
|
} // switch (Code)
|
|
|
|
|
|
if (fContinueUnload)
|
|
{
|
|
do {
|
|
PRM_HASH_LINK *ppLink, *ppLinkEnd;
|
|
UINT uIndex;
|
|
PRM_HASH_LINK pLink;
|
|
PRM_OBJECT_HEADER pObj;
|
|
PRM_TASK pUnloadObjectTask;
|
|
|
|
NdisAcquireSpinLock(&pGroup->OsLock);
|
|
|
|
uIndex = pMyTask->uIndex;
|
|
|
|
//
|
|
// With fEnabled set to FALSE by us, we expect the following:
|
|
// (a) pHashTable->pTable is going to stay the same size.
|
|
// (b) No items are going to be added or removed by anyone else.
|
|
//
|
|
|
|
// Find the next non-empty hash table entry, starting at
|
|
// offset pMyTask->uIndex.
|
|
//
|
|
ASSERTEX(!pGroup->fEnabled, pGroup);
|
|
ASSERTEX(uIndex <= pGroup->HashTable.TableLength, pGroup);
|
|
ppLinkEnd = ppLink = pGroup->HashTable.pTable;
|
|
ppLink += uIndex;
|
|
ppLinkEnd += pGroup->HashTable.TableLength;
|
|
while (ppLink < ppLinkEnd && *ppLink == NULL)
|
|
{
|
|
ppLink++;
|
|
}
|
|
|
|
// Update index to our current position in the hash table.
|
|
//
|
|
pMyTask->uIndex = (UINT)(ppLink - pGroup->HashTable.pTable);
|
|
|
|
if (ppLink >= ppLinkEnd)
|
|
{
|
|
//
|
|
// We're done...
|
|
//
|
|
NdisReleaseSpinLock(&pGroup->OsLock);
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Found another object to unload...
|
|
// We'll allocate a task (pUnloadObjectTask) to unload that object,
|
|
// pend ourselves on it, and then start it.
|
|
//
|
|
//
|
|
|
|
pLink = *ppLink;
|
|
pObj = CONTAINING_RECORD(pLink, RM_OBJECT_HEADER, HashLink);
|
|
|
|
if (RMISALLOCATED(pTask->Hdr.pParentObject)== FALSE)
|
|
{
|
|
//
|
|
// This object is already de-allocated. Continue past this one.
|
|
//
|
|
NdisReleaseSpinLock (&pGroup->OsLock);
|
|
continue;
|
|
}
|
|
|
|
RmTmpReferenceObject(pObj, pSR);
|
|
ASSERT(pObj->pStaticInfo == pGroup->pStaticInfo);
|
|
NdisReleaseSpinLock(&pGroup->OsLock);
|
|
|
|
Status = pMyTask->pfnUnloadTaskAllocator(
|
|
pObj, // pParentObject,
|
|
pMyTask->pfnTaskUnloadObjectHandler, // pfnHandler,
|
|
0, // Timeout,
|
|
"Task:Unload Object",
|
|
&pUnloadObjectTask,
|
|
pSR
|
|
);
|
|
if (FAIL(Status))
|
|
{
|
|
// Aargh... we couldn't allocate a task to unload this object.
|
|
// We'll return quietly, leaving the other objects intact...
|
|
//
|
|
ASSERTEX(!"Couldn't allocat unload task for object.", pObj);
|
|
RmTmpDereferenceObject(pObj, pSR);
|
|
break;
|
|
}
|
|
|
|
RmTmpDereferenceObject(pObj, pSR);
|
|
|
|
#if OBSOLETE // See 03/26/1999 notes.txt entry "Some proposed ..."
|
|
|
|
RmPendTaskOnOtherTask(
|
|
pTask,
|
|
PEND_UnloadObject,
|
|
pUnloadObjectTask, // task to pend on
|
|
pSR
|
|
);
|
|
|
|
(void)RmStartTask(
|
|
pUnloadObjectTask,
|
|
0, // UserParam (unused)
|
|
pSR
|
|
);
|
|
|
|
Status = NDIS_STATUS_PENDING;
|
|
|
|
#else // !OBSOLETE
|
|
RmTmpReferenceObject(&pUnloadObjectTask->Hdr, pSR);
|
|
RmStartTask(
|
|
pUnloadObjectTask,
|
|
0, // UserParam (unused)
|
|
pSR
|
|
);
|
|
Status = RmPendOnOtherTaskV2(
|
|
pTask,
|
|
PEND_UnloadObject,
|
|
pUnloadObjectTask,
|
|
pSR
|
|
);
|
|
RmTmpDereferenceObject(&pUnloadObjectTask->Hdr, pSR);
|
|
if (PEND(Status))
|
|
{
|
|
break;
|
|
}
|
|
#endif // !OBSOLETE
|
|
|
|
}
|
|
#if OBSOLETE // See 03/26/1999 notes.txt entry "Some proposed ..."
|
|
while (FALSE);
|
|
#else // !OBSOLETE
|
|
while (TRUE);
|
|
#endif // !OBSOLETE
|
|
|
|
} // if(fContinueUnload)
|
|
|
|
RM_ASSERT_NOLOCKS(pSR);
|
|
EXIT()
|
|
|
|
return Status;
|
|
}
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
|
|
BOOLEAN
|
|
rmDbgAssociationCompareKey(
|
|
PVOID pKey,
|
|
PRM_HASH_LINK pItem
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Comparison function used to test for exact equality of items
|
|
in the debug association table.
|
|
|
|
Arguments:
|
|
|
|
pKey - Actually a pointer to an RM_PRIVATE_DBG_ASSOCIATION structure.
|
|
pItem - Points to RM_PRIVATE_DBG_ASSOCIATION.HashLink.
|
|
|
|
Return Value:
|
|
|
|
TRUE IFF the (Entity1, Entity2 and AssociationID) fields of the key
|
|
exactly match the corresponding fields of
|
|
CONTAINING_RECORD(pItem, RM_PRIVATE_DBG_ASSOCIATION, HashLink);
|
|
|
|
--*/
|
|
{
|
|
RM_PRIVATE_DBG_ASSOCIATION *pA =
|
|
CONTAINING_RECORD(pItem, RM_PRIVATE_DBG_ASSOCIATION, HashLink);
|
|
|
|
// pKey is actually a RM_PRIVATE_DBG_ASSOCIATION structure.
|
|
//
|
|
RM_PRIVATE_DBG_ASSOCIATION *pTrueKey = (RM_PRIVATE_DBG_ASSOCIATION *) pKey;
|
|
|
|
|
|
if ( pA->Entity1 == pTrueKey->Entity1
|
|
&& pA->Entity2 == pTrueKey->Entity2
|
|
&& pA->AssociationID == pTrueKey->AssociationID)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
ULONG
|
|
rmDbgAssociationHash(
|
|
PVOID pKey
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Hash generating function used to compute a ULONG-sized hash from
|
|
key, which is actually a pointer to an RM_PRIVATE_DBG_ASSOCIATION structure.
|
|
|
|
Arguments:
|
|
|
|
pKey - Actually a pointer to an RM_PRIVATE_DBG_ASSOCIATION structure.
|
|
|
|
Return Value:
|
|
|
|
ULONG-sized hash generated from the (Entity1, Entity2 and AssociationID)
|
|
fields of the key.
|
|
|
|
--*/
|
|
{
|
|
// pKey is actually a RM_PRIVATE_DBG_ASSOCIATION structure.
|
|
//
|
|
RM_PRIVATE_DBG_ASSOCIATION *pTrueKey = (RM_PRIVATE_DBG_ASSOCIATION *) pKey;
|
|
ULONG_PTR big_hash;
|
|
|
|
big_hash = pTrueKey->Entity1;
|
|
big_hash ^= pTrueKey->Entity2;
|
|
big_hash ^= pTrueKey->AssociationID;
|
|
|
|
// Warning: Below, the return value would be truncated in 64-bit.
|
|
// That tolerable because after all this is just a hash.
|
|
// TODO: for 64-bit, consider xoring hi- and lo- DWORD instead of truncationg.
|
|
//
|
|
|
|
return (ULONG) big_hash;
|
|
}
|
|
|
|
|
|
// Static hash information use for the hash table (in the diagnostic information
|
|
// of each object) that keeps track of associations.
|
|
//
|
|
RM_HASH_INFO
|
|
rmDbgAssociation_HashInfo =
|
|
{
|
|
NULL, // pfnTableAllocator
|
|
|
|
NULL, // pfnTableDeallocator
|
|
|
|
rmDbgAssociationCompareKey, // fnCompare
|
|
|
|
// Function to generate a ULONG-sized hash.
|
|
//
|
|
rmDbgAssociationHash // pfnHash
|
|
|
|
};
|
|
|
|
|
|
VOID
|
|
rmDbgInitializeDiagnosticInfo(
|
|
PRM_OBJECT_HEADER pObject,
|
|
PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate and initialize the diagnostic information associated with
|
|
object pObject. This includes initializing the hash table used to keep
|
|
track of arbitrary associations.
|
|
|
|
--*/
|
|
{
|
|
ENTER("InitializeDiagnosticInfo", 0x55db57a2)
|
|
PRM_OBJECT_DIAGNOSTIC_INFO pDiagInfo;
|
|
|
|
TR_VERB((" pObj=0x%p\n", pObject));
|
|
// TODO: use lookaside lists for the allocation of these objects.
|
|
//
|
|
RM_ALLOCSTRUCT(pDiagInfo, MTAG_DBGINFO);
|
|
if (pDiagInfo != NULL)
|
|
{
|
|
RM_ZEROSTRUCT(pDiagInfo);
|
|
|
|
NdisAllocateSpinLock(&pDiagInfo->OsLock);
|
|
RmInitializeHashTable(
|
|
&rmDbgAssociation_HashInfo,
|
|
NULL, // pAllocationContext
|
|
&pDiagInfo->AssociationTable
|
|
);
|
|
pObject->pDiagInfo = pDiagInfo;
|
|
pDiagInfo->pOwningObject = pObject;
|
|
|
|
// Initialize the per-object log list.
|
|
//
|
|
InitializeListHead(&pDiagInfo->listObjectLog);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
rmDbgFreeObjectLogEntries(
|
|
LIST_ENTRY *pObjectLog
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove and free all items from the object log pObjectLog.
|
|
It is assumed that no one is trying to add items to this log at this time.
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY *pLink=NULL, *pNextLink=NULL;
|
|
|
|
if (IsListEmpty(pObjectLog)) return; // EARLY RETURN
|
|
|
|
NdisAcquireSpinLock(&RmGlobals.GlobalOsLock);
|
|
|
|
for(
|
|
pLink = pObjectLog->Flink;
|
|
pLink != pObjectLog;
|
|
pLink = pNextLink)
|
|
{
|
|
RM_DBG_LOG_ENTRY *pLogEntry;
|
|
LIST_ENTRY *pLinkGlobalLog;
|
|
|
|
pLogEntry = CONTAINING_RECORD(pLink, RM_DBG_LOG_ENTRY, linkObjectLog);
|
|
pLinkGlobalLog = &pLogEntry->linkGlobalLog;
|
|
|
|
// Remove entry from global log.
|
|
// We don't bother removing the entry from the local log list, because
|
|
// it's going away anyway.
|
|
//
|
|
RemoveEntryList(pLinkGlobalLog);
|
|
|
|
// Move to next entry in object's log (which may not be the next entry
|
|
// in the global log).
|
|
//
|
|
pNextLink = pLink->Flink;
|
|
|
|
// Free the buffer in the log entry, if any.
|
|
// TODO: need to use log buffer deallocation function --
|
|
// See notes.txt 03/07/1999 entry "Registering root objects with RM".
|
|
// For now we assume the this memory was allocated using
|
|
// NdisAllocateMemory[WithTag].
|
|
//
|
|
if (pLogEntry->pvBuf != NULL)
|
|
{
|
|
NdisFreeMemory(pLogEntry->pvBuf, 0, 0);
|
|
}
|
|
|
|
// Free the log entry itself.
|
|
//
|
|
rmDbgDeallocateLogEntry(pLogEntry);
|
|
|
|
}
|
|
NdisReleaseSpinLock(&RmGlobals.GlobalOsLock);
|
|
}
|
|
|
|
|
|
VOID
|
|
rmDbgDeinitializeDiagnosticInfo(
|
|
PRM_OBJECT_HEADER pObject,
|
|
PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
(Debug only)
|
|
|
|
Deinitialize and free the diagnostic information associated with
|
|
object pObject. This includes verifying that there are no remaining
|
|
associations and links.
|
|
--*/
|
|
{
|
|
ENTER("DeinitializeDiagnosticInfo", 0xa969291f)
|
|
PRM_OBJECT_DIAGNOSTIC_INFO pDiagInfo = pObject->pDiagInfo;
|
|
|
|
TR_VERB((" pObj=0x%p\n", pObject));
|
|
if (pDiagInfo != NULL)
|
|
{
|
|
|
|
// Free all the per-object log entries.
|
|
// Note: no one should be trying to add items to this log at this time
|
|
// because we're aboute to deallocate this object.
|
|
//
|
|
{
|
|
rmDbgFreeObjectLogEntries(&pDiagInfo->listObjectLog);
|
|
RM_ZEROSTRUCT(&pDiagInfo->listObjectLog);
|
|
}
|
|
|
|
|
|
if (pDiagInfo->AssociationTable.NumItems != 0)
|
|
{
|
|
//
|
|
// Ouch! Associations still left. We print out the associations and then
|
|
// DebugBreak.
|
|
//
|
|
|
|
TR_FATAL((
|
|
"FATAL: Object 0x%p still has some associations left!\n",
|
|
pObject
|
|
));
|
|
RmDbgPrintAssociations(pObject, pSR);
|
|
ASSERT(!"Object has associations left at deallocation time.");
|
|
}
|
|
|
|
pObject->pDiagInfo = NULL;
|
|
|
|
RmDeinitializeHashTable(
|
|
&pDiagInfo->AssociationTable
|
|
);
|
|
|
|
//
|
|
// Add any other checks here...
|
|
//
|
|
|
|
NdisFreeSpinLock(&pDiagInfo->OsLock);
|
|
RM_FREE(pDiagInfo);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
rmDbgPrintOneAssociation (
|
|
PRM_HASH_LINK pLink,
|
|
PVOID pvContext,
|
|
PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump a single association.
|
|
|
|
Arguments:
|
|
|
|
pLink - Points to RM_PRIVATE_DBG_ASSOCIATION.HashLink.
|
|
pvContext - Unused
|
|
|
|
--*/
|
|
{
|
|
RM_PRIVATE_DBG_ASSOCIATION *pA =
|
|
CONTAINING_RECORD(pLink, RM_PRIVATE_DBG_ASSOCIATION, HashLink);
|
|
DbgPrint(
|
|
(char*) (pA->szFormatString),
|
|
pA->Entity1,
|
|
pA->Entity2,
|
|
pA->AssociationID
|
|
);
|
|
}
|
|
|
|
|
|
VOID
|
|
_cdecl
|
|
rmDefaultDumpEntry (
|
|
char *szFormatString,
|
|
UINT_PTR Param1,
|
|
UINT_PTR Param2,
|
|
UINT_PTR Param3,
|
|
UINT_PTR Param4
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Default function to dump the contents of an association.
|
|
|
|
--*/
|
|
{
|
|
DbgPrint(
|
|
szFormatString,
|
|
Param1,
|
|
Param2,
|
|
Param3,
|
|
Param4
|
|
);
|
|
}
|
|
|
|
UINT
|
|
rmSafeAppend(
|
|
char *szBuf,
|
|
const char *szAppend,
|
|
UINT cbBuf
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Append szAppend to szBuf, but don't exceed cbBuf, and make sure the
|
|
resulting string is null-terminated.
|
|
|
|
Return Value:
|
|
|
|
Total length of string (excluding null termination) after append.
|
|
|
|
--*/
|
|
{
|
|
UINT uRet;
|
|
char *pc = szBuf;
|
|
char *pcEnd = szBuf+cbBuf-1; // possible overflow, but we check below.
|
|
const char *pcFrom = szAppend;
|
|
|
|
if (cbBuf==0) return 0; // EARLY RETURN;
|
|
|
|
// Skip to end of szBuf
|
|
//
|
|
while (pc < pcEnd && *pc!=0)
|
|
{
|
|
pc++;
|
|
}
|
|
|
|
// Append szAppend
|
|
while (pc < pcEnd && *pcFrom!=0)
|
|
{
|
|
*pc++ = *pcFrom++;
|
|
}
|
|
|
|
// Append final zero
|
|
//
|
|
*pc=0;
|
|
|
|
return (UINT) (UINT_PTR) (pc-szBuf);
|
|
}
|
|
|
|
#endif //RM_EXTRA_CHECKING
|
|
|
|
VOID
|
|
rmWorkItemHandler_ResumeTaskAsync(
|
|
IN PNDIS_WORK_ITEM pWorkItem,
|
|
IN PVOID pTaskToResume
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
NDIS work item handler which resumes the give task.
|
|
|
|
Arguments:
|
|
|
|
pWorkItem - Work item associated with the handler.
|
|
pTaskToResume - Actually a pointer to the task to resume.
|
|
|
|
--*/
|
|
{
|
|
PRM_TASK pTask = pTaskToResume;
|
|
UINT_PTR CompletionParam = pTask->AsyncCompletionParam;
|
|
RM_DECLARE_STACK_RECORD(sr)
|
|
|
|
ASSERTEX(RMISALLOCATED(&pTask->Hdr), pTask);
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
// Undo the association added in RmResumeTasyAsync...
|
|
//
|
|
RmDbgDeleteAssociation(
|
|
0xfc39a878, // Location ID
|
|
&pTask->Hdr, // pObject
|
|
CompletionParam, // Instance1
|
|
(UINT_PTR) pWorkItem, // Instance2
|
|
RM_PRIVATE_ASSOC_RESUME_TASK_ASYNC, // AssociationID
|
|
&sr
|
|
);
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
// Actually resume the task.
|
|
//
|
|
RmResumeTask(pTask, CompletionParam, &sr);
|
|
|
|
RM_ASSERT_CLEAR(&sr)
|
|
}
|
|
|
|
|
|
VOID
|
|
rmTimerHandler_ResumeTaskDelayed(
|
|
IN PVOID SystemSpecific1,
|
|
IN PVOID FunctionContext,
|
|
IN PVOID SystemSpecific2,
|
|
IN PVOID SystemSpecific3
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
NDIS timer handler which resumes the give task.
|
|
|
|
WARNING: This handler is also called internally by the RM APIs.
|
|
Implementation notes: -- see notes.txt 07/14/1999 entry.
|
|
|
|
Arguments:
|
|
|
|
SystemSpecific1 - Unused
|
|
FunctionContext - Actually a pointer to the task to be resumed.
|
|
SystemSpecific2 - Unused
|
|
SystemSpecific3 - Unused
|
|
|
|
--*/
|
|
{
|
|
PRM_TASK pTask = FunctionContext;
|
|
UINT_PTR CompletionParam = pTask->AsyncCompletionParam;
|
|
RM_DECLARE_STACK_RECORD(sr)
|
|
|
|
ASSERTEX(RMISALLOCATED(&pTask->Hdr), pTask);
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
// Undo the association added in RmResumeTasyDelayed...
|
|
//
|
|
RmDbgDeleteAssociation(
|
|
0xfc39a878, // Location ID
|
|
&pTask->Hdr, // pObject
|
|
CompletionParam, // Instance1
|
|
(UINT_PTR) NULL, // Instance2
|
|
RM_PRIVATE_ASSOC_RESUME_TASK_DELAYED, // AssociationID
|
|
&sr
|
|
);
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
RMPRIVATELOCK(&pTask->Hdr, &sr);
|
|
ASSERT(RM_CHECK_STATE(pTask, RMTSKDELSTATE_MASK, RMTSKDELSTATE_DELAYED));
|
|
RM_SET_STATE(pTask, RMTSKDELSTATE_MASK, 0);
|
|
RM_SET_STATE(pTask, RMTSKABORTSTATE_MASK, 0);
|
|
RMPRIVATEUNLOCK(&pTask->Hdr, &sr);
|
|
|
|
// Actually resume the task.
|
|
//
|
|
RmResumeTask(pTask, CompletionParam, &sr);
|
|
|
|
RM_ASSERT_CLEAR(&sr)
|
|
}
|
|
|
|
|
|
VOID
|
|
rmPrivateTaskDelete (
|
|
PRM_OBJECT_HEADER pObj,
|
|
PRM_STACK_RECORD psr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free a private task (which was allocated using rmAllocatePrivateTask.
|
|
|
|
Arguments:
|
|
|
|
pObj - Actually a pointer to a task of subtype RM_PRIVATE_TASK.
|
|
|
|
--*/
|
|
{
|
|
PRM_TASK pTask = (PRM_TASK) pObj;
|
|
if (CHECK_TASK_IS_BACKUP(pTask) == TRUE)
|
|
{
|
|
arpReturnBackupTask((ARP1394_TASK*)pTask);
|
|
}
|
|
else
|
|
{
|
|
RM_FREE(pObj);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
#if RM_EXTRA_CHECKING
|
|
|
|
|
|
RM_DBG_LOG_ENTRY *
|
|
rmDbgAllocateLogEntry(VOID)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate an object log entry.
|
|
|
|
TODO use lookaside lists, and implement per-component global logs.
|
|
See notes.txt 03/07/1999 entry "Registering root objects with RM".
|
|
|
|
--*/
|
|
{
|
|
RM_DBG_LOG_ENTRY *pLE;
|
|
RM_ALLOCSTRUCT(pLE, MTAG_DBGINFO);
|
|
return pLE;
|
|
}
|
|
|
|
VOID
|
|
rmDbgDeallocateLogEntry(
|
|
RM_DBG_LOG_ENTRY *pLogEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free an object log entry.
|
|
|
|
TODO use lookaside lists, and implement per-component global logs.
|
|
See notes.txt 03/07/1999 entry "Registering root objects with RM".
|
|
|
|
--*/
|
|
{
|
|
RM_FREE(pLogEntry);
|
|
}
|
|
#endif // RM_EXTRA_CHECKING
|
|
|
|
|
|
VOID
|
|
rmUpdateHashTableStats(
|
|
PULONG pStats,
|
|
ULONG LinksTraversed
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Update the stats (loword == links traversed, hiword == num accesses)
|
|
|
|
--*/
|
|
{
|
|
ULONG OldStats;
|
|
ULONG Stats;
|
|
|
|
// Clip LinksTraversed to 2^13, or 8192
|
|
//
|
|
if (LinksTraversed > (1<<13))
|
|
{
|
|
LinksTraversed = 1<<13;
|
|
}
|
|
|
|
Stats = OldStats = *pStats;
|
|
|
|
// If either the loword or hiword of Stats is greater-than 2^13, we
|
|
// intiger-devide both by 2. We're really only interested in the ratio
|
|
// of the two, which is preserved by the division.
|
|
//
|
|
#define rmSTATS_MASK (0x11<<30|0x11<<14)
|
|
if (OldStats & rmSTATS_MASK)
|
|
{
|
|
Stats >>= 1;
|
|
Stats &= ~rmSTATS_MASK;
|
|
}
|
|
|
|
// Compute the updated value of stats..
|
|
// "1<<16" below means "one access"
|
|
//
|
|
Stats += LinksTraversed | (1<<16);
|
|
|
|
// Update the stats, but only if they haven't already been updated by
|
|
// someone else. Note that if they HAVE been updated, we will lose this
|
|
// update. Not a big deal as we are not looking for 100% exact statistics here.
|
|
//
|
|
InterlockedCompareExchange(pStats, Stats, OldStats);
|
|
}
|
|
|
|
|
|
VOID
|
|
rmEnumObjectInGroupHashTable (
|
|
PRM_HASH_LINK pLink,
|
|
PVOID pvContext,
|
|
PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
Hash table enumerator to implement "STRONG" enumeration -- see
|
|
RmEnumerateObjectsInGroup.
|
|
--*/
|
|
{
|
|
PRM_STRONG_ENUMERATION_CONTEXT pCtxt = (PRM_STRONG_ENUMERATION_CONTEXT)pvContext;
|
|
|
|
if (pCtxt->fContinue)
|
|
{
|
|
PRM_OBJECT_HEADER pHdr;
|
|
pHdr = CONTAINING_RECORD(pLink, RM_OBJECT_HEADER, HashLink);
|
|
pCtxt->fContinue = pCtxt->pfnObjEnumerator(
|
|
pHdr,
|
|
pCtxt->pvCallerContext,
|
|
pSR
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
rmConstructGroupSnapshot (
|
|
PRM_HASH_LINK pLink,
|
|
PVOID pvContext,
|
|
PRM_STACK_RECORD pSR
|
|
)
|
|
/*++
|
|
Hash table enumerator to construct a snapshot of a group for weak enumeration.
|
|
See RmWeakEnumerateObjectsInGroup.
|
|
--*/
|
|
{
|
|
PRM_WEAK_ENUMERATION_CONTEXT pCtxt = (PRM_WEAK_ENUMERATION_CONTEXT)pvContext;
|
|
|
|
if (pCtxt->ppCurrent < pCtxt->ppEnd)
|
|
{
|
|
PRM_OBJECT_HEADER pHdr;
|
|
pHdr = CONTAINING_RECORD(pLink, RM_OBJECT_HEADER, HashLink);
|
|
RmTmpReferenceObject(pHdr, pSR);
|
|
*pCtxt->ppCurrent = pHdr;
|
|
pCtxt->ppCurrent++;
|
|
}
|
|
}
|
|
|