kernel
Loading...
Searching...
No Matches
pushlock.c
Go to the documentation of this file.
1/*++
2
3Module Name:
4
5 pushlock.c
6
7Purpose:
8
9 This translation unit contains the implementation of kernel push lock synchronization.
10
11Author:
12
13 slep (Matanel) 2025.
14
15Revision History:
16
17--*/
18
19#include "../../includes/ms.h"
21#include "../../includes/mm.h"
22
23static
24void
25MspSuspendPushLock(
26 IN PUSH_LOCK* Lock,
27 IN PUSH_LOCK_WAIT_BLOCK* WaitBlock,
28 IN uint64_t CurrentValue
29)
30{
31 // We use SynchronizationEvent because we want to wake 1 waiter at a time.
32 WaitBlock->WakeEvent.type = SynchronizationEvent;
33 WaitBlock->WakeEvent.signaled = false;
34 WaitBlock->WakeEvent.lock.locked = 0;
35
36 // Initialize list head.
37 InitializeListHead((PDOUBLY_LINKED_LIST)&WaitBlock->WakeEvent.waitingQueue);
38
39 WaitBlock->Signaled = false;
40 WaitBlock->ShareCount = 0;
41
42 // If the lock currently has readers (Shared Count > 0) and is not a pointer to a wait list yet,
43 // we must save that count so we don't lose track of those readers.
44 if ((CurrentValue & ~PL_FLAG_MASK) > 0 && !(CurrentValue & PL_WAIT_BIT)) {
45 WaitBlock->ShareCount = (uint32_t)(CurrentValue >> 4);
46 }
47
48 // Push waitblock to head of &Lock->Value
49 while (true) {
50 // If the Waiting bit is set, the value is a pointer to the current head
51 if (CurrentValue & PL_WAIT_BIT) {
52 WaitBlock->Next = (PUSH_LOCK_WAIT_BLOCK*)(CurrentValue & ~PL_FLAG_MASK);
53 }
54 else {
55 // No waiters yet. Next is NULL.
56 WaitBlock->Next = NULL;
57 }
58
59 // Calculate new value: Pointer to Us | Waiting Bit | Lock Bit
60 // We keep the Lock Bit set to prevent new fast path acquires while we wait.
61 uint64_t NewValue = (uint64_t)WaitBlock | PL_WAIT_BIT | PL_LOCK_BIT;
62
63 // Atomic Swap
64 uint64_t Result = InterlockedCompareExchangeU64(&Lock->Value, NewValue, CurrentValue);
65
66 if (Result == CurrentValue) {
67 // Success
68 break;
69 }
70
71 // Failed (value changed by another core), retry.
72 CurrentValue = Result;
73 }
74
75 // We are now in the queue. We wait for our WakeEvent to be signaled by the releaser.
76 MsWaitForEvent(&WaitBlock->WakeEvent);
77}
78
79void
81 IN PUSH_LOCK* Lock
82)
83{
84 // If nobody owns an exclusive lock, we return and set it to owned.
85 if (InterlockedCompareExchangeU64(&Lock->Value, PL_LOCK_BIT, 0) == 0) {
86 return;
87 }
88
89 // Allocate a wait block.
91 if (!WaitBlock) return; // idk what to do here to be honest.
92
93 // An exclusive lock is owned.., we just push to the waitblock.
94 WaitBlock->Flags = PL_FLAGS_EXCLUSIVE;
95 MspSuspendPushLock(Lock, WaitBlock, Lock->Value);
96}
97
98void
100 IN PUSH_LOCK* Lock
101)
102{
103 uint64_t Value, NewValue;
104
105 // If the value is the bit, we just set to 0 (no waiters exist)
106 if (InterlockedCompareExchangeU64(&Lock->Value, 0, PL_LOCK_BIT) == PL_LOCK_BIT) {
107 return;
108 }
109
110 // Waiters exist, we must release them.
111 while (true) {
112 Value = Lock->Value;
113
114 // If somebody cleared the lock bit but left waiters? (shouldn't happen in standard flow but safe to check)
115 if (!(Value & PL_WAIT_BIT)) {
116 InterlockedAndU64(&Lock->Value, ~PL_LOCK_BIT);
117 return;
118 }
119
120 // Get the list head
122
123 // Pop next item
124 PUSH_LOCK_WAIT_BLOCK* Next = Head->Next;
125
126 // Calculate New Value
127 // If Next is NULL, we clear the Waiting bit.
128 NewValue = (uint64_t)Next;
129 if (NewValue != 0) NewValue |= PL_WAIT_BIT;
130
131 // If ther waiter we are waking is an exclusive waiter, we can leave the bit, its an optimization.
132 if (Head->Flags == PL_FLAGS_EXCLUSIVE) {
133 NewValue |= PL_LOCK_BIT;
134 }
135
136 // Try to update the lock pointer
137 if (InterlockedCompareExchangeU64(&Lock->Value, NewValue, Value) == Value) {
138 // Wake the next waiter.
139 MsSetEvent(&Head->WakeEvent);
140
141 // Free the waiters memory allocated.
142 MmFreePool(Head);
143
144 return;
145 }
146 }
147}
148
149void
151 IN PUSH_LOCK* Lock
152)
153{
154 uint64_t Value, NewValue;
155
156 while (true) {
157 Value = Lock->Value;
158
159 // If Locked (Bit 0) or Waiting (Bit 1) is set, we must wait.
160 // We just sleep.
161 if (Value & (PL_LOCK_BIT | PL_WAIT_BIT)) {
163 if (!WaitBlock) return; // idk what to do here to be honest.
164 WaitBlock->Flags = PL_FLAGS_SHARED;
165 MspSuspendPushLock(Lock, WaitBlock, Value);
166 return;
167 }
168
169 // Increment share count, no one is locking or waiting.
170 NewValue = Value + PL_SHARE_INC;
171
172 if (InterlockedCompareExchangeU64(&Lock->Value, NewValue, Value) == Value) {
173 return;
174 }
175 }
176}
177
178void
180 IN PUSH_LOCK* Lock
181)
182{
183 uint64_t Value, NewValue;
184
185 while (true) {
186 Value = Lock->Value;
187
188 // Check if there are waiters
189 if (Value & PL_WAIT_BIT) {
190 // If someone is waiting, the Value is no longer a count of shared threads, but a pointer to setting the event to wake the exclusive waiter.
191 // If no more shares, we wake up the waiter and he acquires the exclusive lock.
192
194 PUSH_LOCK_WAIT_BLOCK* Last = Head;
195
196 // Find the end of the chain (TODO LAST PTR TO MAKE IT FASTER (HINT))
197 while (Last->Next != NULL) {
198 Last = Last->Next;
199 }
200
201 // Decrement the saved count in the wait block
202 if (InterlockedDecrementU32(&Last->ShareCount) == 0) {
203 // If we hit zero, we signal the writer that he can stop waiting for readers.
204 MsSetEvent(&Last->WakeEvent);
205 }
206 return;
207 }
208
209 // Decrement shared count.
210 NewValue = Value - PL_SHARE_INC;
211
212 if (InterlockedCompareExchangeU64(&Lock->Value, NewValue, Value) == Value) {
213 return;
214 }
215 }
216}
#define IN
Definition annotations.h:8
FORCEINLINE uint64_t InterlockedAndU64(volatile uint64_t *target, uint64_t value)
Definition atomic.h:140
FORCEINLINE uint64_t InterlockedCompareExchangeU64(volatile uint64_t *target, uint64_t value, uint64_t comparand)
Definition atomic.h:86
FORCEINLINE uint32_t InterlockedDecrementU32(volatile uint32_t *target)
Definition atomic.h:122
struct _DOUBLY_LINKED_LIST * PDOUBLY_LINKED_LIST
MTSTATUS MsWaitForEvent(IN PEVENT event)
Definition events.c:117
MTSTATUS MsSetEvent(IN PEVENT event)
Definition events.c:13
@ NonPagedPool
Definition mm.h:355
struct _PUSH_LOCK PUSH_LOCK
#define PL_FLAGS_SHARED
Definition ms.h:118
FORCEINLINE void InitializeListHead(PDOUBLY_LINKED_LIST Head)
Definition ms.h:223
struct _PUSH_LOCK_WAIT_BLOCK PUSH_LOCK_WAIT_BLOCK
@ SynchronizationEvent
Definition ms.h:63
#define PL_FLAG_MASK
Definition ms.h:124
#define PL_SHARE_INC
Definition ms.h:125
#define PL_FLAGS_EXCLUSIVE
Definition ms.h:117
#define PL_LOCK_BIT
Definition ms.h:121
#define PL_WAIT_BIT
Definition ms.h:122
void MmFreePool(IN void *buf)
Definition pool.c:632
void * MmAllocatePoolWithTag(IN enum _POOL_TYPE PoolType, IN size_t NumberOfBytes, IN uint32_t Tag)
Definition pool.c:443
void MsAcquirePushLockExclusive(IN PUSH_LOCK *Lock)
Definition pushlock.c:80
void MsAcquirePushLockShared(IN PUSH_LOCK *Lock)
Definition pushlock.c:150
void MsReleasePushLockExclusive(IN PUSH_LOCK *Lock)
Definition pushlock.c:99
void MsReleasePushLockShared(IN PUSH_LOCK *Lock)
Definition pushlock.c:179
struct _PUSH_LOCK_WAIT_BLOCK * Next
Definition ms.h:107
uint32_t ShareCount
Definition ms.h:113
uint32_t Flags
Definition ms.h:112