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