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