mirror of
https://github.com/reactos/reactos.git
synced 2024-08-30 23:28:58 +00:00
512 lines
12 KiB
C
512 lines
12 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Kernel
|
|
* FILE: ntoskrnl/ex/rundown.c
|
|
* PURPOSE: Rundown and Cache-Aware Rundown Protection
|
|
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
|
|
* Thomas Weidenmueller
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
/*++
|
|
* @name ExfAcquireRundownProtection
|
|
* @implemented NT5.1
|
|
*
|
|
* The ExfAcquireRundownProtection routine acquires rundown protection for
|
|
* the specified descriptor.
|
|
*
|
|
* @param RunRef
|
|
* Pointer to a rundown reference descriptor.
|
|
*
|
|
* @return TRUE if access to the protected structure was granted, FALSE otherwise.
|
|
*
|
|
* @remarks Callers of ExfAcquireRundownProtection can be running at any IRQL.
|
|
*
|
|
*--*/
|
|
BOOLEAN
|
|
FASTCALL
|
|
ExfAcquireRundownProtection(IN PEX_RUNDOWN_REF RunRef)
|
|
{
|
|
ULONG_PTR Value = RunRef->Count, NewValue;
|
|
|
|
/* Loop until successfully incremented the counter */
|
|
for (;;)
|
|
{
|
|
/* Make sure a rundown is not active */
|
|
if (Value & EX_RUNDOWN_ACTIVE) return FALSE;
|
|
|
|
/* Add a reference */
|
|
NewValue = Value + EX_RUNDOWN_COUNT_INC;
|
|
|
|
/* Change the value */
|
|
NewValue = ExpChangeRundown(RunRef, NewValue, Value);
|
|
if (NewValue == Value) return TRUE;
|
|
|
|
/* Update it */
|
|
Value = NewValue;
|
|
}
|
|
}
|
|
|
|
/*++
|
|
* @name ExfAcquireRundownProtectionEx
|
|
* @implemented NT5.2
|
|
*
|
|
* The ExfAcquireRundownProtectionEx routine acquires multiple rundown
|
|
* protection references for the specified descriptor.
|
|
*
|
|
* @param RunRef
|
|
* Pointer to a rundown reference descriptor.
|
|
*
|
|
* @param Count
|
|
* Number of times to reference the descriptor.
|
|
*
|
|
* @return TRUE if access to the protected structure was granted, FALSE otherwise.
|
|
*
|
|
* @remarks Callers of ExfAcquireRundownProtectionEx can be running at any IRQL.
|
|
*
|
|
*--*/
|
|
BOOLEAN
|
|
FASTCALL
|
|
ExfAcquireRundownProtectionEx(IN PEX_RUNDOWN_REF RunRef,
|
|
IN ULONG Count)
|
|
{
|
|
ULONG_PTR Value = RunRef->Count, NewValue;
|
|
|
|
/* Loop until successfully incremented the counter */
|
|
for (;;)
|
|
{
|
|
/* Make sure a rundown is not active */
|
|
if (Value & EX_RUNDOWN_ACTIVE) return FALSE;
|
|
|
|
/* Add references */
|
|
NewValue = Value + EX_RUNDOWN_COUNT_INC * Count;
|
|
|
|
/* Change the value */
|
|
NewValue = ExpChangeRundown(RunRef, NewValue, Value);
|
|
if (NewValue == Value) return TRUE;
|
|
|
|
/* Update the value */
|
|
Value = NewValue;
|
|
}
|
|
}
|
|
|
|
/*++
|
|
* @name ExfInitializeRundownProtection
|
|
* @implemented NT5.1
|
|
*
|
|
* The ExfInitializeRundownProtection routine initializes a rundown
|
|
* protection descriptor.
|
|
*
|
|
* @param RunRef
|
|
* Pointer to a rundown reference descriptor.
|
|
*
|
|
* @return None.
|
|
*
|
|
* @remarks Callers of ExfInitializeRundownProtection can be running at any IRQL.
|
|
*
|
|
*--*/
|
|
VOID
|
|
FASTCALL
|
|
ExfInitializeRundownProtection(IN PEX_RUNDOWN_REF RunRef)
|
|
{
|
|
/* Set the count to zero */
|
|
RunRef->Count = 0;
|
|
}
|
|
|
|
/*++
|
|
* @name ExfReInitializeRundownProtection
|
|
* @implemented NT5.1
|
|
*
|
|
* The ExfReInitializeRundownProtection routine re-initializes a rundown
|
|
* protection descriptor.
|
|
*
|
|
* @param RunRef
|
|
* Pointer to a rundown reference descriptor.
|
|
*
|
|
* @return None.
|
|
*
|
|
* @remarks Callers of ExfReInitializeRundownProtection can be running at any IRQL.
|
|
*
|
|
*--*/
|
|
VOID
|
|
FASTCALL
|
|
ExfReInitializeRundownProtection(IN PEX_RUNDOWN_REF RunRef)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
/* Sanity check */
|
|
ASSERT((RunRef->Count & EX_RUNDOWN_ACTIVE) != 0);
|
|
|
|
/* Reset the count */
|
|
ExpSetRundown(RunRef, 0);
|
|
}
|
|
|
|
/*++
|
|
* @name ExfRundownCompleted
|
|
* @implemented NT5.1
|
|
*
|
|
* The ExfRundownCompleted routine completes the rundown of the specified
|
|
* descriptor by setting the active bit.
|
|
*
|
|
* @param RunRef
|
|
* Pointer to a rundown reference descriptor.
|
|
*
|
|
* @return None.
|
|
*
|
|
* @remarks Callers of ExfRundownCompleted must be running at IRQL <= APC_LEVEL.
|
|
*
|
|
*--*/
|
|
VOID
|
|
FASTCALL
|
|
ExfRundownCompleted(IN PEX_RUNDOWN_REF RunRef)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
/* Sanity check */
|
|
ASSERT((RunRef->Count & EX_RUNDOWN_ACTIVE) != 0);
|
|
|
|
/* Mark the counter as active */
|
|
ExpSetRundown(RunRef, EX_RUNDOWN_ACTIVE);
|
|
}
|
|
|
|
/*++
|
|
* @name ExfReleaseRundownProtection
|
|
* @implemented NT5.1
|
|
*
|
|
* The ExfReleaseRundownProtection routine releases the rundown protection
|
|
* reference for the specified descriptor.
|
|
*
|
|
* @param RunRef
|
|
* Pointer to a rundown reference descriptor.
|
|
*
|
|
* @return None.
|
|
*
|
|
* @remarks Callers of ExfReleaseRundownProtection can be running at any IRQL.
|
|
*
|
|
*--*/
|
|
VOID
|
|
FASTCALL
|
|
ExfReleaseRundownProtection(IN PEX_RUNDOWN_REF RunRef)
|
|
{
|
|
ULONG_PTR Value = RunRef->Count, NewValue;
|
|
PEX_RUNDOWN_WAIT_BLOCK WaitBlock;
|
|
|
|
/* Loop until successfully incremented the counter */
|
|
for (;;)
|
|
{
|
|
/* Check if rundown is not active */
|
|
if (!(Value & EX_RUNDOWN_ACTIVE))
|
|
{
|
|
/* Sanity check */
|
|
ASSERT((Value >= EX_RUNDOWN_COUNT_INC) || (KeNumberProcessors > 1));
|
|
|
|
/* Get the new value */
|
|
NewValue = Value - EX_RUNDOWN_COUNT_INC;
|
|
|
|
/* Change the value */
|
|
NewValue = ExpChangeRundown(RunRef, NewValue, Value);
|
|
if (NewValue == Value) break;
|
|
|
|
/* Update value */
|
|
Value = NewValue;
|
|
}
|
|
else
|
|
{
|
|
/* Get the wait block */
|
|
WaitBlock = (PEX_RUNDOWN_WAIT_BLOCK)(Value & ~EX_RUNDOWN_ACTIVE);
|
|
ASSERT((WaitBlock->Count > 0) || (KeNumberProcessors > 1));
|
|
|
|
/* Remove the one count */
|
|
if (!InterlockedDecrementSizeT(&WaitBlock->Count))
|
|
{
|
|
/* We're down to 0 now, so signal the event */
|
|
KeSetEvent(&WaitBlock->WakeEvent, IO_NO_INCREMENT, FALSE);
|
|
}
|
|
|
|
/* We're all done */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*++
|
|
* @name ExfReleaseRundownProtectionEx
|
|
* @implemented NT5.2
|
|
*
|
|
* The ExfReleaseRundownProtectionEx routine releases multiple rundown
|
|
* protection references for the specified descriptor.
|
|
*
|
|
* @param RunRef
|
|
* Pointer to a rundown reference descriptor.
|
|
*
|
|
* @param Count
|
|
* Number of times to dereference the descriptor.
|
|
*
|
|
* @return None.
|
|
*
|
|
* @remarks Callers of ExfAcquireRundownProtectionEx can be running at any IRQL.
|
|
*
|
|
*--*/
|
|
VOID
|
|
FASTCALL
|
|
ExfReleaseRundownProtectionEx(IN PEX_RUNDOWN_REF RunRef,
|
|
IN ULONG Count)
|
|
{
|
|
ULONG_PTR Value = RunRef->Count, NewValue;
|
|
PEX_RUNDOWN_WAIT_BLOCK WaitBlock;
|
|
|
|
/* Loop until successfully incremented the counter */
|
|
for (;;)
|
|
{
|
|
/* Check if rundown is not active */
|
|
if (!(Value & EX_RUNDOWN_ACTIVE))
|
|
{
|
|
/* Sanity check */
|
|
ASSERT((Value >= EX_RUNDOWN_COUNT_INC * Count) ||
|
|
(KeNumberProcessors > 1));
|
|
|
|
/* Get the new value */
|
|
NewValue = Value - EX_RUNDOWN_COUNT_INC * Count;
|
|
|
|
/* Change the value */
|
|
NewValue = ExpChangeRundown(RunRef, NewValue, Value);
|
|
if (NewValue == Value) break;
|
|
|
|
/* Update value */
|
|
Value = NewValue;
|
|
}
|
|
else
|
|
{
|
|
/* Get the wait block */
|
|
WaitBlock = (PEX_RUNDOWN_WAIT_BLOCK)(Value & ~EX_RUNDOWN_ACTIVE);
|
|
ASSERT((WaitBlock->Count >= Count) || (KeNumberProcessors > 1));
|
|
|
|
/* Remove the counts */
|
|
if (InterlockedExchangeAddSizeT(&WaitBlock->Count,
|
|
-(LONG)Count) == (LONG)Count)
|
|
{
|
|
/* We're down to 0 now, so signal the event */
|
|
KeSetEvent(&WaitBlock->WakeEvent, IO_NO_INCREMENT, FALSE);
|
|
}
|
|
|
|
/* We're all done */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*++
|
|
* @name ExfWaitForRundownProtectionRelease
|
|
* @implemented NT5.1
|
|
*
|
|
* The ExfWaitForRundownProtectionRelease routine waits until the specified
|
|
* rundown descriptor has been released.
|
|
*
|
|
* @param RunRef
|
|
* Pointer to a rundown reference descriptor.
|
|
*
|
|
* @return None.
|
|
*
|
|
* @remarks Callers of ExfWaitForRundownProtectionRelease must be running
|
|
* at IRQL <= APC_LEVEL.
|
|
*
|
|
*--*/
|
|
VOID
|
|
FASTCALL
|
|
ExfWaitForRundownProtectionRelease(IN PEX_RUNDOWN_REF RunRef)
|
|
{
|
|
ULONG_PTR Value, Count, NewValue;
|
|
EX_RUNDOWN_WAIT_BLOCK WaitBlock;
|
|
PEX_RUNDOWN_WAIT_BLOCK WaitBlockPointer;
|
|
PKEVENT Event;
|
|
PAGED_CODE();
|
|
|
|
/* Set the active bit */
|
|
Value = ExpChangeRundown(RunRef, EX_RUNDOWN_ACTIVE, 0);
|
|
if ((Value == 0) || (Value == EX_RUNDOWN_ACTIVE)) return;
|
|
|
|
/* No event for now */
|
|
Event = NULL;
|
|
WaitBlockPointer = (PEX_RUNDOWN_WAIT_BLOCK)((ULONG_PTR)&WaitBlock |
|
|
EX_RUNDOWN_ACTIVE);
|
|
|
|
/* Start waitblock set loop */
|
|
for (;;)
|
|
{
|
|
/* Save the count */
|
|
Count = Value >> EX_RUNDOWN_COUNT_SHIFT;
|
|
|
|
/* If the count is over one and we don't have en event yet, create it */
|
|
if ((Count) && !(Event))
|
|
{
|
|
/* Initialize the event */
|
|
KeInitializeEvent(&WaitBlock.WakeEvent,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
/* Set the pointer */
|
|
Event = &WaitBlock.WakeEvent;
|
|
}
|
|
|
|
/* Set the count */
|
|
WaitBlock.Count = Count;
|
|
|
|
/* Now set the pointer */
|
|
NewValue = ExpChangeRundown(RunRef, (ULONG_PTR)WaitBlockPointer, Value);
|
|
if (NewValue == Value) break;
|
|
|
|
/* Loop again */
|
|
Value = NewValue;
|
|
ASSERT((Value & EX_RUNDOWN_ACTIVE) == 0);
|
|
}
|
|
|
|
/* If the count was 0, we're done */
|
|
if (!Count) return;
|
|
|
|
/* Wait for whoever needs to release to notify us */
|
|
KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL);
|
|
ASSERT(WaitBlock.Count == 0);
|
|
}
|
|
|
|
/* FIXME: STUBS **************************************************************/
|
|
|
|
/*
|
|
* @unimplemented NT5.2
|
|
*/
|
|
BOOLEAN
|
|
FASTCALL
|
|
ExfAcquireRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
|
|
{
|
|
DBG_UNREFERENCED_PARAMETER(RunRefCacheAware);
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented 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;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented NT5.2
|
|
*/
|
|
VOID
|
|
FASTCALL
|
|
ExfReleaseRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
|
|
{
|
|
DBG_UNREFERENCED_PARAMETER(RunRefCacheAware);
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented NT5.2
|
|
*/
|
|
VOID
|
|
FASTCALL
|
|
ExfReleaseRundownProtectionCacheAwareEx(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware,
|
|
IN ULONG Count)
|
|
{
|
|
DBG_UNREFERENCED_PARAMETER(RunRefCacheAware);
|
|
DBG_UNREFERENCED_PARAMETER(Count);
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented NT5.2
|
|
*/
|
|
VOID
|
|
FASTCALL
|
|
ExfWaitForRundownProtectionReleaseCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
|
|
{
|
|
DBG_UNREFERENCED_PARAMETER(RunRefCacheAware);
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented NT5.2
|
|
*/
|
|
VOID
|
|
FASTCALL
|
|
ExfRundownCompletedCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
|
|
{
|
|
DBG_UNREFERENCED_PARAMETER(RunRefCacheAware);
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented NT5.2
|
|
*/
|
|
VOID
|
|
FASTCALL
|
|
ExfReInitializeRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
|
|
{
|
|
DBG_UNREFERENCED_PARAMETER(RunRefCacheAware);
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented NT5.2
|
|
*/
|
|
PEX_RUNDOWN_REF_CACHE_AWARE
|
|
NTAPI
|
|
ExAllocateCacheAwareRundownProtection(IN POOL_TYPE PoolType,
|
|
IN ULONG Tag)
|
|
{
|
|
DBG_UNREFERENCED_PARAMETER(PoolType);
|
|
DBG_UNREFERENCED_PARAMETER(Tag);
|
|
UNIMPLEMENTED;
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented NT5.2
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
ExFreeCacheAwareRundownProtection(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
|
|
{
|
|
DBG_UNREFERENCED_PARAMETER(RunRefCacheAware);
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented NT5.2
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
ExInitializeRundownProtectionCacheAware(IN PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware,
|
|
IN SIZE_T Count)
|
|
{
|
|
DBG_UNREFERENCED_PARAMETER(RunRefCacheAware);
|
|
DBG_UNREFERENCED_PARAMETER(Count);
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented NT5.2
|
|
*/
|
|
SIZE_T
|
|
NTAPI
|
|
ExSizeOfRundownProtectionCacheAware(VOID)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return 0;
|
|
}
|
|
|