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.

1167 lines
25 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. Workque.c
  5. Abstract:
  6. This module implements the queue of work from the FSD to the
  7. FSP threads (system worker threads) for the NetWare redirector.
  8. Author:
  9. Colin Watson [ColinW] 19-Dec-1992
  10. Revision History:
  11. --*/
  12. #include "Procs.h"
  13. LIST_ENTRY IrpContextList;
  14. KSPIN_LOCK IrpContextInterlock;
  15. KSPIN_LOCK ContextInterlock;
  16. LONG FreeContextCount = 4; // Allow up to 4 free contexts
  17. LIST_ENTRY MiniIrpContextList;
  18. LONG FreeMiniContextCount = 20; // Allow up to 20 free mini contexts
  19. LONG MiniContextCount = 0; // Allow up to 20 free mini contexts
  20. HANDLE WorkerThreadHandle;
  21. //
  22. // The debug trace level
  23. //
  24. #define Dbg (DEBUG_TRACE_WORKQUE)
  25. #ifdef ALLOC_PRAGMA
  26. #pragma alloc_text( PAGE, InitializeIrpContext )
  27. #pragma alloc_text( PAGE, UninitializeIrpContext )
  28. #pragma alloc_text( PAGE, NwAppendToQueueAndWait )
  29. #pragma alloc_text( PAGE, WorkerThread )
  30. #ifndef QFE_BUILD
  31. //
  32. //#pragma alloc_text( PAGE1, NwDequeueIrpContext )
  33. //We hold a spinlock coming in or we acquire one inside the function
  34. //So, this should be non-paged.
  35. //
  36. #pragma alloc_text( PAGE1, AllocateMiniIrpContext )
  37. #pragma alloc_text( PAGE1, FreeMiniIrpContext )
  38. #endif
  39. #endif
  40. #if 0 // Not pageable
  41. AllocateIrpContext
  42. FreeIrpContext
  43. NwCompleteRequest
  44. SpawnWorkerThread
  45. // see ifndef QFE_BUILD above
  46. #endif
  47. PIRP_CONTEXT
  48. AllocateIrpContext (
  49. PIRP pIrp
  50. )
  51. /*++
  52. Routine Description:
  53. Initialize a work queue structure, allocating all structures used for it.
  54. Arguments:
  55. pIrp - Supplies the Irp for the applications request
  56. Return Value:
  57. PIRP_CONTEXT - Newly allocated Irp Context.
  58. --*/
  59. {
  60. PIRP_CONTEXT IrpContext;
  61. if ((IrpContext = (PIRP_CONTEXT )ExInterlockedRemoveHeadList(&IrpContextList, &IrpContextInterlock)) == NULL) {
  62. try {
  63. //
  64. // If there are no IRP contexts in the "zone", allocate a new
  65. // Irp context from non paged pool.
  66. //
  67. IrpContext = ALLOCATE_POOL_EX(NonPagedPool, sizeof(IRP_CONTEXT));
  68. RtlFillMemory( IrpContext, sizeof(IRP_CONTEXT), 0 );
  69. IrpContext->TxMdl = NULL;
  70. IrpContext->RxMdl = NULL;
  71. KeInitializeEvent( &IrpContext->Event, SynchronizationEvent, FALSE );
  72. IrpContext->NodeTypeCode = NW_NTC_IRP_CONTEXT;
  73. IrpContext->NodeByteSize = sizeof(IRP_CONTEXT);
  74. IrpContext->TxMdl = ALLOCATE_MDL( &IrpContext->req, MAX_SEND_DATA, FALSE, FALSE, NULL );
  75. if ( IrpContext->TxMdl == NULL) {
  76. InternalError(("Could not allocate TxMdl for IRP context\n"));
  77. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  78. }
  79. IrpContext->RxMdl = ALLOCATE_MDL( &IrpContext->rsp, MAX_RECV_DATA, FALSE, FALSE, NULL );
  80. if ( IrpContext->RxMdl == NULL) {
  81. InternalError(("Could not allocate RxMdl for IRP context\n"));
  82. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  83. }
  84. } finally {
  85. if ( AbnormalTermination() ) {
  86. if ( IrpContext != NULL ) {
  87. if (IrpContext->TxMdl != NULL ) {
  88. FREE_MDL( IrpContext->TxMdl );
  89. }
  90. FREE_POOL( IrpContext );
  91. } else {
  92. InternalError(("Could not allocate pool for IRP context\n"));
  93. }
  94. }
  95. }
  96. MmBuildMdlForNonPagedPool(IrpContext->TxMdl);
  97. MmBuildMdlForNonPagedPool(IrpContext->RxMdl);
  98. #ifdef NWDBG
  99. // Make it easy to find fields in the context
  100. IrpContext->Signature1 = 0xfeedf00d;
  101. IrpContext->Signature2 = 0xfeedf00d;
  102. IrpContext->Signature3 = 0xfeedf00d;
  103. #endif
  104. // IrpContext is allocated. Finish off initialization.
  105. } else {
  106. // Record that we have removed an entry from the free list
  107. InterlockedIncrement(&FreeContextCount);
  108. ASSERT( IrpContext != NULL );
  109. //
  110. // The free list uses the start of the structure for the list entry
  111. // so restore corrupted fields.
  112. //
  113. IrpContext->NodeTypeCode = NW_NTC_IRP_CONTEXT;
  114. IrpContext->NodeByteSize = sizeof(IRP_CONTEXT);
  115. // Ensure mdl's are clean
  116. IrpContext->TxMdl->Next = NULL;
  117. IrpContext->RxMdl->Next = NULL;
  118. IrpContext->RxMdl->ByteCount = MAX_RECV_DATA;
  119. //
  120. // Clean "used" fields
  121. //
  122. IrpContext->Flags = 0;
  123. IrpContext->Icb = NULL;
  124. IrpContext->pEx = NULL;
  125. IrpContext->TimeoutRoutine = NULL;
  126. IrpContext->CompletionSendRoutine = NULL;
  127. IrpContext->ReceiveDataRoutine = NULL;
  128. IrpContext->pTdiStruct = NULL;
  129. //
  130. // Clean the specific data zone.
  131. //
  132. RtlZeroMemory( &(IrpContext->Specific), sizeof( IrpContext->Specific ) );
  133. // 8/13/96 cjc Fix problem with apps not being able to save
  134. // files to NDS drives. This was never reset so
  135. // ExchangeWithWait ret'd an error to WriteNCP.
  136. IrpContext->ResponseParameters.Error = 0;
  137. }
  138. InterlockedIncrement(&ContextCount);
  139. //
  140. // Save away the fields in the Irp that might be tromped by
  141. // building the Irp for the exchange with the server.
  142. //
  143. IrpContext->pOriginalIrp = pIrp;
  144. if ( pIrp != NULL) {
  145. IrpContext->pOriginalSystemBuffer = pIrp->AssociatedIrp.SystemBuffer;
  146. IrpContext->pOriginalUserBuffer = pIrp->UserBuffer;
  147. IrpContext->pOriginalMdlAddress = pIrp->MdlAddress;
  148. }
  149. #ifdef NWDBG
  150. IrpContext->pNpScb = NULL;
  151. #endif
  152. ASSERT( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) );
  153. return IrpContext;
  154. }
  155. VOID
  156. FreeIrpContext (
  157. PIRP_CONTEXT IrpContext
  158. )
  159. /*++
  160. Routine Description:
  161. Initialize a work queue structure, allocating all structures used for it.
  162. Arguments:
  163. PIRP_CONTEXT IrpContext - Irp Context to free.
  164. None
  165. Return Value:
  166. --*/
  167. {
  168. ASSERT( IrpContext->NodeTypeCode == NW_NTC_IRP_CONTEXT );
  169. ASSERT( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) );
  170. ASSERT( IrpContext->PostProcessRoutine == NULL );
  171. FreeReceiveIrp( IrpContext );
  172. #ifdef NWDBG
  173. IrpContext->DebugValue = 0;
  174. #endif
  175. IrpContext->Flags = 0;
  176. //
  177. // Cleanup the Irp needs to be restored to its original settings.
  178. //
  179. if ( IrpContext->pOriginalIrp != NULL ) {
  180. PIRP pIrp = IrpContext->pOriginalIrp;
  181. pIrp->AssociatedIrp.SystemBuffer = IrpContext->pOriginalSystemBuffer;
  182. pIrp->UserBuffer = IrpContext->pOriginalUserBuffer;
  183. pIrp->MdlAddress = IrpContext->pOriginalMdlAddress;
  184. #ifdef NWDBG
  185. IrpContext->pOriginalIrp = NULL;
  186. #endif
  187. }
  188. #ifdef NWDBG
  189. RtlZeroMemory( &IrpContext->WorkQueueItem, sizeof( WORK_QUEUE_ITEM ) );
  190. #endif
  191. InterlockedDecrement(&ContextCount);
  192. if ( InterlockedDecrement(&FreeContextCount) >= 0 ) {
  193. //
  194. // We use the first two longwords of the IRP context as a list entry
  195. // when we free it to the list.
  196. //
  197. ExInterlockedInsertTailList(&IrpContextList,
  198. (PLIST_ENTRY )IrpContext,
  199. &IrpContextInterlock);
  200. } else {
  201. //
  202. // We already have as many free context as we allow so destroy
  203. // this context. Restore FreeContextCount to its original value.
  204. //
  205. InterlockedIncrement( &FreeContextCount );
  206. FREE_MDL( IrpContext->TxMdl );
  207. FREE_MDL( IrpContext->RxMdl );
  208. FREE_POOL(IrpContext);
  209. #ifdef NWDBG
  210. ContextCount --;
  211. #endif
  212. }
  213. }
  214. VOID
  215. InitializeIrpContext (
  216. VOID
  217. )
  218. /*++
  219. Routine Description:
  220. Initialize the Irp Context system
  221. Arguments:
  222. None.
  223. Return Value:
  224. None.
  225. --*/
  226. {
  227. PAGED_CODE();
  228. KeInitializeSpinLock(&IrpContextInterlock);
  229. KeInitializeSpinLock(&ContextInterlock);
  230. InitializeListHead(&IrpContextList);
  231. InitializeListHead(&MiniIrpContextList);
  232. }
  233. VOID
  234. UninitializeIrpContext (
  235. VOID
  236. )
  237. /*++
  238. Routine Description:
  239. Initialize the Irp Context system
  240. Arguments:
  241. None.
  242. Return Value:
  243. None.
  244. --*/
  245. {
  246. PIRP_CONTEXT IrpContext;
  247. PLIST_ENTRY ListEntry;
  248. PMINI_IRP_CONTEXT MiniIrpContext;
  249. PAGED_CODE();
  250. //
  251. // Free all the IRP contexts.
  252. //
  253. while ( !IsListEmpty( &IrpContextList ) ) {
  254. IrpContext = (PIRP_CONTEXT)RemoveHeadList( &IrpContextList );
  255. FREE_MDL( IrpContext->TxMdl );
  256. FREE_MDL( IrpContext->RxMdl );
  257. FREE_POOL(IrpContext);
  258. }
  259. while ( !IsListEmpty( &MiniIrpContextList ) ) {
  260. ListEntry = RemoveHeadList( &MiniIrpContextList );
  261. MiniIrpContext = CONTAINING_RECORD( ListEntry, MINI_IRP_CONTEXT, Next );
  262. FREE_POOL( MiniIrpContext->Buffer );
  263. FREE_MDL( MiniIrpContext->Mdl2 );
  264. FREE_MDL( MiniIrpContext->Mdl1 );
  265. FREE_IRP( MiniIrpContext->Irp );
  266. FREE_POOL( MiniIrpContext );
  267. }
  268. }
  269. VOID
  270. NwCompleteRequest (
  271. PIRP_CONTEXT IrpContext,
  272. NTSTATUS Status
  273. )
  274. /*++
  275. Routine Description:
  276. The following procedure is used by the FSP and FSD routines to complete
  277. an IRP.
  278. Arguments:
  279. IrpContext - A pointer to the IRP context information.
  280. Status - The status to use to complete the IRP.
  281. Return Value:
  282. None.
  283. --*/
  284. {
  285. PIRP Irp;
  286. if ( IrpContext == NULL ) {
  287. return;
  288. }
  289. if ( Status == STATUS_PENDING ) {
  290. return;
  291. }
  292. if ( Status == STATUS_INSUFFICIENT_RESOURCES ) {
  293. Error( EVENT_NWRDR_RESOURCE_SHORTAGE, Status, NULL, 0, 0 );
  294. }
  295. Irp = IrpContext->pOriginalIrp;
  296. Irp->IoStatus.Status = Status;
  297. DebugTrace(0, Dbg, "Completing Irp with status %X\n", Status );
  298. // Restore the Irp to its original state
  299. if ((Irp->CurrentLocation) > (CCHAR) (Irp->StackCount +1)) {
  300. DbgPrint("Irp is already completed.\n", Irp);
  301. DbgBreakPoint();
  302. }
  303. FreeIrpContext( IrpContext );
  304. IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
  305. return;
  306. }
  307. VOID
  308. NwAppendToQueueAndWait(
  309. PIRP_CONTEXT IrpContext
  310. )
  311. /*++
  312. Routine Description:
  313. This routine appends an IrpContext to the SCB queue, and waits the
  314. the queue to be ready to process the Irp.
  315. Arguments:
  316. IrpContext - A pointer to the IRP context information.
  317. Return Value:
  318. None.
  319. --*/
  320. {
  321. BOOLEAN AtFront;
  322. PAGED_CODE();
  323. DebugTrace(+1, Dbg, "NwAppendToQueueAndWait\n", 0);
  324. IrpContext->RunRoutine = SetEvent;
  325. #ifdef MSWDBG
  326. ASSERT( IrpContext->Event.Header.SignalState == 0 );
  327. #endif
  328. AtFront = AppendToScbQueue( IrpContext, IrpContext->pNpScb );
  329. if ( AtFront ) {
  330. KickQueue( IrpContext->pNpScb );
  331. }
  332. //
  333. // Wait until we get to the front of the queue.
  334. //
  335. KeWaitForSingleObject(
  336. &IrpContext->Event,
  337. UserRequest,
  338. KernelMode,
  339. FALSE,
  340. NULL );
  341. ASSERT( IrpContext->pNpScb->Requests.Flink == &IrpContext->NextRequest );
  342. DebugTrace(-1, Dbg, "NwAppendToQueueAndWait\n", 0);
  343. return;
  344. }
  345. VOID
  346. NwDequeueIrpContext(
  347. IN PIRP_CONTEXT pIrpContext,
  348. IN BOOLEAN OwnSpinLock
  349. )
  350. /*++
  351. Routine Description:
  352. This routine removes an IRP Context from the front the SCB queue.
  353. Arguments:
  354. IrpContext - A pointer to the IRP context information.
  355. OwnSpinLock - If TRUE, the caller owns the SCB spin lock.
  356. Return Value:
  357. None.
  358. --*/
  359. {
  360. PLIST_ENTRY pListEntry;
  361. KIRQL OldIrql;
  362. PNONPAGED_SCB pNpScb;
  363. DebugTrace(+1, Dbg, "NwDequeueIrpContext\n", 0);
  364. if (!BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) ) {
  365. DebugTrace(-1, Dbg, "NwDequeueIrpContext\n", 0);
  366. return;
  367. }
  368. pNpScb = pIrpContext->pNpScb;
  369. if ( !OwnSpinLock ) {
  370. KeAcquireSpinLock( &pNpScb->NpScbSpinLock, &OldIrql );
  371. }
  372. //
  373. // Disable timer from looking at this queue.
  374. //
  375. pNpScb->OkToReceive = FALSE;
  376. pListEntry = RemoveHeadList( &pNpScb->Requests );
  377. if ( !OwnSpinLock ) {
  378. KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
  379. }
  380. #ifdef NWDBG
  381. ASSERT ( CONTAINING_RECORD( pListEntry, IRP_CONTEXT, NextRequest ) == pIrpContext );
  382. {
  383. PIRP_CONTEXT RemovedContext = CONTAINING_RECORD( pListEntry, IRP_CONTEXT, NextRequest );
  384. if ( RemovedContext != pIrpContext ) {
  385. DbgBreakPoint();
  386. }
  387. }
  388. DebugTrace(
  389. 0,
  390. Dbg,
  391. "Dequeued IRP Context %08lx\n",
  392. CONTAINING_RECORD( pListEntry, IRP_CONTEXT, NextRequest ) );
  393. #ifdef MSWDBG
  394. pNpScb->RequestDequeued = TRUE;
  395. #endif
  396. #endif
  397. ClearFlag( pIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
  398. //
  399. // Give the next IRP context on the SCB queue a chance to run.
  400. //
  401. KickQueue( pNpScb );
  402. DebugTrace(-1, Dbg, "NwDequeueIrpContext\n", 0);
  403. return;
  404. }
  405. VOID
  406. NwCancelIrp (
  407. IN PDEVICE_OBJECT DeviceObject,
  408. IN PIRP Irp
  409. )
  410. /*++
  411. Routine Description:
  412. This routine implements the cancel function for an IRP being processed
  413. by the redirector.
  414. Arguments:
  415. DeviceObject - ignored
  416. Irp - Supplies the Irp being cancelled.
  417. Return Value:
  418. None.
  419. --*/
  420. {
  421. PLIST_ENTRY listEntry, nextListEntry;
  422. KIRQL OldIrql;
  423. PIRP_CONTEXT pTestIrpContext;
  424. PIRP pTestIrp;
  425. UNREFERENCED_PARAMETER( DeviceObject );
  426. //
  427. // We now need to void the cancel routine and release the io cancel
  428. // spin-lock.
  429. //
  430. IoSetCancelRoutine( Irp, NULL );
  431. IoReleaseCancelSpinLock( Irp->CancelIrql );
  432. //
  433. // Now we have to search for the IRP to cancel everywhere. So just
  434. // look for cancelled IRPs and process them all.
  435. //
  436. //
  437. // Process the Get Message queue.
  438. //
  439. KeAcquireSpinLock( &NwMessageSpinLock, &OldIrql );
  440. for ( listEntry = NwGetMessageList.Flink;
  441. listEntry != &NwGetMessageList;
  442. listEntry = nextListEntry ) {
  443. nextListEntry = listEntry->Flink;
  444. //
  445. // If the file object of the queued request, matches the file object
  446. // that is being closed, remove the IRP from the queue, and
  447. // complete it with an error.
  448. //
  449. pTestIrpContext = CONTAINING_RECORD( listEntry, IRP_CONTEXT, NextRequest );
  450. pTestIrp = pTestIrpContext->pOriginalIrp;
  451. if ( pTestIrp->Cancel ) {
  452. RemoveEntryList( listEntry );
  453. NwCompleteRequest( pTestIrpContext, STATUS_CANCELLED );
  454. }
  455. }
  456. KeReleaseSpinLock( &NwMessageSpinLock, OldIrql );
  457. //
  458. // Process the set of SCB IRP queues.
  459. //
  460. //
  461. // And return to our caller
  462. //
  463. return;
  464. }
  465. PMINI_IRP_CONTEXT
  466. AllocateMiniIrpContext (
  467. PIRP_CONTEXT IrpContext
  468. )
  469. /*++
  470. Routine Description:
  471. This routine allocates an IRP, a buffer, and an MDL for sending
  472. a burst write fragment.
  473. Arguments:
  474. None.
  475. Return Value:
  476. Irp - The allocated and initialized IRP.
  477. NULL - The IRP allocation failed.
  478. --*/
  479. {
  480. PMINI_IRP_CONTEXT MiniIrpContext;
  481. PIRP Irp = NULL;
  482. PMDL Mdl1 = NULL, Mdl2 = NULL;
  483. PVOID Buffer = NULL;
  484. PLIST_ENTRY ListEntry;
  485. ListEntry = ExInterlockedRemoveHeadList(
  486. &MiniIrpContextList,
  487. &IrpContextInterlock);
  488. if ( ListEntry == NULL) {
  489. try {
  490. MiniIrpContext = ALLOCATE_POOL_EX( NonPagedPool, sizeof( *MiniIrpContext ) );
  491. MiniIrpContext->NodeTypeCode = NW_NTC_MINI_IRP_CONTEXT;
  492. MiniIrpContext->NodeByteSize = sizeof( *MiniIrpContext );
  493. Irp = ALLOCATE_IRP(
  494. IrpContext->pNpScb->Server.pDeviceObject->StackSize,
  495. FALSE );
  496. if ( Irp == NULL ) {
  497. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  498. }
  499. Buffer = ALLOCATE_POOL_EX( NonPagedPool, sizeof( NCP_BURST_HEADER ) );
  500. Mdl1 = ALLOCATE_MDL( Buffer, sizeof( NCP_BURST_HEADER ), FALSE, FALSE, NULL );
  501. if ( Mdl1 == NULL ) {
  502. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  503. }
  504. MmBuildMdlForNonPagedPool( Mdl1 );
  505. //
  506. // Since this MDL can be used to send a packet on any server,
  507. // allocate an MDL large enough for any packet size.
  508. //
  509. Mdl2 = ALLOCATE_MDL( 0, 65535 + PAGE_SIZE - 1, FALSE, FALSE, NULL );
  510. if ( Mdl2 == NULL ) {
  511. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  512. }
  513. Mdl1->Next = Mdl2;
  514. MiniIrpContext->Irp = Irp;
  515. MiniIrpContext->Buffer = Buffer;
  516. MiniIrpContext->Mdl1 = Mdl1;
  517. MiniIrpContext->Mdl2 = Mdl2;
  518. InterlockedIncrement( &MiniContextCount );
  519. } except( EXCEPTION_EXECUTE_HANDLER ) {
  520. if ( Buffer != NULL ) {
  521. FREE_POOL( Buffer );
  522. }
  523. if ( Irp != NULL ) {
  524. FREE_IRP( Irp );
  525. }
  526. if ( Mdl1 != NULL ) {
  527. FREE_MDL( Mdl1 );
  528. }
  529. return( NULL );
  530. }
  531. } else {
  532. //
  533. // Record that we have removed an entry from the free list.
  534. //
  535. InterlockedIncrement( &FreeMiniContextCount );
  536. MiniIrpContext = CONTAINING_RECORD( ListEntry, MINI_IRP_CONTEXT, Next );
  537. }
  538. MiniIrpContext->IrpContext = IrpContext;
  539. return( MiniIrpContext );
  540. }
  541. VOID
  542. FreeMiniIrpContext (
  543. PMINI_IRP_CONTEXT MiniIrpContext
  544. )
  545. /*++
  546. Routine Description:
  547. This routine frees a mini IRP Context.
  548. Arguments:
  549. MiniIrpContext - The mini IRP context to free.
  550. Return Value:
  551. None.
  552. --*/
  553. {
  554. InterlockedDecrement( &MiniContextCount );
  555. if ( InterlockedDecrement( &FreeMiniContextCount ) >= 0 ) {
  556. //
  557. // Ok to keep this mini irp context. Just queue it to the free list.
  558. //
  559. MmPrepareMdlForReuse( MiniIrpContext->Mdl2 );
  560. ExInterlockedInsertTailList(
  561. &MiniIrpContextList,
  562. &MiniIrpContext->Next,
  563. &IrpContextInterlock );
  564. } else {
  565. //
  566. // We already have as many free context as we allow so destroy
  567. // this context. Restore FreeContextCount to its original value.
  568. //
  569. InterlockedIncrement( &FreeContextCount );
  570. FREE_POOL( MiniIrpContext->Buffer );
  571. FREE_MDL( MiniIrpContext->Mdl2 );
  572. FREE_MDL( MiniIrpContext->Mdl1 );
  573. FREE_IRP( MiniIrpContext->Irp );
  574. FREE_POOL( MiniIrpContext );
  575. }
  576. }
  577. PWORK_CONTEXT
  578. AllocateWorkContext (
  579. VOID
  580. )
  581. /*++
  582. Routine Description:
  583. Allocates a work queue structure, and initializes it.
  584. Arguments:
  585. None.
  586. Return Value:
  587. PWORK_CONTEXT - Newly allocated Work Context.
  588. --*/
  589. {
  590. PWORK_CONTEXT pWorkContext;
  591. try {
  592. pWorkContext = ALLOCATE_POOL_EX(NonPagedPool, sizeof(WORK_CONTEXT));
  593. RtlFillMemory( pWorkContext, sizeof(WORK_CONTEXT), 0 );
  594. pWorkContext->NodeTypeCode = NW_NTC_WORK_CONTEXT;
  595. pWorkContext->NodeByteSize = sizeof(WORK_CONTEXT);
  596. return pWorkContext;
  597. } except( EXCEPTION_EXECUTE_HANDLER ) {
  598. DebugTrace( 0, Dbg, "Failed to allocate work context\n", 0 );
  599. return NULL;
  600. }
  601. }
  602. VOID
  603. FreeWorkContext (
  604. PWORK_CONTEXT WorkContext
  605. )
  606. /*++
  607. Routine Description:
  608. Free the supplied work context.
  609. Arguments:
  610. PWORK_CONTEXT IrpContext - Work Context to free.
  611. Return Value:
  612. None
  613. --*/
  614. {
  615. ASSERT( WorkContext->NodeTypeCode == NW_NTC_WORK_CONTEXT );
  616. FREE_POOL(WorkContext);
  617. }
  618. VOID
  619. SpawnWorkerThread (
  620. VOID
  621. )
  622. /*++
  623. Routine Description:
  624. Create our own worker thread which will service reroute and reconnect
  625. attempts.
  626. Arguments:
  627. None.
  628. Return Value:
  629. None.
  630. --*/
  631. {
  632. NTSTATUS status;
  633. status = PsCreateSystemThread(
  634. &WorkerThreadHandle,
  635. PROCESS_ALL_ACCESS, // Access mask
  636. NULL, // object attributes
  637. NULL, // Process handle
  638. NULL, // client id
  639. (PKSTART_ROUTINE) WorkerThread, // Start routine
  640. NULL // Startcontext
  641. );
  642. if ( !NT_SUCCESS(status) ) {
  643. //
  644. // If we can't create the worker thread, it means that we
  645. // cannot service reconnect or reroute attempts. It is a
  646. // non-critical error.
  647. //
  648. DebugTrace( 0, Dbg, "SpawnWorkerThread: Can't create worker thread", 0 );
  649. WorkerThreadRunning = FALSE;
  650. } else {
  651. DebugTrace( 0, Dbg, "SpawnWorkerThread: created worker thread", 0 );
  652. WorkerThreadRunning = TRUE;
  653. }
  654. }
  655. VOID
  656. WorkerThread (
  657. VOID
  658. )
  659. {
  660. PLIST_ENTRY listentry;
  661. PWORK_CONTEXT workContext;
  662. PIRP_CONTEXT pIrpContext;
  663. NODE_WORK_CODE workCode;
  664. PNONPAGED_SCB OriginalNpScb = NULL;
  665. PAGED_CODE();
  666. DebugTrace( 0, Dbg, "Worker thread \n", 0 );
  667. IoSetThreadHardErrorMode( FALSE );
  668. while (TRUE) {
  669. //
  670. // Check to see if we have any work to do.
  671. //
  672. listentry = KeRemoveQueue (
  673. &KernelQueue, // Kernel queue object
  674. KernelMode, // Processor wait mode
  675. NULL // No timeout
  676. );
  677. ASSERT( listentry != (PVOID) STATUS_TIMEOUT);
  678. //
  679. // We have atleast one reroute attempt to look into. Get the address of the
  680. // work item.
  681. //
  682. workContext = CONTAINING_RECORD (
  683. listentry,
  684. WORK_CONTEXT,
  685. Next
  686. );
  687. pIrpContext = workContext->pIrpC;
  688. workCode = workContext->NodeWorkCode;
  689. if (pIrpContext) {
  690. OriginalNpScb = pIrpContext->pNpScb;
  691. }
  692. //
  693. // We don't need the work context anymore
  694. //
  695. FreeWorkContext( workContext );
  696. //
  697. // The work which this thread does can be one of the following:
  698. //
  699. // - Attempt a reroute
  700. // - Attempt a reconnect
  701. // - Terminate itself
  702. //
  703. switch (workCode) {
  704. case NWC_NWC_REROUTE:
  705. {
  706. ASSERT(BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_REROUTE_IN_PROGRESS ));
  707. DebugTrace( 0, Dbg, "worker got reroute work for scb 0x%x.\n", OriginalNpScb );
  708. if ( BooleanFlagOn( pIrpContext->Flags,
  709. IRP_FLAG_BURST_PACKET ) ) {
  710. NewRouteBurstRetry( pIrpContext );
  711. } else {
  712. NewRouteRetry( pIrpContext );
  713. }
  714. NwDereferenceScb( OriginalNpScb );
  715. ClearFlag( pIrpContext->Flags, IRP_FLAG_REROUTE_IN_PROGRESS );
  716. break;
  717. }
  718. case NWC_NWC_RECONNECT:
  719. {
  720. DebugTrace( 0, Dbg, "worker got reconnect work.\n", 0 );
  721. ReconnectRetry( pIrpContext );
  722. break;
  723. }
  724. case NWC_NWC_TERMINATE:
  725. {
  726. DebugTrace( 0, Dbg, "Terminated worker thread.\n", 0 );
  727. //
  728. // Flush any remaining work items out of the work queue...
  729. //
  730. while (listentry != NULL) {
  731. listentry = KeRundownQueue( &KernelQueue );
  732. DebugTrace( 0, Dbg, "Residual workitem in q %X.\n",listentry );
  733. }
  734. //
  735. // and terminate yourself.
  736. //
  737. WorkerThreadRunning = FALSE;
  738. PsTerminateSystemThread( STATUS_SUCCESS );
  739. break;
  740. }
  741. default:
  742. {
  743. //
  744. // There is something wrong here.
  745. //
  746. DebugTrace( 0, Dbg, "Unknown work code...ignoring\n", 0 );
  747. }
  748. }
  749. }
  750. }
  751. VOID
  752. TerminateWorkerThread (
  753. VOID
  754. )
  755. {
  756. PWORK_CONTEXT workContext = NULL;
  757. LARGE_INTEGER timeout;
  758. NTSTATUS status;
  759. if (WorkerThreadRunning == TRUE) {
  760. //
  761. // set a 5 second timeout for retrying allocation failures
  762. //
  763. timeout.QuadPart = (LONGLONG) ( NwOneSecond * 5 * (-1) );
  764. //
  765. // Prepare the work context
  766. //
  767. workContext = AllocateWorkContext();
  768. while ( workContext == NULL) {
  769. KeDelayExecutionThread( KernelMode,
  770. FALSE,
  771. &timeout
  772. );
  773. workContext = AllocateWorkContext();
  774. }
  775. workContext->NodeWorkCode = NWC_NWC_TERMINATE;
  776. workContext->pIrpC = NULL;
  777. //
  778. // and queue it.
  779. //
  780. KeInsertQueue( &KernelQueue,
  781. &workContext->Next
  782. );
  783. //
  784. // We now have to wait until the thread terminates itself.
  785. //
  786. DebugTrace( 0, Dbg, "TerminateWorkerThread: Waiting for thread termination.\n", 0 );
  787. do {
  788. status = ZwWaitForSingleObject( WorkerThreadHandle,
  789. FALSE,
  790. NULL // No timeout
  791. );
  792. } while ( !NT_SUCCESS( status ) );
  793. DebugTrace( 0, Dbg, "TerminateWorkerThread: Wait returned with 0x%x\n", status );
  794. status = ZwClose( WorkerThreadHandle );
  795. }
  796. }