[APPHELP_APITEST] Add tests for SdbGetMatchingExe, to test matching on version resour...
[reactos.git] / reactos / drivers / storage / classpnp / lock.c
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
27 LONG LockHighWatermark = 0;
28 LONG LockLowWatermark = 0;
29 LONG MaxLockedMinutes = 5;
30
31 //
32 // Structure used for tracking remove lock allocations in checked builds
33 //
34 typedef struct _REMOVE_TRACKING_BLOCK {
35 struct _REMOVE_TRACKING_BLOCK *NextBlock;
36 PVOID Tag;
37 LARGE_INTEGER TimeLocked;
38 PCSTR File;
39 ULONG Line;
40 } REMOVE_TRACKING_BLOCK, *PREMOVE_TRACKING_BLOCK;
41
42 \f
43 /*++////////////////////////////////////////////////////////////////////////////
44
45 ClassAcquireRemoveLockEx()
46
47 Routine Description:
48
49 This routine is called to acquire the remove lock on the device object.
50 While the lock is held, the caller can assume that no pending pnp REMOVE
51 requests will be completed.
52
53 The lock should be acquired immediately upon entering a dispatch routine.
54 It should also be acquired before creating any new reference to the
55 device object if there's a chance of releasing the reference before the
56 new one is done.
57
58 This routine will return TRUE if the lock was successfully acquired or
59 FALSE if it cannot be because the device object has already been removed.
60
61 Arguments:
62
63 DeviceObject - the device object to lock
64
65 Tag - Used for tracking lock allocation and release. If an irp is
66 specified when acquiring the lock then the same Tag must be
67 used to release the lock before the Tag is completed.
68
69 Return Value:
70
71 The value of the IsRemoved flag in the device extension. If this is
72 non-zero then the device object has received a Remove irp and non-cleanup
73 IRP's should fail.
74
75 If the value is REMOVE_COMPLETE, the caller should not even release the
76 lock.
77
78 --*/
79 ULONG
80 NTAPI
81 ClassAcquireRemoveLockEx(
82 IN PDEVICE_OBJECT DeviceObject,
83 IN OPTIONAL PVOID Tag,
84 IN PCSTR File,
85 IN ULONG Line
86 )
87 {
88 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
89 LONG lockValue;
90
91
92
93 //
94 // Grab the remove lock
95 //
96 lockValue = InterlockedIncrement(&commonExtension->RemoveLock);
97
98 #if DBG
99
100 DebugPrint((ClassDebugRemoveLock, "ClassAcquireRemoveLock: "
101 "Acquired for Object %p & irp %p - count is %d\n",
102 DeviceObject, Tag, lockValue));
103
104 ASSERTMSG("ClassAcquireRemoveLock - lock value was negative : ",
105 (lockValue > 0));
106
107 ASSERTMSG("RemoveLock increased to meet LockHighWatermark",
108 ((LockHighWatermark == 0) ||
109 (lockValue != LockHighWatermark)));
110
111 if (commonExtension->IsRemoved != REMOVE_COMPLETE){
112 PREMOVE_TRACKING_BLOCK trackingBlock;
113
114 trackingBlock = ExAllocatePool(NonPagedPool,
115 sizeof(REMOVE_TRACKING_BLOCK));
116
117 if(trackingBlock == NULL) {
118
119 KIRQL oldIrql;
120
121 KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
122 &oldIrql);
123
124 commonExtension->RemoveTrackingUntrackedCount++;
125
126 DebugPrint((ClassDebugWarning, ">>>>>ClassAcquireRemoveLock: "
127 "Cannot track Tag %p - currently %d untracked requests\n",
128 Tag, commonExtension->RemoveTrackingUntrackedCount));
129
130 KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock,
131 oldIrql);
132 }
133 else {
134 PREMOVE_TRACKING_BLOCK *removeTrackingList =
135 (PREMOVE_TRACKING_BLOCK*)&commonExtension->RemoveTrackingList;
136
137 KIRQL oldIrql;
138
139 trackingBlock->Tag = Tag;
140
141 trackingBlock->File = File;
142 trackingBlock->Line = Line;
143
144 KeQueryTickCount((&trackingBlock->TimeLocked));
145
146 KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
147 &oldIrql);
148
149 while(*removeTrackingList != NULL) {
150
151 if((*removeTrackingList)->Tag > Tag) {
152 break;
153 }
154
155 if((*removeTrackingList)->Tag == Tag) {
156
157 DebugPrint((ClassDebugError, ">>>>>ClassAcquireRemoveLock: "
158 "already tracking Tag %p\n", Tag));
159 DebugPrint((ClassDebugError, ">>>>>ClassAcquireRemoveLock: "
160 "acquired in file %s on line %d\n",
161 (*removeTrackingList)->File,
162 (*removeTrackingList)->Line));
163 ASSERT(FALSE);
164 }
165
166 removeTrackingList = &((*removeTrackingList)->NextBlock);
167 }
168
169 trackingBlock->NextBlock = *removeTrackingList;
170 *removeTrackingList = trackingBlock;
171
172 KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock,
173 oldIrql);
174
175 }
176 }
177
178 #endif
179
180 return (commonExtension->IsRemoved);
181 }
182 \f
183 /*++////////////////////////////////////////////////////////////////////////////
184
185 ClassReleaseRemoveLock()
186
187 Routine Description:
188
189 This routine is called to release the remove lock on the device object. It
190 must be called when finished using a previously locked reference to the
191 device object. If an Tag was specified when acquiring the lock then the
192 same Tag must be specified when releasing the lock.
193
194 When the lock count reduces to zero, this routine will signal the waiting
195 remove Tag to delete the device object. As a result the DeviceObject
196 pointer should not be used again once the lock has been released.
197
198 Arguments:
199
200 DeviceObject - the device object to lock
201
202 Tag - The irp (if any) specified when acquiring the lock. This is used
203 for lock tracking purposes
204
205 Return Value:
206
207 none
208
209 --*/
210 VOID
211 NTAPI
212 ClassReleaseRemoveLock(
213 IN PDEVICE_OBJECT DeviceObject,
214 IN OPTIONAL PIRP Tag
215 )
216 {
217 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
218 LONG lockValue;
219
220 #if DBG
221 PREMOVE_TRACKING_BLOCK *listEntry =
222 (PREMOVE_TRACKING_BLOCK*)&commonExtension->RemoveTrackingList;
223
224 BOOLEAN found = FALSE;
225
226 LONGLONG maxCount;
227
228 BOOLEAN isRemoved = (commonExtension->IsRemoved == REMOVE_COMPLETE);
229
230 KIRQL oldIrql;
231
232 if(isRemoved) {
233 DBGTRAP(("ClassReleaseRemoveLock: REMOVE_COMPLETE set; this should never happen"));
234 InterlockedDecrement(&(commonExtension->RemoveLock));
235 return;
236 }
237
238 //
239 // Check the tick count and make sure this thing hasn't been locked
240 // for more than MaxLockedMinutes.
241 //
242
243 maxCount = KeQueryTimeIncrement() * 10; // microseconds
244 maxCount *= 1000; // milliseconds
245 maxCount *= 1000; // seconds
246 maxCount *= 60; // minutes
247 maxCount *= MaxLockedMinutes;
248
249 DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: "
250 "maxCount = %0I64x\n", maxCount));
251
252 KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
253 &oldIrql);
254
255 while(*listEntry != NULL) {
256
257 PREMOVE_TRACKING_BLOCK block;
258 LARGE_INTEGER difference;
259
260 block = *listEntry;
261
262 KeQueryTickCount((&difference));
263
264 difference.QuadPart -= block->TimeLocked.QuadPart;
265
266 DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: "
267 "Object %p (tag %p) locked for %I64d ticks\n",
268 DeviceObject, block->Tag, difference.QuadPart));
269
270 if(difference.QuadPart >= maxCount) {
271
272 DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: "
273 "Object %p (tag %p) locked for %I64d ticks - TOO LONG\n",
274 DeviceObject, block->Tag, difference.QuadPart));
275 DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: "
276 "Lock acquired in file %s on line %d\n",
277 block->File, block->Line));
278 ASSERT(FALSE);
279 }
280
281 if((found == FALSE) && ((*listEntry)->Tag == Tag)) {
282
283 *listEntry = block->NextBlock;
284 ExFreePool(block);
285 found = TRUE;
286
287 } else {
288
289 listEntry = &((*listEntry)->NextBlock);
290
291 }
292 }
293
294 if(!found) {
295 if(commonExtension->RemoveTrackingUntrackedCount == 0) {
296 DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: "
297 "Couldn't find Tag %p in the lock tracking list\n",
298 Tag));
299 ASSERT(FALSE);
300 } else {
301 DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: "
302 "Couldn't find Tag %p in the lock tracking list - "
303 "may be one of the %d untracked requests still "
304 "outstanding\n",
305 Tag,
306 commonExtension->RemoveTrackingUntrackedCount));
307
308 commonExtension->RemoveTrackingUntrackedCount--;
309 ASSERT(commonExtension->RemoveTrackingUntrackedCount >= 0);
310 }
311 }
312
313 KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock,
314 oldIrql);
315
316 #endif
317
318 lockValue = InterlockedDecrement(&commonExtension->RemoveLock);
319
320 DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: "
321 "Released for Object %p & irp %p - count is %d\n",
322 DeviceObject, Tag, lockValue));
323
324 ASSERT(lockValue >= 0);
325
326 ASSERTMSG("RemoveLock decreased to meet LockLowWatermark",
327 ((LockLowWatermark == 0) || !(lockValue == LockLowWatermark)));
328
329 if(lockValue == 0) {
330
331 ASSERT(commonExtension->IsRemoved);
332
333 //
334 // The device needs to be removed. Signal the remove event
335 // that it's safe to go ahead.
336 //
337
338 DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: "
339 "Release for object %p & irp %p caused lock to go to zero\n",
340 DeviceObject, Tag));
341
342 KeSetEvent(&commonExtension->RemoveEvent,
343 IO_NO_INCREMENT,
344 FALSE);
345
346 }
347 return;
348 }
349 \f
350 /*++////////////////////////////////////////////////////////////////////////////
351
352 ClassCompleteRequest()
353
354 Routine Description:
355
356 This routine is a wrapper around (and should be used instead of)
357 IoCompleteRequest. It is used primarily for debugging purposes.
358 The routine will assert if the Irp being completed is still holding
359 the release lock.
360
361 Arguments:
362
363 DeviceObject - the device object that was handling this request
364
365 Irp - the irp to be completed by IoCompleteRequest
366
367 PriorityBoost - the priority boost to pass to IoCompleteRequest
368
369 Return Value:
370
371 none
372
373 --*/
374 VOID
375 NTAPI
376 ClassCompleteRequest(
377 IN PDEVICE_OBJECT DeviceObject,
378 IN PIRP Irp,
379 IN CCHAR PriorityBoost
380 )
381 {
382
383 #if DBG
384 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
385 PREMOVE_TRACKING_BLOCK *listEntry =
386 (PREMOVE_TRACKING_BLOCK*)&commonExtension->RemoveTrackingList;
387
388 KIRQL oldIrql;
389
390 KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
391 &oldIrql);
392
393 while(*listEntry != NULL) {
394
395 if((*listEntry)->Tag == Irp) {
396 break;
397 }
398
399 listEntry = &((*listEntry)->NextBlock);
400 }
401
402 if(*listEntry != NULL) {
403
404 DebugPrint((ClassDebugError, ">>>>>ClassCompleteRequest: "
405 "Irp %p completed while still holding the remove lock\n",
406 Irp));
407 DebugPrint((ClassDebugError, ">>>>>ClassCompleteRequest: "
408 "Lock acquired in file %s on line %d\n",
409 (*listEntry)->File, (*listEntry)->Line));
410 ASSERT(FALSE);
411 }
412
413 KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql);
414 #endif
415
416 IoCompleteRequest(Irp, PriorityBoost);
417 return;
418 } // end ClassCompleteRequest()
419