Source code of Windows XP (NT5)
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.

5906 lines
154 KiB

  1. /*++
  2. Copyright (c) 1998-1999 Microsoft Corporation
  3. Module Name:
  4. rm.c
  5. Abstract:
  6. Implementation of the "Resource Manager" APIs.
  7. Revision History:
  8. Who When What
  9. -------- -------- ----------------------------------------------
  10. josephj 11-18-98 Created
  11. Notes:
  12. --*/
  13. #include <precomp.h>
  14. //
  15. // File-specific debugging defaults.
  16. //
  17. #define TM_CURRENT TM_RM
  18. //=========================================================================
  19. // U T I L I T Y M A C R O S
  20. //=========================================================================
  21. #define RM_ALLOC(_pp, _size, _tag) \
  22. NdisAllocateMemoryWithTag((_pp), (_size), (_tag))
  23. #define RM_ALLOCSTRUCT(_p, _tag) \
  24. NdisAllocateMemoryWithTag(&(_p), sizeof(*(_p)), (_tag))
  25. #define RM_FREE(_p) NdisFreeMemory((_p), 0, 0)
  26. #define RM_ZEROSTRUCT(_p) \
  27. NdisZeroMemory((_p), sizeof(*(_p)))
  28. #define RM_PRIVATE_UNLINK_NEXT_HASH(_pHashTable, _ppLink) \
  29. ((*(_ppLink) = (*(_ppLink))->pNext), ((_pHashTable)->NumItems--))
  30. #define SET_RM_STATE(_pHdr, _Mask, _Val) \
  31. (((_pHdr)->RmState) = (((_pHdr)->RmState) & ~(_Mask)) | (_Val))
  32. #define CHECK_RM_STATE(_pHdr, _Mask, _Val) \
  33. ((((_pHdr)->RmState) & (_Mask)) == (_Val))
  34. #define RMISALLOCATED(_pHdr) \
  35. CHECK_RM_STATE((_pHdr), RMOBJSTATE_ALLOCMASK, RMOBJSTATE_ALLOCATED)
  36. #define SET_RM_TASK_STATE(_pTask, _pState) \
  37. SET_RM_STATE(&(_pTask)->Hdr, RMTSKSTATE_MASK, (_pState))
  38. #define CHECK_RM_TASK_STATE(_pTask, _pState) \
  39. CHECK_RM_STATE(&(_pTask)->Hdr, RMTSKSTATE_MASK, (_pState))
  40. #define GET_RM_TASK_STATE(_pTask) \
  41. ((_pTask)->Hdr.RmState & RMTSKSTATE_MASK)
  42. #if RM_EXTRA_CHECKING
  43. #define RMPRIVATELOCK(_pobj, _psr) \
  44. rmLock(&(_pobj)->RmPrivateLock, 0, rmPrivateLockVerifier, (_pobj), (_psr))
  45. #else // !RM_EXTRA_CHECKING
  46. #define RMPRIVATELOCK(_pobj, _psr) \
  47. rmLock(&(_pobj)->RmPrivateLock, (_psr))
  48. #endif // !RM_EXTRA_CHECKING
  49. #define RMPRIVATEUNLOCK(_pobj, _psr) \
  50. rmUnlock(&(_pobj)->RmPrivateLock, (_psr))
  51. #if 0
  52. #define RM_TEST_SIG 0x59dcfd36
  53. #define RM_TEST_DEALLOC_SIG 0x21392147
  54. #define RM_OBJECT_IS_ALLOCATED(_pobj) \
  55. ((_pobj)->Sig == RM_TEST_SIG)
  56. #define RM_MARK_OBJECT_AS_DEALLOCATED(_pobj) \
  57. ((_pobj)->Sig = RM_TEST_DEALLOC_SIG)
  58. #else
  59. #define RM_OBJECT_IS_ALLOCATED(_pobj) 0x1
  60. #define RM_MARK_OBJECT_AS_DEALLOCATED(_pobj) (0)
  61. #endif
  62. //=========================================================================
  63. // L O C A L P R O T O T Y P E S
  64. //=========================================================================
  65. #if RM_EXTRA_CHECKING
  66. // The lowest AssociationID used internal to the RM API implementation.
  67. //
  68. #define RM_PRIVATE_ASSOC_BASE (0x1<<31)
  69. // Association types internal to RM API impmenentation.
  70. //
  71. enum
  72. {
  73. RM_PRIVATE_ASSOC_LINK = RM_PRIVATE_ASSOC_BASE,
  74. RM_PRIVATE_ASSOC_LINK_CHILDOF,
  75. RM_PRIVATE_ASSOC_LINK_PARENTOF,
  76. RM_PRIVATE_ASSOC_LINK_TASKPENDINGON,
  77. RM_PRIVATE_ASSOC_LINK_TASKBLOCKS,
  78. RM_PRIVATE_ASSOC_INITGROUP,
  79. RM_PRIVATE_ASSOC_RESUME_TASK_ASYNC,
  80. RM_PRIVATE_ASSOC_RESUME_TASK_DELAYED
  81. };
  82. const char *szASSOCFORMAT_LINK = " Linked to 0x%p (%s)\n";
  83. const char *szASSOCFORMAT_LINK_CHILDOF = " Child of 0x%p (%s)\n";
  84. const char *szASSOCFORMAT_LINK_PARENTOF = " Parent of 0x%p (%s)\n";
  85. const char *szASSOCFORMAT_LINK_TASKPENDINGON = " Pending on 0x%p (%s)\n";
  86. const char *szASSOCFORMAT_LINK_TASKBLOCKS = " Blocks 0x%p (%s)\n";
  87. const char *szASSOCFORMAT_INITGROUP = " Owns group 0x%p (%s)\n";
  88. const char *szASSOCFORMAT_RESUME_TASK_ASYNC = " Resume async (param=0x%p)\n";
  89. const char *szASSOCFORMAT_RESUME_TASK_DELAYED = " Resume delayed (param=0x%p)\n";
  90. // Linked to 0x098889(LocalIP)
  91. // Parent of 0x098889(InitIPTask)
  92. // Child of 0x098889(Interface)
  93. #endif // RM_EXTRA_CHECKING
  94. // Private RM task to unload all objects in a group.
  95. //
  96. typedef struct
  97. {
  98. RM_TASK TskHdr; // Common task header
  99. PRM_GROUP pGroup; // Group being unloaded
  100. UINT uIndex; // Index of hash-table currently being
  101. // unloaded.
  102. NDIS_EVENT BlockEvent; // Event to optionally signal when done.
  103. BOOLEAN fUseEvent; // TRUE IFF event is to be signaled.
  104. PFN_RM_TASK_HANDLER pfnTaskUnloadObjectHandler; // ...
  105. // Object's unload task.
  106. PFN_RM_TASK_ALLOCATOR pfnUnloadTaskAllocator;
  107. } TASK_UNLOADGROUP;
  108. //
  109. // RM_PRIVATE_TASK is the union of all tasks structures used intenally in rm.c.
  110. // rmAllocateTask allocates memory of sizeof(RM_PRIVATE_TASK), which is guaranteed
  111. // to be large enough to hold any task internal to rm.c
  112. //
  113. typedef union
  114. {
  115. RM_TASK TskHdr;
  116. TASK_UNLOADGROUP UnloadGroup;
  117. } RM_PRIVATE_TASK;
  118. #if RM_EXTRA_CHECKING
  119. VOID
  120. rmDbgInitializeDiagnosticInfo(
  121. PRM_OBJECT_HEADER pObject,
  122. PRM_STACK_RECORD pSR
  123. );
  124. VOID
  125. rmDbgDeinitializeDiagnosticInfo(
  126. PRM_OBJECT_HEADER pObject,
  127. PRM_STACK_RECORD pSR
  128. );
  129. VOID
  130. rmDbgPrintOneAssociation (
  131. PRM_HASH_LINK pLink,
  132. PVOID pvContext,
  133. PRM_STACK_RECORD pSR
  134. );
  135. VOID
  136. _cdecl
  137. rmDefaultDumpEntry (
  138. char *szFormatString,
  139. UINT_PTR Param1,
  140. UINT_PTR Param2,
  141. UINT_PTR Param3,
  142. UINT_PTR Param4
  143. );
  144. UINT
  145. rmSafeAppend(
  146. char *szBuf,
  147. const char *szAppend,
  148. UINT cbBuf
  149. );
  150. #endif // RM_EXTRA_CHECKING
  151. NDIS_STATUS
  152. rmTaskUnloadGroup(
  153. IN struct _RM_TASK * pTask,
  154. IN RM_TASK_OPERATION Code,
  155. IN UINT_PTR UserParam, // Unused
  156. IN PRM_STACK_RECORD pSR
  157. );
  158. NDIS_STATUS
  159. rmAllocatePrivateTask(
  160. IN PRM_OBJECT_HEADER pParentObject,
  161. IN PFN_RM_TASK_HANDLER pfnHandler,
  162. IN UINT Timeout,
  163. IN const char * szDescription, OPTIONAL
  164. OUT PRM_TASK *ppTask,
  165. IN PRM_STACK_RECORD pSR
  166. );
  167. VOID
  168. rmWorkItemHandler_ResumeTaskAsync(
  169. IN PNDIS_WORK_ITEM pWorkItem,
  170. IN PVOID pTaskToResume
  171. );
  172. VOID
  173. rmTimerHandler_ResumeTaskDelayed(
  174. IN PVOID SystemSpecific1,
  175. IN PVOID FunctionContext,
  176. IN PVOID SystemSpecific2,
  177. IN PVOID SystemSpecific3
  178. );
  179. VOID
  180. rmPrivateTaskDelete (
  181. PRM_OBJECT_HEADER pObj,
  182. PRM_STACK_RECORD psr
  183. );
  184. VOID
  185. rmDerefObject(
  186. PRM_OBJECT_HEADER pObject,
  187. PRM_STACK_RECORD pSR
  188. );
  189. VOID
  190. rmLock(
  191. PRM_LOCK pLock,
  192. #if RM_EXTRA_CHECKING
  193. UINT uLocID,
  194. PFNLOCKVERIFIER pfnVerifier,
  195. PVOID pVerifierContext,
  196. #endif //RM_EXTRA_CHECKING
  197. PRM_STACK_RECORD pSR
  198. );
  199. VOID
  200. rmUnlock(
  201. PRM_LOCK pLock,
  202. PRM_STACK_RECORD pSR
  203. );
  204. #if RM_EXTRA_CHECKING
  205. ULONG
  206. rmPrivateLockVerifier(
  207. PRM_LOCK pLock,
  208. BOOLEAN fLock,
  209. PVOID pContext,
  210. PRM_STACK_RECORD pSR
  211. );
  212. ULONG
  213. rmVerifyObjectState(
  214. PRM_LOCK pLock,
  215. BOOLEAN fLock,
  216. PVOID pContext,
  217. PRM_STACK_RECORD pSR
  218. );
  219. RM_DBG_LOG_ENTRY *
  220. rmDbgAllocateLogEntry(VOID);
  221. VOID
  222. rmDbgDeallocateLogEntry(
  223. RM_DBG_LOG_ENTRY *pLogEntry
  224. );
  225. #endif // RM_EXTRA_CHECKING
  226. VOID
  227. rmEndTask(
  228. PRM_TASK pTask,
  229. NDIS_STATUS Status,
  230. PRM_STACK_RECORD pSR
  231. );
  232. VOID
  233. rmUpdateHashTableStats(
  234. PULONG pStats,
  235. ULONG LinksTraversed
  236. );
  237. typedef struct
  238. {
  239. PFN_RM_GROUP_ENUMERATOR pfnObjEnumerator;
  240. PVOID pvCallerContext;
  241. INT fContinue;
  242. } RM_STRONG_ENUMERATION_CONTEXT, *PRM_STRONG_ENUMERATION_CONTEXT;
  243. typedef struct
  244. {
  245. PRM_OBJECT_HEADER *ppCurrent;
  246. PRM_OBJECT_HEADER *ppEnd;
  247. } RM_WEAK_ENUMERATION_CONTEXT, *PRM_WEAK_ENUMERATION_CONTEXT;
  248. VOID
  249. rmEnumObjectInGroupHashTable (
  250. PRM_HASH_LINK pLink,
  251. PVOID pvContext,
  252. PRM_STACK_RECORD pSR
  253. );
  254. VOID
  255. rmConstructGroupSnapshot (
  256. PRM_HASH_LINK pLink,
  257. PVOID pvContext,
  258. PRM_STACK_RECORD pSR
  259. );
  260. //=========================================================================
  261. // L O C A L D A T A
  262. //=========================================================================
  263. // Global struture for the RM apis.
  264. //
  265. struct
  266. {
  267. // Accessed via interlocked operation.
  268. //
  269. ULONG Initialized;
  270. RM_OS_LOCK GlobalOsLock;
  271. LIST_ENTRY listGlobalLog;
  272. UINT NumGlobalLogEntries;
  273. } RmGlobals;
  274. RM_STATIC_OBJECT_INFO
  275. RmPrivateTasks_StaticInfo =
  276. {
  277. 0, // TypeUID
  278. 0, // TypeFlags
  279. "RM Private Task", // TypeName
  280. 0, // Timeout
  281. NULL, // pfnCreate
  282. rmPrivateTaskDelete, // pfnDelete
  283. NULL, // LockVerifier
  284. 0, // length of resource table
  285. NULL // Resource Table
  286. };
  287. // TODO: make constant
  288. static
  289. RM_STATIC_OBJECT_INFO
  290. RmTask_StaticInfo =
  291. {
  292. 0, // TypeUID
  293. 0, // TypeFlags
  294. "Task", // TypeName
  295. 0, // Timeout
  296. NULL, // Create
  297. NULL, // Delete
  298. NULL, // LockVerifier
  299. 0, // ResourceTable size
  300. NULL // ResourceTable
  301. };
  302. //=========================================================================
  303. // R M A P I S
  304. //=========================================================================
  305. #define RM_INITIALIZATION_STARTING 1
  306. #define RM_INITIALIZATION_COMPLETE 2
  307. VOID
  308. RmInitializeRm(VOID)
  309. /*++
  310. Must be called before any RM APIs are called.
  311. TODO: replace by registration mechanism.
  312. See notes.txt 03/07/1999 entry "Registering root objects with RM".
  313. --*/
  314. {
  315. ENTER("RmInitializeRm", 0x29f5d167)
  316. if (InterlockedCompareExchange(
  317. &RmGlobals.Initialized, RM_INITIALIZATION_STARTING, 0)==0)
  318. {
  319. TR_INFO(("Initializing RM APIs Global Info\n"));
  320. NdisAllocateSpinLock(&RmGlobals.GlobalOsLock);
  321. InitializeListHead(&RmGlobals.listGlobalLog);
  322. InterlockedExchange(&RmGlobals.Initialized, RM_INITIALIZATION_COMPLETE);
  323. }
  324. else
  325. {
  326. // Spin waiting for it to get to RM_INITIALIZATION_COMPLETE (allocated).
  327. TR_INFO(("Spinning, waiting for initialization to complete.\n"));
  328. while (RmGlobals.Initialized != RM_INITIALIZATION_COMPLETE)
  329. {
  330. // spin
  331. }
  332. }
  333. EXIT()
  334. }
  335. VOID
  336. RmDeinitializeRm(VOID)
  337. /*++
  338. Must be called to deinitialze, after last RM api is called and all async
  339. activity is over.
  340. TODO: replace by deregistration mechanism.
  341. See notes.txt 03/07/1999 entry "Registering root objects with RM".
  342. --*/
  343. {
  344. ENTER("RmDeinitializeRm", 0x9a8407e9)
  345. ASSERT(RmGlobals.Initialized == RM_INITIALIZATION_COMPLETE);
  346. TR_INFO(("Deinitializing RM APIs Global Info\n"));
  347. // Make sure global log list is empty. Acquiring the GLobalOsLock is
  348. // not necessary here because all activity has stopped by now.
  349. //
  350. ASSERT(IsListEmpty(&RmGlobals.listGlobalLog));
  351. EXIT()
  352. }
  353. VOID
  354. RmInitializeHeader(
  355. IN PRM_OBJECT_HEADER pParentObject,
  356. IN PRM_OBJECT_HEADER pObject,
  357. IN UINT Sig,
  358. IN PRM_LOCK pLock,
  359. IN PRM_STATIC_OBJECT_INFO pStaticInfo,
  360. IN const char * szDescription,
  361. IN PRM_STACK_RECORD pSR
  362. )
  363. /*++
  364. Routine Description:
  365. Initialize the RM_OBJECT_HEADER portion of an object.
  366. Arguments:
  367. pParentObject - NULL for a root object.
  368. pObject - Object to be initialized.
  369. Sig - Signature of the object.
  370. pLock - Lock used to serialize access to the object.
  371. pStaticInfo - Static informatation about the object.
  372. szDescription - A discriptive string (for debugging only) to be associated
  373. with this object.
  374. --*/
  375. {
  376. ENTER("RmInitializeHeader", 0x47dea382)
  377. NdisZeroMemory(pObject, sizeof(*pObject));
  378. if (szDescription == NULL)
  379. {
  380. szDescription = pStaticInfo->szTypeName;
  381. }
  382. TR_VERB(("Initializing header 0x%p (%s)\n", pObject, szDescription));
  383. pObject->Sig = Sig;
  384. pObject->pLock = pLock;
  385. pObject->pStaticInfo = pStaticInfo;
  386. pObject->szDescription = szDescription;
  387. SET_RM_STATE(pObject, RMOBJSTATE_ALLOCMASK, RMOBJSTATE_ALLOCATED);
  388. // The private lock is set to level (UINT)-1, which is the highest
  389. // possible level.
  390. //
  391. RmInitializeLock(&pObject->RmPrivateLock, (UINT)-1);
  392. #if RM_EXTRA_CHECKING
  393. rmDbgInitializeDiagnosticInfo(pObject, pSR);
  394. #endif //RM_EXTRA_CHECKING
  395. // Link to parent if non NULL.
  396. //
  397. if (pParentObject != NULL)
  398. {
  399. pObject->pParentObject = pParentObject;
  400. pObject->pRootObject = pParentObject->pRootObject;
  401. #if RM_EXTRA_CHECKING
  402. RmLinkObjectsEx(
  403. pObject,
  404. pParentObject,
  405. 0x11f25620,
  406. RM_PRIVATE_ASSOC_LINK_CHILDOF,
  407. szASSOCFORMAT_LINK_CHILDOF,
  408. RM_PRIVATE_ASSOC_LINK_PARENTOF,
  409. szASSOCFORMAT_LINK_PARENTOF,
  410. pSR
  411. );
  412. #else // !RM_EXTRA_CHECKING
  413. RmLinkObjects(pObject, pParentObject, pSR);
  414. #endif // !RM_EXTRA_CHECKING
  415. }
  416. else
  417. {
  418. pObject->pRootObject = pObject;
  419. }
  420. // We increment the total-ref count once for the allocation. This
  421. // reference is removed in the call to RmDeallocateObject.
  422. // Note that this reference is in addition to the reference implicitly
  423. // added by the call to RmLinkObjects above.
  424. //
  425. NdisInterlockedIncrement(&pObject->TotRefs);
  426. #if RM_TRACK_OBJECT_TREE
  427. // Initialize our list of children.
  428. //
  429. InitializeListHead(&pObject->listChildren);
  430. if (pParentObject != NULL)
  431. {
  432. // Insert ourselves into our parent's list of children.
  433. //
  434. RMPRIVATELOCK(pParentObject, pSR);
  435. InsertHeadList(&pParentObject->listChildren, &pObject->linkSiblings);
  436. RMPRIVATEUNLOCK(pParentObject, pSR);
  437. }
  438. #endif // RM_TRACK_OBJECT_TREE
  439. EXIT()
  440. return;
  441. }
  442. VOID
  443. RmDeallocateObject(
  444. IN PRM_OBJECT_HEADER pObject,
  445. IN PRM_STACK_RECORD pSR
  446. )
  447. /*++
  448. Routine Description:
  449. Logically deallocate the object pObject. We don't actually unlink it from
  450. its parent or deallocate if there are non zero references to it.
  451. --*/
  452. {
  453. UINT Refs;
  454. ENTER("RmDeallocateObject", 0xa87fdf4a)
  455. TR_INFO(("0x%p (%s)\n", pObject, pObject->szDescription));
  456. RMPRIVATELOCK(pObject, pSR);
  457. RETAILASSERTEX(RMISALLOCATED(pObject), pObject);
  458. // Set state to deallocated.
  459. //
  460. SET_RM_STATE(pObject, RMOBJSTATE_ALLOCMASK, RMOBJSTATE_DEALLOCATED);
  461. RMPRIVATEUNLOCK(pObject, pSR);
  462. // Remove the ref explicitly added in RmInitializeAllocateObject.
  463. // rmDerefObject will remove the link to the parent, if any, if the
  464. // ref count drop to 1.
  465. //
  466. rmDerefObject(pObject, pSR);
  467. EXIT()
  468. }
  469. VOID
  470. RmInitializeLock(
  471. IN PRM_LOCK pLock,
  472. IN UINT Level
  473. )
  474. /*++
  475. Routine Description:
  476. Initialize a lock.
  477. Arguments:
  478. pLock - Unitialized memory to hold a struct of type RM_LOCK.
  479. Level - Level to be associated with this lock. Locks must be acquired
  480. in strictly increasing order of the locks' "Level" values.
  481. --*/
  482. {
  483. ASSERT(Level > 0);
  484. NdisAllocateSpinLock(&pLock->OsLock);
  485. pLock->Level = Level;
  486. #if RM_EXTRA_CHECKING
  487. pLock->pDbgInfo = &pLock->DbgInfo;
  488. NdisZeroMemory(&pLock->DbgInfo, sizeof(pLock->DbgInfo));
  489. #endif // RM_EXTRA_CHECKING
  490. }
  491. VOID
  492. RmDoWriteLock(
  493. PRM_LOCK pLock,
  494. PRM_STACK_RECORD pSR
  495. )
  496. /*++
  497. Routine Description:
  498. Acquire (write lock) lock pLock.
  499. --*/
  500. {
  501. rmLock(
  502. pLock,
  503. #if RM_EXTRA_CHECKING
  504. 0x16323980, // uLocID,
  505. NULL,
  506. NULL,
  507. #endif //RM_EXTRA_CHECKING
  508. pSR
  509. );
  510. }
  511. VOID
  512. RmDoUnlock(
  513. PRM_LOCK pLock,
  514. PRM_STACK_RECORD pSR
  515. )
  516. /*++
  517. Routine Description:
  518. Unlock lock pLock.
  519. --*/
  520. {
  521. rmUnlock(
  522. pLock,
  523. pSR
  524. );
  525. }
  526. #if TODO // Currently RmReadLockObject is a macro defined to be RmWriteLockObject.
  527. // TODO: Verifier need to to also make sure that object hasn't changed state
  528. // *while* the object has been read-locked.
  529. VOID
  530. RmReadLockObject(
  531. IN PRM_OBJECT_HEADER pObj,
  532. #if RM_EXTRA_CHECKING
  533. UINT uLocID,
  534. #endif //RM_EXTRA_CHECKING
  535. IN PRM_STACK_RECORD pSR
  536. )
  537. /*++
  538. Routine Description:
  539. Acquire (read lock) lock pLock.
  540. --*/
  541. {
  542. ASSERT(!"Unimplemented");
  543. }
  544. #endif //TODO
  545. VOID
  546. RmWriteLockObject(
  547. IN PRM_OBJECT_HEADER pObj,
  548. #if RM_EXTRA_CHECKING
  549. UINT uLocID,
  550. #endif //RM_EXTRA_CHECKING
  551. IN PRM_STACK_RECORD pSR
  552. )
  553. /*++
  554. Routine Description:
  555. Acquire (write lock) the lock associated with object pObj.
  556. Arguments:
  557. pObj -- Object whose lock to acquire.
  558. uLocID -- Arbitrary UINT identifying static location from which this
  559. call is made.
  560. --*/
  561. {
  562. ENTER("RmWriteLockObject", 0x590ed543)
  563. TR_VERB(("Locking 0x%p (%s)\n", pObj, pObj->szDescription));
  564. rmLock(
  565. pObj->pLock,
  566. #if RM_EXTRA_CHECKING
  567. uLocID,
  568. // pObj->pStaticInfo->pfnLockVerifier,
  569. rmVerifyObjectState,
  570. pObj,
  571. #endif //RM_EXTRA_CHECKING
  572. pSR
  573. );
  574. EXIT()
  575. }
  576. VOID
  577. RmUnlockObject(
  578. IN PRM_OBJECT_HEADER pObj,
  579. IN PRM_STACK_RECORD pSR
  580. )
  581. /*++
  582. Routine Description:
  583. Release the lock associated with object pObj.
  584. --*/
  585. {
  586. ENTER("RmUnLockObject", 0x0307dd84)
  587. TR_VERB(("Unlocking 0x%p (%s)\n", pObj, pObj->szDescription));
  588. #if RM_EXTRA_CHECKING
  589. //
  590. // Make sure that pObject is the object that is *supposed* to be freed.
  591. //
  592. ASSERT(pSR->LockInfo.pNextFree[-1].pVerifierContext == (PVOID) pObj);
  593. #endif // RM_EXTRA_CHECKING
  594. rmUnlock(
  595. pObj->pLock,
  596. pSR
  597. );
  598. EXIT()
  599. }
  600. VOID
  601. RmUnlockAll(
  602. IN PRM_STACK_RECORD pSR
  603. )
  604. /*++
  605. Routine Description:
  606. Unlocks all currently held locks as recorded in pSR.
  607. If the locks are associated with objects, RmUnlockObject is called for
  608. each of the held locks. Otherwise the raw unlock is performed.
  609. --*/
  610. {
  611. ENTER("RmUnLockObject", 0x9878be96)
  612. TR_VERB(("Unlocking all\n"));
  613. while (pSR->LockInfo.CurrentLevel != 0)
  614. {
  615. rmUnlock(
  616. pSR->LockInfo.pNextFree[-1].pLock,
  617. pSR
  618. );
  619. }
  620. EXIT()
  621. }
  622. VOID
  623. RmDbgChangeLockScope(
  624. IN PRM_OBJECT_HEADER pPreviouslyLockedObject,
  625. IN PRM_OBJECT_HEADER pObject,
  626. IN ULONG LocID,
  627. IN PRM_STACK_RECORD pSR
  628. )
  629. /*++
  630. Routine Description:
  631. (Debug only)
  632. Munge things so that the following sequence works:
  633. Rm[Read|Write]LockObject(pPreviouslyLockedObject, pSR);
  634. RmChangeLockScope(pPreviouslyLockedObject, pSR);
  635. RmUnlockObject(pObject, pSR);
  636. Of course, we require that the two objects have the same lock!
  637. NOTE: We only support changing scope of the MOST RECENTLY
  638. acquired lock.
  639. Arguments:
  640. pPreviouslyLockedObject - Currently locked object.
  641. pObject - Object to transfer lock scope to.
  642. LocID - Arbitrary UINT identifying static location from
  643. which this call is made.
  644. --*/
  645. {
  646. //
  647. // This is a NOOP unless extra-checking is enabled.
  648. // TODO: make this inline in the fre build.
  649. //
  650. #if RM_EXTRA_CHECKING
  651. RM_LOCKING_INFO * pLI = pSR->LockInfo.pNextFree-1;
  652. PRM_LOCK pLock = pPreviouslyLockedObject->pLock;
  653. ASSERT(
  654. pLock->Level == pSR->LockInfo.CurrentLevel
  655. && pLock == pObject->pLock
  656. && pLock == pLI->pLock
  657. && pLI->pVerifierContext == (PVOID) pPreviouslyLockedObject);
  658. ASSERT(pLI->pfnVerifier == rmVerifyObjectState);
  659. rmVerifyObjectState(pLock, FALSE, pLI->pVerifierContext, pSR);
  660. pLI->pVerifierContext = pObject;
  661. pLock->DbgInfo.uLocID = LocID;
  662. rmVerifyObjectState(pLock, TRUE, pLI->pVerifierContext, pSR);
  663. #endif // RM_EXTRA_CHECING
  664. }
  665. VOID
  666. RmLinkObjects(
  667. IN PRM_OBJECT_HEADER pObj1,
  668. IN PRM_OBJECT_HEADER pObj2,
  669. IN PRM_STACK_RECORD pSr
  670. )
  671. /*++
  672. Routine Description:
  673. Link object pObj1 to object pObj2. Basically, this function refs both
  674. objects.
  675. OK to call with some locks held, including RmPrivateLock.
  676. TODO: remove arp pSr above -- we don't need it.
  677. --*/
  678. {
  679. ENTER("RmLinkObjects", 0xfe2832dd)
  680. // Maybe we're being too harsh here -- if required, remove this...
  681. // This could happen where a task is linked at the point where the object
  682. // is being deallocated, so I'm changing the following to debug asserts
  683. // (used to be retail asserts).
  684. //
  685. ASSERT(RMISALLOCATED(pObj1));
  686. ASSERT(RMISALLOCATED(pObj2));
  687. TR_INFO(("0x%p (%s) linked to 0x%p (%s)\n",
  688. pObj1,
  689. pObj1->szDescription,
  690. pObj2,
  691. pObj2->szDescription
  692. ));
  693. NdisInterlockedIncrement(&pObj1->TotRefs);
  694. NdisInterlockedIncrement(&pObj2->TotRefs);
  695. }
  696. VOID
  697. RmLinkObjectsEx(
  698. IN PRM_OBJECT_HEADER pObj1,
  699. IN PRM_OBJECT_HEADER pObj2,
  700. IN ULONG LocID,
  701. IN ULONG AssocID,
  702. IN const char * szAssociationFormat,
  703. IN ULONG InvAssocID,
  704. IN const char * szInvAssociationFormat,
  705. IN PRM_STACK_RECORD pSR
  706. )
  707. /*++
  708. Routine Description:
  709. Same as RmLinkObjects, execpt that (debug only) it also sets up association
  710. (pObj2, pObj2->szDescription, AssocID)
  711. on pObj1, and association
  712. (pObj1, pObj1->szDescription, InvAssocID)
  713. on object pObj2
  714. Arguments:
  715. pObj1 - Object whose lock to acquire.
  716. pObj2 - Object whose lock to acquire.
  717. LocID - Arbitrary UINT identifying static location from which
  718. this call is made.
  719. AssocID - ID of the association (see RmDbgAddAssociation) that
  720. represents the link from pObj1 to pObj2.
  721. szAssociationFormat - Format of the association (see RmDbgAddAssociation)
  722. InvAssocId - ID of the inverse association (i.e., represents
  723. link from pObj2 to pObj1).
  724. szInvAssociationFormat - Format of the inverse association.
  725. --*/
  726. {
  727. ENTER("RmLinkObjectsEx", 0xef50263b)
  728. #if RM_EXTRA_CHECKING
  729. RmDbgAddAssociation(
  730. LocID, // Location ID
  731. pObj1, // pObject
  732. (UINT_PTR) pObj2, // Instance1
  733. (UINT_PTR) (pObj2->szDescription), // Instance2
  734. AssocID, // AssociationID
  735. szAssociationFormat,
  736. pSR
  737. );
  738. RmDbgAddAssociation(
  739. LocID, // Location ID
  740. pObj2, // pObject
  741. (UINT_PTR) pObj1, // Instance1
  742. (UINT_PTR) (pObj1->szDescription), // Instance2
  743. InvAssocID, // AssociationID
  744. szInvAssociationFormat,
  745. pSR
  746. );
  747. #endif // RM_EXTRA_CHECKING
  748. RmLinkObjects(
  749. pObj1,
  750. pObj2,
  751. pSR
  752. );
  753. }
  754. VOID
  755. RmUnlinkObjects(
  756. IN PRM_OBJECT_HEADER pObj1,
  757. IN PRM_OBJECT_HEADER pObj2,
  758. IN PRM_STACK_RECORD pSR
  759. )
  760. /*++
  761. Routine Description:
  762. Unlink objects pObj1 and pObj2 (i.e., undo the effect of
  763. RmLinkObjects(pObj1, pObj2, pSR)).
  764. --*/
  765. {
  766. ENTER("RmUnlinkObjects", 0x7c64356a)
  767. TR_INFO(("0x%p (%s) unlinked from 0x%p (%s)\n",
  768. pObj1,
  769. pObj1->szDescription,
  770. pObj2,
  771. pObj2->szDescription
  772. ));
  773. #if RM_EXTRA_CHECKING
  774. //
  775. // TODO: remove explict link
  776. //
  777. #endif // RM_EXTRA_CHECKING
  778. // Remove link refs.
  779. //
  780. rmDerefObject(pObj1, pSR);
  781. rmDerefObject(pObj2, pSR);
  782. }
  783. VOID
  784. RmUnlinkObjectsEx(
  785. IN PRM_OBJECT_HEADER pObj1,
  786. IN PRM_OBJECT_HEADER pObj2,
  787. IN ULONG LocID,
  788. IN ULONG AssocID,
  789. IN ULONG InvAssocID,
  790. IN PRM_STACK_RECORD pSR
  791. )
  792. /*++
  793. Routine Description:
  794. Same as RmUnlinkObjects, execpt that it also removes the association
  795. (pObj2, pObj2->szDescription, AssocID)
  796. on pObj1, and association
  797. (pObj1, pObj1->szDescription, InvAssocID)
  798. object pObj2
  799. Arguments:
  800. See RmLinkObjectsEx.
  801. --*/
  802. {
  803. ENTER("RmUnlinkObjectsEx", 0x65d3536c)
  804. #if RM_EXTRA_CHECKING
  805. RmDbgDeleteAssociation(
  806. LocID, // Location ID
  807. pObj1, // pObject
  808. (UINT_PTR) pObj2, // Instance1
  809. (UINT_PTR) (pObj2->szDescription), // Instance2
  810. AssocID, // AssociationID
  811. pSR
  812. );
  813. RmDbgDeleteAssociation(
  814. LocID, // Location ID
  815. pObj2, // pObject
  816. (UINT_PTR) pObj1, // Instance1
  817. (UINT_PTR) (pObj1->szDescription), // Instance2
  818. InvAssocID, // AssociationID
  819. pSR
  820. );
  821. #endif // RM_EXTRA_CHECKING
  822. RmUnlinkObjects(
  823. pObj1,
  824. pObj2,
  825. pSR
  826. );
  827. }
  828. VOID
  829. RmLinkToExternalEx(
  830. IN PRM_OBJECT_HEADER pObj,
  831. IN ULONG LocID,
  832. IN UINT_PTR ExternalEntity,
  833. IN ULONG AssocID,
  834. IN const char * szAssociationFormat,
  835. IN PRM_STACK_RECORD pSR
  836. )
  837. /*++
  838. Routine Description:
  839. Links object pObj to external entity ExternalEntity. Basically, this function
  840. adds a reference to pObj. In addition (debug only) this function sets up
  841. an association on pObj that links the external entity to pObj. pObj can be
  842. linked to ExternalEntity with the specified association ID AssocID only ONCE
  843. at any particular point of time.
  844. Once this link is setup, an attempt to deallocate pObj without removing the
  845. link results in an assertion failure.
  846. RmUnlinkFromExternalEx is the inverse function.
  847. Arguments:
  848. pObj - Object to be linked to an external entity.
  849. (following are for debug only...)
  850. LocID - Arbitrary UINT identifying static location from
  851. which this call is made.
  852. ExternalEntity - Opaque value representing the external entity.
  853. AssocID - Association ID representing the linkage.
  854. szAssociationFormat - Association format for the linkage.
  855. --*/
  856. {
  857. ENTER("RmLinkToExternalEx", 0x9aeaca74)
  858. #if RM_EXTRA_CHECKING
  859. RmDbgAddAssociation(
  860. LocID, // Location ID
  861. pObj, // pObject
  862. (UINT_PTR) ExternalEntity, // Instance1
  863. (UINT_PTR) 0, // Instance2 (unused)
  864. AssocID, // AssociationID
  865. szAssociationFormat,
  866. pSR
  867. );
  868. #endif // RM_EXTRA_CHECKING
  869. RmLinkToExternalFast(pObj);
  870. EXIT()
  871. }
  872. VOID
  873. RmUnlinkFromExternalEx(
  874. IN PRM_OBJECT_HEADER pObj,
  875. IN ULONG LocID,
  876. IN UINT_PTR ExternalEntity,
  877. IN ULONG AssocID,
  878. IN PRM_STACK_RECORD pSR
  879. )
  880. /*++
  881. Routine Description:
  882. Inverse of RmUnlinkFromExternalEx -- removes the link set up between
  883. pObj and ExternalEntity.
  884. Arguments:
  885. See RmLinkToExternalEx.
  886. --*/
  887. {
  888. ENTER("RmUnlinkFromExternalEx", 0x9fb084c3)
  889. #if RM_EXTRA_CHECKING
  890. RmDbgDeleteAssociation(
  891. LocID, // Location ID
  892. pObj, // pObject
  893. (UINT_PTR) ExternalEntity, // Instance1
  894. (UINT_PTR) 0, // Instance2 (unused)
  895. AssocID, // AssociationID
  896. pSR
  897. );
  898. #endif // RM_EXTRA_CHECKING
  899. RmUnlinkFromExternalFast(pObj);
  900. EXIT()
  901. }
  902. VOID
  903. RmLinkToExternalFast( // TODO make inline
  904. IN PRM_OBJECT_HEADER pObj
  905. )
  906. /*++
  907. Routine Description:
  908. Fast version of RmLinkToExternalEx -- same behavior in retail. No associations
  909. are setup.
  910. Arguments:
  911. See RmLinkToExternalEx.
  912. --*/
  913. {
  914. NdisInterlockedIncrement(&pObj->TotRefs);
  915. }
  916. VOID
  917. RmUnlinkFromExternalFast( // TODO make inline
  918. IN PRM_OBJECT_HEADER pObj
  919. )
  920. /*++
  921. Routine Description:
  922. Inverse of RmUnlinkFromExternalFast -- removes the link set up between
  923. pObj and ExternalEntity.
  924. TODO -- we need a fast implementation for the case that the object is
  925. not going to go away. For now we actually declare a stack record here each
  926. time, becaues rmDerefObject wants one! Bad bad.
  927. Arguments:
  928. See RmLinkToExternalFast.
  929. --*/
  930. {
  931. RM_DECLARE_STACK_RECORD(sr)
  932. rmDerefObject(pObj, &sr);
  933. }
  934. VOID
  935. RmTmpReferenceObject(
  936. IN PRM_OBJECT_HEADER pObj,
  937. IN PRM_STACK_RECORD pSR
  938. )
  939. /*++
  940. Routine Description:
  941. Add a temporary reference to object pObj.
  942. (Debug only) Increments the count of tmprefs maintained in pSR.
  943. --*/
  944. {
  945. ENTER("RmTmpReferenceObject", 0xdd981024)
  946. TR_VERB(("0x%p (%s)\n", pObj, pObj->szDescription));
  947. ASSERT(RM_OBJECT_IS_ALLOCATED(pObj));
  948. pSR->TmpRefs++;
  949. NdisInterlockedIncrement(&pObj->TotRefs);
  950. }
  951. VOID
  952. RmTmpDereferenceObject(
  953. IN PRM_OBJECT_HEADER pObj,
  954. IN PRM_STACK_RECORD pSR
  955. )
  956. /*++
  957. Routine Description:
  958. Remove a temporary reference to object pObj.
  959. (Debug only) Decrements the count of tmprefs maintained in pSR.
  960. --*/
  961. {
  962. ENTER("RmTmpDereferenceObject", 0xd1630c11)
  963. TR_VERB(("0x%p (%s)\n", pObj, pObj->szDescription));
  964. RETAILASSERTEX(pSR->TmpRefs>0, pSR);
  965. pSR->TmpRefs--;
  966. rmDerefObject(pObj, pSR);
  967. }
  968. VOID
  969. RmDbgAddAssociation(
  970. IN ULONG LocID,
  971. IN PRM_OBJECT_HEADER pObject,
  972. IN ULONG_PTR Entity1,
  973. IN ULONG_PTR Entity2,
  974. IN ULONG AssociationID,
  975. IN const char * szFormatString, OPTIONAL
  976. IN PRM_STACK_RECORD pSR
  977. )
  978. /*++
  979. Routine Description:
  980. Add an arbitrary association, for debugging purposes, under
  981. object pObject. The association is defined by the triple
  982. (Entity1, Entity2, AssociationID) -- only ONE such tuple may
  983. be registered at any time with object pParentObject.
  984. Note: It is valid for both of the following to be registered at the same
  985. time: (a, b, 1) and (a, b, 2)
  986. No association should exist at the point the object is deleted.
  987. Arguments:
  988. LocID - Arbitrary ID, typically representing the source location
  989. - from which this function is called.
  990. pObject - Object to add the association.
  991. Entity1 - The 1st entity making up the association. May be NULL.
  992. Entity2 - The 2nd entity making up the association. May be NULL.
  993. AssociationID - ID defining the association.
  994. NOTE: AssociationID must not have the high-bit set.
  995. Associations with the high bit set are reserved for
  996. internal use of the Rm API implementation.
  997. --*/
  998. {
  999. #if RM_EXTRA_CHECKING
  1000. PRM_OBJECT_DIAGNOSTIC_INFO pDiagInfo = pObject->pDiagInfo;
  1001. ENTER("RmDbgAddAssociation", 0x512192eb)
  1002. if (pDiagInfo)
  1003. {
  1004. //
  1005. // Allocate an association and enter it into the hash table.
  1006. // Assert if it already exists.
  1007. //
  1008. RM_PRIVATE_DBG_ASSOCIATION *pA;
  1009. RM_ALLOCSTRUCT(pA, MTAG_DBGINFO); // TODO use lookaside lists.
  1010. if (pA == NULL)
  1011. {
  1012. //
  1013. // Allocation failed. Record this fact, so that
  1014. // RmDbgDeleteAssociation doesn't assert
  1015. // if an attempt is made to remove an assertion which doesn't exist.
  1016. //
  1017. NdisAcquireSpinLock(&pDiagInfo->OsLock);
  1018. pDiagInfo->AssociationTableAllocationFailure = TRUE;
  1019. NdisReleaseSpinLock(&pDiagInfo->OsLock);
  1020. }
  1021. else
  1022. {
  1023. BOOLEAN fFound;
  1024. PRM_HASH_LINK *ppLink;
  1025. RM_ZEROSTRUCT(pA);
  1026. pA->Entity1 = Entity1;
  1027. pA->Entity2 = Entity2;
  1028. pA->AssociationID = AssociationID;
  1029. if (szFormatString == NULL)
  1030. {
  1031. // Put in the default description format string.
  1032. //
  1033. szFormatString = " Association (E1=0x%x, E2=0x%x, T=0x%x)\n";
  1034. }
  1035. TR_VERB((" Obj:0x%p (%s)...\n", pObject, pObject->szDescription));
  1036. TRACE0(TL_INFO,((char*)szFormatString, Entity1, Entity2, AssociationID));
  1037. pA->szFormatString = szFormatString;
  1038. NdisAcquireSpinLock(&pDiagInfo->OsLock);
  1039. fFound = RmLookupHashTable(
  1040. &pDiagInfo->AssociationTable,
  1041. &ppLink,
  1042. pA // We use pA as the key.
  1043. );
  1044. if (fFound)
  1045. {
  1046. ASSERTEX(
  1047. !"Association already exists:",
  1048. CONTAINING_RECORD(*ppLink, RM_PRIVATE_DBG_ASSOCIATION, HashLink)
  1049. );
  1050. RM_FREE(pA);
  1051. pA = NULL;
  1052. }
  1053. else
  1054. {
  1055. //
  1056. // Enter the association into the hash table.
  1057. //
  1058. RmAddHashItem(
  1059. &pDiagInfo->AssociationTable,
  1060. ppLink,
  1061. &pA->HashLink,
  1062. pA // We use pA as the key
  1063. );
  1064. }
  1065. NdisReleaseSpinLock(&pDiagInfo->OsLock);
  1066. // Now, just for grins, make a note of this in the object's log.
  1067. // WARNING: Although pEntity1/2 may contain pointers,
  1068. // we expect the the format string is such that if there are any
  1069. // references to regular or unicode strings, those strings will
  1070. // be valid for the life of the object (typically these strings
  1071. // are statically-allocated strings).
  1072. //
  1073. // We COULD use the more conservative format string to display the
  1074. // log entry, but it's useful to have the information displayed
  1075. // properly.
  1076. //
  1077. // Note-- we could also do different things depending on the type
  1078. // of association.
  1079. //
  1080. #if 0 // conservative format
  1081. RmDbgLogToObject(
  1082. pObject,
  1083. " Add association (E1=0x%x, E2=0x%x, T=0x%x)\n",
  1084. Entity1,
  1085. Entity2,
  1086. AssociationID,
  1087. 0, // Param4 // (UINT_PTR) szFormatString,
  1088. NULL,
  1089. NULL
  1090. );
  1091. #else // aggresive format
  1092. {
  1093. #define szADDASSOC " Add assoc:"
  1094. #if OBSOLETE // This doesn't work because rgMungedFormat is on the stack!
  1095. char rgMungedFormat[128];
  1096. UINT uLength;
  1097. rgMungedFormat[0]=0;
  1098. rmSafeAppend(rgMungedFormat, szADDASSOC, sizeof(rgMungedFormat));
  1099. uLength = rmSafeAppend(
  1100. rgMungedFormat,
  1101. szFormatString,
  1102. sizeof(rgMungedFormat)
  1103. );
  1104. if (uLength && rgMungedFormat[uLength-1] != '\n')
  1105. {
  1106. rgMungedFormat[uLength-1] = '\n';
  1107. }
  1108. RmDbgLogToObject(
  1109. pObject,
  1110. rgMungedFormat,
  1111. Entity1,
  1112. Entity2,
  1113. AssociationID,
  1114. 0,
  1115. NULL,
  1116. NULL
  1117. );
  1118. #endif // OBSOLETE
  1119. RmDbgLogToObject(
  1120. pObject,
  1121. szADDASSOC,
  1122. (char*)szFormatString,
  1123. Entity1,
  1124. Entity2,
  1125. AssociationID,
  1126. 0,
  1127. NULL,
  1128. NULL
  1129. );
  1130. }
  1131. #endif // aggressive format
  1132. }
  1133. }
  1134. EXIT()
  1135. #endif // RM_EXTRA_CHECKING
  1136. }
  1137. VOID
  1138. RmDbgDeleteAssociation(
  1139. IN ULONG LocID,
  1140. IN PRM_OBJECT_HEADER pObject,
  1141. IN ULONG_PTR Entity1,
  1142. IN ULONG_PTR Entity2,
  1143. IN ULONG AssociationID,
  1144. IN PRM_STACK_RECORD pSR
  1145. )
  1146. /*++
  1147. Routine Description:
  1148. Removes the previously-added association (Entity1, Entity2, Association)
  1149. from object pObject. See the description of RmDbgAddAssociation for
  1150. details.
  1151. Arguments:
  1152. See RmDbgAddAssociation.
  1153. --*/
  1154. {
  1155. #if RM_EXTRA_CHECKING
  1156. ENTER("RmDbgDelAssociation", 0x8354559f)
  1157. PRM_OBJECT_DIAGNOSTIC_INFO pDiagInfo = pObject->pDiagInfo;
  1158. if (pDiagInfo)
  1159. {
  1160. BOOLEAN fFound;
  1161. PRM_HASH_LINK *ppLink;
  1162. RM_PRIVATE_DBG_ASSOCIATION TrueKey;
  1163. // Only the following 3 fields of TrueKey make up the key
  1164. //
  1165. TrueKey.Entity1 = Entity1;
  1166. TrueKey.Entity2 = Entity2;
  1167. TrueKey.AssociationID = AssociationID;
  1168. NdisAcquireSpinLock(&pDiagInfo->OsLock);
  1169. fFound = RmLookupHashTable(
  1170. &pDiagInfo->AssociationTable,
  1171. &ppLink,
  1172. &TrueKey
  1173. );
  1174. if (fFound)
  1175. {
  1176. RM_PRIVATE_DBG_ASSOCIATION *pA =
  1177. CONTAINING_RECORD(*ppLink, RM_PRIVATE_DBG_ASSOCIATION, HashLink);
  1178. TR_VERB((" Obj:0x%p (%s)...\n", pObject, pObject->szDescription));
  1179. TRACE0(TL_INFO,
  1180. ((char*)pA->szFormatString,
  1181. pA->Entity1,
  1182. pA->Entity2,
  1183. pA->AssociationID));
  1184. //
  1185. // Now, just for grins, make a note of this in the oject's log.
  1186. // Note that of pEntity1/2 contain pointers, we can't expect them
  1187. // to be valid for as long as the object is alive, so we use
  1188. // the more conservative format string to display the log entry.
  1189. //
  1190. // TODO/BUGUG -- see comments under RmDbgAddAssociation
  1191. // about the risk of directly passing szFormat
  1192. //
  1193. #if 0 // conservative
  1194. RmDbgLogToObject(
  1195. pObject,
  1196. NULL,
  1197. " Deleted Association (E1=0x%x, E2=0x%x, T=0x%x)\n",
  1198. pA->Entity1,
  1199. pA->Entity2,
  1200. pA->AssociationID,
  1201. 0,
  1202. NULL,
  1203. NULL
  1204. );
  1205. #else // aggressive
  1206. #define szDELASSOC " Del assoc:"
  1207. RmDbgLogToObject(
  1208. pObject,
  1209. szDELASSOC,
  1210. (char*) pA->szFormatString,
  1211. pA->Entity1,
  1212. pA->Entity2,
  1213. pA->AssociationID,
  1214. 0, // Param4 // (UINT_PTR) szFormatString,
  1215. NULL,
  1216. NULL
  1217. );
  1218. #endif // aggressive
  1219. //
  1220. // Remove the association and free it.
  1221. //
  1222. RM_PRIVATE_UNLINK_NEXT_HASH( &pDiagInfo->AssociationTable, ppLink );
  1223. RM_FREE(pA);
  1224. }
  1225. else
  1226. {
  1227. if (!pDiagInfo->AssociationTableAllocationFailure)
  1228. {
  1229. ASSERT(!"Association doesn't exist");
  1230. }
  1231. }
  1232. NdisReleaseSpinLock(&pDiagInfo->OsLock);
  1233. }
  1234. EXIT()
  1235. #endif // RM_EXTRA_CHECKING
  1236. }
  1237. VOID
  1238. RmDbgPrintAssociations(
  1239. PRM_OBJECT_HEADER pObject,
  1240. PRM_STACK_RECORD pSR
  1241. )
  1242. /*++
  1243. Routine Description:
  1244. (Debug only) Dumps the associations on object pObject.
  1245. --*/
  1246. {
  1247. #if RM_EXTRA_CHECKING
  1248. ENTER("RmPrintAssociations", 0x8354559f)
  1249. PRM_OBJECT_DIAGNOSTIC_INFO pDiagInfo = pObject->pDiagInfo;
  1250. if (pDiagInfo)
  1251. {
  1252. TR_INFO((
  1253. "Obj 0x%p (%s):\n",
  1254. pObject,
  1255. pObject->szDescription
  1256. ));
  1257. NdisAcquireSpinLock(&pDiagInfo->OsLock);
  1258. RmEnumHashTable(
  1259. &pDiagInfo->AssociationTable,
  1260. rmDbgPrintOneAssociation, // pfnEnumerator
  1261. pObject, // context
  1262. pSR
  1263. );
  1264. NdisReleaseSpinLock(&pDiagInfo->OsLock);
  1265. }
  1266. EXIT()
  1267. #endif // RM_EXTRA_CHECKING
  1268. }
  1269. //
  1270. // Diagnostic per-object logging.
  1271. //
  1272. VOID
  1273. RmDbgLogToObject(
  1274. IN PRM_OBJECT_HEADER pObject,
  1275. IN char * szPrefix, OPTIONAL
  1276. IN char * szFormatString,
  1277. IN UINT_PTR Param1,
  1278. IN UINT_PTR Param2,
  1279. IN UINT_PTR Param3,
  1280. IN UINT_PTR Param4,
  1281. IN PFN_DBG_DUMP_LOG_ENTRY pfnDumpEntry, OPTIONAL
  1282. IN PVOID pvBuf OPTIONAL
  1283. )
  1284. /*++
  1285. Routine Description:
  1286. Make one log entry in pObject's log.
  1287. TODO: See notes.txt entry "03/07/1999 ... Registering root objects with RM"
  1288. on how we will find the deallocator function fo pvBuf. For now we simply
  1289. use NdisFreeMemory.
  1290. TODO: need to implement trimming of log when we reach a maximum. Currently we
  1291. just stop logging.
  1292. Arguments:
  1293. pfnDumpEntry - Function to be used for dumping the log.
  1294. If NULL, a default function is used, which interprets
  1295. szFormatString as the standard printf format string.
  1296. szFormatString - Format string for log display -- 1st arg to pfnDumpEntry
  1297. Param1-4 - Remaining args to pfnDumpEntry;
  1298. pvBuf - If non-NULL, piece of memory to be freed when the log entry
  1299. is freed.
  1300. NOTE: If Param1-4 contain pointers, the memory they refer to is assumed
  1301. to be valid for as long as the object is alive. If the entities being logged
  1302. may go away before the object is deallocated, the caller should
  1303. allocate a buffer to hold a copy of the entities, and pass the pointer to
  1304. that buffer as pvBuf.
  1305. --*/
  1306. {
  1307. #if RM_EXTRA_CHECKING
  1308. ENTER("RmDbgLogToObject", 0x2b2015b5)
  1309. PRM_OBJECT_DIAGNOSTIC_INFO pDiagInfo = pObject->pDiagInfo;
  1310. if (pDiagInfo && RmGlobals.NumGlobalLogEntries < 4000)
  1311. {
  1312. RM_DBG_LOG_ENTRY *pLogEntry;
  1313. NdisAcquireSpinLock(&RmGlobals.GlobalOsLock);
  1314. pLogEntry = rmDbgAllocateLogEntry();
  1315. if (pLogEntry != NULL)
  1316. {
  1317. if (pfnDumpEntry == NULL)
  1318. {
  1319. pfnDumpEntry = rmDefaultDumpEntry;
  1320. }
  1321. pLogEntry->pObject = pObject;
  1322. pLogEntry->pfnDumpEntry = pfnDumpEntry;
  1323. pLogEntry->szPrefix = szPrefix;
  1324. pLogEntry->szFormatString = szFormatString;
  1325. pLogEntry->Param1 = Param1;
  1326. pLogEntry->Param2 = Param2;
  1327. pLogEntry->Param3 = Param3;
  1328. pLogEntry->Param4 = Param4;
  1329. pLogEntry->pvBuf = pvBuf;
  1330. // Insert item at head of object log.
  1331. //
  1332. InsertHeadList(&pDiagInfo->listObjectLog, &pLogEntry->linkObjectLog);
  1333. // Insert item at head of global log.
  1334. //
  1335. InsertHeadList(&RmGlobals.listGlobalLog, &pLogEntry->linkGlobalLog);
  1336. pDiagInfo->NumObjectLogEntries++;
  1337. RmGlobals.NumGlobalLogEntries++;
  1338. }
  1339. NdisReleaseSpinLock(&RmGlobals.GlobalOsLock);
  1340. #if 0
  1341. pfnDumpEntry(
  1342. szFormatString,
  1343. Param1,
  1344. Param2,
  1345. Param3,
  1346. Param4
  1347. );
  1348. #endif // 0
  1349. }
  1350. else
  1351. {
  1352. // TODO -- free pvBuf if NON NULL.
  1353. }
  1354. EXIT()
  1355. #endif // RM_EXTRA_CHECKING
  1356. }
  1357. VOID
  1358. RmDbgPrintObjectLog(
  1359. IN PRM_OBJECT_HEADER pObject
  1360. )
  1361. /*++
  1362. Routine Description:
  1363. (Debug only) Dumps object pObject's log.
  1364. --*/
  1365. {
  1366. #if RM_EXTRA_CHECKING
  1367. ENTER("RmPrintObjectLog", 0xe06507e5)
  1368. PRM_OBJECT_DIAGNOSTIC_INFO pDiagInfo = pObject->pDiagInfo;
  1369. TR_INFO((" pObj=0x%p (%s)\n", pObject, pObject->szDescription));
  1370. if (pDiagInfo != NULL)
  1371. {
  1372. LIST_ENTRY *pLink=NULL;
  1373. LIST_ENTRY * pObjectLog = &pDiagInfo->listObjectLog;
  1374. NdisAcquireSpinLock(&RmGlobals.GlobalOsLock);
  1375. for(
  1376. pLink = pObjectLog->Flink;
  1377. pLink != pObjectLog;
  1378. pLink = pLink->Flink)
  1379. {
  1380. RM_DBG_LOG_ENTRY *pLE;
  1381. pLE = CONTAINING_RECORD(pLink, RM_DBG_LOG_ENTRY, linkObjectLog);
  1382. if (pLE->szPrefix != NULL)
  1383. {
  1384. // Print the prefix.
  1385. DbgPrint(pLE->szPrefix);
  1386. }
  1387. // Call the dump function for this entry.
  1388. //
  1389. //
  1390. pLE->pfnDumpEntry(
  1391. pLE->szFormatString,
  1392. pLE->Param1,
  1393. pLE->Param2,
  1394. pLE->Param3,
  1395. pLE->Param4
  1396. );
  1397. }
  1398. NdisReleaseSpinLock(&RmGlobals.GlobalOsLock);
  1399. }
  1400. EXIT()
  1401. #endif // RM_EXTRA_CHECKING
  1402. }
  1403. VOID
  1404. RmDbgPrintGlobalLog(VOID)
  1405. /*++
  1406. Routine Description:
  1407. (Debug only) Dumps the global log (which contains entries from all object's
  1408. logs.
  1409. --*/
  1410. {
  1411. #if RM_EXTRA_CHECKING
  1412. ENTER("RmPrintGlobalLog", 0xe9915066)
  1413. LIST_ENTRY *pLink=NULL;
  1414. LIST_ENTRY *pGlobalLog = &RmGlobals.listGlobalLog;
  1415. TR_INFO(("Enter\n"));
  1416. NdisAcquireSpinLock(&RmGlobals.GlobalOsLock);
  1417. for(
  1418. pLink = pGlobalLog->Flink;
  1419. pLink != pGlobalLog;
  1420. pLink = pLink->Flink)
  1421. {
  1422. RM_DBG_LOG_ENTRY *pLE;
  1423. pLE = CONTAINING_RECORD(pLink, RM_DBG_LOG_ENTRY, linkGlobalLog);
  1424. // Print the ptr and name of the object whose entry this is...
  1425. //
  1426. DbgPrint(
  1427. "Entry for 0x%p (%s):\n",
  1428. pLE->pObject,
  1429. pLE->pObject->szDescription
  1430. );
  1431. if (pLE->szPrefix != NULL)
  1432. {
  1433. // Print the prefix.
  1434. DbgPrint(pLE->szPrefix);
  1435. }
  1436. // Call the dump function for this entry.
  1437. //
  1438. //
  1439. pLE->pfnDumpEntry(
  1440. pLE->szFormatString,
  1441. pLE->Param1,
  1442. pLE->Param2,
  1443. pLE->Param3,
  1444. pLE->Param4
  1445. );
  1446. }
  1447. NdisReleaseSpinLock(&RmGlobals.GlobalOsLock);
  1448. EXIT()
  1449. #endif // RM_EXTRA_CHECKING
  1450. }
  1451. RM_STATUS
  1452. RmLoadGenericResource(
  1453. IN PRM_OBJECT_HEADER pObj,
  1454. IN UINT GenericResourceID,
  1455. IN PRM_STACK_RECORD pSR
  1456. )
  1457. /*++
  1458. Routine Description:
  1459. TODO This function is going away...
  1460. --*/
  1461. {
  1462. PRM_STATIC_OBJECT_INFO pSI = pObj->pStaticInfo;
  1463. RM_STATUS Status;
  1464. // The resource ID should be less than number of bits in the ResourceMap
  1465. //
  1466. ASSERT(GenericResourceID < 8*sizeof(pObj->ResourceMap));
  1467. RMPRIVATELOCK(pObj, pSR);
  1468. do
  1469. {
  1470. UINT ResFlag = 1<<GenericResourceID;
  1471. if (!RMISALLOCATED(pObj))
  1472. {
  1473. Status = NDIS_STATUS_FAILURE;
  1474. RMPRIVATEUNLOCK(pObj, pSR);
  1475. break;
  1476. }
  1477. if (pSI->NumResourceTableEntries <= GenericResourceID)
  1478. {
  1479. ASSERTEX(!"Invalid GenericResourceID", pObj);
  1480. Status = NDIS_STATUS_FAILURE;
  1481. RMPRIVATEUNLOCK(pObj, pSR);
  1482. break;
  1483. }
  1484. // The resource entry indexed must have its ID == GenericResourceID
  1485. //
  1486. //
  1487. if (pSI->pResourceTable[GenericResourceID].ID != GenericResourceID)
  1488. {
  1489. ASSERTEX(!"Resource ID doesn't match table entry", pObj);
  1490. Status = NDIS_STATUS_FAILURE;
  1491. RMPRIVATEUNLOCK(pObj, pSR);
  1492. break;
  1493. }
  1494. if ( ResFlag & pObj->ResourceMap)
  1495. {
  1496. ASSERTEX(!"Resource already allocated", pObj);
  1497. Status = NDIS_STATUS_FAILURE;
  1498. RMPRIVATEUNLOCK(pObj, pSR);
  1499. break;
  1500. }
  1501. pObj->ResourceMap |= ResFlag;
  1502. RMPRIVATEUNLOCK(pObj, pSR);
  1503. Status = pSI->pResourceTable[GenericResourceID].pfnHandler(
  1504. pObj,
  1505. RM_RESOURCE_OP_LOAD,
  1506. NULL, // pvUserParams (unused)
  1507. pSR
  1508. );
  1509. if (FAIL(Status))
  1510. {
  1511. // Clear the resource map bit on failure.
  1512. //
  1513. RMPRIVATELOCK(pObj, pSR);
  1514. ASSERTEX(ResFlag & pObj->ResourceMap, pObj);
  1515. pObj->ResourceMap &= ~ResFlag;
  1516. RMPRIVATEUNLOCK(pObj, pSR);
  1517. }
  1518. } while (FALSE);
  1519. return Status;
  1520. }
  1521. VOID
  1522. RmUnloadGenericResource(
  1523. IN PRM_OBJECT_HEADER pObj,
  1524. IN UINT GenericResourceID,
  1525. IN PRM_STACK_RECORD pSR
  1526. )
  1527. /*++
  1528. Routine Description:
  1529. TODO This function is going away...
  1530. --*/
  1531. {
  1532. PRM_STATIC_OBJECT_INFO pSI = pObj->pStaticInfo;
  1533. RM_STATUS Status;
  1534. // The resource ID should be less than number of bits in the ResourceMap
  1535. //
  1536. ASSERT(GenericResourceID < 8*sizeof(pObj->ResourceMap));
  1537. RMPRIVATELOCK(pObj, pSR);
  1538. do
  1539. {
  1540. UINT ResFlag = 1<<GenericResourceID;
  1541. if (pSI->NumResourceTableEntries <= GenericResourceID)
  1542. {
  1543. ASSERTEX(!"Invalid GenericResourceID", pObj);
  1544. RMPRIVATEUNLOCK(pObj, pSR);
  1545. break;
  1546. }
  1547. if ( !(ResFlag & pObj->ResourceMap))
  1548. {
  1549. ASSERTEX(!"Resource not allocated", pObj);
  1550. RMPRIVATEUNLOCK(pObj, pSR);
  1551. break;
  1552. }
  1553. // Clear the resource flag.
  1554. //
  1555. pObj->ResourceMap &= ~ResFlag;
  1556. RMPRIVATEUNLOCK(pObj, pSR);
  1557. pSI->pResourceTable[GenericResourceID].pfnHandler(
  1558. pObj,
  1559. RM_RESOURCE_OP_UNLOAD,
  1560. NULL, // pvUserParams (unused)
  1561. pSR
  1562. );
  1563. } while (FALSE);
  1564. }
  1565. VOID
  1566. RmUnloadAllGenericResources(
  1567. IN PRM_OBJECT_HEADER pObj,
  1568. IN PRM_STACK_RECORD pSR
  1569. )
  1570. /*++
  1571. Synchronously unload all previously loaded resources for this object,
  1572. in reverse order to which they were loaded.
  1573. TODO this function is going away...
  1574. --*/
  1575. {
  1576. PRM_STATIC_OBJECT_INFO pSI = pObj->pStaticInfo;
  1577. RM_STATUS Status;
  1578. UINT u;
  1579. RMPRIVATELOCK(pObj, pSR);
  1580. for(u = pSI->NumResourceTableEntries;
  1581. u && pObj->ResourceMap;
  1582. u--)
  1583. {
  1584. UINT ResID = u-1;
  1585. UINT ResFlag = 1<<ResID;
  1586. if ( !(ResFlag & pObj->ResourceMap))
  1587. {
  1588. continue;
  1589. }
  1590. if (pSI->NumResourceTableEntries <= ResID)
  1591. {
  1592. ASSERTEX(!"Corrupt ResourceMap", pObj);
  1593. RMPRIVATEUNLOCK(pObj, pSR);
  1594. break;
  1595. }
  1596. // Clear the resource flag.
  1597. //
  1598. pObj->ResourceMap &= ~ResFlag;
  1599. RMPRIVATEUNLOCK(pObj, pSR);
  1600. pSI->pResourceTable[ResID].pfnHandler(
  1601. pObj,
  1602. RM_RESOURCE_OP_UNLOAD,
  1603. NULL, // pvUserParams (unused)
  1604. pSR
  1605. );
  1606. RMPRIVATELOCK(pObj, pSR);
  1607. }
  1608. ASSERTEX(!pObj->ResourceMap, pObj);
  1609. RMPRIVATEUNLOCK(pObj, pSR);
  1610. }
  1611. VOID
  1612. RmInitializeGroup(
  1613. IN PRM_OBJECT_HEADER pOwningObject,
  1614. IN PRM_STATIC_OBJECT_INFO pStaticInfo,
  1615. IN PRM_GROUP pGroup,
  1616. IN const char* szDescription,
  1617. IN PRM_STACK_RECORD pSR
  1618. )
  1619. /*++
  1620. Routine Description:
  1621. Initialize a group structure.
  1622. Arguments:
  1623. pOwningObject - Object that will own the group.
  1624. pStaticInfo - Static information about objects IN the group.
  1625. pGroup - Uninitialized memory that is to hold the group structure. It
  1626. will be initialized on return from this function.
  1627. szDescription - (Debug only) descriptive name for this group.
  1628. TODO: make pStaticInfo const.
  1629. --*/
  1630. {
  1631. NdisZeroMemory(pGroup, sizeof(*pGroup));
  1632. RMPRIVATELOCK(pOwningObject, pSR);
  1633. do
  1634. {
  1635. if (!RMISALLOCATED(pOwningObject))
  1636. {
  1637. ASSERT(!"pObject not allocated");
  1638. break;
  1639. }
  1640. if (pStaticInfo->pHashInfo == NULL)
  1641. {
  1642. ASSERT(!"NULL pHashInfo");
  1643. // Static info MUST have non-NULL pHashInfo in order
  1644. // for it to be used for groups.
  1645. //
  1646. break;
  1647. }
  1648. RmInitializeHashTable(
  1649. pStaticInfo->pHashInfo,
  1650. pOwningObject, // pAllocationContext
  1651. &pGroup->HashTable
  1652. );
  1653. pGroup->pOwningObject = pOwningObject;
  1654. pGroup->pStaticInfo = pStaticInfo;
  1655. pGroup->szDescription = szDescription;
  1656. NdisAllocateSpinLock(&pGroup->OsLock);
  1657. pGroup->fEnabled = TRUE;
  1658. #if RM_EXTRA_CHECKING
  1659. RmDbgAddAssociation(
  1660. 0xc0e5362f, // Location ID
  1661. pOwningObject, // pObject
  1662. (UINT_PTR) pGroup, // Instance1
  1663. (UINT_PTR) (pGroup->szDescription), // Instance2
  1664. RM_PRIVATE_ASSOC_INITGROUP, // AssociationID
  1665. szASSOCFORMAT_INITGROUP, // szAssociationFormat
  1666. pSR
  1667. );
  1668. #endif // RM_EXTRA_CHECKING
  1669. } while (FALSE);
  1670. RMPRIVATEUNLOCK(pOwningObject, pSR);
  1671. }
  1672. VOID
  1673. RmDeinitializeGroup(
  1674. IN PRM_GROUP pGroup,
  1675. IN PRM_STACK_RECORD pSR
  1676. )
  1677. /*++
  1678. Routine Description:
  1679. Deinitialize group structure pGroup. Must only be called when there are no
  1680. members in the group.
  1681. --*/
  1682. {
  1683. #if RM_EXTRA_CHECKING
  1684. RmDbgDeleteAssociation(
  1685. 0x1486def9, // Location ID
  1686. pGroup->pOwningObject, // pObject
  1687. (UINT_PTR) pGroup, // Instance1
  1688. (UINT_PTR) (pGroup->szDescription), // Instance2
  1689. RM_PRIVATE_ASSOC_INITGROUP, // AssociationID
  1690. pSR
  1691. );
  1692. #endif // RM_EXTRA_CHECKING
  1693. NdisAcquireSpinLock(&pGroup->OsLock);
  1694. RmDeinitializeHashTable(&pGroup->HashTable);
  1695. NdisReleaseSpinLock(&pGroup->OsLock);
  1696. NdisFreeSpinLock(&pGroup->OsLock);
  1697. NdisZeroMemory(pGroup, sizeof(*pGroup));
  1698. }
  1699. RM_STATUS
  1700. RmLookupObjectInGroup(
  1701. IN PRM_GROUP pGroup,
  1702. IN ULONG Flags, // create, remove, lock
  1703. IN PVOID pvKey,
  1704. IN PVOID pvCreateParams,
  1705. OUT PRM_OBJECT_HEADER * ppObject,
  1706. OUT INT * pfCreated, OPTIONAL
  1707. IN PRM_STACK_RECORD pSR
  1708. )
  1709. /*++
  1710. Routine Description:
  1711. TODO: split this into a pure lookup and a lookupand/orcreate function..
  1712. Lookup and/or create an object in the specified group.
  1713. #if OBSOLETE // Must allow fCreate w/o locking -- see notes.txt entry:
  1714. // 03/04/1999 JosephJ Problems with deadlock when using Groups.
  1715. MUST ONLY be NON-NULL if the fLOCKED flag is specified.
  1716. Why? Because if the lock is not held on exit, it would be possible
  1717. for someone else to pick up the object in the freshly-created state.
  1718. We want to discourage that situation.
  1719. #endif // OBSOLETE
  1720. Typically the caller specifes the
  1721. fRM_LOCKED|fRM_CREATE flags as well as non-null pfCreated. On return, if
  1722. *pfCreated is TRUE, the caller then would go on to do some more
  1723. initialization before releasing the lock.
  1724. A new important point is that if the user requests that the object be
  1725. created and locked, then the RM no longer guranteees that the create and
  1726. lock will happen in the context of holding the GroupLock
  1727. FUNDAMENTAL ASSUMPTION: The key of an object doesn't change once
  1728. it's in the group. Based on this assumption, we don't try to claim
  1729. the object's lock when looking for the object with a matching key.
  1730. Arguments:
  1731. pGroup - Group in which to lookup/create object.
  1732. Flags - One or more of fRM_LOCKED, fRM_CREATE, fRM_NEW
  1733. pvKey - Key used to lookup object.
  1734. pvCreateParams - If object is to be created, parameters to be passed to the
  1735. object's creation function.
  1736. ppObject - Place to store pointer to the found/created object.
  1737. pfCreated - If non-NULL, *pfCreated is set to TRUE IFF the object was
  1738. created.
  1739. Return Value:
  1740. NDIS_STATUS_SUCCESS If the operation succeeded.
  1741. NDIS_STATUS_RESOURCES If a new object could not be created.
  1742. NDIS_STATUS_RFAILURE If the object was not found.
  1743. --*/
  1744. {
  1745. RM_STATUS Status = NDIS_STATUS_FAILURE;
  1746. BOOLEAN fUnlockOutOfOrder = FALSE;
  1747. PRM_OBJECT_HEADER pOwningObject = pGroup->pOwningObject;
  1748. PRM_OBJECT_HEADER pObject;
  1749. #if DBG
  1750. KIRQL EntryIrql = KeGetCurrentIrql();
  1751. #endif // DBG
  1752. ENTER("RmLookupObjectInGroup", 0xd2cd6379)
  1753. ASSERT(pOwningObject!=NULL);
  1754. // OBSOLETE -- see comments above: ASSERT(pfCreated==NULL || (Flags&RM_LOCKED));
  1755. if (pfCreated != NULL) *pfCreated = FALSE;
  1756. NdisAcquireSpinLock(&pGroup->OsLock);
  1757. do
  1758. {
  1759. BOOLEAN fFound;
  1760. PRM_HASH_LINK *ppLink = NULL;
  1761. if (!RMISALLOCATED(pGroup->pOwningObject)) break;
  1762. if (pGroup->fEnabled != TRUE) break;
  1763. fFound = RmLookupHashTable(
  1764. &pGroup->HashTable,
  1765. &ppLink,
  1766. pvKey
  1767. );
  1768. if (fFound)
  1769. {
  1770. if (Flags & RM_NEW)
  1771. {
  1772. // Caller wanted us to created a new object, but the object already
  1773. // exists, so we fail...
  1774. //
  1775. // TODO: return appropriate error code.
  1776. //
  1777. break;
  1778. }
  1779. // Go from hash-link to object.
  1780. // TODO: once HashLink goes away, need some other way to get
  1781. // to the object.
  1782. //
  1783. pObject = CONTAINING_RECORD(*ppLink, RM_OBJECT_HEADER, HashLink);
  1784. ASSERT(pObject->pStaticInfo == pGroup->pStaticInfo);
  1785. }
  1786. else
  1787. {
  1788. if (!(Flags & RM_CREATE))
  1789. {
  1790. // Couldn't find it, and caller doesn't want us to create one, so
  1791. // we fail...
  1792. break;
  1793. }
  1794. // Create object...
  1795. //
  1796. ASSERTEX(pGroup->pStaticInfo->pfnCreate!=NULL, pGroup);
  1797. pObject = pGroup->pStaticInfo->pfnCreate(
  1798. pOwningObject,
  1799. pvCreateParams,
  1800. pSR
  1801. );
  1802. if (pObject == NULL)
  1803. {
  1804. Status = NDIS_STATUS_RESOURCES;
  1805. break;
  1806. }
  1807. TR_INFO((
  1808. "Created 0x%p (%s) in Group 0x%p (%s)\n",
  1809. pObject,
  1810. pObject->szDescription,
  1811. pGroup,
  1812. pGroup->szDescription
  1813. ));
  1814. ASSERTEX(RMISALLOCATED(pObject), pObject);
  1815. // Now enter it into the hash table.
  1816. //
  1817. RmAddHashItem(
  1818. &pGroup->HashTable,
  1819. ppLink,
  1820. &pObject->HashLink,
  1821. pvKey
  1822. );
  1823. if (pfCreated != NULL)
  1824. {
  1825. *pfCreated = TRUE;
  1826. }
  1827. }
  1828. RmTmpReferenceObject(pObject, pSR);
  1829. Status = NDIS_STATUS_SUCCESS;
  1830. } while(FALSE);
  1831. NdisReleaseSpinLock(&pGroup->OsLock);
  1832. // Do we need to look the object before returning it
  1833. // to the user
  1834. if ((!FAIL(Status)) && (Flags & RM_LOCKED))
  1835. {
  1836. RmWriteLockObject(
  1837. pObject,
  1838. #if RM_EXTRA_CHECKING
  1839. 0x6197fdda,
  1840. #endif //RM_EXTRA_CHECKING
  1841. pSR
  1842. );
  1843. if (!RMISALLOCATED(pObject))
  1844. {
  1845. // We don't allow this...
  1846. RmUnlockObject(
  1847. pObject,
  1848. pSR
  1849. );
  1850. }
  1851. }
  1852. if (FAIL(Status))
  1853. {
  1854. *ppObject = NULL;
  1855. }
  1856. else
  1857. {
  1858. *ppObject = pObject;
  1859. }
  1860. #if DBG
  1861. {
  1862. KIRQL ExitIrql = KeGetCurrentIrql();
  1863. TR_VERB(("Exiting. EntryIrql=%lu, ExitIrql = %lu\n", EntryIrql, ExitIrql));
  1864. }
  1865. #endif //DBG
  1866. return Status;
  1867. }
  1868. RM_STATUS
  1869. RmGetNextObjectInGroup(
  1870. IN PRM_GROUP pGroup,
  1871. IN PRM_OBJECT_HEADER pCurrentObject, // OPTIONAL
  1872. OUT PRM_OBJECT_HEADER * ppNextObject,
  1873. IN PRM_STACK_RECORD pSR
  1874. )
  1875. /*++
  1876. Routine Description:
  1877. Get the 1st object in group (if pCurrentObject == NULL), or the object
  1878. "after" pCurrentObject (if pCurrentObject != NULL).
  1879. The definition of "after" is hidden -- the only guarantee is if this
  1880. function is 1st called with NULL pCurrentObject and subsequently with
  1881. pCurrentObject set to the value previously returned in ppNextObject, until
  1882. the function returns NDIS_STATUS_FAILURE, all objects in the group will
  1883. be returned once and only once. This guarantee is only valid if no objects
  1884. are added or removed during the enumeration process.
  1885. On success, the "next" object is tmpref'd a pointer to it is saved in
  1886. *ppNextObject.
  1887. Arguments:
  1888. pGroup - The group
  1889. pCurrentObject - (OPTIONAL) An object in the group.
  1890. ppNextObject - Place to return the the object "after" pCurrentObject
  1891. (see RoutineDescription for details.)
  1892. Return Value:
  1893. NDIS_STATUS_SUCCESS if we could find a "next" object.
  1894. NDIS_STATUS_FAILURE otherwise
  1895. --*/
  1896. {
  1897. RM_STATUS Status = NDIS_STATUS_FAILURE;
  1898. PRM_OBJECT_HEADER pOwningObject = pGroup->pOwningObject;
  1899. PRM_OBJECT_HEADER pObject;
  1900. ENTER("RmGetNextObjectInGroup", 0x11523db7)
  1901. ASSERT(pOwningObject!=NULL);
  1902. NdisAcquireSpinLock(&pGroup->OsLock);
  1903. do
  1904. {
  1905. BOOLEAN fFound;
  1906. PRM_HASH_LINK pLink = NULL;
  1907. PRM_HASH_LINK pCurrentLink = NULL;
  1908. if (!RMISALLOCATED(pGroup->pOwningObject)) break;
  1909. if (pGroup->fEnabled != TRUE) break;
  1910. if (pCurrentObject != NULL)
  1911. {
  1912. pCurrentLink = &pCurrentObject->HashLink;
  1913. }
  1914. fFound = RmNextHashTableItem(
  1915. &pGroup->HashTable,
  1916. pCurrentLink, // pCurrentLink
  1917. &pLink // pNextLink
  1918. );
  1919. if (fFound)
  1920. {
  1921. // Go from hash-link to object.
  1922. // TODO: once HashLink goes away, need some other way to get
  1923. // to the object.
  1924. //
  1925. pObject = CONTAINING_RECORD(pLink, RM_OBJECT_HEADER, HashLink);
  1926. ASSERT(pObject->pStaticInfo == pGroup->pStaticInfo);
  1927. }
  1928. else
  1929. {
  1930. // Couldn't find one.
  1931. // we fail...
  1932. break;
  1933. }
  1934. RmTmpReferenceObject(pObject, pSR);
  1935. Status = NDIS_STATUS_SUCCESS;
  1936. } while(FALSE);
  1937. NdisReleaseSpinLock(&pGroup->OsLock);
  1938. if (FAIL(Status))
  1939. {
  1940. *ppNextObject = NULL;
  1941. }
  1942. else
  1943. {
  1944. *ppNextObject = pObject;
  1945. }
  1946. return Status;
  1947. }
  1948. VOID
  1949. RmFreeObjectInGroup(
  1950. IN PRM_GROUP pGroup,
  1951. IN PRM_OBJECT_HEADER pObject,
  1952. IN struct _RM_TASK *pTask, OPTIONAL // Unused. TODO: remove this.
  1953. IN PRM_STACK_RECORD pSR
  1954. )
  1955. /*++
  1956. Routine Description:
  1957. Remove object pObject from group pGroup and deallocate pObject.
  1958. --*/
  1959. {
  1960. ENTER("RmFreeObjectInGroup", 0xd2cd6379)
  1961. PRM_OBJECT_HEADER pOwningObject = pGroup->pOwningObject;
  1962. ASSERTEX(pOwningObject!=NULL, pGroup);
  1963. ASSERTEX(pTask==NULL, pGroup);
  1964. NdisAcquireSpinLock(&pGroup->OsLock);
  1965. // TODO: what if at this time, someone else is doing FreeAllObjects in Group?
  1966. //
  1967. TR_INFO((
  1968. "Freeing 0x%p (%s) in Group 0x%p (%s)\n",
  1969. pObject,
  1970. pObject->szDescription,
  1971. pGroup,
  1972. pGroup->szDescription
  1973. ));
  1974. ASSERTEX(RMISALLOCATED(pObject), pObject);
  1975. RmRemoveHashItem(
  1976. &pGroup->HashTable,
  1977. &pObject->HashLink
  1978. );
  1979. NdisReleaseSpinLock(&pGroup->OsLock);
  1980. // Deallocate the object.
  1981. //
  1982. RmDeallocateObject(
  1983. pObject,
  1984. pSR
  1985. );
  1986. EXIT()
  1987. }
  1988. VOID
  1989. RmFreeAllObjectsInGroup(
  1990. IN PRM_GROUP pGroup,
  1991. IN struct _RM_TASK *pTask, OPTIONAL // Unused. TODO: remove this.
  1992. IN PRM_STACK_RECORD pSR
  1993. )
  1994. /*++
  1995. Routine Description:
  1996. Remove and deallocate all object in pGroup.
  1997. --*/
  1998. {
  1999. PRM_HASH_LINK *ppLink, *ppLinkEnd;
  2000. NdisAcquireSpinLock(&pGroup->OsLock);
  2001. if (pGroup->fEnabled)
  2002. {
  2003. pGroup->fEnabled = FALSE;
  2004. }
  2005. else
  2006. {
  2007. NdisReleaseSpinLock(&pGroup->OsLock);
  2008. return; // EARLY RETURN
  2009. }
  2010. //
  2011. // With fEnabled set to FALSE by us, we expect the following:
  2012. // (a) pHashTable->pTable is going to stay the same size.
  2013. // (b) No items are going to be added or removed by anyone else.
  2014. //
  2015. ppLink = pGroup->HashTable.pTable;
  2016. ppLinkEnd = ppLink + pGroup->HashTable.TableLength;
  2017. for ( ; ppLink < ppLinkEnd; ppLink++)
  2018. {
  2019. while (*ppLink != NULL)
  2020. {
  2021. PRM_HASH_LINK pLink = *ppLink;
  2022. PRM_OBJECT_HEADER pObj;
  2023. // Remove it from the bucket list.
  2024. //
  2025. *ppLink = pLink->pNext;
  2026. pLink->pNext = NULL;
  2027. pGroup->HashTable.NumItems--;
  2028. NdisReleaseSpinLock(&pGroup->OsLock);
  2029. pObj = CONTAINING_RECORD(pLink, RM_OBJECT_HEADER, HashLink);
  2030. ASSERT(pObj->pStaticInfo == pGroup->pStaticInfo);
  2031. // Deallocate the object.
  2032. //
  2033. RmDeallocateObject(
  2034. pObj,
  2035. pSR
  2036. );
  2037. NdisAcquireSpinLock(&pGroup->OsLock);
  2038. }
  2039. }
  2040. NdisReleaseSpinLock(&pGroup->OsLock);
  2041. }
  2042. VOID
  2043. RmUnloadAllObjectsInGroup(
  2044. IN PRM_GROUP pGroup,
  2045. PFN_RM_TASK_ALLOCATOR pfnUnloadTaskAllocator,
  2046. PFN_RM_TASK_HANDLER pfnUnloadTaskHandler,
  2047. PVOID pvUserParam,
  2048. IN struct _RM_TASK *pTask, OPTIONAL
  2049. IN UINT uTaskPendCode,
  2050. IN PRM_STACK_RECORD pSR
  2051. )
  2052. /*++
  2053. Routine Description:
  2054. Stops new objects from being added and unloads(see below) all objects
  2055. currently in the group.
  2056. "Unload" consists of allocating and starting a pfnUnloadTaskHask task
  2057. on each object. The unload task is responsible for
  2058. removing and deallocating the object from the group.
  2059. If pTask if non-NULL, it will be resumed on completion of the unload.
  2060. Otherwise, this function will BLOCK until the unload is complete.
  2061. Arguments:
  2062. pGroup - Group to unload.
  2063. pfnUnloadTaskAllocator - Use to allocate the object-unload tasks.
  2064. pfnTaskAllocator - Function used to allocate the unload task.
  2065. pfnUnloadTaskHandler - The handler of the unload task
  2066. pvUserParam - Task creation user-param.
  2067. WARNING: this param must be valid for the duration
  2068. of the unload process, not just until this
  2069. function returns. Of course, if pTask is NULL,
  2070. the two cases are equivalent.
  2071. pTask - (OPTIONAL) Task to resume when unload is complete.
  2072. If NULL, this function will block until the
  2073. unload is complete.
  2074. uTaskPendCode - (OPTIONAL) PendCode to use when resuming pTask.
  2075. --*/
  2076. {
  2077. PRM_TASK pUnloadTask;
  2078. NDIS_STATUS Status;
  2079. NdisAcquireSpinLock(&pGroup->OsLock);
  2080. //
  2081. // We don't check if there is already an unload task active for this group.
  2082. // Instead we go ahead and allocate and start an unload task. This latter
  2083. // task will pend on the already running unload task if there is on.
  2084. //
  2085. // Allocate a private task to coordinate the unloading of all the objects.
  2086. //
  2087. Status = rmAllocatePrivateTask(
  2088. pGroup->pOwningObject,
  2089. rmTaskUnloadGroup,
  2090. 0,
  2091. "Task:UnloadAllObjectsInGroup",
  2092. &pUnloadTask,
  2093. pSR
  2094. );
  2095. if (FAIL(Status))
  2096. {
  2097. //
  2098. // Ouch -- ugly failure...
  2099. //
  2100. ASSERT(FALSE);
  2101. NdisReleaseSpinLock(&pGroup->OsLock);
  2102. }
  2103. else
  2104. {
  2105. TASK_UNLOADGROUP *pUGTask = (TASK_UNLOADGROUP *) pUnloadTask;
  2106. pUGTask->pGroup = pGroup;
  2107. pUGTask->pfnTaskUnloadObjectHandler = pfnUnloadTaskHandler;
  2108. pUGTask->pfnUnloadTaskAllocator = pfnUnloadTaskAllocator;
  2109. if (pTask == NULL)
  2110. {
  2111. // Set up an event which we'll wait on. The event will be signaled
  2112. // by pUnloadTask when it completes.
  2113. //
  2114. NdisInitializeEvent(&pUGTask->BlockEvent);
  2115. pUGTask->fUseEvent = TRUE;
  2116. // Tmpref it so pUnloadTask will stay around even afer it's
  2117. // completed -- because we wait on the event that's actually
  2118. // located in the task memory.
  2119. //
  2120. RmTmpReferenceObject(&pUnloadTask->Hdr, pSR);
  2121. }
  2122. NdisReleaseSpinLock(&pGroup->OsLock);
  2123. if (pTask != NULL)
  2124. {
  2125. RmPendTaskOnOtherTask(
  2126. pTask,
  2127. uTaskPendCode,
  2128. pUnloadTask,
  2129. pSR
  2130. );
  2131. }
  2132. Status = RmStartTask(pUnloadTask, 0, pSR);
  2133. if (pTask == NULL)
  2134. {
  2135. NdisWaitEvent(&pUGTask->BlockEvent, 0);
  2136. RmTmpDereferenceObject(&pUnloadTask->Hdr, pSR);
  2137. }
  2138. }
  2139. }
  2140. VOID
  2141. RmEnableGroup(
  2142. IN PRM_GROUP pGroup,
  2143. IN PRM_STACK_RECORD pSR
  2144. )
  2145. /*++
  2146. TODO: need better name for this.
  2147. Routine Description:
  2148. Enables items to be added to a group.
  2149. This function is typically called with a group which has completed
  2150. RmUnloadAllObjectsFromGroup or RmFreeAllObjectsInGroup.
  2151. On return from this call items may once more be added to this group.
  2152. This call must only be called after UnloadAllObjectsInGroup or
  2153. RmFreeAllObjectsInGroup have completed (synchronously or asynchronously).
  2154. If there are items in in group or there is an unload
  2155. task associated with the group at the time this function is called,
  2156. the group is NOT reinited and the DBG version will assert.
  2157. This function and may be called with an already enabled group, provided
  2158. the condition above is met (no items in group, no unload task).
  2159. --*/
  2160. {
  2161. NdisAcquireSpinLock(&pGroup->OsLock);
  2162. if ( pGroup->pUnloadTask == NULL
  2163. && pGroup->HashTable.NumItems == 0)
  2164. {
  2165. pGroup->fEnabled = TRUE;
  2166. }
  2167. else
  2168. {
  2169. ASSERT("invalid state.");
  2170. }
  2171. NdisReleaseSpinLock(&pGroup->OsLock);
  2172. }
  2173. VOID
  2174. RmInitializeTask(
  2175. IN PRM_TASK pTask,
  2176. IN PRM_OBJECT_HEADER pParentObject,
  2177. IN PFN_RM_TASK_HANDLER pfnHandler,
  2178. IN PRM_STATIC_OBJECT_INFO pStaticInfo, OPTIONAL
  2179. IN const char * szDescription, OPTIONAL
  2180. IN UINT Timeout,
  2181. IN PRM_STACK_RECORD pSR
  2182. )
  2183. /*++
  2184. Routine Description:
  2185. Initialize the specified task.
  2186. The task is tempref'd. It is the responsibility of the caller to
  2187. de-ref it when done. Typically this is implicitly done by calling
  2188. RmStartTask.
  2189. Arguments:
  2190. pTask - points to unitialized memory to hold the task.
  2191. pParentObject - will be the parent of the task.
  2192. pfnHandler - task's handler function.
  2193. pStaticInfo - (OPTIONAL) Static information about the task.
  2194. szDescription - (debug only, OPTIONAL) description of the task
  2195. Timeout - unused
  2196. --*/
  2197. {
  2198. ASSERT(!Timeout); // TODO: Timeouts unimplemented.
  2199. NdisZeroMemory(pTask, sizeof(*pTask));
  2200. RmInitializeHeader(
  2201. pParentObject,
  2202. &pTask->Hdr,
  2203. MTAG_TASK,
  2204. pParentObject->pLock,
  2205. (pStaticInfo) ? pStaticInfo : &RmTask_StaticInfo,
  2206. szDescription,
  2207. pSR
  2208. );
  2209. pTask->pfnHandler = pfnHandler;
  2210. SET_RM_TASK_STATE(pTask, RMTSKSTATE_IDLE);
  2211. InitializeListHead(&pTask->listTasksPendingOnMe);
  2212. RmTmpReferenceObject(&pTask->Hdr, pSR);
  2213. }
  2214. VOID
  2215. RmAbortTask(
  2216. IN PRM_TASK pTask,
  2217. IN PRM_STACK_RECORD pSR
  2218. )
  2219. {
  2220. ASSERT(!"Unimplemented");
  2221. }
  2222. RM_STATUS
  2223. RmStartTask(
  2224. IN PRM_TASK pTask,
  2225. IN UINT_PTR UserParam,
  2226. IN PRM_STACK_RECORD pSR
  2227. )
  2228. /*++
  2229. Routine Description:
  2230. Start the specified task.
  2231. NO locks should be held on entry and none are held on exit.
  2232. pTask is expected to have a tmp-ref which is deref'd here.
  2233. The task is automatically deallocated on completion (either synchronous
  2234. or asynchronous completion, either successful or failed completion).
  2235. Unless the caller is explicitly added a reference to pTask before calling
  2236. this function, the caller should not assume that pTask is still valid
  2237. on return from this function.
  2238. Arguments:
  2239. pTask - points to the task to be started.
  2240. UserParam - opaque value passed to the task handler with the
  2241. RM_TASKOP_START message.
  2242. --*/
  2243. {
  2244. ENTER("RmStartTask", 0xf80502d5)
  2245. NDIS_STATUS Status;
  2246. RM_ASSERT_NOLOCKS(pSR);
  2247. RMPRIVATELOCK(&pTask->Hdr, pSR);
  2248. if (!CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_IDLE))
  2249. {
  2250. ASSERTEX(!"Invalid state", pTask);
  2251. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2252. Status = NDIS_STATUS_FAILURE;
  2253. }
  2254. else
  2255. {
  2256. if (!RMISALLOCATED(pTask->Hdr.pParentObject))
  2257. {
  2258. //
  2259. // TODO: consider not calling the handler if the parent object is
  2260. // deallocated, but that may be confusing.
  2261. // Consider not allowing children to be linked to an object
  2262. // (RmInitializeHeader returns failure) if the parent object is
  2263. // deallocated.
  2264. //
  2265. TR_WARN((
  2266. "Starting task 0x%p (%s) with DEALLOCATED parent 0x%p (%s).\n",
  2267. pTask,
  2268. pTask->Hdr.szDescription,
  2269. pTask->Hdr.pParentObject,
  2270. pTask->Hdr.pParentObject->szDescription
  2271. ));
  2272. }
  2273. SET_RM_TASK_STATE(pTask, RMTSKSTATE_STARTING);
  2274. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2275. TR_INFO((
  2276. "STARTING Task 0x%p (%s); UserParam = 0x%lx\n",
  2277. pTask,
  2278. pTask->Hdr.szDescription,
  2279. UserParam
  2280. ));
  2281. Status = pTask->pfnHandler(
  2282. pTask,
  2283. RM_TASKOP_START,
  2284. UserParam,
  2285. pSR
  2286. );
  2287. RM_ASSERT_NOLOCKS(pSR);
  2288. RMPRIVATELOCK(&pTask->Hdr, pSR);
  2289. switch(GET_RM_TASK_STATE(pTask))
  2290. {
  2291. case RMTSKSTATE_STARTING:
  2292. // This task is completing synchronously.
  2293. //
  2294. ASSERT(Status != NDIS_STATUS_PENDING);
  2295. SET_RM_TASK_STATE(pTask, RMTSKSTATE_ENDING);
  2296. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2297. rmEndTask(pTask, Status, pSR);
  2298. RmDeallocateObject(&pTask->Hdr, pSR);
  2299. break;
  2300. case RMTSKSTATE_PENDING:
  2301. ASSERTEX(Status == NDIS_STATUS_PENDING, pTask);
  2302. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2303. break;
  2304. case RMTSKSTATE_ENDING:
  2305. // This task is completing synchronously and the RM_TASKOP_END
  2306. // notification has already been sent.
  2307. //
  2308. // ??? ASSERT(Status != NDIS_STATUS_PENDING);
  2309. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2310. // ??? RmDeallocateObject(&pTask->Hdr, pSR);
  2311. break;
  2312. default:
  2313. ASSERTEX(FALSE, pTask);
  2314. // Fall through ...
  2315. case RMTSKSTATE_ACTIVE:
  2316. // This can happen if the task is in the process of being resumed
  2317. // in the context of some other thread. Nothing to do here...
  2318. // (This actually happens sometimes on a MP machine).
  2319. //
  2320. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2321. break;
  2322. }
  2323. }
  2324. // Remove the tmp ref added when the task was allocated.
  2325. //
  2326. RmTmpDereferenceObject(
  2327. &pTask->Hdr,
  2328. pSR
  2329. );
  2330. RM_ASSERT_NOLOCKS(pSR);
  2331. EXIT()
  2332. return Status;
  2333. }
  2334. VOID
  2335. RmDbgDumpTask(
  2336. IN PRM_TASK pTask,
  2337. IN PRM_STACK_RECORD pSR
  2338. )
  2339. {
  2340. }
  2341. RM_STATUS
  2342. RmSuspendTask(
  2343. IN PRM_TASK pTask,
  2344. IN UINT SuspendContext,
  2345. IN PRM_STACK_RECORD pSR
  2346. )
  2347. /*++
  2348. Routine Description:
  2349. Suspends the specified task.
  2350. RmSuspendTask is always called in the context of a task handler.
  2351. pTask is may be locked on entry -- we don't care.
  2352. Arguments:
  2353. pTask - task to be suspended.
  2354. SuspendContext - context to be presented to the task's handler when
  2355. the task is subsequently resumed. Specifically, this
  2356. context may be accessed using the RM_PEND_CODE macro,
  2357. when the task's handler is called with code
  2358. RM_TASKOP_PENDCOMPLETE.
  2359. --*/
  2360. {
  2361. ENTER("RmSuspendTask", 0xd80fdc00)
  2362. NDIS_STATUS Status;
  2363. // RM_ASSERT_NOLOCKS(pSR);
  2364. RMPRIVATELOCK(&pTask->Hdr, pSR);
  2365. TR_INFO((
  2366. "SUSPENDING Task 0x%p (%s); SuspendContext = 0x%lx\n",
  2367. pTask,
  2368. pTask->Hdr.szDescription,
  2369. SuspendContext
  2370. ));
  2371. if ( !CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_STARTING)
  2372. && !CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_ACTIVE))
  2373. {
  2374. ASSERTEX(!"Invalid state", pTask);
  2375. Status = NDIS_STATUS_FAILURE;
  2376. }
  2377. else
  2378. {
  2379. SET_RM_TASK_STATE(pTask, RMTSKSTATE_PENDING);
  2380. pTask->SuspendContext = SuspendContext;
  2381. Status = NDIS_STATUS_SUCCESS;
  2382. }
  2383. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2384. // RM_ASSERT_NOLOCKS(pSR);
  2385. EXIT()
  2386. return Status;
  2387. }
  2388. VOID
  2389. RmUnsuspendTask(
  2390. IN PRM_TASK pTask,
  2391. IN PRM_STACK_RECORD pSR
  2392. )
  2393. /*++
  2394. Routine Description:
  2395. Undoes the effect of a previous call to RmSuspendTask.
  2396. Task MUST be in the pending state and MUST NOT be pending on another task.
  2397. Debug version will ASSERT if above conditions are not met.
  2398. RmUnsuspendTask is always called in the context of a task handler.
  2399. pTask is may be locked on entry -- we don't care.
  2400. Arguments:
  2401. pTask - task to be suspended.
  2402. --*/
  2403. {
  2404. ENTER("RmUnsuspendTask", 0xcf713639)
  2405. RMPRIVATELOCK(&pTask->Hdr, pSR);
  2406. TR_INFO((
  2407. "UN-SUSPENDING Task 0x%p (%s). SuspendContext = 0x%x\n",
  2408. pTask,
  2409. pTask->Hdr.szDescription,
  2410. pTask->SuspendContext
  2411. ));
  2412. ASSERT(CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_PENDING));
  2413. ASSERT(pTask->pTaskIAmPendingOn == NULL);
  2414. SET_RM_TASK_STATE(pTask, RMTSKSTATE_ACTIVE);
  2415. pTask->SuspendContext = 0;
  2416. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2417. EXIT()
  2418. }
  2419. VOID
  2420. RmResumeTask(
  2421. IN PRM_TASK pTask,
  2422. IN UINT_PTR SuspendCompletionParam,
  2423. IN PRM_STACK_RECORD pSR
  2424. )
  2425. /*++
  2426. Routine Description:
  2427. Resume a previously-suspended task.
  2428. No locks held on entry or exit.
  2429. SuspendCompletionParam is user-defined, and must be agreed upon between
  2430. the caller of RmUnpendTask and the task that's being unpended.
  2431. The Task's handler is ALWAYS called in the context of the caller of RmUnpendTask.
  2432. So it is ok for the caller to declare a structure on the stack and pass
  2433. a pointer to it as SuspendCompletionParam.
  2434. WARNING: pTask could well be invalid (deallocate) by the time we return
  2435. from this function. The caller is responsible for tmprefing pTask if it needs
  2436. to access after return from this function.
  2437. Arguments:
  2438. pTask - task to be resumed.
  2439. SuspendCompletionParam - arbitrary value that is passed on to the task's
  2440. handler as "UserParan" when the handler is called
  2441. with code RM_TASKOP_PENDCOMPLETE.
  2442. --*/
  2443. {
  2444. ENTER("RmResumeTask", 0xd261f3c6)
  2445. NDIS_STATUS Status;
  2446. RM_ASSERT_NOLOCKS(pSR);
  2447. RMPRIVATELOCK(&pTask->Hdr, pSR);
  2448. TR_INFO((
  2449. "RESUMING Task 0x%p (%s); SuspendCompletionParam = 0x%lx\n",
  2450. pTask,
  2451. pTask->Hdr.szDescription,
  2452. SuspendCompletionParam
  2453. ));
  2454. if (!CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_PENDING))
  2455. {
  2456. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2457. ASSERTEX(!"Invalid state", pTask);
  2458. }
  2459. else
  2460. {
  2461. // Add tmp ref, because we need to look at pTask after the return
  2462. // from calling pfnHandler.
  2463. //
  2464. RmTmpReferenceObject(&pTask->Hdr, pSR);
  2465. SET_RM_TASK_STATE(pTask, RMTSKSTATE_ACTIVE);
  2466. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2467. Status = pTask->pfnHandler(
  2468. pTask,
  2469. RM_TASKOP_PENDCOMPLETE,
  2470. SuspendCompletionParam,
  2471. pSR
  2472. );
  2473. RM_ASSERT_NOLOCKS(pSR);
  2474. RMPRIVATELOCK(&pTask->Hdr, pSR);
  2475. switch(GET_RM_TASK_STATE(pTask))
  2476. {
  2477. case RMTSKSTATE_ACTIVE:
  2478. // This task is completing here (maybe)
  2479. //
  2480. if (Status != NDIS_STATUS_PENDING)
  2481. {
  2482. SET_RM_TASK_STATE(pTask, RMTSKSTATE_ENDING);
  2483. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2484. rmEndTask(pTask, Status, pSR);
  2485. RmDeallocateObject(&pTask->Hdr, pSR);
  2486. }
  2487. else
  2488. {
  2489. // It could be returning pending, but the state could
  2490. // by now be active because it was completed elsewhere.
  2491. // ASSERT(Status != NDIS_STATUS_PENDING);
  2492. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2493. }
  2494. break;
  2495. case RMTSKSTATE_PENDING:
  2496. ASSERTEX(Status == NDIS_STATUS_PENDING, pTask);
  2497. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2498. break;
  2499. case RMTSKSTATE_ENDING:
  2500. // This task is completing synchronously and the RM_TASKOP_END
  2501. // notification has already been sent.
  2502. //
  2503. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2504. break;
  2505. default:
  2506. ASSERTEX(FALSE, pTask);
  2507. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2508. }
  2509. // Remove tmpref added above. pTask may well go away now...
  2510. //
  2511. RmTmpDereferenceObject(&pTask->Hdr, pSR);
  2512. }
  2513. RM_ASSERT_NOLOCKS(pSR);
  2514. EXIT()
  2515. }
  2516. VOID
  2517. RmResumeTaskAsync(
  2518. IN PRM_TASK pTask,
  2519. IN UINT_PTR SuspendCompletionParam,
  2520. IN OS_WORK_ITEM * pOsWorkItem,
  2521. IN PRM_STACK_RECORD pSR
  2522. )
  2523. /*++
  2524. Routine Description:
  2525. Similar to RmResumeTask, except that the task is resumed in the context
  2526. of a work-item thread.
  2527. Arguments:
  2528. pTask - see RmResumeTask
  2529. SuspendCompletionParam - see RmResumeTask
  2530. pOsWorkItem - caller supplied UNitialized work item (must stay
  2531. around until the task is resumed). Typically this
  2532. will be located within the user-specific portion
  2533. of pTask
  2534. --*/
  2535. {
  2536. NDIS_STATUS Status;
  2537. RM_ASSERT_NOLOCKS(pSR);
  2538. #if RM_EXTRA_CHECKING
  2539. // This may seem paranoid, but is such a powerful check that it's worth it.
  2540. //
  2541. RmDbgAddAssociation(
  2542. 0x33d63ece, // Location ID
  2543. &pTask->Hdr, // pObject
  2544. (UINT_PTR) SuspendCompletionParam, // Instance1
  2545. (UINT_PTR) pOsWorkItem, // Instance2
  2546. RM_PRIVATE_ASSOC_RESUME_TASK_ASYNC, // AssociationID
  2547. szASSOCFORMAT_RESUME_TASK_ASYNC, // szAssociationFormat
  2548. pSR
  2549. );
  2550. #endif // RM_EXTRA_CHECKING
  2551. // We don't need to grab the private lock to set this, because only one
  2552. // entity can call RmResumeTaskAsync. Note that we also ensure things are clean
  2553. // (in the debug case) by the association added above.
  2554. //
  2555. pTask->AsyncCompletionParam = SuspendCompletionParam;
  2556. NdisInitializeWorkItem(
  2557. pOsWorkItem,
  2558. rmWorkItemHandler_ResumeTaskAsync,
  2559. pTask
  2560. );
  2561. Status = NdisScheduleWorkItem(pOsWorkItem);
  2562. if (FAIL(Status))
  2563. {
  2564. ASSERT(!"NdisStatusWorkItem failed.");
  2565. // It so happens that NdisScheudleWorkItem (current implementation
  2566. // doesn't fail. Nevertheless, we do the best we can and actually
  2567. // resume the task. If the caller was at dpc level and was expecting
  2568. // the task to resume at passive, they're out of luck.
  2569. //
  2570. RmResumeTask(pTask, SuspendCompletionParam, pSR);
  2571. }
  2572. }
  2573. VOID
  2574. RmResumeTaskDelayed(
  2575. IN PRM_TASK pTask,
  2576. IN UINT_PTR SuspendCompletionParam,
  2577. IN ULONG MsDelay,
  2578. IN OS_TIMER * pOsTimer,
  2579. IN PRM_STACK_RECORD pSR
  2580. )
  2581. /*++
  2582. Routine Description:
  2583. Similar to RmResumeTask, except that the task is resumed in the context
  2584. of a os timer handler which is set to fire after MsDelay milliseconds
  2585. from the time RmResumeTaskDelayed is called.
  2586. EXCEPTION: if someone has previously called RmResumeDelayedTaskNow, this
  2587. task could be resumed in the context of this function call itself.
  2588. Abort implementation notes: see notes.txt 07/14/1999 entry.
  2589. Arguments:
  2590. pTask - see RmResumeTask
  2591. SuspendCompletionParam - see RmResumeTask
  2592. pOsTimer - caller supplied UNitialized timer
  2593. (must stay around until the task is resumed).
  2594. Typically this will be located within the
  2595. user-specific portion of pTask.
  2596. --*/
  2597. {
  2598. NDIS_STATUS Status;
  2599. RM_ASSERT_NOLOCKS(pSR);
  2600. #if RM_EXTRA_CHECKING
  2601. // This may seem paranoid, but is such a powerful check that it's worth it.
  2602. //
  2603. RmDbgAddAssociation(
  2604. 0x33d63ece, // Location ID
  2605. &pTask->Hdr, // pObject
  2606. (UINT_PTR) SuspendCompletionParam, // Instance1
  2607. (UINT_PTR) NULL, // Instance2
  2608. RM_PRIVATE_ASSOC_RESUME_TASK_DELAYED, // AssociationID
  2609. szASSOCFORMAT_RESUME_TASK_DELAYED, // szAssociationFormat
  2610. pSR
  2611. );
  2612. #endif // RM_EXTRA_CHECKING
  2613. // Ddk states that it's best to call this function at passive level.
  2614. //
  2615. NdisInitializeTimer(
  2616. pOsTimer,
  2617. rmTimerHandler_ResumeTaskDelayed,
  2618. pTask
  2619. );
  2620. RMPRIVATELOCK(&pTask->Hdr, pSR);
  2621. // The task-del state should NOT be "delayed"
  2622. //
  2623. ASSERT(RM_CHECK_STATE(pTask, RMTSKDELSTATE_MASK, 0));
  2624. pTask->AsyncCompletionParam = SuspendCompletionParam;
  2625. RM_SET_STATE(pTask, RMTSKDELSTATE_MASK, RMTSKDELSTATE_DELAYED);
  2626. if (RM_CHECK_STATE(pTask, RMTSKABORTSTATE_MASK, RMTSKABORTSTATE_ABORT_DELAY))
  2627. {
  2628. // Oops, the delay has been aborted -- we call the tick handler now!
  2629. //
  2630. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2631. rmTimerHandler_ResumeTaskDelayed(
  2632. NULL, // SystemSpecific1,
  2633. pTask, // FunctionContext,
  2634. NULL, // SystemSpecific2,
  2635. NULL // SystemSpecific3
  2636. );
  2637. }
  2638. else
  2639. {
  2640. //
  2641. // Not currently aborting, let's set the timer.
  2642. //
  2643. NdisSetTimer(pOsTimer, MsDelay);
  2644. // Very important to unlock the private lock AFTER calling set timer,
  2645. // other wise someone could call RmResumeDelayedTaskNow BEFORE we call
  2646. // NdisSetTimer, in which we would not end up aborting the delayed task.
  2647. //
  2648. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2649. }
  2650. }
  2651. VOID
  2652. RmResumeDelayedTaskNow(
  2653. IN PRM_TASK pTask,
  2654. IN OS_TIMER * pOsTimer,
  2655. OUT PUINT pTaskResumed,
  2656. IN PRM_STACK_RECORD pSR
  2657. )
  2658. /*++
  2659. Routine Description:
  2660. Cut's short the delay and resumes the task immediately.
  2661. Implementation notes: see notes.txt 07/14/1999 entry.
  2662. Arguments:
  2663. pTask - see RmResumeTask
  2664. pOsTimer - caller supplied initialized timer
  2665. (must stay around until the task is resumed).
  2666. Typically this will be located within the
  2667. user-specific portion of pTask.
  2668. pTaskResumed - Points to a caller-supplied variable.
  2669. RmResumeDelayedTask sets this variable to TRUE if the
  2670. task was resumed as a consequence of this call, or to
  2671. FALSE if the task was resumed due to some other reason.
  2672. --*/
  2673. {
  2674. UINT_PTR CompletionParam = pTask->AsyncCompletionParam;
  2675. *pTaskResumed = FALSE;
  2676. ASSERTEX(RMISALLOCATED(&pTask->Hdr), pTask);
  2677. RMPRIVATELOCK(&pTask->Hdr, pSR);
  2678. RM_SET_STATE(pTask, RMTSKABORTSTATE_MASK, RMTSKABORTSTATE_ABORT_DELAY);
  2679. if (RM_CHECK_STATE(pTask, RMTSKDELSTATE_MASK, RMTSKDELSTATE_DELAYED))
  2680. {
  2681. BOOLEAN TimerCanceled = FALSE;
  2682. //
  2683. // The task is actually delayed. Let's go ahead and cancel the timer
  2684. // and resume the task now (which we do indirectly by calling
  2685. // the timer handler ourselves).
  2686. //
  2687. NdisCancelTimer(pOsTimer, &TimerCanceled);
  2688. if (TimerCanceled)
  2689. {
  2690. //
  2691. // The timer was actually canceled -- so we call the timer handler
  2692. // ourselves.
  2693. //
  2694. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2695. rmTimerHandler_ResumeTaskDelayed(
  2696. NULL, // SystemSpecific1,
  2697. pTask, // FunctionContext,
  2698. NULL, // SystemSpecific2,
  2699. NULL // SystemSpecific3
  2700. );
  2701. *pTaskResumed = TRUE;
  2702. }
  2703. else
  2704. {
  2705. //
  2706. // Hmm -- the timer is not enabled. This is either because
  2707. // the timer handler has just been called (and not yet cleared
  2708. // the "DELAY" state) OR someone has previously called
  2709. // RmResumeDelayedTaskNow.
  2710. //
  2711. //
  2712. // Nothing to do.
  2713. //
  2714. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2715. }
  2716. }
  2717. else
  2718. {
  2719. //
  2720. // The task state is not delayed -- so we just set the abort state
  2721. // and go away.
  2722. //
  2723. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  2724. }
  2725. }
  2726. RM_STATUS
  2727. RmPendTaskOnOtherTask(
  2728. IN PRM_TASK pTask,
  2729. IN UINT SuspendContext,
  2730. IN PRM_TASK pOtherTask,
  2731. IN PRM_STACK_RECORD pSR
  2732. )
  2733. /*++
  2734. Routine Description:
  2735. Pend task pTask on task pOtherTask.
  2736. Note: RmPendTaskOnOtherTask will cause pTask's pend operation to be
  2737. completed in the context of this call itself, if pOtherTask is already
  2738. in the completed state.
  2739. 03/26/1999 -- see RmPendTaskOnOtherTaskV2, and also
  2740. 03/26/1999 notes.txt entry "Some proposed ..."
  2741. Arguments:
  2742. pTask - task to be suspended.
  2743. SuspendContext - Context associated with the suspend (see
  2744. RmSuspendTask for details).
  2745. pOtherTask - task that pTask is to pend on.
  2746. Return Value:
  2747. NDIS_STATUS_SUCCESS on success.
  2748. NDIS_STATUS_FAILURE on failure (typically because pTask is not in as
  2749. position to be suspended.)
  2750. --*/
  2751. {
  2752. ENTER("RmPendTaskOnOtherTask", 0x0416873e)
  2753. NDIS_STATUS Status = NDIS_STATUS_FAILURE;
  2754. BOOLEAN fResumeTask = FALSE;
  2755. RM_ASSERT_NOLOCKS(pSR);
  2756. TR_INFO((
  2757. "PENDING Task 0x%p (%s) on Task 0x%p (%s). SuspendCompletionParam = 0x%lx\n",
  2758. pTask,
  2759. pTask->Hdr.szDescription,
  2760. pOtherTask,
  2761. pOtherTask->Hdr.szDescription,
  2762. SuspendContext
  2763. ));
  2764. //
  2765. // WARNING: we break the locking rules here by getting the lock on
  2766. // both pTask and pOtherTask.
  2767. // TODO: consider acquiring them in order of increasing numerical value.
  2768. //
  2769. ASSERT(pTask != pOtherTask);
  2770. NdisAcquireSpinLock(&(pOtherTask->Hdr.RmPrivateLock.OsLock));
  2771. NdisAcquireSpinLock(&(pTask->Hdr.RmPrivateLock.OsLock));
  2772. do
  2773. {
  2774. // Break if we can't pend pTask on pOtherTask.
  2775. //
  2776. {
  2777. if ( !CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_STARTING)
  2778. && !CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_ACTIVE))
  2779. {
  2780. ASSERTEX(!"Invalid state", pTask);
  2781. break;
  2782. }
  2783. // Non-NULL pTaskIAmPendingOn implies that pTask is already pending on
  2784. // some other task!
  2785. //
  2786. if (pTask->pTaskIAmPendingOn != NULL)
  2787. {
  2788. ASSERTEX(!"Invalid state", pTask);
  2789. break;
  2790. }
  2791. }
  2792. Status = NDIS_STATUS_SUCCESS;
  2793. SET_RM_TASK_STATE(pTask, RMTSKSTATE_PENDING);
  2794. pTask->SuspendContext = SuspendContext;
  2795. if (CHECK_RM_TASK_STATE(pOtherTask, RMTSKSTATE_ENDING))
  2796. {
  2797. //
  2798. // Other task is done -- so we resume pTask before returning...
  2799. //
  2800. fResumeTask = TRUE;
  2801. break;
  2802. }
  2803. //
  2804. // pOtherTask is not ended -- add pTask to the list of tasks pending
  2805. // on pOtherTask.
  2806. //
  2807. pTask->pTaskIAmPendingOn = pOtherTask;
  2808. #if RM_EXTRA_CHECKING
  2809. RmLinkObjectsEx(
  2810. &pTask->Hdr,
  2811. &pOtherTask->Hdr,
  2812. 0x77c488ca,
  2813. RM_PRIVATE_ASSOC_LINK_TASKPENDINGON,
  2814. szASSOCFORMAT_LINK_TASKPENDINGON,
  2815. RM_PRIVATE_ASSOC_LINK_TASKBLOCKS,
  2816. szASSOCFORMAT_LINK_TASKBLOCKS,
  2817. pSR
  2818. );
  2819. #else // !RM_EXTRA_CHECKING
  2820. RmLinkObjects(&pTask->Hdr, &pOtherTask->Hdr, pSR);
  2821. #endif // !RM_EXTRA_CHECKING
  2822. ASSERTEX(pTask->linkFellowPendingTasks.Blink == NULL, pTask);
  2823. ASSERTEX(pTask->linkFellowPendingTasks.Flink == NULL, pTask);
  2824. InsertHeadList(
  2825. &pOtherTask->listTasksPendingOnMe,
  2826. &pTask->linkFellowPendingTasks
  2827. );
  2828. } while(FALSE);
  2829. NdisReleaseSpinLock(&(pTask->Hdr.RmPrivateLock.OsLock));
  2830. NdisReleaseSpinLock(&(pOtherTask->Hdr.RmPrivateLock.OsLock));
  2831. if (fResumeTask)
  2832. {
  2833. RmResumeTask(
  2834. pTask,
  2835. NDIS_STATUS_SUCCESS, // SuspendCompletionParam. TODO: put real code.
  2836. pSR
  2837. );
  2838. }
  2839. RM_ASSERT_NOLOCKS(pSR);
  2840. EXIT()
  2841. return Status;
  2842. }
  2843. RM_STATUS
  2844. RmPendOnOtherTaskV2(
  2845. IN PRM_TASK pTask,
  2846. IN UINT SuspendContext,
  2847. IN PRM_TASK pOtherTask,
  2848. IN PRM_STACK_RECORD pSR
  2849. )
  2850. /*++
  2851. Routine Description:
  2852. if pOtherTask is not complete, Pend task pTask on task pOtherTask and return
  2853. NDIS_STATUS_PENDING. However, if pOtherTask is already complete,
  2854. then don't pend and instead return NDIS_STATUS_SUCCESS.
  2855. See 03/26/1999 notes.txt entry "Some proposed ...". This function
  2856. is currently used only by rmTaskUnloadGroup, to avoid the problem describted
  2857. in the above-referenced notes.txt entry.
  2858. TODO: Eventually get rid of RmPendTaskOnOtherTask.
  2859. Arguments:
  2860. See RmPendTaskOnOtherTask
  2861. Return Value:
  2862. NDIS_STATUS_PENDING if pTask is pending on pOtherTask
  2863. NDIS_STATUS_SUCCESS if pOtherTask is complete.
  2864. NDIS_STATUS_FAILURE if there was some failure (typically pTask is not
  2865. in a position to be pended.)
  2866. --*/
  2867. {
  2868. ENTER("RmPendTaskOnOtherTaskV2", 0x0e7d1b89)
  2869. NDIS_STATUS Status = NDIS_STATUS_FAILURE;
  2870. RM_ASSERT_NOLOCKS(pSR);
  2871. TR_INFO((
  2872. "PENDING(V2) Task 0x%p (%s) on Task 0x%p (%s). SuspendCompletionParam = 0x%lx\n",
  2873. pTask,
  2874. pTask->Hdr.szDescription,
  2875. pOtherTask,
  2876. pOtherTask->Hdr.szDescription,
  2877. SuspendContext
  2878. ));
  2879. // This is not a useless assert -- I'e had a bug elsewhere which caused this
  2880. // assert to get hit.
  2881. //
  2882. ASSERT(pTask != pOtherTask);
  2883. //
  2884. // WARNING: we break the locking rules here by getting the lock on
  2885. // both pTask and pOtherTask.
  2886. // TODO: consider acquiring them in order of increasing numerical value.
  2887. //
  2888. NdisAcquireSpinLock(&(pOtherTask->Hdr.RmPrivateLock.OsLock));
  2889. NdisAcquireSpinLock(&(pTask->Hdr.RmPrivateLock.OsLock));
  2890. do
  2891. {
  2892. // Break if we can't pend pTask on pOtherTask.
  2893. //
  2894. {
  2895. if ( !CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_STARTING)
  2896. && !CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_ACTIVE))
  2897. {
  2898. ASSERTEX(!"Invalid state", pTask);
  2899. break;
  2900. }
  2901. // Non-NULL pTaskIAmPendingOn implies that pTask is already pending on
  2902. // some other task!
  2903. //
  2904. if (pTask->pTaskIAmPendingOn != NULL)
  2905. {
  2906. ASSERTEX(!"Invalid state", pTask);
  2907. break;
  2908. }
  2909. }
  2910. if (CHECK_RM_TASK_STATE(pOtherTask, RMTSKSTATE_ENDING))
  2911. {
  2912. //
  2913. // Other task is done -- so we simply return success...
  2914. //
  2915. Status = NDIS_STATUS_SUCCESS;
  2916. break;
  2917. }
  2918. //
  2919. // pOtherTask is not ended -- set pTask state to pending, and
  2920. // add it to the list of tasks pending on pOtherTask.
  2921. //
  2922. SET_RM_TASK_STATE(pTask, RMTSKSTATE_PENDING);
  2923. pTask->SuspendContext = SuspendContext;
  2924. pTask->pTaskIAmPendingOn = pOtherTask;
  2925. #if RM_EXTRA_CHECKING
  2926. RmLinkObjectsEx(
  2927. &pTask->Hdr,
  2928. &pOtherTask->Hdr,
  2929. 0x77c488ca,
  2930. RM_PRIVATE_ASSOC_LINK_TASKPENDINGON,
  2931. szASSOCFORMAT_LINK_TASKPENDINGON,
  2932. RM_PRIVATE_ASSOC_LINK_TASKBLOCKS,
  2933. szASSOCFORMAT_LINK_TASKBLOCKS,
  2934. pSR
  2935. );
  2936. #else // !RM_EXTRA_CHECKING
  2937. RmLinkObjects(&pTask->Hdr, &pOtherTask->Hdr, pSR);
  2938. #endif // !RM_EXTRA_CHECKING
  2939. ASSERTEX(pTask->linkFellowPendingTasks.Blink == NULL, pTask);
  2940. ASSERTEX(pTask->linkFellowPendingTasks.Flink == NULL, pTask);
  2941. InsertHeadList(
  2942. &pOtherTask->listTasksPendingOnMe,
  2943. &pTask->linkFellowPendingTasks
  2944. );
  2945. Status = NDIS_STATUS_PENDING;
  2946. } while(FALSE);
  2947. NdisReleaseSpinLock(&(pTask->Hdr.RmPrivateLock.OsLock));
  2948. NdisReleaseSpinLock(&(pOtherTask->Hdr.RmPrivateLock.OsLock));
  2949. RM_ASSERT_NOLOCKS(pSR);
  2950. EXIT()
  2951. return Status;
  2952. }
  2953. VOID
  2954. RmCancelPendOnOtherTask(
  2955. IN PRM_TASK pTask,
  2956. IN PRM_TASK pOtherTask,
  2957. IN UINT_PTR UserParam,
  2958. IN PRM_STACK_RECORD pSR
  2959. )
  2960. /*++
  2961. Routine Description:
  2962. Resume task pTask which is currently pending on pOtherTask.
  2963. Since no locks are held, pOtherTask needs to be specified, to make sure
  2964. that pTask is indeed pending on pOtherTask before canceling the pend.
  2965. If pTask is indeed pending on pOtherTask, this function will cause the
  2966. completion of the pend status with the specified user param.
  2967. Has no effect if the task is not pending.
  2968. Arguments:
  2969. pTask - Task to be "unpended"
  2970. pOtherTask - Task pTask is currently pending on.
  2971. UserParam - Passed to pTask's handler if and when pTask is resumed.
  2972. --*/
  2973. {
  2974. ENTER("RmCancelPendOnOtherTask", 0x6e113266)
  2975. NDIS_STATUS Status = NDIS_STATUS_FAILURE;
  2976. BOOLEAN fResumeTask = FALSE;
  2977. RM_ASSERT_NOLOCKS(pSR);
  2978. //
  2979. // WARNING: we break the locking rules here by getting the lock on
  2980. // both pTask and pOtherTask.
  2981. // TODO: consider acquiring them in order of increasing numerical value.
  2982. //
  2983. TR_INFO((
  2984. "CANCEL PEND of Task 0x%p (%s) on other Task 0x%p (%s); UserParam = 0x%lx\n",
  2985. pTask,
  2986. pTask->Hdr.szDescription,
  2987. pOtherTask,
  2988. pOtherTask->Hdr.szDescription,
  2989. UserParam
  2990. ));
  2991. // With pTask locked, tmp ref the task it is pending on, if any...
  2992. //
  2993. {
  2994. NdisAcquireSpinLock(&(pTask->Hdr.RmPrivateLock.OsLock));
  2995. if (pOtherTask == pTask->pTaskIAmPendingOn)
  2996. {
  2997. RmTmpReferenceObject(&(pOtherTask->Hdr), pSR);
  2998. }
  2999. else
  3000. {
  3001. // Oops -- pTask is not pending on pOtherTask ...
  3002. //
  3003. pOtherTask = NULL;
  3004. }
  3005. NdisReleaseSpinLock(&(pTask->Hdr.RmPrivateLock.OsLock));
  3006. }
  3007. if (pOtherTask == NULL) return; // EARLY RETURN
  3008. NdisAcquireSpinLock(&(pOtherTask->Hdr.RmPrivateLock.OsLock));
  3009. NdisAcquireSpinLock(&(pTask->Hdr.RmPrivateLock.OsLock));
  3010. do
  3011. {
  3012. // Now that we have both task's locks, check again if pTask is pending
  3013. // on pOtherTask
  3014. //
  3015. if (pTask->pTaskIAmPendingOn != pOtherTask)
  3016. {
  3017. // Oops -- the situation is different than when we started -- quietly
  3018. // get out of here...
  3019. //
  3020. break;
  3021. }
  3022. pTask->pTaskIAmPendingOn = NULL;
  3023. #if RM_EXTRA_CHECKING
  3024. RmUnlinkObjectsEx(
  3025. &pTask->Hdr,
  3026. &pOtherTask->Hdr,
  3027. 0x6992b7a1,
  3028. RM_PRIVATE_ASSOC_LINK_TASKPENDINGON,
  3029. RM_PRIVATE_ASSOC_LINK_TASKBLOCKS,
  3030. pSR
  3031. );
  3032. #else // !RM_EXTRA_CHECKING
  3033. RmUnlinkObjects(&pTask->Hdr, &pOtherTask->Hdr, pSR);
  3034. #endif // !RM_EXTRA_CHECKING
  3035. RemoveEntryList(&pTask->linkFellowPendingTasks);
  3036. pTask->linkFellowPendingTasks.Flink = NULL;
  3037. pTask->linkFellowPendingTasks.Blink = NULL;
  3038. if (CHECK_RM_TASK_STATE(pTask, RMTSKSTATE_PENDING))
  3039. {
  3040. fResumeTask = TRUE;
  3041. }
  3042. else
  3043. {
  3044. //
  3045. // We shouldn't get here -- after we are pending on another task...
  3046. //
  3047. ASSERTEX(!"Invalid state", pTask);
  3048. break;
  3049. }
  3050. } while (FALSE);
  3051. NdisReleaseSpinLock(&(pTask->Hdr.RmPrivateLock.OsLock));
  3052. NdisReleaseSpinLock(&(pOtherTask->Hdr.RmPrivateLock.OsLock));
  3053. RmTmpDereferenceObject(&(pOtherTask->Hdr), pSR);
  3054. if (fResumeTask)
  3055. {
  3056. RmResumeTask(
  3057. pTask,
  3058. UserParam, // SuspendCompletionParam
  3059. pSR
  3060. );
  3061. }
  3062. RM_ASSERT_NOLOCKS(pSR);
  3063. EXIT()
  3064. }
  3065. VOID
  3066. RmInitializeHashTable(
  3067. PRM_HASH_INFO pHashInfo,
  3068. PVOID pAllocationContext,
  3069. PRM_HASH_TABLE pHashTable
  3070. )
  3071. /*++
  3072. Routine Description:
  3073. Initialize a hash table data structure.
  3074. Caller is responsible for serializing access to the hash table structure.
  3075. Arguments:
  3076. pHashInfo - Points to static information about the hash table
  3077. pAllocationContext - Passed to the allocation and deallocation functions
  3078. (pHashInfo->pfnTableAllocator and
  3079. pHashInfo0->pfnTableDeallocator) which are used to
  3080. dynamically grow /shrink the hash table.
  3081. pHashTable - Points to uninitialized memory that is to contain the
  3082. hash table.
  3083. --*/
  3084. {
  3085. NdisZeroMemory(pHashTable, sizeof(*pHashTable));
  3086. pHashTable->pHashInfo = pHashInfo;
  3087. pHashTable->pAllocationContext = pAllocationContext;
  3088. pHashTable->pTable = pHashTable->InitialTable;
  3089. pHashTable->TableLength = sizeof(pHashTable->InitialTable)
  3090. /sizeof(pHashTable->InitialTable[0]);
  3091. }
  3092. VOID
  3093. RmDeinitializeHashTable(
  3094. PRM_HASH_TABLE pHashTable
  3095. )
  3096. /*++
  3097. Routine Description:
  3098. Deinitialize a previously-initialized a hash table data structure.
  3099. There must be no items in the hash table when this function is called.
  3100. Caller is responsible for serializing access to the hash table structure.
  3101. Arguments:
  3102. pHashTable - Hash table to be deinitialized.
  3103. --*/
  3104. {
  3105. PRM_HASH_LINK *pTable = pHashTable->pTable;
  3106. ASSERTEX(pHashTable->NumItems == 0, pHashTable);
  3107. if (pTable != pHashTable->InitialTable)
  3108. {
  3109. NdisZeroMemory(pTable, pHashTable->TableLength*sizeof(*pTable));
  3110. pHashTable->pHashInfo->pfnTableDeallocator(
  3111. pTable,
  3112. pHashTable->pAllocationContext
  3113. );
  3114. }
  3115. NdisZeroMemory(pHashTable, sizeof(*pHashTable));
  3116. }
  3117. BOOLEAN
  3118. RmLookupHashTable(
  3119. PRM_HASH_TABLE pHashTable,
  3120. PRM_HASH_LINK ** pppLink,
  3121. PVOID pvRealKey
  3122. )
  3123. /*++
  3124. Routine Description:
  3125. Lookup an item in the hash table and/or find the place where the item
  3126. is to be inserted.
  3127. Caller is expected to serialize access to the hash table.
  3128. OK to use read-locks to serialize access.
  3129. Return value: TRUE if item found; false otherwise.
  3130. On return, *pppLink is set to a the location containing a pointer to
  3131. a RM_HASH_LINK. If the return value is TRUE, the latter pointer points
  3132. to the found RM_HASH_LINK. If the return value is FALSE, the location
  3133. is where the item is to be inserted, if required.
  3134. Arguments:
  3135. pHashTable - Hash table to look up
  3136. pppLink - place to store a pointer to a link which points
  3137. to an item (see above for details).
  3138. pvRealKey - Key used to lookup item.
  3139. Return Value:
  3140. TRUE if item is found
  3141. FALSE otherwise.
  3142. --*/
  3143. {
  3144. PRM_HASH_LINK *ppLink, pLink;
  3145. UINT LinksTraversed = 0;
  3146. UINT TableLength = pHashTable->TableLength;
  3147. PFN_RM_COMPARISON_FUNCTION pfnCompare = pHashTable->pHashInfo->pfnCompare;
  3148. BOOLEAN fRet = FALSE;
  3149. ULONG uHash = pHashTable->pHashInfo->pfnHash(pvRealKey);
  3150. for (
  3151. ppLink = pHashTable->pTable + (uHash%TableLength);
  3152. (pLink = *ppLink) != NULL;
  3153. ppLink = &(pLink->pNext), LinksTraversed++)
  3154. {
  3155. if (pLink->uHash == uHash
  3156. && pfnCompare(pvRealKey, pLink))
  3157. {
  3158. // found it
  3159. //
  3160. fRet = TRUE;
  3161. break;
  3162. }
  3163. }
  3164. // Update stats
  3165. //
  3166. rmUpdateHashTableStats(&pHashTable->Stats, LinksTraversed);
  3167. *pppLink = ppLink;
  3168. return fRet;
  3169. }
  3170. BOOLEAN
  3171. RmNextHashTableItem(
  3172. PRM_HASH_TABLE pHashTable,
  3173. PRM_HASH_LINK pCurrentLink, // OPTIONAL
  3174. PRM_HASH_LINK * ppNextLink
  3175. )
  3176. /*++
  3177. Routine Description:
  3178. Find the first (if pCurrentLink is NULL) or "next" (if pCurrentLink is not NULL)
  3179. item in the hash table.
  3180. Caller is expected to serialize access to the hash table.
  3181. OK to use read-locks to serialize access.
  3182. NOTE: The "next" item returned is in no particular order.
  3183. Arguments:
  3184. pHashTable - Hash table to look up
  3185. pCurrentLink - if non-NULL, points to an existing hash link in the
  3186. hash table.
  3187. ppLinkLink - place to store a pointer to the link "after"
  3188. pCurrentLink, or the first link (if pCurrentLink is NULL).
  3189. Return Value:
  3190. TRUE if there is a "next" item.
  3191. FALSE otherwise.
  3192. --*/
  3193. {
  3194. PRM_HASH_LINK pLink, *ppLink, *ppLinkEnd;
  3195. UINT TableLength;
  3196. ppLink = pHashTable->pTable;
  3197. TableLength = pHashTable->TableLength;
  3198. ppLinkEnd = ppLink + TableLength;
  3199. if (pCurrentLink != NULL)
  3200. {
  3201. #if DBG
  3202. {
  3203. // Make sure this link is valid!
  3204. pLink = *(ppLink + (pCurrentLink->uHash % TableLength));
  3205. while (pLink != NULL && pLink != pCurrentLink)
  3206. {
  3207. pLink = pLink->pNext;
  3208. }
  3209. if (pLink != pCurrentLink)
  3210. {
  3211. ASSERTEX(!"Invalid pCurrentLink", pCurrentLink);
  3212. *ppNextLink = NULL;
  3213. return FALSE; // EARLY RETURN
  3214. }
  3215. }
  3216. #endif // DBG
  3217. if (pCurrentLink->pNext != NULL)
  3218. {
  3219. // Found a next link.
  3220. //
  3221. *ppNextLink = pCurrentLink->pNext;
  3222. return TRUE; // EARLY RETURN
  3223. }
  3224. else
  3225. {
  3226. // End of current bucket, move to next one.
  3227. // We check later if we've gone past the end of the table.
  3228. //
  3229. ppLink += (pCurrentLink->uHash % TableLength) + 1;
  3230. }
  3231. }
  3232. // Look for next non-null item.
  3233. //
  3234. for ( ; ppLink < ppLinkEnd; ppLink++)
  3235. {
  3236. pLink = *ppLink;
  3237. if (pLink != NULL)
  3238. {
  3239. *ppNextLink = pLink;
  3240. return TRUE; // EARLY RETURN
  3241. }
  3242. }
  3243. *ppNextLink = NULL;
  3244. return FALSE;
  3245. }
  3246. VOID
  3247. RmAddHashItem(
  3248. PRM_HASH_TABLE pHashTable,
  3249. PRM_HASH_LINK * ppLink,
  3250. PRM_HASH_LINK pLink,
  3251. PVOID pvKey
  3252. )
  3253. /*++
  3254. Routine Description:
  3255. Add an item to the hash table at the specified location.
  3256. Caller is expected to serialize access to the hash table.
  3257. Arguments:
  3258. pHashTable - Hash table in which to add item.
  3259. ppLink - Points to place within table to add new item.
  3260. pLink - New item to add.
  3261. pvKey - key associated with the item.
  3262. TODO: pvKey is only used to compute uHash -- consider passing in uHash directly.
  3263. --*/
  3264. {
  3265. pLink->uHash = pHashTable->pHashInfo->pfnHash(pvKey);
  3266. pLink->pNext = *ppLink;
  3267. *ppLink = pLink;
  3268. pHashTable->NumItems++;
  3269. // TODO: if required, resize
  3270. }
  3271. VOID
  3272. RmRemoveHashItem(
  3273. PRM_HASH_TABLE pHashTable,
  3274. PRM_HASH_LINK pLinkToRemove
  3275. )
  3276. /*++
  3277. Routine Description:
  3278. Remove an item from the hash table.
  3279. Caller is expected to serialize access to the hash table.
  3280. (debug only): Asserts if pLinkToRemove is no in the hash table.
  3281. Arguments:
  3282. pHashTable - Hash table in which to add item.
  3283. pLinkToRemove - Link to remove.
  3284. --*/
  3285. {
  3286. PRM_HASH_LINK *ppLink, pLink;
  3287. UINT TableLength = pHashTable->TableLength;
  3288. ULONG uHash = pLinkToRemove->uHash;
  3289. BOOLEAN fFound = FALSE;
  3290. for (
  3291. ppLink = pHashTable->pTable + (uHash%TableLength);
  3292. (pLink = *ppLink) != NULL;
  3293. ppLink = &(pLink->pNext))
  3294. {
  3295. if (pLink == pLinkToRemove)
  3296. {
  3297. // found it -- remove it and get out.
  3298. //
  3299. RM_PRIVATE_UNLINK_NEXT_HASH(pHashTable, ppLink);
  3300. pLink->pNext = NULL; // Important, so that enumeration works.
  3301. fFound=TRUE;
  3302. break;
  3303. }
  3304. }
  3305. // TODO: if required, resize
  3306. ASSERT(fFound);
  3307. }
  3308. VOID
  3309. RmEnumHashTable(
  3310. PRM_HASH_TABLE pHashTable,
  3311. PFN_ENUM_HASH_TABLE pfnEnumerator,
  3312. PVOID pvContext,
  3313. PRM_STACK_RECORD pSR
  3314. )
  3315. /*++
  3316. Routine Description:
  3317. Calls function pfnEnumerator for each item in the hash table.
  3318. Caller is expected to serialize access to the hash table.
  3319. Arguments:
  3320. pHashTable - Hash table to enumerate.
  3321. pfnEnumerator - Enumerator function.
  3322. pvContext - Opaque context passed to enumerator function.
  3323. --*/
  3324. {
  3325. PRM_HASH_LINK *ppLink, *ppLinkEnd;
  3326. ppLink = pHashTable->pTable;
  3327. ppLinkEnd = ppLink + pHashTable->TableLength;
  3328. for ( ; ppLink < ppLinkEnd; ppLink++)
  3329. {
  3330. PRM_HASH_LINK pLink = *ppLink;
  3331. while (pLink != NULL)
  3332. {
  3333. pfnEnumerator(
  3334. pLink,
  3335. pvContext,
  3336. pSR
  3337. );
  3338. pLink = pLink->pNext;
  3339. }
  3340. }
  3341. }
  3342. VOID
  3343. RmEnumerateObjectsInGroup(
  3344. PRM_GROUP pGroup,
  3345. PFN_RM_GROUP_ENUMERATOR pfnEnumerator,
  3346. PVOID pvContext,
  3347. INT fStrong,
  3348. PRM_STACK_RECORD pSR
  3349. )
  3350. /*++
  3351. Routine Description:
  3352. Calls function pfnEnumerator for each item in the group, until
  3353. the funcition return FALSE.
  3354. WARNING: Enumeration is "STRONG" -- the group lock
  3355. is held during the whole enumeration process. The
  3356. enumerator function is therefore called at DPR level, and more importantly,
  3357. the enumerator function avoid locking anything to avoid risk of deadlock.
  3358. Specifically, the enumerator function MUST NOT lock the object -- if any other
  3359. thread has called a group-related RM function with the object's lock held,
  3360. we WILL deadlock.
  3361. This function should only be used to access parts of the object that do
  3362. not need to be protected by the objects lock.
  3363. If locking needs to be performed, use RmWeakEnumerateObjectsInGroup.
  3364. Arguments:
  3365. pGroup - Hash table to enumerate.
  3366. pfnEnumerator - Enumerator function.
  3367. pvContext - Opaque context passed to enumerator function.
  3368. fStrong - MUST be TRUE.
  3369. --*/
  3370. {
  3371. if (fStrong)
  3372. {
  3373. RM_STRONG_ENUMERATION_CONTEXT Ctxt;
  3374. Ctxt.pfnObjEnumerator = pfnEnumerator;
  3375. Ctxt.pvCallerContext = pvContext;
  3376. Ctxt.fContinue = TRUE;
  3377. NdisAcquireSpinLock(&pGroup->OsLock);
  3378. RmEnumHashTable(
  3379. &pGroup->HashTable,
  3380. rmEnumObjectInGroupHashTable, // pfnEnumerator
  3381. &Ctxt, // context
  3382. pSR
  3383. );
  3384. NdisReleaseSpinLock(&pGroup->OsLock);
  3385. }
  3386. else
  3387. {
  3388. ASSERT(!"Unimplemented");
  3389. }
  3390. }
  3391. VOID
  3392. RmWeakEnumerateObjectsInGroup(
  3393. PRM_GROUP pGroup,
  3394. PFN_RM_GROUP_ENUMERATOR pfnEnumerator,
  3395. PVOID pvContext,
  3396. PRM_STACK_RECORD pSR
  3397. )
  3398. /*++
  3399. Routine Description:
  3400. Calls function pfnEnumerator for each item in the group, until
  3401. the funcition return FALSE.
  3402. Enumeration is "WEAK" -- the group lock is
  3403. NOT held the whole time, and is not held when the enumerator
  3404. function is called.
  3405. A snapshot of the entire group is first taken with the group lock held,
  3406. and each object is tempref'd. The group lock is then released and the
  3407. enumerator function is called for each object in the snapshot.
  3408. The objects are then derefd.
  3409. NOTE: It is possible that when the enumeration function is called for an
  3410. object, the object is no longer in the group. The enumeration function can
  3411. lock the object and check its internal state to determine if it is still
  3412. relevant to process the object.
  3413. Arguments:
  3414. pGroup - Hash table to enumerate.
  3415. pfnEnumerator - Enumerator function.
  3416. pvContext - Opaque context passed to enumerator function.
  3417. --*/
  3418. {
  3419. #define RM_SMALL_GROUP_SIZE 10
  3420. #define RM_MAX_ENUM_GROUP_SIZE 100000
  3421. PRM_OBJECT_HEADER *ppSnapshot;
  3422. PRM_OBJECT_HEADER SmallSnapshot[RM_SMALL_GROUP_SIZE];
  3423. UINT NumItems = pGroup->HashTable.NumItems;
  3424. do
  3425. {
  3426. RM_WEAK_ENUMERATION_CONTEXT Ctxt;
  3427. if (NumItems <= RM_SMALL_GROUP_SIZE)
  3428. {
  3429. if (NumItems == 0) break;
  3430. ppSnapshot = SmallSnapshot;
  3431. }
  3432. else if (NumItems > RM_MAX_ENUM_GROUP_SIZE)
  3433. {
  3434. // TODO: LOG_RETAIL_ERROR
  3435. ASSERT(FALSE);
  3436. break;
  3437. }
  3438. else
  3439. {
  3440. RM_ALLOC(
  3441. &(void* )ppSnapshot,
  3442. NumItems*sizeof(PRM_OBJECT_HEADER),
  3443. MTAG_RMINTERNAL
  3444. );
  3445. if (ppSnapshot == NULL)
  3446. {
  3447. ASSERT(FALSE);
  3448. break;
  3449. }
  3450. }
  3451. Ctxt.ppCurrent = ppSnapshot;
  3452. Ctxt.ppEnd = ppSnapshot+NumItems;
  3453. NdisAcquireSpinLock(&pGroup->OsLock);
  3454. RmEnumHashTable(
  3455. &pGroup->HashTable,
  3456. rmConstructGroupSnapshot, // pfnEnumerator
  3457. &Ctxt, // context
  3458. pSR
  3459. );
  3460. NdisReleaseSpinLock(&pGroup->OsLock);
  3461. ASSERT(Ctxt.ppCurrent >= ppSnapshot);
  3462. ASSERT(Ctxt.ppCurrent <= Ctxt.ppEnd);
  3463. // Fix up ppEnd to point to the last actually-filled pointer.
  3464. //
  3465. Ctxt.ppEnd = Ctxt.ppCurrent;
  3466. Ctxt.ppCurrent = ppSnapshot;
  3467. for (;Ctxt.ppCurrent < Ctxt.ppEnd; Ctxt.ppCurrent++)
  3468. {
  3469. pfnEnumerator(
  3470. *Ctxt.ppCurrent,
  3471. pvContext,
  3472. pSR
  3473. );
  3474. RmTmpDereferenceObject(*Ctxt.ppCurrent, pSR);
  3475. }
  3476. if (ppSnapshot != SmallSnapshot)
  3477. {
  3478. RM_FREE(ppSnapshot);
  3479. ppSnapshot = NULL;
  3480. }
  3481. } while (FALSE);
  3482. }
  3483. //=========================================================================
  3484. // L O C A L F U N C T I O N S
  3485. //=========================================================================
  3486. VOID
  3487. rmDerefObject(
  3488. PRM_OBJECT_HEADER pObject,
  3489. PRM_STACK_RECORD pSR
  3490. )
  3491. /*++
  3492. Routine Description:
  3493. Dereference object pObject. Deallocate it if the reference count goes to zero.
  3494. --*/
  3495. {
  3496. ULONG Refs;
  3497. ENTER("rmDerefObject", 0x5f9d81dd)
  3498. ASSERT(RM_OBJECT_IS_ALLOCATED(pObject));
  3499. //
  3500. // On entry, the ref count should be at-least 2 -- one the
  3501. // explicit ref added in RmAllocateObject, and the 2nd the ref due to
  3502. // the link to the parent.
  3503. //
  3504. // Exception to the above: if the object has no parent, the refcount should be
  3505. // at-least 1.
  3506. //
  3507. // Deref the ref added in RmAllocateObject, and if the ref count is now <=1,
  3508. // we actually unlink and free the object.
  3509. //
  3510. Refs = NdisInterlockedDecrement(&pObject->TotRefs);
  3511. if (Refs <= 1)
  3512. {
  3513. PRM_OBJECT_HEADER pParentObject;
  3514. RMPRIVATELOCK(pObject, pSR);
  3515. //
  3516. // Unlink from parent, if there is one...
  3517. //
  3518. pParentObject = pObject->pParentObject;
  3519. pObject->pParentObject = NULL;
  3520. #if RM_TRACK_OBJECT_TREE
  3521. // Verify that there are no siblings...
  3522. //
  3523. RETAILASSERTEX(IsListEmpty(&pObject->listChildren), pObject);
  3524. #endif // RM_TRACK_OBJECT_TREE
  3525. RMPRIVATEUNLOCK(pObject, pSR);
  3526. if (pParentObject != NULL)
  3527. {
  3528. ASSERTEX(!RMISALLOCATED(pObject), pObject);
  3529. ASSERTEX(Refs == 1, pObject);
  3530. #if RM_TRACK_OBJECT_TREE
  3531. RMPRIVATELOCK(pParentObject, pSR);
  3532. // Remove object from parent's list of children.
  3533. //
  3534. RETAILASSERTEX(
  3535. !IsListEmpty(&pParentObject->listChildren),
  3536. pObject);
  3537. RemoveEntryList(&pObject->linkSiblings);
  3538. RMPRIVATEUNLOCK(pParentObject, pSR);
  3539. #endif // RM_TRACK_OBJECT_TREE
  3540. #if RM_EXTRA_CHECKING
  3541. RmUnlinkObjectsEx(
  3542. pObject,
  3543. pParentObject,
  3544. 0xac73e169,
  3545. RM_PRIVATE_ASSOC_LINK_CHILDOF,
  3546. RM_PRIVATE_ASSOC_LINK_PARENTOF,
  3547. pSR
  3548. );
  3549. #else // !RM_EXTRA_CHECKING
  3550. RmUnlinkObjects(pObject, pParentObject, pSR);
  3551. #endif // !RM_EXTRA_CHECKING
  3552. }
  3553. else if (Refs == 0)
  3554. {
  3555. //
  3556. // Free to deallocate this thing...
  3557. //
  3558. ASSERTEX(!RMISALLOCATED(pObject), pObject);
  3559. #if RM_EXTRA_CHECKING
  3560. rmDbgDeinitializeDiagnosticInfo(pObject, pSR);
  3561. #endif // RM_EXTRA_CHECKING
  3562. RM_MARK_OBJECT_AS_DEALLOCATED(pObject);
  3563. if (pObject->pStaticInfo->pfnDelete!= NULL)
  3564. {
  3565. TR_INFO((
  3566. "Actually freeing 0x%p (%s)\n",
  3567. pObject,
  3568. pObject->szDescription
  3569. ));
  3570. pObject->pStaticInfo->pfnDelete(pObject, pSR);
  3571. }
  3572. }
  3573. }
  3574. EXIT()
  3575. }
  3576. VOID
  3577. rmLock(
  3578. PRM_LOCK pLock,
  3579. #if RM_EXTRA_CHECKING
  3580. UINT uLocID,
  3581. PFNLOCKVERIFIER pfnVerifier,
  3582. PVOID pVerifierContext,
  3583. #endif //RM_EXTRA_CHECKING
  3584. PRM_STACK_RECORD pSR
  3585. )
  3586. /*++
  3587. Routine Description:
  3588. Lock pLock.
  3589. Arguments:
  3590. pLock - Lock to lock.
  3591. LocID - Arbitrary ID, typically representing the source location
  3592. from which this function is called.
  3593. Following are for debug only:
  3594. pfnVerifier - Optional function that is called just after locking
  3595. pfnVerifierContext - Passed in call to pfnVerifier
  3596. --*/
  3597. {
  3598. //UINT Level = pSR->LockInfo.CurrentLevel;
  3599. RM_LOCKING_INFO li;
  3600. RETAILASSERTEX(pLock->Level > pSR->LockInfo.CurrentLevel, pLock);
  3601. RETAILASSERTEX(pSR->LockInfo.pNextFree < pSR->LockInfo.pLast, pLock);
  3602. pSR->LockInfo.CurrentLevel = pLock->Level;
  3603. // Save information about this lock in the stack record.
  3604. //
  3605. li.pLock = pLock;
  3606. #if RM_EXTRA_CHECKING
  3607. li.pfnVerifier = pfnVerifier;
  3608. li.pVerifierContext = pVerifierContext;
  3609. #endif //RM_EXTRA_CHECKING
  3610. *(pSR->LockInfo.pNextFree++) = li; // struct copy.
  3611. // Get the lock.
  3612. // TODO: uncomment the following optimization...
  3613. //if (Level)
  3614. //{
  3615. // NdisDprAcquireSpinLock(&pLock->OsLock);
  3616. //}
  3617. //else
  3618. //{
  3619. NdisAcquireSpinLock(&pLock->OsLock);
  3620. //}
  3621. #if RM_EXTRA_CHECKING
  3622. ASSERTEX(pLock->DbgInfo.uLocID == 0, pLock);
  3623. ASSERTEX(pLock->DbgInfo.pSR == NULL, pLock);
  3624. pLock->DbgInfo.uLocID = uLocID;
  3625. pLock->DbgInfo.pSR = pSR;
  3626. // Call the verifier routine if there is one.
  3627. //
  3628. if (pfnVerifier)
  3629. {
  3630. pfnVerifier(pLock, TRUE, pVerifierContext, pSR);
  3631. }
  3632. #endif //RM_EXTRA_CHECKING
  3633. }
  3634. VOID
  3635. rmUnlock(
  3636. PRM_LOCK pLock,
  3637. PRM_STACK_RECORD pSR
  3638. )
  3639. /*++
  3640. Routine Description:
  3641. Unlock pLock.
  3642. Debug only: if there is a verifier function associated with this lock
  3643. we call it just before unlocking pLock.
  3644. Arguments:
  3645. pLock - Lock to unlock.
  3646. --*/
  3647. {
  3648. RM_LOCKING_INFO * pLI;
  3649. pSR->LockInfo.pNextFree--;
  3650. pLI = pSR->LockInfo.pNextFree;
  3651. RETAILASSERTEX(pLI->pLock == pLock, pLock);
  3652. ASSERTEX(pLock->DbgInfo.pSR == pSR, pLock);
  3653. ASSERTEX(pLock->Level == pSR->LockInfo.CurrentLevel, pLock);
  3654. pLI->pLock = NULL;
  3655. if (pLI > pSR->LockInfo.pFirst)
  3656. {
  3657. PRM_LOCK pPrevLock = (pLI-1)->pLock;
  3658. pSR->LockInfo.CurrentLevel = pPrevLock->Level;
  3659. ASSERTEX(pPrevLock->DbgInfo.pSR == pSR, pPrevLock);
  3660. }
  3661. else
  3662. {
  3663. pSR->LockInfo.CurrentLevel = 0;
  3664. }
  3665. #if RM_EXTRA_CHECKING
  3666. // Call the verifier routine if there is one.
  3667. //
  3668. if (pLI->pfnVerifier)
  3669. {
  3670. pLI->pfnVerifier(pLock, FALSE, pLI->pVerifierContext, pSR);
  3671. pLI->pfnVerifier = NULL;
  3672. pLI->pVerifierContext = NULL;
  3673. }
  3674. pLock->DbgInfo.uLocID = 0;
  3675. pLock->DbgInfo.pSR = NULL;
  3676. #endif //RM_EXTRA_CHECKING
  3677. // Release the lock.
  3678. //
  3679. NdisReleaseSpinLock(&pLock->OsLock);
  3680. }
  3681. #if RM_EXTRA_CHECKING
  3682. ULONG
  3683. rmPrivateLockVerifier(
  3684. PRM_LOCK pLock,
  3685. BOOLEAN fLock,
  3686. PVOID pContext,
  3687. PRM_STACK_RECORD pSR
  3688. )
  3689. /*++
  3690. Routine Description:
  3691. (Debug only)
  3692. The Verifier function for an object's RmPrivateLock.
  3693. Arguments:
  3694. pLock - Lock being locked/unlocked
  3695. fLock - TRUE if lock has just been locked.
  3696. FALSE if lock is about to be unlocked.
  3697. Return Value:
  3698. Unused: TODO make return value VOID.
  3699. --*/
  3700. {
  3701. ENTER("rmPrivateLockVerifier", 0xc3b63ac5)
  3702. TR_VERB(("Called with pLock=0x%p, fLock=%lu, pContext=%p\n",
  3703. pLock, fLock, pContext, pSR));
  3704. EXIT()
  3705. return 0;
  3706. }
  3707. ULONG
  3708. rmVerifyObjectState(
  3709. PRM_LOCK pLock,
  3710. BOOLEAN fLock,
  3711. PVOID pContext,
  3712. PRM_STACK_RECORD pSR
  3713. )
  3714. /*++
  3715. Routine Description:
  3716. (Debug only)
  3717. Uses the object's verification function (if there is one) to
  3718. compute a signature that is checked each time the object is locked,
  3719. and is updated each time the object is unlocked. Assert if this signature
  3720. has changed while the object was supposedly unlocked.
  3721. Also: Update RM_OBJECT_HEADER.pDiagInfo->PrevState if there is been a
  3722. change of state while the object was locked.
  3723. Arguments:
  3724. pLock - Lock being locked/unlocked
  3725. fLock - TRUE if lock has just been locked.
  3726. FALSE if lock is about to be unlocked.
  3727. pContext - Actually pointer to object being locked.
  3728. Return Value:
  3729. Unused: TODO make return value VOID.
  3730. --*/
  3731. {
  3732. PRM_OBJECT_HEADER pObj = (PRM_OBJECT_HEADER) pContext;
  3733. PRM_OBJECT_DIAGNOSTIC_INFO pDiagInfo = pObj->pDiagInfo;
  3734. ULONG NewChecksum;
  3735. ENTER("rmVerifyObjectState", 0xb8ff7a67)
  3736. TR_VERB(("Called with pLock=0x%p, fLock=%lu, pObj=%p\n",
  3737. pLock, fLock, pObj, pSR));
  3738. if (pDiagInfo != NULL
  3739. && !(pDiagInfo->DiagState & fRM_PRIVATE_DISABLE_LOCK_CHECKING))
  3740. {
  3741. // Compute the new checksum and as part of that call the
  3742. // object-specific verifier if there is one....
  3743. //
  3744. {
  3745. PFNLOCKVERIFIER pfnVerifier;
  3746. // We verify that the objset-specific state was not modified
  3747. // without the lock held. This is done by including the object-specific
  3748. // state in the checksum computation.
  3749. //
  3750. NewChecksum = pObj->State;
  3751. // Then, if the object has a verifier function, we call it, and
  3752. // fold in the return value into the checkum.
  3753. //
  3754. pfnVerifier = pObj->pStaticInfo->pfnLockVerifier;
  3755. if (pfnVerifier != NULL)
  3756. {
  3757. NewChecksum ^= pfnVerifier(pLock, fLock, pObj, pSR);
  3758. }
  3759. }
  3760. if (fLock) // We've just locked the object.
  3761. {
  3762. // First thing we do is to save the current value of pObj->State in
  3763. // the TmpState location -- we'll look at it again on unlocking.
  3764. //
  3765. pDiagInfo->TmpState = pObj->State;
  3766. // Now we compare the new checksum value with the value that wase
  3767. // saved the last time this object was locked...
  3768. // Special case: old Checksum was 0 -- as it is on initialization.
  3769. //
  3770. if (NewChecksum != pDiagInfo->Checksum && pDiagInfo->Checksum)
  3771. {
  3772. TR_WARN((
  3773. "Object 0x%p (%s) possibly modified without lock held!\n",
  3774. pObj,
  3775. pObj->szDescription
  3776. ));
  3777. // Unfortunately we hit this assert because there are places where
  3778. // the same lock is shared by many objects and
  3779. #if 0
  3780. // Give users the option to ignore further validation on this
  3781. // object.
  3782. //
  3783. TR_FATAL((
  3784. "To skip this assert, type \"ed 0x%p %lx; g\"\n",
  3785. &pDiagInfo->DiagState,
  3786. pDiagInfo->DiagState | fRM_PRIVATE_DISABLE_LOCK_CHECKING
  3787. ));
  3788. ASSERTEX(!"Object was modified without lock held!", pObj);
  3789. #endif // 0
  3790. }
  3791. }
  3792. else // We're just about to unlock the object....
  3793. {
  3794. // Update the signature...
  3795. //
  3796. pDiagInfo->Checksum = NewChecksum;
  3797. // If there has been a change in state between locking and unlockng
  3798. // this object, save the previous state.
  3799. //
  3800. if (pDiagInfo->TmpState != pObj->State)
  3801. {
  3802. pDiagInfo->PrevState = pDiagInfo->TmpState;
  3803. }
  3804. }
  3805. }
  3806. EXIT()
  3807. return 0;
  3808. }
  3809. #endif // RM_EXTRA_CHECKING
  3810. VOID
  3811. rmEndTask(
  3812. PRM_TASK pTask,
  3813. NDIS_STATUS Status,
  3814. PRM_STACK_RECORD pSR
  3815. )
  3816. /*++
  3817. Routine Description:
  3818. Send the RM_TASKOP_END to the task handler, and resume any tasks pending on
  3819. pTask.
  3820. Arguments:
  3821. pTask - Task to end.
  3822. Status - Completion status -- passed on to the task handler.
  3823. --*/
  3824. {
  3825. ENTER("rmEndtask", 0x5060d952)
  3826. PRM_TASK pPendingTask;
  3827. RM_ASSERT_NOLOCKS(pSR);
  3828. TR_INFO((
  3829. "ENDING Task 0x%p (%s); Status = 0x%lx\n",
  3830. pTask,
  3831. pTask->Hdr.szDescription,
  3832. Status
  3833. ));
  3834. // TODO: could change behavior so that we use the return value, but
  3835. // currently we ignore it...
  3836. //
  3837. pTask->pfnHandler(
  3838. pTask,
  3839. RM_TASKOP_END,
  3840. Status, // UserParam is overloaded here.
  3841. pSR
  3842. );
  3843. RM_ASSERT_NOLOCKS(pSR);
  3844. do
  3845. {
  3846. pPendingTask = NULL;
  3847. RMPRIVATELOCK(&pTask->Hdr, pSR);
  3848. if (!IsListEmpty(&pTask->listTasksPendingOnMe))
  3849. {
  3850. pPendingTask = CONTAINING_RECORD(
  3851. (pTask->listTasksPendingOnMe.Flink),
  3852. RM_TASK,
  3853. linkFellowPendingTasks
  3854. );
  3855. RmTmpReferenceObject(&pPendingTask->Hdr, pSR);
  3856. }
  3857. RMPRIVATEUNLOCK(&pTask->Hdr, pSR);
  3858. if (pPendingTask != NULL)
  3859. {
  3860. RmCancelPendOnOtherTask(
  3861. pPendingTask,
  3862. pTask,
  3863. Status,
  3864. pSR
  3865. );
  3866. RmTmpDereferenceObject(&pPendingTask->Hdr, pSR);
  3867. }
  3868. }
  3869. while(pPendingTask != NULL);
  3870. RM_ASSERT_NOLOCKS(pSR);
  3871. EXIT()
  3872. }
  3873. NDIS_STATUS
  3874. rmAllocatePrivateTask(
  3875. IN PRM_OBJECT_HEADER pParentObject,
  3876. IN PFN_RM_TASK_HANDLER pfnHandler,
  3877. IN UINT Timeout,
  3878. IN const char * szDescription, OPTIONAL
  3879. OUT PRM_TASK *ppTask,
  3880. IN PRM_STACK_RECORD pSR
  3881. )
  3882. /*++
  3883. Routine Description:
  3884. Allocate and initialize a task of subtype RM_PRIVATE_TASK.
  3885. Arguments:
  3886. pParentObject - Object that is to be the parent of the allocated task.
  3887. pfnHandler - The task handler for the task.
  3888. Timeout - Unused.
  3889. szDescription - Text describing this task.
  3890. ppTask - Place to store pointer to the new task.
  3891. Return Value:
  3892. NDIS_STATUS_SUCCESS if we could allocate and initialize the task.
  3893. NDIS_STATUS_RESOURCES otherwise
  3894. --*/
  3895. {
  3896. RM_PRIVATE_TASK *pRmTask;
  3897. NDIS_STATUS Status;
  3898. RM_ALLOCSTRUCT(pRmTask, MTAG_TASK); // TODO use lookaside lists.
  3899. *ppTask = NULL;
  3900. if (pRmTask != NULL)
  3901. {
  3902. RM_ZEROSTRUCT(pRmTask);
  3903. RmInitializeTask(
  3904. &(pRmTask->TskHdr),
  3905. pParentObject,
  3906. pfnHandler,
  3907. &RmPrivateTasks_StaticInfo,
  3908. szDescription,
  3909. Timeout,
  3910. pSR
  3911. );
  3912. *ppTask = &(pRmTask->TskHdr);
  3913. Status = NDIS_STATUS_SUCCESS;
  3914. }
  3915. else
  3916. {
  3917. Status = NDIS_STATUS_RESOURCES;
  3918. }
  3919. return Status;
  3920. }
  3921. NDIS_STATUS
  3922. rmTaskUnloadGroup(
  3923. IN struct _RM_TASK * pTask,
  3924. IN RM_TASK_OPERATION Code,
  3925. IN UINT_PTR UserParam,
  3926. IN PRM_STACK_RECORD pSR
  3927. )
  3928. /*++
  3929. Routine Description:
  3930. This task is responsible for unloading all the objects in the group.
  3931. pTask is a pointer to TASK_UNLOADGROUP, and that structure is expected
  3932. to be initialized, including containing the pGroup to unload.
  3933. Arguments:
  3934. UserParam for (Code == RM_TASKOP_START) : unused
  3935. --*/
  3936. {
  3937. NDIS_STATUS Status = NDIS_STATUS_FAILURE;
  3938. TASK_UNLOADGROUP *pMyTask = (TASK_UNLOADGROUP*) pTask;
  3939. PRM_GROUP pGroup = pMyTask->pGroup;
  3940. BOOLEAN fContinueUnload = FALSE;
  3941. ENTER("TaskUnloadGroup", 0x964ee422)
  3942. enum
  3943. {
  3944. PEND_WaitOnOtherTask,
  3945. PEND_UnloadObject
  3946. };
  3947. switch(Code)
  3948. {
  3949. case RM_TASKOP_START:
  3950. {
  3951. // If there is already an unload task bound to pGroup, we
  3952. // pend on it.
  3953. //
  3954. NdisAcquireSpinLock(&pGroup->OsLock);
  3955. if (pGroup->pUnloadTask != NULL)
  3956. {
  3957. PRM_TASK pOtherTask = pGroup->pUnloadTask;
  3958. TR_WARN(("unload task 0x%p already bound to pGroup 0x%p; pending on it.\n",
  3959. pOtherTask, pGroup));
  3960. RmTmpReferenceObject(&pOtherTask->Hdr, pSR);
  3961. NdisReleaseSpinLock(&pGroup->OsLock);
  3962. RmPendTaskOnOtherTask(
  3963. pTask,
  3964. PEND_WaitOnOtherTask,
  3965. pOtherTask,
  3966. pSR
  3967. );
  3968. RmTmpDereferenceObject(&pOtherTask->Hdr, pSR);
  3969. Status = NDIS_STATUS_PENDING;
  3970. break;
  3971. }
  3972. else if (!pGroup->fEnabled)
  3973. {
  3974. //
  3975. // Presumably this group has already been unloaded of all objects
  3976. // and is simply sitting around. We complete right away.
  3977. //
  3978. Status = NDIS_STATUS_SUCCESS;
  3979. }
  3980. else
  3981. {
  3982. //
  3983. // We're the 1st ones here -- continue on to unloading objects, if
  3984. // any...
  3985. //
  3986. pGroup->pUnloadTask = pTask;
  3987. pGroup->fEnabled = FALSE; // This will prevent new objects from
  3988. // being added and from the hash table
  3989. // itself from changing size.
  3990. pMyTask->uIndex = 0; // This keeps track of where we are in the
  3991. // hash table.
  3992. fContinueUnload = TRUE;
  3993. }
  3994. NdisReleaseSpinLock(&pGroup->OsLock);
  3995. }
  3996. break;
  3997. case RM_TASKOP_PENDCOMPLETE:
  3998. {
  3999. switch(RM_PEND_CODE(pTask))
  4000. {
  4001. case PEND_WaitOnOtherTask:
  4002. {
  4003. //
  4004. // Nothing to do -- finish task.
  4005. //
  4006. Status = NDIS_STATUS_SUCCESS;
  4007. }
  4008. break;
  4009. case PEND_UnloadObject:
  4010. {
  4011. //
  4012. // Just done unloading an object; unload another if required.
  4013. //
  4014. fContinueUnload = TRUE;
  4015. Status = NDIS_STATUS_SUCCESS;
  4016. }
  4017. break;
  4018. default:
  4019. ASSERTEX(!"Unknown pend code!", pTask);
  4020. break;
  4021. }
  4022. }
  4023. break;
  4024. case RM_TASKOP_END:
  4025. {
  4026. BOOLEAN fSignal;
  4027. NdisAcquireSpinLock(&pGroup->OsLock);
  4028. // Clear ourselves from pGroup, if we're there.
  4029. //
  4030. if (pGroup->pUnloadTask == pTask)
  4031. {
  4032. pGroup->pUnloadTask = NULL;
  4033. }
  4034. fSignal = pMyTask->fUseEvent;
  4035. pMyTask->fUseEvent = FALSE;
  4036. NdisReleaseSpinLock(&pGroup->OsLock);
  4037. if (fSignal)
  4038. {
  4039. NdisSetEvent(&pMyTask->BlockEvent);
  4040. }
  4041. Status = NDIS_STATUS_SUCCESS;
  4042. }
  4043. break;
  4044. default:
  4045. ASSERTEX(!"Unknown task op", pTask);
  4046. break;
  4047. } // switch (Code)
  4048. if (fContinueUnload)
  4049. {
  4050. do {
  4051. PRM_HASH_LINK *ppLink, *ppLinkEnd;
  4052. UINT uIndex;
  4053. PRM_HASH_LINK pLink;
  4054. PRM_OBJECT_HEADER pObj;
  4055. PRM_TASK pUnloadObjectTask;
  4056. NdisAcquireSpinLock(&pGroup->OsLock);
  4057. uIndex = pMyTask->uIndex;
  4058. //
  4059. // With fEnabled set to FALSE by us, we expect the following:
  4060. // (a) pHashTable->pTable is going to stay the same size.
  4061. // (b) No items are going to be added or removed by anyone else.
  4062. //
  4063. // Find the next non-empty hash table entry, starting at
  4064. // offset pMyTask->uIndex.
  4065. //
  4066. ASSERTEX(!pGroup->fEnabled, pGroup);
  4067. ASSERTEX(uIndex <= pGroup->HashTable.TableLength, pGroup);
  4068. ppLinkEnd = ppLink = pGroup->HashTable.pTable;
  4069. ppLink += uIndex;
  4070. ppLinkEnd += pGroup->HashTable.TableLength;
  4071. while (ppLink < ppLinkEnd && *ppLink == NULL)
  4072. {
  4073. ppLink++;
  4074. }
  4075. // Update index to our current position in the hash table.
  4076. //
  4077. pMyTask->uIndex = (UINT)(ppLink - pGroup->HashTable.pTable);
  4078. if (ppLink >= ppLinkEnd)
  4079. {
  4080. //
  4081. // We're done...
  4082. //
  4083. NdisReleaseSpinLock(&pGroup->OsLock);
  4084. Status = NDIS_STATUS_SUCCESS;
  4085. break;
  4086. }
  4087. //
  4088. // Found another object to unload...
  4089. // We'll allocate a task (pUnloadObjectTask) to unload that object,
  4090. // pend ourselves on it, and then start it.
  4091. //
  4092. //
  4093. pLink = *ppLink;
  4094. pObj = CONTAINING_RECORD(pLink, RM_OBJECT_HEADER, HashLink);
  4095. RmTmpReferenceObject(pObj, pSR);
  4096. ASSERT(pObj->pStaticInfo == pGroup->pStaticInfo);
  4097. NdisReleaseSpinLock(&pGroup->OsLock);
  4098. Status = pMyTask->pfnUnloadTaskAllocator(
  4099. pObj, // pParentObject,
  4100. pMyTask->pfnTaskUnloadObjectHandler, // pfnHandler,
  4101. 0, // Timeout,
  4102. "Task:Unload Object",
  4103. &pUnloadObjectTask,
  4104. pSR
  4105. );
  4106. if (FAIL(Status))
  4107. {
  4108. // Aargh... we couldn't allocate a task to unload this object.
  4109. // We'll return quietly, leaving the other objects intact...
  4110. //
  4111. ASSERTEX(!"Couldn't allocat unload task for object.", pObj);
  4112. RmTmpDereferenceObject(pObj, pSR);
  4113. break;
  4114. }
  4115. RmTmpDereferenceObject(pObj, pSR);
  4116. #if OBSOLETE // See 03/26/1999 notes.txt entry "Some proposed ..."
  4117. RmPendTaskOnOtherTask(
  4118. pTask,
  4119. PEND_UnloadObject,
  4120. pUnloadObjectTask, // task to pend on
  4121. pSR
  4122. );
  4123. (void)RmStartTask(
  4124. pUnloadObjectTask,
  4125. 0, // UserParam (unused)
  4126. pSR
  4127. );
  4128. Status = NDIS_STATUS_PENDING;
  4129. #else // !OBSOLETE
  4130. RmTmpReferenceObject(&pUnloadObjectTask->Hdr, pSR);
  4131. RmStartTask(
  4132. pUnloadObjectTask,
  4133. 0, // UserParam (unused)
  4134. pSR
  4135. );
  4136. Status = RmPendOnOtherTaskV2(
  4137. pTask,
  4138. PEND_UnloadObject,
  4139. pUnloadObjectTask,
  4140. pSR
  4141. );
  4142. RmTmpDereferenceObject(&pUnloadObjectTask->Hdr, pSR);
  4143. if (PEND(Status))
  4144. {
  4145. break;
  4146. }
  4147. #endif // !OBSOLETE
  4148. }
  4149. #if OBSOLETE // See 03/26/1999 notes.txt entry "Some proposed ..."
  4150. while (FALSE);
  4151. #else // !OBSOLETE
  4152. while (TRUE);
  4153. #endif // !OBSOLETE
  4154. } // if(fContinueUnload)
  4155. RM_ASSERT_NOLOCKS(pSR);
  4156. EXIT()
  4157. return Status;
  4158. }
  4159. #if RM_EXTRA_CHECKING
  4160. BOOLEAN
  4161. rmDbgAssociationCompareKey(
  4162. PVOID pKey,
  4163. PRM_HASH_LINK pItem
  4164. )
  4165. /*++
  4166. Routine Description:
  4167. Comparison function used to test for exact equality of items
  4168. in the debug association table.
  4169. Arguments:
  4170. pKey - Actually a pointer to an RM_PRIVATE_DBG_ASSOCIATION structure.
  4171. pItem - Points to RM_PRIVATE_DBG_ASSOCIATION.HashLink.
  4172. Return Value:
  4173. TRUE IFF the (Entity1, Entity2 and AssociationID) fields of the key
  4174. exactly match the corresponding fields of
  4175. CONTAINING_RECORD(pItem, RM_PRIVATE_DBG_ASSOCIATION, HashLink);
  4176. --*/
  4177. {
  4178. RM_PRIVATE_DBG_ASSOCIATION *pA =
  4179. CONTAINING_RECORD(pItem, RM_PRIVATE_DBG_ASSOCIATION, HashLink);
  4180. // pKey is actually a RM_PRIVATE_DBG_ASSOCIATION structure.
  4181. //
  4182. RM_PRIVATE_DBG_ASSOCIATION *pTrueKey = (RM_PRIVATE_DBG_ASSOCIATION *) pKey;
  4183. if ( pA->Entity1 == pTrueKey->Entity1
  4184. && pA->Entity2 == pTrueKey->Entity2
  4185. && pA->AssociationID == pTrueKey->AssociationID)
  4186. {
  4187. return TRUE;
  4188. }
  4189. else
  4190. {
  4191. return FALSE;
  4192. }
  4193. }
  4194. ULONG
  4195. rmDbgAssociationHash(
  4196. PVOID pKey
  4197. )
  4198. /*++
  4199. Routine Description:
  4200. Hash generating function used to compute a ULONG-sized hash from
  4201. key, which is actually a pointer to an RM_PRIVATE_DBG_ASSOCIATION structure.
  4202. Arguments:
  4203. pKey - Actually a pointer to an RM_PRIVATE_DBG_ASSOCIATION structure.
  4204. Return Value:
  4205. ULONG-sized hash generated from the (Entity1, Entity2 and AssociationID)
  4206. fields of the key.
  4207. --*/
  4208. {
  4209. // pKey is actually a RM_PRIVATE_DBG_ASSOCIATION structure.
  4210. //
  4211. RM_PRIVATE_DBG_ASSOCIATION *pTrueKey = (RM_PRIVATE_DBG_ASSOCIATION *) pKey;
  4212. ULONG_PTR big_hash;
  4213. big_hash = pTrueKey->Entity1;
  4214. big_hash ^= pTrueKey->Entity2;
  4215. big_hash ^= pTrueKey->AssociationID;
  4216. // Warning: Below, the return value would be truncated in 64-bit.
  4217. // That tolerable because after all this is just a hash.
  4218. // TODO: for 64-bit, consider xoring hi- and lo- DWORD instead of truncationg.
  4219. //
  4220. return (ULONG) big_hash;
  4221. }
  4222. // Static hash information use for the hash table (in the diagnostic information
  4223. // of each object) that keeps track of associations.
  4224. //
  4225. RM_HASH_INFO
  4226. rmDbgAssociation_HashInfo =
  4227. {
  4228. NULL, // pfnTableAllocator
  4229. NULL, // pfnTableDeallocator
  4230. rmDbgAssociationCompareKey, // fnCompare
  4231. // Function to generate a ULONG-sized hash.
  4232. //
  4233. rmDbgAssociationHash // pfnHash
  4234. };
  4235. VOID
  4236. rmDbgInitializeDiagnosticInfo(
  4237. PRM_OBJECT_HEADER pObject,
  4238. PRM_STACK_RECORD pSR
  4239. )
  4240. /*++
  4241. Routine Description:
  4242. Allocate and initialize the diagnostic information associated with
  4243. object pObject. This includes initializing the hash table used to keep
  4244. track of arbitrary associations.
  4245. --*/
  4246. {
  4247. ENTER("InitializeDiagnosticInfo", 0x55db57a2)
  4248. PRM_OBJECT_DIAGNOSTIC_INFO pDiagInfo;
  4249. TR_VERB((" pObj=0x%p\n", pObject));
  4250. // TODO: use lookaside lists for the allocation of these objects.
  4251. //
  4252. RM_ALLOCSTRUCT(pDiagInfo, MTAG_DBGINFO);
  4253. if (pDiagInfo != NULL)
  4254. {
  4255. RM_ZEROSTRUCT(pDiagInfo);
  4256. NdisAllocateSpinLock(&pDiagInfo->OsLock);
  4257. RmInitializeHashTable(
  4258. &rmDbgAssociation_HashInfo,
  4259. NULL, // pAllocationContext
  4260. &pDiagInfo->AssociationTable
  4261. );
  4262. pObject->pDiagInfo = pDiagInfo;
  4263. pDiagInfo->pOwningObject = pObject;
  4264. // Initialize the per-object log list.
  4265. //
  4266. InitializeListHead(&pDiagInfo->listObjectLog);
  4267. }
  4268. }
  4269. VOID
  4270. rmDbgFreeObjectLogEntries(
  4271. LIST_ENTRY *pObjectLog
  4272. )
  4273. /*++
  4274. Routine Description:
  4275. Remove and free all items from the object log pObjectLog.
  4276. It is assumed that no one is trying to add items to this log at this time.
  4277. --*/
  4278. {
  4279. LIST_ENTRY *pLink=NULL, *pNextLink=NULL;
  4280. if (IsListEmpty(pObjectLog)) return; // EARLY RETURN
  4281. NdisAcquireSpinLock(&RmGlobals.GlobalOsLock);
  4282. for(
  4283. pLink = pObjectLog->Flink;
  4284. pLink != pObjectLog;
  4285. pLink = pNextLink)
  4286. {
  4287. RM_DBG_LOG_ENTRY *pLogEntry;
  4288. LIST_ENTRY *pLinkGlobalLog;
  4289. pLogEntry = CONTAINING_RECORD(pLink, RM_DBG_LOG_ENTRY, linkObjectLog);
  4290. pLinkGlobalLog = &pLogEntry->linkGlobalLog;
  4291. // Remove entry from global log.
  4292. // We don't bother removing the entry from the local log list, because
  4293. // it's going away anyway.
  4294. //
  4295. RemoveEntryList(pLinkGlobalLog);
  4296. // Move to next entry in object's log (which may not be the next entry
  4297. // in the global log).
  4298. //
  4299. pNextLink = pLink->Flink;
  4300. // Free the buffer in the log entry, if any.
  4301. // TODO: need to use log buffer deallocation function --
  4302. // See notes.txt 03/07/1999 entry "Registering root objects with RM".
  4303. // For now we assume the this memory was allocated using
  4304. // NdisAllocateMemory[WithTag].
  4305. //
  4306. if (pLogEntry->pvBuf != NULL)
  4307. {
  4308. NdisFreeMemory(pLogEntry->pvBuf, 0, 0);
  4309. }
  4310. // Free the log entry itself.
  4311. //
  4312. rmDbgDeallocateLogEntry(pLogEntry);
  4313. }
  4314. NdisReleaseSpinLock(&RmGlobals.GlobalOsLock);
  4315. }
  4316. VOID
  4317. rmDbgDeinitializeDiagnosticInfo(
  4318. PRM_OBJECT_HEADER pObject,
  4319. PRM_STACK_RECORD pSR
  4320. )
  4321. /*++
  4322. Routine Description:
  4323. (Debug only)
  4324. Deinitialize and free the diagnostic information associated with
  4325. object pObject. This includes verifying that there are no remaining
  4326. associations and links.
  4327. --*/
  4328. {
  4329. ENTER("DeinitializeDiagnosticInfo", 0xa969291f)
  4330. PRM_OBJECT_DIAGNOSTIC_INFO pDiagInfo = pObject->pDiagInfo;
  4331. TR_VERB((" pObj=0x%p\n", pObject));
  4332. if (pDiagInfo != NULL)
  4333. {
  4334. // Free all the per-object log entries.
  4335. // Note: no one should be trying to add items to this log at this time
  4336. // because we're aboute to deallocate this object.
  4337. //
  4338. {
  4339. rmDbgFreeObjectLogEntries(&pDiagInfo->listObjectLog);
  4340. RM_ZEROSTRUCT(&pDiagInfo->listObjectLog);
  4341. }
  4342. if (pDiagInfo->AssociationTable.NumItems != 0)
  4343. {
  4344. //
  4345. // Ouch! Associations still left. We print out the associations and then
  4346. // DebugBreak.
  4347. //
  4348. TR_FATAL((
  4349. "FATAL: Object 0x%p still has some associations left!\n",
  4350. pObject
  4351. ));
  4352. RmDbgPrintAssociations(pObject, pSR);
  4353. ASSERT(!"Object has associations left at deallocation time.");
  4354. }
  4355. pObject->pDiagInfo = NULL;
  4356. RmDeinitializeHashTable(
  4357. &pDiagInfo->AssociationTable
  4358. );
  4359. //
  4360. // Add any other checks here...
  4361. //
  4362. NdisFreeSpinLock(&pDiagInfo->OsLock);
  4363. RM_FREE(pDiagInfo);
  4364. }
  4365. }
  4366. VOID
  4367. rmDbgPrintOneAssociation (
  4368. PRM_HASH_LINK pLink,
  4369. PVOID pvContext,
  4370. PRM_STACK_RECORD pSR
  4371. )
  4372. /*++
  4373. Routine Description:
  4374. Dump a single association.
  4375. Arguments:
  4376. pLink - Points to RM_PRIVATE_DBG_ASSOCIATION.HashLink.
  4377. pvContext - Unused
  4378. --*/
  4379. {
  4380. RM_PRIVATE_DBG_ASSOCIATION *pA =
  4381. CONTAINING_RECORD(pLink, RM_PRIVATE_DBG_ASSOCIATION, HashLink);
  4382. DbgPrint(
  4383. (char*) (pA->szFormatString),
  4384. pA->Entity1,
  4385. pA->Entity2,
  4386. pA->AssociationID
  4387. );
  4388. }
  4389. VOID
  4390. _cdecl
  4391. rmDefaultDumpEntry (
  4392. char *szFormatString,
  4393. UINT_PTR Param1,
  4394. UINT_PTR Param2,
  4395. UINT_PTR Param3,
  4396. UINT_PTR Param4
  4397. )
  4398. /*++
  4399. Routine Description:
  4400. Default function to dump the contents of an association.
  4401. --*/
  4402. {
  4403. DbgPrint(
  4404. szFormatString,
  4405. Param1,
  4406. Param2,
  4407. Param3,
  4408. Param4
  4409. );
  4410. }
  4411. UINT
  4412. rmSafeAppend(
  4413. char *szBuf,
  4414. const char *szAppend,
  4415. UINT cbBuf
  4416. )
  4417. /*++
  4418. Routine Description:
  4419. Append szAppend to szBuf, but don't exceed cbBuf, and make sure the
  4420. resulting string is null-terminated.
  4421. Return Value:
  4422. Total length of string (excluding null termination) after append.
  4423. --*/
  4424. {
  4425. UINT uRet;
  4426. char *pc = szBuf;
  4427. char *pcEnd = szBuf+cbBuf-1; // possible overflow, but we check below.
  4428. const char *pcFrom = szAppend;
  4429. if (cbBuf==0) return 0; // EARLY RETURN;
  4430. // Skip to end of szBuf
  4431. //
  4432. while (pc < pcEnd && *pc!=0)
  4433. {
  4434. pc++;
  4435. }
  4436. // Append szAppend
  4437. while (pc < pcEnd && *pcFrom!=0)
  4438. {
  4439. *pc++ = *pcFrom++;
  4440. }
  4441. // Append final zero
  4442. //
  4443. *pc=0;
  4444. return (UINT) (UINT_PTR) (pc-szBuf);
  4445. }
  4446. #endif //RM_EXTRA_CHECKING
  4447. VOID
  4448. rmWorkItemHandler_ResumeTaskAsync(
  4449. IN PNDIS_WORK_ITEM pWorkItem,
  4450. IN PVOID pTaskToResume
  4451. )
  4452. /*++
  4453. Routine Description:
  4454. NDIS work item handler which resumes the give task.
  4455. Arguments:
  4456. pWorkItem - Work item associated with the handler.
  4457. pTaskToResume - Actually a pointer to the task to resume.
  4458. --*/
  4459. {
  4460. PRM_TASK pTask = pTaskToResume;
  4461. UINT_PTR CompletionParam = pTask->AsyncCompletionParam;
  4462. RM_DECLARE_STACK_RECORD(sr)
  4463. ASSERTEX(RMISALLOCATED(&pTask->Hdr), pTask);
  4464. #if RM_EXTRA_CHECKING
  4465. // Undo the association added in RmResumeTasyAsync...
  4466. //
  4467. RmDbgDeleteAssociation(
  4468. 0xfc39a878, // Location ID
  4469. &pTask->Hdr, // pObject
  4470. CompletionParam, // Instance1
  4471. (UINT_PTR) pWorkItem, // Instance2
  4472. RM_PRIVATE_ASSOC_RESUME_TASK_ASYNC, // AssociationID
  4473. &sr
  4474. );
  4475. #endif // RM_EXTRA_CHECKING
  4476. // Actually resume the task.
  4477. //
  4478. RmResumeTask(pTask, CompletionParam, &sr);
  4479. RM_ASSERT_CLEAR(&sr)
  4480. }
  4481. VOID
  4482. rmTimerHandler_ResumeTaskDelayed(
  4483. IN PVOID SystemSpecific1,
  4484. IN PVOID FunctionContext,
  4485. IN PVOID SystemSpecific2,
  4486. IN PVOID SystemSpecific3
  4487. )
  4488. /*++
  4489. Routine Description:
  4490. NDIS timer handler which resumes the give task.
  4491. WARNING: This handler is also called internally by the RM APIs.
  4492. Implementation notes: -- see notes.txt 07/14/1999 entry.
  4493. Arguments:
  4494. SystemSpecific1 - Unused
  4495. FunctionContext - Actually a pointer to the task to be resumed.
  4496. SystemSpecific2 - Unused
  4497. SystemSpecific3 - Unused
  4498. --*/
  4499. {
  4500. PRM_TASK pTask = FunctionContext;
  4501. UINT_PTR CompletionParam = pTask->AsyncCompletionParam;
  4502. RM_DECLARE_STACK_RECORD(sr)
  4503. ASSERTEX(RMISALLOCATED(&pTask->Hdr), pTask);
  4504. #if RM_EXTRA_CHECKING
  4505. // Undo the association added in RmResumeTasyDelayed...
  4506. //
  4507. RmDbgDeleteAssociation(
  4508. 0xfc39a878, // Location ID
  4509. &pTask->Hdr, // pObject
  4510. CompletionParam, // Instance1
  4511. (UINT_PTR) NULL, // Instance2
  4512. RM_PRIVATE_ASSOC_RESUME_TASK_DELAYED, // AssociationID
  4513. &sr
  4514. );
  4515. #endif // RM_EXTRA_CHECKING
  4516. RMPRIVATELOCK(&pTask->Hdr, &sr);
  4517. ASSERT(RM_CHECK_STATE(pTask, RMTSKDELSTATE_MASK, RMTSKDELSTATE_DELAYED));
  4518. RM_SET_STATE(pTask, RMTSKDELSTATE_MASK, 0);
  4519. RM_SET_STATE(pTask, RMTSKABORTSTATE_MASK, 0);
  4520. RMPRIVATEUNLOCK(&pTask->Hdr, &sr);
  4521. // Actually resume the task.
  4522. //
  4523. RmResumeTask(pTask, CompletionParam, &sr);
  4524. RM_ASSERT_CLEAR(&sr)
  4525. }
  4526. VOID
  4527. rmPrivateTaskDelete (
  4528. PRM_OBJECT_HEADER pObj,
  4529. PRM_STACK_RECORD psr
  4530. )
  4531. /*++
  4532. Routine Description:
  4533. Free a private task (which was allocated using rmAllocatePrivateTask.
  4534. Arguments:
  4535. pObj - Actually a pointer to a task of subtype RM_PRIVATE_TASK.
  4536. --*/
  4537. {
  4538. RM_FREE(pObj);
  4539. }
  4540. #if RM_EXTRA_CHECKING
  4541. RM_DBG_LOG_ENTRY *
  4542. rmDbgAllocateLogEntry(VOID)
  4543. /*++
  4544. Routine Description:
  4545. Allocate an object log entry.
  4546. TODO use lookaside lists, and implement per-component global logs.
  4547. See notes.txt 03/07/1999 entry "Registering root objects with RM".
  4548. --*/
  4549. {
  4550. RM_DBG_LOG_ENTRY *pLE;
  4551. RM_ALLOCSTRUCT(pLE, MTAG_DBGINFO);
  4552. return pLE;
  4553. }
  4554. VOID
  4555. rmDbgDeallocateLogEntry(
  4556. RM_DBG_LOG_ENTRY *pLogEntry
  4557. )
  4558. /*++
  4559. Routine Description:
  4560. Free an object log entry.
  4561. TODO use lookaside lists, and implement per-component global logs.
  4562. See notes.txt 03/07/1999 entry "Registering root objects with RM".
  4563. --*/
  4564. {
  4565. RM_FREE(pLogEntry);
  4566. }
  4567. #endif // RM_EXTRA_CHECKING
  4568. VOID
  4569. rmUpdateHashTableStats(
  4570. PULONG pStats,
  4571. ULONG LinksTraversed
  4572. )
  4573. /*++
  4574. Routine Description:
  4575. Update the stats (loword == links traversed, hiword == num accesses)
  4576. --*/
  4577. {
  4578. ULONG OldStats;
  4579. ULONG Stats;
  4580. // Clip LinksTraversed to 2^13, or 8192
  4581. //
  4582. if (LinksTraversed > (1<<13))
  4583. {
  4584. LinksTraversed = 1<<13;
  4585. }
  4586. Stats = OldStats = *pStats;
  4587. // If either the loword or hiword of Stats is greater-than 2^13, we
  4588. // intiger-devide both by 2. We're really only interested in the ratio
  4589. // of the two, which is preserved by the division.
  4590. //
  4591. #define rmSTATS_MASK (0x11<<30|0x11<<14)
  4592. if (OldStats & rmSTATS_MASK)
  4593. {
  4594. Stats >>= 1;
  4595. Stats &= ~rmSTATS_MASK;
  4596. }
  4597. // Compute the updated value of stats..
  4598. // "1<<16" below means "one access"
  4599. //
  4600. Stats += LinksTraversed | (1<<16);
  4601. // Update the stats, but only if they haven't already been updated by
  4602. // someone else. Note that if they HAVE been updated, we will lose this
  4603. // update. Not a big deal as we are not looking for 100% exact statistics here.
  4604. //
  4605. InterlockedCompareExchange(pStats, Stats, OldStats);
  4606. }
  4607. VOID
  4608. rmEnumObjectInGroupHashTable (
  4609. PRM_HASH_LINK pLink,
  4610. PVOID pvContext,
  4611. PRM_STACK_RECORD pSR
  4612. )
  4613. /*++
  4614. Hash table enumerator to implement "STRONG" enumeration -- see
  4615. RmEnumerateObjectsInGroup.
  4616. --*/
  4617. {
  4618. PRM_STRONG_ENUMERATION_CONTEXT pCtxt = (PRM_STRONG_ENUMERATION_CONTEXT)pvContext;
  4619. if (pCtxt->fContinue)
  4620. {
  4621. PRM_OBJECT_HEADER pHdr;
  4622. pHdr = CONTAINING_RECORD(pLink, RM_OBJECT_HEADER, HashLink);
  4623. pCtxt->fContinue = pCtxt->pfnObjEnumerator(
  4624. pHdr,
  4625. pCtxt->pvCallerContext,
  4626. pSR
  4627. );
  4628. }
  4629. }
  4630. VOID
  4631. rmConstructGroupSnapshot (
  4632. PRM_HASH_LINK pLink,
  4633. PVOID pvContext,
  4634. PRM_STACK_RECORD pSR
  4635. )
  4636. /*++
  4637. Hash table enumerator to construct a snapshot of a group for weak enumeration.
  4638. See RmWeakEnumerateObjectsInGroup.
  4639. --*/
  4640. {
  4641. PRM_WEAK_ENUMERATION_CONTEXT pCtxt = (PRM_WEAK_ENUMERATION_CONTEXT)pvContext;
  4642. if (pCtxt->ppCurrent < pCtxt->ppEnd)
  4643. {
  4644. PRM_OBJECT_HEADER pHdr;
  4645. pHdr = CONTAINING_RECORD(pLink, RM_OBJECT_HEADER, HashLink);
  4646. RmTmpReferenceObject(pHdr, pSR);
  4647. *pCtxt->ppCurrent = pHdr;
  4648. pCtxt->ppCurrent++;
  4649. }
  4650. }