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