[NTOSKRNL] When mapping data, try to find if there's already a BCB
[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 BOOLEAN
36 NTAPI
37 CcpMapData(
38 IN PROS_SHARED_CACHE_MAP SharedCacheMap,
39 IN PLARGE_INTEGER FileOffset,
40 IN ULONG Length,
41 IN ULONG Flags,
42 OUT PVOID *pBcb,
43 OUT PVOID *pBuffer)
44 {
45 LONGLONG ReadOffset;
46 BOOLEAN Valid;
47 PROS_VACB Vacb;
48 NTSTATUS Status;
49 PINTERNAL_BCB iBcb;
50 LONGLONG ROffset;
51 KIRQL OldIrql;
52
53 ReadOffset = FileOffset->QuadPart;
54
55 DPRINT("SectionSize %I64x, FileSize %I64x\n",
56 SharedCacheMap->SectionSize.QuadPart,
57 SharedCacheMap->FileSize.QuadPart);
58
59 if (ReadOffset % VACB_MAPPING_GRANULARITY + Length > VACB_MAPPING_GRANULARITY)
60 {
61 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%p Length=%lu Flags=0x%lx -> FALSE\n",
62 SharedCacheMap->FileObject, FileOffset, Length, Flags);
63 return FALSE;
64 }
65
66 if (!BooleanFlagOn(Flags, MAP_NO_READ))
67 {
68 static int Warned = 0;
69
70 SetFlag(Flags, MAP_NO_READ);
71 if (!Warned)
72 {
73 DPRINT1("Mapping/pinning with no read not implemented. Forcing read, might fail if wait not allowed\n");
74 Warned++;
75 }
76 }
77
78 ROffset = ROUND_DOWN(ReadOffset, VACB_MAPPING_GRANULARITY);
79 Status = CcRosRequestVacb(SharedCacheMap,
80 ROffset,
81 pBuffer,
82 &Valid,
83 &Vacb);
84 if (!NT_SUCCESS(Status))
85 {
86 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%p Length=%lu Flags=0x%lx -> FALSE\n",
87 SharedCacheMap->FileObject, FileOffset, Length, Flags);
88 ExRaiseStatus(Status);
89 return FALSE;
90 }
91
92 if (!Valid && BooleanFlagOn(Flags, MAP_NO_READ))
93 {
94 if (!BooleanFlagOn(Flags, MAP_WAIT))
95 {
96 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
97 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%p Length=%lu Flags=0x%lx -> FALSE\n",
98 SharedCacheMap->FileObject, FileOffset, Length, Flags);
99 return FALSE;
100 }
101
102 Status = CcReadVirtualAddress(Vacb);
103 if (!NT_SUCCESS(Status))
104 {
105 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
106 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%p Length=%lu Flags=0x%lx -> FALSE\n",
107 SharedCacheMap->FileObject, FileOffset, Length, Flags);
108 ExRaiseStatus(Status);
109 return FALSE;
110 }
111 }
112
113 *pBuffer = (PUCHAR)*pBuffer + ReadOffset % VACB_MAPPING_GRANULARITY;
114 iBcb = ExAllocateFromNPagedLookasideList(&iBcbLookasideList);
115 if (iBcb == NULL)
116 {
117 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE);
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_INSUFFICIENT_RESOURCES);
121 return FALSE;
122 }
123
124 RtlZeroMemory(iBcb, sizeof(*iBcb));
125 iBcb->PFCB.NodeTypeCode = 0xDE45; /* Undocumented (CAPTIVE_PUBLIC_BCB_NODETYPECODE) */
126 iBcb->PFCB.NodeByteSize = sizeof(PUBLIC_BCB);
127 iBcb->PFCB.MappedLength = Length;
128 iBcb->PFCB.MappedFileOffset = *FileOffset;
129 iBcb->Vacb = Vacb;
130 iBcb->Dirty = FALSE;
131 iBcb->PinCount = 0;
132 iBcb->RefCount = 1;
133 ExInitializeResourceLite(&iBcb->Lock);
134 *pBcb = (PVOID)iBcb;
135
136 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql);
137 InsertTailList(&SharedCacheMap->BcbList, &iBcb->BcbEntry);
138 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
139
140 return TRUE;
141 }
142
143 static
144 PINTERNAL_BCB
145 NTAPI
146 CcpFindBcb(
147 IN PROS_SHARED_CACHE_MAP SharedCacheMap,
148 IN PLARGE_INTEGER FileOffset,
149 IN ULONG Length,
150 IN BOOLEAN Pinned)
151 {
152 PINTERNAL_BCB Bcb;
153 BOOLEAN Found = FALSE;
154 PLIST_ENTRY NextEntry;
155
156 for (NextEntry = SharedCacheMap->BcbList.Flink;
157 NextEntry != &SharedCacheMap->BcbList;
158 NextEntry = NextEntry->Flink)
159 {
160 Bcb = CONTAINING_RECORD(NextEntry, INTERNAL_BCB, BcbEntry);
161
162 if (Bcb->PFCB.MappedFileOffset.QuadPart <= FileOffset->QuadPart &&
163 (Bcb->PFCB.MappedFileOffset.QuadPart + Bcb->PFCB.MappedLength) >=
164 (FileOffset->QuadPart + Length))
165 {
166 if ((Pinned && Bcb->PinCount > 0) || (!Pinned && Bcb->PinCount == 0))
167 {
168 Found = TRUE;
169 break;
170 }
171 }
172 }
173
174 return (Found ? Bcb : NULL);
175 }
176
177 /*
178 * @implemented
179 */
180 BOOLEAN
181 NTAPI
182 CcMapData (
183 IN PFILE_OBJECT FileObject,
184 IN PLARGE_INTEGER FileOffset,
185 IN ULONG Length,
186 IN ULONG Flags,
187 OUT PVOID *pBcb,
188 OUT PVOID *pBuffer)
189 {
190 BOOLEAN Ret;
191 KIRQL OldIrql;
192 PINTERNAL_BCB iBcb;
193 PROS_SHARED_CACHE_MAP SharedCacheMap;
194
195 DPRINT("CcMapData(FileObject 0x%p, FileOffset %I64x, Length %lu, Flags 0x%lx,"
196 " pBcb 0x%p, pBuffer 0x%p)\n", FileObject, FileOffset->QuadPart,
197 Length, Flags, pBcb, pBuffer);
198
199 ASSERT(FileObject);
200 ASSERT(FileObject->SectionObjectPointer);
201 ASSERT(FileObject->SectionObjectPointer->SharedCacheMap);
202
203 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
204 ASSERT(SharedCacheMap);
205
206 if (Flags & MAP_WAIT)
207 {
208 ++CcMapDataWait;
209 }
210 else
211 {
212 ++CcMapDataNoWait;
213 }
214
215 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql);
216 iBcb = CcpFindBcb(SharedCacheMap, FileOffset, Length, FALSE);
217 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
218
219 if (iBcb == NULL)
220 {
221 Ret = CcpMapData(SharedCacheMap, FileOffset, Length, Flags, pBcb, pBuffer);
222 }
223 else
224 {
225 ++iBcb->RefCount;
226 *pBcb = iBcb;
227 *pBuffer = (PUCHAR)iBcb->Vacb->BaseAddress + FileOffset->QuadPart % VACB_MAPPING_GRANULARITY;
228 Ret = TRUE;
229 }
230
231 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%p Length=%lu Flags=0x%lx -> %d Bcb=%p\n",
232 FileObject, FileOffset, Length, Flags, Ret, *pBcb);
233 return Ret;
234 }
235
236 /*
237 * @unimplemented
238 */
239 BOOLEAN
240 NTAPI
241 CcPinMappedData (
242 IN PFILE_OBJECT FileObject,
243 IN PLARGE_INTEGER FileOffset,
244 IN ULONG Length,
245 IN ULONG Flags,
246 OUT PVOID * Bcb)
247 {
248 BOOLEAN Result;
249 PINTERNAL_BCB iBcb;
250 PROS_SHARED_CACHE_MAP SharedCacheMap;
251
252 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%p Length=%lu Flags=0x%lx\n",
253 FileObject, FileOffset, Length, Flags);
254
255 ASSERT(FileObject);
256 ASSERT(FileObject->SectionObjectPointer);
257 ASSERT(FileObject->SectionObjectPointer->SharedCacheMap);
258
259 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
260 ASSERT(SharedCacheMap);
261 if (!SharedCacheMap->PinAccess)
262 {
263 DPRINT1("FIXME: Pinning a file with no pin access!\n");
264 return FALSE;
265 }
266
267 iBcb = *Bcb;
268 ASSERT(iBcb->PinCount == 0);
269
270 iBcb->PinCount++;
271
272 if (BooleanFlagOn(Flags, PIN_EXCLUSIVE))
273 {
274 Result = ExAcquireResourceExclusiveLite(&iBcb->Lock, BooleanFlagOn(Flags, PIN_WAIT));
275 }
276 else
277 {
278 Result = ExAcquireSharedStarveExclusive(&iBcb->Lock, BooleanFlagOn(Flags, PIN_WAIT));
279 }
280
281 if (!Result)
282 {
283 iBcb->PinCount--;
284 }
285
286 return Result;
287 }
288
289 /*
290 * @unimplemented
291 */
292 BOOLEAN
293 NTAPI
294 CcPinRead (
295 IN PFILE_OBJECT FileObject,
296 IN PLARGE_INTEGER FileOffset,
297 IN ULONG Length,
298 IN ULONG Flags,
299 OUT PVOID * Bcb,
300 OUT PVOID * Buffer)
301 {
302 PROS_SHARED_CACHE_MAP SharedCacheMap;
303
304 CCTRACE(CC_API_DEBUG, "FileOffset=%p FileOffset=%p Length=%lu Flags=0x%lx\n",
305 FileObject, FileOffset, Length, Flags);
306
307 ASSERT(FileObject);
308 ASSERT(FileObject->SectionObjectPointer);
309 ASSERT(FileObject->SectionObjectPointer->SharedCacheMap);
310
311 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
312 ASSERT(SharedCacheMap);
313
314 if (Flags & PIN_WAIT)
315 {
316 ++CcPinReadWait;
317 }
318 else
319 {
320 ++CcPinReadNoWait;
321 }
322
323 /* Map first */
324 if (!CcpMapData(SharedCacheMap, FileOffset, Length, Flags, Bcb, Buffer))
325 {
326 return FALSE;
327 }
328
329 /* Pin then */
330 if (!CcPinMappedData(FileObject, FileOffset, Length, Flags, Bcb))
331 {
332 CcUnpinData(*Bcb);
333 return FALSE;
334 }
335
336 return TRUE;
337 }
338
339 /*
340 * @unimplemented
341 */
342 BOOLEAN
343 NTAPI
344 CcPreparePinWrite (
345 IN PFILE_OBJECT FileObject,
346 IN PLARGE_INTEGER FileOffset,
347 IN ULONG Length,
348 IN BOOLEAN Zero,
349 IN ULONG Flags,
350 OUT PVOID * Bcb,
351 OUT PVOID * Buffer)
352 {
353 CCTRACE(CC_API_DEBUG, "FileOffset=%p FileOffset=%p Length=%lu Zero=%d Flags=0x%lx\n",
354 FileObject, FileOffset, Length, Zero, Flags);
355
356 /*
357 * FIXME: This is function is similar to CcPinRead, but doesn't
358 * read the data if they're not present. Instead it should just
359 * prepare the VACBs and zero them out if Zero != FALSE.
360 *
361 * For now calling CcPinRead is better than returning error or
362 * just having UNIMPLEMENTED here.
363 */
364 return CcPinRead(FileObject, FileOffset, Length, Flags, Bcb, Buffer);
365 }
366
367 /*
368 * @implemented
369 */
370 VOID NTAPI
371 CcSetDirtyPinnedData (
372 IN PVOID Bcb,
373 IN PLARGE_INTEGER Lsn)
374 {
375 PINTERNAL_BCB iBcb = Bcb;
376
377 CCTRACE(CC_API_DEBUG, "Bcb=%p Lsn=%p\n",
378 Bcb, Lsn);
379
380 iBcb->Dirty = TRUE;
381 if (!iBcb->Vacb->Dirty)
382 {
383 CcRosMarkDirtyVacb(iBcb->Vacb);
384 }
385 }
386
387
388 /*
389 * @implemented
390 */
391 VOID NTAPI
392 CcUnpinData (
393 IN PVOID Bcb)
394 {
395 CCTRACE(CC_API_DEBUG, "Bcb=%p\n", Bcb);
396
397 CcUnpinDataForThread(Bcb, (ERESOURCE_THREAD)PsGetCurrentThread());
398 }
399
400 /*
401 * @unimplemented
402 */
403 VOID
404 NTAPI
405 CcUnpinDataForThread (
406 IN PVOID Bcb,
407 IN ERESOURCE_THREAD ResourceThreadId)
408 {
409 PINTERNAL_BCB iBcb = Bcb;
410
411 CCTRACE(CC_API_DEBUG, "Bcb=%p ResourceThreadId=%lu\n", Bcb, ResourceThreadId);
412
413 if (iBcb->PinCount != 0)
414 {
415 ExReleaseResourceForThreadLite(&iBcb->Lock, ResourceThreadId);
416 iBcb->PinCount--;
417 }
418
419 if (--iBcb->RefCount == 0)
420 {
421 KIRQL OldIrql;
422 PROS_SHARED_CACHE_MAP SharedCacheMap;
423
424 ASSERT(iBcb->PinCount == 0);
425 SharedCacheMap = iBcb->Vacb->SharedCacheMap;
426 CcRosReleaseVacb(SharedCacheMap,
427 iBcb->Vacb,
428 TRUE,
429 iBcb->Dirty,
430 FALSE);
431
432 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql);
433 RemoveEntryList(&iBcb->BcbEntry);
434 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
435
436 ExDeleteResourceLite(&iBcb->Lock);
437 ExFreeToNPagedLookasideList(&iBcbLookasideList, iBcb);
438 }
439 }
440
441 /*
442 * @implemented
443 */
444 VOID
445 NTAPI
446 CcRepinBcb (
447 IN PVOID Bcb)
448 {
449 PINTERNAL_BCB iBcb = Bcb;
450
451 CCTRACE(CC_API_DEBUG, "Bcb=%p\n", Bcb);
452
453 iBcb->RefCount++;
454 }
455
456 /*
457 * @unimplemented
458 */
459 VOID
460 NTAPI
461 CcUnpinRepinnedBcb (
462 IN PVOID Bcb,
463 IN BOOLEAN WriteThrough,
464 IN PIO_STATUS_BLOCK IoStatus)
465 {
466 PINTERNAL_BCB iBcb = Bcb;
467 KIRQL OldIrql;
468 PROS_SHARED_CACHE_MAP SharedCacheMap;
469
470 CCTRACE(CC_API_DEBUG, "Bcb=%p WriteThrough=%d\n", Bcb, WriteThrough);
471
472 IoStatus->Status = STATUS_SUCCESS;
473 if (--iBcb->RefCount == 0)
474 {
475 IoStatus->Information = 0;
476 if (WriteThrough)
477 {
478 if (iBcb->Vacb->Dirty)
479 {
480 IoStatus->Status = CcRosFlushVacb(iBcb->Vacb);
481 }
482 else
483 {
484 IoStatus->Status = STATUS_SUCCESS;
485 }
486 }
487 else
488 {
489 IoStatus->Status = STATUS_SUCCESS;
490 }
491
492 if (iBcb->PinCount != 0)
493 {
494 ExReleaseResourceLite(&iBcb->Lock);
495 iBcb->PinCount--;
496 ASSERT(iBcb->PinCount == 0);
497 }
498
499 SharedCacheMap = iBcb->Vacb->SharedCacheMap;
500 CcRosReleaseVacb(iBcb->Vacb->SharedCacheMap,
501 iBcb->Vacb,
502 TRUE,
503 iBcb->Dirty,
504 FALSE);
505
506 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql);
507 RemoveEntryList(&iBcb->BcbEntry);
508 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
509
510 ExDeleteResourceLite(&iBcb->Lock);
511 ExFreeToNPagedLookasideList(&iBcbLookasideList, iBcb);
512 }
513 }