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.

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