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