3 Copyright (C) Microsoft Corporation, 1991 - 1999
11 SCSI class driver routines
30 #pragma alloc_text(PAGE, ClassGetDeviceParameter)
31 #pragma alloc_text(PAGE, ClassScanForSpecial)
32 #pragma alloc_text(PAGE, ClassSetDeviceParameter)
37 // custom string match -- careful!
38 BOOLEAN
ClasspMyStringMatches(IN PCHAR StringToMatch OPTIONAL
, IN PCHAR TargetString
)
40 ULONG length
; // strlen returns an int, not size_t (!)
43 // if no match requested, return TRUE
44 if (StringToMatch
== NULL
) {
47 // cache the string length for efficiency
48 length
= strlen(StringToMatch
);
49 // ZERO-length strings may only match zero-length strings
51 return (strlen(TargetString
) == 0);
53 // strncmp returns zero if the strings match
54 return (strncmp(StringToMatch
, TargetString
, length
) == 0);
58 VOID
ClassGetDeviceParameter(
59 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
60 IN PWSTR SubkeyName OPTIONAL
,
61 IN PWSTR ParameterName
,
62 IN OUT PULONG ParameterValue
// also default value
66 RTL_QUERY_REGISTRY_TABLE queryTable
[2];
67 HANDLE deviceParameterHandle
;
68 HANDLE deviceSubkeyHandle
;
69 ULONG defaultParameterValue
;
74 // open the given parameter
77 status
= IoOpenDeviceRegistryKey(FdoExtension
->LowerPdo
,
78 PLUGPLAY_REGKEY_DEVICE
,
80 &deviceParameterHandle
);
82 if (NT_SUCCESS(status
) && (SubkeyName
!= NULL
)) {
84 UNICODE_STRING subkeyName
;
85 OBJECT_ATTRIBUTES objectAttributes
;
87 RtlInitUnicodeString(&subkeyName
, SubkeyName
);
88 InitializeObjectAttributes(&objectAttributes
,
90 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
91 deviceParameterHandle
,
94 status
= ZwOpenKey(&deviceSubkeyHandle
,
97 if (!NT_SUCCESS(status
)) {
98 ZwClose(deviceParameterHandle
);
103 if (NT_SUCCESS(status
)) {
105 RtlZeroMemory(queryTable
, sizeof(queryTable
));
107 defaultParameterValue
= *ParameterValue
;
109 queryTable
->Flags
= RTL_QUERY_REGISTRY_DIRECT
| RTL_QUERY_REGISTRY_REQUIRED
;
110 queryTable
->Name
= ParameterName
;
111 queryTable
->EntryContext
= ParameterValue
;
112 queryTable
->DefaultType
= REG_DWORD
;
113 queryTable
->DefaultData
= NULL
;
114 queryTable
->DefaultLength
= 0;
116 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
,
119 deviceParameterHandle
),
123 if (!NT_SUCCESS(status
)) {
124 *ParameterValue
= defaultParameterValue
; // use default value
128 // close what we open
132 ZwClose(deviceSubkeyHandle
);
135 ZwClose(deviceParameterHandle
);
140 } // end ClassGetDeviceParameter()
143 NTSTATUS
ClassSetDeviceParameter(
144 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
145 IN PWSTR SubkeyName OPTIONAL
,
146 IN PWSTR ParameterName
,
147 IN ULONG ParameterValue
)
150 HANDLE deviceParameterHandle
;
151 HANDLE deviceSubkeyHandle
;
156 // open the given parameter
159 status
= IoOpenDeviceRegistryKey(FdoExtension
->LowerPdo
,
160 PLUGPLAY_REGKEY_DEVICE
,
161 KEY_READ
| KEY_WRITE
,
162 &deviceParameterHandle
);
164 if (NT_SUCCESS(status
) && (SubkeyName
!= NULL
)) {
166 UNICODE_STRING subkeyName
;
167 OBJECT_ATTRIBUTES objectAttributes
;
169 RtlInitUnicodeString(&subkeyName
, SubkeyName
);
170 InitializeObjectAttributes(&objectAttributes
,
172 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
173 deviceParameterHandle
,
176 status
= ZwCreateKey(&deviceSubkeyHandle
,
177 KEY_READ
| KEY_WRITE
,
180 if (!NT_SUCCESS(status
)) {
181 ZwClose(deviceParameterHandle
);
186 if (NT_SUCCESS(status
)) {
188 status
= RtlWriteRegistryValue(
190 (PWSTR
) (SubkeyName
?
192 deviceParameterHandle
),
199 // close what we open
203 ZwClose(deviceSubkeyHandle
);
206 ZwClose(deviceParameterHandle
);
211 } // end ClassSetDeviceParameter()
215 * ClassScanForSpecial
217 * This routine was written to simplify scanning for special
218 * hardware based upon id strings. it does not check the registry.
221 VOID
ClassScanForSpecial(
222 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
223 IN CLASSPNP_SCAN_FOR_SPECIAL_INFO DeviceList
[],
224 IN PCLASS_SCAN_FOR_SPECIAL_HANDLER Function
)
226 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor
;
229 PUCHAR productRevision
;
230 UCHAR nullString
[] = "";
237 deviceDescriptor
= FdoExtension
->DeviceDescriptor
;
239 if (DeviceList
== NULL
) {
242 if (Function
== NULL
) {
247 // SCSI sets offsets to -1, ATAPI sets to 0. check for both.
250 if (deviceDescriptor
->VendorIdOffset
!= 0 &&
251 deviceDescriptor
->VendorIdOffset
!= -1) {
252 vendorId
= ((PUCHAR
)deviceDescriptor
);
253 vendorId
+= deviceDescriptor
->VendorIdOffset
;
255 vendorId
= nullString
;
257 if (deviceDescriptor
->ProductIdOffset
!= 0 &&
258 deviceDescriptor
->ProductIdOffset
!= -1) {
259 productId
= ((PUCHAR
)deviceDescriptor
);
260 productId
+= deviceDescriptor
->ProductIdOffset
;
262 productId
= nullString
;
264 if (deviceDescriptor
->VendorIdOffset
!= 0 &&
265 deviceDescriptor
->VendorIdOffset
!= -1) {
266 productRevision
= ((PUCHAR
)deviceDescriptor
);
267 productRevision
+= deviceDescriptor
->ProductRevisionOffset
;
269 productRevision
= nullString
;
273 // loop while the device list is valid (not null-filled)
276 for (;(DeviceList
->VendorId
!= NULL
||
277 DeviceList
->ProductId
!= NULL
||
278 DeviceList
->ProductRevision
!= NULL
);DeviceList
++) {
280 if (ClasspMyStringMatches(DeviceList
->VendorId
, vendorId
) &&
281 ClasspMyStringMatches(DeviceList
->ProductId
, productId
) &&
282 ClasspMyStringMatches(DeviceList
->ProductRevision
, productRevision
)
285 DebugPrint((1, "ClasspScanForSpecialByInquiry: Found matching "
286 "controller Ven: %s Prod: %s Rev: %s\n",
287 vendorId
, productId
, productRevision
));
290 // pass the context to the call back routine and exit
293 (Function
)(FdoExtension
, DeviceList
->Data
);
296 // for CHK builds, try to prevent wierd stacks by having a debug
297 // print here. it's a hack, but i know of no other way to prevent
298 // the stack from being wrong.
301 DebugPrint((16, "ClasspScanForSpecialByInquiry: "
302 "completed callback\n"));
305 } // else the strings did not match
307 } // none of the devices matched.
309 DebugPrint((1, "ClasspScanForSpecialByInquiry: no match found for %p\n",
310 FdoExtension
->DeviceObject
));
313 } // end ClasspScanForSpecialByInquiry()
317 // In order to provide better performance without the need to reboot,
318 // we need to implement a self-adjusting method to set and clear the
319 // srb flags based upon current performance.
321 // whenever there is an error, immediately grab the spin lock. the
322 // MP perf hit here is acceptable, since we're in an error path. this
323 // is also neccessary because we are guaranteed to be modifying the
324 // SRB flags here, setting SuccessfulIO to zero, and incrementing the
325 // actual error count (which is always done within this spinlock).
327 // whenever there is no error, increment a counter. if there have been
328 // errors on the device, and we've enabled dynamic perf, *and* we've
329 // just crossed the perf threshhold, then grab the spin lock and
330 // double check that the threshhold has, indeed been hit(*). then
331 // decrement the error count, and if it's dropped sufficiently, undo
332 // some of the safety changes made in the SRB flags due to the errors.
334 // * this works in all cases. even if lots of ios occur after the
335 // previous guy went in and cleared the successfulio counter, that
336 // just means that we've hit the threshhold again, and so it's proper
337 // to run the inner loop again.
341 ClasspPerfIncrementErrorCount(
342 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
345 PCLASS_PRIVATE_FDO_DATA fdoData
= FdoExtension
->PrivateFdoData
;
349 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
351 fdoData
->Perf
.SuccessfulIO
= 0; // implicit interlock
352 errors
= InterlockedIncrement(&FdoExtension
->ErrorCount
);
354 if (errors
>= CLASS_ERROR_LEVEL_1
) {
357 // If the error count has exceeded the error limit, then disable
358 // any tagged queuing, multiple requests per lu queueing
359 // and sychronous data transfers.
361 // Clearing the no queue freeze flag prevents the port driver
362 // from sending multiple requests per logical unit.
365 CLEAR_FLAG(FdoExtension
->SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
366 CLEAR_FLAG(FdoExtension
->SrbFlags
, SRB_FLAGS_QUEUE_ACTION_ENABLE
);
368 SET_FLAG(FdoExtension
->SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
370 DebugPrint((ClassDebugError
, "ClasspPerfIncrementErrorCount: "
371 "Too many errors; disabling tagged queuing and "
372 "synchronous data tranfers.\n"));
376 if (errors
>= CLASS_ERROR_LEVEL_2
) {
379 // If a second threshold is reached, disable disconnects.
382 SET_FLAG(FdoExtension
->SrbFlags
, SRB_FLAGS_DISABLE_DISCONNECT
);
383 DebugPrint((ClassDebugError
, "ClasspPerfIncrementErrorCount: "
384 "Too many errors; disabling disconnects.\n"));
387 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
392 ClasspPerfIncrementSuccessfulIo(
393 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
396 PCLASS_PRIVATE_FDO_DATA fdoData
= FdoExtension
->PrivateFdoData
;
402 // don't take a hit from the interlocked op unless we're in
403 // a degraded state and we've got a threshold to hit.
406 if (FdoExtension
->ErrorCount
== 0) {
410 if (fdoData
->Perf
.ReEnableThreshhold
== 0) {
414 succeeded
= InterlockedIncrement(&fdoData
->Perf
.SuccessfulIO
);
415 if (succeeded
< fdoData
->Perf
.ReEnableThreshhold
) {
420 // if we hit the threshold, grab the spinlock and verify we've
421 // actually done so. this allows us to ignore the spinlock 99%
425 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
428 // re-read the value, so we don't run this multiple times
429 // for a single threshhold being hit. this keeps errorcount
433 succeeded
= fdoData
->Perf
.SuccessfulIO
;
435 if ((FdoExtension
->ErrorCount
!= 0) &&
436 (fdoData
->Perf
.ReEnableThreshhold
<= succeeded
)
439 fdoData
->Perf
.SuccessfulIO
= 0; // implicit interlock
441 ASSERT(FdoExtension
->ErrorCount
> 0);
442 errors
= InterlockedDecrement(&FdoExtension
->ErrorCount
);
445 // note: do in reverse order of the sets "just in case"
448 if (errors
< CLASS_ERROR_LEVEL_2
) {
449 if (errors
== CLASS_ERROR_LEVEL_2
- 1) {
450 DebugPrint((ClassDebugError
, "ClasspPerfIncrementSuccessfulIo: "
451 "Error level 2 no longer required.\n"));
453 if (!TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
454 SRB_FLAGS_DISABLE_DISCONNECT
)) {
455 CLEAR_FLAG(FdoExtension
->SrbFlags
,
456 SRB_FLAGS_DISABLE_DISCONNECT
);
460 if (errors
< CLASS_ERROR_LEVEL_1
) {
461 if (errors
== CLASS_ERROR_LEVEL_1
- 1) {
462 DebugPrint((ClassDebugError
, "ClasspPerfIncrementSuccessfulIo: "
463 "Error level 1 no longer required.\n"));
465 if (!TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
466 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
)) {
467 CLEAR_FLAG(FdoExtension
->SrbFlags
,
468 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
470 if (TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
471 SRB_FLAGS_QUEUE_ACTION_ENABLE
)) {
472 SET_FLAG(FdoExtension
->SrbFlags
,
473 SRB_FLAGS_QUEUE_ACTION_ENABLE
);
475 if (TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
476 SRB_FLAGS_NO_QUEUE_FREEZE
)) {
477 SET_FLAG(FdoExtension
->SrbFlags
,
478 SRB_FLAGS_NO_QUEUE_FREEZE
);
481 } // end of threshhold definitely being hit for first time
483 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
488 PMDL
BuildDeviceInputMdl(PVOID Buffer
, ULONG BufferLen
)
492 mdl
= IoAllocateMdl(Buffer
, BufferLen
, FALSE
, FALSE
, NULL
);
496 * We are reading from the device.
497 * Therefore, the device is WRITING to the locked memory.
498 * So we request IoWriteAccess.
500 MmProbeAndLockPages(mdl
, KernelMode
, IoWriteAccess
);
502 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
503 NTSTATUS status
= _SEH2_GetExceptionCode();
505 DBGWARN(("BuildReadMdl: MmProbeAndLockPages failed with %xh.", status
));
511 DBGWARN(("BuildReadMdl: IoAllocateMdl failed"));
518 VOID
FreeDeviceInputMdl(PMDL Mdl
)
527 ClasspPerfResetCounters(
528 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
531 PCLASS_PRIVATE_FDO_DATA fdoData
= FdoExtension
->PrivateFdoData
;
534 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
535 DebugPrint((ClassDebugError
, "ClasspPerfResetCounters: "
536 "Resetting all perf counters.\n"));
537 fdoData
->Perf
.SuccessfulIO
= 0;
538 FdoExtension
->ErrorCount
= 0;
540 if (!TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
541 SRB_FLAGS_DISABLE_DISCONNECT
)) {
542 CLEAR_FLAG(FdoExtension
->SrbFlags
,
543 SRB_FLAGS_DISABLE_DISCONNECT
);
545 if (!TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
546 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
)) {
547 CLEAR_FLAG(FdoExtension
->SrbFlags
,
548 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
550 if (TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
551 SRB_FLAGS_QUEUE_ACTION_ENABLE
)) {
552 SET_FLAG(FdoExtension
->SrbFlags
,
553 SRB_FLAGS_QUEUE_ACTION_ENABLE
);
555 if (TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
556 SRB_FLAGS_NO_QUEUE_FREEZE
)) {
557 SET_FLAG(FdoExtension
->SrbFlags
,
558 SRB_FLAGS_NO_QUEUE_FREEZE
);
560 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);