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.

716 lines
19 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. WorkQue.c
  5. Abstract:
  6. This module implements the Work queue routines for the Ntfs File
  7. system.
  8. Author:
  9. Gary Kimura [GaryKi] 21-May-1991
  10. Revision History:
  11. --*/
  12. #include "NtfsProc.h"
  13. //
  14. // The following constant is the maximum number of ExWorkerThreads that we
  15. // will allow to be servicing a particular target device at any one time.
  16. //
  17. #define FSP_PER_DEVICE_THRESHOLD (2)
  18. #ifdef ALLOC_PRAGMA
  19. #pragma alloc_text(PAGE, NtfsOplockComplete)
  20. #endif
  21. VOID
  22. NtfsAddToWorkqueInternal (
  23. IN PIRP_CONTEXT IrpContext,
  24. IN PIRP Irp OPTIONAL,
  25. IN BOOLEAN CanBlock
  26. );
  27. VOID
  28. NtfsOplockComplete (
  29. IN PVOID Context,
  30. IN PIRP Irp
  31. )
  32. /*++
  33. Routine Description:
  34. This routine is called by the oplock package when an oplock break has
  35. completed, allowing an Irp to resume execution. If the status in
  36. the Irp is STATUS_SUCCESS, then we either queue the Irp to the Fsp queue or
  37. signal an event depending on whether the caller handles oplock completions synchronously.
  38. Otherwise we complete the Irp with the status in the Irp.
  39. Arguments:
  40. Context - Pointer to the IrpContext to be queued to the Fsp
  41. Irp - I/O Request Packet.
  42. Return Value:
  43. None.
  44. --*/
  45. {
  46. NTSTATUS Status = Irp->IoStatus.Status;
  47. PIRP_CONTEXT IrpContext = (PIRP_CONTEXT) Context;
  48. PKEVENT Event = NULL;
  49. PAGED_CODE();
  50. //
  51. // Check for an event that we should to signal synchronous completion
  52. // This exists in 2 cases
  53. //
  54. // 1) Non-fsp creates (fsp creates don't have a completion context which is
  55. // how we distinguish them
  56. //
  57. // 2) Successful Non fsp read/writes (These indicate the NTFS_IO_CONTEXT_INLINE_OPLOCK
  58. // flag in their NtfsIoContext
  59. //
  60. if (IrpContext->MajorFunction == IRP_MJ_CREATE) {
  61. if ((IrpContext->Union.OplockCleanup != NULL) &&
  62. (IrpContext->Union.OplockCleanup->CompletionContext != NULL)) {
  63. Event = &IrpContext->Union.OplockCleanup->CompletionContext->Event;
  64. ASSERT( FlagOn( IrpContext->State, IRP_CONTEXT_STATE_PERSISTENT ) );
  65. }
  66. } else if ((IrpContext->MajorFunction == IRP_MJ_WRITE) &&
  67. (IrpContext->Union.NtfsIoContext != NULL) &&
  68. FlagOn( IrpContext->Union.NtfsIoContext->Flags, NTFS_IO_CONTEXT_INLINE_OPLOCK )) {
  69. Event = &IrpContext->Union.NtfsIoContext->Wait.SyncEvent;
  70. //
  71. // Set the irp to not delete itself or the ntfsiocontext when we clean it up
  72. //
  73. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DONT_DELETE );
  74. }
  75. //
  76. // If we have a completion event then we want to clean up the IrpContext
  77. // and then signal the event
  78. //
  79. if (Event) {
  80. NtfsCompleteRequest( IrpContext, NULL, Status );
  81. KeSetEvent( Event, 0, FALSE );
  82. ASSERT( Status != STATUS_PENDING && Status != STATUS_REPARSE );
  83. } else if (Status == STATUS_SUCCESS) {
  84. //
  85. // Insert the Irp context in the workqueue to retry on a regular
  86. // successfull oplock break
  87. //
  88. NtfsAddToWorkqueInternal( IrpContext, Irp, FALSE );
  89. } else {
  90. //
  91. // Otherwise complete the Irp and cleanup the IrpContext.
  92. //
  93. ASSERT( Status != STATUS_PENDING && Status != STATUS_REPARSE );
  94. NtfsCompleteRequest( IrpContext, Irp, Status );
  95. }
  96. return;
  97. }
  98. VOID
  99. NtfsPrePostIrp (
  100. IN PVOID Context,
  101. IN PIRP Irp OPTIONAL
  102. )
  103. /*++
  104. Routine Description:
  105. This routine performs any neccessary work before STATUS_PENDING is
  106. returned with the Fsd thread. This routine is called within the
  107. filesystem and by the oplock package.
  108. Arguments:
  109. Context - Pointer to the IrpContext to be queued to the Fsp
  110. Irp - I/O Request Packet (or FileObject in special close path)
  111. Return Value:
  112. None.
  113. --*/
  114. {
  115. NtfsPrePostIrpInternal( Context, Irp, TRUE, FALSE );
  116. }
  117. VOID
  118. NtfsWriteOplockPrePostIrp (
  119. IN PVOID Context,
  120. IN PIRP Irp OPTIONAL
  121. )
  122. /*++
  123. Routine Description:
  124. This routine performs any neccessary work before STATUS_PENDING is
  125. returned with the Fsd thread. This routine is called by the oplock package
  126. for write irps. We will decide whether to save the toplevelcontext based
  127. on whether the oplock is being handled inline or not
  128. Arguments:
  129. Context - Pointer to the IrpContext to be queued to the Fsp
  130. Irp - I/O Request Packet (or FileObject in special close path)
  131. Return Value:
  132. None.
  133. --*/
  134. {
  135. PIRP_CONTEXT IrpContext = (PIRP_CONTEXT)Context;
  136. BOOLEAN Inline = BooleanFlagOn( IrpContext->Union.NtfsIoContext->Flags, NTFS_IO_CONTEXT_INLINE_OPLOCK );
  137. //
  138. // Cleanup the iocontext before posting an oplock - so if its on the stack
  139. // we don't attempt to reference it during oplock completion
  140. //
  141. if (!Inline) {
  142. if (FlagOn( IrpContext->State, IRP_CONTEXT_STATE_ALLOC_IO_CONTEXT )) {
  143. ExFreeToNPagedLookasideList( &NtfsIoContextLookasideList, IrpContext->Union.NtfsIoContext );
  144. }
  145. IrpContext->Union.NtfsIoContext = NULL;
  146. }
  147. NtfsPrePostIrpInternal( Context, Irp, TRUE, Inline );
  148. }
  149. VOID
  150. NtfsPrePostIrpInternal (
  151. IN PVOID Context,
  152. IN PIRP Irp OPTIONAL,
  153. IN BOOLEAN PendIrp,
  154. IN BOOLEAN SaveContext
  155. )
  156. /*++
  157. Routine Description:
  158. This routine performs any neccessary work before STATUS_PENDING is
  159. returned with the Fsd thread. This routine is called within the
  160. filesystem and by the oplock package.
  161. Arguments:
  162. Context - Pointer to the IrpContext to be queued to the Fsp
  163. Irp - I/O Request Packet (or FileObject in special close path)
  164. PendIrp - if true mark the irp pending as well
  165. SaveContext - if true don't restore top level context even if its owned
  166. The caller will be waiting on the posted irp inline and continuing processing
  167. Return Value:
  168. None.
  169. --*/
  170. {
  171. PIRP_CONTEXT IrpContext;
  172. PIO_STACK_LOCATION IrpSp = NULL;
  173. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  174. PUSN_FCB ThisUsn, LastUsn;
  175. #endif
  176. IrpContext = (PIRP_CONTEXT) Context;
  177. //
  178. // Make this is a valid allocated IrpContext. It's ok for
  179. // this to be allocated on the caller's stack as long as the
  180. // caller's not doing this operation asynchronously.
  181. //
  182. ASSERT_IRP_CONTEXT( IrpContext );
  183. ASSERT((FlagOn( IrpContext->State, IRP_CONTEXT_STATE_ALLOC_FROM_POOL )) ||
  184. (IrpContext->NodeTypeCode == NTFS_NTC_IRP_CONTEXT));
  185. //
  186. // Make sure if we are posting the request, which may be
  187. // because of log file full, that we free any Fcbs or PagingIo
  188. // resources which were acquired.
  189. //
  190. //
  191. // Just in case we somehow get here with a transaction ID, clear
  192. // it here so we do not loop forever.
  193. //
  194. if (IrpContext->TransactionId != 0) {
  195. NtfsCleanupFailedTransaction( IrpContext );
  196. }
  197. //
  198. // Cleanup all of the fields of the IrpContext.
  199. // Restore the thread context pointer if associated with this IrpContext.
  200. //
  201. if (!SaveContext && FlagOn( IrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL )) {
  202. NtfsRestoreTopLevelIrp();
  203. ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL );
  204. }
  205. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DONT_DELETE );
  206. NtfsCleanupIrpContext( IrpContext, FALSE );
  207. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  208. //
  209. // If we are aborting a transaction, then it is important to clear out the
  210. // Usn reasons, so we do not try to write a Usn Journal record for
  211. // somthing that did not happen! Worse yet if we get a log file full
  212. // we fail the abort, which is not allowed.
  213. //
  214. // First, reset the bits in the Fcb, so we will not fail to allow posting
  215. // and writing these bits later. Note that all the reversible changes are
  216. // done with the Fcb exclusive, and they are actually backed out anyway.
  217. // All the nonreversible ones (only unnamed and named data overwrite) are
  218. // forced out first anyway before the data is actually modified.
  219. //
  220. ThisUsn = &IrpContext->Usn;
  221. do {
  222. ASSERT( !FlagOn( ThisUsn->UsnFcbFlags, USN_FCB_FLAG_NEW_REASON ));
  223. if (ThisUsn->NextUsnFcb == NULL) { break; }
  224. LastUsn = ThisUsn;
  225. ThisUsn = ThisUsn->NextUsnFcb;
  226. } while (TRUE);
  227. #endif
  228. IrpContext->OriginatingIrp = Irp;
  229. //
  230. // Note that close.c uses a trick where the "Irp" is really
  231. // a file object.
  232. //
  233. if (ARGUMENT_PRESENT( Irp )) {
  234. if (Irp->Type == IO_TYPE_IRP) {
  235. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  236. //
  237. // We need to lock the user's buffer, unless this is an MDL-read,
  238. // in which case there is no user buffer.
  239. //
  240. // **** we need a better test than non-MDL (read or write)!
  241. if ((IrpContext->MajorFunction == IRP_MJ_READ) ||
  242. (IrpContext->MajorFunction == IRP_MJ_WRITE)) {
  243. ClearFlag( IrpContext->MinorFunction, IRP_MN_DPC );
  244. //
  245. // Lock the user's buffer if this is not an Mdl request.
  246. //
  247. if (!FlagOn( IrpContext->MinorFunction, IRP_MN_MDL )) {
  248. NtfsLockUserBuffer( IrpContext,
  249. Irp,
  250. (IrpContext->MajorFunction == IRP_MJ_READ) ?
  251. IoWriteAccess : IoReadAccess,
  252. IrpSp->Parameters.Write.Length );
  253. }
  254. //
  255. // We also need to check whether this is a query directory operation.
  256. //
  257. } else if (IrpContext->MajorFunction == IRP_MJ_DIRECTORY_CONTROL
  258. && IrpContext->MinorFunction == IRP_MN_QUERY_DIRECTORY) {
  259. NtfsLockUserBuffer( IrpContext,
  260. Irp,
  261. IoWriteAccess,
  262. IrpSp->Parameters.QueryDirectory.Length );
  263. //
  264. // These two FSCTLs use neither I/O, so check for them.
  265. //
  266. } else if ((IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
  267. (IrpContext->MinorFunction == IRP_MN_USER_FS_REQUEST) &&
  268. ((IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_READ_USN_JOURNAL) ||
  269. (IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_GET_RETRIEVAL_POINTERS))) {
  270. NtfsLockUserBuffer( IrpContext,
  271. Irp,
  272. IoWriteAccess,
  273. IrpSp->Parameters.FileSystemControl.OutputBufferLength );
  274. }
  275. //
  276. // Mark that we've already returned pending to the user
  277. //
  278. if (PendIrp) {
  279. IoMarkIrpPending( Irp );
  280. }
  281. }
  282. }
  283. return;
  284. }
  285. NTSTATUS
  286. NtfsPostRequest (
  287. IN PIRP_CONTEXT IrpContext,
  288. IN PIRP Irp OPTIONAL
  289. )
  290. /*++
  291. Routine Description:
  292. This routine enqueues the request packet specified by IrpContext to the
  293. work queue associated with the FileSystemDeviceObject. This is a FSD
  294. routine.
  295. Arguments:
  296. IrpContext - Pointer to the IrpContext to be queued to the Fsp
  297. Irp - I/O Request Packet (or FileObject in special close path)
  298. Return Value:
  299. STATUS_PENDING
  300. --*/
  301. {
  302. //
  303. // Before posting, free any Scb snapshots. Note that if someone
  304. // is calling this routine directly to post, then he better not
  305. // have changed any disk structures, and thus we should have no
  306. // work to do. On the other hand, if someone raised a status
  307. // (like STATUS_CANT_WAIT), then we do both a transaction abort
  308. // and restore of these Scb values.
  309. //
  310. NtfsPrePostIrp( IrpContext, Irp );
  311. NtfsAddToWorkque( IrpContext, Irp );
  312. //
  313. // And return to our caller
  314. //
  315. return STATUS_PENDING;
  316. }
  317. VOID
  318. NtfsCancelOverflowRequest (
  319. IN PDEVICE_OBJECT Device,
  320. IN PIRP Irp
  321. )
  322. /*++
  323. Routine Description:
  324. This routine may be called by the I/O system to cancel an outstanding
  325. Irp in the overflow queue. If its an irp that must be processed we move the irp to the
  326. top of the queue o.w we cancel it direclty. The dequeuing code guarantees the cancel routine is removed before
  327. the irpcontext is dequeued. It also won't dequeue an irp that is marked with a 1 in the info
  328. field. Note we are guarranteed by io subsys that
  329. the irp will remain for the lifetime of this call even after we drop the spinlock
  330. Arguments:
  331. DeviceObject - DeviceObject from I/O system
  332. Irp - Supplies the pointer to the Irp being canceled.
  333. Return Value:
  334. None
  335. --*/
  336. {
  337. PIRP_CONTEXT IrpContext;
  338. PVOLUME_DEVICE_OBJECT Vdo;
  339. KIRQL SavedIrql;
  340. PIO_STACK_LOCATION IrpSp;
  341. BOOLEAN Cancel;
  342. IrpContext = (PIRP_CONTEXT)Irp->IoStatus.Information;
  343. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  344. Cancel = (IrpContext->MajorFunction != IRP_MJ_CLEANUP) &&
  345. (IrpContext->MajorFunction != IRP_MJ_CLOSE);
  346. ASSERT( Cancel );
  347. ASSERT( IrpContext->NodeTypeCode == NTFS_NTC_IRP_CONTEXT );
  348. Vdo = CONTAINING_RECORD( Device,
  349. VOLUME_DEVICE_OBJECT,
  350. DeviceObject );
  351. IoReleaseCancelSpinLock( Irp->CancelIrql );
  352. //
  353. // Gain the critical workqueue spinlock and
  354. // either cancel it or move it to the head of the list
  355. // Note the workqueue code always tests the cancel first before working which
  356. // is what synchronizes this
  357. //
  358. ExAcquireSpinLock( &Vdo->OverflowQueueSpinLock, &SavedIrql );
  359. RemoveEntryList( &IrpContext->WorkQueueItem.List );
  360. //
  361. // Reset the shared fields
  362. //
  363. InitializeListHead( &IrpContext->RecentlyDeallocatedQueue );
  364. InitializeListHead( &IrpContext->ExclusiveFcbList );
  365. if (!Cancel) {
  366. RtlZeroMemory( &IrpContext->WorkQueueItem, sizeof( WORK_QUEUE_ITEM ));
  367. InsertHeadList( &Vdo->OverflowQueue, &IrpContext->WorkQueueItem.List );
  368. Irp->Cancel = 0;
  369. } else {
  370. Vdo->OverflowQueueCount -= 1;
  371. }
  372. ExReleaseSpinLock( &Vdo->OverflowQueueSpinLock, SavedIrql );
  373. if (Cancel) {
  374. if (Vdo->OverflowQueueCount < OVERFLOW_QUEUE_LIMIT) {
  375. KeSetEvent( &Vdo->OverflowQueueEvent, IO_NO_INCREMENT, FALSE );
  376. }
  377. NtfsCompleteRequest( IrpContext, Irp, STATUS_CANCELLED );
  378. }
  379. }
  380. VOID
  381. NtfsAddToWorkque (
  382. IN PIRP_CONTEXT IrpContext,
  383. IN PIRP Irp OPTIONAL
  384. )
  385. {
  386. NtfsAddToWorkqueInternal( IrpContext, Irp, TRUE );
  387. }
  388. //
  389. // Local support routine.
  390. //
  391. VOID
  392. NtfsAddToWorkqueInternal (
  393. IN PIRP_CONTEXT IrpContext,
  394. IN PIRP Irp OPTIONAL,
  395. IN BOOLEAN CanBlock
  396. )
  397. /*++
  398. Routine Description:
  399. This routine is called to acually store the posted Irp to the Fsp
  400. workque.
  401. Arguments:
  402. IrpContext - Pointer to the IrpContext to be queued to the Fsp
  403. Irp - I/O Request Packet.
  404. Return Value:
  405. None.
  406. --*/
  407. {
  408. PIO_STACK_LOCATION IrpSp;
  409. NTSTATUS Status = STATUS_SUCCESS;
  410. KIRQL Irql;
  411. Irql = KeGetCurrentIrql();
  412. if (ARGUMENT_PRESENT( Irp )) {
  413. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  414. //
  415. // Check if this request has an associated file object, and thus volume
  416. // device object.
  417. //
  418. if ( IrpSp->FileObject != NULL ) {
  419. KIRQL SavedIrql;
  420. PVOLUME_DEVICE_OBJECT Vdo;
  421. Vdo = CONTAINING_RECORD( IrpSp->DeviceObject,
  422. VOLUME_DEVICE_OBJECT,
  423. DeviceObject );
  424. //
  425. // Check to see if this request should be sent to the overflow
  426. // queue. If not, then send it off to an exworker thread. Block here
  427. // for non deferred write threads when the overflow queue is full and
  428. // we're not in a dpc (hotfix from async completion routine)
  429. //
  430. if ((Vdo->OverflowQueueCount >= OVERFLOW_QUEUE_LIMIT) &&
  431. CanBlock &&
  432. !FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED_WRITE ) &&
  433. (Irql < DISPATCH_LEVEL)) {
  434. KeWaitForSingleObject( &Vdo->OverflowQueueEvent, Executive, KernelMode, FALSE, NULL );
  435. }
  436. ExAcquireSpinLock( &Vdo->OverflowQueueSpinLock, &SavedIrql );
  437. if ( Vdo->PostedRequestCount > FSP_PER_DEVICE_THRESHOLD) {
  438. //
  439. // We cannot currently respond to this IRP so we'll just enqueue it
  440. // to the overflow queue on the volume.
  441. //
  442. if (NtfsSetCancelRoutine( Irp, NtfsCancelOverflowRequest, (ULONG_PTR)IrpContext, TRUE )) {
  443. if (Status == STATUS_SUCCESS) {
  444. ASSERT( IsListEmpty( &IrpContext->ExclusiveFcbList ) );
  445. ASSERT( IsListEmpty( &IrpContext->RecentlyDeallocatedQueue ) );
  446. RtlZeroMemory( &IrpContext->WorkQueueItem, sizeof( WORK_QUEUE_ITEM ));
  447. InsertTailList( &Vdo->OverflowQueue, &IrpContext->WorkQueueItem.List );
  448. Vdo->OverflowQueueCount += 1;
  449. }
  450. } else {
  451. Status = STATUS_CANCELLED;
  452. }
  453. ExReleaseSpinLock( &Vdo->OverflowQueueSpinLock, SavedIrql );
  454. if (Status != STATUS_SUCCESS) {
  455. if (Vdo->OverflowQueueCount < OVERFLOW_QUEUE_LIMIT) {
  456. KeSetEvent( &Vdo->OverflowQueueEvent, IO_NO_INCREMENT, FALSE );
  457. }
  458. NtfsCompleteRequest( IrpContext, Irp, Status );
  459. }
  460. return;
  461. } else {
  462. //
  463. // We are going to send this Irp to an ex worker thread so up
  464. // the count.
  465. //
  466. if (Vdo->OverflowQueueCount < OVERFLOW_QUEUE_LIMIT) {
  467. KeSetEvent( &Vdo->OverflowQueueEvent, IO_NO_INCREMENT, FALSE );
  468. }
  469. Vdo->PostedRequestCount += 1;
  470. ExReleaseSpinLock( &Vdo->OverflowQueueSpinLock, SavedIrql );
  471. }
  472. }
  473. }
  474. //
  475. // Send it off.....
  476. //
  477. ASSERT( IsListEmpty( &IrpContext->ExclusiveFcbList ) );
  478. ASSERT( IsListEmpty( &IrpContext->RecentlyDeallocatedQueue ) );
  479. RtlZeroMemory( &IrpContext->WorkQueueItem, sizeof( WORK_QUEUE_ITEM ));
  480. ExInitializeWorkItem( &IrpContext->WorkQueueItem,
  481. NtfsFspDispatch,
  482. (PVOID)IrpContext );
  483. ExQueueWorkItem( &IrpContext->WorkQueueItem, CriticalWorkQueue );
  484. return;
  485. }