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