[NTOSKRNL] Translate pinning flags to mapping flags when first mapping a file
[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 ULONG MapFlags;
271
272 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql);
273 NewBcb = CcpFindBcb(SharedCacheMap, FileOffset, Length, TRUE);
274 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
275
276 if (NewBcb != NULL)
277 {
278 NewBcb->PinCount++;
279
280 if (BooleanFlagOn(Flags, PIN_EXCLUSIVE))
281 {
282 Result = ExAcquireResourceExclusiveLite(&NewBcb->Lock, BooleanFlagOn(Flags, PIN_WAIT));
283 }
284 else
285 {
286 Result = ExAcquireSharedStarveExclusive(&NewBcb->Lock, BooleanFlagOn(Flags, PIN_WAIT));
287 }
288
289 if (!Result)
290 {
291 NewBcb->PinCount--;
292 }
293 else
294 {
295 NewBcb->RefCount++;
296 *Bcb = NewBcb;
297 *Buffer = (PUCHAR)NewBcb->Vacb->BaseAddress + FileOffset->QuadPart % VACB_MAPPING_GRANULARITY;
298 }
299
300 return Result;
301 }
302 else
303 {
304 if (BooleanFlagOn(Flags, PIN_IF_BCB))
305 {
306 return FALSE;
307 }
308
309 MapFlags = Flags & PIN_WAIT;
310 if (BooleanFlagOn(Flags, PIN_NO_READ))
311 {
312 SetFlag(MapFlags, MAP_NO_READ);
313 }
314
315 Result = CcpMapData(SharedCacheMap, FileOffset, Length, MapFlags, &Vacb, Buffer);
316 if (Result)
317 {
318 NewBcb = CcpGetAppropriateBcb(SharedCacheMap, Vacb, FileOffset, Length, Flags, TRUE);
319 if (NewBcb == NULL)
320 {
321 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE);
322 Result = FALSE;
323 }
324 else
325 {
326 *Bcb = NewBcb;
327 }
328 }
329 }
330
331 return Result;
332 }
333
334 /*
335 * @implemented
336 */
337 BOOLEAN
338 NTAPI
339 CcMapData (
340 IN PFILE_OBJECT FileObject,
341 IN PLARGE_INTEGER FileOffset,
342 IN ULONG Length,
343 IN ULONG Flags,
344 OUT PVOID *pBcb,
345 OUT PVOID *pBuffer)
346 {
347 BOOLEAN Ret;
348 KIRQL OldIrql;
349 PINTERNAL_BCB iBcb;
350 PROS_VACB Vacb;
351 PROS_SHARED_CACHE_MAP SharedCacheMap;
352
353 DPRINT("CcMapData(FileObject 0x%p, FileOffset %I64x, Length %lu, Flags 0x%lx,"
354 " pBcb 0x%p, pBuffer 0x%p)\n", FileObject, FileOffset->QuadPart,
355 Length, Flags, pBcb, pBuffer);
356
357 ASSERT(FileObject);
358 ASSERT(FileObject->SectionObjectPointer);
359 ASSERT(FileObject->SectionObjectPointer->SharedCacheMap);
360
361 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
362 ASSERT(SharedCacheMap);
363
364 if (Flags & MAP_WAIT)
365 {
366 ++CcMapDataWait;
367 }
368 else
369 {
370 ++CcMapDataNoWait;
371 }
372
373 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql);
374 iBcb = CcpFindBcb(SharedCacheMap, FileOffset, Length, FALSE);
375 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
376
377 if (iBcb == NULL)
378 {
379 Ret = CcpMapData(SharedCacheMap, FileOffset, Length, Flags, &Vacb, pBuffer);
380 if (Ret)
381 {
382 iBcb = CcpGetAppropriateBcb(SharedCacheMap, Vacb, FileOffset, Length, 0, FALSE);
383 if (iBcb == NULL)
384 {
385 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE);
386 Ret = FALSE;
387 }
388 else
389 {
390 *pBcb = iBcb;
391 }
392 }
393 }
394 else
395 {
396 ++iBcb->RefCount;
397 *pBcb = iBcb;
398 *pBuffer = (PUCHAR)iBcb->Vacb->BaseAddress + FileOffset->QuadPart % VACB_MAPPING_GRANULARITY;
399 Ret = TRUE;
400 }
401
402 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%p Length=%lu Flags=0x%lx -> %d Bcb=%p\n",
403 FileObject, FileOffset, Length, Flags, Ret, *pBcb);
404 return Ret;
405 }
406
407 /*
408 * @unimplemented
409 */
410 BOOLEAN
411 NTAPI
412 CcPinMappedData (
413 IN PFILE_OBJECT FileObject,
414 IN PLARGE_INTEGER FileOffset,
415 IN ULONG Length,
416 IN ULONG Flags,
417 OUT PVOID * Bcb)
418 {
419 BOOLEAN Result;
420 PVOID Buffer;
421 PINTERNAL_BCB iBcb;
422 PROS_SHARED_CACHE_MAP SharedCacheMap;
423
424 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%p Length=%lu Flags=0x%lx\n",
425 FileObject, FileOffset, Length, Flags);
426
427 ASSERT(FileObject);
428 ASSERT(FileObject->SectionObjectPointer);
429 ASSERT(FileObject->SectionObjectPointer->SharedCacheMap);
430
431 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
432 ASSERT(SharedCacheMap);
433 if (!SharedCacheMap->PinAccess)
434 {
435 DPRINT1("FIXME: Pinning a file with no pin access!\n");
436 return FALSE;
437 }
438
439 iBcb = *Bcb;
440
441 ++CcPinMappedDataCount;
442
443 Result = CcpPinData(SharedCacheMap, FileOffset, Length, Flags, Bcb, &Buffer);
444 if (Result)
445 {
446 CcUnpinData(iBcb);
447 }
448
449 return Result;
450 }
451
452 /*
453 * @unimplemented
454 */
455 BOOLEAN
456 NTAPI
457 CcPinRead (
458 IN PFILE_OBJECT FileObject,
459 IN PLARGE_INTEGER FileOffset,
460 IN ULONG Length,
461 IN ULONG Flags,
462 OUT PVOID * Bcb,
463 OUT PVOID * Buffer)
464 {
465 PROS_SHARED_CACHE_MAP SharedCacheMap;
466
467 CCTRACE(CC_API_DEBUG, "FileOffset=%p FileOffset=%p Length=%lu Flags=0x%lx\n",
468 FileObject, FileOffset, Length, Flags);
469
470 ASSERT(FileObject);
471 ASSERT(FileObject->SectionObjectPointer);
472 ASSERT(FileObject->SectionObjectPointer->SharedCacheMap);
473
474 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
475 ASSERT(SharedCacheMap);
476 if (!SharedCacheMap->PinAccess)
477 {
478 DPRINT1("FIXME: Pinning a file with no pin access!\n");
479 return FALSE;
480 }
481
482 if (Flags & PIN_WAIT)
483 {
484 ++CcPinReadWait;
485 }
486 else
487 {
488 ++CcPinReadNoWait;
489 }
490
491 return CcpPinData(SharedCacheMap, FileOffset, Length, Flags, Bcb, Buffer);
492 }
493
494 /*
495 * @unimplemented
496 */
497 BOOLEAN
498 NTAPI
499 CcPreparePinWrite (
500 IN PFILE_OBJECT FileObject,
501 IN PLARGE_INTEGER FileOffset,
502 IN ULONG Length,
503 IN BOOLEAN Zero,
504 IN ULONG Flags,
505 OUT PVOID * Bcb,
506 OUT PVOID * Buffer)
507 {
508 CCTRACE(CC_API_DEBUG, "FileOffset=%p FileOffset=%p Length=%lu Zero=%d Flags=0x%lx\n",
509 FileObject, FileOffset, Length, Zero, Flags);
510
511 /*
512 * FIXME: This is function is similar to CcPinRead, but doesn't
513 * read the data if they're not present. Instead it should just
514 * prepare the VACBs and zero them out if Zero != FALSE.
515 *
516 * For now calling CcPinRead is better than returning error or
517 * just having UNIMPLEMENTED here.
518 */
519 return CcPinRead(FileObject, FileOffset, Length, Flags, Bcb, Buffer);
520 }
521
522 /*
523 * @implemented
524 */
525 VOID NTAPI
526 CcSetDirtyPinnedData (
527 IN PVOID Bcb,
528 IN PLARGE_INTEGER Lsn)
529 {
530 PINTERNAL_BCB iBcb = Bcb;
531
532 CCTRACE(CC_API_DEBUG, "Bcb=%p Lsn=%p\n",
533 Bcb, Lsn);
534
535 iBcb->Dirty = TRUE;
536 if (!iBcb->Vacb->Dirty)
537 {
538 CcRosMarkDirtyVacb(iBcb->Vacb);
539 }
540 }
541
542
543 /*
544 * @implemented
545 */
546 VOID NTAPI
547 CcUnpinData (
548 IN PVOID Bcb)
549 {
550 CCTRACE(CC_API_DEBUG, "Bcb=%p\n", Bcb);
551
552 CcUnpinDataForThread(Bcb, (ERESOURCE_THREAD)PsGetCurrentThread());
553 }
554
555 /*
556 * @unimplemented
557 */
558 VOID
559 NTAPI
560 CcUnpinDataForThread (
561 IN PVOID Bcb,
562 IN ERESOURCE_THREAD ResourceThreadId)
563 {
564 PINTERNAL_BCB iBcb = Bcb;
565
566 CCTRACE(CC_API_DEBUG, "Bcb=%p ResourceThreadId=%lu\n", Bcb, ResourceThreadId);
567
568 if (iBcb->PinCount != 0)
569 {
570 ExReleaseResourceForThreadLite(&iBcb->Lock, ResourceThreadId);
571 iBcb->PinCount--;
572 }
573
574 if (--iBcb->RefCount == 0)
575 {
576 KIRQL OldIrql;
577 PROS_SHARED_CACHE_MAP SharedCacheMap;
578
579 ASSERT(iBcb->PinCount == 0);
580 SharedCacheMap = iBcb->Vacb->SharedCacheMap;
581 CcRosReleaseVacb(SharedCacheMap,
582 iBcb->Vacb,
583 TRUE,
584 iBcb->Dirty,
585 FALSE);
586
587 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql);
588 if (!IsListEmpty(&iBcb->BcbEntry))
589 {
590 RemoveEntryList(&iBcb->BcbEntry);
591 }
592 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
593
594 ExDeleteResourceLite(&iBcb->Lock);
595 ExFreeToNPagedLookasideList(&iBcbLookasideList, iBcb);
596 }
597 }
598
599 /*
600 * @implemented
601 */
602 VOID
603 NTAPI
604 CcRepinBcb (
605 IN PVOID Bcb)
606 {
607 PINTERNAL_BCB iBcb = Bcb;
608
609 CCTRACE(CC_API_DEBUG, "Bcb=%p\n", Bcb);
610
611 iBcb->RefCount++;
612 }
613
614 /*
615 * @unimplemented
616 */
617 VOID
618 NTAPI
619 CcUnpinRepinnedBcb (
620 IN PVOID Bcb,
621 IN BOOLEAN WriteThrough,
622 IN PIO_STATUS_BLOCK IoStatus)
623 {
624 PINTERNAL_BCB iBcb = Bcb;
625 KIRQL OldIrql;
626 PROS_SHARED_CACHE_MAP SharedCacheMap;
627
628 CCTRACE(CC_API_DEBUG, "Bcb=%p WriteThrough=%d\n", Bcb, WriteThrough);
629
630 IoStatus->Status = STATUS_SUCCESS;
631 if (--iBcb->RefCount == 0)
632 {
633 IoStatus->Information = 0;
634 if (WriteThrough)
635 {
636 if (iBcb->Vacb->Dirty)
637 {
638 IoStatus->Status = CcRosFlushVacb(iBcb->Vacb);
639 }
640 else
641 {
642 IoStatus->Status = STATUS_SUCCESS;
643 }
644 }
645 else
646 {
647 IoStatus->Status = STATUS_SUCCESS;
648 }
649
650 if (iBcb->PinCount != 0)
651 {
652 ExReleaseResourceLite(&iBcb->Lock);
653 iBcb->PinCount--;
654 ASSERT(iBcb->PinCount == 0);
655 }
656
657 SharedCacheMap = iBcb->Vacb->SharedCacheMap;
658 CcRosReleaseVacb(iBcb->Vacb->SharedCacheMap,
659 iBcb->Vacb,
660 TRUE,
661 iBcb->Dirty,
662 FALSE);
663
664 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql);
665 if (!IsListEmpty(&iBcb->BcbEntry))
666 {
667 RemoveEntryList(&iBcb->BcbEntry);
668 }
669 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
670
671 ExDeleteResourceLite(&iBcb->Lock);
672 ExFreeToNPagedLookasideList(&iBcbLookasideList, iBcb);
673 }
674 }