[NTOSKRNL] Reimplement the lazy writer in Cc and remove the "basic" one in Mm.
[reactos.git] / ntoskrnl / cc / copy.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/cc/copy.c
5 * PURPOSE: Implements cache managers copy interface
6 *
7 * PROGRAMMERS:
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS *******************************************************************/
17
18 static PFN_NUMBER CcZeroPage = 0;
19
20 #define MAX_ZERO_LENGTH (256 * 1024)
21
22 typedef enum _CC_COPY_OPERATION
23 {
24 CcOperationRead,
25 CcOperationWrite,
26 CcOperationZero
27 } CC_COPY_OPERATION;
28
29 ULONG CcRosTraceLevel = 0;
30 ULONG CcFastMdlReadWait;
31 ULONG CcFastMdlReadNotPossible;
32 ULONG CcFastReadNotPossible;
33 ULONG CcFastReadWait;
34 ULONG CcFastReadNoWait;
35 ULONG CcFastReadResourceMiss;
36
37 extern KEVENT iLazyWriterNotify;
38
39 /* FUNCTIONS *****************************************************************/
40
41 VOID
42 NTAPI
43 MiZeroPhysicalPage (
44 IN PFN_NUMBER PageFrameIndex
45 );
46
47 VOID
48 NTAPI
49 CcInitCacheZeroPage (
50 VOID)
51 {
52 NTSTATUS Status;
53
54 MI_SET_USAGE(MI_USAGE_CACHE);
55 //MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName);
56 Status = MmRequestPageMemoryConsumer(MC_SYSTEM, TRUE, &CcZeroPage);
57 if (!NT_SUCCESS(Status))
58 {
59 DbgPrint("Can't allocate CcZeroPage.\n");
60 KeBugCheck(CACHE_MANAGER);
61 }
62 MiZeroPhysicalPage(CcZeroPage);
63 }
64
65 NTSTATUS
66 NTAPI
67 CcReadVirtualAddress (
68 PROS_VACB Vacb)
69 {
70 ULONG Size, Pages;
71 PMDL Mdl;
72 NTSTATUS Status;
73 IO_STATUS_BLOCK IoStatus;
74 KEVENT Event;
75
76 Size = (ULONG)(Vacb->SharedCacheMap->SectionSize.QuadPart - Vacb->FileOffset.QuadPart);
77 if (Size > VACB_MAPPING_GRANULARITY)
78 {
79 Size = VACB_MAPPING_GRANULARITY;
80 }
81
82 Pages = BYTES_TO_PAGES(Size);
83 ASSERT(Pages * PAGE_SIZE <= VACB_MAPPING_GRANULARITY);
84
85 Mdl = IoAllocateMdl(Vacb->BaseAddress, Pages * PAGE_SIZE, FALSE, FALSE, NULL);
86 if (!Mdl)
87 {
88 return STATUS_INSUFFICIENT_RESOURCES;
89 }
90
91 Status = STATUS_SUCCESS;
92 _SEH2_TRY
93 {
94 MmProbeAndLockPages(Mdl, KernelMode, IoWriteAccess);
95 }
96 _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
97 {
98 Status = _SEH2_GetExceptionCode();
99 KeBugCheck(CACHE_MANAGER);
100 } _SEH2_END;
101
102 if (NT_SUCCESS(Status))
103 {
104 Mdl->MdlFlags |= MDL_IO_PAGE_READ;
105 KeInitializeEvent(&Event, NotificationEvent, FALSE);
106 Status = IoPageRead(Vacb->SharedCacheMap->FileObject, Mdl, &Vacb->FileOffset, &Event, &IoStatus);
107 if (Status == STATUS_PENDING)
108 {
109 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
110 Status = IoStatus.Status;
111 }
112
113 MmUnlockPages(Mdl);
114 }
115
116 IoFreeMdl(Mdl);
117
118 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
119 {
120 DPRINT1("IoPageRead failed, Status %x\n", Status);
121 return Status;
122 }
123
124 if (Size < VACB_MAPPING_GRANULARITY)
125 {
126 RtlZeroMemory((char*)Vacb->BaseAddress + Size,
127 VACB_MAPPING_GRANULARITY - Size);
128 }
129
130 return STATUS_SUCCESS;
131 }
132
133 NTSTATUS
134 NTAPI
135 CcWriteVirtualAddress (
136 PROS_VACB Vacb)
137 {
138 ULONG Size;
139 PMDL Mdl;
140 NTSTATUS Status;
141 IO_STATUS_BLOCK IoStatus;
142 KEVENT Event;
143
144 Vacb->Dirty = FALSE;
145 Size = (ULONG)(Vacb->SharedCacheMap->SectionSize.QuadPart - Vacb->FileOffset.QuadPart);
146 if (Size > VACB_MAPPING_GRANULARITY)
147 {
148 Size = VACB_MAPPING_GRANULARITY;
149 }
150 //
151 // Nonpaged pool PDEs in ReactOS must actually be synchronized between the
152 // MmGlobalPageDirectory and the real system PDE directory. What a mess...
153 //
154 {
155 ULONG i = 0;
156 do
157 {
158 MmGetPfnForProcess(NULL, (PVOID)((ULONG_PTR)Vacb->BaseAddress + (i << PAGE_SHIFT)));
159 } while (++i < (Size >> PAGE_SHIFT));
160 }
161
162 Mdl = IoAllocateMdl(Vacb->BaseAddress, Size, FALSE, FALSE, NULL);
163 if (!Mdl)
164 {
165 return STATUS_INSUFFICIENT_RESOURCES;
166 }
167
168 Status = STATUS_SUCCESS;
169 _SEH2_TRY
170 {
171 MmProbeAndLockPages(Mdl, KernelMode, IoReadAccess);
172 }
173 _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
174 {
175 Status = _SEH2_GetExceptionCode();
176 KeBugCheck(CACHE_MANAGER);
177 } _SEH2_END;
178
179 if (NT_SUCCESS(Status))
180 {
181 KeInitializeEvent(&Event, NotificationEvent, FALSE);
182 Status = IoSynchronousPageWrite(Vacb->SharedCacheMap->FileObject, Mdl, &Vacb->FileOffset, &Event, &IoStatus);
183 if (Status == STATUS_PENDING)
184 {
185 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
186 Status = IoStatus.Status;
187 }
188
189 MmUnlockPages(Mdl);
190 }
191 IoFreeMdl(Mdl);
192 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
193 {
194 DPRINT1("IoPageWrite failed, Status %x\n", Status);
195 Vacb->Dirty = TRUE;
196 return Status;
197 }
198
199 return STATUS_SUCCESS;
200 }
201
202 NTSTATUS
203 ReadWriteOrZero(
204 _Inout_ PVOID BaseAddress,
205 _Inout_opt_ PVOID Buffer,
206 _In_ ULONG Length,
207 _In_ CC_COPY_OPERATION Operation)
208 {
209 NTSTATUS Status = STATUS_SUCCESS;
210
211 if (Operation == CcOperationZero)
212 {
213 /* Zero */
214 RtlZeroMemory(BaseAddress, Length);
215 }
216 else
217 {
218 _SEH2_TRY
219 {
220 if (Operation == CcOperationWrite)
221 RtlCopyMemory(BaseAddress, Buffer, Length);
222 else
223 RtlCopyMemory(Buffer, BaseAddress, Length);
224 }
225 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
226 {
227 Status = _SEH2_GetExceptionCode();
228 }
229 _SEH2_END;
230 }
231 return Status;
232 }
233
234 BOOLEAN
235 CcCopyData (
236 _In_ PFILE_OBJECT FileObject,
237 _In_ LONGLONG FileOffset,
238 _Inout_ PVOID Buffer,
239 _In_ LONGLONG Length,
240 _In_ CC_COPY_OPERATION Operation,
241 _In_ BOOLEAN Wait,
242 _Out_ PIO_STATUS_BLOCK IoStatus)
243 {
244 NTSTATUS Status;
245 LONGLONG CurrentOffset;
246 ULONG BytesCopied;
247 KIRQL OldIrql;
248 PROS_SHARED_CACHE_MAP SharedCacheMap;
249 PLIST_ENTRY ListEntry;
250 PROS_VACB Vacb;
251 ULONG PartialLength;
252 PVOID BaseAddress;
253 BOOLEAN Valid;
254
255 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
256 CurrentOffset = FileOffset;
257 BytesCopied = 0;
258
259 if (!Wait)
260 {
261 /* test if the requested data is available */
262 KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &OldIrql);
263 /* FIXME: this loop doesn't take into account areas that don't have
264 * a VACB in the list yet */
265 ListEntry = SharedCacheMap->CacheMapVacbListHead.Flink;
266 while (ListEntry != &SharedCacheMap->CacheMapVacbListHead)
267 {
268 Vacb = CONTAINING_RECORD(ListEntry,
269 ROS_VACB,
270 CacheMapVacbListEntry);
271 ListEntry = ListEntry->Flink;
272 if (!Vacb->Valid &&
273 DoRangesIntersect(Vacb->FileOffset.QuadPart,
274 VACB_MAPPING_GRANULARITY,
275 CurrentOffset, Length))
276 {
277 KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, OldIrql);
278 /* data not available */
279 return FALSE;
280 }
281 if (Vacb->FileOffset.QuadPart >= CurrentOffset + Length)
282 break;
283 }
284 KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, OldIrql);
285 }
286
287 PartialLength = CurrentOffset % VACB_MAPPING_GRANULARITY;
288 if (PartialLength != 0)
289 {
290 PartialLength = min(Length, VACB_MAPPING_GRANULARITY - PartialLength);
291 Status = CcRosRequestVacb(SharedCacheMap,
292 ROUND_DOWN(CurrentOffset,
293 VACB_MAPPING_GRANULARITY),
294 &BaseAddress,
295 &Valid,
296 &Vacb);
297 if (!NT_SUCCESS(Status))
298 ExRaiseStatus(Status);
299 if (!Valid)
300 {
301 Status = CcReadVirtualAddress(Vacb);
302 if (!NT_SUCCESS(Status))
303 {
304 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
305 ExRaiseStatus(Status);
306 }
307 }
308 Status = ReadWriteOrZero((PUCHAR)BaseAddress + CurrentOffset % VACB_MAPPING_GRANULARITY,
309 Buffer,
310 PartialLength,
311 Operation);
312
313 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, Operation != CcOperationRead, FALSE);
314
315 if (!NT_SUCCESS(Status))
316 ExRaiseStatus(STATUS_INVALID_USER_BUFFER);
317
318 Length -= PartialLength;
319 CurrentOffset += PartialLength;
320 BytesCopied += PartialLength;
321
322 if (Operation != CcOperationZero)
323 Buffer = (PVOID)((ULONG_PTR)Buffer + PartialLength);
324 }
325
326 while (Length > 0)
327 {
328 ASSERT(CurrentOffset % VACB_MAPPING_GRANULARITY == 0);
329 PartialLength = min(VACB_MAPPING_GRANULARITY, Length);
330 Status = CcRosRequestVacb(SharedCacheMap,
331 CurrentOffset,
332 &BaseAddress,
333 &Valid,
334 &Vacb);
335 if (!NT_SUCCESS(Status))
336 ExRaiseStatus(Status);
337 if (!Valid &&
338 (Operation == CcOperationRead ||
339 PartialLength < VACB_MAPPING_GRANULARITY))
340 {
341 Status = CcReadVirtualAddress(Vacb);
342 if (!NT_SUCCESS(Status))
343 {
344 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
345 ExRaiseStatus(Status);
346 }
347 }
348 Status = ReadWriteOrZero(BaseAddress, Buffer, PartialLength, Operation);
349
350 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, Operation != CcOperationRead, FALSE);
351
352 if (!NT_SUCCESS(Status))
353 ExRaiseStatus(STATUS_INVALID_USER_BUFFER);
354
355 Length -= PartialLength;
356 CurrentOffset += PartialLength;
357 BytesCopied += PartialLength;
358
359 if (Operation != CcOperationZero)
360 Buffer = (PVOID)((ULONG_PTR)Buffer + PartialLength);
361 }
362 IoStatus->Status = STATUS_SUCCESS;
363 IoStatus->Information = BytesCopied;
364 return TRUE;
365 }
366
367 /*
368 * @unimplemented
369 */
370 BOOLEAN
371 NTAPI
372 CcCanIWrite (
373 IN PFILE_OBJECT FileObject,
374 IN ULONG BytesToWrite,
375 IN BOOLEAN Wait,
376 IN BOOLEAN Retrying)
377 {
378 CCTRACE(CC_API_DEBUG, "FileObject=%p BytesToWrite=%lu Wait=%d Retrying=%d\n",
379 FileObject, BytesToWrite, Wait, Retrying);
380
381 return TRUE;
382 }
383
384 /*
385 * @implemented
386 */
387 BOOLEAN
388 NTAPI
389 CcCopyRead (
390 IN PFILE_OBJECT FileObject,
391 IN PLARGE_INTEGER FileOffset,
392 IN ULONG Length,
393 IN BOOLEAN Wait,
394 OUT PVOID Buffer,
395 OUT PIO_STATUS_BLOCK IoStatus)
396 {
397 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%I64d Length=%lu Wait=%d\n",
398 FileObject, FileOffset->QuadPart, Length, Wait);
399
400 DPRINT("CcCopyRead(FileObject 0x%p, FileOffset %I64x, "
401 "Length %lu, Wait %u, Buffer 0x%p, IoStatus 0x%p)\n",
402 FileObject, FileOffset->QuadPart, Length, Wait,
403 Buffer, IoStatus);
404
405 return CcCopyData(FileObject,
406 FileOffset->QuadPart,
407 Buffer,
408 Length,
409 CcOperationRead,
410 Wait,
411 IoStatus);
412 }
413
414 /*
415 * @implemented
416 */
417 BOOLEAN
418 NTAPI
419 CcCopyWrite (
420 IN PFILE_OBJECT FileObject,
421 IN PLARGE_INTEGER FileOffset,
422 IN ULONG Length,
423 IN BOOLEAN Wait,
424 IN PVOID Buffer)
425 {
426 IO_STATUS_BLOCK IoStatus;
427
428 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%I64d Length=%lu Wait=%d Buffer=%p\n",
429 FileObject, FileOffset->QuadPart, Length, Wait, Buffer);
430
431 DPRINT("CcCopyWrite(FileObject 0x%p, FileOffset %I64x, "
432 "Length %lu, Wait %u, Buffer 0x%p)\n",
433 FileObject, FileOffset->QuadPart, Length, Wait, Buffer);
434
435 return CcCopyData(FileObject,
436 FileOffset->QuadPart,
437 Buffer,
438 Length,
439 CcOperationWrite,
440 Wait,
441 &IoStatus);
442 }
443
444 /*
445 * @unimplemented
446 */
447 VOID
448 NTAPI
449 CcDeferWrite (
450 IN PFILE_OBJECT FileObject,
451 IN PCC_POST_DEFERRED_WRITE PostRoutine,
452 IN PVOID Context1,
453 IN PVOID Context2,
454 IN ULONG BytesToWrite,
455 IN BOOLEAN Retrying)
456 {
457 CCTRACE(CC_API_DEBUG, "FileObject=%p PostRoutine=%p Context1=%p Context2=%p BytesToWrite=%lu Retrying=%d\n",
458 FileObject, PostRoutine, Context1, Context2, BytesToWrite, Retrying);
459
460 PostRoutine(Context1, Context2);
461 }
462
463 /*
464 * @unimplemented
465 */
466 VOID
467 NTAPI
468 CcFastCopyRead (
469 IN PFILE_OBJECT FileObject,
470 IN ULONG FileOffset,
471 IN ULONG Length,
472 IN ULONG PageCount,
473 OUT PVOID Buffer,
474 OUT PIO_STATUS_BLOCK IoStatus)
475 {
476 LARGE_INTEGER LargeFileOffset;
477 BOOLEAN Success;
478
479 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%lu Length=%lu PageCount=%lu Buffer=%p\n",
480 FileObject, FileOffset, Length, PageCount, Buffer);
481
482 DBG_UNREFERENCED_PARAMETER(PageCount);
483
484 LargeFileOffset.QuadPart = FileOffset;
485 Success = CcCopyRead(FileObject,
486 &LargeFileOffset,
487 Length,
488 TRUE,
489 Buffer,
490 IoStatus);
491 ASSERT(Success == TRUE);
492 }
493
494 /*
495 * @unimplemented
496 */
497 VOID
498 NTAPI
499 CcFastCopyWrite (
500 IN PFILE_OBJECT FileObject,
501 IN ULONG FileOffset,
502 IN ULONG Length,
503 IN PVOID Buffer)
504 {
505 LARGE_INTEGER LargeFileOffset;
506 BOOLEAN Success;
507
508 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%lu Length=%lu Buffer=%p\n",
509 FileObject, FileOffset, Length, Buffer);
510
511 LargeFileOffset.QuadPart = FileOffset;
512 Success = CcCopyWrite(FileObject,
513 &LargeFileOffset,
514 Length,
515 TRUE,
516 Buffer);
517 ASSERT(Success == TRUE);
518 }
519
520 /*
521 * @implemented
522 */
523 NTSTATUS
524 NTAPI
525 CcWaitForCurrentLazyWriterActivity (
526 VOID)
527 {
528 NTSTATUS Status;
529
530 /* Lazy writer is done when its event is set */
531 Status = KeWaitForSingleObject(&iLazyWriterNotify,
532 Executive,
533 KernelMode,
534 FALSE,
535 NULL);
536 if (!NT_SUCCESS(Status))
537 {
538 return Status;
539 }
540
541 return STATUS_SUCCESS;
542 }
543
544 /*
545 * @implemented
546 */
547 BOOLEAN
548 NTAPI
549 CcZeroData (
550 IN PFILE_OBJECT FileObject,
551 IN PLARGE_INTEGER StartOffset,
552 IN PLARGE_INTEGER EndOffset,
553 IN BOOLEAN Wait)
554 {
555 NTSTATUS Status;
556 LARGE_INTEGER WriteOffset;
557 LONGLONG Length;
558 ULONG CurrentLength;
559 PMDL Mdl;
560 ULONG i;
561 IO_STATUS_BLOCK Iosb;
562 KEVENT Event;
563
564 CCTRACE(CC_API_DEBUG, "FileObject=%p StartOffset=%I64u EndOffset=%I64u Wait=%d\n",
565 FileObject, StartOffset->QuadPart, EndOffset->QuadPart, Wait);
566
567 DPRINT("CcZeroData(FileObject 0x%p, StartOffset %I64x, EndOffset %I64x, "
568 "Wait %u)\n", FileObject, StartOffset->QuadPart, EndOffset->QuadPart,
569 Wait);
570
571 Length = EndOffset->QuadPart - StartOffset->QuadPart;
572 WriteOffset.QuadPart = StartOffset->QuadPart;
573
574 if (FileObject->SectionObjectPointer->SharedCacheMap == NULL)
575 {
576 /* File is not cached */
577
578 Mdl = _alloca(MmSizeOfMdl(NULL, MAX_ZERO_LENGTH));
579
580 while (Length > 0)
581 {
582 if (Length + WriteOffset.QuadPart % PAGE_SIZE > MAX_ZERO_LENGTH)
583 {
584 CurrentLength = MAX_ZERO_LENGTH - WriteOffset.QuadPart % PAGE_SIZE;
585 }
586 else
587 {
588 CurrentLength = Length;
589 }
590 MmInitializeMdl(Mdl, (PVOID)(ULONG_PTR)WriteOffset.QuadPart, CurrentLength);
591 Mdl->MdlFlags |= (MDL_PAGES_LOCKED | MDL_IO_PAGE_READ);
592 for (i = 0; i < ((Mdl->Size - sizeof(MDL)) / sizeof(ULONG)); i++)
593 {
594 ((PPFN_NUMBER)(Mdl + 1))[i] = CcZeroPage;
595 }
596 KeInitializeEvent(&Event, NotificationEvent, FALSE);
597 Status = IoSynchronousPageWrite(FileObject, Mdl, &WriteOffset, &Event, &Iosb);
598 if (Status == STATUS_PENDING)
599 {
600 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
601 Status = Iosb.Status;
602 }
603 if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
604 {
605 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
606 }
607 if (!NT_SUCCESS(Status))
608 {
609 return FALSE;
610 }
611 WriteOffset.QuadPart += CurrentLength;
612 Length -= CurrentLength;
613 }
614 }
615 else
616 {
617 IO_STATUS_BLOCK IoStatus;
618
619 return CcCopyData(FileObject,
620 WriteOffset.QuadPart,
621 NULL,
622 Length,
623 CcOperationZero,
624 Wait,
625 &IoStatus);
626 }
627
628 return TRUE;
629 }