* PURPOSE: Rundown and Cache-Aware Rundown Protection
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
* Thomas Weidenmueller
+ * Pierre Schweitzer
*/
/* INCLUDES *****************************************************************/
ASSERT(WaitBlock.Count == 0);
}
-/* FIXME: STUBS **************************************************************/
-
/*
- * @unimplemented NT5.2
+ * @implemented NT5.2
*/
BOOLEAN
FASTCALL
ExfAcquireRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
{
- DBG_UNREFERENCED_PARAMETER(RunRefCacheAware);
- UNIMPLEMENTED;
- return FALSE;
+ PEX_RUNDOWN_REF RunRef;
+
+ RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, KeGetCurrentProcessorNumber());
+ return _ExAcquireRundownProtection(RunRef);
}
/*
- * @unimplemented NT5.2
+ * @implemented NT5.2
*/
BOOLEAN
FASTCALL
ExfAcquireRundownProtectionCacheAwareEx(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware,
IN ULONG Count)
{
- DBG_UNREFERENCED_PARAMETER(RunRefCacheAware);
- DBG_UNREFERENCED_PARAMETER(Count);
- UNIMPLEMENTED;
- return FALSE;
+ PEX_RUNDOWN_REF RunRef;
+
+ RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, KeGetCurrentProcessorNumber());
+ return ExfAcquireRundownProtectionEx(RunRef, Count);
}
/*
- * @unimplemented NT5.2
+ * @implemented NT5.2
*/
VOID
FASTCALL
ExfReleaseRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
{
- DBG_UNREFERENCED_PARAMETER(RunRefCacheAware);
- UNIMPLEMENTED;
+ PEX_RUNDOWN_REF RunRef;
+
+ RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, KeGetCurrentProcessorNumber());
+ _ExReleaseRundownProtection(RunRef);
}
/*
- * @unimplemented NT5.2
+ * @implemented NT5.2
*/
VOID
FASTCALL
ExfReleaseRundownProtectionCacheAwareEx(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware,
IN ULONG Count)
{
- DBG_UNREFERENCED_PARAMETER(RunRefCacheAware);
- DBG_UNREFERENCED_PARAMETER(Count);
- UNIMPLEMENTED;
+ PEX_RUNDOWN_REF RunRef;
+
+ RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, KeGetCurrentProcessorNumber());
+ ExfReleaseRundownProtectionEx(RunRef, Count);
}
/*
- * @unimplemented NT5.2
+ * @implemented NT5.2
*/
VOID
FASTCALL
ExfWaitForRundownProtectionReleaseCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
{
- DBG_UNREFERENCED_PARAMETER(RunRefCacheAware);
- UNIMPLEMENTED;
+ PEX_RUNDOWN_REF RunRef;
+ EX_RUNDOWN_WAIT_BLOCK WaitBlock;
+ PEX_RUNDOWN_WAIT_BLOCK WaitBlockPointer;
+ ULONG_PTR ProcCount, Current, Value, OldValue, TotalCount;
+
+ ProcCount = RunRefCacheAware->Number;
+ /* No proc, nothing to do */
+ if (ProcCount == 0)
+ {
+ return;
+ }
+
+ TotalCount = 0;
+ WaitBlock.Count = 0;
+ WaitBlockPointer = (PEX_RUNDOWN_WAIT_BLOCK)((ULONG_PTR)&WaitBlock |
+ EX_RUNDOWN_ACTIVE);
+ /* We will check all our runrefs */
+ for (Current = 0; Current < ProcCount; ++Current)
+ {
+ /* Get the runref for the proc */
+ RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, Current);
+ /* Loop for setting the wait block */
+ do
+ {
+ Value = RunRef->Count;
+ ASSERT((Value & EX_RUNDOWN_ACTIVE) == 0);
+
+ /* Remove old value and set our waitblock instead */
+ OldValue = ExpChangeRundown(RunRef, WaitBlockPointer, Value);
+ if (OldValue == Value)
+ {
+ break;
+ }
+
+ Value = OldValue;
+ }
+ while (TRUE);
+
+ /* Count the deleted values */
+ TotalCount += Value;
+ }
+
+ /* Sanity check: we didn't overflow */
+ ASSERT((LONG_PTR)TotalCount >= 0);
+ if (TotalCount != 0)
+ {
+ /* Init the waitblock event */
+ KeInitializeEvent(&WaitBlock.WakeEvent,
+ SynchronizationEvent,
+ FALSE);
+
+ /* Do we have to wait? If so, go ahead! */
+ if (InterlockedExchangeAddSizeT(&WaitBlock.Count,
+ (LONG_PTR)TotalCount >> EX_RUNDOWN_COUNT_SHIFT) ==
+ -(LONG_PTR)(TotalCount >> EX_RUNDOWN_COUNT_SHIFT))
+ {
+ KeWaitForSingleObject(&WaitBlock.WakeEvent, Executive, KernelMode, FALSE, NULL);
+ }
+ }
}
/*
- * @unimplemented NT5.2
+ * @implemented NT5.2
*/
VOID
FASTCALL
ExfRundownCompletedCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
{
- DBG_UNREFERENCED_PARAMETER(RunRefCacheAware);
- UNIMPLEMENTED;
+ PEX_RUNDOWN_REF RunRef;
+ ULONG ProcCount, Current;
+
+ ProcCount = RunRefCacheAware->Number;
+ /* No proc, nothing to do */
+ if (ProcCount == 0)
+ {
+ return;
+ }
+
+ /* We will mark all our runrefs active */
+ for (Current = 0; Current < ProcCount; ++Current)
+ {
+ /* Get the runref for the proc */
+ RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, Current);
+ ASSERT((RunRef->Count & EX_RUNDOWN_ACTIVE) != 0);
+
+ ExpSetRundown(RunRef, EX_RUNDOWN_ACTIVE);
+ }
}
/*
- * @unimplemented NT5.2
+ * @implemented NT5.2
*/
VOID
FASTCALL
ExfReInitializeRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
{
- DBG_UNREFERENCED_PARAMETER(RunRefCacheAware);
- UNIMPLEMENTED;
+ PEX_RUNDOWN_REF RunRef;
+ ULONG ProcCount, Current;
+
+ ProcCount = RunRefCacheAware->Number;
+ /* No proc, nothing to do */
+ if (ProcCount == 0)
+ {
+ return;
+ }
+
+ /* We will mark all our runrefs inactive */
+ for (Current = 0; Current < ProcCount; ++Current)
+ {
+ /* Get the runref for the proc */
+ RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, Current);
+ ASSERT((RunRef->Count & EX_RUNDOWN_ACTIVE) != 0);
+
+ ExpSetRundown(RunRef, 0);
+ }
}
/*
ExAllocateCacheAwareRundownProtection(IN POOL_TYPE PoolType,
IN ULONG Tag)
{
- PVOID PoolToFree;
PEX_RUNDOWN_REF RunRef;
- ULONG RunRefSize, Count, Offset;
+ PVOID PoolToFree, RunRefs;
+ ULONG RunRefSize, Count, Align;
PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware;
PAGED_CODE();
}
else
{
- RunRefSize = KeGetRecommendedSharedDataAlignment();
+ Align = KeGetRecommendedSharedDataAlignment();
+ RunRefSize = Align;
ASSERT((RunRefSize & (RunRefSize - 1)) == 0);
}
}
/* On SMP, check for alignment */
- if (RunRefCacheAware->Number > 1)
+ if (RunRefCacheAware->Number > 1 && (ULONG_PTR)PoolToFree & (Align - 1))
+ {
+ /* Not properly aligned, do it again! */
+ ExFreePoolWithTag(PoolToFree, Tag);
+
+ /* Allocate a bigger buffer to be able to align properly */
+ PoolToFree = ExAllocatePoolWithTag(PoolType, RunRefSize * (RunRefCacheAware->Number + 1), Tag);
+ if (PoolToFree == NULL)
+ {
+ ExFreePoolWithTag(RunRefCacheAware, Tag);
+ return NULL;
+ }
+
+ RunRefs = (PVOID)ALIGN_UP_BY(PoolToFree, Align);
+ }
+ else
{
- /* FIXME: properly align run refs */
- UNIMPLEMENTED;
+ RunRefs = PoolToFree;
}
- RunRefCacheAware->RunRefs = PoolToFree;
+ RunRefCacheAware->RunRefs = RunRefs;
RunRefCacheAware->PoolToFree = PoolToFree;
/* And initialize runref */
{
for (Count = 0; Count < RunRefCacheAware->Number; ++Count)
{
- Offset = RunRefCacheAware->RunRefSize * Count;
- RunRef = (PEX_RUNDOWN_REF)((ULONG_PTR)RunRefCacheAware->RunRefs + Offset);
- RunRef->Count = 0;
+ RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, Count);
+ _ExInitializeRundownProtection(RunRef);
}
}
}
/*
- * @unimplemented NT5.2
+ * @implemented NT5.2
*/
VOID
NTAPI
ExInitializeRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware,
- IN SIZE_T Count)
+ IN SIZE_T Size)
{
- DBG_UNREFERENCED_PARAMETER(RunRefCacheAware);
- DBG_UNREFERENCED_PARAMETER(Count);
- UNIMPLEMENTED;
+ PVOID Pool;
+ PEX_RUNDOWN_REF RunRef;
+ ULONG Count, RunRefSize, Align;
+
+ PAGED_CODE();
+
+ /* Get the user allocate pool for runrefs */
+ Pool = (PVOID)((ULONG_PTR)RunRefCacheAware + sizeof(EX_RUNDOWN_REF_CACHE_AWARE));
+
+ /* By default a runref is structure-sized */
+ RunRefSize = sizeof(EX_RUNDOWN_REF);
+
+ /*
+ * If we just have enough room for a single runref, deduce were on a single
+ * processor machine
+ */
+ if (Size == sizeof(EX_RUNDOWN_REF_CACHE_AWARE) + sizeof(EX_RUNDOWN_REF))
+ {
+ Count = 1;
+ }
+ else
+ {
+ /* Get alignment constraint */
+ Align = KeGetRecommendedSharedDataAlignment();
+
+ /* How many runrefs given the alignment? */
+ RunRefSize = Align;
+ Count = ((Size - sizeof(EX_RUNDOWN_REF_CACHE_AWARE)) / Align) - 1;
+ Pool = (PVOID)ALIGN_UP_BY(Pool, Align);
+ }
+
+ /* Initialize the structure */
+ RunRefCacheAware->RunRefs = Pool;
+ RunRefCacheAware->RunRefSize = RunRefSize;
+ RunRefCacheAware->Number = Count;
+
+ /* There is no allocated pool! */
+ RunRefCacheAware->PoolToFree = (PVOID)0xBADCA11u;
+
+ /* Initialize runref */
+ if (RunRefCacheAware->Number != 0)
+ {
+ for (Count = 0; Count < RunRefCacheAware->Number; ++Count)
+ {
+ RunRef = ExGetRunRefForGivenProcessor(RunRefCacheAware, Count);
+ _ExInitializeRundownProtection(RunRef);
+ }
+ }
}
/*
- * @unimplemented NT5.2
+ * @implemented NT5.2
*/
SIZE_T
NTAPI
ExSizeOfRundownProtectionCacheAware(VOID)
{
- UNIMPLEMENTED;
- return 0;
+ SIZE_T Size;
+
+ PAGED_CODE();
+
+ /* Compute the needed size for runrefs */
+ if (KeNumberProcessors <= 1)
+ {
+ Size = sizeof(EX_RUNDOWN_REF);
+ }
+ else
+ {
+ /* We +1, to have enough room for alignment */
+ Size = (KeNumberProcessors + 1) * KeGetRecommendedSharedDataAlignment();
+ }
+
+ /* Return total size (master structure and runrefs) */
+ return Size + sizeof(EX_RUNDOWN_REF_CACHE_AWARE);
}