* Sync to trunk HEAD (r53473).
[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 ClassAcquireRemoveLockEx(
83 IN PDEVICE_OBJECT DeviceObject,
84 IN OPTIONAL PVOID Tag,
85 IN PCSTR File,
86 IN ULONG Line
87 )
88 {
89 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
90 LONG lockValue;
91
92
93
94 //
95 // Grab the remove lock
96 //
97 lockValue = InterlockedIncrement(&commonExtension->RemoveLock);
98
99 #if DBG
100
101 DebugPrint((ClassDebugRemoveLock, "ClassAcquireRemoveLock: "
102 "Acquired for Object %p & irp %p - count is %d\n",
103 DeviceObject, Tag, lockValue));
104
105 ASSERTMSG("ClassAcquireRemoveLock - lock value was negative : ",
106 (lockValue > 0));
107
108 ASSERTMSG("RemoveLock increased to meet LockHighWatermark",
109 ((LockHighWatermark == 0) ||
110 (lockValue != LockHighWatermark)));
111
112 if (commonExtension->IsRemoved != REMOVE_COMPLETE){
113 PREMOVE_TRACKING_BLOCK trackingBlock;
114
115 trackingBlock = ExAllocatePool(NonPagedPool,
116 sizeof(REMOVE_TRACKING_BLOCK));
117
118 if(trackingBlock == NULL) {
119
120 KIRQL oldIrql;
121
122 KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
123 &oldIrql);
124
125 commonExtension->RemoveTrackingUntrackedCount++;
126
127 DebugPrint((ClassDebugWarning, ">>>>>ClassAcquireRemoveLock: "
128 "Cannot track Tag %p - currently %d untracked requsts\n",
129 Tag, commonExtension->RemoveTrackingUntrackedCount));
130
131 KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock,
132 oldIrql);
133 }
134 else {
135 PREMOVE_TRACKING_BLOCK *removeTrackingList =
136 (PREMOVE_TRACKING_BLOCK)&commonExtension->RemoveTrackingList;
137
138 KIRQL oldIrql;
139
140 trackingBlock->Tag = Tag;
141
142 trackingBlock->File = File;
143 trackingBlock->Line = Line;
144
145 KeQueryTickCount((&trackingBlock->TimeLocked));
146
147 KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
148 &oldIrql);
149
150 while(*removeTrackingList != NULL) {
151
152 if((*removeTrackingList)->Tag > Tag) {
153 break;
154 }
155
156 if((*removeTrackingList)->Tag == Tag) {
157
158 DebugPrint((ClassDebugError, ">>>>>ClassAcquireRemoveLock: "
159 "already tracking Tag %p\n", Tag));
160 DebugPrint((ClassDebugError, ">>>>>ClassAcquireRemoveLock: "
161 "acquired in file %s on line %d\n",
162 (*removeTrackingList)->File,
163 (*removeTrackingList)->Line));
164 ASSERT(FALSE);
165 }
166
167 removeTrackingList = &((*removeTrackingList)->NextBlock);
168 }
169
170 trackingBlock->NextBlock = *removeTrackingList;
171 *removeTrackingList = trackingBlock;
172
173 KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock,
174 oldIrql);
175
176 }
177 }
178
179 #endif
180
181 return (commonExtension->IsRemoved);
182 }
183 \f
184 /*++////////////////////////////////////////////////////////////////////////////
185
186 ClassReleaseRemoveLock()
187
188 Routine Description:
189
190 This routine is called to release the remove lock on the device object. It
191 must be called when finished using a previously locked reference to the
192 device object. If an Tag was specified when acquiring the lock then the
193 same Tag must be specified when releasing the lock.
194
195 When the lock count reduces to zero, this routine will signal the waiting
196 remove Tag to delete the device object. As a result the DeviceObject
197 pointer should not be used again once the lock has been released.
198
199 Arguments:
200
201 DeviceObject - the device object to lock
202
203 Tag - The irp (if any) specified when acquiring the lock. This is used
204 for lock tracking purposes
205
206 Return Value:
207
208 none
209
210 --*/
211 VOID
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 ClassCompleteRequest(
376 IN PDEVICE_OBJECT DeviceObject,
377 IN PIRP Irp,
378 IN CCHAR PriorityBoost
379 )
380 {
381
382 #if DBG
383 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
384 PREMOVE_TRACKING_BLOCK *listEntry =
385 (PREMOVE_TRACKING_BLOCK)&commonExtension->RemoveTrackingList;
386
387 KIRQL oldIrql;
388
389 KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
390 &oldIrql);
391
392 while(*listEntry != NULL) {
393
394 if((*listEntry)->Tag == Irp) {
395 break;
396 }
397
398 listEntry = &((*listEntry)->NextBlock);
399 }
400
401 if(*listEntry != NULL) {
402
403 DebugPrint((ClassDebugError, ">>>>>ClassCompleteRequest: "
404 "Irp %p completed while still holding the remove lock\n",
405 Irp));
406 DebugPrint((ClassDebugError, ">>>>>ClassCompleteRequest: "
407 "Lock acquired in file %s on line %d\n",
408 (*listEntry)->File, (*listEntry)->Line));
409 ASSERT(FALSE);
410 }
411
412 KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql);
413 #endif
414
415 IoCompleteRequest(Irp, PriorityBoost);
416 return;
417 } // end ClassCompleteRequest()
418