1 /*++
2
3 Copyright (C) Microsoft Corporation, 1990 - 1998
4
5 Module Name:
6
7 lock.c
8
9 Abstract:
10
11 This is the NT SCSI port driver.
12
13 Environment:
14
15 kernel mode only
16
17 Notes:
18
19 This module is a driver dll for scsi miniports.
20
21 Revision History:
22
23 --*/
24
25 #include "classp.h"
26 #include "debug.h"
27
28 #ifdef DEBUG_USE_WPP
29 #include "lock.tmh"
30 #endif
31
32
33 LONG LockHighWatermark = 0;
34 LONG LockLowWatermark = 0;
35 LONG MaxLockedMinutes = 5;
36
37 //
38 // Structure used for tracking remove lock allocations in checked builds
39 //
40 typedef struct _REMOVE_TRACKING_BLOCK {
41 PVOID Tag;
42 LARGE_INTEGER TimeLocked;
43 PCSTR File;
44 ULONG Line;
45 } REMOVE_TRACKING_BLOCK, *PREMOVE_TRACKING_BLOCK;
46
47 /*++////////////////////////////////////////////////////////////////////////////
48
49 Classpnp RemoveLockRundown
50
51 RemoveLockRundown is a cacheaware rundown protection for the classpnp device object. While this
52 rundown protection is held successfully, the caller can assume that no pending pnp REMOVE
53 requests will be completed.
54
55 The RemoveLockRundown is a replacement of the original RemoveLock to improve the scalability.
56 For backward compatibility, we still keep the RemoveLock field in the device common extension structure.
57 However, the old RemoveLock is only being used in the DBG build.
58
59 The usage of the RemoveLockRundown is slightly different from the normal rundown protection usage.
60 The RemoveLockRundown is acquired via ClassAcquireRemoveLockEx() function
61 and released via ClassReleaseRemoveLock() function. Usually, we bail out when the acquisition
62 of rundown protection fails (calls to ExAcquireRundownProtectionCacheAware returns FALSE) and
63 will not release the rundown protection in acquisition failure. For the RemoveLockRundown,
64 the caller will always call ClassAcquireRemoveLockEx() and ClassReleaseRemoveLock() in a pair no
65 matter the return value of ClassAcquireRemoveLockEx(). Therefore, a thread may still call
66 ClassReleaseRemoveLock() even the previous acquisition RemoveLockRundown protection failed.
67
68 To deal with the previous acquisition failure case, we introduced a new field RemoveLockFailAcquire
69 as a counter for rundown acquisition failures. In the ClassReleaseRemoveLock() function, we only
70 release the rundown protection when this counter is decremented to zero. Since the change of RemoveLockFailAcquire
71 and release rundown protection is not protected by a lock as an atomic operation, we use a while loop over
72 InterlockedCompareExchange operation to make sure when we release the rundown protection, this counter is
73 actually zero.
74
75 --*/
76
77 /*++////////////////////////////////////////////////////////////////////////////
78
79 ClassAcquireRemoveLockEx()
80
81 Routine Description:
82
83 This routine is called to acquire the remove lock on the device object.
84 While the lock is held, the caller can assume that no pending pnp REMOVE
85 requests will be completed.
86
87 The lock should be acquired immediately upon entering a dispatch routine.
88 It should also be acquired before creating any new reference to the
89 device object if there's a chance of releasing the reference before the
90 new one is done.
91
92 This routine will return TRUE if the lock was successfully acquired or
93 FALSE if it cannot be because the device object has already been removed.
94
95 Arguments:
96
97 DeviceObject - the device object to lock
98
99 Tag - Used for tracking lock allocation and release. If an irp is
100 specified when acquiring the lock then the same Tag must be
101 used to release the lock before the Tag is completed.
102
103 Return Value:
104
105 The value of the IsRemoved flag in the device extension. If this is
106 non-zero then the device object has received a Remove irp and non-cleanup
107 IRP's should fail.
108
109 If the value is REMOVE_COMPLETE, the caller should not even release the
110 lock.
111
112 --*/
113 ULONG
114 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassAcquireRemoveLockEx(_In_ PDEVICE_OBJECT DeviceObject,_In_ PVOID Tag,_In_ PCSTR File,_In_ ULONG Line)115 ClassAcquireRemoveLockEx(
116 _In_ PDEVICE_OBJECT DeviceObject,
117 _In_ PVOID Tag,
118 _In_ PCSTR File,
119 _In_ ULONG Line
120 )
121 // This function implements the acquisition of Tag
122 #ifdef _MSC_VER
123 #pragma warning(suppress:28104)
124 #endif
125 {
126 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
127 BOOLEAN rundownAcquired;
128 PEX_RUNDOWN_REF_CACHE_AWARE removeLockRundown = NULL;
129
130 //
131 // Grab the remove lock
132 //
133
134 #if DBG
135
136 LONG lockValue;
137
138 lockValue = InterlockedIncrement(&commonExtension->RemoveLock);
139
140
141 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_LOCK, "ClassAcquireRemoveLock: "
142 "Acquired for Object %p & irp %p - count is %d\n",
143 DeviceObject, Tag, lockValue));
144
145 NT_ASSERTMSG("ClassAcquireRemoveLock - lock value was negative : ",
146 (lockValue > 0));
147
148 NT_ASSERTMSG("RemoveLock increased to meet LockHighWatermark",
149 ((LockHighWatermark == 0) ||
150 (lockValue != LockHighWatermark)));
151
152 if (commonExtension->IsRemoved != REMOVE_COMPLETE) {
153 PRTL_GENERIC_TABLE removeTrackingList = NULL;
154 REMOVE_TRACKING_BLOCK trackingBlock;
155 PREMOVE_TRACKING_BLOCK insertedTrackingBlock = NULL;
156 BOOLEAN newElement = FALSE;
157
158 KIRQL oldIrql;
159
160 trackingBlock.Tag = Tag;
161
162 trackingBlock.File = File;
163 trackingBlock.Line = Line;
164
165 KeQueryTickCount((&trackingBlock.TimeLocked));
166
167 KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
168 &oldIrql);
169
170 removeTrackingList = commonExtension->RemoveTrackingList;
171
172 if (removeTrackingList != NULL) {
173 insertedTrackingBlock = RtlInsertElementGenericTable(removeTrackingList,
174 &trackingBlock,
175 sizeof(REMOVE_TRACKING_BLOCK),
176 &newElement);
177 }
178
179 if (insertedTrackingBlock != NULL) {
180 if (!newElement) {
181 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassAcquireRemoveLock: "
182 "already tracking Tag %p\n", Tag));
183 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassAcquireRemoveLock: "
184 "acquired in file %s on line %d\n",
185 insertedTrackingBlock->File, insertedTrackingBlock->Line));
186 // NT_ASSERT(FALSE);
187
188 }
189 } else {
190 commonExtension->RemoveTrackingUntrackedCount++;
191
192 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_LOCK, ">>>>>ClassAcquireRemoveLock: "
193 "Cannot track Tag %p - currently %d untracked requsts\n",
194 Tag, commonExtension->RemoveTrackingUntrackedCount));
195 }
196
197 KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql);
198 }
199 #else
200
201 UNREFERENCED_PARAMETER(Tag);
202 UNREFERENCED_PARAMETER(File);
203 UNREFERENCED_PARAMETER(Line);
204
205 #endif
206
207 removeLockRundown = (PEX_RUNDOWN_REF_CACHE_AWARE)
208 ((PCHAR)commonExtension->PrivateCommonData + sizeof(CLASS_PRIVATE_COMMON_DATA));
209 rundownAcquired = ExAcquireRundownProtectionCacheAware(removeLockRundown);
210 if (!rundownAcquired) {
211 InterlockedIncrement((volatile LONG*) &(commonExtension->PrivateCommonData->RemoveLockFailAcquire));
212 TracePrint((TRACE_LEVEL_VERBOSE,
213 TRACE_FLAG_LOCK,
214 "ClassAcquireRemoveLockEx: RemoveLockRundown acquisition failed"
215 "RemoveLockFailAcquire = %d\n",
216 commonExtension->PrivateCommonData->RemoveLockFailAcquire));
217 }
218
219 return (commonExtension->IsRemoved);
220 }
221
222 /*++////////////////////////////////////////////////////////////////////////////
223
224 ClassReleaseRemoveLock()
225
226 Routine Description:
227
228 This routine is called to release the remove lock on the device object. It
229 must be called when finished using a previously locked reference to the
230 device object. If an Tag was specified when acquiring the lock then the
231 same Tag must be specified when releasing the lock.
232
233 When the lock count reduces to zero, this routine will signal the waiting
234 remove Tag to delete the device object. As a result the DeviceObject
235 pointer should not be used again once the lock has been released.
236
237 Arguments:
238
239 DeviceObject - the device object to lock
240
241 Tag - The irp (if any) specified when acquiring the lock. This is used
242 for lock tracking purposes
243
244 Return Value:
245
246 none
247
248 --*/
249 VOID
250 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassReleaseRemoveLock(_In_ PDEVICE_OBJECT DeviceObject,_In_opt_ PIRP Tag)251 ClassReleaseRemoveLock(
252 _In_ PDEVICE_OBJECT DeviceObject,
253 _In_opt_ PIRP Tag
254 )
255 // This function implements the release of Tag
256 #ifdef _MSC_VER
257 #pragma warning(suppress:28103)
258 #endif
259 {
260 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
261 LONG lockValue;
262 LONG oldValue;
263 PEX_RUNDOWN_REF_CACHE_AWARE removeLockRundown = NULL;
264
265 #if DBG
266 PRTL_GENERIC_TABLE removeTrackingList = NULL;
267 REMOVE_TRACKING_BLOCK searchDataBlock;
268
269 BOOLEAN found = FALSE;
270
271 BOOLEAN isRemoved = (commonExtension->IsRemoved == REMOVE_COMPLETE);
272
273 KIRQL oldIrql;
274
275 if (isRemoved) {
276 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_LOCK, "ClassReleaseRemoveLock: REMOVE_COMPLETE set; this should never happen"));
277 InterlockedDecrement(&(commonExtension->RemoveLock));
278 return;
279 }
280
281 KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
282 &oldIrql);
283
284 removeTrackingList = commonExtension->RemoveTrackingList;
285
286 if (removeTrackingList != NULL) {
287 searchDataBlock.Tag = Tag;
288 found = RtlDeleteElementGenericTable(removeTrackingList, &searchDataBlock);
289 }
290
291 if (!found) {
292 if(commonExtension->RemoveTrackingUntrackedCount == 0) {
293 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassReleaseRemoveLock: "
294 "Couldn't find Tag %p in the lock tracking list\n", Tag));
295 //
296 // This might happen if the device is being removed and the tracking list
297 // has already been freed. Don't assert if that is the case.
298 //
299 NT_ASSERT((removeTrackingList == NULL) && (commonExtension->IsRemoved != NO_REMOVE));
300 } else {
301 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassReleaseRemoveLock: "
302 "Couldn't find Tag %p in the lock tracking list - "
303 "may be one of the %d untracked requests still outstanding\n",
304 Tag, commonExtension->RemoveTrackingUntrackedCount));
305
306 commonExtension->RemoveTrackingUntrackedCount--;
307 NT_ASSERT(commonExtension->RemoveTrackingUntrackedCount >= 0);
308 }
309 }
310
311 KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock,
312 oldIrql);
313
314 lockValue = InterlockedDecrement(&commonExtension->RemoveLock);
315
316 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_LOCK, "ClassReleaseRemoveLock: "
317 "Released for Object %p & irp %p - count is %d\n",
318 DeviceObject, Tag, lockValue));
319
320 NT_ASSERT(lockValue >= 0);
321
322 NT_ASSERTMSG("RemoveLock decreased to meet LockLowWatermark",
323 ((LockLowWatermark == 0) || !(lockValue == LockLowWatermark)));
324
325 if (lockValue == 0) {
326
327 NT_ASSERT(commonExtension->IsRemoved);
328
329 //
330 // The device needs to be removed. Signal the remove event
331 // that it's safe to go ahead.
332 //
333
334 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_LOCK, "ClassReleaseRemoveLock: "
335 "Release for object %p & irp %p caused lock to go to zero\n",
336 DeviceObject, Tag));
337
338 }
339
340 #else
341
342 UNREFERENCED_PARAMETER(Tag);
343
344 #endif
345
346 //
347 // Decrement the RemoveLockFailAcquire by 1 when RemoveLockFailAcquire is non-zero.
348 // Release the RemoveLockRundown only when RemoveLockFailAcquire is zero.
349 //
350
351 oldValue = 1;
352 lockValue = commonExtension->PrivateCommonData->RemoveLockFailAcquire;
353 while (lockValue != 0) {
354 oldValue =
355 InterlockedCompareExchange((volatile LONG *) &commonExtension->PrivateCommonData->RemoveLockFailAcquire,
356 lockValue - 1,
357 lockValue);
358
359 if (oldValue == lockValue) {
360 break;
361 }
362
363 lockValue = oldValue;
364 }
365
366 if (lockValue == 0) {
367 removeLockRundown = (PEX_RUNDOWN_REF_CACHE_AWARE)
368 ((PCHAR)commonExtension->PrivateCommonData + sizeof(CLASS_PRIVATE_COMMON_DATA));
369 ExReleaseRundownProtectionCacheAware(removeLockRundown);
370 }
371
372 return;
373 }
374
375 /*++////////////////////////////////////////////////////////////////////////////
376
377 ClassCompleteRequest()
378
379 Routine Description:
380
381 This routine is a wrapper around (and should be used instead of)
382 IoCompleteRequest. It is used primarily for debugging purposes.
383 The routine will assert if the Irp being completed is still holding
384 the release lock.
385
386 Arguments:
387
388 DeviceObject - the device object that was handling this request
389
390 Irp - the irp to be completed by IoCompleteRequest
391
392 PriorityBoost - the priority boost to pass to IoCompleteRequest
393
394 Return Value:
395
396 none
397
398 --*/
399 VOID
400 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassCompleteRequest(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp,_In_ CCHAR PriorityBoost)401 ClassCompleteRequest(
402 _In_ PDEVICE_OBJECT DeviceObject,
403 _In_ PIRP Irp,
404 _In_ CCHAR PriorityBoost
405 )
406 {
407 #if DBG
408 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
409
410 PRTL_GENERIC_TABLE removeTrackingList = NULL;
411 REMOVE_TRACKING_BLOCK searchDataBlock;
412 PREMOVE_TRACKING_BLOCK foundTrackingBlock;
413
414 KIRQL oldIrql;
415
416 KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock, &oldIrql);
417
418 removeTrackingList = commonExtension->RemoveTrackingList;
419
420 if (removeTrackingList != NULL)
421 {
422 searchDataBlock.Tag = Irp;
423
424 foundTrackingBlock = RtlLookupElementGenericTable(removeTrackingList, &searchDataBlock);
425
426 if(foundTrackingBlock != NULL) {
427
428 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassCompleteRequest: "
429 "Irp %p completed while still holding the remove lock\n", Irp));
430 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassCompleteRequest: "
431 "Lock acquired in file %s on line %d\n",
432 foundTrackingBlock->File, foundTrackingBlock->Line));
433 NT_ASSERT(FALSE);
434 }
435 }
436
437 KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql);
438 #endif
439
440
441 UNREFERENCED_PARAMETER(DeviceObject);
442
443 IoCompleteRequest(Irp, PriorityBoost);
444 return;
445 } // end ClassCompleteRequest()
446
447
448 RTL_GENERIC_COMPARE_RESULTS
449 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
RemoveTrackingCompareRoutine(PRTL_GENERIC_TABLE Table,PVOID FirstStruct,PVOID SecondStruct)450 RemoveTrackingCompareRoutine(
451 PRTL_GENERIC_TABLE Table,
452 PVOID FirstStruct,
453 PVOID SecondStruct
454 )
455 {
456 PVOID tag1, tag2;
457
458 UNREFERENCED_PARAMETER(Table);
459
460 tag1 = ((PREMOVE_TRACKING_BLOCK)FirstStruct)->Tag;
461 tag2 = ((PREMOVE_TRACKING_BLOCK)SecondStruct)->Tag;
462
463 if (tag1 < tag2)
464 {
465 return GenericLessThan;
466 }
467 else if (tag1 > tag2)
468 {
469 return GenericGreaterThan;
470 }
471
472 return GenericEqual;
473 }
474
475 PVOID
476 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
RemoveTrackingAllocateRoutine(PRTL_GENERIC_TABLE Table,CLONG ByteSize)477 RemoveTrackingAllocateRoutine(
478 PRTL_GENERIC_TABLE Table,
479 CLONG ByteSize
480 )
481 {
482 UNREFERENCED_PARAMETER(Table);
483
484 return ExAllocatePoolWithTag(NonPagedPoolNx, ByteSize, CLASS_TAG_LOCK_TRACKING);
485 }
486
487 VOID
488 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
RemoveTrackingFreeRoutine(PRTL_GENERIC_TABLE Table,PVOID Buffer)489 RemoveTrackingFreeRoutine(
490 PRTL_GENERIC_TABLE Table,
491 PVOID Buffer
492 )
493 {
494 UNREFERENCED_PARAMETER(Table);
495
496 FREE_POOL(Buffer);
497 }
498
499 VOID
ClasspInitializeRemoveTracking(_In_ PDEVICE_OBJECT DeviceObject)500 ClasspInitializeRemoveTracking(
501 _In_ PDEVICE_OBJECT DeviceObject
502 )
503 {
504 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
505
506 #if DBG
507 KeInitializeSpinLock(&commonExtension->RemoveTrackingSpinlock);
508
509 commonExtension->RemoveTrackingList = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(RTL_GENERIC_TABLE), CLASS_TAG_LOCK_TRACKING);
510
511 if (commonExtension->RemoveTrackingList != NULL)
512 {
513 RtlInitializeGenericTable(commonExtension->RemoveTrackingList,
514 RemoveTrackingCompareRoutine,
515 RemoveTrackingAllocateRoutine,
516 RemoveTrackingFreeRoutine,
517 NULL);
518 }
519 #else
520
521 UNREFERENCED_PARAMETER(DeviceObject);
522
523 commonExtension->RemoveTrackingSpinlock = (ULONG_PTR) -1;
524 commonExtension->RemoveTrackingList = NULL;
525 #endif
526 }
527
528 VOID
ClasspUninitializeRemoveTracking(_In_ PDEVICE_OBJECT DeviceObject)529 ClasspUninitializeRemoveTracking(
530 _In_ PDEVICE_OBJECT DeviceObject
531 )
532 {
533 #if DBG
534 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
535 PRTL_GENERIC_TABLE removeTrackingList = commonExtension->RemoveTrackingList;
536
537 ASSERTMSG("Removing the device while still holding remove locks",
538 commonExtension->RemoveTrackingUntrackedCount == 0 &&
539 removeTrackingList != NULL ? RtlNumberGenericTableElements(removeTrackingList) == 0 : TRUE);
540
541 if (removeTrackingList != NULL)
542 {
543 KIRQL oldIrql;
544 KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock, &oldIrql);
545
546 FREE_POOL(removeTrackingList);
547 commonExtension->RemoveTrackingList = NULL;
548
549 KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql);
550 }
551
552 #else
553
554 UNREFERENCED_PARAMETER(DeviceObject);
555 #endif
556 }
557
558
559
560
561