[NTOSKRNL] Replace ExGetRunRefForCurrentProcessor() by a more generic function
[reactos.git] / ntoskrnl / ex / rundown.c
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 /* FIXME: STUBS **************************************************************/
380
381 /*
382 * @implemented NT5.2
383 */
384 BOOLEAN
385 FASTCALL
386 ExfAcquireRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
387 {
388 PEX_RUNDOWN_REF RunRef;
389
390 RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, KeGetCurrentProcessorNumber());
391 return _ExAcquireRundownProtection(RunRef);
392 }
393
394 /*
395 * @implemented NT5.2
396 */
397 BOOLEAN
398 FASTCALL
399 ExfAcquireRundownProtectionCacheAwareEx(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware,
400 IN ULONG Count)
401 {
402 PEX_RUNDOWN_REF RunRef;
403
404 RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, KeGetCurrentProcessorNumber());
405 return ExfAcquireRundownProtectionEx(RunRef, Count);
406 }
407
408 /*
409 * @implemented NT5.2
410 */
411 VOID
412 FASTCALL
413 ExfReleaseRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
414 {
415 PEX_RUNDOWN_REF RunRef;
416
417 RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, KeGetCurrentProcessorNumber());
418 return _ExReleaseRundownProtection(RunRef);
419 }
420
421 /*
422 * @implemented NT5.2
423 */
424 VOID
425 FASTCALL
426 ExfReleaseRundownProtectionCacheAwareEx(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware,
427 IN ULONG Count)
428 {
429 PEX_RUNDOWN_REF RunRef;
430
431 RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, KeGetCurrentProcessorNumber());
432 return ExfReleaseRundownProtectionEx(RunRef, Count);
433 }
434
435 /*
436 * @implemented NT5.2
437 */
438 VOID
439 FASTCALL
440 ExfWaitForRundownProtectionReleaseCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
441 {
442 PEX_RUNDOWN_REF RunRef;
443 EX_RUNDOWN_WAIT_BLOCK WaitBlock;
444 PEX_RUNDOWN_WAIT_BLOCK WaitBlockPointer;
445 ULONG ProcCount, Current, Value, OldValue, TotalCount;
446
447 ProcCount = RunRefCacheAware->Number;
448 /* No proc, nothing to do */
449 if (ProcCount == 0)
450 {
451 return;
452 }
453
454 TotalCount = 0;
455 WaitBlock.Count = 0;
456 WaitBlockPointer = (PEX_RUNDOWN_WAIT_BLOCK)((ULONG_PTR)&WaitBlock |
457 EX_RUNDOWN_ACTIVE);
458 /* We will check all our runrefs */
459 for (Current = 0; Current < ProcCount; ++Current)
460 {
461 /* Get the runref for the proc */
462 RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, Current);
463 /* Loop for setting the wait block */
464 do
465 {
466 Value = RunRef->Count;
467 ASSERT((Value & EX_RUNDOWN_ACTIVE) == 0);
468
469 /* Remove old value and set our waitblock instead */
470 OldValue = ExpChangeRundown(RunRef, WaitBlockPointer, Value);
471 if (OldValue == Value)
472 {
473 break;
474 }
475
476 Value = OldValue;
477 }
478 while (TRUE);
479
480 /* Count the deleted values */
481 TotalCount += Value;
482 }
483
484 /* Sanity check: we didn't overflow */
485 ASSERT((LONG)TotalCount >= 0);
486 if (TotalCount != 0)
487 {
488 /* Init the waitblock event */
489 KeInitializeEvent(&WaitBlock.WakeEvent,
490 SynchronizationEvent,
491 FALSE);
492
493 /* Do we have to wait? If so, go ahead! */
494 if (InterlockedExchangeAddSizeT(&WaitBlock.Count,
495 (LONG)TotalCount >> EX_RUNDOWN_COUNT_SHIFT) ==
496 -(LONG)(TotalCount >> EX_RUNDOWN_COUNT_SHIFT))
497 {
498 KeWaitForSingleObject(&WaitBlock.WakeEvent, Executive, KernelMode, FALSE, NULL);
499 }
500 }
501 }
502
503 /*
504 * @unimplemented NT5.2
505 */
506 VOID
507 FASTCALL
508 ExfRundownCompletedCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
509 {
510 DBG_UNREFERENCED_PARAMETER(RunRefCacheAware);
511 UNIMPLEMENTED;
512 }
513
514 /*
515 * @unimplemented NT5.2
516 */
517 VOID
518 FASTCALL
519 ExfReInitializeRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
520 {
521 DBG_UNREFERENCED_PARAMETER(RunRefCacheAware);
522 UNIMPLEMENTED;
523 }
524
525 /*
526 * @implemented NT5.2
527 */
528 PEX_RUNDOWN_REF_CACHE_AWARE
529 NTAPI
530 ExAllocateCacheAwareRundownProtection(IN POOL_TYPE PoolType,
531 IN ULONG Tag)
532 {
533 PEX_RUNDOWN_REF RunRef;
534 PVOID PoolToFree, RunRefs;
535 ULONG RunRefSize, Count, Align;
536 PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware;
537
538 PAGED_CODE();
539
540 /* Allocate the master structure */
541 RunRefCacheAware = ExAllocatePoolWithTag(PoolType, sizeof(EX_RUNDOWN_REF_CACHE_AWARE), Tag);
542 if (RunRefCacheAware == NULL)
543 {
544 return NULL;
545 }
546
547 /* Compute the size of each runref */
548 RunRefCacheAware->Number = KeNumberProcessors;
549 if (KeNumberProcessors <= 1)
550 {
551 RunRefSize = sizeof(EX_RUNDOWN_REF);
552 }
553 else
554 {
555 Align = KeGetRecommendedSharedDataAlignment();
556 RunRefSize = Align;
557 ASSERT((RunRefSize & (RunRefSize - 1)) == 0);
558 }
559
560 /* It must at least hold a EX_RUNDOWN_REF structure */
561 ASSERT(sizeof(EX_RUNDOWN_REF) <= RunRefSize);
562 RunRefCacheAware->RunRefSize = RunRefSize;
563
564 /* Allocate our runref pool */
565 PoolToFree = ExAllocatePoolWithTag(PoolType, RunRefSize * RunRefCacheAware->Number, Tag);
566 if (PoolToFree == NULL)
567 {
568 ExFreePoolWithTag(RunRefCacheAware, Tag);
569 return NULL;
570 }
571
572 /* On SMP, check for alignment */
573 if (RunRefCacheAware->Number > 1 && (ULONG_PTR)PoolToFree & (Align - 1))
574 {
575 /* Not properly aligned, do it again! */
576 ExFreePoolWithTag(PoolToFree, Tag);
577
578 /* Allocate a bigger buffer to be able to align properly */
579 PoolToFree = ExAllocatePoolWithTag(PoolType, RunRefSize * (RunRefCacheAware->Number + 1), Tag);
580 if (PoolToFree == NULL)
581 {
582 ExFreePoolWithTag(RunRefCacheAware, Tag);
583 return NULL;
584 }
585
586 RunRefs = (PVOID)ALIGN_UP_BY(PoolToFree, Align);
587 }
588 else
589 {
590 RunRefs = PoolToFree;
591 }
592
593 RunRefCacheAware->RunRefs = RunRefs;
594 RunRefCacheAware->PoolToFree = PoolToFree;
595
596 /* And initialize runref */
597 if (RunRefCacheAware->Number != 0)
598 {
599 for (Count = 0; Count < RunRefCacheAware->Number; ++Count)
600 {
601 RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, Count);
602 _ExInitializeRundownProtection(RunRef);
603 }
604 }
605
606 return RunRefCacheAware;
607 }
608
609 /*
610 * @implemented NT5.2
611 */
612 VOID
613 NTAPI
614 ExFreeCacheAwareRundownProtection(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
615 {
616 PAGED_CODE();
617
618 /*
619 * This is to be called for RunRefCacheAware that were allocated with
620 * ExAllocateCacheAwareRundownProtection and not for user-allocated
621 * ones
622 */
623 ASSERT(RunRefCacheAware->PoolToFree != (PVOID)0xBADCA11);
624
625 /* We don't know the tag that as used for allocation */
626 ExFreePoolWithTag(RunRefCacheAware->PoolToFree, 0);
627 ExFreePoolWithTag(RunRefCacheAware, 0);
628 }
629
630 /*
631 * @implemented NT5.2
632 */
633 VOID
634 NTAPI
635 ExInitializeRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware,
636 IN SIZE_T Size)
637 {
638 PVOID Pool;
639 PEX_RUNDOWN_REF RunRef;
640 ULONG Count, RunRefSize, Align;
641
642 PAGED_CODE();
643
644 /* Get the user allocate pool for runrefs */
645 Pool = (PVOID)((ULONG_PTR)RunRefCacheAware + sizeof(EX_RUNDOWN_REF_CACHE_AWARE));
646
647 /* By default a runref is structure-sized */
648 RunRefSize = sizeof(EX_RUNDOWN_REF);
649
650 /*
651 * If we just have enough room for a single runref, deduce were on a single
652 * processor machine
653 */
654 if (Size == sizeof(EX_RUNDOWN_REF_CACHE_AWARE) + sizeof(EX_RUNDOWN_REF))
655 {
656 Count = 1;
657 }
658 else
659 {
660 /* Get alignment constraint */
661 Align = KeGetRecommendedSharedDataAlignment();
662
663 /* How many runrefs given the alignment? */
664 RunRefSize = Align;
665 Count = ((Size - sizeof(EX_RUNDOWN_REF_CACHE_AWARE)) / Align) - 1;
666 Pool = (PVOID)ALIGN_UP_BY(Pool, Align);
667 }
668
669 /* Initialize the structure */
670 RunRefCacheAware->RunRefs = Pool;
671 RunRefCacheAware->RunRefSize = RunRefSize;
672 RunRefCacheAware->Number = Count;
673
674 /* There is no allocated pool! */
675 RunRefCacheAware->PoolToFree = (PVOID)0xBADCA11u;
676
677 /* Initialize runref */
678 if (RunRefCacheAware->Number != 0)
679 {
680 for (Count = 0; Count < RunRefCacheAware->Number; ++Count)
681 {
682 RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, Count);
683 _ExInitializeRundownProtection(RunRef);
684 }
685 }
686 }
687
688 /*
689 * @implemented NT5.2
690 */
691 SIZE_T
692 NTAPI
693 ExSizeOfRundownProtectionCacheAware(VOID)
694 {
695 SIZE_T Size;
696
697 PAGED_CODE();
698
699 /* Compute the needed size for runrefs */
700 if (KeNumberProcessors <= 1)
701 {
702 Size = sizeof(EX_RUNDOWN_REF);
703 }
704 else
705 {
706 /* We +1, to have enough room for alignment */
707 Size = (KeNumberProcessors + 1) * KeGetRecommendedSharedDataAlignment();
708 }
709
710 /* Return total size (master structure and runrefs) */
711 return Size + sizeof(EX_RUNDOWN_REF_CACHE_AWARE);
712 }
713