xref: /reactos/ntoskrnl/cc/pin.c (revision cf8ba3bd)
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