* Sync up to trunk head (r64939).
[reactos.git] / drivers / storage / classpnp / utils.c
1 /*++
2
3 Copyright (C) Microsoft Corporation, 1991 - 1999
4
5 Module Name:
6
7 utils.c
8
9 Abstract:
10
11 SCSI class driver routines
12
13 Environment:
14
15 kernel mode only
16
17 Notes:
18
19
20 Revision History:
21
22 --*/
23
24 #include "classp.h"
25
26 #ifdef ALLOC_PRAGMA
27 #pragma alloc_text(PAGE, ClassGetDeviceParameter)
28 #pragma alloc_text(PAGE, ClassScanForSpecial)
29 #pragma alloc_text(PAGE, ClassSetDeviceParameter)
30 #endif
31
32 // custom string match -- careful!
33 BOOLEAN NTAPI ClasspMyStringMatches(IN PCSTR StringToMatch OPTIONAL, IN PCSTR TargetString)
34 {
35 ULONG length; // strlen returns an int, not size_t (!)
36 PAGED_CODE();
37 ASSERT(TargetString);
38 // if no match requested, return TRUE
39 if (StringToMatch == NULL) {
40 return TRUE;
41 }
42 // cache the string length for efficiency
43 length = strlen(StringToMatch);
44 // ZERO-length strings may only match zero-length strings
45 if (length == 0) {
46 return (strlen(TargetString) == 0);
47 }
48 // strncmp returns zero if the strings match
49 return (strncmp(StringToMatch, TargetString, length) == 0);
50 }
51
52 VOID NTAPI ClassGetDeviceParameter(
53 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
54 IN PWSTR SubkeyName OPTIONAL,
55 IN PWSTR ParameterName,
56 IN OUT PULONG ParameterValue // also default value
57 )
58 {
59 NTSTATUS status;
60 RTL_QUERY_REGISTRY_TABLE queryTable[2];
61 HANDLE deviceParameterHandle;
62 HANDLE deviceSubkeyHandle;
63 ULONG defaultParameterValue;
64
65 PAGED_CODE();
66
67 //
68 // open the given parameter
69 //
70
71 status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
72 PLUGPLAY_REGKEY_DEVICE,
73 KEY_READ,
74 &deviceParameterHandle);
75
76 if (NT_SUCCESS(status) && (SubkeyName != NULL)) {
77
78 UNICODE_STRING subkeyName;
79 OBJECT_ATTRIBUTES objectAttributes;
80
81 RtlInitUnicodeString(&subkeyName, SubkeyName);
82 InitializeObjectAttributes(&objectAttributes,
83 &subkeyName,
84 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
85 deviceParameterHandle,
86 NULL);
87
88 status = ZwOpenKey(&deviceSubkeyHandle,
89 KEY_READ,
90 &objectAttributes);
91 if (!NT_SUCCESS(status)) {
92 ZwClose(deviceParameterHandle);
93 }
94
95 }
96
97 if (NT_SUCCESS(status)) {
98
99 RtlZeroMemory(queryTable, sizeof(queryTable));
100
101 defaultParameterValue = *ParameterValue;
102
103 queryTable->Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
104 queryTable->Name = ParameterName;
105 queryTable->EntryContext = ParameterValue;
106 queryTable->DefaultType = REG_DWORD;
107 queryTable->DefaultData = NULL;
108 queryTable->DefaultLength = 0;
109
110 status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
111 (PWSTR)(SubkeyName ?
112 deviceSubkeyHandle :
113 deviceParameterHandle),
114 queryTable,
115 NULL,
116 NULL);
117 if (!NT_SUCCESS(status)) {
118 *ParameterValue = defaultParameterValue; // use default value
119 }
120
121 //
122 // close what we open
123 //
124
125 if (SubkeyName) {
126 ZwClose(deviceSubkeyHandle);
127 }
128
129 ZwClose(deviceParameterHandle);
130 }
131
132 return;
133
134 } // end ClassGetDeviceParameter()
135
136 NTSTATUS NTAPI ClassSetDeviceParameter(
137 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
138 IN PWSTR SubkeyName OPTIONAL,
139 IN PWSTR ParameterName,
140 IN ULONG ParameterValue)
141 {
142 NTSTATUS status;
143 HANDLE deviceParameterHandle;
144 HANDLE deviceSubkeyHandle;
145
146 PAGED_CODE();
147
148 //
149 // open the given parameter
150 //
151
152 status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
153 PLUGPLAY_REGKEY_DEVICE,
154 KEY_READ | KEY_WRITE,
155 &deviceParameterHandle);
156
157 if (NT_SUCCESS(status) && (SubkeyName != NULL)) {
158
159 UNICODE_STRING subkeyName;
160 OBJECT_ATTRIBUTES objectAttributes;
161
162 RtlInitUnicodeString(&subkeyName, SubkeyName);
163 InitializeObjectAttributes(&objectAttributes,
164 &subkeyName,
165 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
166 deviceParameterHandle,
167 NULL);
168
169 status = ZwCreateKey(&deviceSubkeyHandle,
170 KEY_READ | KEY_WRITE,
171 &objectAttributes,
172 0, NULL, 0, NULL);
173 if (!NT_SUCCESS(status)) {
174 ZwClose(deviceParameterHandle);
175 }
176
177 }
178
179 if (NT_SUCCESS(status)) {
180
181 status = RtlWriteRegistryValue(
182 RTL_REGISTRY_HANDLE,
183 (PWSTR) (SubkeyName ?
184 deviceSubkeyHandle :
185 deviceParameterHandle),
186 ParameterName,
187 REG_DWORD,
188 &ParameterValue,
189 sizeof(ULONG));
190
191 //
192 // close what we open
193 //
194
195 if (SubkeyName) {
196 ZwClose(deviceSubkeyHandle);
197 }
198
199 ZwClose(deviceParameterHandle);
200 }
201
202 return status;
203
204 } // end ClassSetDeviceParameter()
205
206 /*
207 * ClassScanForSpecial
208 *
209 * This routine was written to simplify scanning for special
210 * hardware based upon id strings. it does not check the registry.
211 */
212
213 VOID NTAPI ClassScanForSpecial(
214 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
215 IN CLASSPNP_SCAN_FOR_SPECIAL_INFO DeviceList[],
216 IN PCLASS_SCAN_FOR_SPECIAL_HANDLER Function)
217 {
218 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
219 PCSTR vendorId;
220 PCSTR productId;
221 PCSTR productRevision;
222 CHAR nullString[] = "";
223 //ULONG j;
224
225 PAGED_CODE();
226 ASSERT(DeviceList);
227 ASSERT(Function);
228
229 deviceDescriptor = FdoExtension->DeviceDescriptor;
230
231 if (DeviceList == NULL) {
232 return;
233 }
234 if (Function == NULL) {
235 return;
236 }
237
238 //
239 // SCSI sets offsets to -1, ATAPI sets to 0. check for both.
240 //
241
242 if (deviceDescriptor->VendorIdOffset != 0 &&
243 deviceDescriptor->VendorIdOffset != -1) {
244 vendorId = ((PCSTR)deviceDescriptor);
245 vendorId += deviceDescriptor->VendorIdOffset;
246 } else {
247 vendorId = nullString;
248 }
249 if (deviceDescriptor->ProductIdOffset != 0 &&
250 deviceDescriptor->ProductIdOffset != -1) {
251 productId = ((PCSTR)deviceDescriptor);
252 productId += deviceDescriptor->ProductIdOffset;
253 } else {
254 productId = nullString;
255 }
256 if (deviceDescriptor->VendorIdOffset != 0 &&
257 deviceDescriptor->VendorIdOffset != -1) {
258 productRevision = ((PCSTR)deviceDescriptor);
259 productRevision += deviceDescriptor->ProductRevisionOffset;
260 } else {
261 productRevision = nullString;
262 }
263
264 //
265 // loop while the device list is valid (not null-filled)
266 //
267
268 for (;(DeviceList->VendorId != NULL ||
269 DeviceList->ProductId != NULL ||
270 DeviceList->ProductRevision != NULL);DeviceList++) {
271
272 if (ClasspMyStringMatches(DeviceList->VendorId, vendorId) &&
273 ClasspMyStringMatches(DeviceList->ProductId, productId) &&
274 ClasspMyStringMatches(DeviceList->ProductRevision, productRevision)
275 ) {
276
277 DebugPrint((1, "ClasspScanForSpecialByInquiry: Found matching "
278 "controller Ven: %s Prod: %s Rev: %s\n",
279 vendorId, productId, productRevision));
280
281 //
282 // pass the context to the call back routine and exit
283 //
284
285 (Function)(FdoExtension, DeviceList->Data);
286
287 //
288 // for CHK builds, try to prevent wierd stacks by having a debug
289 // print here. it's a hack, but i know of no other way to prevent
290 // the stack from being wrong.
291 //
292
293 DebugPrint((16, "ClasspScanForSpecialByInquiry: "
294 "completed callback\n"));
295 return;
296
297 } // else the strings did not match
298
299 } // none of the devices matched.
300
301 DebugPrint((1, "ClasspScanForSpecialByInquiry: no match found for %p\n",
302 FdoExtension->DeviceObject));
303 return;
304
305 } // end ClasspScanForSpecialByInquiry()
306
307 //
308 // In order to provide better performance without the need to reboot,
309 // we need to implement a self-adjusting method to set and clear the
310 // srb flags based upon current performance.
311 //
312 // whenever there is an error, immediately grab the spin lock. the
313 // MP perf hit here is acceptable, since we're in an error path. this
314 // is also neccessary because we are guaranteed to be modifying the
315 // SRB flags here, setting SuccessfulIO to zero, and incrementing the
316 // actual error count (which is always done within this spinlock).
317 //
318 // whenever there is no error, increment a counter. if there have been
319 // errors on the device, and we've enabled dynamic perf, *and* we've
320 // just crossed the perf threshhold, then grab the spin lock and
321 // double check that the threshhold has, indeed been hit(*). then
322 // decrement the error count, and if it's dropped sufficiently, undo
323 // some of the safety changes made in the SRB flags due to the errors.
324 //
325 // * this works in all cases. even if lots of ios occur after the
326 // previous guy went in and cleared the successfulio counter, that
327 // just means that we've hit the threshhold again, and so it's proper
328 // to run the inner loop again.
329 //
330
331 VOID
332 NTAPI
333 ClasspPerfIncrementErrorCount(
334 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
335 )
336 {
337 PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
338 KIRQL oldIrql;
339 ULONG errors;
340
341 KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
342
343 fdoData->Perf.SuccessfulIO = 0; // implicit interlock
344 errors = InterlockedIncrement((PLONG)&FdoExtension->ErrorCount);
345
346 if (errors >= CLASS_ERROR_LEVEL_1) {
347
348 //
349 // If the error count has exceeded the error limit, then disable
350 // any tagged queuing, multiple requests per lu queueing
351 // and sychronous data transfers.
352 //
353 // Clearing the no queue freeze flag prevents the port driver
354 // from sending multiple requests per logical unit.
355 //
356
357 CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
358 CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
359
360 SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
361
362 DebugPrint((ClassDebugError, "ClasspPerfIncrementErrorCount: "
363 "Too many errors; disabling tagged queuing and "
364 "synchronous data tranfers.\n"));
365
366 }
367
368 if (errors >= CLASS_ERROR_LEVEL_2) {
369
370 //
371 // If a second threshold is reached, disable disconnects.
372 //
373
374 SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT);
375 DebugPrint((ClassDebugError, "ClasspPerfIncrementErrorCount: "
376 "Too many errors; disabling disconnects.\n"));
377 }
378
379 KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
380 return;
381 }
382
383 VOID
384 NTAPI
385 ClasspPerfIncrementSuccessfulIo(
386 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
387 )
388 {
389 PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
390 KIRQL oldIrql;
391 ULONG errors;
392 ULONG succeeded = 0;
393
394 //
395 // don't take a hit from the interlocked op unless we're in
396 // a degraded state and we've got a threshold to hit.
397 //
398
399 if (FdoExtension->ErrorCount == 0) {
400 return;
401 }
402
403 if (fdoData->Perf.ReEnableThreshhold == 0) {
404 return;
405 }
406
407 succeeded = InterlockedIncrement((PLONG)&fdoData->Perf.SuccessfulIO);
408 if (succeeded < fdoData->Perf.ReEnableThreshhold) {
409 return;
410 }
411
412 //
413 // if we hit the threshold, grab the spinlock and verify we've
414 // actually done so. this allows us to ignore the spinlock 99%
415 // of the time.
416 //
417
418 KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
419
420 //
421 // re-read the value, so we don't run this multiple times
422 // for a single threshhold being hit. this keeps errorcount
423 // somewhat useful.
424 //
425
426 succeeded = fdoData->Perf.SuccessfulIO;
427
428 if ((FdoExtension->ErrorCount != 0) &&
429 (fdoData->Perf.ReEnableThreshhold <= succeeded)
430 ) {
431
432 fdoData->Perf.SuccessfulIO = 0; // implicit interlock
433
434 ASSERT(FdoExtension->ErrorCount > 0);
435 errors = InterlockedDecrement((PLONG)&FdoExtension->ErrorCount);
436
437 //
438 // note: do in reverse order of the sets "just in case"
439 //
440
441 if (errors < CLASS_ERROR_LEVEL_2) {
442 if (errors == CLASS_ERROR_LEVEL_2 - 1) {
443 DebugPrint((ClassDebugError, "ClasspPerfIncrementSuccessfulIo: "
444 "Error level 2 no longer required.\n"));
445 }
446 if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
447 SRB_FLAGS_DISABLE_DISCONNECT)) {
448 CLEAR_FLAG(FdoExtension->SrbFlags,
449 SRB_FLAGS_DISABLE_DISCONNECT);
450 }
451 }
452
453 if (errors < CLASS_ERROR_LEVEL_1) {
454 if (errors == CLASS_ERROR_LEVEL_1 - 1) {
455 DebugPrint((ClassDebugError, "ClasspPerfIncrementSuccessfulIo: "
456 "Error level 1 no longer required.\n"));
457 }
458 if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
459 SRB_FLAGS_DISABLE_SYNCH_TRANSFER)) {
460 CLEAR_FLAG(FdoExtension->SrbFlags,
461 SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
462 }
463 if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
464 SRB_FLAGS_QUEUE_ACTION_ENABLE)) {
465 SET_FLAG(FdoExtension->SrbFlags,
466 SRB_FLAGS_QUEUE_ACTION_ENABLE);
467 }
468 if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
469 SRB_FLAGS_NO_QUEUE_FREEZE)) {
470 SET_FLAG(FdoExtension->SrbFlags,
471 SRB_FLAGS_NO_QUEUE_FREEZE);
472 }
473 }
474 } // end of threshhold definitely being hit for first time
475
476 KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
477 return;
478 }
479
480 PMDL NTAPI BuildDeviceInputMdl(PVOID Buffer, ULONG BufferLen)
481 {
482 PMDL mdl;
483
484 mdl = IoAllocateMdl(Buffer, BufferLen, FALSE, FALSE, NULL);
485 if (mdl){
486 _SEH2_TRY {
487 /*
488 * We are reading from the device.
489 * Therefore, the device is WRITING to the locked memory.
490 * So we request IoWriteAccess.
491 */
492 MmProbeAndLockPages(mdl, KernelMode, IoWriteAccess);
493
494 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
495 NTSTATUS status = _SEH2_GetExceptionCode();
496
497 DBGWARN(("BuildReadMdl: MmProbeAndLockPages failed with %xh.", status));
498 IoFreeMdl(mdl);
499 mdl = NULL;
500 } _SEH2_END;
501 }
502 else {
503 DBGWARN(("BuildReadMdl: IoAllocateMdl failed"));
504 }
505
506 return mdl;
507 }
508
509 VOID NTAPI FreeDeviceInputMdl(PMDL Mdl)
510 {
511 MmUnlockPages(Mdl);
512 IoFreeMdl(Mdl);
513 }
514
515 #if 0
516 VOID
517 ClasspPerfResetCounters(
518 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
519 )
520 {
521 PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
522 KIRQL oldIrql;
523
524 KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
525 DebugPrint((ClassDebugError, "ClasspPerfResetCounters: "
526 "Resetting all perf counters.\n"));
527 fdoData->Perf.SuccessfulIO = 0;
528 FdoExtension->ErrorCount = 0;
529
530 if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
531 SRB_FLAGS_DISABLE_DISCONNECT)) {
532 CLEAR_FLAG(FdoExtension->SrbFlags,
533 SRB_FLAGS_DISABLE_DISCONNECT);
534 }
535 if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
536 SRB_FLAGS_DISABLE_SYNCH_TRANSFER)) {
537 CLEAR_FLAG(FdoExtension->SrbFlags,
538 SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
539 }
540 if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
541 SRB_FLAGS_QUEUE_ACTION_ENABLE)) {
542 SET_FLAG(FdoExtension->SrbFlags,
543 SRB_FLAGS_QUEUE_ACTION_ENABLE);
544 }
545 if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
546 SRB_FLAGS_NO_QUEUE_FREEZE)) {
547 SET_FLAG(FdoExtension->SrbFlags,
548 SRB_FLAGS_NO_QUEUE_FREEZE);
549 }
550 KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
551 return;
552 }
553 #endif