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
ExfAcquireRundownProtection(IN PEX_RUNDOWN_REF RunRef)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
ExfAcquireRundownProtectionEx(IN PEX_RUNDOWN_REF RunRef,IN ULONG Count)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
ExfInitializeRundownProtection(IN PEX_RUNDOWN_REF RunRef)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
ExfReInitializeRundownProtection(IN PEX_RUNDOWN_REF RunRef)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
ExfRundownCompleted(IN PEX_RUNDOWN_REF RunRef)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
ExfReleaseRundownProtection(IN PEX_RUNDOWN_REF RunRef)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
ExfReleaseRundownProtectionEx(IN PEX_RUNDOWN_REF RunRef,IN ULONG Count)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
ExfWaitForRundownProtectionRelease(IN PEX_RUNDOWN_REF RunRef)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
ExfAcquireRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)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
ExfAcquireRundownProtectionCacheAwareEx(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware,IN ULONG Count)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
ExfReleaseRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)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
ExfReleaseRundownProtectionCacheAwareEx(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware,IN ULONG Count)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
ExfWaitForRundownProtectionReleaseCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)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_PTR 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_PTR)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_PTR)TotalCount >> EX_RUNDOWN_COUNT_SHIFT) ==
494 -(LONG_PTR)(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
ExfRundownCompletedCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)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
ExfReInitializeRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)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
ExAllocateCacheAwareRundownProtection(IN POOL_TYPE PoolType,IN ULONG Tag)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
ExFreeCacheAwareRundownProtection(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)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
ExInitializeRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware,IN SIZE_T Size)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
ExSizeOfRundownProtectionCacheAware(VOID)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