xref: /reactos/drivers/storage/class/classpnp/utils.c (revision cc7cf826)
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 necessary 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 threshold, then grab the spin lock and
321 // double check that the threshold 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 threshold 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 synchronous 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.ReEnableThreshold == 0) {
404         return;
405     }
406 
407     succeeded = InterlockedIncrement((PLONG)&fdoData->Perf.SuccessfulIO);
408     if (succeeded < fdoData->Perf.ReEnableThreshold) {
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 threshold being hit.  this keeps errorcount
423     // somewhat useful.
424     //
425 
426     succeeded = fdoData->Perf.SuccessfulIO;
427 
428     if ((FdoExtension->ErrorCount != 0) &&
429         (fdoData->Perf.ReEnableThreshold <= 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 threshold 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
554