4c68bf036b00e5d9b8758bf478c1c24e4c3f1f4c
[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 /* We cannot write if dirty pages count is above threshold */
382 if (CcTotalDirtyPages > CcDirtyPageThreshold)
383 {
384 return FALSE;
385 }
386
387 /* We cannot write if dirty pages count will bring use above
388 * XXX: Might not be accurate
389 */
390 if (CcTotalDirtyPages + (BytesToWrite / PAGE_SIZE) > CcDirtyPageThreshold)
391 {
392 return FALSE;
393 }
394
395 /* FIXME: Handle per-file threshold */
396
397 return TRUE;
398 }
399
400 /*
401 * @implemented
402 */
403 BOOLEAN
404 NTAPI
405 CcCopyRead (
406 IN PFILE_OBJECT FileObject,
407 IN PLARGE_INTEGER FileOffset,
408 IN ULONG Length,
409 IN BOOLEAN Wait,
410 OUT PVOID Buffer,
411 OUT PIO_STATUS_BLOCK IoStatus)
412 {
413 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%I64d Length=%lu Wait=%d\n",
414 FileObject, FileOffset->QuadPart, Length, Wait);
415
416 DPRINT("CcCopyRead(FileObject 0x%p, FileOffset %I64x, "
417 "Length %lu, Wait %u, Buffer 0x%p, IoStatus 0x%p)\n",
418 FileObject, FileOffset->QuadPart, Length, Wait,
419 Buffer, IoStatus);
420
421 return CcCopyData(FileObject,
422 FileOffset->QuadPart,
423 Buffer,
424 Length,
425 CcOperationRead,
426 Wait,
427 IoStatus);
428 }
429
430 /*
431 * @implemented
432 */
433 BOOLEAN
434 NTAPI
435 CcCopyWrite (
436 IN PFILE_OBJECT FileObject,
437 IN PLARGE_INTEGER FileOffset,
438 IN ULONG Length,
439 IN BOOLEAN Wait,
440 IN PVOID Buffer)
441 {
442 IO_STATUS_BLOCK IoStatus;
443
444 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%I64d Length=%lu Wait=%d Buffer=%p\n",
445 FileObject, FileOffset->QuadPart, Length, Wait, Buffer);
446
447 DPRINT("CcCopyWrite(FileObject 0x%p, FileOffset %I64x, "
448 "Length %lu, Wait %u, Buffer 0x%p)\n",
449 FileObject, FileOffset->QuadPart, Length, Wait, Buffer);
450
451 return CcCopyData(FileObject,
452 FileOffset->QuadPart,
453 Buffer,
454 Length,
455 CcOperationWrite,
456 Wait,
457 &IoStatus);
458 }
459
460 /*
461 * @implemented
462 */
463 VOID
464 NTAPI
465 CcDeferWrite (
466 IN PFILE_OBJECT FileObject,
467 IN PCC_POST_DEFERRED_WRITE PostRoutine,
468 IN PVOID Context1,
469 IN PVOID Context2,
470 IN ULONG BytesToWrite,
471 IN BOOLEAN Retrying)
472 {
473 PROS_DEFERRED_WRITE_CONTEXT Context;
474
475 CCTRACE(CC_API_DEBUG, "FileObject=%p PostRoutine=%p Context1=%p Context2=%p BytesToWrite=%lu Retrying=%d\n",
476 FileObject, PostRoutine, Context1, Context2, BytesToWrite, Retrying);
477
478 /* Try to allocate a context for queueing the write operation */
479 Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(ROS_DEFERRED_WRITE_CONTEXT), 'CcDw');
480 /* If it failed, immediately execute the operation! */
481 if (Context == NULL)
482 {
483 PostRoutine(Context1, Context2);
484 return;
485 }
486
487 /* Otherwise, initialize the context */
488 Context->FileObject = FileObject;
489 Context->PostRoutine = PostRoutine;
490 Context->Context1 = Context1;
491 Context->Context2 = Context2;
492 Context->BytesToWrite = BytesToWrite;
493 Context->Retrying = Retrying;
494
495 /* And queue it */
496 if (Retrying)
497 {
498 /* To the top, if that's a retry */
499 ExInterlockedInsertHeadList(&CcDeferredWrites,
500 &Context->CcDeferredWritesEntry,
501 &CcDeferredWriteSpinLock);
502 }
503 else
504 {
505 /* To the bottom, if that's a first time */
506 ExInterlockedInsertTailList(&CcDeferredWrites,
507 &Context->CcDeferredWritesEntry,
508 &CcDeferredWriteSpinLock);
509 }
510 }
511
512 /*
513 * @unimplemented
514 */
515 VOID
516 NTAPI
517 CcFastCopyRead (
518 IN PFILE_OBJECT FileObject,
519 IN ULONG FileOffset,
520 IN ULONG Length,
521 IN ULONG PageCount,
522 OUT PVOID Buffer,
523 OUT PIO_STATUS_BLOCK IoStatus)
524 {
525 LARGE_INTEGER LargeFileOffset;
526 BOOLEAN Success;
527
528 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%lu Length=%lu PageCount=%lu Buffer=%p\n",
529 FileObject, FileOffset, Length, PageCount, Buffer);
530
531 DBG_UNREFERENCED_PARAMETER(PageCount);
532
533 LargeFileOffset.QuadPart = FileOffset;
534 Success = CcCopyRead(FileObject,
535 &LargeFileOffset,
536 Length,
537 TRUE,
538 Buffer,
539 IoStatus);
540 ASSERT(Success == TRUE);
541 }
542
543 /*
544 * @unimplemented
545 */
546 VOID
547 NTAPI
548 CcFastCopyWrite (
549 IN PFILE_OBJECT FileObject,
550 IN ULONG FileOffset,
551 IN ULONG Length,
552 IN PVOID Buffer)
553 {
554 LARGE_INTEGER LargeFileOffset;
555 BOOLEAN Success;
556
557 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%lu Length=%lu Buffer=%p\n",
558 FileObject, FileOffset, Length, Buffer);
559
560 LargeFileOffset.QuadPart = FileOffset;
561 Success = CcCopyWrite(FileObject,
562 &LargeFileOffset,
563 Length,
564 TRUE,
565 Buffer);
566 ASSERT(Success == TRUE);
567 }
568
569 /*
570 * @implemented
571 */
572 NTSTATUS
573 NTAPI
574 CcWaitForCurrentLazyWriterActivity (
575 VOID)
576 {
577 NTSTATUS Status;
578
579 /* Lazy writer is done when its event is set */
580 Status = KeWaitForSingleObject(&iLazyWriterNotify,
581 Executive,
582 KernelMode,
583 FALSE,
584 NULL);
585 if (!NT_SUCCESS(Status))
586 {
587 return Status;
588 }
589
590 return STATUS_SUCCESS;
591 }
592
593 /*
594 * @implemented
595 */
596 BOOLEAN
597 NTAPI
598 CcZeroData (
599 IN PFILE_OBJECT FileObject,
600 IN PLARGE_INTEGER StartOffset,
601 IN PLARGE_INTEGER EndOffset,
602 IN BOOLEAN Wait)
603 {
604 NTSTATUS Status;
605 LARGE_INTEGER WriteOffset;
606 LONGLONG Length;
607 ULONG CurrentLength;
608 PMDL Mdl;
609 ULONG i;
610 IO_STATUS_BLOCK Iosb;
611 KEVENT Event;
612
613 CCTRACE(CC_API_DEBUG, "FileObject=%p StartOffset=%I64u EndOffset=%I64u Wait=%d\n",
614 FileObject, StartOffset->QuadPart, EndOffset->QuadPart, Wait);
615
616 DPRINT("CcZeroData(FileObject 0x%p, StartOffset %I64x, EndOffset %I64x, "
617 "Wait %u)\n", FileObject, StartOffset->QuadPart, EndOffset->QuadPart,
618 Wait);
619
620 Length = EndOffset->QuadPart - StartOffset->QuadPart;
621 WriteOffset.QuadPart = StartOffset->QuadPart;
622
623 if (FileObject->SectionObjectPointer->SharedCacheMap == NULL)
624 {
625 /* File is not cached */
626
627 Mdl = _alloca(MmSizeOfMdl(NULL, MAX_ZERO_LENGTH));
628
629 while (Length > 0)
630 {
631 if (Length + WriteOffset.QuadPart % PAGE_SIZE > MAX_ZERO_LENGTH)
632 {
633 CurrentLength = MAX_ZERO_LENGTH - WriteOffset.QuadPart % PAGE_SIZE;
634 }
635 else
636 {
637 CurrentLength = Length;
638 }
639 MmInitializeMdl(Mdl, (PVOID)(ULONG_PTR)WriteOffset.QuadPart, CurrentLength);
640 Mdl->MdlFlags |= (MDL_PAGES_LOCKED | MDL_IO_PAGE_READ);
641 for (i = 0; i < ((Mdl->Size - sizeof(MDL)) / sizeof(ULONG)); i++)
642 {
643 ((PPFN_NUMBER)(Mdl + 1))[i] = CcZeroPage;
644 }
645 KeInitializeEvent(&Event, NotificationEvent, FALSE);
646 Status = IoSynchronousPageWrite(FileObject, Mdl, &WriteOffset, &Event, &Iosb);
647 if (Status == STATUS_PENDING)
648 {
649 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
650 Status = Iosb.Status;
651 }
652 if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
653 {
654 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
655 }
656 if (!NT_SUCCESS(Status))
657 {
658 return FALSE;
659 }
660 WriteOffset.QuadPart += CurrentLength;
661 Length -= CurrentLength;
662 }
663 }
664 else
665 {
666 IO_STATUS_BLOCK IoStatus;
667
668 return CcCopyData(FileObject,
669 WriteOffset.QuadPart,
670 NULL,
671 Length,
672 CcOperationZero,
673 Wait,
674 &IoStatus);
675 }
676
677 return TRUE;
678 }