1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS kernel 4 * FILE: ntoskrnl/cc/pin.c 5 * PURPOSE: Implements cache managers pinning interface 6 * 7 * PROGRAMMERS: ? 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 extern NPAGED_LOOKASIDE_LIST iBcbLookasideList; 20 21 /* Counters: 22 * - Number of calls to CcMapData that could wait 23 * - Number of calls to CcMapData that couldn't wait 24 * - Number of calls to CcPinRead that could wait 25 * - Number of calls to CcPinRead that couldn't wait 26 */ 27 ULONG CcMapDataWait = 0; 28 ULONG CcMapDataNoWait = 0; 29 ULONG CcPinReadWait = 0; 30 ULONG CcPinReadNoWait = 0; 31 32 /* FUNCTIONS *****************************************************************/ 33 34 static 35 PINTERNAL_BCB 36 NTAPI 37 CcpFindBcb( 38 IN PROS_SHARED_CACHE_MAP SharedCacheMap, 39 IN PLARGE_INTEGER FileOffset, 40 IN ULONG Length, 41 IN BOOLEAN Pinned) 42 { 43 PINTERNAL_BCB Bcb; 44 BOOLEAN Found = FALSE; 45 PLIST_ENTRY NextEntry; 46 47 for (NextEntry = SharedCacheMap->BcbList.Flink; 48 NextEntry != &SharedCacheMap->BcbList; 49 NextEntry = NextEntry->Flink) 50 { 51 Bcb = CONTAINING_RECORD(NextEntry, INTERNAL_BCB, BcbEntry); 52 53 if (Bcb->PFCB.MappedFileOffset.QuadPart <= FileOffset->QuadPart && 54 (Bcb->PFCB.MappedFileOffset.QuadPart + Bcb->PFCB.MappedLength) >= 55 (FileOffset->QuadPart + Length)) 56 { 57 if ((Pinned && Bcb->PinCount > 0) || (!Pinned && Bcb->PinCount == 0)) 58 { 59 Found = TRUE; 60 break; 61 } 62 } 63 } 64 65 return (Found ? Bcb : NULL); 66 } 67 68 static 69 BOOLEAN 70 NTAPI 71 CcpMapData( 72 IN PROS_SHARED_CACHE_MAP SharedCacheMap, 73 IN PLARGE_INTEGER FileOffset, 74 IN ULONG Length, 75 IN ULONG Flags, 76 IN BOOLEAN ToPin, 77 OUT PVOID *pBcb, 78 OUT PVOID *pBuffer) 79 { 80 LONGLONG ReadOffset; 81 BOOLEAN Valid; 82 PROS_VACB Vacb; 83 NTSTATUS Status; 84 PINTERNAL_BCB iBcb, DupBcb; 85 LONGLONG ROffset; 86 KIRQL OldIrql; 87 88 ReadOffset = FileOffset->QuadPart; 89 90 DPRINT("SectionSize %I64x, FileSize %I64x\n", 91 SharedCacheMap->SectionSize.QuadPart, 92 SharedCacheMap->FileSize.QuadPart); 93 94 if (ReadOffset % VACB_MAPPING_GRANULARITY + Length > VACB_MAPPING_GRANULARITY) 95 { 96 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%p Length=%lu Flags=0x%lx -> FALSE\n", 97 SharedCacheMap->FileObject, FileOffset, Length, Flags); 98 return FALSE; 99 } 100 101 if (!BooleanFlagOn(Flags, MAP_NO_READ)) 102 { 103 static int Warned = 0; 104 105 SetFlag(Flags, MAP_NO_READ); 106 if (!Warned) 107 { 108 DPRINT1("Mapping/pinning with no read not implemented. Forcing read, might fail if wait not allowed\n"); 109 Warned++; 110 } 111 } 112 113 ROffset = ROUND_DOWN(ReadOffset, VACB_MAPPING_GRANULARITY); 114 Status = CcRosRequestVacb(SharedCacheMap, 115 ROffset, 116 pBuffer, 117 &Valid, 118 &Vacb); 119 if (!NT_SUCCESS(Status)) 120 { 121 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%p Length=%lu Flags=0x%lx -> FALSE\n", 122 SharedCacheMap->FileObject, FileOffset, Length, Flags); 123 ExRaiseStatus(Status); 124 return FALSE; 125 } 126 127 if (!Valid && BooleanFlagOn(Flags, MAP_NO_READ)) 128 { 129 if (!BooleanFlagOn(Flags, MAP_WAIT)) 130 { 131 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE); 132 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%p Length=%lu Flags=0x%lx -> FALSE\n", 133 SharedCacheMap->FileObject, FileOffset, Length, Flags); 134 return FALSE; 135 } 136 137 Status = CcReadVirtualAddress(Vacb); 138 if (!NT_SUCCESS(Status)) 139 { 140 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE); 141 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%p Length=%lu Flags=0x%lx -> FALSE\n", 142 SharedCacheMap->FileObject, FileOffset, Length, Flags); 143 ExRaiseStatus(Status); 144 return FALSE; 145 } 146 } 147 148 *pBuffer = (PUCHAR)*pBuffer + ReadOffset % VACB_MAPPING_GRANULARITY; 149 iBcb = ExAllocateFromNPagedLookasideList(&iBcbLookasideList); 150 if (iBcb == NULL) 151 { 152 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE); 153 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%p Length=%lu Flags=0x%lx -> FALSE\n", 154 SharedCacheMap->FileObject, FileOffset, Length, Flags); 155 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); 156 return FALSE; 157 } 158 159 RtlZeroMemory(iBcb, sizeof(*iBcb)); 160 iBcb->PFCB.NodeTypeCode = 0xDE45; /* Undocumented (CAPTIVE_PUBLIC_BCB_NODETYPECODE) */ 161 iBcb->PFCB.NodeByteSize = sizeof(PUBLIC_BCB); 162 iBcb->PFCB.MappedLength = Length; 163 iBcb->PFCB.MappedFileOffset = *FileOffset; 164 iBcb->Vacb = Vacb; 165 iBcb->Dirty = FALSE; 166 iBcb->PinCount = 0; 167 iBcb->RefCount = 1; 168 ExInitializeResourceLite(&iBcb->Lock); 169 *pBcb = (PVOID)iBcb; 170 171 /* Only insert if we're not to pin data */ 172 if (!ToPin) 173 { 174 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql); 175 176 /* Check if we raced with another BCB creation */ 177 DupBcb = CcpFindBcb(SharedCacheMap, FileOffset, Length, FALSE); 178 /* Yes, and we've lost */ 179 if (DupBcb != NULL) 180 { 181 /* We'll return that BCB */ 182 ++DupBcb->RefCount; 183 184 /* Delete the loser */ 185 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE); 186 ExDeleteResourceLite(&iBcb->Lock); 187 ExFreeToNPagedLookasideList(&iBcbLookasideList, iBcb); 188 189 /* Return the winner - no need to update buffer address, it's 190 * relative to the VACB, which is unchanged. 191 */ 192 *pBcb = DupBcb; 193 } 194 /* Nope, insert ourselves */ 195 else 196 { 197 InsertTailList(&SharedCacheMap->BcbList, &iBcb->BcbEntry); 198 } 199 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql); 200 } 201 else 202 { 203 InitializeListHead(&iBcb->BcbEntry); 204 } 205 206 return TRUE; 207 } 208 209 /* 210 * @implemented 211 */ 212 BOOLEAN 213 NTAPI 214 CcMapData ( 215 IN PFILE_OBJECT FileObject, 216 IN PLARGE_INTEGER FileOffset, 217 IN ULONG Length, 218 IN ULONG Flags, 219 OUT PVOID *pBcb, 220 OUT PVOID *pBuffer) 221 { 222 BOOLEAN Ret; 223 KIRQL OldIrql; 224 PINTERNAL_BCB iBcb; 225 PROS_SHARED_CACHE_MAP SharedCacheMap; 226 227 DPRINT("CcMapData(FileObject 0x%p, FileOffset %I64x, Length %lu, Flags 0x%lx," 228 " pBcb 0x%p, pBuffer 0x%p)\n", FileObject, FileOffset->QuadPart, 229 Length, Flags, pBcb, pBuffer); 230 231 ASSERT(FileObject); 232 ASSERT(FileObject->SectionObjectPointer); 233 ASSERT(FileObject->SectionObjectPointer->SharedCacheMap); 234 235 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap; 236 ASSERT(SharedCacheMap); 237 238 if (Flags & MAP_WAIT) 239 { 240 ++CcMapDataWait; 241 } 242 else 243 { 244 ++CcMapDataNoWait; 245 } 246 247 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql); 248 iBcb = CcpFindBcb(SharedCacheMap, FileOffset, Length, FALSE); 249 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql); 250 251 if (iBcb == NULL) 252 { 253 Ret = CcpMapData(SharedCacheMap, FileOffset, Length, Flags, FALSE, pBcb, pBuffer); 254 } 255 else 256 { 257 ++iBcb->RefCount; 258 *pBcb = iBcb; 259 *pBuffer = (PUCHAR)iBcb->Vacb->BaseAddress + FileOffset->QuadPart % VACB_MAPPING_GRANULARITY; 260 Ret = TRUE; 261 } 262 263 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%p Length=%lu Flags=0x%lx -> %d Bcb=%p\n", 264 FileObject, FileOffset, Length, Flags, Ret, *pBcb); 265 return Ret; 266 } 267 268 /* 269 * @unimplemented 270 */ 271 BOOLEAN 272 NTAPI 273 CcPinMappedData ( 274 IN PFILE_OBJECT FileObject, 275 IN PLARGE_INTEGER FileOffset, 276 IN ULONG Length, 277 IN ULONG Flags, 278 OUT PVOID * Bcb) 279 { 280 BOOLEAN Result; 281 PINTERNAL_BCB iBcb; 282 PROS_SHARED_CACHE_MAP SharedCacheMap; 283 284 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%p Length=%lu Flags=0x%lx\n", 285 FileObject, FileOffset, Length, Flags); 286 287 ASSERT(FileObject); 288 ASSERT(FileObject->SectionObjectPointer); 289 ASSERT(FileObject->SectionObjectPointer->SharedCacheMap); 290 291 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap; 292 ASSERT(SharedCacheMap); 293 if (!SharedCacheMap->PinAccess) 294 { 295 DPRINT1("FIXME: Pinning a file with no pin access!\n"); 296 return FALSE; 297 } 298 299 iBcb = *Bcb; 300 ASSERT(iBcb->PinCount == 0); 301 302 iBcb->PinCount++; 303 304 if (BooleanFlagOn(Flags, PIN_EXCLUSIVE)) 305 { 306 Result = ExAcquireResourceExclusiveLite(&iBcb->Lock, BooleanFlagOn(Flags, PIN_WAIT)); 307 } 308 else 309 { 310 Result = ExAcquireSharedStarveExclusive(&iBcb->Lock, BooleanFlagOn(Flags, PIN_WAIT)); 311 } 312 313 if (!Result) 314 { 315 iBcb->PinCount--; 316 } 317 318 return Result; 319 } 320 321 /* 322 * @unimplemented 323 */ 324 BOOLEAN 325 NTAPI 326 CcPinRead ( 327 IN PFILE_OBJECT FileObject, 328 IN PLARGE_INTEGER FileOffset, 329 IN ULONG Length, 330 IN ULONG Flags, 331 OUT PVOID * Bcb, 332 OUT PVOID * Buffer) 333 { 334 KIRQL OldIrql; 335 BOOLEAN Result; 336 PINTERNAL_BCB iBcb; 337 PROS_SHARED_CACHE_MAP SharedCacheMap; 338 339 CCTRACE(CC_API_DEBUG, "FileOffset=%p FileOffset=%p Length=%lu Flags=0x%lx\n", 340 FileObject, FileOffset, Length, Flags); 341 342 ASSERT(FileObject); 343 ASSERT(FileObject->SectionObjectPointer); 344 ASSERT(FileObject->SectionObjectPointer->SharedCacheMap); 345 346 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap; 347 ASSERT(SharedCacheMap); 348 349 if (Flags & PIN_WAIT) 350 { 351 ++CcPinReadWait; 352 } 353 else 354 { 355 ++CcPinReadNoWait; 356 } 357 358 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql); 359 iBcb = CcpFindBcb(SharedCacheMap, FileOffset, Length, TRUE); 360 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql); 361 362 if (iBcb == NULL) 363 { 364 /* We failed to find an already existing BCB */ 365 if (BooleanFlagOn(Flags, PIN_IF_BCB)) 366 { 367 return FALSE; 368 } 369 370 /* Map first */ 371 if (!CcpMapData(SharedCacheMap, FileOffset, Length, Flags, TRUE, Bcb, Buffer)) 372 { 373 return FALSE; 374 } 375 376 /* Pin then */ 377 if (!CcPinMappedData(FileObject, FileOffset, Length, Flags, Bcb)) 378 { 379 CcUnpinData(*Bcb); 380 return FALSE; 381 } 382 383 /* Did we race for the BCB creation? */ 384 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql); 385 iBcb = CcpFindBcb(SharedCacheMap, FileOffset, Length, TRUE); 386 if (iBcb != NULL) 387 { 388 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql); 389 390 /* Free our now unused BCB */ 391 CcUnpinData(*Bcb); 392 393 /* Lock the found BCB and return it */ 394 if (BooleanFlagOn(Flags, PIN_EXCLUSIVE)) 395 { 396 Result = ExAcquireResourceExclusiveLite(&iBcb->Lock, BooleanFlagOn(Flags, PIN_WAIT)); 397 } 398 else 399 { 400 Result = ExAcquireSharedStarveExclusive(&iBcb->Lock, BooleanFlagOn(Flags, PIN_WAIT)); 401 } 402 403 if (!Result) 404 { 405 return FALSE; 406 } 407 408 ++iBcb->PinCount; 409 ++iBcb->RefCount; 410 411 *Bcb = iBcb; 412 } 413 else 414 { 415 iBcb = *Bcb; 416 417 /* Insert ourselves in the linked list */ 418 InsertTailList(&SharedCacheMap->BcbList, &iBcb->BcbEntry); 419 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql); 420 } 421 } 422 /* We found a BCB, lock it and return it */ 423 else 424 { 425 if (BooleanFlagOn(Flags, PIN_EXCLUSIVE)) 426 { 427 Result = ExAcquireResourceExclusiveLite(&iBcb->Lock, BooleanFlagOn(Flags, PIN_WAIT)); 428 } 429 else 430 { 431 Result = ExAcquireSharedStarveExclusive(&iBcb->Lock, BooleanFlagOn(Flags, PIN_WAIT)); 432 } 433 434 if (!Result) 435 { 436 return FALSE; 437 } 438 439 ++iBcb->PinCount; 440 ++iBcb->RefCount; 441 442 *Bcb = iBcb; 443 *Buffer = (PUCHAR)iBcb->Vacb->BaseAddress + FileOffset->QuadPart % VACB_MAPPING_GRANULARITY; 444 } 445 446 return TRUE; 447 } 448 449 /* 450 * @unimplemented 451 */ 452 BOOLEAN 453 NTAPI 454 CcPreparePinWrite ( 455 IN PFILE_OBJECT FileObject, 456 IN PLARGE_INTEGER FileOffset, 457 IN ULONG Length, 458 IN BOOLEAN Zero, 459 IN ULONG Flags, 460 OUT PVOID * Bcb, 461 OUT PVOID * Buffer) 462 { 463 CCTRACE(CC_API_DEBUG, "FileOffset=%p FileOffset=%p Length=%lu Zero=%d Flags=0x%lx\n", 464 FileObject, FileOffset, Length, Zero, Flags); 465 466 /* 467 * FIXME: This is function is similar to CcPinRead, but doesn't 468 * read the data if they're not present. Instead it should just 469 * prepare the VACBs and zero them out if Zero != FALSE. 470 * 471 * For now calling CcPinRead is better than returning error or 472 * just having UNIMPLEMENTED here. 473 */ 474 return CcPinRead(FileObject, FileOffset, Length, Flags, Bcb, Buffer); 475 } 476 477 /* 478 * @implemented 479 */ 480 VOID NTAPI 481 CcSetDirtyPinnedData ( 482 IN PVOID Bcb, 483 IN PLARGE_INTEGER Lsn) 484 { 485 PINTERNAL_BCB iBcb = Bcb; 486 487 CCTRACE(CC_API_DEBUG, "Bcb=%p Lsn=%p\n", 488 Bcb, Lsn); 489 490 iBcb->Dirty = TRUE; 491 if (!iBcb->Vacb->Dirty) 492 { 493 CcRosMarkDirtyVacb(iBcb->Vacb); 494 } 495 } 496 497 498 /* 499 * @implemented 500 */ 501 VOID NTAPI 502 CcUnpinData ( 503 IN PVOID Bcb) 504 { 505 CCTRACE(CC_API_DEBUG, "Bcb=%p\n", Bcb); 506 507 CcUnpinDataForThread(Bcb, (ERESOURCE_THREAD)PsGetCurrentThread()); 508 } 509 510 /* 511 * @unimplemented 512 */ 513 VOID 514 NTAPI 515 CcUnpinDataForThread ( 516 IN PVOID Bcb, 517 IN ERESOURCE_THREAD ResourceThreadId) 518 { 519 PINTERNAL_BCB iBcb = Bcb; 520 521 CCTRACE(CC_API_DEBUG, "Bcb=%p ResourceThreadId=%lu\n", Bcb, ResourceThreadId); 522 523 if (iBcb->PinCount != 0) 524 { 525 ExReleaseResourceForThreadLite(&iBcb->Lock, ResourceThreadId); 526 iBcb->PinCount--; 527 } 528 529 if (--iBcb->RefCount == 0) 530 { 531 KIRQL OldIrql; 532 PROS_SHARED_CACHE_MAP SharedCacheMap; 533 534 ASSERT(iBcb->PinCount == 0); 535 SharedCacheMap = iBcb->Vacb->SharedCacheMap; 536 CcRosReleaseVacb(SharedCacheMap, 537 iBcb->Vacb, 538 TRUE, 539 iBcb->Dirty, 540 FALSE); 541 542 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql); 543 if (!IsListEmpty(&iBcb->BcbEntry)) 544 { 545 RemoveEntryList(&iBcb->BcbEntry); 546 } 547 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql); 548 549 ExDeleteResourceLite(&iBcb->Lock); 550 ExFreeToNPagedLookasideList(&iBcbLookasideList, iBcb); 551 } 552 } 553 554 /* 555 * @implemented 556 */ 557 VOID 558 NTAPI 559 CcRepinBcb ( 560 IN PVOID Bcb) 561 { 562 PINTERNAL_BCB iBcb = Bcb; 563 564 CCTRACE(CC_API_DEBUG, "Bcb=%p\n", Bcb); 565 566 iBcb->RefCount++; 567 } 568 569 /* 570 * @unimplemented 571 */ 572 VOID 573 NTAPI 574 CcUnpinRepinnedBcb ( 575 IN PVOID Bcb, 576 IN BOOLEAN WriteThrough, 577 IN PIO_STATUS_BLOCK IoStatus) 578 { 579 PINTERNAL_BCB iBcb = Bcb; 580 KIRQL OldIrql; 581 PROS_SHARED_CACHE_MAP SharedCacheMap; 582 583 CCTRACE(CC_API_DEBUG, "Bcb=%p WriteThrough=%d\n", Bcb, WriteThrough); 584 585 IoStatus->Status = STATUS_SUCCESS; 586 if (--iBcb->RefCount == 0) 587 { 588 IoStatus->Information = 0; 589 if (WriteThrough) 590 { 591 if (iBcb->Vacb->Dirty) 592 { 593 IoStatus->Status = CcRosFlushVacb(iBcb->Vacb); 594 } 595 else 596 { 597 IoStatus->Status = STATUS_SUCCESS; 598 } 599 } 600 else 601 { 602 IoStatus->Status = STATUS_SUCCESS; 603 } 604 605 if (iBcb->PinCount != 0) 606 { 607 ExReleaseResourceLite(&iBcb->Lock); 608 iBcb->PinCount--; 609 ASSERT(iBcb->PinCount == 0); 610 } 611 612 SharedCacheMap = iBcb->Vacb->SharedCacheMap; 613 CcRosReleaseVacb(iBcb->Vacb->SharedCacheMap, 614 iBcb->Vacb, 615 TRUE, 616 iBcb->Dirty, 617 FALSE); 618 619 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql); 620 if (!IsListEmpty(&iBcb->BcbEntry)) 621 { 622 RemoveEntryList(&iBcb->BcbEntry); 623 } 624 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql); 625 626 ExDeleteResourceLite(&iBcb->Lock); 627 ExFreeToNPagedLookasideList(&iBcbLookasideList, iBcb); 628 } 629 } 630