Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

635 lines
20 KiB

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