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 /* Free our now unused BCB */ 389 CcUnpinData(*Bcb); 390 391 /* Lock the found BCB and return it */ 392 if (BooleanFlagOn(Flags, PIN_EXCLUSIVE)) 393 { 394 Result = ExAcquireResourceExclusiveLite(&iBcb->Lock, BooleanFlagOn(Flags, PIN_WAIT)); 395 } 396 else 397 { 398 Result = ExAcquireSharedStarveExclusive(&iBcb->Lock, BooleanFlagOn(Flags, PIN_WAIT)); 399 } 400 401 if (!Result) 402 { 403 return FALSE; 404 } 405 406 ++iBcb->PinCount; 407 ++iBcb->RefCount; 408 409 *Bcb = iBcb; 410 } 411 else 412 { 413 iBcb = *Bcb; 414 415 /* Insert ourselves in the linked list */ 416 InsertTailList(&SharedCacheMap->BcbList, &iBcb->BcbEntry); 417 } 418 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql); 419 } 420 /* We found a BCB, lock it and return it */ 421 else 422 { 423 if (BooleanFlagOn(Flags, PIN_EXCLUSIVE)) 424 { 425 Result = ExAcquireResourceExclusiveLite(&iBcb->Lock, BooleanFlagOn(Flags, PIN_WAIT)); 426 } 427 else 428 { 429 Result = ExAcquireSharedStarveExclusive(&iBcb->Lock, BooleanFlagOn(Flags, PIN_WAIT)); 430 } 431 432 if (!Result) 433 { 434 return FALSE; 435 } 436 437 ++iBcb->PinCount; 438 ++iBcb->RefCount; 439 440 *Bcb = iBcb; 441 *Buffer = (PUCHAR)iBcb->Vacb->BaseAddress + FileOffset->QuadPart % VACB_MAPPING_GRANULARITY; 442 } 443 444 return TRUE; 445 } 446 447 /* 448 * @unimplemented 449 */ 450 BOOLEAN 451 NTAPI 452 CcPreparePinWrite ( 453 IN PFILE_OBJECT FileObject, 454 IN PLARGE_INTEGER FileOffset, 455 IN ULONG Length, 456 IN BOOLEAN Zero, 457 IN ULONG Flags, 458 OUT PVOID * Bcb, 459 OUT PVOID * Buffer) 460 { 461 CCTRACE(CC_API_DEBUG, "FileOffset=%p FileOffset=%p Length=%lu Zero=%d Flags=0x%lx\n", 462 FileObject, FileOffset, Length, Zero, Flags); 463 464 /* 465 * FIXME: This is function is similar to CcPinRead, but doesn't 466 * read the data if they're not present. Instead it should just 467 * prepare the VACBs and zero them out if Zero != FALSE. 468 * 469 * For now calling CcPinRead is better than returning error or 470 * just having UNIMPLEMENTED here. 471 */ 472 return CcPinRead(FileObject, FileOffset, Length, Flags, Bcb, Buffer); 473 } 474 475 /* 476 * @implemented 477 */ 478 VOID NTAPI 479 CcSetDirtyPinnedData ( 480 IN PVOID Bcb, 481 IN PLARGE_INTEGER Lsn) 482 { 483 PINTERNAL_BCB iBcb = Bcb; 484 485 CCTRACE(CC_API_DEBUG, "Bcb=%p Lsn=%p\n", 486 Bcb, Lsn); 487 488 iBcb->Dirty = TRUE; 489 if (!iBcb->Vacb->Dirty) 490 { 491 CcRosMarkDirtyVacb(iBcb->Vacb); 492 } 493 } 494 495 496 /* 497 * @implemented 498 */ 499 VOID NTAPI 500 CcUnpinData ( 501 IN PVOID Bcb) 502 { 503 CCTRACE(CC_API_DEBUG, "Bcb=%p\n", Bcb); 504 505 CcUnpinDataForThread(Bcb, (ERESOURCE_THREAD)PsGetCurrentThread()); 506 } 507 508 /* 509 * @unimplemented 510 */ 511 VOID 512 NTAPI 513 CcUnpinDataForThread ( 514 IN PVOID Bcb, 515 IN ERESOURCE_THREAD ResourceThreadId) 516 { 517 PINTERNAL_BCB iBcb = Bcb; 518 519 CCTRACE(CC_API_DEBUG, "Bcb=%p ResourceThreadId=%lu\n", Bcb, ResourceThreadId); 520 521 if (iBcb->PinCount != 0) 522 { 523 ExReleaseResourceForThreadLite(&iBcb->Lock, ResourceThreadId); 524 iBcb->PinCount--; 525 } 526 527 if (--iBcb->RefCount == 0) 528 { 529 KIRQL OldIrql; 530 PROS_SHARED_CACHE_MAP SharedCacheMap; 531 532 ASSERT(iBcb->PinCount == 0); 533 SharedCacheMap = iBcb->Vacb->SharedCacheMap; 534 CcRosReleaseVacb(SharedCacheMap, 535 iBcb->Vacb, 536 TRUE, 537 iBcb->Dirty, 538 FALSE); 539 540 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql); 541 if (!IsListEmpty(&iBcb->BcbEntry)) 542 { 543 RemoveEntryList(&iBcb->BcbEntry); 544 } 545 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql); 546 547 ExDeleteResourceLite(&iBcb->Lock); 548 ExFreeToNPagedLookasideList(&iBcbLookasideList, iBcb); 549 } 550 } 551 552 /* 553 * @implemented 554 */ 555 VOID 556 NTAPI 557 CcRepinBcb ( 558 IN PVOID Bcb) 559 { 560 PINTERNAL_BCB iBcb = Bcb; 561 562 CCTRACE(CC_API_DEBUG, "Bcb=%p\n", Bcb); 563 564 iBcb->RefCount++; 565 } 566 567 /* 568 * @unimplemented 569 */ 570 VOID 571 NTAPI 572 CcUnpinRepinnedBcb ( 573 IN PVOID Bcb, 574 IN BOOLEAN WriteThrough, 575 IN PIO_STATUS_BLOCK IoStatus) 576 { 577 PINTERNAL_BCB iBcb = Bcb; 578 KIRQL OldIrql; 579 PROS_SHARED_CACHE_MAP SharedCacheMap; 580 581 CCTRACE(CC_API_DEBUG, "Bcb=%p WriteThrough=%d\n", Bcb, WriteThrough); 582 583 IoStatus->Status = STATUS_SUCCESS; 584 if (--iBcb->RefCount == 0) 585 { 586 IoStatus->Information = 0; 587 if (WriteThrough) 588 { 589 if (iBcb->Vacb->Dirty) 590 { 591 IoStatus->Status = CcRosFlushVacb(iBcb->Vacb); 592 } 593 else 594 { 595 IoStatus->Status = STATUS_SUCCESS; 596 } 597 } 598 else 599 { 600 IoStatus->Status = STATUS_SUCCESS; 601 } 602 603 if (iBcb->PinCount != 0) 604 { 605 ExReleaseResourceLite(&iBcb->Lock); 606 iBcb->PinCount--; 607 ASSERT(iBcb->PinCount == 0); 608 } 609 610 SharedCacheMap = iBcb->Vacb->SharedCacheMap; 611 CcRosReleaseVacb(iBcb->Vacb->SharedCacheMap, 612 iBcb->Vacb, 613 TRUE, 614 iBcb->Dirty, 615 FALSE); 616 617 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql); 618 if (!IsListEmpty(&iBcb->BcbEntry)) 619 { 620 RemoveEntryList(&iBcb->BcbEntry); 621 } 622 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql); 623 624 ExDeleteResourceLite(&iBcb->Lock); 625 ExFreeToNPagedLookasideList(&iBcbLookasideList, iBcb); 626 } 627 } 628