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);
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
69 RTL_QUERY_REGISTRY_TABLE queryTable
[2];
70 HANDLE deviceParameterHandle
;
71 HANDLE deviceSubkeyHandle
;
72 ULONG defaultParameterValue
;
77 // open the given parameter
80 status
= IoOpenDeviceRegistryKey(FdoExtension
->LowerPdo
,
81 PLUGPLAY_REGKEY_DEVICE
,
83 &deviceParameterHandle
);
85 if (NT_SUCCESS(status
) && (SubkeyName
!= NULL
)) {
87 UNICODE_STRING subkeyName
;
88 OBJECT_ATTRIBUTES objectAttributes
;
90 RtlInitUnicodeString(&subkeyName
, SubkeyName
);
91 InitializeObjectAttributes(&objectAttributes
,
93 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
94 deviceParameterHandle
,
97 status
= ZwOpenKey(&deviceSubkeyHandle
,
100 if (!NT_SUCCESS(status
)) {
101 ZwClose(deviceParameterHandle
);
106 if (NT_SUCCESS(status
)) {
108 RtlZeroMemory(queryTable
, sizeof(queryTable
));
110 defaultParameterValue
= *ParameterValue
;
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;
119 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
,
122 deviceParameterHandle
),
126 if (!NT_SUCCESS(status
)) {
127 *ParameterValue
= defaultParameterValue
; // use default value
131 // close what we open
135 ZwClose(deviceSubkeyHandle
);
138 ZwClose(deviceParameterHandle
);
143 } // end ClassGetDeviceParameter()
149 ClassSetDeviceParameter(
150 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
151 IN PWSTR SubkeyName OPTIONAL
,
152 IN PWSTR ParameterName
,
153 IN ULONG ParameterValue
)
156 HANDLE deviceParameterHandle
;
157 HANDLE deviceSubkeyHandle
;
162 // open the given parameter
165 status
= IoOpenDeviceRegistryKey(FdoExtension
->LowerPdo
,
166 PLUGPLAY_REGKEY_DEVICE
,
167 KEY_READ
| KEY_WRITE
,
168 &deviceParameterHandle
);
170 if (NT_SUCCESS(status
) && (SubkeyName
!= NULL
)) {
172 UNICODE_STRING subkeyName
;
173 OBJECT_ATTRIBUTES objectAttributes
;
175 RtlInitUnicodeString(&subkeyName
, SubkeyName
);
176 InitializeObjectAttributes(&objectAttributes
,
178 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
179 deviceParameterHandle
,
182 status
= ZwCreateKey(&deviceSubkeyHandle
,
183 KEY_READ
| KEY_WRITE
,
186 if (!NT_SUCCESS(status
)) {
187 ZwClose(deviceParameterHandle
);
192 if (NT_SUCCESS(status
)) {
194 status
= RtlWriteRegistryValue(
196 (PWSTR
) (SubkeyName
?
198 deviceParameterHandle
),
205 // close what we open
209 ZwClose(deviceSubkeyHandle
);
212 ZwClose(deviceParameterHandle
);
217 } // end ClassSetDeviceParameter()
221 * ClassScanForSpecial
223 * This routine was written to simplify scanning for special
224 * hardware based upon id strings. it does not check the registry.
231 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
232 IN CLASSPNP_SCAN_FOR_SPECIAL_INFO DeviceList
[],
233 IN PCLASS_SCAN_FOR_SPECIAL_HANDLER Function
)
235 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor
;
238 PUCHAR productRevision
;
239 UCHAR nullString
[] = "";
246 deviceDescriptor
= FdoExtension
->DeviceDescriptor
;
248 if (DeviceList
== NULL
) {
251 if (Function
== NULL
) {
256 // SCSI sets offsets to -1, ATAPI sets to 0. check for both.
259 if (deviceDescriptor
->VendorIdOffset
!= 0 &&
260 deviceDescriptor
->VendorIdOffset
!= -1) {
261 vendorId
= ((PUCHAR
)deviceDescriptor
);
262 vendorId
+= deviceDescriptor
->VendorIdOffset
;
264 vendorId
= nullString
;
266 if (deviceDescriptor
->ProductIdOffset
!= 0 &&
267 deviceDescriptor
->ProductIdOffset
!= -1) {
268 productId
= ((PUCHAR
)deviceDescriptor
);
269 productId
+= deviceDescriptor
->ProductIdOffset
;
271 productId
= nullString
;
273 if (deviceDescriptor
->VendorIdOffset
!= 0 &&
274 deviceDescriptor
->VendorIdOffset
!= -1) {
275 productRevision
= ((PUCHAR
)deviceDescriptor
);
276 productRevision
+= deviceDescriptor
->ProductRevisionOffset
;
278 productRevision
= nullString
;
282 // loop while the device list is valid (not null-filled)
285 for (;(DeviceList
->VendorId
!= NULL
||
286 DeviceList
->ProductId
!= NULL
||
287 DeviceList
->ProductRevision
!= NULL
);DeviceList
++) {
289 if (ClasspMyStringMatches(DeviceList
->VendorId
, vendorId
) &&
290 ClasspMyStringMatches(DeviceList
->ProductId
, productId
) &&
291 ClasspMyStringMatches(DeviceList
->ProductRevision
, productRevision
)
294 DebugPrint((1, "ClasspScanForSpecialByInquiry: Found matching "
295 "controller Ven: %s Prod: %s Rev: %s\n",
296 vendorId
, productId
, productRevision
));
299 // pass the context to the call back routine and exit
302 (Function
)(FdoExtension
, DeviceList
->Data
);
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.
310 DebugPrint((16, "ClasspScanForSpecialByInquiry: "
311 "completed callback\n"));
314 } // else the strings did not match
316 } // none of the devices matched.
318 DebugPrint((1, "ClasspScanForSpecialByInquiry: no match found for %p\n",
319 FdoExtension
->DeviceObject
));
322 } // end ClasspScanForSpecialByInquiry()
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.
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).
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.
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.
350 ClasspPerfIncrementErrorCount(
351 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
354 PCLASS_PRIVATE_FDO_DATA fdoData
= FdoExtension
->PrivateFdoData
;
358 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
360 fdoData
->Perf
.SuccessfulIO
= 0; // implicit interlock
361 errors
= InterlockedIncrement(&FdoExtension
->ErrorCount
);
363 if (errors
>= CLASS_ERROR_LEVEL_1
) {
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.
370 // Clearing the no queue freeze flag prevents the port driver
371 // from sending multiple requests per logical unit.
374 CLEAR_FLAG(FdoExtension
->SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
375 CLEAR_FLAG(FdoExtension
->SrbFlags
, SRB_FLAGS_QUEUE_ACTION_ENABLE
);
377 SET_FLAG(FdoExtension
->SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
379 DebugPrint((ClassDebugError
, "ClasspPerfIncrementErrorCount: "
380 "Too many errors; disabling tagged queuing and "
381 "synchronous data tranfers.\n"));
385 if (errors
>= CLASS_ERROR_LEVEL_2
) {
388 // If a second threshold is reached, disable disconnects.
391 SET_FLAG(FdoExtension
->SrbFlags
, SRB_FLAGS_DISABLE_DISCONNECT
);
392 DebugPrint((ClassDebugError
, "ClasspPerfIncrementErrorCount: "
393 "Too many errors; disabling disconnects.\n"));
396 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
401 ClasspPerfIncrementSuccessfulIo(
402 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
405 PCLASS_PRIVATE_FDO_DATA fdoData
= FdoExtension
->PrivateFdoData
;
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.
415 if (FdoExtension
->ErrorCount
== 0) {
419 if (fdoData
->Perf
.ReEnableThreshhold
== 0) {
423 succeeded
= InterlockedIncrement(&fdoData
->Perf
.SuccessfulIO
);
424 if (succeeded
< fdoData
->Perf
.ReEnableThreshhold
) {
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%
434 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
437 // re-read the value, so we don't run this multiple times
438 // for a single threshhold being hit. this keeps errorcount
442 succeeded
= fdoData
->Perf
.SuccessfulIO
;
444 if ((FdoExtension
->ErrorCount
!= 0) &&
445 (fdoData
->Perf
.ReEnableThreshhold
<= succeeded
)
448 fdoData
->Perf
.SuccessfulIO
= 0; // implicit interlock
450 ASSERT(FdoExtension
->ErrorCount
> 0);
451 errors
= InterlockedDecrement(&FdoExtension
->ErrorCount
);
454 // note: do in reverse order of the sets "just in case"
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"));
462 if (!TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
463 SRB_FLAGS_DISABLE_DISCONNECT
)) {
464 CLEAR_FLAG(FdoExtension
->SrbFlags
,
465 SRB_FLAGS_DISABLE_DISCONNECT
);
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"));
474 if (!TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
475 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
)) {
476 CLEAR_FLAG(FdoExtension
->SrbFlags
,
477 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
479 if (TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
480 SRB_FLAGS_QUEUE_ACTION_ENABLE
)) {
481 SET_FLAG(FdoExtension
->SrbFlags
,
482 SRB_FLAGS_QUEUE_ACTION_ENABLE
);
484 if (TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
485 SRB_FLAGS_NO_QUEUE_FREEZE
)) {
486 SET_FLAG(FdoExtension
->SrbFlags
,
487 SRB_FLAGS_NO_QUEUE_FREEZE
);
490 } // end of threshhold definitely being hit for first time
492 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
497 PMDL
BuildDeviceInputMdl(PVOID Buffer
, ULONG BufferLen
)
501 mdl
= IoAllocateMdl(Buffer
, BufferLen
, FALSE
, FALSE
, NULL
);
505 * We are reading from the device.
506 * Therefore, the device is WRITING to the locked memory.
507 * So we request IoWriteAccess.
509 MmProbeAndLockPages(mdl
, KernelMode
, IoWriteAccess
);
511 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
512 NTSTATUS status
= _SEH2_GetExceptionCode();
514 DBGWARN(("BuildReadMdl: MmProbeAndLockPages failed with %xh.", status
));
520 DBGWARN(("BuildReadMdl: IoAllocateMdl failed"));
527 VOID
FreeDeviceInputMdl(PMDL Mdl
)
536 ClasspPerfResetCounters(
537 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
540 PCLASS_PRIVATE_FDO_DATA fdoData
= FdoExtension
->PrivateFdoData
;
543 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
544 DebugPrint((ClassDebugError
, "ClasspPerfResetCounters: "
545 "Resetting all perf counters.\n"));
546 fdoData
->Perf
.SuccessfulIO
= 0;
547 FdoExtension
->ErrorCount
= 0;
549 if (!TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
550 SRB_FLAGS_DISABLE_DISCONNECT
)) {
551 CLEAR_FLAG(FdoExtension
->SrbFlags
,
552 SRB_FLAGS_DISABLE_DISCONNECT
);
554 if (!TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
555 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
)) {
556 CLEAR_FLAG(FdoExtension
->SrbFlags
,
557 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
559 if (TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
560 SRB_FLAGS_QUEUE_ACTION_ENABLE
)) {
561 SET_FLAG(FdoExtension
->SrbFlags
,
562 SRB_FLAGS_QUEUE_ACTION_ENABLE
);
564 if (TEST_FLAG(fdoData
->Perf
.OriginalSrbFlags
,
565 SRB_FLAGS_NO_QUEUE_FREEZE
)) {
566 SET_FLAG(FdoExtension
->SrbFlags
,
567 SRB_FLAGS_NO_QUEUE_FREEZE
);
569 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);