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.

548 lines
14 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. NtfsOplockComplete (
  23. IN PVOID Context,
  24. IN PIRP Irp
  25. )
  26. /*++
  27. Routine Description:
  28. This routine is called by the oplock package when an oplock break has
  29. completed, allowing an Irp to resume execution. If the status in
  30. the Irp is STATUS_SUCCESS, then we queue the Irp to the Fsp queue.
  31. Otherwise we complete the Irp with the status in the Irp.
  32. Arguments:
  33. Context - Pointer to the IrpContext to be queued to the Fsp
  34. Irp - I/O Request Packet.
  35. Return Value:
  36. None.
  37. --*/
  38. {
  39. NTSTATUS Status = Irp->IoStatus.Status;
  40. PIRP_CONTEXT IrpContext = (PIRP_CONTEXT) Context;
  41. PAGED_CODE();
  42. //
  43. // Check on the return value in the Irp.
  44. //
  45. if (Status == STATUS_SUCCESS) {
  46. //
  47. // Insert the Irp context in the workqueue.
  48. //
  49. NtfsAddToWorkque( IrpContext, Irp );
  50. //
  51. // If this is create and we have a completion event then
  52. // we just want to signal the event and clean up the IrpContext.
  53. // There is someone waiting who will clean up the Irp.
  54. //
  55. } else if ((IrpContext->MajorFunction == IRP_MJ_CREATE) &&
  56. (IrpContext->Union.OplockCleanup != NULL) &&
  57. (IrpContext->Union.OplockCleanup->CompletionContext != NULL)) {
  58. KeInitializeEvent( &IrpContext->Union.OplockCleanup->CompletionContext->Event,
  59. NotificationEvent,
  60. TRUE );
  61. ASSERT( Status != STATUS_PENDING && Status != STATUS_REPARSE );
  62. NtfsCompleteRequest( IrpContext, NULL, Status );
  63. //
  64. // Otherwise complete the Irp and cleanup the IrpContext.
  65. //
  66. } else {
  67. ASSERT( Status != STATUS_PENDING && Status != STATUS_REPARSE );
  68. NtfsCompleteRequest( IrpContext, Irp, Status );
  69. }
  70. return;
  71. }
  72. VOID
  73. NtfsPrePostIrp (
  74. IN PVOID Context,
  75. IN PIRP Irp OPTIONAL
  76. )
  77. /*++
  78. Routine Description:
  79. This routine performs any neccessary work before STATUS_PENDING is
  80. returned with the Fsd thread. This routine is called within the
  81. filesystem and by the oplock package.
  82. Arguments:
  83. Context - Pointer to the IrpContext to be queued to the Fsp
  84. Irp - I/O Request Packet (or FileObject in special close path)
  85. Return Value:
  86. None.
  87. --*/
  88. {
  89. PIRP_CONTEXT IrpContext;
  90. PIO_STACK_LOCATION IrpSp = NULL;
  91. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  92. PUSN_FCB ThisUsn, LastUsn;
  93. #endif
  94. IrpContext = (PIRP_CONTEXT) Context;
  95. //
  96. // Make this is a valid allocated IrpContext. It's ok for
  97. // this to be allocated on the caller's stack as long as the
  98. // caller's not doing this operation asynchronously.
  99. //
  100. ASSERT_IRP_CONTEXT( IrpContext );
  101. ASSERT((FlagOn( IrpContext->State, IRP_CONTEXT_STATE_ALLOC_FROM_POOL )) ||
  102. (IrpContext->NodeTypeCode == NTFS_NTC_IRP_CONTEXT));
  103. //
  104. // Make sure if we are posting the request, which may be
  105. // because of log file full, that we free any Fcbs or PagingIo
  106. // resources which were acquired.
  107. //
  108. //
  109. // Just in case we somehow get here with a transaction ID, clear
  110. // it here so we do not loop forever.
  111. //
  112. if (IrpContext->TransactionId != 0) {
  113. NtfsCleanupFailedTransaction( IrpContext );
  114. }
  115. //
  116. // Cleanup all of the fields of the IrpContext.
  117. // Restore the thread context pointer if associated with this IrpContext.
  118. //
  119. if (FlagOn( IrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL )) {
  120. NtfsRestoreTopLevelIrp();
  121. ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL );
  122. }
  123. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DONT_DELETE );
  124. NtfsCleanupIrpContext( IrpContext, FALSE );
  125. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  126. //
  127. // If we are aborting a transaction, then it is important to clear out the
  128. // Usn reasons, so we do not try to write a Usn Journal record for
  129. // somthing that did not happen! Worse yet if we get a log file full
  130. // we fail the abort, which is not allowed.
  131. //
  132. // First, reset the bits in the Fcb, so we will not fail to allow posting
  133. // and writing these bits later. Note that all the reversible changes are
  134. // done with the Fcb exclusive, and they are actually backed out anyway.
  135. // All the nonreversible ones (only unnamed and named data overwrite) are
  136. // forced out first anyway before the data is actually modified.
  137. //
  138. ThisUsn = &IrpContext->Usn;
  139. do {
  140. ASSERT( !FlagOn( ThisUsn->UsnFcbFlags, USN_FCB_FLAG_NEW_REASON ));
  141. if (ThisUsn->NextUsnFcb == NULL) { break; }
  142. LastUsn = ThisUsn;
  143. ThisUsn = ThisUsn->NextUsnFcb;
  144. } while (TRUE);
  145. #endif
  146. IrpContext->OriginatingIrp = Irp;
  147. //
  148. // Note that close.c uses a trick where the "Irp" is really
  149. // a file object.
  150. //
  151. if (ARGUMENT_PRESENT( Irp )) {
  152. if (Irp->Type == IO_TYPE_IRP) {
  153. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  154. //
  155. // We need to lock the user's buffer, unless this is an MDL-read,
  156. // in which case there is no user buffer.
  157. //
  158. // **** we need a better test than non-MDL (read or write)!
  159. if (IrpContext->MajorFunction == IRP_MJ_READ
  160. || IrpContext->MajorFunction == IRP_MJ_WRITE) {
  161. ClearFlag(IrpContext->MinorFunction, IRP_MN_DPC);
  162. //
  163. // Lock the user's buffer if this is not an Mdl request.
  164. //
  165. if (!FlagOn( IrpContext->MinorFunction, IRP_MN_MDL )) {
  166. NtfsLockUserBuffer( IrpContext,
  167. Irp,
  168. (IrpContext->MajorFunction == IRP_MJ_READ) ?
  169. IoWriteAccess : IoReadAccess,
  170. IrpSp->Parameters.Write.Length );
  171. }
  172. //
  173. // We also need to check whether this is a query directory operation.
  174. //
  175. } else if (IrpContext->MajorFunction == IRP_MJ_DIRECTORY_CONTROL
  176. && IrpContext->MinorFunction == IRP_MN_QUERY_DIRECTORY) {
  177. NtfsLockUserBuffer( IrpContext,
  178. Irp,
  179. IoWriteAccess,
  180. IrpSp->Parameters.QueryDirectory.Length );
  181. //
  182. // These two FSCTLs use neither I/O, so check for them.
  183. //
  184. } else if ((IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
  185. (IrpContext->MinorFunction == IRP_MN_USER_FS_REQUEST) &&
  186. ((IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_READ_USN_JOURNAL) ||
  187. (IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_GET_RETRIEVAL_POINTERS))) {
  188. NtfsLockUserBuffer( IrpContext,
  189. Irp,
  190. IoWriteAccess,
  191. IrpSp->Parameters.FileSystemControl.OutputBufferLength );
  192. }
  193. //
  194. // Mark that we've already returned pending to the user
  195. //
  196. IoMarkIrpPending( Irp );
  197. }
  198. }
  199. return;
  200. }
  201. NTSTATUS
  202. NtfsPostRequest(
  203. IN PIRP_CONTEXT IrpContext,
  204. IN PIRP Irp OPTIONAL
  205. )
  206. /*++
  207. Routine Description:
  208. This routine enqueues the request packet specified by IrpContext to the
  209. work queue associated with the FileSystemDeviceObject. This is a FSD
  210. routine.
  211. Arguments:
  212. IrpContext - Pointer to the IrpContext to be queued to the Fsp
  213. Irp - I/O Request Packet (or FileObject in special close path)
  214. Return Value:
  215. STATUS_PENDING
  216. --*/
  217. {
  218. //
  219. // Before posting, free any Scb snapshots. Note that if someone
  220. // is calling this routine directly to post, then he better not
  221. // have changed any disk structures, and thus we should have no
  222. // work to do. On the other hand, if someone raised a status
  223. // (like STATUS_CANT_WAIT), then we do both a transaction abort
  224. // and restore of these Scb values.
  225. //
  226. NtfsPrePostIrp( IrpContext, Irp );
  227. NtfsAddToWorkque( IrpContext, Irp );
  228. //
  229. // And return to our caller
  230. //
  231. return STATUS_PENDING;
  232. }
  233. VOID
  234. NtfsCancelOverflowRequest (
  235. IN PDEVICE_OBJECT Device,
  236. IN PIRP Irp
  237. )
  238. /*++
  239. Routine Description:
  240. This routine may be called by the I/O system to cancel an outstanding
  241. Irp in the overflow queue. If its an irp that must be processed we move the irp to the
  242. top of the queue o.w we cancel it direclty. The dequeuing code guarantees the cancel routine is removed before
  243. the irpcontext is dequeued. It also won't dequeue an irp that is marked with a 1 in the info
  244. field. Note we are guarranteed by io subsys that
  245. the irp will remain for the lifetime of this call even after we drop the spinlock
  246. Arguments:
  247. DeviceObject - DeviceObject from I/O system
  248. Irp - Supplies the pointer to the Irp being canceled.
  249. Return Value:
  250. None
  251. --*/
  252. {
  253. PIRP_CONTEXT IrpContext;
  254. PVOLUME_DEVICE_OBJECT Vdo;
  255. KIRQL SavedIrql;
  256. PIO_STACK_LOCATION IrpSp;
  257. BOOLEAN Cancel;
  258. IrpContext = (PIRP_CONTEXT)Irp->IoStatus.Information;
  259. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  260. Cancel = (IrpContext->MajorFunction != IRP_MJ_CLEANUP) &&
  261. (IrpContext->MajorFunction != IRP_MJ_CLOSE);
  262. ASSERT( Cancel );
  263. ASSERT( IrpContext->NodeTypeCode == NTFS_NTC_IRP_CONTEXT );
  264. Vdo = CONTAINING_RECORD( Device,
  265. VOLUME_DEVICE_OBJECT,
  266. DeviceObject );
  267. IoReleaseCancelSpinLock( Irp->CancelIrql );
  268. //
  269. // Gain the critical workqueue spinlock and
  270. // either cancel it or move it to the head of the list
  271. // Note the workqueue code always tests the cancel first before working which
  272. // is what synchronizes this
  273. //
  274. ExAcquireSpinLock( &Vdo->OverflowQueueSpinLock, &SavedIrql );
  275. RemoveEntryList( &IrpContext->WorkQueueItem.List );
  276. if (!Cancel) {
  277. InsertHeadList( &Vdo->OverflowQueue, &IrpContext->WorkQueueItem.List );
  278. Irp->Cancel = 0;
  279. } else {
  280. Vdo->OverflowQueueCount -= 1;
  281. }
  282. ExReleaseSpinLock( &Vdo->OverflowQueueSpinLock, SavedIrql );
  283. if (Cancel) {
  284. if (Vdo->OverflowQueueCount < OVERFLOW_QUEUE_LIMIT) {
  285. KeSetEvent( &Vdo->OverflowQueueEvent, IO_NO_INCREMENT, FALSE );
  286. }
  287. NtfsCompleteRequest( IrpContext, Irp, STATUS_CANCELLED );
  288. }
  289. }
  290. //
  291. // Local support routine.
  292. //
  293. VOID
  294. NtfsAddToWorkque (
  295. IN PIRP_CONTEXT IrpContext,
  296. IN PIRP Irp OPTIONAL
  297. )
  298. /*++
  299. Routine Description:
  300. This routine is called to acually store the posted Irp to the Fsp
  301. workque.
  302. Arguments:
  303. IrpContext - Pointer to the IrpContext to be queued to the Fsp
  304. Irp - I/O Request Packet.
  305. Return Value:
  306. None.
  307. --*/
  308. {
  309. PIO_STACK_LOCATION IrpSp;
  310. NTSTATUS Status = STATUS_SUCCESS;
  311. if (ARGUMENT_PRESENT( Irp )) {
  312. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  313. //
  314. // Check if this request has an associated file object, and thus volume
  315. // device object.
  316. //
  317. if ( IrpSp->FileObject != NULL ) {
  318. KIRQL SavedIrql;
  319. PVOLUME_DEVICE_OBJECT Vdo;
  320. Vdo = CONTAINING_RECORD( IrpSp->DeviceObject,
  321. VOLUME_DEVICE_OBJECT,
  322. DeviceObject );
  323. //
  324. // Check to see if this request should be sent to the overflow
  325. // queue. If not, then send it off to an exworker thread. Block here
  326. // for non deferred write threads when the overflow queue is full
  327. //
  328. if ((Vdo->OverflowQueueCount >= OVERFLOW_QUEUE_LIMIT) &&
  329. !FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED_WRITE )) {
  330. KeWaitForSingleObject( &Vdo->OverflowQueueEvent, Executive, KernelMode, FALSE, NULL );
  331. }
  332. ExAcquireSpinLock( &Vdo->OverflowQueueSpinLock, &SavedIrql );
  333. if ( Vdo->PostedRequestCount > FSP_PER_DEVICE_THRESHOLD) {
  334. //
  335. // We cannot currently respond to this IRP so we'll just enqueue it
  336. // to the overflow queue on the volume.
  337. //
  338. if (NtfsSetCancelRoutine( Irp, NtfsCancelOverflowRequest, (ULONG_PTR)IrpContext, TRUE )) {
  339. if (Status == STATUS_SUCCESS) {
  340. InsertTailList( &Vdo->OverflowQueue,
  341. &IrpContext->WorkQueueItem.List );
  342. Vdo->OverflowQueueCount += 1;
  343. }
  344. } else {
  345. Status = STATUS_CANCELLED;
  346. }
  347. ExReleaseSpinLock( &Vdo->OverflowQueueSpinLock, SavedIrql );
  348. if (Status != STATUS_SUCCESS) {
  349. if (Vdo->OverflowQueueCount < OVERFLOW_QUEUE_LIMIT) {
  350. KeSetEvent( &Vdo->OverflowQueueEvent, IO_NO_INCREMENT, FALSE );
  351. }
  352. NtfsCompleteRequest( IrpContext, Irp, Status );
  353. }
  354. return;
  355. } else {
  356. //
  357. // We are going to send this Irp to an ex worker thread so up
  358. // the count.
  359. //
  360. if (Vdo->OverflowQueueCount < OVERFLOW_QUEUE_LIMIT) {
  361. KeSetEvent( &Vdo->OverflowQueueEvent, IO_NO_INCREMENT, FALSE );
  362. }
  363. Vdo->PostedRequestCount += 1;
  364. ExReleaseSpinLock( &Vdo->OverflowQueueSpinLock, SavedIrql );
  365. }
  366. }
  367. }
  368. //
  369. // Send it off.....
  370. //
  371. ExInitializeWorkItem( &IrpContext->WorkQueueItem,
  372. NtfsFspDispatch,
  373. (PVOID)IrpContext );
  374. ExQueueWorkItem( &IrpContext->WorkQueueItem, CriticalWorkQueue );
  375. return;
  376. }