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: Some people? 8 * Pierre Schweitzer (pierre@reactos.org) 9 */ 10 11 /* INCLUDES ******************************************************************/ 12 13 #include <ntoskrnl.h> 14 #define NDEBUG 15 #include <debug.h> 16 17 /* GLOBALS *******************************************************************/ 18 19 static PFN_NUMBER CcZeroPage = 0; 20 21 #define MAX_ZERO_LENGTH (256 * 1024) 22 23 typedef enum _CC_COPY_OPERATION 24 { 25 CcOperationRead, 26 CcOperationWrite, 27 CcOperationZero 28 } CC_COPY_OPERATION; 29 30 typedef enum _CC_CAN_WRITE_RETRY 31 { 32 FirstTry = 0, 33 RetryAllowRemote = 253, 34 RetryForceCheckPerFile = 254, 35 RetryMasterLocked = 255, 36 } CC_CAN_WRITE_RETRY; 37 38 ULONG CcRosTraceLevel = 0; 39 ULONG CcFastMdlReadWait; 40 ULONG CcFastMdlReadNotPossible; 41 ULONG CcFastReadNotPossible; 42 ULONG CcFastReadWait; 43 ULONG CcFastReadNoWait; 44 ULONG CcFastReadResourceMiss; 45 46 /* Counters: 47 * - Amount of pages flushed to the disk 48 * - Number of flush operations 49 */ 50 ULONG CcDataPages = 0; 51 ULONG CcDataFlushes = 0; 52 53 /* FUNCTIONS *****************************************************************/ 54 55 VOID 56 NTAPI 57 MiZeroPhysicalPage ( 58 IN PFN_NUMBER PageFrameIndex 59 ); 60 61 VOID 62 NTAPI 63 CcInitCacheZeroPage ( 64 VOID) 65 { 66 NTSTATUS Status; 67 68 MI_SET_USAGE(MI_USAGE_CACHE); 69 //MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName); 70 Status = MmRequestPageMemoryConsumer(MC_SYSTEM, TRUE, &CcZeroPage); 71 if (!NT_SUCCESS(Status)) 72 { 73 DbgPrint("Can't allocate CcZeroPage.\n"); 74 KeBugCheck(CACHE_MANAGER); 75 } 76 MiZeroPhysicalPage(CcZeroPage); 77 } 78 79 NTSTATUS 80 NTAPI 81 CcReadVirtualAddress ( 82 PROS_VACB Vacb) 83 { 84 ULONG Size, Pages; 85 PMDL Mdl; 86 NTSTATUS Status; 87 IO_STATUS_BLOCK IoStatus; 88 KEVENT Event; 89 90 Size = (ULONG)(Vacb->SharedCacheMap->SectionSize.QuadPart - Vacb->FileOffset.QuadPart); 91 if (Size > VACB_MAPPING_GRANULARITY) 92 { 93 Size = VACB_MAPPING_GRANULARITY; 94 } 95 96 Pages = BYTES_TO_PAGES(Size); 97 ASSERT(Pages * PAGE_SIZE <= VACB_MAPPING_GRANULARITY); 98 99 Mdl = IoAllocateMdl(Vacb->BaseAddress, Pages * PAGE_SIZE, FALSE, FALSE, NULL); 100 if (!Mdl) 101 { 102 return STATUS_INSUFFICIENT_RESOURCES; 103 } 104 105 Status = STATUS_SUCCESS; 106 _SEH2_TRY 107 { 108 MmProbeAndLockPages(Mdl, KernelMode, IoWriteAccess); 109 } 110 _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) 111 { 112 Status = _SEH2_GetExceptionCode(); 113 DPRINT1("MmProbeAndLockPages failed with: %lx for %p (%p, %p)\n", Status, Mdl, Vacb, Vacb->BaseAddress); 114 KeBugCheck(CACHE_MANAGER); 115 } _SEH2_END; 116 117 if (NT_SUCCESS(Status)) 118 { 119 Mdl->MdlFlags |= MDL_IO_PAGE_READ; 120 KeInitializeEvent(&Event, NotificationEvent, FALSE); 121 Status = IoPageRead(Vacb->SharedCacheMap->FileObject, Mdl, &Vacb->FileOffset, &Event, &IoStatus); 122 if (Status == STATUS_PENDING) 123 { 124 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); 125 Status = IoStatus.Status; 126 } 127 128 MmUnlockPages(Mdl); 129 } 130 131 IoFreeMdl(Mdl); 132 133 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE)) 134 { 135 DPRINT1("IoPageRead failed, Status %x\n", Status); 136 return Status; 137 } 138 139 if (Size < VACB_MAPPING_GRANULARITY) 140 { 141 RtlZeroMemory((char*)Vacb->BaseAddress + Size, 142 VACB_MAPPING_GRANULARITY - Size); 143 } 144 145 return STATUS_SUCCESS; 146 } 147 148 NTSTATUS 149 NTAPI 150 CcWriteVirtualAddress ( 151 PROS_VACB Vacb) 152 { 153 ULONG Size; 154 PMDL Mdl; 155 NTSTATUS Status; 156 IO_STATUS_BLOCK IoStatus; 157 KEVENT Event; 158 159 Size = (ULONG)(Vacb->SharedCacheMap->SectionSize.QuadPart - Vacb->FileOffset.QuadPart); 160 if (Size > VACB_MAPPING_GRANULARITY) 161 { 162 Size = VACB_MAPPING_GRANULARITY; 163 } 164 // 165 // Nonpaged pool PDEs in ReactOS must actually be synchronized between the 166 // MmGlobalPageDirectory and the real system PDE directory. What a mess... 167 // 168 { 169 ULONG i = 0; 170 do 171 { 172 MmGetPfnForProcess(NULL, (PVOID)((ULONG_PTR)Vacb->BaseAddress + (i << PAGE_SHIFT))); 173 } while (++i < (Size >> PAGE_SHIFT)); 174 } 175 176 Mdl = IoAllocateMdl(Vacb->BaseAddress, Size, FALSE, FALSE, NULL); 177 if (!Mdl) 178 { 179 return STATUS_INSUFFICIENT_RESOURCES; 180 } 181 182 Status = STATUS_SUCCESS; 183 _SEH2_TRY 184 { 185 MmProbeAndLockPages(Mdl, KernelMode, IoReadAccess); 186 } 187 _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) 188 { 189 Status = _SEH2_GetExceptionCode(); 190 DPRINT1("MmProbeAndLockPages failed with: %lx for %p (%p, %p)\n", Status, Mdl, Vacb, Vacb->BaseAddress); 191 KeBugCheck(CACHE_MANAGER); 192 } _SEH2_END; 193 194 if (NT_SUCCESS(Status)) 195 { 196 KeInitializeEvent(&Event, NotificationEvent, FALSE); 197 Status = IoSynchronousPageWrite(Vacb->SharedCacheMap->FileObject, Mdl, &Vacb->FileOffset, &Event, &IoStatus); 198 if (Status == STATUS_PENDING) 199 { 200 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); 201 Status = IoStatus.Status; 202 } 203 204 MmUnlockPages(Mdl); 205 } 206 IoFreeMdl(Mdl); 207 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE)) 208 { 209 DPRINT1("IoPageWrite failed, Status %x\n", Status); 210 return Status; 211 } 212 213 return STATUS_SUCCESS; 214 } 215 216 NTSTATUS 217 ReadWriteOrZero( 218 _Inout_ PVOID BaseAddress, 219 _Inout_opt_ PVOID Buffer, 220 _In_ ULONG Length, 221 _In_ CC_COPY_OPERATION Operation) 222 { 223 NTSTATUS Status = STATUS_SUCCESS; 224 225 if (Operation == CcOperationZero) 226 { 227 /* Zero */ 228 RtlZeroMemory(BaseAddress, Length); 229 } 230 else 231 { 232 _SEH2_TRY 233 { 234 if (Operation == CcOperationWrite) 235 RtlCopyMemory(BaseAddress, Buffer, Length); 236 else 237 RtlCopyMemory(Buffer, BaseAddress, Length); 238 } 239 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 240 { 241 Status = _SEH2_GetExceptionCode(); 242 } 243 _SEH2_END; 244 } 245 return Status; 246 } 247 248 BOOLEAN 249 CcCopyData ( 250 _In_ PFILE_OBJECT FileObject, 251 _In_ LONGLONG FileOffset, 252 _Inout_ PVOID Buffer, 253 _In_ LONGLONG Length, 254 _In_ CC_COPY_OPERATION Operation, 255 _In_ BOOLEAN Wait, 256 _Out_ PIO_STATUS_BLOCK IoStatus) 257 { 258 NTSTATUS Status; 259 LONGLONG CurrentOffset; 260 ULONG BytesCopied; 261 KIRQL OldIrql; 262 PROS_SHARED_CACHE_MAP SharedCacheMap; 263 PLIST_ENTRY ListEntry; 264 PROS_VACB Vacb; 265 ULONG PartialLength; 266 PVOID BaseAddress; 267 BOOLEAN Valid; 268 PPRIVATE_CACHE_MAP PrivateCacheMap; 269 270 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap; 271 PrivateCacheMap = FileObject->PrivateCacheMap; 272 CurrentOffset = FileOffset; 273 BytesCopied = 0; 274 275 if (!Wait) 276 { 277 /* test if the requested data is available */ 278 KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &OldIrql); 279 /* FIXME: this loop doesn't take into account areas that don't have 280 * a VACB in the list yet */ 281 ListEntry = SharedCacheMap->CacheMapVacbListHead.Flink; 282 while (ListEntry != &SharedCacheMap->CacheMapVacbListHead) 283 { 284 Vacb = CONTAINING_RECORD(ListEntry, 285 ROS_VACB, 286 CacheMapVacbListEntry); 287 ListEntry = ListEntry->Flink; 288 if (!Vacb->Valid && 289 DoRangesIntersect(Vacb->FileOffset.QuadPart, 290 VACB_MAPPING_GRANULARITY, 291 CurrentOffset, Length)) 292 { 293 KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, OldIrql); 294 /* data not available */ 295 return FALSE; 296 } 297 if (Vacb->FileOffset.QuadPart >= CurrentOffset + Length) 298 break; 299 } 300 KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, OldIrql); 301 } 302 303 PartialLength = CurrentOffset % VACB_MAPPING_GRANULARITY; 304 if (PartialLength != 0) 305 { 306 PartialLength = min(Length, VACB_MAPPING_GRANULARITY - PartialLength); 307 Status = CcRosRequestVacb(SharedCacheMap, 308 ROUND_DOWN(CurrentOffset, 309 VACB_MAPPING_GRANULARITY), 310 &BaseAddress, 311 &Valid, 312 &Vacb); 313 if (!NT_SUCCESS(Status)) 314 ExRaiseStatus(Status); 315 if (!Valid) 316 { 317 Status = CcReadVirtualAddress(Vacb); 318 if (!NT_SUCCESS(Status)) 319 { 320 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE); 321 ExRaiseStatus(Status); 322 } 323 } 324 Status = ReadWriteOrZero((PUCHAR)BaseAddress + CurrentOffset % VACB_MAPPING_GRANULARITY, 325 Buffer, 326 PartialLength, 327 Operation); 328 329 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, Operation != CcOperationRead, FALSE); 330 331 if (!NT_SUCCESS(Status)) 332 ExRaiseStatus(STATUS_INVALID_USER_BUFFER); 333 334 Length -= PartialLength; 335 CurrentOffset += PartialLength; 336 BytesCopied += PartialLength; 337 338 if (Operation != CcOperationZero) 339 Buffer = (PVOID)((ULONG_PTR)Buffer + PartialLength); 340 } 341 342 while (Length > 0) 343 { 344 ASSERT(CurrentOffset % VACB_MAPPING_GRANULARITY == 0); 345 PartialLength = min(VACB_MAPPING_GRANULARITY, Length); 346 Status = CcRosRequestVacb(SharedCacheMap, 347 CurrentOffset, 348 &BaseAddress, 349 &Valid, 350 &Vacb); 351 if (!NT_SUCCESS(Status)) 352 ExRaiseStatus(Status); 353 if (!Valid && 354 (Operation == CcOperationRead || 355 PartialLength < VACB_MAPPING_GRANULARITY)) 356 { 357 Status = CcReadVirtualAddress(Vacb); 358 if (!NT_SUCCESS(Status)) 359 { 360 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE); 361 ExRaiseStatus(Status); 362 } 363 } 364 Status = ReadWriteOrZero(BaseAddress, Buffer, PartialLength, Operation); 365 366 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, Operation != CcOperationRead, FALSE); 367 368 if (!NT_SUCCESS(Status)) 369 ExRaiseStatus(STATUS_INVALID_USER_BUFFER); 370 371 Length -= PartialLength; 372 CurrentOffset += PartialLength; 373 BytesCopied += PartialLength; 374 375 if (Operation != CcOperationZero) 376 Buffer = (PVOID)((ULONG_PTR)Buffer + PartialLength); 377 } 378 379 /* If that was a successful sync read operation, let's handle read ahead */ 380 if (Operation == CcOperationRead && Length == 0 && Wait) 381 { 382 /* If file isn't random access and next read may get us cross VACB boundary, 383 * schedule next read 384 */ 385 if (!BooleanFlagOn(FileObject->Flags, FO_RANDOM_ACCESS) && 386 (CurrentOffset - 1) / VACB_MAPPING_GRANULARITY != (CurrentOffset + BytesCopied - 1) / VACB_MAPPING_GRANULARITY) 387 { 388 CcScheduleReadAhead(FileObject, (PLARGE_INTEGER)&FileOffset, BytesCopied); 389 } 390 391 /* And update read history in private cache map */ 392 PrivateCacheMap->FileOffset1.QuadPart = PrivateCacheMap->FileOffset2.QuadPart; 393 PrivateCacheMap->BeyondLastByte1.QuadPart = PrivateCacheMap->BeyondLastByte2.QuadPart; 394 PrivateCacheMap->FileOffset2.QuadPart = FileOffset; 395 PrivateCacheMap->BeyondLastByte2.QuadPart = FileOffset + BytesCopied; 396 } 397 398 IoStatus->Status = STATUS_SUCCESS; 399 IoStatus->Information = BytesCopied; 400 return TRUE; 401 } 402 403 VOID 404 CcPostDeferredWrites(VOID) 405 { 406 ULONG WrittenBytes; 407 408 /* We'll try to write as much as we can */ 409 WrittenBytes = 0; 410 while (TRUE) 411 { 412 KIRQL OldIrql; 413 PLIST_ENTRY ListEntry; 414 PDEFERRED_WRITE DeferredWrite; 415 416 DeferredWrite = NULL; 417 418 /* Lock our deferred writes list */ 419 KeAcquireSpinLock(&CcDeferredWriteSpinLock, &OldIrql); 420 for (ListEntry = CcDeferredWrites.Flink; 421 ListEntry != &CcDeferredWrites; 422 ListEntry = ListEntry->Flink) 423 { 424 /* Extract an entry */ 425 DeferredWrite = CONTAINING_RECORD(ListEntry, DEFERRED_WRITE, DeferredWriteLinks); 426 427 /* Compute the modified bytes, based on what we already wrote */ 428 WrittenBytes += DeferredWrite->BytesToWrite; 429 /* We overflowed, give up */ 430 if (WrittenBytes < DeferredWrite->BytesToWrite) 431 { 432 DeferredWrite = NULL; 433 break; 434 } 435 436 /* Check we can write */ 437 if (CcCanIWrite(DeferredWrite->FileObject, WrittenBytes, FALSE, RetryForceCheckPerFile)) 438 { 439 /* We can, so remove it from the list and stop looking for entry */ 440 RemoveEntryList(&DeferredWrite->DeferredWriteLinks); 441 break; 442 } 443 444 /* If we don't accept modified pages, stop here */ 445 if (!DeferredWrite->LimitModifiedPages) 446 { 447 DeferredWrite = NULL; 448 break; 449 } 450 451 /* Reset count as nothing was written yet */ 452 WrittenBytes -= DeferredWrite->BytesToWrite; 453 DeferredWrite = NULL; 454 } 455 KeReleaseSpinLock(&CcDeferredWriteSpinLock, OldIrql); 456 457 /* Nothing to write found, give up */ 458 if (DeferredWrite == NULL) 459 { 460 break; 461 } 462 463 /* If we have an event, set it and quit */ 464 if (DeferredWrite->Event) 465 { 466 KeSetEvent(DeferredWrite->Event, IO_NO_INCREMENT, FALSE); 467 } 468 /* Otherwise, call the write routine and free the context */ 469 else 470 { 471 DeferredWrite->PostRoutine(DeferredWrite->Context1, DeferredWrite->Context2); 472 ExFreePoolWithTag(DeferredWrite, 'CcDw'); 473 } 474 } 475 } 476 477 VOID 478 CcPerformReadAhead( 479 IN PFILE_OBJECT FileObject) 480 { 481 NTSTATUS Status; 482 LONGLONG CurrentOffset; 483 KIRQL OldIrql; 484 PROS_SHARED_CACHE_MAP SharedCacheMap; 485 PROS_VACB Vacb; 486 ULONG PartialLength; 487 PVOID BaseAddress; 488 BOOLEAN Valid; 489 ULONG Length; 490 PPRIVATE_CACHE_MAP PrivateCacheMap; 491 BOOLEAN Locked; 492 493 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap; 494 495 /* Critical: 496 * PrivateCacheMap might disappear in-between if the handle 497 * to the file is closed (private is attached to the handle not to 498 * the file), so we need to lock the master lock while we deal with 499 * it. It won't disappear without attempting to lock such lock. 500 */ 501 OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); 502 PrivateCacheMap = FileObject->PrivateCacheMap; 503 /* If the handle was closed since the read ahead was scheduled, just quit */ 504 if (PrivateCacheMap == NULL) 505 { 506 KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); 507 ObDereferenceObject(FileObject); 508 return; 509 } 510 /* Otherwise, extract read offset and length and release private map */ 511 else 512 { 513 KeAcquireSpinLockAtDpcLevel(&PrivateCacheMap->ReadAheadSpinLock); 514 CurrentOffset = PrivateCacheMap->ReadAheadOffset[1].QuadPart; 515 Length = PrivateCacheMap->ReadAheadLength[1]; 516 KeReleaseSpinLockFromDpcLevel(&PrivateCacheMap->ReadAheadSpinLock); 517 } 518 KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); 519 520 /* Time to go! */ 521 DPRINT("Doing ReadAhead for %p\n", FileObject); 522 /* Lock the file, first */ 523 if (!SharedCacheMap->Callbacks->AcquireForReadAhead(SharedCacheMap->LazyWriteContext, FALSE)) 524 { 525 Locked = FALSE; 526 goto Clear; 527 } 528 529 /* Remember it's locked */ 530 Locked = TRUE; 531 532 /* Don't read past the end of the file */ 533 if (CurrentOffset >= SharedCacheMap->FileSize.QuadPart) 534 { 535 goto Clear; 536 } 537 if (CurrentOffset + Length > SharedCacheMap->FileSize.QuadPart) 538 { 539 Length = SharedCacheMap->FileSize.QuadPart - CurrentOffset; 540 } 541 542 /* Next of the algorithm will lock like CcCopyData with the slight 543 * difference that we don't copy data back to an user-backed buffer 544 * We just bring data into Cc 545 */ 546 PartialLength = CurrentOffset % VACB_MAPPING_GRANULARITY; 547 if (PartialLength != 0) 548 { 549 PartialLength = min(Length, VACB_MAPPING_GRANULARITY - PartialLength); 550 Status = CcRosRequestVacb(SharedCacheMap, 551 ROUND_DOWN(CurrentOffset, 552 VACB_MAPPING_GRANULARITY), 553 &BaseAddress, 554 &Valid, 555 &Vacb); 556 if (!NT_SUCCESS(Status)) 557 { 558 DPRINT1("Failed to request VACB: %lx!\n", Status); 559 goto Clear; 560 } 561 562 if (!Valid) 563 { 564 Status = CcReadVirtualAddress(Vacb); 565 if (!NT_SUCCESS(Status)) 566 { 567 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE); 568 DPRINT1("Failed to read data: %lx!\n", Status); 569 goto Clear; 570 } 571 } 572 573 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE); 574 575 Length -= PartialLength; 576 CurrentOffset += PartialLength; 577 } 578 579 while (Length > 0) 580 { 581 ASSERT(CurrentOffset % VACB_MAPPING_GRANULARITY == 0); 582 PartialLength = min(VACB_MAPPING_GRANULARITY, Length); 583 Status = CcRosRequestVacb(SharedCacheMap, 584 CurrentOffset, 585 &BaseAddress, 586 &Valid, 587 &Vacb); 588 if (!NT_SUCCESS(Status)) 589 { 590 DPRINT1("Failed to request VACB: %lx!\n", Status); 591 goto Clear; 592 } 593 594 if (!Valid) 595 { 596 Status = CcReadVirtualAddress(Vacb); 597 if (!NT_SUCCESS(Status)) 598 { 599 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE); 600 DPRINT1("Failed to read data: %lx!\n", Status); 601 goto Clear; 602 } 603 } 604 605 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE); 606 607 Length -= PartialLength; 608 CurrentOffset += PartialLength; 609 } 610 611 Clear: 612 /* See previous comment about private cache map */ 613 OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); 614 PrivateCacheMap = FileObject->PrivateCacheMap; 615 if (PrivateCacheMap != NULL) 616 { 617 /* Mark read ahead as unactive */ 618 KeAcquireSpinLockAtDpcLevel(&PrivateCacheMap->ReadAheadSpinLock); 619 InterlockedAnd((volatile long *)&PrivateCacheMap->UlongFlags, ~PRIVATE_CACHE_MAP_READ_AHEAD_ACTIVE); 620 KeReleaseSpinLockFromDpcLevel(&PrivateCacheMap->ReadAheadSpinLock); 621 } 622 KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); 623 624 /* If file was locked, release it */ 625 if (Locked) 626 { 627 SharedCacheMap->Callbacks->ReleaseFromReadAhead(SharedCacheMap->LazyWriteContext); 628 } 629 630 /* And drop our extra reference (See: CcScheduleReadAhead) */ 631 ObDereferenceObject(FileObject); 632 633 return; 634 } 635 636 /* 637 * @unimplemented 638 */ 639 BOOLEAN 640 NTAPI 641 CcCanIWrite ( 642 IN PFILE_OBJECT FileObject, 643 IN ULONG BytesToWrite, 644 IN BOOLEAN Wait, 645 IN BOOLEAN Retrying) 646 { 647 KIRQL OldIrql; 648 KEVENT WaitEvent; 649 ULONG Length, Pages; 650 BOOLEAN PerFileDefer; 651 DEFERRED_WRITE Context; 652 PFSRTL_COMMON_FCB_HEADER Fcb; 653 CC_CAN_WRITE_RETRY TryContext; 654 PROS_SHARED_CACHE_MAP SharedCacheMap; 655 656 CCTRACE(CC_API_DEBUG, "FileObject=%p BytesToWrite=%lu Wait=%d Retrying=%d\n", 657 FileObject, BytesToWrite, Wait, Retrying); 658 659 /* Write through is always OK */ 660 if (BooleanFlagOn(FileObject->Flags, FO_WRITE_THROUGH)) 661 { 662 return TRUE; 663 } 664 665 TryContext = Retrying; 666 /* Allow remote file if not from posted */ 667 if (IoIsFileOriginRemote(FileObject) && TryContext < RetryAllowRemote) 668 { 669 return TRUE; 670 } 671 672 /* Don't exceed max tolerated size */ 673 Length = MAX_ZERO_LENGTH; 674 if (BytesToWrite < MAX_ZERO_LENGTH) 675 { 676 Length = BytesToWrite; 677 } 678 679 /* Convert it to pages count */ 680 Pages = (Length + PAGE_SIZE - 1) >> PAGE_SHIFT; 681 682 /* By default, assume limits per file won't be hit */ 683 PerFileDefer = FALSE; 684 Fcb = FileObject->FsContext; 685 /* Do we have to check for limits per file? */ 686 if (TryContext >= RetryForceCheckPerFile || 687 BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES)) 688 { 689 /* If master is not locked, lock it now */ 690 if (TryContext != RetryMasterLocked) 691 { 692 OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); 693 } 694 695 /* Let's not assume the file is cached... */ 696 if (FileObject->SectionObjectPointer != NULL && 697 FileObject->SectionObjectPointer->SharedCacheMap != NULL) 698 { 699 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap; 700 /* Do we have limits per file set? */ 701 if (SharedCacheMap->DirtyPageThreshold != 0 && 702 SharedCacheMap->DirtyPages != 0) 703 { 704 /* Yes, check whether they are blocking */ 705 if (Pages + SharedCacheMap->DirtyPages > SharedCacheMap->DirtyPageThreshold) 706 { 707 PerFileDefer = TRUE; 708 } 709 } 710 } 711 712 /* And don't forget to release master */ 713 if (TryContext != RetryMasterLocked) 714 { 715 KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); 716 } 717 } 718 719 /* So, now allow write if: 720 * - Not the first try or we have no throttling yet 721 * AND: 722 * - We don't exceed threshold! 723 * - We don't exceed what Mm can allow us to use 724 * + If we're above top, that's fine 725 * + If we're above bottom with limited modified pages, that's fine 726 * + Otherwise, throttle! 727 */ 728 if ((TryContext != FirstTry || IsListEmpty(&CcDeferredWrites)) && 729 CcTotalDirtyPages + Pages < CcDirtyPageThreshold && 730 (MmAvailablePages > MmThrottleTop || 731 (MmModifiedPageListHead.Total < 1000 && MmAvailablePages > MmThrottleBottom)) && 732 !PerFileDefer) 733 { 734 return TRUE; 735 } 736 737 /* If we can wait, we'll start the wait loop for waiting till we can 738 * write for real 739 */ 740 if (!Wait) 741 { 742 return FALSE; 743 } 744 745 /* Otherwise, if there are no deferred writes yet, start the lazy writer */ 746 if (IsListEmpty(&CcDeferredWrites)) 747 { 748 KIRQL OldIrql; 749 750 OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); 751 CcScheduleLazyWriteScan(TRUE); 752 KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); 753 } 754 755 /* Initialize our wait event */ 756 KeInitializeEvent(&WaitEvent, NotificationEvent, FALSE); 757 758 /* And prepare a dummy context */ 759 Context.NodeTypeCode = NODE_TYPE_DEFERRED_WRITE; 760 Context.NodeByteSize = sizeof(DEFERRED_WRITE); 761 Context.FileObject = FileObject; 762 Context.BytesToWrite = BytesToWrite; 763 Context.LimitModifiedPages = BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES); 764 Context.Event = &WaitEvent; 765 766 /* And queue it */ 767 if (Retrying) 768 { 769 /* To the top, if that's a retry */ 770 ExInterlockedInsertHeadList(&CcDeferredWrites, 771 &Context.DeferredWriteLinks, 772 &CcDeferredWriteSpinLock); 773 } 774 else 775 { 776 /* To the bottom, if that's a first time */ 777 ExInterlockedInsertTailList(&CcDeferredWrites, 778 &Context.DeferredWriteLinks, 779 &CcDeferredWriteSpinLock); 780 } 781 782 /* Now, we'll loop until our event is set. When it is set, it means that caller 783 * can immediately write, and has to 784 */ 785 do 786 { 787 CcPostDeferredWrites(); 788 } while (KeWaitForSingleObject(&WaitEvent, Executive, KernelMode, FALSE, &CcIdleDelay) != STATUS_SUCCESS); 789 790 return TRUE; 791 } 792 793 /* 794 * @implemented 795 */ 796 BOOLEAN 797 NTAPI 798 CcCopyRead ( 799 IN PFILE_OBJECT FileObject, 800 IN PLARGE_INTEGER FileOffset, 801 IN ULONG Length, 802 IN BOOLEAN Wait, 803 OUT PVOID Buffer, 804 OUT PIO_STATUS_BLOCK IoStatus) 805 { 806 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%I64d Length=%lu Wait=%d\n", 807 FileObject, FileOffset->QuadPart, Length, Wait); 808 809 DPRINT("CcCopyRead(FileObject 0x%p, FileOffset %I64x, " 810 "Length %lu, Wait %u, Buffer 0x%p, IoStatus 0x%p)\n", 811 FileObject, FileOffset->QuadPart, Length, Wait, 812 Buffer, IoStatus); 813 814 return CcCopyData(FileObject, 815 FileOffset->QuadPart, 816 Buffer, 817 Length, 818 CcOperationRead, 819 Wait, 820 IoStatus); 821 } 822 823 /* 824 * @implemented 825 */ 826 BOOLEAN 827 NTAPI 828 CcCopyWrite ( 829 IN PFILE_OBJECT FileObject, 830 IN PLARGE_INTEGER FileOffset, 831 IN ULONG Length, 832 IN BOOLEAN Wait, 833 IN PVOID Buffer) 834 { 835 IO_STATUS_BLOCK IoStatus; 836 837 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%I64d Length=%lu Wait=%d Buffer=%p\n", 838 FileObject, FileOffset->QuadPart, Length, Wait, Buffer); 839 840 DPRINT("CcCopyWrite(FileObject 0x%p, FileOffset %I64x, " 841 "Length %lu, Wait %u, Buffer 0x%p)\n", 842 FileObject, FileOffset->QuadPart, Length, Wait, Buffer); 843 844 return CcCopyData(FileObject, 845 FileOffset->QuadPart, 846 Buffer, 847 Length, 848 CcOperationWrite, 849 Wait, 850 &IoStatus); 851 } 852 853 /* 854 * @implemented 855 */ 856 VOID 857 NTAPI 858 CcDeferWrite ( 859 IN PFILE_OBJECT FileObject, 860 IN PCC_POST_DEFERRED_WRITE PostRoutine, 861 IN PVOID Context1, 862 IN PVOID Context2, 863 IN ULONG BytesToWrite, 864 IN BOOLEAN Retrying) 865 { 866 KIRQL OldIrql; 867 PDEFERRED_WRITE Context; 868 PFSRTL_COMMON_FCB_HEADER Fcb; 869 870 CCTRACE(CC_API_DEBUG, "FileObject=%p PostRoutine=%p Context1=%p Context2=%p BytesToWrite=%lu Retrying=%d\n", 871 FileObject, PostRoutine, Context1, Context2, BytesToWrite, Retrying); 872 873 /* Try to allocate a context for queueing the write operation */ 874 Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(DEFERRED_WRITE), 'CcDw'); 875 /* If it failed, immediately execute the operation! */ 876 if (Context == NULL) 877 { 878 PostRoutine(Context1, Context2); 879 return; 880 } 881 882 Fcb = FileObject->FsContext; 883 884 /* Otherwise, initialize the context */ 885 RtlZeroMemory(Context, sizeof(DEFERRED_WRITE)); 886 Context->NodeTypeCode = NODE_TYPE_DEFERRED_WRITE; 887 Context->NodeByteSize = sizeof(DEFERRED_WRITE); 888 Context->FileObject = FileObject; 889 Context->PostRoutine = PostRoutine; 890 Context->Context1 = Context1; 891 Context->Context2 = Context2; 892 Context->BytesToWrite = BytesToWrite; 893 Context->LimitModifiedPages = BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES); 894 895 /* And queue it */ 896 if (Retrying) 897 { 898 /* To the top, if that's a retry */ 899 ExInterlockedInsertHeadList(&CcDeferredWrites, 900 &Context->DeferredWriteLinks, 901 &CcDeferredWriteSpinLock); 902 } 903 else 904 { 905 /* To the bottom, if that's a first time */ 906 ExInterlockedInsertTailList(&CcDeferredWrites, 907 &Context->DeferredWriteLinks, 908 &CcDeferredWriteSpinLock); 909 } 910 911 /* Try to execute the posted writes */ 912 CcPostDeferredWrites(); 913 914 /* Schedule a lazy writer run to handle deferred writes */ 915 OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); 916 if (!LazyWriter.ScanActive) 917 { 918 CcScheduleLazyWriteScan(FALSE); 919 } 920 KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); 921 } 922 923 /* 924 * @unimplemented 925 */ 926 VOID 927 NTAPI 928 CcFastCopyRead ( 929 IN PFILE_OBJECT FileObject, 930 IN ULONG FileOffset, 931 IN ULONG Length, 932 IN ULONG PageCount, 933 OUT PVOID Buffer, 934 OUT PIO_STATUS_BLOCK IoStatus) 935 { 936 LARGE_INTEGER LargeFileOffset; 937 BOOLEAN Success; 938 939 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%lu Length=%lu PageCount=%lu Buffer=%p\n", 940 FileObject, FileOffset, Length, PageCount, Buffer); 941 942 DBG_UNREFERENCED_PARAMETER(PageCount); 943 944 LargeFileOffset.QuadPart = FileOffset; 945 Success = CcCopyRead(FileObject, 946 &LargeFileOffset, 947 Length, 948 TRUE, 949 Buffer, 950 IoStatus); 951 ASSERT(Success == TRUE); 952 } 953 954 /* 955 * @unimplemented 956 */ 957 VOID 958 NTAPI 959 CcFastCopyWrite ( 960 IN PFILE_OBJECT FileObject, 961 IN ULONG FileOffset, 962 IN ULONG Length, 963 IN PVOID Buffer) 964 { 965 LARGE_INTEGER LargeFileOffset; 966 BOOLEAN Success; 967 968 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%lu Length=%lu Buffer=%p\n", 969 FileObject, FileOffset, Length, Buffer); 970 971 LargeFileOffset.QuadPart = FileOffset; 972 Success = CcCopyWrite(FileObject, 973 &LargeFileOffset, 974 Length, 975 TRUE, 976 Buffer); 977 ASSERT(Success == TRUE); 978 } 979 980 /* 981 * @implemented 982 */ 983 BOOLEAN 984 NTAPI 985 CcZeroData ( 986 IN PFILE_OBJECT FileObject, 987 IN PLARGE_INTEGER StartOffset, 988 IN PLARGE_INTEGER EndOffset, 989 IN BOOLEAN Wait) 990 { 991 NTSTATUS Status; 992 LARGE_INTEGER WriteOffset; 993 LONGLONG Length; 994 ULONG CurrentLength; 995 PMDL Mdl; 996 ULONG i; 997 IO_STATUS_BLOCK Iosb; 998 KEVENT Event; 999 1000 CCTRACE(CC_API_DEBUG, "FileObject=%p StartOffset=%I64u EndOffset=%I64u Wait=%d\n", 1001 FileObject, StartOffset->QuadPart, EndOffset->QuadPart, Wait); 1002 1003 DPRINT("CcZeroData(FileObject 0x%p, StartOffset %I64x, EndOffset %I64x, " 1004 "Wait %u)\n", FileObject, StartOffset->QuadPart, EndOffset->QuadPart, 1005 Wait); 1006 1007 Length = EndOffset->QuadPart - StartOffset->QuadPart; 1008 WriteOffset.QuadPart = StartOffset->QuadPart; 1009 1010 if (FileObject->SectionObjectPointer->SharedCacheMap == NULL) 1011 { 1012 /* File is not cached */ 1013 1014 Mdl = _alloca(MmSizeOfMdl(NULL, MAX_ZERO_LENGTH)); 1015 1016 while (Length > 0) 1017 { 1018 if (Length + WriteOffset.QuadPart % PAGE_SIZE > MAX_ZERO_LENGTH) 1019 { 1020 CurrentLength = MAX_ZERO_LENGTH - WriteOffset.QuadPart % PAGE_SIZE; 1021 } 1022 else 1023 { 1024 CurrentLength = Length; 1025 } 1026 MmInitializeMdl(Mdl, (PVOID)(ULONG_PTR)WriteOffset.QuadPart, CurrentLength); 1027 Mdl->MdlFlags |= (MDL_PAGES_LOCKED | MDL_IO_PAGE_READ); 1028 for (i = 0; i < ((Mdl->Size - sizeof(MDL)) / sizeof(ULONG)); i++) 1029 { 1030 ((PPFN_NUMBER)(Mdl + 1))[i] = CcZeroPage; 1031 } 1032 KeInitializeEvent(&Event, NotificationEvent, FALSE); 1033 Status = IoSynchronousPageWrite(FileObject, Mdl, &WriteOffset, &Event, &Iosb); 1034 if (Status == STATUS_PENDING) 1035 { 1036 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); 1037 Status = Iosb.Status; 1038 } 1039 if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) 1040 { 1041 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl); 1042 } 1043 if (!NT_SUCCESS(Status)) 1044 { 1045 return FALSE; 1046 } 1047 WriteOffset.QuadPart += CurrentLength; 1048 Length -= CurrentLength; 1049 } 1050 } 1051 else 1052 { 1053 IO_STATUS_BLOCK IoStatus; 1054 1055 return CcCopyData(FileObject, 1056 WriteOffset.QuadPart, 1057 NULL, 1058 Length, 1059 CcOperationZero, 1060 Wait, 1061 &IoStatus); 1062 } 1063 1064 return TRUE; 1065 } 1066