ReactOS 0.4.15-dev-7961-gdcf9eb0
lock.c
Go to the documentation of this file.
1/*++
2
3Copyright (C) Microsoft Corporation, 1990 - 1998
4
5Module Name:
6
7 lock.c
8
9Abstract:
10
11 This is the NT SCSI port driver.
12
13Environment:
14
15 kernel mode only
16
17Notes:
18
19 This module is a driver dll for scsi miniports.
20
21Revision 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
36
37//
38// Structure used for tracking remove lock allocations in checked builds
39//
40typedef struct _REMOVE_TRACKING_BLOCK {
46
47/*++////////////////////////////////////////////////////////////////////////////
48
49Classpnp RemoveLockRundown
50
51RemoveLockRundown is a cacheaware rundown protection for the classpnp device object. While this
52rundown protection is held successfully, the caller can assume that no pending pnp REMOVE
53requests will be completed.
54
55The RemoveLockRundown is a replacement of the original RemoveLock to improve the scalability.
56For backward compatibility, we still keep the RemoveLock field in the device common extension structure.
57However, the old RemoveLock is only being used in the DBG build.
58
59The usage of the RemoveLockRundown is slightly different from the normal rundown protection usage.
60The RemoveLockRundown is acquired via ClassAcquireRemoveLockEx() function
61and released via ClassReleaseRemoveLock() function. Usually, we bail out when the acquisition
62of rundown protection fails (calls to ExAcquireRundownProtectionCacheAware returns FALSE) and
63will not release the rundown protection in acquisition failure. For the RemoveLockRundown,
64the caller will always call ClassAcquireRemoveLockEx() and ClassReleaseRemoveLock() in a pair no
65matter the return value of ClassAcquireRemoveLockEx(). Therefore, a thread may still call
66ClassReleaseRemoveLock() even the previous acquisition RemoveLockRundown protection failed.
67
68To deal with the previous acquisition failure case, we introduced a new field RemoveLockFailAcquire
69as a counter for rundown acquisition failures. In the ClassReleaseRemoveLock() function, we only
70release the rundown protection when this counter is decremented to zero. Since the change of RemoveLockFailAcquire
71and release rundown protection is not protected by a lock as an atomic operation, we use a while loop over
72InterlockedCompareExchange operation to make sure when we release the rundown protection, this counter is
73actually zero.
74
75--*/
76
77/*++////////////////////////////////////////////////////////////////////////////
78
79ClassAcquireRemoveLockEx()
80
81Routine 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
95Arguments:
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
103Return 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--*/
113ULONG
114NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
117 _In_ PVOID Tag,
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
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
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
224ClassReleaseRemoveLock()
225
226Routine 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
237Arguments:
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
244Return Value:
245
246 none
247
248--*/
249VOID
250NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
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
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
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
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 =
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
377ClassCompleteRequest()
378
379Routine 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
386Arguments:
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
394Return Value:
395
396 none
397
398--*/
399VOID
400NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
403 _In_ PIRP Irp,
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));
434 }
435 }
436
437 KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql);
438 #endif
439
440
442
444 return;
445} // end ClassCompleteRequest()
446
447
449NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
454 )
455{
456 PVOID tag1, tag2;
457
459
460 tag1 = ((PREMOVE_TRACKING_BLOCK)FirstStruct)->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
475PVOID
476NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
480 )
481{
483
485}
486
487VOID
488NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
492 )
493{
495
497}
498
499VOID
502 )
503{
504 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
505
506 #if DBG
508
509 commonExtension->RemoveTrackingList = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(RTL_GENERIC_TABLE), CLASS_TAG_LOCK_TRACKING);
510
511 if (commonExtension->RemoveTrackingList != NULL)
512 {
517 NULL);
518 }
519 #else
520
522
523 commonExtension->RemoveTrackingSpinlock = (ULONG_PTR) -1;
524 commonExtension->RemoveTrackingList = NULL;
525 #endif
526}
527
528VOID
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
555 #endif
556}
557
558
559
560
unsigned char BOOLEAN
#define InterlockedIncrement
Definition: armddk.h:53
#define InterlockedDecrement
Definition: armddk.h:52
#define FREE_POOL(_PoolPtr)
Definition: cdrom.h:782
Definition: bufpool.h:45
Definition: File.h:16
RTL_GENERIC_FREE_ROUTINE RemoveTrackingFreeRoutine
Definition: classp.h:2404
RTL_GENERIC_ALLOCATE_ROUTINE RemoveTrackingAllocateRoutine
Definition: classp.h:2402
RTL_GENERIC_COMPARE_ROUTINE RemoveTrackingCompareRoutine
Definition: classp.h:2400
struct _CLASS_PRIVATE_COMMON_DATA CLASS_PRIVATE_COMMON_DATA
Definition: classpnp.h:285
#define CLASS_TAG_LOCK_TRACKING
Definition: classpnp.h:88
#define NO_REMOVE
Definition: classpnp.h:96
#define REMOVE_COMPLETE
Definition: classpnp.h:98
_In_ PIRP Irp
Definition: csq.h:116
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
VOID NTAPI ClassCompleteRequest(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ CCHAR PriorityBoost)
Definition: lock.c:401
struct _REMOVE_TRACKING_BLOCK REMOVE_TRACKING_BLOCK
VOID NTAPI ClassReleaseRemoveLock(_In_ PDEVICE_OBJECT DeviceObject, _In_opt_ PIRP Tag)
Definition: lock.c:251
VOID ClasspUninitializeRemoveTracking(_In_ PDEVICE_OBJECT DeviceObject)
Definition: lock.c:529
ULONG NTAPI ClassAcquireRemoveLockEx(_In_ PDEVICE_OBJECT DeviceObject, _In_ PVOID Tag, _In_ PCSTR File, _In_ ULONG Line)
Definition: lock.c:115
LONG LockHighWatermark
Definition: lock.c:33
LONG LockLowWatermark
Definition: lock.c:34
struct _REMOVE_TRACKING_BLOCK * PREMOVE_TRACKING_BLOCK
VOID ClasspInitializeRemoveTracking(_In_ PDEVICE_OBJECT DeviceObject)
Definition: lock.c:500
LONG MaxLockedMinutes
Definition: lock.c:35
#define ULONG_PTR
Definition: config.h:101
#define ExAllocatePoolWithTag(hernya, size, tag)
Definition: env_spec_w32.h:350
UCHAR KIRQL
Definition: env_spec_w32.h:591
#define KeReleaseSpinLock(sl, irql)
Definition: env_spec_w32.h:627
#define KeAcquireSpinLock(sl, irql)
Definition: env_spec_w32.h:609
#define KeInitializeSpinLock(sl)
Definition: env_spec_w32.h:604
VOID NTAPI RtlInitializeGenericTable(IN PRTL_GENERIC_TABLE Table, IN PRTL_GENERIC_COMPARE_ROUTINE CompareRoutine, IN PRTL_GENERIC_ALLOCATE_ROUTINE AllocateRoutine, IN PRTL_GENERIC_FREE_ROUTINE FreeRoutine, IN PVOID TableContext)
Definition: generictable.c:100
ASMGENDATA Table[]
Definition: genincdata.c:61
#define InterlockedCompareExchange
Definition: interlocked.h:104
#define _In_
Definition: ms_sal.h:308
#define _In_opt_
Definition: ms_sal.h:309
#define ASSERTMSG(msg, exp)
Definition: nt_native.h:431
#define UNREFERENCED_PARAMETER(P)
Definition: ntbasedef.h:317
#define IoCompleteRequest
Definition: irp.c:1240
long LONG
Definition: pedump.c:60
#define KeQueryTickCount(CurrentCount)
Definition: ke.h:43
#define TRACE_LEVEL_WARNING
Definition: storswtr.h:28
#define TRACE_LEVEL_VERBOSE
Definition: storswtr.h:30
#define TRACE_LEVEL_ERROR
Definition: storswtr.h:27
Definition: ncftp.h:79
KSPIN_LOCK RemoveTrackingSpinlock
Definition: classpnp.h:603
PCLASS_PRIVATE_COMMON_DATA PrivateCommonData
Definition: classpnp.h:633
LARGE_INTEGER TimeLocked
Definition: lock.c:42
#define NTAPI
Definition: typedefs.h:36
const char * PCSTR
Definition: typedefs.h:52
uint32_t ULONG
Definition: typedefs.h:59
char CCHAR
Definition: typedefs.h:51
char * PCHAR
Definition: typedefs.h:51
ULONG CLONG
Definition: umtypes.h:126
_In_ PDEVICE_OBJECT DeviceObject
Definition: wdfdevice.h:2055
_Must_inspect_result_ _In_ WDFDEVICE _In_ BOOLEAN _In_opt_ PVOID Tag
Definition: wdfdevice.h:4065
_In_ WDFREQUEST _In_ NTSTATUS _In_ CCHAR PriorityBoost
Definition: wdfrequest.h:1016
NTKERNELAPI VOID FASTCALL ExReleaseRundownProtectionCacheAware(_Inout_ PEX_RUNDOWN_REF_CACHE_AWARE RunRefCacheAware)
struct _EX_RUNDOWN_REF_CACHE_AWARE * PEX_RUNDOWN_REF_CACHE_AWARE
Definition: extypes.h:186
NTSYSAPI BOOLEAN NTAPI RtlDeleteElementGenericTable(_In_ PRTL_GENERIC_TABLE Table, _In_ PVOID Buffer)
#define NT_ASSERT
Definition: rtlfuncs.h:3310
NTSYSAPI PVOID NTAPI RtlInsertElementGenericTable(_In_ PRTL_GENERIC_TABLE Table, _In_reads_bytes_(BufferSize) PVOID Buffer, _In_ CLONG BufferSize, _Out_opt_ PBOOLEAN NewElement)
#define NT_ASSERTMSG
Definition: rtlfuncs.h:3311
NTSYSAPI ULONG NTAPI RtlNumberGenericTableElements(_In_ PRTL_GENERIC_TABLE Table)
_Must_inspect_result_ NTSYSAPI PVOID NTAPI RtlLookupElementGenericTable(_In_ PRTL_GENERIC_TABLE Table, _In_ PVOID Buffer)
_IRQL_requires_same_ _In_ PVOID _In_ PVOID SecondStruct
Definition: rtltypes.h:390
_IRQL_requires_same_ _In_ CLONG ByteSize
Definition: rtltypes.h:399
_IRQL_requires_same_ _In_ PVOID FirstStruct
Definition: rtltypes.h:389
@ GenericLessThan
Definition: rtltypes.h:376
@ GenericEqual
Definition: rtltypes.h:378
@ GenericGreaterThan
Definition: rtltypes.h:377
enum _RTL_GENERIC_COMPARE_RESULTS RTL_GENERIC_COMPARE_RESULTS