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

684 lines
18 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. nbqueue.c
  5. Abstract:
  6. This module implements non-blocking fifo queue.
  7. Author:
  8. David N. Cutler (davec) 24-Apr-2000
  9. Environment:
  10. Kernel mode only.
  11. Revision History:
  12. --*/
  13. #include "windows.h"
  14. #include "malloc.h"
  15. VOID
  16. DbgBreakPoint (
  17. VOID
  18. );
  19. extern ULONG StopSignal;
  20. //
  21. // Define non-blocking interlocked queue functions.
  22. //
  23. // A non-blocking queue is a singly link list of queue entries with a
  24. // head pointer and a tail pointer. The head and tail pointers use
  25. // sequenced pointers as do next links in the entries themselves. The
  26. // queueing discipline is FIFO. New entries are inserted at the tail
  27. // of the list and current entries are removed from the front of the
  28. // list.
  29. //
  30. // Non-blocking queues require a descriptor for each entry in the queue.
  31. // A descriptor consists of a sequenced next pointer and a PVOID data
  32. // value. Descriptors for a queue must be preallocated and inserted in
  33. // an SLIST before calling the function to initialize a non-blocking
  34. // queue header. The SLIST should have as many entries as required for
  35. // the respective queue.
  36. //
  37. typedef struct _NBQUEUE_BLOCK {
  38. ULONG64 Next;
  39. ULONG64 Data;
  40. } NBQUEUE_BLOCK, *PNBQUEUE_BLOCK;
  41. PVOID
  42. ExInitializeNBQueueHead (
  43. IN PSLIST_HEADER SlistHead
  44. );
  45. BOOLEAN
  46. ExInsertTailNBQueue (
  47. IN PVOID Header,
  48. IN ULONG64 Value
  49. );
  50. BOOLEAN
  51. ExRemoveHeadNBQueue (
  52. IN PVOID Header,
  53. OUT PULONG64 Value
  54. );
  55. #if defined(_X86_)
  56. #define InterlockedCompareExchange64(Destination, Exchange, Comperand) \
  57. xInterlockedCompareExchange64(Destination, &(Exchange), &(Comperand))
  58. LONG64
  59. __fastcall
  60. xInterlockedCompareExchange64 (
  61. IN OUT LONG64 volatile * Destination,
  62. IN PLONG64 Exchange,
  63. IN PLONG64 Comparand
  64. );
  65. #elif defined(_IA64_)
  66. #define InterlockedCompareExchange64 _InterlockedCompareExchange64
  67. LONGLONG
  68. __cdecl
  69. InterlockedCompareExchange64 (
  70. IN OUT LONGLONG volatile *Destination,
  71. IN LONGLONG ExChange,
  72. IN LONGLONG Comperand
  73. );
  74. #pragma intrinsic(_InterlockedCompareExchange64)
  75. #endif
  76. //
  77. // Define queue pointer structure - this is platform target specific.
  78. //
  79. #if defined(_AMD64_)
  80. typedef union _NBQUEUE_POINTER {
  81. struct {
  82. LONG64 Node : 48;
  83. LONG64 Count : 16;
  84. };
  85. LONG64 Data;
  86. } NBQUEUE_POINTER, *PNBQUEUE_POINTER;
  87. #elif defined(_X86_)
  88. typedef union _NBQUEUE_POINTER {
  89. struct {
  90. LONG Count;
  91. LONG Node;
  92. };
  93. LONG64 Data;
  94. } NBQUEUE_POINTER, *PNBQUEUE_POINTER;
  95. #elif defined(_IA64_)
  96. typedef union _NBQUEUE_POINTER {
  97. struct {
  98. LONG64 Node : 45;
  99. LONG64 Region : 3;
  100. LONG64 Count : 16;
  101. };
  102. LONG64 Data;
  103. } NBQUEUE_POINTER, *PNBQUEUE_POINTER;
  104. #else
  105. #error "no target architecture"
  106. #endif
  107. //
  108. // Define queue node struture.
  109. //
  110. typedef struct _NBQUEUE_NODE {
  111. NBQUEUE_POINTER Next;
  112. ULONG64 Value;
  113. } NBQUEUE_NODE, *PNBQUEUE_NODE;
  114. //
  115. // Define inline functions to pack and unpack pointers in the platform
  116. // specific non-blocking queue pointer structure.
  117. //
  118. #if defined(_AMD64_)
  119. __inline
  120. VOID
  121. PackNBQPointer (
  122. IN PNBQUEUE_POINTER Entry,
  123. IN PNBQUEUE_NODE Node
  124. )
  125. {
  126. Entry->Node = (LONG64)Node;
  127. return;
  128. }
  129. __inline
  130. PNBQUEUE_NODE
  131. UnpackNBQPointer (
  132. IN PNBQUEUE_POINTER Entry
  133. )
  134. {
  135. return (PVOID)((LONG64)(Entry->Node));
  136. }
  137. #elif defined(_X86_)
  138. __inline
  139. VOID
  140. PackNBQPointer (
  141. IN PNBQUEUE_POINTER Entry,
  142. IN PNBQUEUE_NODE Node
  143. )
  144. {
  145. Entry->Node = (LONG)Node;
  146. return;
  147. }
  148. __inline
  149. PNBQUEUE_NODE
  150. UnpackNBQPointer (
  151. IN PNBQUEUE_POINTER Entry
  152. )
  153. {
  154. return (PVOID)(Entry->Node);
  155. }
  156. #elif defined(_IA64_)
  157. __inline
  158. VOID
  159. PackNBQPointer (
  160. IN PNBQUEUE_POINTER Entry,
  161. IN PNBQUEUE_NODE Node
  162. )
  163. {
  164. Entry->Node = (LONG64)Node;
  165. Entry->Region = (LONG64)Node >> 61;
  166. return;
  167. }
  168. __inline
  169. PNBQUEUE_NODE
  170. UnpackNBQPointer (
  171. IN PNBQUEUE_POINTER Entry
  172. )
  173. {
  174. LONG64 Value;
  175. Value = Entry->Node & 0x1fffffffffffffff;
  176. Value |= Entry->Region << 61;
  177. return (PVOID)(Value);
  178. }
  179. #else
  180. #error "no target architecture"
  181. #endif
  182. //
  183. // Define queue descriptor structure.
  184. //
  185. typedef struct _NBQUEUE_HEADER {
  186. NBQUEUE_POINTER Head;
  187. NBQUEUE_POINTER Tail;
  188. PSLIST_HEADER SlistHead;
  189. } NBQUEUE_HEADER, *PNBQUEUE_HEADER;
  190. typedef struct _NBQUEUE_LOG {
  191. ULONG_PTR Type;
  192. PNBQUEUE_HEADER Queue;
  193. NBQUEUE_POINTER Head;
  194. NBQUEUE_POINTER Tail;
  195. NBQUEUE_POINTER Next;
  196. ULONG_PTR Value;
  197. PNBQUEUE_NODE Node;
  198. PVOID *Address;
  199. ULONG_PTR Fill;
  200. } NBQUEUE_LOG, *PNBQUEUE_LOG;
  201. #define NBQUEUE_LOG_SIZE 64
  202. NBQUEUE_LOG NbLog[NBQUEUE_LOG_SIZE + 1];
  203. ULONG xLogIndex = -1;
  204. #define LogInsertData(_queue_, _head_, _tail_, _next_) { \
  205. if (StopSignal != 0) { \
  206. LogIndex = NBQUEUE_LOG_SIZE; \
  207. } else { \
  208. LogIndex = InterlockedIncrement(&xLogIndex) & (NBQUEUE_LOG_SIZE - 1); \
  209. } \
  210. NbLog[LogIndex].Type = 0; \
  211. NbLog[LogIndex].Queue = _queue_; \
  212. NbLog[LogIndex].Head.Data = (_head_); \
  213. NbLog[LogIndex].Tail.Data = (_tail_); \
  214. NbLog[LogIndex].Next.Data = (_next_); \
  215. }
  216. #define LogRemoveData(_queue_, _head_, _tail_, _next_) { \
  217. if (StopSignal != 0) { \
  218. LogIndex = NBQUEUE_LOG_SIZE; \
  219. } else { \
  220. LogIndex = InterlockedIncrement(&xLogIndex) & (NBQUEUE_LOG_SIZE - 1); \
  221. } \
  222. NbLog[LogIndex].Type = 1; \
  223. NbLog[LogIndex].Queue = _queue_; \
  224. NbLog[LogIndex].Head.Data = (_head_); \
  225. NbLog[LogIndex].Tail.Data = (_tail_); \
  226. NbLog[LogIndex].Next.Data = (_next_); \
  227. }
  228. #pragma alloc_text(PAGE, ExInitializeNBQueueHead)
  229. PVOID
  230. ExInitializeNBQueueHead (
  231. IN PSLIST_HEADER SlistHead
  232. )
  233. /*++
  234. Routine Description:
  235. This function initializes a non-blocking queue header.
  236. N.B. It is assumed that the specified SLIST has been populated with
  237. non-blocking queue nodes prior to calling this routine.
  238. Arguments:
  239. SlistHead - Supplies a pointer to an SLIST header.
  240. Return Value:
  241. If the non-blocking queue is successfully initialized, then the
  242. address of the queue header is returned as the function value.
  243. Otherwise, NULL is returned as the function value.
  244. --*/
  245. {
  246. PNBQUEUE_HEADER QueueHead;
  247. PNBQUEUE_NODE QueueNode;
  248. //
  249. // Attempt to allocate the queue header. If the allocation fails, then
  250. // return NULL.
  251. //
  252. QueueHead = (PNBQUEUE_HEADER)malloc(sizeof(NBQUEUE_HEADER));
  253. if (QueueHead == NULL) {
  254. return NULL;
  255. }
  256. //
  257. // Attempt to allocate a queue node from the specified SLIST. If a node
  258. // can be allocated, then initialize the non-blocking queue header and
  259. // return the address of the queue header. Otherwise, free the queue
  260. // header and return NULL.
  261. //
  262. QueueHead->SlistHead = SlistHead;
  263. QueueNode = (PNBQUEUE_NODE)InterlockedPopEntrySList(QueueHead->SlistHead);
  264. if (QueueNode != NULL) {
  265. //
  266. // Initialize the queue node next pointer and value.
  267. //
  268. QueueNode->Next.Data = 0;
  269. QueueNode->Value = 0;
  270. //
  271. // Initialize the head and tail pointers in the queue header.
  272. //
  273. PackNBQPointer(&QueueHead->Head, QueueNode);
  274. QueueHead->Head.Count = 0;
  275. PackNBQPointer(&QueueHead->Tail, QueueNode);
  276. QueueHead->Tail.Count = 0;
  277. return QueueHead;
  278. } else {
  279. free(QueueHead);
  280. return NULL;
  281. }
  282. }
  283. BOOLEAN
  284. ExInsertTailNBQueue (
  285. IN PVOID Header,
  286. IN ULONG64 Value
  287. )
  288. /*++
  289. Routine Description:
  290. This function inserts the specific data value at the tail of the
  291. specified non-blocking queue.
  292. Arguments:
  293. Header - Supplies an opaque pointer to a non-blocking queue header.
  294. Value - Supplies a pointer to an opaque data value.
  295. Return Value:
  296. If the specified opaque data value is successfully inserted at the tail
  297. of the specified non-blocking queue, then a value of TRUE is returned as
  298. the function value. Otherwise, a value of FALSE is returned.
  299. N.B. FALSE is returned if a queue node cannot be allocated from the
  300. associated SLIST.
  301. --*/
  302. {
  303. NBQUEUE_POINTER Head;
  304. NBQUEUE_POINTER Insert;
  305. ULONG LogIndex;
  306. NBQUEUE_POINTER Next;
  307. PNBQUEUE_NODE NextNode;
  308. PNBQUEUE_HEADER QueueHead;
  309. PNBQUEUE_NODE QueueNode;
  310. NBQUEUE_POINTER Tail;
  311. PNBQUEUE_NODE TailNode;
  312. //
  313. // Attempt to allocate a queue node from the SLIST associated with
  314. // the specified non-blocking queue. If a node can be allocated, then
  315. // the node is inserted at the tail of the specified non-blocking
  316. // queue, and TRUE is returned as the function value. Otherwise, FALSE
  317. // is returned.
  318. //
  319. QueueHead = (PNBQUEUE_HEADER)Header;
  320. QueueNode = (PNBQUEUE_NODE)InterlockedPopEntrySList(QueueHead->SlistHead);
  321. if (QueueNode != NULL) {
  322. //
  323. // Initialize the queue node next pointer and value.
  324. //
  325. QueueNode->Next.Data = 0;
  326. QueueNode->Value = Value;
  327. //
  328. // The following loop is executed until the specified entry can
  329. // be safely inserted at the tail of the specified non-blocking
  330. // queue.
  331. //
  332. do {
  333. //
  334. // Read the tail queue pointer and the next queue pointer of
  335. // the tail queue pointer making sure the two pointers are
  336. // coherent.
  337. //
  338. Head.Data = *((volatile LONG64 *)(&QueueHead->Head.Data));
  339. Tail.Data = *((volatile LONG64 *)(&QueueHead->Tail.Data));
  340. TailNode = UnpackNBQPointer(&Tail);
  341. Next.Data = *((volatile LONG64 *)(&TailNode->Next.Data));
  342. LogInsertData(QueueHead, Head.Data, Tail.Data, Next.Data);
  343. NbLog[LogIndex].Address = &Header;
  344. QueueNode->Next.Count = Tail.Count + 1;
  345. if (Tail.Data == *((volatile LONG64 *)(&QueueHead->Tail.Data))) {
  346. //
  347. // If the tail is pointing to the last node in the list,
  348. // then attempt to insert the new node at the end of the
  349. // list. Otherwise, the tail is not pointing to the last
  350. // node in the list and an attempt is made to move the
  351. // tail pointer to the next node.
  352. //
  353. NextNode = UnpackNBQPointer(&Next);
  354. if (NextNode == NULL) {
  355. PackNBQPointer(&Insert, QueueNode);
  356. Insert.Count = Next.Count + 1;
  357. if (InterlockedCompareExchange64(&TailNode->Next.Data,
  358. Insert.Data,
  359. Next.Data) == Next.Data) {
  360. NbLog[LogIndex].Value = (ULONG)Value;
  361. NbLog[LogIndex].Node = QueueNode;
  362. break;
  363. } else {
  364. NbLog[LogIndex].Value = 0xffffffff;
  365. NbLog[LogIndex].Node = QueueNode;
  366. }
  367. } else {
  368. PackNBQPointer(&Insert, NextNode);
  369. Insert.Count = Tail.Count + 1;
  370. if (InterlockedCompareExchange64(&QueueHead->Tail.Data,
  371. Insert.Data,
  372. Tail.Data) == Tail.Data) {
  373. NbLog[LogIndex].Value = 0xffffff00;
  374. NbLog[LogIndex].Node = QueueNode;
  375. } else {
  376. NbLog[LogIndex].Value = 0xffff0000;
  377. NbLog[LogIndex].Node = QueueNode;
  378. }
  379. }
  380. } else {
  381. NbLog[LogIndex].Value = 0x000000ff;
  382. NbLog[LogIndex].Node = QueueNode;
  383. }
  384. } while (TRUE);
  385. //
  386. // Attempt to move the tail to the new tail node.
  387. //
  388. LogInsertData(QueueHead, Head.Data, Tail.Data, Next.Data);
  389. NbLog[LogIndex].Address = &Header;
  390. PackNBQPointer(&Insert, QueueNode);
  391. Insert.Count = Tail.Count + 1;
  392. if (InterlockedCompareExchange64(&QueueHead->Tail.Data,
  393. Insert.Data,
  394. Tail.Data) == Tail.Data) {
  395. NbLog[LogIndex].Value = 0x0000ffff;
  396. NbLog[LogIndex].Node = QueueNode;
  397. } else {
  398. NbLog[LogIndex].Value = 0x00ffffff;
  399. NbLog[LogIndex].Node = QueueNode;
  400. }
  401. return TRUE;
  402. } else {
  403. return FALSE;
  404. }
  405. }
  406. BOOLEAN
  407. ExRemoveHeadNBQueue (
  408. IN PVOID Header,
  409. OUT PULONG64 Value
  410. )
  411. /*++
  412. Routine Description:
  413. This function removes a queue entry from the head of the specified
  414. non-blocking queue and returns the associated data value.
  415. Arguments:
  416. Header - Supplies an opaque pointer to a non-blocking queue header.
  417. Value - Supplies a pointer to a variable that receives the queue
  418. element value.
  419. Return Value:
  420. If an entry is removed from the specified non-blocking queue, then
  421. TRUE is returned as the function value. Otherwise, FALSE is returned.
  422. --*/
  423. {
  424. NBQUEUE_POINTER Head;
  425. PNBQUEUE_NODE HeadNode;
  426. NBQUEUE_POINTER Insert;
  427. ULONG LogIndex;
  428. NBQUEUE_POINTER Next;
  429. PNBQUEUE_NODE NextNode;
  430. PNBQUEUE_HEADER QueueHead;
  431. NBQUEUE_POINTER Tail;
  432. PNBQUEUE_NODE TailNode;
  433. //
  434. // The following loop is executed until an entry can be removed from
  435. // the specified non-blocking queue or until it can be determined that
  436. // the queue is empty.
  437. //
  438. QueueHead = (PNBQUEUE_HEADER)Header;
  439. do {
  440. //
  441. // Read the head queue pointer, the tail queue pointer, and the
  442. // next queue pointer of the head queue pointer making sure the
  443. // three pointers are coherent.
  444. //
  445. Head.Data = *((volatile LONG64 *)(&QueueHead->Head.Data));
  446. Tail.Data = *((volatile LONG64 *)(&QueueHead->Tail.Data));
  447. HeadNode = UnpackNBQPointer(&Head);
  448. Next.Data = *((volatile LONG64 *)(&HeadNode->Next.Data));
  449. LogRemoveData(QueueHead, Head.Data, Tail.Data, Next.Data);
  450. NbLog[LogIndex].Address = &Header;
  451. if (Head.Data == *((volatile LONG64 *)(&QueueHead->Head.Data))) {
  452. //
  453. // If the queue header node is equal to the queue tail node,
  454. // then either the queue is empty or the tail pointer is falling
  455. // behind. Otherwise, there is an entry in the queue that can
  456. // be removed.
  457. //
  458. NextNode = UnpackNBQPointer(&Next);
  459. TailNode = UnpackNBQPointer(&Tail);
  460. if (HeadNode == TailNode) {
  461. //
  462. // If the next node of head pointer is NULL, then the queue
  463. // is empty. Otherwise, attempt to move the tail forward.
  464. //
  465. if (NextNode == NULL) {
  466. NbLog[LogIndex].Value = 0xffffffff;
  467. NbLog[LogIndex].Node = NULL;
  468. *Value = 0xffffffff;
  469. return FALSE;
  470. } else {
  471. PackNBQPointer(&Insert, NextNode);
  472. Insert.Count = Tail.Count + 1;
  473. if (InterlockedCompareExchange64(&QueueHead->Tail.Data,
  474. Insert.Data,
  475. Tail.Data) == Tail.Data) {
  476. NbLog[LogIndex].Value = 0xffffff00;
  477. NbLog[LogIndex].Node = NULL;
  478. } else {
  479. NbLog[LogIndex].Value = 0xffff0000;
  480. NbLog[LogIndex].Node = NULL;
  481. }
  482. }
  483. } else {
  484. //
  485. // Attempt to remove the first entry at the head of the queue.
  486. //
  487. *Value = ((ULONG64)LogIndex << 32) | NextNode->Value;
  488. PackNBQPointer(&Insert, NextNode);
  489. Insert.Count = Head.Count + 1;
  490. if (InterlockedCompareExchange64(&QueueHead->Head.Data,
  491. Insert.Data,
  492. Head.Data) == Head.Data) {
  493. NbLog[LogIndex].Value = (ULONG)*Value;
  494. NbLog[LogIndex].Node = NextNode;
  495. break;
  496. } else {
  497. NbLog[LogIndex].Value = 0x00ffffff;
  498. NbLog[LogIndex].Node = NextNode;
  499. }
  500. }
  501. } else {
  502. NbLog[LogIndex].Value = 0x0000ffff;
  503. NbLog[LogIndex].Node = NULL;
  504. }
  505. } while (TRUE);
  506. //
  507. // Free the node that was removed for the list by inserting the node
  508. // in the associated SLIST.
  509. //
  510. InterlockedPushEntrySList(QueueHead->SlistHead,
  511. (PSLIST_ENTRY)HeadNode);
  512. return TRUE;
  513. }