cachedio.c 72 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431
  1. /*++
  2. Copyright (c) 2014 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. cachedio.c
  5. Abstract:
  6. This module implements I/O routines for cacheable I/O objects.
  7. Author:
  8. Chris Stevens - 12-Mar-2014
  9. Environment:
  10. Kernel
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include <minoca/kernel/kernel.h>
  16. #include "iop.h"
  17. #include "pagecach.h"
  18. //
  19. // ---------------------------------------------------------------- Definitions
  20. //
  21. //
  22. // ------------------------------------------------------ Data Type Definitions
  23. //
  24. /*++
  25. Structure Description:
  26. This structure defines the context needed to iterate over write operations
  27. to the page cache. It is supplied to cache hit and miss routines.
  28. Members:
  29. FileSize - Stores the size of the file being operated on.
  30. FileOffset - Store the current page-aligned offset into the file where the
  31. write is to be performed.
  32. BytesRemaining - Stores the number of bytes remaining to write.
  33. BytesCompleted - Stores the number of bytes already written.
  34. SourceOffset - Stores the current offset into the source buffer where the
  35. data should be copied from for the write.
  36. SourceBuffer - Stores a pointer to the source data for the write operation.
  37. CacheBuffer - Stores a pointer to the cache buffer to be used for the flush
  38. on synchronized writes.
  39. CacheBufferOffset - Stores the file offset that the cache buffer begins at.
  40. This may not be the file offset of the write if the first few pages
  41. were page cache entry hits.
  42. BytesThisRound - Stores the number of bytes to be written during the
  43. current round of I/O.
  44. PageByteOffset - Stores the offset into a page where the write is to be
  45. performed. The file offset plus the page byte offset gets the exact
  46. byte offset.
  47. IoFlags - Stores the I/O flags from the request. See IO_FLAG_* definitions.
  48. --*/
  49. typedef struct _IO_WRITE_CONTEXT {
  50. ULONGLONG FileSize;
  51. IO_OFFSET FileOffset;
  52. UINTN BytesRemaining;
  53. UINTN BytesCompleted;
  54. UINTN SourceOffset;
  55. PIO_BUFFER SourceBuffer;
  56. PIO_BUFFER CacheBuffer;
  57. IO_OFFSET CacheBufferOffset;
  58. ULONG BytesThisRound;
  59. ULONG PageByteOffset;
  60. ULONG IoFlags;
  61. } IO_WRITE_CONTEXT, *PIO_WRITE_CONTEXT;
  62. //
  63. // ----------------------------------------------- Internal Function Prototypes
  64. //
  65. KSTATUS
  66. IopPerformCachedRead (
  67. PFILE_OBJECT FileObject,
  68. PIO_CONTEXT IoContext,
  69. PBOOL LockHeldExclusive
  70. );
  71. KSTATUS
  72. IopPerformCachedWrite (
  73. PFILE_OBJECT FileObject,
  74. PIO_CONTEXT IoContext
  75. );
  76. KSTATUS
  77. IopHandleCacheWriteMiss (
  78. PFILE_OBJECT FileObject,
  79. PIO_WRITE_CONTEXT WriteContext,
  80. ULONG TimeoutInMilliseconds
  81. );
  82. KSTATUS
  83. IopHandleCacheWriteHit (
  84. PPAGE_CACHE_ENTRY PageCacheEntry,
  85. PIO_WRITE_CONTEXT WriteContext
  86. );
  87. KSTATUS
  88. IopHandleCacheReadMiss (
  89. PFILE_OBJECT FileObject,
  90. PIO_CONTEXT IoContext
  91. );
  92. KSTATUS
  93. IopPerformCachedIoBufferWrite (
  94. PFILE_OBJECT FileObject,
  95. PIO_CONTEXT IoContext,
  96. BOOL WriteOutNow
  97. );
  98. KSTATUS
  99. IopPerformDefaultNonCachedRead (
  100. PFILE_OBJECT FileObject,
  101. PIO_CONTEXT IoContext,
  102. PVOID DeviceContext
  103. );
  104. KSTATUS
  105. IopPerformDefaultNonCachedWrite (
  106. PFILE_OBJECT FileObject,
  107. PIO_CONTEXT IoContext,
  108. PVOID DeviceContext
  109. );
  110. KSTATUS
  111. IopPerformDefaultPartialWrite (
  112. PFILE_OBJECT FileObject,
  113. PIO_CONTEXT IoContext,
  114. PVOID DeviceContext,
  115. UINTN IoBufferOffset
  116. );
  117. //
  118. // -------------------------------------------------------------------- Globals
  119. //
  120. //
  121. // ------------------------------------------------------------------ Functions
  122. //
  123. KSTATUS
  124. IopPerformCacheableIoOperation (
  125. PIO_HANDLE Handle,
  126. PIO_CONTEXT IoContext
  127. )
  128. /*++
  129. Routine Description:
  130. This routine reads from or writes to the given handle. The I/O object type
  131. of the given handle must be cacheable.
  132. Arguments:
  133. Handle - Supplies a pointer to the I/O handle.
  134. IoContext - Supplies a pointer to the I/O context.
  135. Return Value:
  136. Status code. A failing status code does not necessarily mean no I/O made it
  137. in or out. Check the bytes completed value in the I/O context to find out
  138. how much occurred.
  139. --*/
  140. {
  141. PFILE_OBJECT FileObject;
  142. UINTN FlushCount;
  143. BOOL LockHeldExclusive;
  144. IO_OFFSET OriginalOffset;
  145. ULONG PageShift;
  146. IO_OFFSET StartOffset;
  147. KSTATUS Status;
  148. FILE_OBJECT_TIME_TYPE TimeType;
  149. BOOL TimidTrim;
  150. FileObject = Handle->FileObject;
  151. ASSERT(IoContext->IoBuffer != NULL);
  152. ASSERT((IoContext->Flags & IO_FLAG_NO_ALLOCATE) == 0);
  153. ASSERT(IO_IS_CACHEABLE_TYPE(FileObject->Properties.Type) != FALSE);
  154. OriginalOffset = IoContext->Offset;
  155. StartOffset = OriginalOffset;
  156. //
  157. // Assuming this call is going to generate more pages, ask this thread to
  158. // do some trimming if things are too big. If this is the file system
  159. // doing writes, then file-level file object locks might already be held,
  160. // so give up easily when trying to acquire file object locks during
  161. // trimming.
  162. //
  163. TimidTrim = FALSE;
  164. if ((IoContext->Flags & IO_FLAG_FS_DATA) != 0) {
  165. TimidTrim = TRUE;
  166. }
  167. IopTrimPageCache(TimidTrim);
  168. //
  169. // If this is a write operation, then acquire the file object's lock
  170. // exclusively and perform the cached write.
  171. //
  172. if (IoContext->Write != FALSE) {
  173. //
  174. // It's important to prevent runaway writers from making things
  175. // overwhelmingly dirty.
  176. // 1) If it's a write to a block device, make it synchronized. This
  177. // covers the case of the file system writing tons of zeros to catch
  178. // up to a far offset.
  179. // 2) Otherwise if the FS flags are set, let the write go through
  180. // unimpeded.
  181. // 3) Otherwise go clean some entries.
  182. //
  183. if (IopIsPageCacheTooDirty() != FALSE) {
  184. if (FileObject->Properties.Type == IoObjectBlockDevice) {
  185. IoContext->Flags |= IO_FLAG_DATA_SYNCHRONIZED;
  186. } else if ((IoContext->Flags & IO_FLAG_FS_DATA) == 0) {
  187. PageShift = MmPageShift();
  188. FlushCount = PAGE_CACHE_DIRTY_PENANCE_PAGES;
  189. if ((IoContext->SizeInBytes >> PageShift) >= FlushCount) {
  190. FlushCount = (IoContext->SizeInBytes >> PageShift) + 1;
  191. }
  192. Status = IopFlushFileObjects(0, 0, &FlushCount);
  193. if (!KSUCCESS(Status)) {
  194. return Status;
  195. }
  196. }
  197. }
  198. KeAcquireSharedExclusiveLockExclusive(FileObject->Lock);
  199. LockHeldExclusive = TRUE;
  200. if (OriginalOffset == IO_OFFSET_NONE) {
  201. IoContext->Offset =
  202. RtlAtomicOr64((PULONGLONG)&(Handle->CurrentOffset), 0);
  203. StartOffset = IoContext->Offset;
  204. }
  205. //
  206. // In append mode, set the offset to the end of the file.
  207. //
  208. if ((Handle->OpenFlags & OPEN_FLAG_APPEND) != 0) {
  209. READ_INT64_SYNC(&(FileObject->Properties.FileSize),
  210. &(IoContext->Offset));
  211. }
  212. if (IO_IS_FILE_OBJECT_CACHEABLE(FileObject) != FALSE) {
  213. Status = IopPerformCachedWrite(FileObject, IoContext);
  214. } else {
  215. Status = IopPerformNonCachedWrite(FileObject,
  216. IoContext,
  217. Handle->DeviceContext);
  218. }
  219. TimeType = FileObjectModifiedTime;
  220. //
  221. // Read operations acquire the file object's lock in shared mode and then
  222. // perform the cached read.
  223. //
  224. } else {
  225. KeAcquireSharedExclusiveLockShared(FileObject->Lock);
  226. if (OriginalOffset == IO_OFFSET_NONE) {
  227. IoContext->Offset =
  228. RtlAtomicOr64((PULONGLONG)&(Handle->CurrentOffset), 0);
  229. StartOffset = IoContext->Offset;
  230. }
  231. LockHeldExclusive = FALSE;
  232. if (IO_IS_FILE_OBJECT_CACHEABLE(FileObject) != FALSE) {
  233. Status = IopPerformCachedRead(FileObject,
  234. IoContext,
  235. &LockHeldExclusive);
  236. } else {
  237. Status = IopPerformNonCachedRead(FileObject,
  238. IoContext,
  239. Handle->DeviceContext);
  240. }
  241. TimeType = FileObjectAccessTime;
  242. }
  243. //
  244. // If no offset was provided, update the current offset.
  245. //
  246. if (OriginalOffset == IO_OFFSET_NONE) {
  247. RtlAtomicExchange64((PULONGLONG)&(Handle->CurrentOffset),
  248. StartOffset + IoContext->BytesCompleted);
  249. }
  250. //
  251. // Update the access and modified times if some bytes were read or written.
  252. //
  253. if (IoContext->BytesCompleted != 0) {
  254. if ((TimeType == FileObjectModifiedTime) ||
  255. ((Handle->OpenFlags & OPEN_FLAG_NO_ACCESS_TIME) == 0)) {
  256. if (LockHeldExclusive == FALSE) {
  257. KeSharedExclusiveLockConvertToExclusive(FileObject->Lock);
  258. LockHeldExclusive = TRUE;
  259. }
  260. IopUpdateFileObjectTime(FileObject, TimeType);
  261. }
  262. }
  263. if (LockHeldExclusive != FALSE) {
  264. KeReleaseSharedExclusiveLockExclusive(FileObject->Lock);
  265. } else {
  266. KeReleaseSharedExclusiveLockShared(FileObject->Lock);
  267. }
  268. return Status;
  269. }
  270. KSTATUS
  271. IopPerformNonCachedRead (
  272. PFILE_OBJECT FileObject,
  273. PIO_CONTEXT IoContext,
  274. PVOID DeviceContext
  275. )
  276. /*++
  277. Routine Description:
  278. This routine performs a non-cached read from a cacheable file object. It is
  279. assumed that the file lock is held.
  280. Arguments:
  281. FileObject - Supplies a pointer to a cacheable file object.
  282. IoContext - Supplies a pointer to the I/O context.
  283. DeviceContext - Supplies a pointer to the device context to use when
  284. writing to the backing device.
  285. Return Value:
  286. Status code.
  287. --*/
  288. {
  289. IO_OBJECT_TYPE IoObjectType;
  290. KSTATUS Status;
  291. IoObjectType = FileObject->Properties.Type;
  292. ASSERT(IoContext->Write == FALSE);
  293. ASSERT(IO_IS_CACHEABLE_TYPE(IoObjectType) != FALSE);
  294. switch (IoObjectType) {
  295. case IoObjectSharedMemoryObject:
  296. Status = IopPerformSharedMemoryIoOperation(FileObject, IoContext);
  297. break;
  298. default:
  299. Status = IopPerformDefaultNonCachedRead(FileObject,
  300. IoContext,
  301. DeviceContext);
  302. break;
  303. }
  304. return Status;
  305. }
  306. KSTATUS
  307. IopPerformNonCachedWrite (
  308. PFILE_OBJECT FileObject,
  309. PIO_CONTEXT IoContext,
  310. PVOID DeviceContext
  311. )
  312. /*++
  313. Routine Description:
  314. This routine performs a non-cached write to a cacheable file object. It is
  315. assumed that the file lock is held. This routine will always modify the
  316. file size in the the file properties and conditionally modify the file size
  317. in the file object.
  318. Arguments:
  319. FileObject - Supplies a pointer to the file object for the device or file.
  320. IoContext - Supplies a pointer to the I/O context.
  321. DeviceContext - Supplies a pointer to the device context to use when
  322. writing to the backing device.
  323. Return Value:
  324. Status code.
  325. --*/
  326. {
  327. IO_OBJECT_TYPE IoObjectType;
  328. KSTATUS Status;
  329. ASSERT(IoContext->Write != FALSE);
  330. IoObjectType = FileObject->Properties.Type;
  331. if (IO_IS_CACHEABLE_TYPE(IoObjectType) == FALSE) {
  332. ASSERT(IO_IS_CACHEABLE_TYPE(IoObjectType) != FALSE);
  333. return STATUS_NOT_SUPPORTED;
  334. }
  335. switch (IoObjectType) {
  336. case IoObjectSharedMemoryObject:
  337. Status = IopPerformSharedMemoryIoOperation(FileObject, IoContext);
  338. break;
  339. default:
  340. Status = IopPerformDefaultNonCachedWrite(FileObject,
  341. IoContext,
  342. DeviceContext);
  343. break;
  344. }
  345. return Status;
  346. }
  347. //
  348. // --------------------------------------------------------- Internal Functions
  349. //
  350. KSTATUS
  351. IopPerformCachedRead (
  352. PFILE_OBJECT FileObject,
  353. PIO_CONTEXT IoContext,
  354. PBOOL LockHeldExclusive
  355. )
  356. /*++
  357. Routine Description:
  358. This routine performs reads from the page cache. If any of the reads miss
  359. the cache, then they are read into the cache. Only cacheable objects are
  360. supported by this routine.
  361. Arguments:
  362. FileObject - Supplies a pointer to the file object for the device or file.
  363. IoContext - Supplies a pointer to the I/O context.
  364. LockHeldExclusive - Supplies a pointer indicating whether the file object
  365. lock is held shared (FALSE) or exclusive (TRUE). This routine may
  366. convert a shared acquire into an exclusive one if new entries need to
  367. be inserted into the page cache.
  368. Return Value:
  369. Status code.
  370. --*/
  371. {
  372. UINTN BytesRemaining;
  373. UINTN BytesThisRound;
  374. BOOL CacheMiss;
  375. IO_OFFSET CacheMissOffset;
  376. IO_OFFSET CurrentOffset;
  377. ULONG DestinationByteOffset;
  378. PIO_BUFFER DestinationIoBuffer;
  379. ULONGLONG FileSize;
  380. IO_CONTEXT MissContext;
  381. UINTN MissSize;
  382. PIO_BUFFER PageAlignedIoBuffer;
  383. IO_OFFSET PageAlignedOffset;
  384. UINTN PageAlignedSize;
  385. PPAGE_CACHE_ENTRY PageCacheEntry;
  386. ULONG PageSize;
  387. IO_OFFSET ReadEnd;
  388. UINTN SizeInBytes;
  389. KSTATUS Status;
  390. UINTN TotalBytesRead;
  391. ASSERT(IoContext->IoBuffer != NULL);
  392. ASSERT(IoContext->SizeInBytes != 0);
  393. ASSERT(IO_IS_FILE_OBJECT_CACHEABLE(FileObject) != FALSE);
  394. ASSERT(KeIsSharedExclusiveLockHeldShared(FileObject->Lock) != FALSE);
  395. IoContext->BytesCompleted = 0;
  396. DestinationIoBuffer = IoContext->IoBuffer;
  397. PageAlignedIoBuffer = NULL;
  398. PageCacheEntry = NULL;
  399. PageSize = MmPageSize();
  400. SizeInBytes = IoContext->SizeInBytes;
  401. Status = STATUS_SUCCESS;
  402. TotalBytesRead = 0;
  403. //
  404. // Do not read past the end of the file.
  405. //
  406. READ_INT64_SYNC(&(FileObject->Properties.FileSize), &FileSize);
  407. if (IoContext->Offset >= FileSize) {
  408. Status = STATUS_END_OF_FILE;
  409. goto PerformCachedReadEnd;
  410. }
  411. //
  412. // Truncate the size if the read end is beyond the file. Don't let the size
  413. // wrap.
  414. //
  415. ReadEnd = IoContext->Offset + SizeInBytes;
  416. if (ReadEnd < IoContext->Offset) {
  417. Status = STATUS_INVALID_PARAMETER;
  418. goto PerformCachedReadEnd;
  419. }
  420. if (ReadEnd > FileSize) {
  421. SizeInBytes = FileSize - IoContext->Offset;
  422. }
  423. //
  424. // If this left no bytes to read, exit.
  425. //
  426. if (SizeInBytes == 0) {
  427. Status = STATUS_END_OF_FILE;
  428. goto PerformCachedReadEnd;
  429. }
  430. //
  431. // Page-align the offset and size. Note that the size does not get aligned
  432. // up to a page, just down.
  433. //
  434. PageAlignedOffset = ALIGN_RANGE_DOWN(IoContext->Offset, PageSize);
  435. DestinationByteOffset = REMAINDER(IoContext->Offset, PageSize);
  436. PageAlignedSize = SizeInBytes + DestinationByteOffset;
  437. //
  438. // Validate the page-aligned I/O buffer, which is currently NULL. If the
  439. // I/O request is page aligned in offset and size, then there is a chance
  440. // the I/O buffer could be used directly. Do not use the truncated size
  441. // here, as full page requests at the end of a file can also use the buffer
  442. // directly.
  443. //
  444. if ((DestinationByteOffset == 0) &&
  445. (IS_ALIGNED(IoContext->SizeInBytes, PageSize) != FALSE)) {
  446. PageAlignedIoBuffer = DestinationIoBuffer;
  447. }
  448. Status = MmValidateIoBufferForCachedIo(&PageAlignedIoBuffer,
  449. PageAlignedSize,
  450. PageSize);
  451. if (!KSUCCESS(Status)) {
  452. goto PerformCachedReadEnd;
  453. }
  454. //
  455. // Iterate over each page, searching for page cache entries or creating
  456. // new page cache entries if there is a cache miss. Batch any missed reads
  457. // to limit the calls to the file system.
  458. //
  459. CacheMiss = FALSE;
  460. CurrentOffset = PageAlignedOffset;
  461. CacheMissOffset = CurrentOffset;
  462. BytesRemaining = PageAlignedSize;
  463. while (BytesRemaining != 0) {
  464. if (BytesRemaining < PageSize) {
  465. BytesThisRound = BytesRemaining;
  466. } else {
  467. BytesThisRound = PageSize;
  468. }
  469. //
  470. // First lookup the page in the page cache. If it is found, great. Fill
  471. // it in the buffer.
  472. //
  473. ASSERT(IS_ALIGNED(CurrentOffset, PageSize) != FALSE);
  474. PageCacheEntry = IopLookupPageCacheEntry(FileObject, CurrentOffset);
  475. if (PageCacheEntry != NULL) {
  476. //
  477. // Now read in the missed data and add it into the page-aligned
  478. // buffer.
  479. //
  480. if (CacheMiss != FALSE) {
  481. ASSERT(CurrentOffset < FileSize);
  482. ASSERT((CurrentOffset - CacheMissOffset) <= MAX_UINTN);
  483. MissContext.IoBuffer = PageAlignedIoBuffer;
  484. MissContext.Offset = CacheMissOffset;
  485. MissSize = (UINTN)(CurrentOffset - CacheMissOffset);
  486. MissContext.SizeInBytes = MissSize;
  487. MissContext.Flags = IoContext->Flags;
  488. MissContext.TimeoutInMilliseconds =
  489. IoContext->TimeoutInMilliseconds;
  490. MissContext.Write = FALSE;
  491. Status = IopHandleCacheReadMiss(FileObject, &MissContext);
  492. //
  493. // This should not fail due to end of file because the cache
  494. // was hit after this missed region.
  495. //
  496. ASSERT(Status != STATUS_END_OF_FILE);
  497. if (!KSUCCESS(Status)) {
  498. goto PerformCachedReadEnd;
  499. }
  500. //
  501. // Again, assert that all the expected bytes were read.
  502. //
  503. ASSERT(MissContext.BytesCompleted ==
  504. (CurrentOffset - CacheMissOffset));
  505. TotalBytesRead += MissContext.BytesCompleted;
  506. CacheMiss = FALSE;
  507. }
  508. //
  509. // Add the found page to the buffer. This needs to happen after
  510. // the missed reads are satisfied because the buffer needs to be
  511. // filled in sequentially.
  512. //
  513. MmIoBufferAppendPage(PageAlignedIoBuffer,
  514. PageCacheEntry,
  515. NULL,
  516. INVALID_PHYSICAL_ADDRESS);
  517. IoPageCacheEntryReleaseReference(PageCacheEntry);
  518. PageCacheEntry = NULL;
  519. TotalBytesRead += BytesThisRound;
  520. //
  521. // If there was no page cache entry and this is a new cache miss, then
  522. // mark the start of the miss.
  523. //
  524. } else if (CacheMiss == FALSE) {
  525. CacheMiss = TRUE;
  526. //
  527. // Cache misses are going to modify the page cache tree, so
  528. // the lock needs to be held exclusive.
  529. //
  530. if (*LockHeldExclusive == FALSE) {
  531. KeSharedExclusiveLockConvertToExclusive(FileObject->Lock);
  532. *LockHeldExclusive = TRUE;
  533. }
  534. CacheMissOffset = CurrentOffset;
  535. }
  536. CurrentOffset += BytesThisRound;
  537. BytesRemaining -= BytesThisRound;
  538. }
  539. //
  540. // Handle any final cache read misses.
  541. //
  542. if (CacheMiss != FALSE) {
  543. ASSERT(CurrentOffset <= FileSize);
  544. ASSERT(CurrentOffset == (IoContext->Offset + SizeInBytes));
  545. MissContext.IoBuffer = PageAlignedIoBuffer;
  546. MissContext.Offset = CacheMissOffset;
  547. MissSize = (UINTN)(CurrentOffset - CacheMissOffset);
  548. MissContext.SizeInBytes = MissSize;
  549. MissContext.Flags = IoContext->Flags;
  550. MissContext.TimeoutInMilliseconds = IoContext->TimeoutInMilliseconds;
  551. MissContext.Write = FALSE;
  552. Status = IopHandleCacheReadMiss(FileObject, &MissContext);
  553. ASSERT(Status != STATUS_END_OF_FILE);
  554. if (!KSUCCESS(Status)) {
  555. goto PerformCachedReadEnd;
  556. }
  557. ASSERT(MissContext.BytesCompleted == (CurrentOffset - CacheMissOffset));
  558. TotalBytesRead += MissContext.BytesCompleted;
  559. }
  560. //
  561. // If the destination buffer was not directly filled with page cache
  562. // entries, copy the data read from the cache into it.
  563. //
  564. if (DestinationIoBuffer != PageAlignedIoBuffer) {
  565. TotalBytesRead -= DestinationByteOffset;
  566. ASSERT(TotalBytesRead == SizeInBytes);
  567. Status = MmCopyIoBuffer(DestinationIoBuffer,
  568. 0,
  569. PageAlignedIoBuffer,
  570. DestinationByteOffset,
  571. TotalBytesRead);
  572. if (!KSUCCESS(Status)) {
  573. goto PerformCachedReadEnd;
  574. }
  575. }
  576. PerformCachedReadEnd:
  577. //
  578. // If the routine was not successful and did not read directly into the
  579. // destination buffer, then none of the requested work was done.
  580. //
  581. if (!KSUCCESS(Status) && (DestinationIoBuffer != PageAlignedIoBuffer)) {
  582. TotalBytesRead = 0;
  583. }
  584. if (PageCacheEntry != NULL) {
  585. IoPageCacheEntryReleaseReference(PageCacheEntry);
  586. }
  587. if ((PageAlignedIoBuffer != NULL) &&
  588. (PageAlignedIoBuffer != DestinationIoBuffer)) {
  589. MmFreeIoBuffer(PageAlignedIoBuffer);
  590. PageAlignedIoBuffer = NULL;
  591. }
  592. ASSERT(TotalBytesRead <= SizeInBytes);
  593. IoContext->BytesCompleted = TotalBytesRead;
  594. return Status;
  595. }
  596. KSTATUS
  597. IopPerformCachedWrite (
  598. PFILE_OBJECT FileObject,
  599. PIO_CONTEXT IoContext
  600. )
  601. /*++
  602. Routine Description:
  603. This routine performs writes to the page cache. If any of the writes miss
  604. the cache and it is a complete page of write, a page cache entry is
  605. created. If a cache miss is not for a complete page's worth of writes, a
  606. read is performed to cache the page and then this writes into the cache.
  607. Only cacheable file objects are supported by this routine.
  608. Arguments:
  609. FileObject - Supplies a pointer to the file object for the device or file.
  610. IoContext - Supplies a pointer to the I/O context.
  611. Return Value:
  612. Status code.
  613. --*/
  614. {
  615. UINTN AdjustedSize;
  616. BOOL CacheBacked;
  617. IO_CONTEXT CacheIoContext;
  618. IO_OFFSET EndOffset;
  619. ULONGLONG FileSize;
  620. UINTN FullPageSize;
  621. ULONGLONG NewFileSize;
  622. IO_OFFSET PageAlignedOffset;
  623. ULONG PageByteOffset;
  624. PPAGE_CACHE_ENTRY PageCacheEntry;
  625. ULONG PageSize;
  626. UINTN SizeInBytes;
  627. KSTATUS Status;
  628. IO_WRITE_CONTEXT WriteContext;
  629. BOOL WriteOutNow;
  630. ASSERT(IoContext->IoBuffer != NULL);
  631. ASSERT(IO_IS_FILE_OBJECT_CACHEABLE(FileObject) != FALSE);
  632. ASSERT(KeIsSharedExclusiveLockHeldExclusive(FileObject->Lock) != FALSE);
  633. //
  634. // If the metadata flag is set, the data flag better be set as well.
  635. //
  636. ASSERT((IoContext->Flags &
  637. (IO_FLAG_DATA_SYNCHRONIZED | IO_FLAG_METADATA_SYNCHRONIZED)) !=
  638. IO_FLAG_METADATA_SYNCHRONIZED);
  639. ASSERT((IoContext->Flags & (IO_FLAG_FS_DATA | IO_FLAG_FS_METADATA)) !=
  640. IO_FLAG_FS_METADATA);
  641. IoContext->BytesCompleted = 0;
  642. WriteContext.BytesCompleted = 0;
  643. WriteContext.CacheBuffer = NULL;
  644. WriteContext.IoFlags = IoContext->Flags;
  645. PageByteOffset = 0;
  646. PageSize = MmPageSize();
  647. SizeInBytes = IoContext->SizeInBytes;
  648. WriteOutNow = FALSE;
  649. if ((IoContext->Flags & IO_FLAG_DATA_SYNCHRONIZED) != 0) {
  650. WriteOutNow = TRUE;
  651. }
  652. //
  653. // Do not allow the system to write beyond the end of block devices.
  654. //
  655. if (FileObject->Properties.Type == IoObjectBlockDevice) {
  656. EndOffset = IoContext->Offset + SizeInBytes;
  657. READ_INT64_SYNC(&(FileObject->Properties.FileSize), &FileSize);
  658. if (EndOffset > FileSize) {
  659. SizeInBytes = FileSize - IoContext->Offset;
  660. if (SizeInBytes == 0) {
  661. ASSERT(FALSE);
  662. return STATUS_OUT_OF_BOUNDS;
  663. }
  664. }
  665. }
  666. //
  667. // If the the offset is page-aligned, check to see if the buffer is backed
  668. // by the page cache. If so, pass this on to a short-cut routine that just
  669. // marks page cache entries dirty. Otherwise, just set the size and page
  670. // byte offset as appropriate.
  671. //
  672. PageAlignedOffset = ALIGN_RANGE_DOWN(IoContext->Offset, PageSize);
  673. if (PageAlignedOffset == IoContext->Offset) {
  674. //
  675. // A buffer is page cache backed if the first fragment has a valid page
  676. // cache entry for the current file object and at the current offset.
  677. //
  678. CacheBacked = IopIsIoBufferPageCacheBacked(FileObject,
  679. IoContext->IoBuffer,
  680. IoContext->Offset,
  681. SizeInBytes);
  682. if (CacheBacked != FALSE) {
  683. Status = IopPerformCachedIoBufferWrite(FileObject,
  684. IoContext,
  685. WriteOutNow);
  686. goto PerformCachedWriteEnd;
  687. }
  688. //
  689. // Otherwise, increase the number of bytes that need to be be written
  690. // (aligning down) and set the source byte offset. This does not need to
  691. // align up to a full page.
  692. //
  693. } else {
  694. PageByteOffset = IoContext->Offset - PageAlignedOffset;
  695. ASSERT(PageByteOffset < PageSize);
  696. }
  697. AdjustedSize = SizeInBytes + PageByteOffset;
  698. //
  699. // If this is a synchronized operation, then the "bytes completed" reported
  700. // back to the caller have to be accurate for the disk. As such, this
  701. // routine needs to create a page-aligned cache-backed buffer that will get
  702. // filled in with the cached data along the way. Once everything is cached,
  703. // it can try to flush the data and report back what made it to disk.
  704. //
  705. if ((IoContext->Flags & IO_FLAG_DATA_SYNCHRONIZED) != 0) {
  706. FullPageSize = ALIGN_RANGE_UP(AdjustedSize, PageSize);
  707. WriteContext.CacheBuffer =
  708. MmAllocateUninitializedIoBuffer(FullPageSize, 0);
  709. if (WriteContext.CacheBuffer == NULL) {
  710. Status = STATUS_INSUFFICIENT_RESOURCES;
  711. goto PerformCachedWriteEnd;
  712. }
  713. }
  714. READ_INT64_SYNC(&(FileObject->Properties.FileSize), &FileSize);
  715. //
  716. // Iterate over each page, searching for page cache entries to copy into.
  717. //
  718. WriteContext.FileSize = FileSize;
  719. WriteContext.FileOffset = PageAlignedOffset;
  720. WriteContext.BytesRemaining = SizeInBytes;
  721. WriteContext.SourceOffset = 0;
  722. WriteContext.SourceBuffer = IoContext->IoBuffer;
  723. WriteContext.CacheBufferOffset = WriteContext.FileOffset;
  724. WriteContext.BytesThisRound = 0;
  725. WriteContext.PageByteOffset = PageByteOffset;
  726. while (WriteContext.BytesRemaining != 0) {
  727. //
  728. // Move to the next page if the last page was completed.
  729. //
  730. if (WriteContext.PageByteOffset >= PageSize) {
  731. WriteContext.PageByteOffset = 0;
  732. }
  733. //
  734. // Determine how many bytes to handle this round.
  735. //
  736. WriteContext.BytesThisRound = PageSize - WriteContext.PageByteOffset;
  737. if (WriteContext.BytesThisRound > WriteContext.BytesRemaining) {
  738. WriteContext.BytesThisRound = WriteContext.BytesRemaining;
  739. }
  740. //
  741. // Look for the page in the page cache and if it is found, hand the
  742. // work off to the cache write hit routine.
  743. //
  744. ASSERT(IS_ALIGNED(WriteContext.FileOffset, PageSize) != FALSE);
  745. PageCacheEntry = IopLookupPageCacheEntry(FileObject,
  746. WriteContext.FileOffset);
  747. if (PageCacheEntry != NULL) {
  748. Status = IopHandleCacheWriteHit(PageCacheEntry, &WriteContext);
  749. IoPageCacheEntryReleaseReference(PageCacheEntry);
  750. if (!KSUCCESS(Status)) {
  751. goto PerformCachedWriteEnd;
  752. }
  753. //
  754. // Hits need to update the file size so that future misses don't
  755. // hit the file size and end up zeroing this region.
  756. //
  757. NewFileSize = WriteContext.FileOffset +
  758. WriteContext.PageByteOffset +
  759. WriteContext.BytesThisRound;
  760. if (NewFileSize > FileSize) {
  761. IopUpdateFileObjectFileSize(FileObject, NewFileSize);
  762. }
  763. //
  764. // If no page cache entry was found at this file offset, then handle
  765. // the write miss.
  766. //
  767. } else {
  768. Status = IopHandleCacheWriteMiss(FileObject,
  769. &WriteContext,
  770. IoContext->TimeoutInMilliseconds);
  771. if (!KSUCCESS(Status)) {
  772. goto PerformCachedWriteEnd;
  773. }
  774. if (IO_IS_CACHEABLE_FILE(FileObject->Properties.Type)) {
  775. WriteOutNow = TRUE;
  776. }
  777. }
  778. WriteContext.PageByteOffset += WriteContext.BytesThisRound;
  779. WriteContext.SourceOffset += WriteContext.BytesThisRound;
  780. WriteContext.BytesRemaining -= WriteContext.BytesThisRound;
  781. WriteContext.FileOffset += PageSize;
  782. }
  783. //
  784. // There is still work left to do if this is a synchronized operation. So
  785. // far, everything is in the cache, but not necessarily on disk! The cache
  786. // buffer contains a buffer with all the page-aligned data that is in the
  787. // cache. Flush it out.
  788. //
  789. if (WriteOutNow != FALSE) {
  790. CacheIoContext.IoBuffer = WriteContext.CacheBuffer;
  791. CacheIoContext.Offset = WriteContext.CacheBufferOffset;
  792. CacheIoContext.SizeInBytes =
  793. AdjustedSize -
  794. (WriteContext.CacheBufferOffset - PageAlignedOffset);
  795. ASSERT(MmGetIoBufferSize(CacheIoContext.IoBuffer) >=
  796. CacheIoContext.SizeInBytes);
  797. CacheIoContext.Flags = IoContext->Flags;
  798. CacheIoContext.TimeoutInMilliseconds = IoContext->TimeoutInMilliseconds;
  799. CacheIoContext.Write = TRUE;
  800. Status = IopPerformCachedIoBufferWrite(FileObject,
  801. &CacheIoContext,
  802. WriteOutNow);
  803. CacheIoContext.BytesCompleted +=
  804. WriteContext.CacheBufferOffset - PageAlignedOffset;
  805. if (CacheIoContext.BytesCompleted > WriteContext.BytesCompleted) {
  806. IoContext->BytesCompleted = WriteContext.BytesCompleted;
  807. } else {
  808. IoContext->BytesCompleted = CacheIoContext.BytesCompleted;
  809. }
  810. if (!KSUCCESS(Status)) {
  811. goto PerformCachedWriteEnd;
  812. }
  813. }
  814. Status = STATUS_SUCCESS;
  815. PerformCachedWriteEnd:
  816. //
  817. // On failure, evict any page cache entries that may have been inserted
  818. // above the file size.
  819. //
  820. if (!KSUCCESS(Status)) {
  821. READ_INT64_SYNC(&(FileObject->Properties.FileSize), &FileSize);
  822. IopEvictPageCacheEntries(FileObject,
  823. FileSize,
  824. PAGE_CACHE_EVICTION_FLAG_TRUNCATE);
  825. }
  826. //
  827. // If this is not synchronized I/O and something was written, update the
  828. // file size and notify the page cache that it's dirty.
  829. //
  830. if (WriteOutNow == FALSE) {
  831. if (IoContext->BytesCompleted == 0) {
  832. IoContext->BytesCompleted = WriteContext.BytesCompleted;
  833. }
  834. }
  835. if (WriteContext.CacheBuffer != NULL) {
  836. MmFreeIoBuffer(WriteContext.CacheBuffer);
  837. }
  838. return Status;
  839. }
  840. KSTATUS
  841. IopHandleCacheWriteMiss (
  842. PFILE_OBJECT FileObject,
  843. PIO_WRITE_CONTEXT WriteContext,
  844. ULONG TimeoutInMilliseconds
  845. )
  846. /*++
  847. Routine Description:
  848. This routine handles cache misses when executing a write to the cache. It
  849. handles a few cases. The first is a partial write. In this case it must
  850. first read in the missed data at the page aligned file offset and then
  851. copy the partial page to the cache. The second is a full page miss. In this
  852. case it can just create a new page cache entry with the data provided.
  853. Arguments:
  854. FileObject - Supplies a pointer to the file object that is the target of
  855. the I/O operation.
  856. WriteContext - Supplies a pointer to the I/O context that stores the
  857. current processing information for the write operation. This routine
  858. updates this information if it processes any bytes.
  859. TimeoutInMilliseconds - Supplies the number of milliseconds that the I/O
  860. operation should be waited on before timing out. Use
  861. WAIT_TIME_INDEFINITE to wait forever on the I/O.
  862. Return Value:
  863. Status code.
  864. --*/
  865. {
  866. UINTN CacheBufferSize;
  867. IO_OFFSET FileOffset;
  868. IO_CONTEXT MissContext;
  869. ULONG PageByteOffset;
  870. IO_BUFFER PageCacheBuffer;
  871. BOOL PageCacheBufferInitialized;
  872. PPAGE_CACHE_ENTRY PageCacheEntry;
  873. ULONG PageSize;
  874. PHYSICAL_ADDRESS PhysicalAddress;
  875. PIO_BUFFER ScratchIoBuffer;
  876. PPAGE_CACHE_ENTRY SourceEntry;
  877. KSTATUS Status;
  878. PVOID VirtualAddress;
  879. ULONG ZeroSize;
  880. PageCacheBufferInitialized = FALSE;
  881. PageCacheEntry = NULL;
  882. PageSize = MmPageSize();
  883. ScratchIoBuffer = NULL;
  884. //
  885. // Handle partial page writes. Partial page cache misses need to read in
  886. // the page, create a cache entry and then write the data. The exceptions
  887. // are if this is a page aligned write that goes up to or beyond the end
  888. // of the file or this is a non-aligned write and the entire page is beyond
  889. // the end of the file. Nothing need be read in and those are handled in
  890. // the "else" clause.
  891. //
  892. if (((WriteContext->PageByteOffset != 0) &&
  893. (WriteContext->FileSize > WriteContext->FileOffset)) ||
  894. ((WriteContext->PageByteOffset == 0) &&
  895. (WriteContext->BytesRemaining < PageSize) &&
  896. ((WriteContext->FileOffset + WriteContext->BytesRemaining) <
  897. WriteContext->FileSize))) {
  898. //
  899. // Prepare a one page I/O buffer to collect the missing page cache
  900. // entry from the read.
  901. //
  902. Status = MmInitializeIoBuffer(&PageCacheBuffer,
  903. NULL,
  904. INVALID_PHYSICAL_ADDRESS,
  905. 0,
  906. IO_BUFFER_FLAG_KERNEL_MODE_DATA);
  907. if (!KSUCCESS(Status)) {
  908. goto HandleCacheWriteMissEnd;
  909. }
  910. PageCacheBufferInitialized = TRUE;
  911. //
  912. // Perform the read as if it were a cache miss on read, complete with
  913. // the normal read ahead behavior.
  914. //
  915. MissContext.IoBuffer = &PageCacheBuffer;
  916. MissContext.Offset = WriteContext->FileOffset;
  917. MissContext.SizeInBytes = PageSize;
  918. MissContext.Flags = WriteContext->IoFlags;
  919. MissContext.TimeoutInMilliseconds = TimeoutInMilliseconds;
  920. MissContext.Write = TRUE;
  921. Status = IopHandleCacheReadMiss(FileObject, &MissContext);
  922. if ((!KSUCCESS(Status)) &&
  923. ((Status != STATUS_END_OF_FILE) ||
  924. (MissContext.BytesCompleted == 0))) {
  925. goto HandleCacheWriteMissEnd;
  926. }
  927. ASSERT(MissContext.BytesCompleted == PageSize);
  928. //
  929. // Copy the data to this new page cache entry.
  930. //
  931. Status = MmCopyIoBuffer(&PageCacheBuffer,
  932. WriteContext->PageByteOffset,
  933. WriteContext->SourceBuffer,
  934. WriteContext->SourceOffset,
  935. WriteContext->BytesThisRound);
  936. if (!KSUCCESS(Status)) {
  937. goto HandleCacheWriteMissEnd;
  938. }
  939. //
  940. // This does not take a reference on the page cache entry. The buffer
  941. // holds the reference.
  942. //
  943. PageCacheEntry = MmGetIoBufferPageCacheEntry(&PageCacheBuffer, 0);
  944. ASSERT(PageCacheEntry != NULL);
  945. WriteContext->BytesCompleted += WriteContext->BytesThisRound;
  946. //
  947. // Otherwise this should be a page-aligned cache miss that is either a
  948. // full page write or a write up to or beyond the end of the file. Try to
  949. // write out a new cache entry.
  950. //
  951. } else {
  952. ASSERT(((WriteContext->PageByteOffset != 0) &&
  953. (WriteContext->FileSize <= WriteContext->FileOffset)) ||
  954. ((WriteContext->PageByteOffset == 0) &&
  955. ((WriteContext->BytesRemaining >= PageSize) ||
  956. ((WriteContext->FileOffset + WriteContext->BytesRemaining) >=
  957. WriteContext->FileSize))));
  958. if (IS_ALIGNED(WriteContext->SourceOffset, PageSize) != FALSE) {
  959. SourceEntry = MmGetIoBufferPageCacheEntry(
  960. WriteContext->SourceBuffer,
  961. WriteContext->SourceOffset);
  962. } else {
  963. SourceEntry = NULL;
  964. }
  965. //
  966. // If there is no source page cache entry or the source and destination
  967. // cannot be linked, allocate a new page, copy the supplied data to it
  968. // and then insert it into the cache. Unfortunately, it's not
  969. // guaranteed that the physical page behind the supplied buffer can be
  970. // used. It could be from paged pool, or user mode.
  971. //
  972. if ((SourceEntry == NULL) ||
  973. (IopCanLinkPageCacheEntry(SourceEntry, FileObject) == FALSE)) {
  974. ScratchIoBuffer = MmAllocateNonPagedIoBuffer(0,
  975. MAX_ULONGLONG,
  976. PageSize,
  977. PageSize,
  978. 0);
  979. if (ScratchIoBuffer == NULL) {
  980. Status = STATUS_INSUFFICIENT_RESOURCES;
  981. goto HandleCacheWriteMissEnd;
  982. }
  983. ASSERT(ScratchIoBuffer->FragmentCount == 1);
  984. //
  985. // If this write does not start at the beginning of the page, zero
  986. // the contents before the write.
  987. //
  988. PageByteOffset = WriteContext->PageByteOffset;
  989. if (PageByteOffset != 0) {
  990. MmZeroIoBuffer(ScratchIoBuffer, 0, PageByteOffset);
  991. }
  992. //
  993. // Copy the contents of the source to the new I/O buffer.
  994. //
  995. Status = MmCopyIoBuffer(ScratchIoBuffer,
  996. PageByteOffset,
  997. WriteContext->SourceBuffer,
  998. WriteContext->SourceOffset,
  999. WriteContext->BytesThisRound);
  1000. if (!KSUCCESS(Status)) {
  1001. goto HandleCacheWriteMissEnd;
  1002. }
  1003. PageByteOffset += WriteContext->BytesThisRound;
  1004. //
  1005. // Zero the rest of the scratch buffer if the bytes this round did
  1006. // not fill it. It should already be mapped and only be one
  1007. // fragment long.
  1008. //
  1009. if (PageByteOffset < PageSize) {
  1010. ZeroSize = PageSize - PageByteOffset;
  1011. MmZeroIoBuffer(ScratchIoBuffer, PageByteOffset, ZeroSize);
  1012. }
  1013. SourceEntry = NULL;
  1014. PhysicalAddress = ScratchIoBuffer->Fragment[0].PhysicalAddress;
  1015. VirtualAddress = ScratchIoBuffer->Fragment[0].VirtualAddress;
  1016. } else {
  1017. PhysicalAddress = IoGetPageCacheEntryPhysicalAddress(SourceEntry);
  1018. VirtualAddress = IoGetPageCacheEntryVirtualAddress(SourceEntry);
  1019. }
  1020. ASSERT(KeIsSharedExclusiveLockHeldExclusive(FileObject->Lock) != FALSE);
  1021. FileOffset = WriteContext->FileOffset;
  1022. PageCacheEntry = IopCreateAndInsertPageCacheEntry(FileObject,
  1023. VirtualAddress,
  1024. PhysicalAddress,
  1025. FileOffset,
  1026. SourceEntry);
  1027. if (PageCacheEntry == NULL) {
  1028. Status = STATUS_INSUFFICIENT_RESOURCES;
  1029. goto HandleCacheWriteMissEnd;
  1030. }
  1031. //
  1032. // If the page cache entry was created from a physical page owned
  1033. // by the scratch buffer, connect them.
  1034. //
  1035. if (ScratchIoBuffer != NULL) {
  1036. MmSetIoBufferPageCacheEntry(ScratchIoBuffer, 0, PageCacheEntry);
  1037. }
  1038. WriteContext->BytesCompleted += WriteContext->BytesThisRound;
  1039. }
  1040. IopMarkPageCacheEntryDirty(PageCacheEntry);
  1041. //
  1042. // This page cache entry was created or read, so if it's a cacheable file
  1043. // type, it will need to go down through the file system to ensure
  1044. // there's disk space allocated to it. Create a cache buffer if one
  1045. // has not been created yet.
  1046. //
  1047. if (IO_IS_CACHEABLE_FILE(FileObject->Properties.Type)) {
  1048. if (WriteContext->CacheBuffer == NULL) {
  1049. CacheBufferSize = WriteContext->PageByteOffset +
  1050. WriteContext->BytesRemaining;
  1051. CacheBufferSize = ALIGN_RANGE_UP(CacheBufferSize, PageSize);
  1052. WriteContext->CacheBuffer =
  1053. MmAllocateUninitializedIoBuffer(CacheBufferSize, 0);
  1054. if (WriteContext->CacheBuffer == NULL) {
  1055. Status = STATUS_INSUFFICIENT_RESOURCES;
  1056. goto HandleCacheWriteMissEnd;
  1057. }
  1058. WriteContext->CacheBufferOffset = WriteContext->FileOffset;
  1059. }
  1060. }
  1061. //
  1062. // Back the cache buffer with this page cache entry, since it will
  1063. // be flushed later.
  1064. //
  1065. if (WriteContext->CacheBuffer != NULL) {
  1066. MmIoBufferAppendPage(WriteContext->CacheBuffer,
  1067. PageCacheEntry,
  1068. NULL,
  1069. INVALID_PHYSICAL_ADDRESS);
  1070. }
  1071. HandleCacheWriteMissEnd:
  1072. if (PageCacheBufferInitialized != FALSE) {
  1073. MmFreeIoBuffer(&PageCacheBuffer);
  1074. } else if (PageCacheEntry != NULL) {
  1075. IoPageCacheEntryReleaseReference(PageCacheEntry);
  1076. }
  1077. if (ScratchIoBuffer != NULL) {
  1078. MmFreeIoBuffer(ScratchIoBuffer);
  1079. }
  1080. return Status;
  1081. }
  1082. KSTATUS
  1083. IopHandleCacheWriteHit (
  1084. PPAGE_CACHE_ENTRY PageCacheEntry,
  1085. PIO_WRITE_CONTEXT WriteContext
  1086. )
  1087. /*++
  1088. Routine Description:
  1089. This routine handles cache hits when executing a write to the cache. It
  1090. copies the data from the I/O context's source buffer to the page cache
  1091. entry. If it is a synchornized I/O operation, then it also backs the cached
  1092. buffer in the I/O context with the page cache entry.
  1093. Arguments:
  1094. PageCacheEntry - Supplies a pointer to the page cache entry that was found
  1095. at the current file offset, which is stored in the I/O context.
  1096. WriteContext - Supplies a pointer to the I/O context that stores the
  1097. current processing information for the write operation. This routine
  1098. updates this information if it processes any bytes.
  1099. Return Value:
  1100. Status code.
  1101. --*/
  1102. {
  1103. BOOL Linked;
  1104. ULONG PageSize;
  1105. PPAGE_CACHE_ENTRY SourceEntry;
  1106. KSTATUS Status;
  1107. //
  1108. // If this is a full page aligned write and the source is backed by the
  1109. // page cache, then try to share the source's physical page with the found
  1110. // page cache entry. Only do this if the FS flag is set, as it would be
  1111. // bad to associate regions of a file with an unassociated portion of the
  1112. // disk.
  1113. //
  1114. Linked = FALSE;
  1115. PageSize = MmPageSize();
  1116. if (((WriteContext->IoFlags & IO_FLAG_FS_DATA) != 0) &&
  1117. (WriteContext->PageByteOffset == 0) &&
  1118. (WriteContext->BytesThisRound == PageSize) &&
  1119. (IS_ALIGNED(WriteContext->SourceOffset, PageSize) != 0)) {
  1120. SourceEntry = MmGetIoBufferPageCacheEntry(WriteContext->SourceBuffer,
  1121. WriteContext->SourceOffset);
  1122. if (SourceEntry != NULL) {
  1123. Linked = IopLinkPageCacheEntries(PageCacheEntry, SourceEntry);
  1124. }
  1125. }
  1126. //
  1127. // If the entries were not linked, copy the contents directly into the
  1128. // cache and mark it dirty.
  1129. //
  1130. if (Linked == FALSE) {
  1131. Status = IopCopyIoBufferToPageCacheEntry(PageCacheEntry,
  1132. WriteContext->PageByteOffset,
  1133. WriteContext->SourceBuffer,
  1134. WriteContext->SourceOffset,
  1135. WriteContext->BytesThisRound);
  1136. if (!KSUCCESS(Status)) {
  1137. goto HandleCacheWriteHitEnd;
  1138. }
  1139. } else {
  1140. IopMarkPageCacheEntryDirty(PageCacheEntry);
  1141. }
  1142. WriteContext->BytesCompleted += WriteContext->BytesThisRound;
  1143. //
  1144. // If there's a cache buffer, add this page cache entry to it.
  1145. //
  1146. if (WriteContext->CacheBuffer != NULL) {
  1147. MmIoBufferAppendPage(WriteContext->CacheBuffer,
  1148. PageCacheEntry,
  1149. NULL,
  1150. INVALID_PHYSICAL_ADDRESS);
  1151. }
  1152. Status = STATUS_SUCCESS;
  1153. HandleCacheWriteHitEnd:
  1154. return Status;
  1155. }
  1156. KSTATUS
  1157. IopHandleCacheReadMiss (
  1158. PFILE_OBJECT FileObject,
  1159. PIO_CONTEXT IoContext
  1160. )
  1161. /*++
  1162. Routine Description:
  1163. This routine handles a cache miss. It performs an aligned read on the given
  1164. handle at the miss offset and then caches the read data. It will update the
  1165. given destination I/O buffer with physical pages from the page cache.
  1166. The file object lock must be held exclusive already.
  1167. Arguments:
  1168. FileObject - Supplies a pointer to the file object for the device or file.
  1169. IoContext - Supplies a pointer to the I/O context for the cache miss.
  1170. Return Value:
  1171. Status code.
  1172. --*/
  1173. {
  1174. IO_OFFSET BlockAlignedOffset;
  1175. UINTN BlockAlignedSize;
  1176. ULONG BlockByteOffset;
  1177. ULONG BlockSize;
  1178. UINTN BytesCopied;
  1179. UINTN CopySize;
  1180. ULONGLONG FileSize;
  1181. ULONG PageSize;
  1182. PIO_BUFFER ReadIoBuffer;
  1183. IO_CONTEXT ReadIoContext;
  1184. KSTATUS Status;
  1185. PageSize = MmPageSize();
  1186. IoContext->BytesCompleted = 0;
  1187. ASSERT(KeIsSharedExclusiveLockHeldExclusive(FileObject->Lock) != FALSE);
  1188. ASSERT(IO_IS_CACHEABLE_TYPE(FileObject->Properties.Type));
  1189. ASSERT(IS_ALIGNED(IoContext->Offset, PageSize) != FALSE);
  1190. //
  1191. // Now read in the missed data. Make sure the offset and size are
  1192. // block aligned. The offset is currently only page-aligned and the size
  1193. // could be any amount.
  1194. //
  1195. BlockSize = FileObject->Properties.BlockSize;
  1196. BlockAlignedOffset = ALIGN_RANGE_DOWN(IoContext->Offset, BlockSize);
  1197. BlockAlignedSize = REMAINDER(IoContext->Offset, BlockSize) +
  1198. IoContext->SizeInBytes;
  1199. BlockAlignedSize = ALIGN_RANGE_UP(BlockAlignedSize, BlockSize);
  1200. BlockAlignedSize = ALIGN_RANGE_UP(BlockAlignedSize, PageSize);
  1201. //
  1202. // The block size should be either a power of 2 less than a page size,
  1203. // making this already aligned, or a multiple of a page size. Therefore,
  1204. // the block aligned offset better be page aligned.
  1205. //
  1206. ASSERT(IS_ALIGNED(BlockAlignedOffset, PageSize) != FALSE);
  1207. //
  1208. // If this is a miss for a device, read ahead some amount in anticipation
  1209. // of accessing the next pages of the device in the near future. Don't read
  1210. // ahead if system memory is low.
  1211. //
  1212. if (FileObject->Properties.Type == IoObjectBlockDevice) {
  1213. READ_INT64_SYNC(&(FileObject->Properties.FileSize), &FileSize);
  1214. if (MmGetPhysicalMemoryWarningLevel() == MemoryWarningLevelNone) {
  1215. BlockAlignedSize = ALIGN_RANGE_UP(BlockAlignedSize,
  1216. IO_READ_AHEAD_SIZE);
  1217. ASSERT(IS_ALIGNED(IO_READ_AHEAD_SIZE, PageSize));
  1218. }
  1219. if (((BlockAlignedOffset + BlockAlignedSize) < BlockAlignedOffset) ||
  1220. ((BlockAlignedOffset + BlockAlignedSize) > FileSize)) {
  1221. BlockAlignedSize = FileSize - BlockAlignedOffset;
  1222. BlockAlignedSize = ALIGN_RANGE_UP(BlockAlignedSize, PageSize);
  1223. }
  1224. }
  1225. //
  1226. // Allocate an I/O buffer that is not backed by any pages. The read will
  1227. // either hit a caching layer and fill in the I/O buffer with page cache
  1228. // entries or hit storage, which should validate the I/O buffer before use.
  1229. // Validation will back the I/O buffer with memory.
  1230. //
  1231. ReadIoBuffer = MmAllocateUninitializedIoBuffer(BlockAlignedSize, 0);
  1232. if (ReadIoBuffer == NULL) {
  1233. Status = STATUS_INSUFFICIENT_RESOURCES;
  1234. goto HandleDefaultCacheReadMissEnd;
  1235. }
  1236. //
  1237. // The file object's lock should already be held, if it exists.
  1238. //
  1239. ASSERT(KeIsSharedExclusiveLockHeld(FileObject->Lock) != FALSE);
  1240. //
  1241. // This read needs to happen without re-acquiring the I/O lock. So directly
  1242. // call the non-cached read routine.
  1243. //
  1244. ReadIoContext.IoBuffer = ReadIoBuffer;
  1245. ReadIoContext.Offset = BlockAlignedOffset;
  1246. ReadIoContext.SizeInBytes = BlockAlignedSize;
  1247. ReadIoContext.BytesCompleted = 0;
  1248. ReadIoContext.Flags = IoContext->Flags;
  1249. ReadIoContext.TimeoutInMilliseconds = IoContext->TimeoutInMilliseconds;
  1250. ReadIoContext.Write = FALSE;
  1251. Status = IopPerformNonCachedRead(FileObject, &ReadIoContext, NULL);
  1252. if ((!KSUCCESS(Status)) &&
  1253. ((Status != STATUS_END_OF_FILE) ||
  1254. (ReadIoContext.BytesCompleted == 0))) {
  1255. goto HandleDefaultCacheReadMissEnd;
  1256. }
  1257. //
  1258. // The I/O buffer allocated above is large enough to accomodate the full
  1259. // range of missed data, but the IRP might not have read into the entire
  1260. // buffer. It could have reached the end of the file. So, zero any
  1261. // remaining data in the buffer.
  1262. //
  1263. if (BlockAlignedSize != ReadIoContext.BytesCompleted) {
  1264. Status = MmZeroIoBuffer(
  1265. ReadIoBuffer,
  1266. ReadIoContext.BytesCompleted,
  1267. BlockAlignedSize - ReadIoContext.BytesCompleted);
  1268. if (!KSUCCESS(Status)) {
  1269. goto HandleDefaultCacheReadMissEnd;
  1270. }
  1271. }
  1272. //
  1273. // Cache the entire read I/O buffer and copy the desired portions into the
  1274. // I/O context's buffer.
  1275. //
  1276. BlockByteOffset = REMAINDER(IoContext->Offset, BlockSize);
  1277. CopySize = ALIGN_RANGE_UP(IoContext->SizeInBytes, PageSize);
  1278. Status = IopCopyAndCacheIoBuffer(FileObject,
  1279. BlockAlignedOffset,
  1280. IoContext->IoBuffer,
  1281. CopySize,
  1282. ReadIoBuffer,
  1283. BlockAlignedSize,
  1284. BlockByteOffset,
  1285. &BytesCopied);
  1286. if (!KSUCCESS(Status)) {
  1287. goto HandleDefaultCacheReadMissEnd;
  1288. }
  1289. ASSERT(BytesCopied != 0);
  1290. //
  1291. // Report back the number of bytes copied but never more than the size
  1292. // requested.
  1293. //
  1294. if (BytesCopied < IoContext->SizeInBytes) {
  1295. IoContext->BytesCompleted = BytesCopied;
  1296. } else {
  1297. IoContext->BytesCompleted = IoContext->SizeInBytes;
  1298. }
  1299. HandleDefaultCacheReadMissEnd:
  1300. if (ReadIoBuffer != NULL) {
  1301. MmFreeIoBuffer(ReadIoBuffer);
  1302. }
  1303. return Status;
  1304. }
  1305. KSTATUS
  1306. IopPerformCachedIoBufferWrite (
  1307. PFILE_OBJECT FileObject,
  1308. PIO_CONTEXT IoContext,
  1309. BOOL WriteOutNow
  1310. )
  1311. /*++
  1312. Routine Description:
  1313. This routine performs a write operation on an I/O buffer that is backed by
  1314. page cache entries. This merely consists of marking the page cache entries
  1315. dirty.
  1316. Arguments:
  1317. FileObject - Supplies a pointer to the file object for the device or file.
  1318. IoContext - Supplies a pointer to the I/O context.
  1319. WriteOutNow - Supplies a boolean indicating whether to flush the data out
  1320. synchronously (TRUE) or just mark it dirty in the page cache (FALSE).
  1321. Return Value:
  1322. Status code.
  1323. --*/
  1324. {
  1325. UINTN BufferOffset;
  1326. UINTN BytesRemaining;
  1327. UINTN BytesThisRound;
  1328. IO_OFFSET FileOffset;
  1329. PPAGE_CACHE_ENTRY PageCacheEntry;
  1330. ULONG PageSize;
  1331. KSTATUS Status;
  1332. BufferOffset = 0;
  1333. BytesRemaining = IoContext->SizeInBytes;
  1334. PageSize = MmPageSize();
  1335. while (BytesRemaining != 0) {
  1336. PageCacheEntry = MmGetIoBufferPageCacheEntry(IoContext->IoBuffer,
  1337. BufferOffset);
  1338. BytesThisRound = PageSize;
  1339. if (BytesThisRound > BytesRemaining) {
  1340. BytesThisRound = BytesRemaining;
  1341. }
  1342. //
  1343. // This routine should only be called with a valid page cached backed
  1344. // I/O buffer.
  1345. //
  1346. ASSERT(PageCacheEntry != NULL);
  1347. ASSERT(IopGetPageCacheEntryOffset(PageCacheEntry) ==
  1348. (IoContext->Offset + BufferOffset));
  1349. //
  1350. // If this is a synchronized I/O call, then mark the pages clean, they
  1351. // are about to be flushed. Otherwise mark them dirty.
  1352. //
  1353. if (WriteOutNow != FALSE) {
  1354. IopMarkPageCacheEntryClean(PageCacheEntry, TRUE);
  1355. } else {
  1356. IopMarkPageCacheEntryDirty(PageCacheEntry);
  1357. }
  1358. BufferOffset += BytesThisRound;
  1359. BytesRemaining -= BytesThisRound;
  1360. }
  1361. //
  1362. // If this is a synchronized I/O call, just flush the buffer immediately.
  1363. // Don't hold the lock for block devices.
  1364. //
  1365. if (WriteOutNow != FALSE) {
  1366. if (FileObject->Properties.Type == IoObjectBlockDevice) {
  1367. KeReleaseSharedExclusiveLockExclusive(FileObject->Lock);
  1368. }
  1369. Status = IopPerformNonCachedWrite(FileObject, IoContext, NULL);
  1370. if (FileObject->Properties.Type == IoObjectBlockDevice) {
  1371. KeAcquireSharedExclusiveLockExclusive(FileObject->Lock);
  1372. }
  1373. //
  1374. // If this did not write out all the bytes then some pages may be
  1375. // incorrectly marked clean, others may be beyond the end of the file
  1376. // and there's no disk space for them. Since it's not clear which ones
  1377. // are which, remove all entires at and above the given offset.
  1378. //
  1379. if (IoContext->BytesCompleted < IoContext->SizeInBytes) {
  1380. FileOffset = IoContext->Offset + IoContext->BytesCompleted;
  1381. FileOffset = ALIGN_RANGE_DOWN(FileOffset, PageSize);
  1382. IopEvictPageCacheEntries(FileObject,
  1383. BufferOffset,
  1384. PAGE_CACHE_EVICTION_FLAG_TRUNCATE);
  1385. }
  1386. //
  1387. // Otherwise notify the page cache that something is dirty.
  1388. //
  1389. } else {
  1390. IopSchedulePageCacheThread();
  1391. IoContext->BytesCompleted = IoContext->SizeInBytes;
  1392. Status = STATUS_SUCCESS;
  1393. }
  1394. return Status;
  1395. }
  1396. KSTATUS
  1397. IopPerformDefaultNonCachedRead (
  1398. PFILE_OBJECT FileObject,
  1399. PIO_CONTEXT IoContext,
  1400. PVOID DeviceContext
  1401. )
  1402. /*++
  1403. Routine Description:
  1404. This routine reads from the given file or device handle. It is assumed that
  1405. the file lock is held in shared mode.
  1406. Arguments:
  1407. FileObject - Supplies a pointer to the file object for the device or file.
  1408. IoContext - Supplies a pointer to the I/O context.
  1409. DeviceContext - Supplies a pointer to the device context to use when
  1410. writing to the backing device.
  1411. Return Value:
  1412. Status code. A failing status code does not necessarily mean no I/O made it
  1413. in. Check the bytes completed value to find out how much occurred.
  1414. --*/
  1415. {
  1416. PIO_BUFFER BlockAlignedIoBuffer;
  1417. IO_OFFSET BlockAlignedOffset;
  1418. UINTN BlockAlignedSize;
  1419. ULONG BlockSize;
  1420. UINTN BytesToCopy;
  1421. ULONG DestinationBlockOffset;
  1422. PDEVICE Device;
  1423. IRP_READ_WRITE Parameters;
  1424. UINTN SizeInBytes;
  1425. KSTATUS Status;
  1426. IoContext->BytesCompleted = 0;
  1427. SizeInBytes = IoContext->SizeInBytes;
  1428. ASSERT(IoContext->IoBuffer != NULL);
  1429. ASSERT(FileObject != NULL);
  1430. ASSERT(IO_IS_CACHEABLE_TYPE(FileObject->Properties.Type));
  1431. //
  1432. // This routine assumes the file object's lock is held in shared or
  1433. // exclusive mode.
  1434. //
  1435. ASSERT(KeIsSharedExclusiveLockHeld(FileObject->Lock) != FALSE);
  1436. if (FileObject->Properties.Type == IoObjectBlockDevice) {
  1437. BlockSize = FileObject->Properties.BlockSize;
  1438. } else {
  1439. BlockSize = MmPageSize();
  1440. }
  1441. //
  1442. // Block-align the offset and size.
  1443. //
  1444. BlockAlignedOffset = ALIGN_RANGE_DOWN(IoContext->Offset, BlockSize);
  1445. DestinationBlockOffset = REMAINDER(IoContext->Offset, BlockSize);
  1446. BlockAlignedSize = SizeInBytes + DestinationBlockOffset;
  1447. BlockAlignedSize = ALIGN_RANGE_UP(BlockAlignedSize, BlockSize);
  1448. //
  1449. // If the I/O request is block aligned in offset and size, then use the
  1450. // provided I/O buffer. Otherwise allocate an uninitialized I/O buffer to
  1451. // use for the read. Either a lower caching layer will fill it with page
  1452. // cache pages or the backing storage will validate the I/O buffer before
  1453. // use, causing it to initialize.
  1454. //
  1455. if ((DestinationBlockOffset != 0) ||
  1456. (IS_ALIGNED(SizeInBytes, BlockSize) == FALSE)) {
  1457. BlockAlignedIoBuffer = MmAllocateUninitializedIoBuffer(BlockAlignedSize,
  1458. 0);
  1459. if (BlockAlignedIoBuffer == NULL) {
  1460. Status = STATUS_INSUFFICIENT_RESOURCES;
  1461. goto PerformDefaultNonCachedReadEnd;
  1462. }
  1463. } else {
  1464. BlockAlignedIoBuffer = IoContext->IoBuffer;
  1465. }
  1466. //
  1467. // The aligned buffer is rounded up and down to full blocks. Read all
  1468. // the data from the aligned offset.
  1469. //
  1470. if (DeviceContext != NULL) {
  1471. Parameters.DeviceContext = DeviceContext;
  1472. } else {
  1473. Parameters.DeviceContext = FileObject->DeviceContext;
  1474. }
  1475. Parameters.IoFlags = IoContext->Flags;
  1476. Parameters.TimeoutInMilliseconds = IoContext->TimeoutInMilliseconds;
  1477. Parameters.FileProperties = &(FileObject->Properties);
  1478. Parameters.IoOffset = BlockAlignedOffset;
  1479. Parameters.IoSizeInBytes = BlockAlignedSize;
  1480. Parameters.IoBytesCompleted = 0;
  1481. Parameters.NewIoOffset = Parameters.IoOffset;
  1482. Parameters.IoBuffer = BlockAlignedIoBuffer;
  1483. Device = FileObject->Device;
  1484. ASSERT(IS_DEVICE_OR_VOLUME(Device));
  1485. //
  1486. // Fire off the I/O!
  1487. //
  1488. Status = IopSendIoReadIrp(Device, &Parameters);
  1489. if ((!KSUCCESS(Status)) &&
  1490. ((Status != STATUS_END_OF_FILE) ||
  1491. (Parameters.IoBytesCompleted == 0))) {
  1492. goto PerformDefaultNonCachedReadEnd;
  1493. }
  1494. //
  1495. // If the original I/O buffer was not used for the read, copy the data from
  1496. // the block aligned I/O buffer to the destination I/O buffer, up to the
  1497. // completed number of bytes.
  1498. //
  1499. if (IoContext->IoBuffer != BlockAlignedIoBuffer) {
  1500. BytesToCopy = Parameters.IoBytesCompleted;
  1501. if (BytesToCopy < DestinationBlockOffset) {
  1502. goto PerformDefaultNonCachedReadEnd;
  1503. }
  1504. BytesToCopy -= DestinationBlockOffset;
  1505. if (IoContext->SizeInBytes < BytesToCopy) {
  1506. BytesToCopy = IoContext->SizeInBytes;
  1507. }
  1508. Status = MmCopyIoBuffer(IoContext->IoBuffer,
  1509. 0,
  1510. BlockAlignedIoBuffer,
  1511. DestinationBlockOffset,
  1512. BytesToCopy);
  1513. if (!KSUCCESS(Status)) {
  1514. goto PerformDefaultNonCachedReadEnd;
  1515. }
  1516. IoContext->BytesCompleted = BytesToCopy;
  1517. } else {
  1518. IoContext->BytesCompleted = Parameters.IoBytesCompleted;
  1519. }
  1520. PerformDefaultNonCachedReadEnd:
  1521. if ((BlockAlignedIoBuffer != NULL) &&
  1522. (BlockAlignedIoBuffer != IoContext->IoBuffer)) {
  1523. MmFreeIoBuffer(BlockAlignedIoBuffer);
  1524. }
  1525. return Status;
  1526. }
  1527. KSTATUS
  1528. IopPerformDefaultNonCachedWrite (
  1529. PFILE_OBJECT FileObject,
  1530. PIO_CONTEXT IoContext,
  1531. PVOID DeviceContext
  1532. )
  1533. /*++
  1534. Routine Description:
  1535. This routine writes an I/O buffer to the given file or device. It is
  1536. assumed that the file lock is held. This routine will always modify the
  1537. file size in the the file properties and conditionally modify the file size
  1538. in the file object.
  1539. Arguments:
  1540. FileObject - Supplies a pointer to the file object for the device or file.
  1541. IoContext - Supplies a pointer to the I/O context.
  1542. DeviceContext - Supplies a pointer to the device context to use when
  1543. writing to the backing device.
  1544. Return Value:
  1545. Status code.
  1546. --*/
  1547. {
  1548. PIO_BUFFER AlignedIoBuffer;
  1549. UINTN AlignedIoBufferSize;
  1550. IO_OFFSET AlignedOffset;
  1551. ULONG BlockSize;
  1552. UINTN BytesToWrite;
  1553. PDEVICE Device;
  1554. ULONGLONG FileSize;
  1555. UINTN IoBufferSize;
  1556. IO_OFFSET Offset;
  1557. IRP_READ_WRITE Parameters;
  1558. IO_CONTEXT PartialContext;
  1559. KSTATUS Status;
  1560. ASSERT(IoContext->IoBuffer != NULL);
  1561. ASSERT(IO_IS_CACHEABLE_TYPE(FileObject->Properties.Type));
  1562. ASSERT(MmGetIoBufferSize(IoContext->IoBuffer) >= IoContext->SizeInBytes);
  1563. ASSERT((FileObject->Properties.Type == IoObjectBlockDevice) ||
  1564. (KeIsSharedExclusiveLockHeld(FileObject->Lock) != FALSE));
  1565. if (FileObject->Properties.Type == IoObjectBlockDevice) {
  1566. BlockSize = FileObject->Properties.BlockSize;
  1567. } else {
  1568. BlockSize = MmPageSize();
  1569. }
  1570. IoContext->BytesCompleted = 0;
  1571. Offset = IoContext->Offset;
  1572. Status = STATUS_SUCCESS;
  1573. //
  1574. // A partial write is needed for the first block if the given offset
  1575. // is not block-aligned.
  1576. //
  1577. if (IS_ALIGNED(Offset, BlockSize) == FALSE) {
  1578. BytesToWrite = BlockSize - REMAINDER(Offset, BlockSize);
  1579. if (BytesToWrite > IoContext->SizeInBytes) {
  1580. BytesToWrite = IoContext->SizeInBytes;
  1581. }
  1582. PartialContext.IoBuffer = IoContext->IoBuffer;
  1583. PartialContext.Offset = Offset;
  1584. PartialContext.SizeInBytes = BytesToWrite;
  1585. PartialContext.Flags = IoContext->Flags;
  1586. PartialContext.TimeoutInMilliseconds = IoContext->TimeoutInMilliseconds;
  1587. PartialContext.Write = TRUE;
  1588. Status = IopPerformDefaultPartialWrite(FileObject,
  1589. &PartialContext,
  1590. DeviceContext,
  1591. IoContext->BytesCompleted);
  1592. IoContext->BytesCompleted += PartialContext.BytesCompleted;
  1593. if (!KSUCCESS(Status)) {
  1594. goto PerformDefaultNonCachedWriteEnd;
  1595. }
  1596. Offset += PartialContext.BytesCompleted;
  1597. }
  1598. BytesToWrite = 0;
  1599. if (IoContext->SizeInBytes > IoContext->BytesCompleted) {
  1600. BytesToWrite = IoContext->SizeInBytes - IoContext->BytesCompleted;
  1601. }
  1602. AlignedIoBufferSize = ALIGN_RANGE_DOWN(BytesToWrite, BlockSize);
  1603. //
  1604. // Glom the last partial write onto the full blocks if:
  1605. // 1) There is a partial write at the end, and
  1606. // 2) The write goes beyond the end of the file, and
  1607. // 3) The supplied buffer is big enough to align up the next block.
  1608. //
  1609. if (BytesToWrite > AlignedIoBufferSize) {
  1610. READ_INT64_SYNC(&(FileObject->Properties.FileSize), &FileSize);
  1611. IoBufferSize = MmGetIoBufferSize(IoContext->IoBuffer) -
  1612. IoContext->BytesCompleted;
  1613. if ((IoContext->Offset + BytesToWrite >= FileSize) &&
  1614. (IoBufferSize >= ALIGN_RANGE_UP(BytesToWrite, BlockSize))) {
  1615. AlignedIoBufferSize += BlockSize;
  1616. }
  1617. }
  1618. //
  1619. // With the first partial block handled, write as many full blocks as
  1620. // possible.
  1621. //
  1622. if (AlignedIoBufferSize >= BlockSize) {
  1623. ASSERT(IS_ALIGNED(Offset, BlockSize) != FALSE);
  1624. //
  1625. // Use the supplied buffer directly without validation. It is up to the
  1626. // driver performing the I/O to validate the buffer.
  1627. //
  1628. AlignedOffset = Offset;
  1629. AlignedIoBuffer = IoContext->IoBuffer;
  1630. MmIoBufferIncrementOffset(AlignedIoBuffer, IoContext->BytesCompleted);
  1631. //
  1632. // Write the data out.
  1633. //
  1634. if (DeviceContext != NULL) {
  1635. Parameters.DeviceContext = DeviceContext;
  1636. } else {
  1637. Parameters.DeviceContext = FileObject->DeviceContext;
  1638. }
  1639. Parameters.IoFlags = IoContext->Flags;
  1640. Parameters.TimeoutInMilliseconds = IoContext->TimeoutInMilliseconds;
  1641. Parameters.FileProperties = &(FileObject->Properties);
  1642. Parameters.IoOffset = AlignedOffset;
  1643. Parameters.IoSizeInBytes = AlignedIoBufferSize;
  1644. Parameters.IoBytesCompleted = 0;
  1645. Parameters.NewIoOffset = Parameters.IoOffset;
  1646. Parameters.IoBuffer = AlignedIoBuffer;
  1647. Device = FileObject->Device;
  1648. ASSERT(IS_DEVICE_OR_VOLUME(Device));
  1649. Status = IopSendIoIrp(Device, IrpMinorIoWrite, &Parameters);
  1650. //
  1651. // Roll the I/O buffer's offset back to where it was before this I/O.
  1652. //
  1653. MmIoBufferDecrementOffset(AlignedIoBuffer, IoContext->BytesCompleted);
  1654. //
  1655. // Update the file size if bytes were written.
  1656. //
  1657. if (Parameters.IoBytesCompleted != 0) {
  1658. FileSize = AlignedOffset + Parameters.IoBytesCompleted;
  1659. ASSERT(Parameters.IoBytesCompleted <= AlignedIoBufferSize);
  1660. ASSERT(FileSize == Parameters.NewIoOffset);
  1661. //
  1662. // If the partial block at the end was glommed on to this write,
  1663. // then the file size might need to be adjusted down a little.
  1664. //
  1665. if (FileSize > Offset + BytesToWrite) {
  1666. FileSize = Offset + BytesToWrite;
  1667. Parameters.IoBytesCompleted = BytesToWrite;
  1668. }
  1669. IopUpdateFileObjectFileSize(FileObject, FileSize);
  1670. }
  1671. IoContext->BytesCompleted += Parameters.IoBytesCompleted;
  1672. if (!KSUCCESS(Status)) {
  1673. goto PerformDefaultNonCachedWriteEnd;
  1674. }
  1675. Offset = Parameters.NewIoOffset;
  1676. BytesToWrite = 0;
  1677. if (IoContext->SizeInBytes > IoContext->BytesCompleted) {
  1678. BytesToWrite = IoContext->SizeInBytes - IoContext->BytesCompleted;
  1679. }
  1680. }
  1681. //
  1682. // Always check for a final partial block. Even if a big aligned chunk was
  1683. // written or not. This also gets invoked for initial file writes (i.e.
  1684. // small writes at the begining of a file). Make sure there wasn't any
  1685. // underflow in the subtraction of the bytes written.
  1686. //
  1687. if (BytesToWrite != 0) {
  1688. PartialContext.IoBuffer = IoContext->IoBuffer;
  1689. PartialContext.Offset = Offset;
  1690. PartialContext.SizeInBytes = BytesToWrite;
  1691. PartialContext.Flags = IoContext->Flags;
  1692. PartialContext.TimeoutInMilliseconds = IoContext->TimeoutInMilliseconds;
  1693. PartialContext.Write = TRUE;
  1694. Status = IopPerformDefaultPartialWrite(FileObject,
  1695. &PartialContext,
  1696. DeviceContext,
  1697. IoContext->BytesCompleted);
  1698. IoContext->BytesCompleted += PartialContext.BytesCompleted;
  1699. Offset += PartialContext.BytesCompleted;
  1700. if (!KSUCCESS(Status)) {
  1701. goto PerformDefaultNonCachedWriteEnd;
  1702. }
  1703. }
  1704. ASSERT(Offset > IoContext->Offset);
  1705. READ_INT64_SYNC(&(FileObject->Properties.FileSize), &FileSize);
  1706. ASSERT(FileSize > IoContext->Offset);
  1707. PerformDefaultNonCachedWriteEnd:
  1708. return Status;
  1709. }
  1710. KSTATUS
  1711. IopPerformDefaultPartialWrite (
  1712. PFILE_OBJECT FileObject,
  1713. PIO_CONTEXT IoContext,
  1714. PVOID DeviceContext,
  1715. UINTN IoBufferOffset
  1716. )
  1717. /*++
  1718. Routine Description:
  1719. This routine completes a partial block write for a file or device. This
  1720. routine will update the file size as necessary.
  1721. Arguments:
  1722. FileObject - Supplies a pointer to the file object for the device or file.
  1723. IoContext - Supplies a pointer to the I/O context for the partial write.
  1724. DeviceContext - Supplies the I/O handle device context.
  1725. IoBufferOffset - Supplies the offset into the context's I/O buffer to get
  1726. data from.
  1727. Return Value:
  1728. Status code.
  1729. --*/
  1730. {
  1731. PIO_BUFFER AlignedIoBuffer;
  1732. IO_OFFSET AlignedOffset;
  1733. ULONG BlockSize;
  1734. ULONG ByteOffset;
  1735. PDEVICE Device;
  1736. ULONGLONG FileSize;
  1737. IRP_READ_WRITE Parameters;
  1738. KSTATUS Status;
  1739. ASSERT(IO_IS_CACHEABLE_TYPE(FileObject->Properties.Type));
  1740. //
  1741. // The lock really should be held exclusively, except that the page cache
  1742. // worker may do partial writes with the lock held shared if the disk
  1743. // block size is larger than a page. Since the page cache worker is single
  1744. // threaded and everyone else acquires it exclusive, this is okay.
  1745. //
  1746. ASSERT(KeIsSharedExclusiveLockHeld(FileObject->Lock) != FALSE);
  1747. IoContext->BytesCompleted = 0;
  1748. Device = FileObject->Device;
  1749. ASSERT(IS_DEVICE_OR_VOLUME(Device));
  1750. BlockSize = FileObject->Properties.BlockSize;
  1751. AlignedIoBuffer = MmAllocateUninitializedIoBuffer(BlockSize, 0);
  1752. if (AlignedIoBuffer == NULL) {
  1753. Status = STATUS_INSUFFICIENT_RESOURCES;
  1754. goto PerformDefaultPartialWriteEnd;
  1755. }
  1756. AlignedOffset = ALIGN_RANGE_DOWN(IoContext->Offset, BlockSize);
  1757. //
  1758. // Read in the block. If the read fails for any reason other than EOF, exit.
  1759. //
  1760. if (DeviceContext != NULL) {
  1761. Parameters.DeviceContext = DeviceContext;
  1762. } else {
  1763. Parameters.DeviceContext = FileObject->DeviceContext;
  1764. }
  1765. Parameters.IoFlags = IoContext->Flags;
  1766. Parameters.TimeoutInMilliseconds = IoContext->TimeoutInMilliseconds;
  1767. Parameters.FileProperties = &(FileObject->Properties);
  1768. Parameters.IoOffset = AlignedOffset;
  1769. Parameters.IoSizeInBytes = BlockSize;
  1770. Parameters.IoBytesCompleted = 0;
  1771. Parameters.NewIoOffset = Parameters.IoOffset;
  1772. Parameters.IoBuffer = AlignedIoBuffer;
  1773. Status = IopSendIoReadIrp(Device, &Parameters);
  1774. if ((!KSUCCESS(Status)) &&
  1775. ((Status != STATUS_END_OF_FILE) ||
  1776. (Parameters.IoBytesCompleted == 0))) {
  1777. goto PerformDefaultPartialWriteEnd;
  1778. }
  1779. //
  1780. // Write the partial bytes to the read buffer. If the bytes read did not
  1781. // reach all the way to the partial write offset within this block, then
  1782. // zero out the bytes in between the read and where the write will start.
  1783. //
  1784. ByteOffset = REMAINDER(IoContext->Offset, BlockSize);
  1785. ASSERT((ByteOffset + IoContext->SizeInBytes) <= BlockSize);
  1786. if (Parameters.IoBytesCompleted < ByteOffset) {
  1787. Status = MmZeroIoBuffer(AlignedIoBuffer,
  1788. Parameters.IoBytesCompleted,
  1789. ByteOffset - Parameters.IoBytesCompleted);
  1790. if (!KSUCCESS(Status)) {
  1791. goto PerformDefaultPartialWriteEnd;
  1792. }
  1793. }
  1794. Status = MmCopyIoBuffer(AlignedIoBuffer,
  1795. ByteOffset,
  1796. IoContext->IoBuffer,
  1797. IoBufferOffset,
  1798. IoContext->SizeInBytes);
  1799. if (!KSUCCESS(Status)) {
  1800. goto PerformDefaultPartialWriteEnd;
  1801. }
  1802. //
  1803. // Now write it back, but only up to the requested size.
  1804. //
  1805. Parameters.IoOffset = AlignedOffset;
  1806. Parameters.IoSizeInBytes = ByteOffset + IoContext->SizeInBytes;
  1807. Parameters.IoBytesCompleted = 0;
  1808. Parameters.NewIoOffset = Parameters.IoOffset;
  1809. Parameters.IoBuffer = AlignedIoBuffer;
  1810. Status = IopSendIoIrp(Device, IrpMinorIoWrite, &Parameters);
  1811. //
  1812. // Determine how many of the bytes meant to be written were delivered.
  1813. //
  1814. if (ByteOffset == 0) {
  1815. if (Parameters.IoBytesCompleted < IoContext->SizeInBytes) {
  1816. IoContext->BytesCompleted = Parameters.IoBytesCompleted;
  1817. } else {
  1818. IoContext->BytesCompleted = IoContext->SizeInBytes;
  1819. }
  1820. } else {
  1821. if (Parameters.IoBytesCompleted >= ByteOffset) {
  1822. if (Parameters.IoBytesCompleted >=
  1823. (ByteOffset + IoContext->SizeInBytes)) {
  1824. IoContext->BytesCompleted = IoContext->SizeInBytes;
  1825. } else {
  1826. IoContext->BytesCompleted = Parameters.IoBytesCompleted -
  1827. ByteOffset;
  1828. }
  1829. } else {
  1830. ASSERT(IoContext->BytesCompleted == 0);
  1831. }
  1832. }
  1833. if (IoContext->BytesCompleted != 0) {
  1834. FileSize = IoContext->Offset + IoContext->BytesCompleted;
  1835. IopUpdateFileObjectFileSize(FileObject, FileSize);
  1836. }
  1837. PerformDefaultPartialWriteEnd:
  1838. if (AlignedIoBuffer != NULL) {
  1839. MmFreeIoBuffer(AlignedIoBuffer);
  1840. }
  1841. return Status;
  1842. }