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