Source code of Windows XP (NT5)
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.

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