diff --git a/NTOSKRNL/KE/sringlock.cpp b/NTOSKRNL/KE/sringlock.cpp new file mode 100644 index 0000000..db9130c --- /dev/null +++ b/NTOSKRNL/KE/sringlock.cpp @@ -0,0 +1,179 @@ +/* + * PROJECT: Alcyone System Kernel + * LICENSE: BSD Clause 3 + * PURPOSE: Spinning Ring Lock + * NT KERNEL: 5.11.9360 + * COPYRIGHT: 2023-2029 Dibymartanda Samanta <> + */ + + /*Alcyone's Spinning Ring Lock is a tweaked Mellor-Crummey and Scott Spin Lock algorithm that utilizes a Ring Buffer for + implementing a FIFO algorithm for lock acquisition and unlocking. The Ring Buffer queue is completely lock-free, utilizing a Hazard + Pointer for accessing members of the structure. + + It has been designed for high contention scenarios to provide: + + - High degree of scalability + - Fairness + - Reduced cache coherency traffic + - Reduced latency + - Superior cache line utilization + + Note: The Spinning Ring Lock should only be used when dealing with high contention scenarios */ + +/* Spinning Ring Lock Data Type */ +typedef struct _MCS_NODE { + struct _MCS_NODE* Next; + LONG Locked; +} MCS_NODE, *PMCS_NODE; + +typedef struct _SRING_LOCK { + volatile PMCS_NODE Tail; + MCS_NODE RingBuffer[RING_BUFFER_SIZE]; + volatile LONG64 Head; + volatile LONG64 Tail; + volatile PMCS_NODE HazardPointer; +} SRING_LOCK, *PSRING_LOCK; + +/* In Future if needed Size can be increased , but fallback to Dynamic Allocation is present*/ +#define RING_BUFFER_SIZE 256 + + +/* Internal Function for MCS Node Allocation */ +PMCS_NODE +KiAllocateRingLockNode( + _Inout_ PSRING_LOCK Lock +) +{ + LONG64 OldTail, NewTail; + PMCS_NODE Node; + + do { + OldTail = Lock->Tail; + NewTail = (OldTail + 1) & (RING_BUFFER_SIZE - 1); + + if (NewTail == (Lock->Head & (RING_BUFFER_SIZE - 1))) + { + /*Ring buffer is full, fallback to dynamic allocation*/ + return ExAllocatePoolWithTag(NonPagedPool, sizeof(MCS_NODE), 'MCSN'); + } + } while (InterlockedCompareExchange64(&Lock->Tail, NewTail, OldTail) != OldTail); + + Node = &Lock->RingBuffer[OldTail]; + + /*Set hazard pointer to protect the node from being freed*/ + InterlockedExchangePointer((PVOID*)&Lock->HazardPointer, Node); + KeMemoryBarrier(); + + return Node; +} +VOID +KiFreeRingLockNode( + _Inout_ PSRING_LOCK Lock, + _In_ PMCS_NODE Node +) +{ + if (Node < &Lock->RingBuffer[0] || Node >= &Lock->RingBuffer[RING_BUFFER_SIZE]) { + /* Node was dynamically allocated, free the pool */ + ExFreePoolWithTag(Node, 'MCSN'); + return; + } + + /*Clear hazard pointer if it's pointing to this node*/ + if (InterlockedCompareExchangePointer((PVOID*)&Lock->HazardPointer, NULL, Node) == Node) { + LONG64 NewHead; + do { + NewHead = (Lock->Head + 1) & (RING_BUFFER_SIZE - 1); + } while (InterlockedCompareExchange64(&Lock->Head, NewHead, Lock->Head) != Lock->Head); + } +} + +/* External Function */ + +KeInitializeSpinningRingLock( + _Out_ PSRING_LOCK Lock +) +{ + Lock->Tail = NULL; + RtlZeroMemory(Lock->RingBuffer, sizeof(Lock->RingBuffer)); + Lock->Head = 0; + Lock->Tail = 0; + Lock->HazardPointer = NULL; +} + +VOID +KeAcquireSpinningRingLock( + _Inout_ PSRING_LOCK Lock +) +{ + PMCS_NODE Node = KiAllocateRingLockNode(Lock); + PMCS_NODE Predecessor; + + Node->Next = NULL; + Node->Locked = 1; + + // Set hazard pointer before we exchange + InterlockedExchangePointer((PVOID*)&Lock->HazardPointer, Node); + KeMemoryBarrier(); + + Predecessor = (PMCS_NODE)InterlockedExchangePointer((PVOID*)&Lock->Tail, Node); + + if (Predecessor != NULL) { + /*Set hazard pointer to predecessor*/ + InterlockedExchangePointer((PVOID*)&Lock->HazardPointer, Predecessor); + KeMemoryBarrier(); + + Predecessor->Next = Node; + while (Node->Locked) { + KeYieldProcessor(); + } + } + + /*Clear hazard pointer*/ + InterlockedExchangePointer((PVOID*)&Lock->HazardPointer, NULL); +} + +VOID +KeReleaseSpinningRingLock( + _Inout_ PSRING_LOCK Lock +) +{ + PMCS_NODE Node = Lock->Tail; + + // Set hazard pointer + InterlockedExchangePointer((PVOID*)&Lock->HazardPointer, Node); + KeMemoryBarrier(); + + if (Node->Next == NULL) { + if (InterlockedCompareExchangePointer((PVOID*)&Lock->Tail, NULL, Node) == Node) + { + /*Clear hazard pointer before returning*/ + InterlockedExchangePointer((PVOID*)&Lock->HazardPointer, NULL); + KiFreeRingLockNode(Lock, Node); + return; + } + + while (Node->Next == NULL) { + KeYieldProcessor(); + } + } + + /*Set hazard pointer to next node*/ + InterlockedExchangePointer((PVOID*)&Lock->HazardPointer, Node->Next); + KeMemoryBarrier(); + + Node->Next->Locked = 0; + + /*Clear hazard pointer*/ + InterlockedExchangePointer((PVOID*)&Lock->HazardPointer, NULL); + + KiFreeRingLockNode(Lock, Node); +} + +/* Understanding Bit Manipulation being done to retrieving data from RING Buffer, +(Lock->Tail + 1) & (RING_BUFFER_SIZE - 1) +RING_BUFFER_SIZE` is defined as 256, which is a power of 2. This is crucial for this operation to work. +(RING_BUFFER_SIZE - 1)` is 255, which in binary is "11111111" +(Lock->Tail + 1)` increments the tail index. +Now, bitwise AND operation `&` with `(RING_BUFFER_SIZE - 1) act like % Operator but much faster in performance */ + +