[NTOSKRNL] Don't keep the spin lock hold too long when we lost the BCB race
[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 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 }