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.

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