xref: /reactos/ntoskrnl/ex/rundown.c (revision c3483339)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Kernel
4  * FILE:            ntoskrnl/ex/rundown.c
5  * PURPOSE:         Rundown and Cache-Aware Rundown Protection
6  * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net)
7  *                  Thomas Weidenmueller
8  *                  Pierre Schweitzer
9  */
10 
11 /* INCLUDES *****************************************************************/
12 
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16 
17 /* FUNCTIONS *****************************************************************/
18 
19 /*++
20  * @name ExfAcquireRundownProtection
21  * @implemented NT5.1
22  *
23  *     The ExfAcquireRundownProtection routine acquires rundown protection for
24  *     the specified descriptor.
25  *
26  * @param RunRef
27  *        Pointer to a rundown reference descriptor.
28  *
29  * @return TRUE if access to the protected structure was granted, FALSE otherwise.
30  *
31  * @remarks Callers of ExfAcquireRundownProtection can be running at any IRQL.
32  *
33  *--*/
34 BOOLEAN
35 FASTCALL
36 ExfAcquireRundownProtection(IN PEX_RUNDOWN_REF RunRef)
37 {
38     ULONG_PTR Value = RunRef->Count, NewValue;
39 
40     /* Loop until successfully incremented the counter */
41     for (;;)
42     {
43         /* Make sure a rundown is not active */
44         if (Value & EX_RUNDOWN_ACTIVE) return FALSE;
45 
46         /* Add a reference */
47         NewValue = Value + EX_RUNDOWN_COUNT_INC;
48 
49         /* Change the value */
50         NewValue = ExpChangeRundown(RunRef, NewValue, Value);
51         if (NewValue == Value) return TRUE;
52 
53         /* Update it */
54         Value = NewValue;
55     }
56 }
57 
58 /*++
59  * @name ExfAcquireRundownProtectionEx
60  * @implemented NT5.2
61  *
62  *     The ExfAcquireRundownProtectionEx routine acquires multiple rundown
63  *     protection references for the specified descriptor.
64  *
65  * @param RunRef
66  *        Pointer to a rundown reference descriptor.
67  *
68  * @param Count
69  *         Number of times to reference the descriptor.
70  *
71  * @return TRUE if access to the protected structure was granted, FALSE otherwise.
72  *
73  * @remarks Callers of ExfAcquireRundownProtectionEx can be running at any IRQL.
74  *
75  *--*/
76 BOOLEAN
77 FASTCALL
78 ExfAcquireRundownProtectionEx(IN PEX_RUNDOWN_REF RunRef,
79                               IN ULONG Count)
80 {
81     ULONG_PTR Value = RunRef->Count, NewValue;
82 
83     /* Loop until successfully incremented the counter */
84     for (;;)
85     {
86         /* Make sure a rundown is not active */
87         if (Value & EX_RUNDOWN_ACTIVE) return FALSE;
88 
89         /* Add references */
90         NewValue = Value + EX_RUNDOWN_COUNT_INC * Count;
91 
92         /* Change the value */
93         NewValue = ExpChangeRundown(RunRef, NewValue, Value);
94         if (NewValue == Value) return TRUE;
95 
96         /* Update the value */
97         Value = NewValue;
98     }
99 }
100 
101 /*++
102  * @name ExfInitializeRundownProtection
103  * @implemented NT5.1
104  *
105  *     The ExfInitializeRundownProtection routine initializes a rundown
106  *     protection descriptor.
107  *
108  * @param RunRef
109  *        Pointer to a rundown reference descriptor.
110  *
111  * @return None.
112  *
113  * @remarks Callers of ExfInitializeRundownProtection can be running at any IRQL.
114  *
115  *--*/
116 VOID
117 FASTCALL
118 ExfInitializeRundownProtection(IN PEX_RUNDOWN_REF RunRef)
119 {
120     /* Set the count to zero */
121     RunRef->Count = 0;
122 }
123 
124 /*++
125  * @name ExfReInitializeRundownProtection
126  * @implemented NT5.1
127  *
128  *     The ExfReInitializeRundownProtection routine re-initializes a rundown
129  *     protection descriptor.
130  *
131  * @param RunRef
132  *        Pointer to a rundown reference descriptor.
133  *
134  * @return None.
135  *
136  * @remarks Callers of ExfReInitializeRundownProtection can be running at any IRQL.
137  *
138  *--*/
139 VOID
140 FASTCALL
141 ExfReInitializeRundownProtection(IN PEX_RUNDOWN_REF RunRef)
142 {
143     PAGED_CODE();
144 
145     /* Sanity check */
146     ASSERT((RunRef->Count & EX_RUNDOWN_ACTIVE) != 0);
147 
148     /* Reset the count */
149     ExpSetRundown(RunRef, 0);
150 }
151 
152 /*++
153  * @name ExfRundownCompleted
154  * @implemented NT5.1
155  *
156  *     The ExfRundownCompleted routine completes the rundown of the specified
157  *     descriptor by setting the active bit.
158  *
159  * @param RunRef
160  *        Pointer to a rundown reference descriptor.
161  *
162  * @return None.
163  *
164  * @remarks Callers of ExfRundownCompleted must be running at IRQL <= APC_LEVEL.
165  *
166  *--*/
167 VOID
168 FASTCALL
169 ExfRundownCompleted(IN PEX_RUNDOWN_REF RunRef)
170 {
171     PAGED_CODE();
172 
173     /* Sanity check */
174     ASSERT((RunRef->Count & EX_RUNDOWN_ACTIVE) != 0);
175 
176     /* Mark the counter as active */
177     ExpSetRundown(RunRef, EX_RUNDOWN_ACTIVE);
178 }
179 
180 /*++
181  * @name ExfReleaseRundownProtection
182  * @implemented NT5.1
183  *
184  *     The ExfReleaseRundownProtection routine releases the rundown protection
185  *     reference for the specified descriptor.
186  *
187  * @param RunRef
188  *        Pointer to a rundown reference descriptor.
189  *
190  * @return None.
191  *
192  * @remarks Callers of ExfReleaseRundownProtection can be running at any IRQL.
193  *
194  *--*/
195 VOID
196 FASTCALL
197 ExfReleaseRundownProtection(IN PEX_RUNDOWN_REF RunRef)
198 {
199     ULONG_PTR Value = RunRef->Count, NewValue;
200     PEX_RUNDOWN_WAIT_BLOCK WaitBlock;
201 
202     /* Loop until successfully incremented the counter */
203     for (;;)
204     {
205         /* Check if rundown is not active */
206         if (!(Value & EX_RUNDOWN_ACTIVE))
207         {
208             /* Sanity check */
209             ASSERT((Value >= EX_RUNDOWN_COUNT_INC) || (KeNumberProcessors > 1));
210 
211             /* Get the new value */
212             NewValue = Value - EX_RUNDOWN_COUNT_INC;
213 
214             /* Change the value */
215             NewValue = ExpChangeRundown(RunRef, NewValue, Value);
216             if (NewValue == Value) break;
217 
218             /* Update value */
219             Value = NewValue;
220         }
221         else
222         {
223             /* Get the wait block */
224             WaitBlock = (PEX_RUNDOWN_WAIT_BLOCK)(Value & ~EX_RUNDOWN_ACTIVE);
225             ASSERT((WaitBlock->Count > 0) || (KeNumberProcessors > 1));
226 
227             /* Remove the one count */
228             if (!InterlockedDecrementSizeT(&WaitBlock->Count))
229             {
230                 /* We're down to 0 now, so signal the event */
231                 KeSetEvent(&WaitBlock->WakeEvent, IO_NO_INCREMENT, FALSE);
232             }
233 
234             /* We're all done */
235             break;
236         }
237     }
238 }
239 
240 /*++
241  * @name ExfReleaseRundownProtectionEx
242  * @implemented NT5.2
243  *
244  *     The ExfReleaseRundownProtectionEx routine releases multiple rundown
245  *     protection references for the specified descriptor.
246  *
247  * @param RunRef
248  *        Pointer to a rundown reference descriptor.
249  *
250  * @param Count
251  *         Number of times to dereference the descriptor.
252  *
253  * @return None.
254  *
255  * @remarks Callers of ExfAcquireRundownProtectionEx can be running at any IRQL.
256  *
257  *--*/
258 VOID
259 FASTCALL
260 ExfReleaseRundownProtectionEx(IN PEX_RUNDOWN_REF RunRef,
261                               IN ULONG Count)
262 {
263     ULONG_PTR Value = RunRef->Count, NewValue;
264     PEX_RUNDOWN_WAIT_BLOCK WaitBlock;
265 
266     /* Loop until successfully incremented the counter */
267     for (;;)
268     {
269         /* Check if rundown is not active */
270         if (!(Value & EX_RUNDOWN_ACTIVE))
271         {
272             /* Sanity check */
273             ASSERT((Value >= EX_RUNDOWN_COUNT_INC * Count) ||
274                    (KeNumberProcessors > 1));
275 
276             /* Get the new value */
277             NewValue = Value - EX_RUNDOWN_COUNT_INC * Count;
278 
279             /* Change the value */
280             NewValue = ExpChangeRundown(RunRef, NewValue, Value);
281             if (NewValue == Value) break;
282 
283             /* Update value */
284             Value = NewValue;
285         }
286         else
287         {
288             /* Get the wait block */
289             WaitBlock = (PEX_RUNDOWN_WAIT_BLOCK)(Value & ~EX_RUNDOWN_ACTIVE);
290             ASSERT((WaitBlock->Count >= Count) || (KeNumberProcessors > 1));
291 
292             /* Remove the counts */
293             if (InterlockedExchangeAddSizeT(&WaitBlock->Count,
294                                             -(LONG)Count) == (LONG)Count)
295             {
296                 /* We're down to 0 now, so signal the event */
297                 KeSetEvent(&WaitBlock->WakeEvent, IO_NO_INCREMENT, FALSE);
298             }
299 
300             /* We're all done */
301             break;
302         }
303     }
304 }
305 
306 /*++
307  * @name ExfWaitForRundownProtectionRelease
308  * @implemented NT5.1
309  *
310  *     The ExfWaitForRundownProtectionRelease routine waits until the specified
311  *     rundown descriptor has been released.
312  *
313  * @param RunRef
314  *        Pointer to a rundown reference descriptor.
315  *
316  * @return None.
317  *
318  * @remarks Callers of ExfWaitForRundownProtectionRelease must be running
319  *          at IRQL <= APC_LEVEL.
320  *
321  *--*/
322 VOID
323 FASTCALL
324 ExfWaitForRundownProtectionRelease(IN PEX_RUNDOWN_REF RunRef)
325 {
326     ULONG_PTR Value, Count, NewValue;
327     EX_RUNDOWN_WAIT_BLOCK WaitBlock;
328     PEX_RUNDOWN_WAIT_BLOCK WaitBlockPointer;
329     PKEVENT Event;
330     PAGED_CODE();
331 
332     /* Set the active bit */
333     Value = ExpChangeRundown(RunRef, EX_RUNDOWN_ACTIVE, 0);
334     if ((Value == 0) || (Value == EX_RUNDOWN_ACTIVE)) return;
335 
336     /* No event for now */
337     Event = NULL;
338     WaitBlockPointer = (PEX_RUNDOWN_WAIT_BLOCK)((ULONG_PTR)&WaitBlock |
339                                                 EX_RUNDOWN_ACTIVE);
340 
341     /* Start waitblock set loop */
342     for (;;)
343     {
344         /* Save the count */
345         Count = Value >> EX_RUNDOWN_COUNT_SHIFT;
346 
347         /* If the count is over one and we don't have en event yet, create it */
348         if ((Count) && !(Event))
349         {
350             /* Initialize the event */
351             KeInitializeEvent(&WaitBlock.WakeEvent,
352                               SynchronizationEvent,
353                               FALSE);
354 
355             /* Set the pointer */
356             Event = &WaitBlock.WakeEvent;
357         }
358 
359         /* Set the count */
360         WaitBlock.Count = Count;
361 
362         /* Now set the pointer */
363         NewValue = ExpChangeRundown(RunRef, (ULONG_PTR)WaitBlockPointer, Value);
364         if (NewValue == Value) break;
365 
366         /* Loop again */
367         Value = NewValue;
368         ASSERT((Value & EX_RUNDOWN_ACTIVE) == 0);
369     }
370 
371     /* If the count was 0, we're done */
372     if (!Count) return;
373 
374     /* Wait for whoever needs to release to notify us */
375     KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL);
376     ASSERT(WaitBlock.Count == 0);
377 }
378 
379 /*
380  * @implemented NT5.2
381  */
382 BOOLEAN
383 FASTCALL
384 ExfAcquireRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
385 {
386     PEX_RUNDOWN_REF RunRef;
387 
388     RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, KeGetCurrentProcessorNumber());
389     return _ExAcquireRundownProtection(RunRef);
390 }
391 
392 /*
393  * @implemented NT5.2
394  */
395 BOOLEAN
396 FASTCALL
397 ExfAcquireRundownProtectionCacheAwareEx(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware,
398                                         IN ULONG Count)
399 {
400     PEX_RUNDOWN_REF RunRef;
401 
402     RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, KeGetCurrentProcessorNumber());
403     return ExfAcquireRundownProtectionEx(RunRef, Count);
404 }
405 
406 /*
407  * @implemented NT5.2
408  */
409 VOID
410 FASTCALL
411 ExfReleaseRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
412 {
413     PEX_RUNDOWN_REF RunRef;
414 
415     RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, KeGetCurrentProcessorNumber());
416     _ExReleaseRundownProtection(RunRef);
417 }
418 
419 /*
420  * @implemented NT5.2
421  */
422 VOID
423 FASTCALL
424 ExfReleaseRundownProtectionCacheAwareEx(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware,
425                                         IN ULONG Count)
426 {
427     PEX_RUNDOWN_REF RunRef;
428 
429     RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, KeGetCurrentProcessorNumber());
430     ExfReleaseRundownProtectionEx(RunRef, Count);
431 }
432 
433 /*
434  * @implemented NT5.2
435  */
436 VOID
437 FASTCALL
438 ExfWaitForRundownProtectionReleaseCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
439 {
440     PEX_RUNDOWN_REF RunRef;
441     EX_RUNDOWN_WAIT_BLOCK WaitBlock;
442     PEX_RUNDOWN_WAIT_BLOCK WaitBlockPointer;
443     ULONG ProcCount, Current, Value, OldValue, TotalCount;
444 
445     ProcCount = RunRefCacheAware->Number;
446     /* No proc, nothing to do */
447     if (ProcCount == 0)
448     {
449         return;
450     }
451 
452     TotalCount = 0;
453     WaitBlock.Count = 0;
454     WaitBlockPointer = (PEX_RUNDOWN_WAIT_BLOCK)((ULONG_PTR)&WaitBlock |
455                                                 EX_RUNDOWN_ACTIVE);
456     /* We will check all our runrefs */
457     for (Current = 0; Current < ProcCount; ++Current)
458     {
459         /* Get the runref for the proc */
460         RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, Current);
461         /* Loop for setting the wait block */
462         do
463         {
464             Value = RunRef->Count;
465             ASSERT((Value & EX_RUNDOWN_ACTIVE) == 0);
466 
467             /* Remove old value and set our waitblock instead */
468             OldValue = ExpChangeRundown(RunRef, WaitBlockPointer, Value);
469             if (OldValue == Value)
470             {
471                 break;
472             }
473 
474             Value = OldValue;
475         }
476         while (TRUE);
477 
478         /* Count the deleted values */
479         TotalCount += Value;
480     }
481 
482     /* Sanity check: we didn't overflow */
483     ASSERT((LONG)TotalCount >= 0);
484     if (TotalCount != 0)
485     {
486         /* Init the waitblock event */
487         KeInitializeEvent(&WaitBlock.WakeEvent,
488                           SynchronizationEvent,
489                           FALSE);
490 
491         /* Do we have to wait? If so, go ahead! */
492         if (InterlockedExchangeAddSizeT(&WaitBlock.Count,
493                                         (LONG)TotalCount >> EX_RUNDOWN_COUNT_SHIFT) ==
494                                        -(LONG)(TotalCount >> EX_RUNDOWN_COUNT_SHIFT))
495         {
496             KeWaitForSingleObject(&WaitBlock.WakeEvent, Executive, KernelMode, FALSE, NULL);
497         }
498     }
499 }
500 
501 /*
502  * @implemented NT5.2
503  */
504 VOID
505 FASTCALL
506 ExfRundownCompletedCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
507 {
508     PEX_RUNDOWN_REF RunRef;
509     ULONG ProcCount, Current;
510 
511     ProcCount = RunRefCacheAware->Number;
512     /* No proc, nothing to do */
513     if (ProcCount == 0)
514     {
515         return;
516     }
517 
518     /* We will mark all our runrefs active */
519     for (Current = 0; Current < ProcCount; ++Current)
520     {
521         /* Get the runref for the proc */
522         RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, Current);
523         ASSERT((RunRef->Count & EX_RUNDOWN_ACTIVE) != 0);
524 
525         ExpSetRundown(RunRef, EX_RUNDOWN_ACTIVE);
526     }
527 }
528 
529 /*
530  * @implemented NT5.2
531  */
532 VOID
533 FASTCALL
534 ExfReInitializeRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
535 {
536     PEX_RUNDOWN_REF RunRef;
537     ULONG ProcCount, Current;
538 
539     ProcCount = RunRefCacheAware->Number;
540     /* No proc, nothing to do */
541     if (ProcCount == 0)
542     {
543         return;
544     }
545 
546     /* We will mark all our runrefs inactive */
547     for (Current = 0; Current < ProcCount; ++Current)
548     {
549         /* Get the runref for the proc */
550         RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, Current);
551         ASSERT((RunRef->Count & EX_RUNDOWN_ACTIVE) != 0);
552 
553         ExpSetRundown(RunRef, 0);
554     }
555 }
556 
557 /*
558  * @implemented NT5.2
559  */
560 PEX_RUNDOWN_REF_CACHE_AWARE
561 NTAPI
562 ExAllocateCacheAwareRundownProtection(IN POOL_TYPE PoolType,
563                                       IN ULONG Tag)
564 {
565     PEX_RUNDOWN_REF RunRef;
566     PVOID PoolToFree, RunRefs;
567     ULONG RunRefSize, Count, Align;
568     PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware;
569 
570     PAGED_CODE();
571 
572     /* Allocate the master structure */
573     RunRefCacheAware = ExAllocatePoolWithTag(PoolType, sizeof(EX_RUNDOWN_REF_CACHE_AWARE), Tag);
574     if (RunRefCacheAware == NULL)
575     {
576         return NULL;
577     }
578 
579     /* Compute the size of each runref */
580     RunRefCacheAware->Number = KeNumberProcessors;
581     if (KeNumberProcessors <= 1)
582     {
583         RunRefSize = sizeof(EX_RUNDOWN_REF);
584     }
585     else
586     {
587         Align = KeGetRecommendedSharedDataAlignment();
588         RunRefSize = Align;
589         ASSERT((RunRefSize & (RunRefSize - 1)) == 0);
590     }
591 
592     /* It must at least hold a EX_RUNDOWN_REF structure */
593     ASSERT(sizeof(EX_RUNDOWN_REF) <= RunRefSize);
594     RunRefCacheAware->RunRefSize = RunRefSize;
595 
596     /* Allocate our runref pool */
597     PoolToFree = ExAllocatePoolWithTag(PoolType, RunRefSize * RunRefCacheAware->Number, Tag);
598     if (PoolToFree == NULL)
599     {
600         ExFreePoolWithTag(RunRefCacheAware, Tag);
601         return NULL;
602     }
603 
604     /* On SMP, check for alignment */
605     if (RunRefCacheAware->Number > 1 && (ULONG_PTR)PoolToFree & (Align - 1))
606     {
607         /* Not properly aligned, do it again! */
608         ExFreePoolWithTag(PoolToFree, Tag);
609 
610         /* Allocate a bigger buffer to be able to align properly */
611         PoolToFree = ExAllocatePoolWithTag(PoolType, RunRefSize * (RunRefCacheAware->Number + 1), Tag);
612         if (PoolToFree == NULL)
613         {
614             ExFreePoolWithTag(RunRefCacheAware, Tag);
615             return NULL;
616         }
617 
618         RunRefs = (PVOID)ALIGN_UP_BY(PoolToFree, Align);
619     }
620     else
621     {
622         RunRefs = PoolToFree;
623     }
624 
625     RunRefCacheAware->RunRefs = RunRefs;
626     RunRefCacheAware->PoolToFree = PoolToFree;
627 
628     /* And initialize runref */
629     if (RunRefCacheAware->Number != 0)
630     {
631         for (Count = 0; Count < RunRefCacheAware->Number; ++Count)
632         {
633             RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, Count);
634             _ExInitializeRundownProtection(RunRef);
635         }
636     }
637 
638     return RunRefCacheAware;
639 }
640 
641 /*
642  * @implemented NT5.2
643  */
644 VOID
645 NTAPI
646 ExFreeCacheAwareRundownProtection(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
647 {
648     PAGED_CODE();
649 
650     /*
651      * This is to be called for RunRefCacheAware that were allocated with
652      * ExAllocateCacheAwareRundownProtection and not for user-allocated
653      * ones
654      */
655     ASSERT(RunRefCacheAware->PoolToFree != (PVOID)0xBADCA11);
656 
657     /* We don't know the tag that as used for allocation */
658     ExFreePoolWithTag(RunRefCacheAware->PoolToFree, 0);
659     ExFreePoolWithTag(RunRefCacheAware, 0);
660 }
661 
662 /*
663  * @implemented NT5.2
664  */
665 VOID
666 NTAPI
667 ExInitializeRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware,
668                                         IN SIZE_T Size)
669 {
670     PVOID Pool;
671     PEX_RUNDOWN_REF RunRef;
672     ULONG Count, RunRefSize, Align;
673 
674     PAGED_CODE();
675 
676     /* Get the user allocate pool for runrefs */
677     Pool = (PVOID)((ULONG_PTR)RunRefCacheAware + sizeof(EX_RUNDOWN_REF_CACHE_AWARE));
678 
679     /* By default a runref is structure-sized */
680     RunRefSize = sizeof(EX_RUNDOWN_REF);
681 
682     /*
683      * If we just have enough room for a single runref, deduce were on a single
684      * processor machine
685      */
686     if (Size == sizeof(EX_RUNDOWN_REF_CACHE_AWARE) + sizeof(EX_RUNDOWN_REF))
687     {
688         Count = 1;
689     }
690     else
691     {
692         /* Get alignment constraint */
693         Align = KeGetRecommendedSharedDataAlignment();
694 
695         /* How many runrefs given the alignment? */
696         RunRefSize = Align;
697         Count = ((Size - sizeof(EX_RUNDOWN_REF_CACHE_AWARE)) / Align) - 1;
698         Pool = (PVOID)ALIGN_UP_BY(Pool, Align);
699     }
700 
701     /* Initialize the structure */
702     RunRefCacheAware->RunRefs = Pool;
703     RunRefCacheAware->RunRefSize = RunRefSize;
704     RunRefCacheAware->Number = Count;
705 
706     /* There is no allocated pool! */
707     RunRefCacheAware->PoolToFree = (PVOID)0xBADCA11u;
708 
709     /* Initialize runref */
710     if (RunRefCacheAware->Number != 0)
711     {
712         for (Count = 0; Count < RunRefCacheAware->Number; ++Count)
713         {
714             RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, Count);
715             _ExInitializeRundownProtection(RunRef);
716         }
717     }
718 }
719 
720 /*
721  * @implemented NT5.2
722  */
723 SIZE_T
724 NTAPI
725 ExSizeOfRundownProtectionCacheAware(VOID)
726 {
727     SIZE_T Size;
728 
729     PAGED_CODE();
730 
731     /* Compute the needed size for runrefs */
732     if (KeNumberProcessors <= 1)
733     {
734         Size = sizeof(EX_RUNDOWN_REF);
735     }
736     else
737     {
738         /* We +1, to have enough room for alignment */
739         Size = (KeNumberProcessors + 1) * KeGetRecommendedSharedDataAlignment();
740     }
741 
742     /* Return total size (master structure and runrefs) */
743     return Size + sizeof(EX_RUNDOWN_REF_CACHE_AWARE);
744 }
745 
746