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