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