[NTOSKRNL] When pinning data, try to find an already pinned 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 KIRQL OldIrql;
303 BOOLEAN Result;
304 PINTERNAL_BCB iBcb;
305 PROS_SHARED_CACHE_MAP SharedCacheMap;
306
307 CCTRACE(CC_API_DEBUG, "FileOffset=%p FileOffset=%p Length=%lu Flags=0x%lx\n",
308 FileObject, FileOffset, Length, Flags);
309
310 ASSERT(FileObject);
311 ASSERT(FileObject->SectionObjectPointer);
312 ASSERT(FileObject->SectionObjectPointer->SharedCacheMap);
313
314 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
315 ASSERT(SharedCacheMap);
316
317 if (Flags & PIN_WAIT)
318 {
319 ++CcPinReadWait;
320 }
321 else
322 {
323 ++CcPinReadNoWait;
324 }
325
326 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql);
327 iBcb = CcpFindBcb(SharedCacheMap, FileOffset, Length, TRUE);
328 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
329
330 if (iBcb == NULL)
331 {
332 /* Map first */
333 if (!CcpMapData(SharedCacheMap, FileOffset, Length, Flags, Bcb, Buffer))
334 {
335 return FALSE;
336 }
337
338 /* Pin then */
339 if (!CcPinMappedData(FileObject, FileOffset, Length, Flags, Bcb))
340 {
341 CcUnpinData(*Bcb);
342 return FALSE;
343 }
344 }
345 /* We found a BCB, lock it and return it */
346 else
347 {
348 if (BooleanFlagOn(Flags, PIN_EXCLUSIVE))
349 {
350 Result = ExAcquireResourceExclusiveLite(&iBcb->Lock, BooleanFlagOn(Flags, PIN_WAIT));
351 }
352 else
353 {
354 Result = ExAcquireSharedStarveExclusive(&iBcb->Lock, BooleanFlagOn(Flags, PIN_WAIT));
355 }
356
357 if (!Result)
358 {
359 return FALSE;
360 }
361
362 ++iBcb->PinCount;
363 ++iBcb->RefCount;
364
365 *Bcb = iBcb;
366 *Buffer = (PUCHAR)iBcb->Vacb->BaseAddress + FileOffset->QuadPart % VACB_MAPPING_GRANULARITY;
367 }
368
369 return TRUE;
370 }
371
372 /*
373 * @unimplemented
374 */
375 BOOLEAN
376 NTAPI
377 CcPreparePinWrite (
378 IN PFILE_OBJECT FileObject,
379 IN PLARGE_INTEGER FileOffset,
380 IN ULONG Length,
381 IN BOOLEAN Zero,
382 IN ULONG Flags,
383 OUT PVOID * Bcb,
384 OUT PVOID * Buffer)
385 {
386 CCTRACE(CC_API_DEBUG, "FileOffset=%p FileOffset=%p Length=%lu Zero=%d Flags=0x%lx\n",
387 FileObject, FileOffset, Length, Zero, Flags);
388
389 /*
390 * FIXME: This is function is similar to CcPinRead, but doesn't
391 * read the data if they're not present. Instead it should just
392 * prepare the VACBs and zero them out if Zero != FALSE.
393 *
394 * For now calling CcPinRead is better than returning error or
395 * just having UNIMPLEMENTED here.
396 */
397 return CcPinRead(FileObject, FileOffset, Length, Flags, Bcb, Buffer);
398 }
399
400 /*
401 * @implemented
402 */
403 VOID NTAPI
404 CcSetDirtyPinnedData (
405 IN PVOID Bcb,
406 IN PLARGE_INTEGER Lsn)
407 {
408 PINTERNAL_BCB iBcb = Bcb;
409
410 CCTRACE(CC_API_DEBUG, "Bcb=%p Lsn=%p\n",
411 Bcb, Lsn);
412
413 iBcb->Dirty = TRUE;
414 if (!iBcb->Vacb->Dirty)
415 {
416 CcRosMarkDirtyVacb(iBcb->Vacb);
417 }
418 }
419
420
421 /*
422 * @implemented
423 */
424 VOID NTAPI
425 CcUnpinData (
426 IN PVOID Bcb)
427 {
428 CCTRACE(CC_API_DEBUG, "Bcb=%p\n", Bcb);
429
430 CcUnpinDataForThread(Bcb, (ERESOURCE_THREAD)PsGetCurrentThread());
431 }
432
433 /*
434 * @unimplemented
435 */
436 VOID
437 NTAPI
438 CcUnpinDataForThread (
439 IN PVOID Bcb,
440 IN ERESOURCE_THREAD ResourceThreadId)
441 {
442 PINTERNAL_BCB iBcb = Bcb;
443
444 CCTRACE(CC_API_DEBUG, "Bcb=%p ResourceThreadId=%lu\n", Bcb, ResourceThreadId);
445
446 if (iBcb->PinCount != 0)
447 {
448 ExReleaseResourceForThreadLite(&iBcb->Lock, ResourceThreadId);
449 iBcb->PinCount--;
450 }
451
452 if (--iBcb->RefCount == 0)
453 {
454 KIRQL OldIrql;
455 PROS_SHARED_CACHE_MAP SharedCacheMap;
456
457 ASSERT(iBcb->PinCount == 0);
458 SharedCacheMap = iBcb->Vacb->SharedCacheMap;
459 CcRosReleaseVacb(SharedCacheMap,
460 iBcb->Vacb,
461 TRUE,
462 iBcb->Dirty,
463 FALSE);
464
465 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql);
466 RemoveEntryList(&iBcb->BcbEntry);
467 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
468
469 ExDeleteResourceLite(&iBcb->Lock);
470 ExFreeToNPagedLookasideList(&iBcbLookasideList, iBcb);
471 }
472 }
473
474 /*
475 * @implemented
476 */
477 VOID
478 NTAPI
479 CcRepinBcb (
480 IN PVOID Bcb)
481 {
482 PINTERNAL_BCB iBcb = Bcb;
483
484 CCTRACE(CC_API_DEBUG, "Bcb=%p\n", Bcb);
485
486 iBcb->RefCount++;
487 }
488
489 /*
490 * @unimplemented
491 */
492 VOID
493 NTAPI
494 CcUnpinRepinnedBcb (
495 IN PVOID Bcb,
496 IN BOOLEAN WriteThrough,
497 IN PIO_STATUS_BLOCK IoStatus)
498 {
499 PINTERNAL_BCB iBcb = Bcb;
500 KIRQL OldIrql;
501 PROS_SHARED_CACHE_MAP SharedCacheMap;
502
503 CCTRACE(CC_API_DEBUG, "Bcb=%p WriteThrough=%d\n", Bcb, WriteThrough);
504
505 IoStatus->Status = STATUS_SUCCESS;
506 if (--iBcb->RefCount == 0)
507 {
508 IoStatus->Information = 0;
509 if (WriteThrough)
510 {
511 if (iBcb->Vacb->Dirty)
512 {
513 IoStatus->Status = CcRosFlushVacb(iBcb->Vacb);
514 }
515 else
516 {
517 IoStatus->Status = STATUS_SUCCESS;
518 }
519 }
520 else
521 {
522 IoStatus->Status = STATUS_SUCCESS;
523 }
524
525 if (iBcb->PinCount != 0)
526 {
527 ExReleaseResourceLite(&iBcb->Lock);
528 iBcb->PinCount--;
529 ASSERT(iBcb->PinCount == 0);
530 }
531
532 SharedCacheMap = iBcb->Vacb->SharedCacheMap;
533 CcRosReleaseVacb(iBcb->Vacb->SharedCacheMap,
534 iBcb->Vacb,
535 TRUE,
536 iBcb->Dirty,
537 FALSE);
538
539 KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql);
540 RemoveEntryList(&iBcb->BcbEntry);
541 KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
542
543 ExDeleteResourceLite(&iBcb->Lock);
544 ExFreeToNPagedLookasideList(&iBcbLookasideList, iBcb);
545 }
546 }