1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/io/iomgr/remlock.c 5 * PURPOSE: Remove Lock Support 6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 7 * Filip Navara (navaraf@reactos.org) 8 * Pierre Schweitzer (pierre.schweitzer@reactos.org) 9 */ 10 11 /* INCLUDES ******************************************************************/ 12 13 #include <ntoskrnl.h> 14 #define NDEBUG 15 #include <debug.h> 16 17 typedef struct _IO_REMOVE_LOCK_TRACKING_BLOCK 18 { 19 PIO_REMOVE_LOCK_TRACKING_BLOCK Next; 20 PVOID Tag; 21 LARGE_INTEGER LockMoment; 22 LPCSTR File; 23 ULONG Line; 24 } IO_REMOVE_LOCK_TRACKING_BLOCK; 25 26 /* FUNCTIONS *****************************************************************/ 27 28 /* 29 * @implemented 30 */ 31 VOID 32 NTAPI 33 IoInitializeRemoveLockEx(IN PIO_REMOVE_LOCK RemoveLock, 34 IN ULONG AllocateTag, 35 IN ULONG MaxLockedMinutes, 36 IN ULONG HighWatermark, 37 IN ULONG RemlockSize) 38 { 39 PEXTENDED_IO_REMOVE_LOCK Lock = (PEXTENDED_IO_REMOVE_LOCK)RemoveLock; 40 PAGED_CODE(); 41 42 DPRINT("%s(%p 0x%08x %u %u %u)\n", __FUNCTION__, RemoveLock, AllocateTag, MaxLockedMinutes, HighWatermark, RemlockSize); 43 44 ASSERT(HighWatermark < MAXLONG); 45 46 /* If no lock given, nothing to do */ 47 if (!Lock) 48 { 49 return; 50 } 51 52 switch (RemlockSize) 53 { 54 /* Check if this is a debug lock */ 55 case (sizeof(IO_REMOVE_LOCK_DBG_BLOCK) + sizeof(IO_REMOVE_LOCK_COMMON_BLOCK)): 56 /* Setup debug parameters */ 57 Lock->Dbg.Signature = 'COLR'; 58 Lock->Dbg.HighWatermark = HighWatermark; 59 Lock->Dbg.MaxLockedTicks = KeQueryTimeIncrement() * MaxLockedMinutes * 600000000; 60 Lock->Dbg.AllocateTag = AllocateTag; 61 KeInitializeSpinLock(&(Lock->Dbg.Spin)); 62 Lock->Dbg.LowMemoryCount = 0; 63 Lock->Dbg.Blocks = NULL; 64 65 case sizeof(IO_REMOVE_LOCK_COMMON_BLOCK): 66 /* Setup a free block */ 67 Lock->Common.Removed = FALSE; 68 Lock->Common.IoCount = 1; 69 KeInitializeEvent(&Lock->Common.RemoveEvent, 70 SynchronizationEvent, 71 FALSE); 72 } 73 } 74 75 /* 76 * @implemented 77 */ 78 NTSTATUS 79 NTAPI 80 IoAcquireRemoveLockEx(IN PIO_REMOVE_LOCK RemoveLock, 81 IN OPTIONAL PVOID Tag, 82 IN LPCSTR File, 83 IN ULONG Line, 84 IN ULONG RemlockSize) 85 { 86 KIRQL OldIrql; 87 LONG LockValue; 88 PIO_REMOVE_LOCK_TRACKING_BLOCK TrackingBlock; 89 PEXTENDED_IO_REMOVE_LOCK Lock = (PEXTENDED_IO_REMOVE_LOCK)RemoveLock; 90 91 DPRINT("%s(%p %p %s %u %u)\n", __FUNCTION__, RemoveLock, Tag, File, Line, RemlockSize); 92 93 /* Increase the lock count */ 94 LockValue = InterlockedIncrement(&(Lock->Common.IoCount)); 95 ASSERT(LockValue > 0); 96 if (!Lock->Common.Removed) 97 { 98 /* Check what kind of lock this is */ 99 if (RemlockSize == (sizeof(IO_REMOVE_LOCK_DBG_BLOCK) + sizeof(IO_REMOVE_LOCK_COMMON_BLOCK))) 100 { 101 ASSERT(Lock->Dbg.HighWatermark == 0 || LockValue <= Lock->Dbg.HighWatermark); 102 103 /* Allocate a tracking block */ 104 TrackingBlock = ExAllocatePoolWithTag(NonPagedPool, sizeof(IO_REMOVE_LOCK_TRACKING_BLOCK), Lock->Dbg.AllocateTag); 105 if (!TrackingBlock) 106 { 107 /* Keep count of failures for lock release and missing tags */ 108 InterlockedIncrement(&(Lock->Dbg.LowMemoryCount)); 109 } 110 else 111 { 112 /* Initialize block */ 113 RtlZeroMemory(TrackingBlock, sizeof(IO_REMOVE_LOCK_TRACKING_BLOCK)); 114 TrackingBlock->Tag = Tag; 115 TrackingBlock->File = File; 116 TrackingBlock->Line = Line; 117 KeQueryTickCount(&(TrackingBlock->LockMoment)); 118 119 /* Queue the block */ 120 KeAcquireSpinLock(&(Lock->Dbg.Spin), &OldIrql); 121 TrackingBlock->Next = Lock->Dbg.Blocks; 122 Lock->Dbg.Blocks = TrackingBlock; 123 KeReleaseSpinLock(&(Lock->Dbg.Spin), OldIrql); 124 } 125 } 126 } 127 else 128 { 129 /* Otherwise, decrement the count and check if it's gone */ 130 if (!InterlockedDecrement(&(Lock->Common.IoCount))) 131 { 132 /* Signal the event */ 133 KeSetEvent(&(Lock->Common.RemoveEvent), IO_NO_INCREMENT, FALSE); 134 } 135 136 /* Return pending delete */ 137 return STATUS_DELETE_PENDING; 138 } 139 140 /* Otherwise, return success */ 141 return STATUS_SUCCESS; 142 } 143 144 /* 145 * @implemented 146 */ 147 VOID 148 NTAPI 149 IoReleaseRemoveLockEx(IN PIO_REMOVE_LOCK RemoveLock, 150 IN PVOID Tag, 151 IN ULONG RemlockSize) 152 { 153 KIRQL OldIrql; 154 LONG LockValue; 155 BOOLEAN TagFound; 156 LARGE_INTEGER CurrentMoment; 157 PIO_REMOVE_LOCK_TRACKING_BLOCK TrackingBlock; 158 PIO_REMOVE_LOCK_TRACKING_BLOCK *TrackingBlockLink; 159 PEXTENDED_IO_REMOVE_LOCK Lock = (PEXTENDED_IO_REMOVE_LOCK)RemoveLock; 160 161 DPRINT("%s(%p %p %u)\n", __FUNCTION__, RemoveLock, Tag, RemlockSize); 162 163 /* Check what kind of lock this is */ 164 if (RemlockSize == (sizeof(IO_REMOVE_LOCK_DBG_BLOCK) + sizeof(IO_REMOVE_LOCK_COMMON_BLOCK))) 165 { 166 /* Acquire blocks queue */ 167 KeAcquireSpinLock(&(Lock->Dbg.Spin), &OldIrql); 168 169 /* Get the release moment */ 170 KeQueryTickCount(&CurrentMoment); 171 172 /* Start browsing tracking blocks to find a block that would match given tag */ 173 TagFound = FALSE; 174 TrackingBlock = Lock->Dbg.Blocks; 175 TrackingBlockLink = &(Lock->Dbg.Blocks); 176 while (TrackingBlock != NULL) 177 { 178 /* First of all, check if the lock was locked for too long */ 179 if (Lock->Dbg.MaxLockedTicks && 180 CurrentMoment.QuadPart - TrackingBlock->LockMoment.QuadPart > Lock->Dbg.MaxLockedTicks) 181 { 182 DPRINT("Lock %#08lx (with tag %#08lx) was supposed to be held at max %I64d ticks but lasted longer\n", 183 Lock, TrackingBlock->Tag, Lock->Dbg.MaxLockedTicks); 184 DPRINT("Lock was acquired in file %s at line %lu\n", TrackingBlock->File, TrackingBlock->Line); 185 ASSERT(FALSE); 186 } 187 188 /* Check if this is the first matching tracking block */ 189 if ((TagFound == FALSE) && (TrackingBlock->Tag == Tag)) 190 { 191 /* Unlink this tracking block, and free it */ 192 TagFound = TRUE; 193 *TrackingBlockLink = TrackingBlock->Next; 194 ExFreePoolWithTag(TrackingBlock, Lock->Dbg.AllocateTag); 195 TrackingBlock = *TrackingBlockLink; 196 } 197 else 198 { 199 /* Go to the next tracking block */ 200 TrackingBlockLink = &(TrackingBlock->Next); 201 TrackingBlock = TrackingBlock->Next; 202 } 203 } 204 205 /* We're done, release queue lock */ 206 KeReleaseSpinLock(&(Lock->Dbg.Spin), OldIrql); 207 208 /* If we didn't find any matching block */ 209 if (TagFound == FALSE) 210 { 211 /* Check if it was because we were low in memory 212 * If yes, then ignore, that's normal 213 */ 214 if (InterlockedDecrement(&(Lock->Dbg.LowMemoryCount)) < 0) 215 { 216 /* Otherwise signal the issue, it shouldn't happen */ 217 InterlockedIncrement(&(Lock->Dbg.LowMemoryCount)); 218 DPRINT("Failed finding block for tag: %#08lx\n", Tag); 219 ASSERT(FALSE); 220 } 221 } 222 } 223 224 /* Decrement the lock count */ 225 LockValue = InterlockedDecrement(&(Lock->Common.IoCount)); 226 ASSERT(LockValue >= 0); 227 228 if (!LockValue) 229 { 230 /* Someone should be waiting... */ 231 ASSERT(Lock->Common.Removed); 232 233 /* Signal the event */ 234 KeSetEvent(&(Lock->Common.RemoveEvent), IO_NO_INCREMENT, FALSE); 235 } 236 } 237 238 /* 239 * @implemented 240 */ 241 VOID 242 NTAPI 243 IoReleaseRemoveLockAndWaitEx(IN PIO_REMOVE_LOCK RemoveLock, 244 IN PVOID Tag, 245 IN ULONG RemlockSize) 246 { 247 LONG LockValue; 248 PIO_REMOVE_LOCK_TRACKING_BLOCK TrackingBlock; 249 PEXTENDED_IO_REMOVE_LOCK Lock = (PEXTENDED_IO_REMOVE_LOCK)RemoveLock; 250 PAGED_CODE(); 251 252 DPRINT("%s(%p %p %u)\n", __FUNCTION__, RemoveLock, Tag, RemlockSize); 253 254 /* Remove the lock and decrement the count */ 255 Lock->Common.Removed = TRUE; 256 LockValue = InterlockedDecrement(&Lock->Common.IoCount); 257 ASSERT(LockValue > 0); 258 259 /* If we are still > 0, then wait for the others to remove lock */ 260 if (InterlockedDecrement(&Lock->Common.IoCount) > 0) 261 { 262 /* Wait for it */ 263 KeWaitForSingleObject(&(Lock->Common.RemoveEvent), 264 Executive, 265 KernelMode, 266 FALSE, 267 NULL); 268 } 269 270 /* Check what kind of lock this is */ 271 if (RemlockSize == (sizeof(IO_REMOVE_LOCK_DBG_BLOCK) + sizeof(IO_REMOVE_LOCK_COMMON_BLOCK))) 272 { 273 /* A block must be remaining */ 274 ASSERT(Lock->Dbg.Blocks); 275 276 /* Get it */ 277 TrackingBlock = Lock->Dbg.Blocks; 278 /* Tag should match */ 279 if (TrackingBlock->Tag != Tag) 280 { 281 DPRINT("Last tracking block tag invalid! Expected: %p, having: %p\n", Tag, TrackingBlock->Tag); 282 ASSERT(TrackingBlock->Tag != Tag); 283 } 284 285 /* Release block */ 286 ExFreePoolWithTag(Lock->Dbg.Blocks, Lock->Dbg.AllocateTag); 287 } 288 } 289 290 /* EOF */ 291