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.

2420 lines
58 KiB

  1. /*++
  2. Copyright (C) 2000-2001 Microsoft Corporation
  3. Module Name:
  4. hpdsm.c
  5. Abstract:
  6. This driver is the DSM for HP XP-256/512
  7. Author:
  8. Environment:
  9. kernel mode only
  10. Notes:
  11. Revision History:
  12. --*/
  13. #include <ntddk.h>
  14. #include <stdio.h>
  15. #include <stdarg.h>
  16. #include "dsm.h"
  17. #include "hpdsm.h"
  18. NTSTATUS
  19. DriverEntry(
  20. IN PDRIVER_OBJECT DriverObject,
  21. IN PUNICODE_STRING RegistryPath
  22. )
  23. /*++
  24. Routine Description:
  25. This routine is called when the driver loads loads.
  26. Arguments:
  27. DriverObject - Supplies the driver object.
  28. RegistryPath - Supplies the registry path.
  29. Return Value:
  30. NTSTATUS
  31. --*/
  32. {
  33. DSM_INIT_DATA initData;
  34. WCHAR dosDeviceName[40];
  35. UNICODE_STRING mpUnicodeName;
  36. PDEVICE_OBJECT deviceObject;
  37. PFILE_OBJECT fileObject;
  38. NTSTATUS status;
  39. PDSM_CONTEXT dsmContext;
  40. PDSM_MPIO_CONTEXT mpctlContext;
  41. PVOID buffer;
  42. //
  43. // Build the init data structure.
  44. //
  45. dsmContext = ExAllocatePool(NonPagedPool, sizeof(DSM_CONTEXT));
  46. if (dsmContext == NULL) {
  47. return STATUS_INSUFFICIENT_RESOURCES;
  48. }
  49. RtlZeroMemory(dsmContext, sizeof(DSM_CONTEXT));
  50. buffer = &initData;
  51. //
  52. // Set-up the init data
  53. //
  54. initData.DsmContext = (PVOID)dsmContext;
  55. initData.InitDataSize = sizeof(DSM_INIT_DATA);
  56. initData.DsmInquireDriver = HPInquire;
  57. initData.DsmCompareDevices = HPCompareDevices;
  58. initData.DsmSetDeviceInfo = HPSetDeviceInfo;
  59. initData.DsmGetControllerInfo = HPGetControllerInfo;
  60. initData.DsmIsPathActive = HPIsPathActive;
  61. initData.DsmPathVerify = HPPathVerify;
  62. initData.DsmInvalidatePath = HPInvalidatePath;
  63. initData.DsmRemoveDevice = HPRemoveDevice;
  64. initData.DsmRemovePath = HPRemovePath;
  65. initData.DsmReenablePath = HPBringPathOnLine;
  66. initData.DsmCategorizeRequest = HPCategorizeRequest;
  67. initData.DsmBroadcastSrb = HPBroadcastRequest;
  68. initData.DsmSrbDeviceControl = HPSrbDeviceControl;
  69. initData.DsmSetCompletion = HPSetCompletion;
  70. initData.DsmLBGetPath = HPLBGetPath;
  71. initData.DsmInterpretError = HPInterpretError;
  72. initData.DsmUnload = HPUnload;
  73. //
  74. // Need to also set-up the WMI info. TODO
  75. //
  76. //
  77. // Set the DriverObject. Used by MPIO for Unloading.
  78. //
  79. initData.DriverObject = DriverObject;
  80. RtlInitUnicodeString(&initData.DisplayName, L"HP FC-12 Device-Specific Module");
  81. //
  82. // Initialize the context objects.
  83. //
  84. KeInitializeSpinLock(&dsmContext->SpinLock);
  85. InitializeListHead(&dsmContext->GroupList);
  86. InitializeListHead(&dsmContext->DeviceList);
  87. InitializeListHead(&dsmContext->FailGroupList);
  88. ExInitializeNPagedLookasideList(&dsmContext->ContextList,
  89. NULL,
  90. NULL,
  91. 0,
  92. sizeof(COMPLETION_CONTEXT),
  93. 'MSDG',
  94. 0);
  95. //
  96. // Build the mpctl name.
  97. //
  98. swprintf(dosDeviceName, L"\\DosDevices\\MPathControl");
  99. RtlInitUnicodeString(&mpUnicodeName, dosDeviceName);
  100. //
  101. // Get mpctl's deviceObject.
  102. //
  103. status = IoGetDeviceObjectPointer(&mpUnicodeName,
  104. FILE_READ_ATTRIBUTES,
  105. &fileObject,
  106. &deviceObject);
  107. if (NT_SUCCESS(status)) {
  108. KEVENT event;
  109. PIRP irp;
  110. IO_STATUS_BLOCK ioStatus;
  111. //
  112. // Send the IOCTL to mpctl.sys to register ourselves.
  113. //
  114. DsmSendDeviceIoControlSynchronous(IOCTL_MPDSM_REGISTER,
  115. deviceObject,
  116. &initData,
  117. &initData,
  118. sizeof(DSM_INIT_DATA),
  119. sizeof(DSM_MPIO_CONTEXT),
  120. TRUE,
  121. &ioStatus);
  122. status = ioStatus.Status;
  123. ObDereferenceObject(fileObject);
  124. }
  125. if (status == STATUS_SUCCESS) {
  126. //
  127. // Grab the context value passed back by mpctl.
  128. //
  129. mpctlContext = buffer;
  130. dsmContext->MPIOContext = mpctlContext->MPIOContext;
  131. } else {
  132. DebugPrint((0,
  133. "HPDsm: Failed to register (%x)\n",
  134. status));
  135. //
  136. // Stay loaded, perhaps mpctl will come up later.
  137. // Will need to implement a mechanism to poll for mpio to arrive.
  138. //
  139. status = STATUS_SUCCESS;
  140. }
  141. return status;
  142. }
  143. NTSTATUS
  144. HPInquire (
  145. IN PVOID DsmContext,
  146. IN PDEVICE_OBJECT TargetDevice,
  147. IN PDEVICE_OBJECT PortObject,
  148. IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor,
  149. IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceIdList,
  150. OUT PVOID *DsmIdentifier
  151. )
  152. {
  153. PDEVICE_INFO deviceInfo;
  154. PGROUP_ENTRY group;
  155. NTSTATUS status;
  156. ULONG deviceState;
  157. ULONG allocationLength;
  158. PHP_ENQUIRY enquiry;
  159. PHP_DAC_STATUS dacStatus;
  160. UCHAR majorRev;
  161. UCHAR minorRev;
  162. ULONG loadBal;
  163. PUCHAR vendorIndex;
  164. PUCHAR productIndex;
  165. BOOLEAN needInquiry = FALSE;
  166. UCHAR nativeSlot;
  167. UCHAR portNumber;
  168. UCHAR logicalPort;
  169. PUCHAR vendorId = "HP ";
  170. PUCHAR productId = "FCArray";
  171. //
  172. // Ensure that the device's serial number is present. If not, can't claim
  173. // support for this drive.
  174. //
  175. if ((Descriptor->SerialNumberOffset == (ULONG)-1) ||
  176. (Descriptor->SerialNumberOffset == 0)) {
  177. // TODO : remove after FW update...
  178. //
  179. //return STATUS_NOT_SUPPORTED;
  180. needInquiry = TRUE;
  181. }
  182. vendorIndex = (PUCHAR)Descriptor;
  183. productIndex = (PUCHAR)Descriptor;
  184. (ULONG_PTR)vendorIndex += Descriptor->VendorIdOffset;
  185. (ULONG_PTR)productIndex += Descriptor->ProductIdOffset;
  186. //
  187. // Determine if the device is supported.
  188. //
  189. if ((!RtlEqualMemory(vendorId, vendorIndex, 8)) ||
  190. (!RtlEqualMemory(productId, productIndex, 7))) {
  191. return STATUS_NOT_SUPPORTED;
  192. }
  193. //
  194. // Allocate the descriptor. This is also used as DsmId.
  195. //
  196. allocationLength = sizeof(DEVICE_INFO);
  197. allocationLength += Descriptor->Size - sizeof(STORAGE_DEVICE_DESCRIPTOR);
  198. deviceInfo = ExAllocatePool(NonPagedPool, allocationLength);
  199. if (deviceInfo == NULL) {
  200. return STATUS_INSUFFICIENT_RESOURCES;
  201. }
  202. RtlZeroMemory(deviceInfo, allocationLength);
  203. //
  204. // Copy over the StorageDescriptor.
  205. //
  206. RtlCopyMemory(&deviceInfo->Descriptor,
  207. Descriptor,
  208. Descriptor->Size);
  209. //
  210. // Save the PortPdo Object.
  211. //
  212. deviceInfo->PortPdo = TargetDevice;
  213. //
  214. // Send the enquire command to get the FW revs. Based on the revision
  215. // we can either do fail-over only or active lb.
  216. //
  217. enquiry = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(HP_ENQUIRY));
  218. if (enquiry == NULL) {
  219. ExFreePool(deviceInfo);
  220. return STATUS_INSUFFICIENT_RESOURCES;
  221. }
  222. RtlZeroMemory(enquiry, sizeof(HP_ENQUIRY));
  223. status = HPSendDirectCommand(TargetDevice,
  224. (PUCHAR)enquiry,
  225. sizeof(HP_ENQUIRY),
  226. DCMD_ENQUIRY);
  227. if (NT_SUCCESS(status)) {
  228. majorRev = enquiry->FwMajorRev;
  229. minorRev = enquiry->FwMinorRev;
  230. DebugPrint((0,
  231. "HPDSM: FirmWare Major (%u) Minor (%u)\n",
  232. majorRev,
  233. minorRev));
  234. //
  235. // Free the buffer.
  236. //
  237. ExFreePool(enquiry);
  238. if (majorRev > 5 || (majorRev == 5 && minorRev >= 46)) {
  239. //
  240. // Supports dual active/active.
  241. //
  242. loadBal = LB_MIN_QUEUE;
  243. } else if (majorRev == 5 && minorRev >= 41) {
  244. //
  245. // Fail-over only.
  246. //
  247. loadBal = LB_ACTIVE_PASSIVE;
  248. } else {
  249. //
  250. // Not supported. LOG.
  251. //
  252. ExFreePool(deviceInfo);
  253. return STATUS_NOT_SUPPORTED;
  254. }
  255. } else {
  256. ExFreePool(enquiry);
  257. ExFreePool(deviceInfo);
  258. return status;
  259. }
  260. //
  261. // Send the Get Controller Status command so that the port
  262. // on which this device lives can be determined.
  263. //
  264. dacStatus = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(HP_DAC_STATUS));
  265. if (dacStatus == NULL) {
  266. ExFreePool(deviceInfo);
  267. return STATUS_INSUFFICIENT_RESOURCES;
  268. }
  269. RtlZeroMemory(dacStatus, sizeof(HP_DAC_STATUS));
  270. status = HPSendDirectCommand(TargetDevice,
  271. (PUCHAR)dacStatus,
  272. sizeof(HP_DAC_STATUS),
  273. DCMD_GET_DAC_STATUS);
  274. if (NT_SUCCESS(status)) {
  275. //
  276. // Build a logical port value (1-4) based on the NativeSlot and PortNumber
  277. // values. NativeSlot 0 or 1, and portNumber 0 or 1 for 4 possibilities.
  278. // NativeSlot refers to the controller and portNumber to the port.
  279. //
  280. nativeSlot = dacStatus->DACInfo[0] >> 4;
  281. portNumber = (dacStatus->DACInfo[1] >> 5) & 0x01;
  282. logicalPort = ((nativeSlot & 1) << 1) + (portNumber + 1);
  283. //
  284. // Set the port number for this device.
  285. // Used to help create the FOGroups and as the index into
  286. // the path array.
  287. //
  288. deviceInfo->Controller = logicalPort;
  289. } else {
  290. ExFreePool(deviceInfo);
  291. return status;
  292. }
  293. //
  294. // Build the controller serial number.
  295. // Bytes 40-47 of the inquiry data have the Node Name of Controller 0.
  296. // Use this as a 64-bit identifier.
  297. //
  298. if (needInquiry) {
  299. CDB cdb;
  300. PINQUIRYDATA inquiryBuffer;
  301. RtlZeroMemory(&cdb, sizeof(CDB));
  302. cdb.START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
  303. cdb.START_STOP.Start = 1;
  304. status = HPSendScsiCommand(TargetDevice,
  305. NULL,
  306. 0,
  307. 6,
  308. &cdb,
  309. 1);
  310. RtlZeroMemory(&cdb, sizeof(CDB));
  311. cdb.CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY;
  312. cdb.CDB6INQUIRY.AllocationLength = 56;
  313. inquiryBuffer = ExAllocatePool(NonPagedPoolCacheAligned, 56);
  314. status = HPSendScsiCommand(TargetDevice,
  315. (PUCHAR)inquiryBuffer,
  316. 56,
  317. 6,
  318. &cdb,
  319. 1);
  320. if (NT_SUCCESS(status)) {
  321. UCHAR controllerSerialNumber[9];
  322. UCHAR driveNumber[11];
  323. RtlZeroMemory(controllerSerialNumber, 9);
  324. RtlZeroMemory(driveNumber, 11);
  325. //
  326. // Copy the serial number over into the deviceInfo.
  327. // SerialNumber is built from the Node Name of the Controller +
  328. // the SystemDrive Number.
  329. //
  330. RtlCopyMemory(controllerSerialNumber,
  331. &inquiryBuffer->VendorSpecific[4],
  332. 8);
  333. //
  334. // Get the drive Number.
  335. //
  336. driveNumber[0] = inquiryBuffer->VendorSpecific[0];
  337. driveNumber[1] = inquiryBuffer->VendorSpecific[1];
  338. //
  339. // cat the driveNumber & controller serial number into 10-byte binary value.
  340. //
  341. RtlCopyMemory(&driveNumber[2],
  342. controllerSerialNumber,
  343. 8);
  344. //
  345. // Convert to ascii.
  346. //
  347. HPConvertHexToAscii(driveNumber,
  348. deviceInfo->SerialNumber,
  349. 10);
  350. DebugPrint((0,
  351. "HPInquiry: SerialNumber: %s\n", deviceInfo->SerialNumber));
  352. }
  353. }
  354. //
  355. // See if there is an existing Muli-path group to which this belongs.
  356. // (same serial number).
  357. //
  358. group = FindDevice(DsmContext,
  359. deviceInfo);
  360. if (group == NULL) {
  361. //
  362. // Build a multi-path group entry.
  363. //
  364. group = BuildGroupEntry(DsmContext,
  365. deviceInfo);
  366. if (group == NULL) {
  367. ExFreePool(deviceInfo);
  368. return STATUS_INSUFFICIENT_RESOURCES;
  369. }
  370. //
  371. // This is the first in the group, so make it the active
  372. // device. The actual active/passive devices will be set-up
  373. // later when the first call to LBGetPath is made.
  374. //
  375. deviceState = DEV_ACTIVE;
  376. } else {
  377. //
  378. // Already something active, this will be the fail-over
  379. // device until the load-balance groups are set-up.
  380. //
  381. deviceState = DEV_PASSIVE;
  382. }
  383. //
  384. // Add it to the list.
  385. //
  386. status = AddDeviceEntry(DsmContext,
  387. group,
  388. deviceInfo,
  389. deviceState);
  390. *DsmIdentifier = deviceInfo;
  391. return status;
  392. }
  393. VOID
  394. HPConvertHexToAscii(
  395. IN PUCHAR HexString,
  396. IN OUT PUCHAR AsciiString,
  397. IN ULONG Count
  398. )
  399. {
  400. ULONG i;
  401. ULONG j;
  402. UCHAR value;
  403. DebugPrint((0,
  404. "ConvertHexToAscii: "));
  405. for (i = 0, j = 0; i < Count; i++, j++) {
  406. value = HexString[i];
  407. DebugPrint((0,
  408. "%x ", value));
  409. if (value <= 9) {
  410. AsciiString[j] = value + '0';
  411. } else {
  412. AsciiString[j] = value - 10 + 'A';
  413. }
  414. }
  415. DebugPrint((0,"\n"));
  416. }
  417. BOOLEAN
  418. HPCompareDevices(
  419. IN PVOID DsmContext,
  420. IN PVOID DsmId1,
  421. IN PVOID DsmId2
  422. )
  423. {
  424. PDEVICE_INFO deviceInfo = DsmId1;
  425. PDEVICE_INFO comparedDevice = DsmId2;
  426. ULONG length;
  427. PUCHAR serialNumber;
  428. PUCHAR comparedSerialNumber;
  429. //
  430. // If this is an RS12, then no serial number in the device descriptor.
  431. //
  432. if (deviceInfo->Descriptor.SerialNumberOffset == (ULONG)-1) {
  433. //
  434. // Use the one's built from inquiry Data.
  435. //
  436. serialNumber = deviceInfo->SerialNumber;
  437. comparedSerialNumber = comparedDevice->SerialNumber;
  438. } else {
  439. //
  440. // Get the two serial numbers.
  441. // They reside at SNOffset from the front of the
  442. // descriptor buffer.
  443. //
  444. serialNumber = (PUCHAR)&deviceInfo->Descriptor;
  445. serialNumber += deviceInfo->Descriptor.SerialNumberOffset;
  446. comparedSerialNumber = (PUCHAR)&comparedDevice->Descriptor;
  447. comparedSerialNumber += comparedDevice->Descriptor.SerialNumberOffset;
  448. }
  449. //
  450. // Get the length of the base-device Serial Number.
  451. //
  452. length = strlen(serialNumber);
  453. //
  454. // If the lengths match, compare the contents.
  455. //
  456. if (length == strlen(comparedSerialNumber)) {
  457. if (RtlEqualMemory(serialNumber,
  458. comparedSerialNumber,
  459. length)) {
  460. return TRUE;
  461. }
  462. }
  463. return FALSE;
  464. }
  465. NTSTATUS
  466. HPSetDeviceInfo(
  467. IN PVOID DsmContext,
  468. IN PDEVICE_OBJECT TargetObject,
  469. IN PVOID DsmId,
  470. IN OUT PVOID *PathId
  471. )
  472. {
  473. PDEVICE_INFO deviceInfo = DsmId;
  474. PGROUP_ENTRY group = deviceInfo->Group;
  475. PFAILOVER_GROUP failGroup;
  476. NTSTATUS status;
  477. //
  478. // TargetObject is the destination for any requests created by this driver.
  479. // Save this for future reference.
  480. //
  481. deviceInfo->TargetObject = TargetObject;
  482. //
  483. // PathId indicates the path on which this device resides. Meaning
  484. // that when a Fail-Over occurs all device's on the same path fail together.
  485. // Search for a matching F.O. Group
  486. //
  487. failGroup = FindFOGroup(DsmContext,
  488. *PathId);
  489. //
  490. // if not found, create a new f.o. group
  491. //
  492. if (failGroup == NULL) {
  493. failGroup = BuildFOGroup(DsmContext,
  494. DsmId,
  495. *PathId);
  496. if (failGroup == NULL) {
  497. return STATUS_INSUFFICIENT_RESOURCES;
  498. }
  499. }
  500. //
  501. // add this deviceInfo to the f.o. group.
  502. //
  503. status = UpdateFOGroup(DsmContext,
  504. failGroup,
  505. deviceInfo);
  506. return status;
  507. }
  508. NTSTATUS
  509. HPGetControllerInfo(
  510. IN PVOID DsmContext,
  511. IN PVOID DsmId,
  512. IN ULONG Flags,
  513. IN OUT PCONTROLLER_INFO *ControllerInfo
  514. )
  515. {
  516. PCONTROLLER_INFO controllerInfo;
  517. if (Flags & DSM_CNTRL_FLAGS_ALLOCATE) {
  518. controllerInfo = ExAllocatePool(NonPagedPool, sizeof(CONTROLLER_INFO));
  519. if (controllerInfo == NULL) {
  520. return STATUS_INSUFFICIENT_RESOURCES;
  521. }
  522. RtlZeroMemory(controllerInfo, sizeof(CONTROLLER_INFO));
  523. //
  524. // TODO Get the ID etc.
  525. //
  526. controllerInfo->State = DSM_CONTROLLER_NO_CNTRL;
  527. *ControllerInfo = controllerInfo;
  528. } else {
  529. controllerInfo = *ControllerInfo;
  530. //
  531. // TODO Get the state.
  532. //
  533. controllerInfo->State = DSM_CONTROLLER_NO_CNTRL;
  534. }
  535. return STATUS_SUCCESS;
  536. }
  537. BOOLEAN
  538. HPIsPathActive(
  539. IN PVOID DsmContext,
  540. IN PVOID PathId
  541. )
  542. {
  543. PFAILOVER_GROUP group;
  544. //
  545. // NOTE: Internal callers of this assume certain behaviours. If it's changed,
  546. // those functions need to be updated appropriately.
  547. //
  548. //
  549. // Get the F.O. Group information.
  550. //
  551. group = FindFOGroup(DsmContext,
  552. PathId);
  553. //
  554. // If there are any devices on this path, and
  555. // it's not in a failed state: it's capable of handling requests
  556. // so it's active.
  557. //
  558. if ((group->Count >= 1) && (group->State == FG_NORMAL)) {
  559. return TRUE;
  560. }
  561. return FALSE;
  562. }
  563. NTSTATUS
  564. HPPathVerify(
  565. IN PVOID DsmContext,
  566. IN PVOID DsmId,
  567. IN PVOID PathId
  568. )
  569. {
  570. PDEVICE_INFO deviceInfo = DsmId;
  571. PFAILOVER_GROUP group;
  572. NTSTATUS status;
  573. ULONG i;
  574. //
  575. // Get the F.O. group
  576. //
  577. group = FindFOGroup(DsmContext,
  578. PathId);
  579. if (group == NULL) {
  580. return STATUS_DEVICE_NOT_CONNECTED;
  581. }
  582. //
  583. // Check the Path state to ensure all is normal.
  584. // Should be in FAILBACK state. This indicates that either
  585. // an admin utility told us we are O.K. or the AutoRecovery detected
  586. // the error was transitory.
  587. // BUGBUG: Need to implement both of the above assumptions.
  588. //
  589. if ((group->Count >= 1) && group->State == FG_FAILBACK) {
  590. //
  591. // Ensure that the device is still there
  592. //
  593. for (i = 0; i < group->Count; i++) {
  594. if (group->DeviceList[i] == deviceInfo) {
  595. //
  596. // Send it a TUR.
  597. //
  598. status = DsmSendTUR(deviceInfo->TargetObject);
  599. }
  600. }
  601. } else {
  602. //
  603. // What really has to happen:
  604. // Ensure the device is in our structs
  605. // Send it a TUR.
  606. // Depending upon prior state - update to the new, appropriate state.
  607. // return status.
  608. //
  609. status = STATUS_UNSUCCESSFUL;
  610. for (i = 0; i < group->Count; i++) {
  611. if (group->DeviceList[i] == deviceInfo) {
  612. status = DsmSendTUR(deviceInfo->TargetObject);
  613. }
  614. }
  615. if (!NT_SUCCESS(status)) {
  616. //
  617. // Either the device is not in the group, or the TUR was not successful.
  618. // TODO - Something.
  619. //
  620. }
  621. }
  622. //
  623. // Update the group State, depending upon the outcome.
  624. // TODO
  625. //
  626. ASSERT(status == STATUS_SUCCESS);
  627. if (status == STATUS_SUCCESS) {
  628. //
  629. // This lets the LBInit run to properly set-up this device.
  630. //
  631. deviceInfo->NeedsVerification = FALSE;
  632. }
  633. return status;
  634. }
  635. NTSTATUS
  636. HPInvalidatePath(
  637. IN PVOID DsmContext,
  638. IN ULONG ErrorMask,
  639. IN PVOID PathId,
  640. IN OUT PVOID *NewPathId
  641. )
  642. {
  643. PFAILOVER_GROUP failGroup;
  644. PFAILOVER_GROUP hintPath;
  645. PGROUP_ENTRY group;
  646. PDEVICE_INFO deviceInfo;
  647. NTSTATUS status;
  648. ULONG i;
  649. ASSERT((ErrorMask & DSM_FATAL_ERROR) || (ErrorMask & DSM_ADMIN_FO));
  650. failGroup = FindFOGroup(DsmContext,
  651. PathId);
  652. //
  653. // Mark the path as failed.
  654. //
  655. failGroup->State = FG_FAILED;
  656. //
  657. // First interation, the hint will be NULL. This allows the
  658. // GetNewPath routine the opportunity to select the best new path
  659. // Subsequent calls will be fed the updated value.
  660. //
  661. hintPath = NULL;
  662. //
  663. // Process each device in the fail-over group
  664. //
  665. for (i = 0; i < failGroup->Count; i++) {
  666. //
  667. // Get the deviceInfo.
  668. //
  669. deviceInfo = failGroup->DeviceList[i];
  670. //
  671. // Set the state of the Failing Devicea
  672. //
  673. deviceInfo->State = DEV_FAILED;
  674. //
  675. // Get it's Multi-Path Group entry.
  676. //
  677. group = deviceInfo->Group;
  678. //
  679. // Get a new path for this failed device.
  680. //
  681. hintPath = SetNewPath(DsmContext,
  682. group,
  683. deviceInfo,
  684. hintPath);
  685. }
  686. if (hintPath == NULL) {
  687. //
  688. // This indicates that no acceptable paths
  689. // were found. Return the error to mpctl.
  690. //
  691. status = STATUS_NO_SUCH_DEVICE;
  692. *NewPathId = NULL;
  693. } else {
  694. //
  695. // return the new path.
  696. //
  697. *NewPathId = hintPath->PathId;
  698. status = STATUS_SUCCESS;
  699. }
  700. return status;
  701. }
  702. NTSTATUS
  703. HPRemoveDevice(
  704. IN PVOID DsmContext,
  705. IN PVOID DsmId,
  706. IN PVOID PathId
  707. )
  708. {
  709. PDSM_CONTEXT dsmContext = DsmContext;
  710. PDEVICE_INFO deviceInfo;
  711. PFAILOVER_GROUP failGroup;
  712. PGROUP_ENTRY group;
  713. ULONG state;
  714. WCHAR buffer[64];
  715. //
  716. // DsmId is our deviceInfo structure.
  717. //
  718. deviceInfo = DsmId;
  719. //
  720. // Get it's Multi-Path Group entry.
  721. //
  722. group = deviceInfo->Group;
  723. //
  724. // Get the Fail-over group.
  725. //
  726. failGroup = deviceInfo->FailGroup;
  727. //
  728. // If it's active, need to 'Fail-Over' to another device in
  729. // the group.
  730. //
  731. state = deviceInfo->State;
  732. //
  733. // Set the state of the Failing Devicea
  734. //
  735. deviceInfo->State = DEV_FAILED;
  736. if (state == DEV_ACTIVE) {
  737. //
  738. // Find the next available device.
  739. // This is basically a fail-over for just
  740. // this device.
  741. //
  742. SetNewPath(DsmContext,
  743. group,
  744. deviceInfo,
  745. NULL);
  746. }
  747. //
  748. // Remove it's entry from the Fail-Over Group.
  749. //
  750. RemoveDeviceFailGroup(DsmContext,
  751. failGroup,
  752. deviceInfo);
  753. //
  754. // Remove it from it's multi-path group. This has the side-effect
  755. // of cleaning up the Group if the number of devices goes to zero.
  756. //
  757. RemoveDeviceEntry(DsmContext,
  758. group,
  759. deviceInfo);
  760. swprintf(buffer, L"Removing Device");
  761. DsmWriteEvent(dsmContext->MPIOContext,
  762. L"HpDsm",
  763. buffer,
  764. 2);
  765. return STATUS_SUCCESS;
  766. }
  767. NTSTATUS
  768. HPRemovePath(
  769. IN PDSM_CONTEXT DsmContext,
  770. IN PVOID PathId
  771. )
  772. {
  773. PFAILOVER_GROUP failGroup;
  774. KIRQL irql;
  775. failGroup = FindFOGroup(DsmContext,
  776. PathId);
  777. if (failGroup == NULL) {
  778. //
  779. // It's already been removed.
  780. // LOG though.
  781. //
  782. return STATUS_SUCCESS;
  783. }
  784. //
  785. // The claim is that a path won't be removed, until all
  786. // the devices on it are.
  787. //
  788. ASSERT(failGroup->Count == 0);
  789. KeAcquireSpinLock(&DsmContext->SpinLock, &irql);
  790. //
  791. // Yank it from the list.
  792. //
  793. RemoveEntryList(&failGroup->ListEntry);
  794. DsmContext->NumberFOGroups--;
  795. //
  796. // Zero the entry.
  797. //
  798. RtlZeroMemory(failGroup, sizeof(FAILOVER_GROUP));
  799. KeReleaseSpinLock(&DsmContext->SpinLock, irql);
  800. //
  801. // Free the allocation.
  802. //
  803. ExFreePool(failGroup);
  804. return STATUS_SUCCESS;
  805. }
  806. NTSTATUS
  807. HPBringPathOnLine(
  808. IN PVOID DsmContext,
  809. IN PVOID PathId,
  810. OUT PULONG DSMError
  811. )
  812. {
  813. PFAILOVER_GROUP failGroup;
  814. //
  815. // PathVerify has been called already, so if
  816. // it came back successfully, then this is O.K.
  817. //
  818. failGroup = FindFOGroup(DsmContext,
  819. PathId);
  820. if (failGroup == NULL) {
  821. //
  822. // LOG
  823. //
  824. *DSMError = 0;
  825. return STATUS_DEVICE_NOT_CONNECTED;
  826. }
  827. //
  828. // Should be in FG_PENDING
  829. //
  830. ASSERT(failGroup->State == FG_PENDING);
  831. //
  832. // Indicate that it's ready to go.
  833. //
  834. failGroup->State = FG_NORMAL;
  835. return STATUS_SUCCESS;
  836. }
  837. PVOID
  838. HPLBGetPath(
  839. IN PVOID DsmContext,
  840. IN PSCSI_REQUEST_BLOCK Srb,
  841. IN PDSM_IDS DsmList,
  842. IN PVOID CurrentPath,
  843. OUT NTSTATUS *Status
  844. )
  845. {
  846. PDEVICE_INFO deviceInfo;
  847. PGROUP_ENTRY group;
  848. PFAILOVER_GROUP failGroup = NULL;
  849. ULONG i;
  850. //
  851. // Up-front checking to minimally validate
  852. // the list of DsmId's being passed in.
  853. //
  854. ASSERT(DsmList->Count);
  855. ASSERT(DsmList->IdList[0]);
  856. //
  857. // Grab the first device from the list.
  858. //
  859. deviceInfo = DsmList->IdList[0];
  860. //
  861. // Get the multi-path group.
  862. //
  863. group = deviceInfo->Group;
  864. //
  865. // See if Load-Balancing has been initialized.
  866. //
  867. if (group->LoadBalanceInit == FALSE) {
  868. PDEVICE_INFO lbDevice;
  869. BOOLEAN doInit = TRUE;
  870. //
  871. // Check to see whether we are really ready to run
  872. // the LBInit. If any of the list aren't verified, then
  873. // we will hold off.
  874. //
  875. for (i = 0; i < DsmList->Count; i++) {
  876. lbDevice = DsmList->IdList[i];
  877. if (lbDevice->NeedsVerification) {
  878. DebugPrint((0,
  879. "LBGetPath: (%x) needs verify\n",
  880. lbDevice));
  881. doInit = FALSE;
  882. break;
  883. }
  884. }
  885. if (doInit) {
  886. //
  887. // Set-up the load-balancing. This routine
  888. // builds a static assignment of multi-path group to
  889. // a particular path.
  890. //
  891. LBInit(DsmContext,
  892. group);
  893. }
  894. }
  895. //
  896. // Ensure that mpctl and this dsm are in sync.
  897. //
  898. ASSERT(DsmList->Count == group->NumberDevices);
  899. //
  900. // Find the active device.
  901. //
  902. for (i = 0; i < DsmList->Count; i++) {
  903. //
  904. // Get each of the DsmId's, in reality the deviceInfo.
  905. //
  906. deviceInfo = DsmList->IdList[i];
  907. //
  908. // Ensure that the device is in our list.
  909. //
  910. ASSERT(FindDevice(DsmContext, deviceInfo));
  911. //
  912. // NOTE: This assumes 'static' Load-Balancing. Once others
  913. // are implemented, this section will have to be updated.
  914. //
  915. // Return the path on which the ACTIVE device resides.
  916. //
  917. if (deviceInfo->State == DEV_ACTIVE) {
  918. //
  919. // Get the F.O.Group, as it contains the
  920. // correct PathId for this device.
  921. //
  922. failGroup = deviceInfo->FailGroup;
  923. *Status = STATUS_SUCCESS;
  924. return failGroup->PathId;
  925. }
  926. }
  927. //
  928. // Should never have gotten here.
  929. //
  930. DebugPrint((0,
  931. "LBGetPath: Returning STATUS_DEVICE_NOT_CONNECTED\n"));
  932. DbgBreakPoint();
  933. ASSERT(failGroup);
  934. *Status = STATUS_DEVICE_NOT_CONNECTED;
  935. return NULL;
  936. }
  937. ULONG
  938. HPCategorizeRequest(
  939. IN PVOID DsmContext,
  940. IN PDSM_IDS DsmIds,
  941. IN PIRP Irp,
  942. IN PSCSI_REQUEST_BLOCK Srb,
  943. IN PVOID CurrentPath,
  944. OUT PVOID *PathId,
  945. OUT NTSTATUS *Status
  946. )
  947. {
  948. ULONG dsmStatus;
  949. NTSTATUS status;
  950. //
  951. // Requests to broadcast
  952. // Reset
  953. // Reserve
  954. // Release
  955. //
  956. // Requests to Handle
  957. // None for now.
  958. //
  959. //
  960. // For all other requests, punt it back to the bus-driver.
  961. // Need to get a path for the request first, so call the Load-Balance
  962. // function.
  963. //
  964. *PathId = HPLBGetPath(DsmContext,
  965. Srb,
  966. DsmIds,
  967. CurrentPath,
  968. &status);
  969. if (NT_SUCCESS(status)) {
  970. //
  971. // Indicate that the path is updated, and mpctl should handle the request.
  972. //
  973. dsmStatus = DSM_PATH_SET;
  974. } else {
  975. //
  976. // Indicate the error back to mpctl.
  977. //
  978. dsmStatus = DSM_ERROR;
  979. //
  980. // Mark-up the Srb to show that a failure has occurred.
  981. // This value is really only for this DSM to know what to do
  982. // in the InterpretError routine - Fatal Error.
  983. // It could be something more meaningful.
  984. //
  985. Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
  986. }
  987. //
  988. // Pass back status info to mpctl.
  989. //
  990. *Status = status;
  991. return dsmStatus;
  992. }
  993. NTSTATUS
  994. HPBroadcastRequest(
  995. IN PVOID DsmContext,
  996. IN PDSM_IDS DsmIds,
  997. IN PIRP Irp,
  998. IN PSCSI_REQUEST_BLOCK Srb,
  999. IN PKEVENT Event
  1000. )
  1001. {
  1002. //
  1003. // BUGBUG: Need to handle Reset, Reserve, and Release.
  1004. //
  1005. return STATUS_INVALID_DEVICE_REQUEST;
  1006. }
  1007. NTSTATUS
  1008. HPSrbDeviceControl(
  1009. IN PVOID DsmContext,
  1010. IN PDSM_IDS DsmIds,
  1011. IN PIRP Irp,
  1012. IN PSCSI_REQUEST_BLOCK Srb,
  1013. IN PKEVENT Event
  1014. )
  1015. {
  1016. //
  1017. // BUGBUG: Need to handle ??
  1018. //
  1019. return STATUS_INVALID_DEVICE_REQUEST;
  1020. }
  1021. VOID
  1022. HPCompletion(
  1023. IN PVOID DsmId,
  1024. IN PIRP Irp,
  1025. IN PSCSI_REQUEST_BLOCK Srb,
  1026. IN PVOID DsmContext
  1027. )
  1028. {
  1029. PCOMPLETION_CONTEXT completionContext = DsmContext;
  1030. PDEVICE_INFO deviceInfo;
  1031. PDSM_CONTEXT dsmContext;
  1032. UCHAR opCode;
  1033. //
  1034. // If it's read or write, save stats.
  1035. // Categorize set-up the Context to have path, target info.
  1036. // TODO
  1037. //
  1038. ASSERT(DsmContext);
  1039. dsmContext = completionContext->DsmContext;
  1040. deviceInfo = completionContext->DeviceInfo;
  1041. opCode = Srb->Cdb[0];
  1042. //
  1043. // Indicate one less request on this device.
  1044. //
  1045. InterlockedDecrement(&deviceInfo->Requests);
  1046. //
  1047. // TODO: Use the timestamp.
  1048. // Path/Device up-time, ave. time/request...
  1049. //
  1050. //
  1051. // If it's a read or a write, update the stats.
  1052. //
  1053. if (opCode == SCSIOP_READ) {
  1054. deviceInfo->Stats.NumberReads++;
  1055. deviceInfo->Stats.BytesRead.QuadPart += Srb->DataTransferLength;
  1056. } else if (opCode == SCSIOP_WRITE) {
  1057. deviceInfo->Stats.NumberWrites++;
  1058. deviceInfo->Stats.BytesWritten.QuadPart += Srb->DataTransferLength;
  1059. }
  1060. //
  1061. // Release the allocation.
  1062. //
  1063. ExFreeToNPagedLookasideList(&dsmContext->ContextList,
  1064. DsmContext);
  1065. }
  1066. VOID
  1067. HPSetCompletion(
  1068. IN PVOID DsmContext,
  1069. IN PVOID DsmId,
  1070. IN PIRP Irp,
  1071. IN PSCSI_REQUEST_BLOCK Srb,
  1072. IN OUT PDSM_COMPLETION_INFO DsmCompletion
  1073. )
  1074. {
  1075. PCOMPLETION_CONTEXT completionContext;
  1076. PDSM_CONTEXT dsmContext = DsmContext;
  1077. PDEVICE_INFO deviceInfo = DsmId;
  1078. //
  1079. // Save the DeviceInfo as being the target for this request.
  1080. // Get a timestamp
  1081. // TODO Determine other data.
  1082. //
  1083. completionContext = ExAllocateFromNPagedLookasideList(&dsmContext->ContextList);
  1084. if (completionContext == NULL) {
  1085. //
  1086. // LOG
  1087. //
  1088. }
  1089. //
  1090. // Time stamp this.
  1091. //
  1092. KeQueryTickCount(&completionContext->TickCount);
  1093. //
  1094. // Indicate the target for this request.
  1095. //
  1096. completionContext->DeviceInfo = deviceInfo;
  1097. completionContext->DsmContext = DsmContext;
  1098. //
  1099. // Indicate one more request on this device.
  1100. // LB may use this.
  1101. //
  1102. InterlockedIncrement(&deviceInfo->Requests);
  1103. DsmCompletion->DsmCompletionRoutine = HPCompletion;
  1104. DsmCompletion->DsmContext = completionContext;
  1105. return;
  1106. }
  1107. ULONG
  1108. HPInterpretError(
  1109. IN PVOID DsmContext,
  1110. IN PVOID DsmId,
  1111. IN PSCSI_REQUEST_BLOCK Srb,
  1112. IN OUT NTSTATUS *Status,
  1113. OUT PBOOLEAN Retry
  1114. )
  1115. {
  1116. ULONG errorMask = 0;
  1117. BOOLEAN failover = FALSE;
  1118. BOOLEAN retry = FALSE;
  1119. BOOLEAN handled = FALSE;
  1120. //
  1121. // Check the NT Status first.
  1122. // Several are clearly failover conditions.
  1123. //
  1124. switch (*Status) {
  1125. case STATUS_DEVICE_NOT_CONNECTED:
  1126. case STATUS_DEVICE_DOES_NOT_EXIST:
  1127. case STATUS_NO_SUCH_DEVICE:
  1128. //
  1129. // The port pdo has either been removed or is
  1130. // very broken. A fail-over is necessary.
  1131. //
  1132. handled = TRUE;
  1133. failover = TRUE;
  1134. break;
  1135. default:
  1136. break;
  1137. }
  1138. if (handled == FALSE) {
  1139. if (Srb) {
  1140. //
  1141. // The ntstatus didn't indicate a fail-over condition, but
  1142. // check various srb status for failover-class error.
  1143. //
  1144. switch (Srb->SrbStatus) {
  1145. case SRB_STATUS_SELECTION_TIMEOUT:
  1146. case SRB_STATUS_INVALID_LUN:
  1147. case SRB_STATUS_INVALID_TARGET_ID:
  1148. case SRB_STATUS_NO_DEVICE:
  1149. case SRB_STATUS_NO_HBA:
  1150. case SRB_STATUS_INVALID_PATH_ID:
  1151. //
  1152. // All of these are fatal.
  1153. //
  1154. failover = TRUE;
  1155. break;
  1156. default:
  1157. break;
  1158. }
  1159. }
  1160. }
  1161. if (failover) {
  1162. DebugPrint((0,
  1163. "InterpretError: Marking Fatal. Srb (%x). *Status (%x)\n",
  1164. Srb,
  1165. *Status));
  1166. errorMask = DSM_FATAL_ERROR;
  1167. }
  1168. //
  1169. // TODO: Gather a list of status that indicate a retry is necessary.
  1170. // Look at InterpretSenseInfo.
  1171. //
  1172. *Retry = retry;
  1173. return errorMask;
  1174. }
  1175. NTSTATUS
  1176. HPUnload(
  1177. IN PVOID DsmContext
  1178. )
  1179. {
  1180. //
  1181. // It's the responsibility of the mpio bus driver to have already
  1182. // destroyed all devices and paths.
  1183. // As those functions free allocations for the objects, the only thing
  1184. // needed here is to free the DsmContext.
  1185. //
  1186. ExFreePool(DsmContext);
  1187. return STATUS_SUCCESS;
  1188. }
  1189. //
  1190. // Utility functions.
  1191. //
  1192. NTSTATUS
  1193. HPSendDirectCommand(
  1194. IN PDEVICE_OBJECT DeviceObject,
  1195. IN PUCHAR Buffer,
  1196. IN ULONG BufferSize,
  1197. IN UCHAR Opcode
  1198. )
  1199. {
  1200. PSCSI_PASS_THROUGH_DIRECT passThrough;
  1201. ULONG length;
  1202. NTSTATUS status;
  1203. PCDB cdb;
  1204. IO_STATUS_BLOCK ioStatus;
  1205. DsmSendTUR(DeviceObject);
  1206. //
  1207. // Allocate the pass through plus sense info buffer.
  1208. //
  1209. length = sizeof(SCSI_PASS_THROUGH_DIRECT) + sizeof(SENSE_DATA);
  1210. passThrough = ExAllocatePool(NonPagedPool,
  1211. length);
  1212. if (passThrough == NULL) {
  1213. return STATUS_INSUFFICIENT_RESOURCES;
  1214. }
  1215. RtlZeroMemory(passThrough, length);
  1216. //
  1217. // These are always 10-byte CDB, guess on the timeout.
  1218. // Buffer is allocated by the caller and is it's responsibility to be correctly
  1219. // sized and aligned.
  1220. //
  1221. passThrough->Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
  1222. passThrough->CdbLength = 10;
  1223. passThrough->SenseInfoLength = sizeof(SENSE_DATA);
  1224. passThrough->DataIn = 1;
  1225. passThrough->DataTransferLength = BufferSize;
  1226. passThrough->TimeOutValue = 20;
  1227. passThrough->DataBuffer = Buffer;
  1228. passThrough->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH_DIRECT);
  1229. cdb = (PCDB)passThrough->Cdb;
  1230. //
  1231. // These are always 0x20.
  1232. //
  1233. cdb->CDB10.OperationCode = 0x20;
  1234. //
  1235. // The sub-code (DCMD OpCode)
  1236. //
  1237. cdb->CDB10.LogicalBlockByte0 = Opcode;
  1238. //
  1239. // Allocation length.
  1240. //
  1241. cdb->CDB10.TransferBlocksLsb = (UCHAR)(BufferSize & 0xFF);
  1242. cdb->CDB10.TransferBlocksMsb = (UCHAR)(BufferSize >> 8);
  1243. //
  1244. // Submit the command.
  1245. //
  1246. DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH_DIRECT,
  1247. DeviceObject,
  1248. passThrough,
  1249. passThrough,
  1250. length,
  1251. length,
  1252. FALSE,
  1253. &ioStatus);
  1254. status = ioStatus.Status;
  1255. // status = DsmSendPassThroughDirect(DeviceObject,
  1256. // passThrough,
  1257. // length,
  1258. // BufferSize);
  1259. if (!NT_SUCCESS(status)) {
  1260. //
  1261. // The call above has already 'interpreted' the senseInfo, but check
  1262. // to see if it is correct.
  1263. //
  1264. if (Opcode == 0x5) {
  1265. //
  1266. // No error conditions reported with this command, so trust the interpretation.
  1267. //
  1268. //
  1269. }
  1270. }
  1271. if (passThrough->ScsiStatus) {
  1272. DebugPrint((0,
  1273. "SendDirect: ScsiStatus (%x)\n",
  1274. passThrough->ScsiStatus));
  1275. }
  1276. ExFreePool(passThrough);
  1277. return status;
  1278. }
  1279. NTSTATUS
  1280. HPSendScsiCommand(
  1281. IN PDEVICE_OBJECT DeviceObject,
  1282. IN PUCHAR Buffer,
  1283. IN ULONG BufferSize,
  1284. IN ULONG CdbLength,
  1285. IN PCDB Cdb,
  1286. IN BOOLEAN DataIn
  1287. )
  1288. {
  1289. PSCSI_PASS_THROUGH_DIRECT passThrough;
  1290. ULONG length;
  1291. NTSTATUS status;
  1292. IO_STATUS_BLOCK ioStatus;
  1293. //
  1294. // Allocate the pass through plus sense info buffer.
  1295. //
  1296. length = sizeof(SCSI_PASS_THROUGH_DIRECT) + sizeof(SENSE_DATA);
  1297. passThrough = ExAllocatePool(NonPagedPool,
  1298. length);
  1299. if (passThrough == NULL) {
  1300. return STATUS_INSUFFICIENT_RESOURCES;
  1301. }
  1302. RtlZeroMemory(passThrough, length);
  1303. //
  1304. // These are always 10-byte CDB, guess on the timeout.
  1305. // Buffer is allocated by the caller and is it's responsibility to be correctly
  1306. // sized and aligned.
  1307. //
  1308. passThrough->Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
  1309. passThrough->CdbLength = (UCHAR)CdbLength;
  1310. passThrough->SenseInfoLength = sizeof(SENSE_DATA);
  1311. passThrough->DataIn = DataIn;
  1312. passThrough->DataTransferLength = BufferSize;
  1313. passThrough->TimeOutValue = 20;
  1314. passThrough->DataBuffer = Buffer;
  1315. passThrough->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH_DIRECT);
  1316. RtlCopyMemory(passThrough->Cdb,
  1317. Cdb,
  1318. CdbLength);
  1319. //
  1320. // Submit the command.
  1321. //
  1322. DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH_DIRECT,
  1323. DeviceObject,
  1324. passThrough,
  1325. passThrough,
  1326. length,
  1327. length,
  1328. FALSE,
  1329. &ioStatus);
  1330. status = ioStatus.Status;
  1331. // status = DsmSendPassThroughDirect(DeviceObject,
  1332. // passThrough,
  1333. // length,
  1334. // BufferSize);
  1335. return status;
  1336. }
  1337. PGROUP_ENTRY
  1338. FindDevice(
  1339. IN PDSM_CONTEXT DsmContext,
  1340. IN PDEVICE_INFO DeviceInfo
  1341. )
  1342. {
  1343. PDEVICE_INFO deviceInfo;
  1344. PLIST_ENTRY entry;
  1345. ULONG i;
  1346. //
  1347. // Run through the DeviceInfo List
  1348. //
  1349. entry = DsmContext->DeviceList.Flink;
  1350. for (i = 0; i < DsmContext->NumberDevices; i++, entry = entry->Flink) {
  1351. //
  1352. // Extract the deviceInfo structure.
  1353. //
  1354. deviceInfo = CONTAINING_RECORD(entry, DEVICE_INFO, ListEntry);
  1355. ASSERT(deviceInfo);
  1356. //
  1357. // Call the Serial Number compare routine.
  1358. //
  1359. if (HPCompareDevices(DsmContext,
  1360. DeviceInfo,
  1361. deviceInfo)) {
  1362. return deviceInfo->Group;
  1363. }
  1364. }
  1365. DebugPrint((0,
  1366. "DsmFindDevice: DsmContext (%x), DeviceInfo (%x)\n",
  1367. DsmContext,
  1368. DeviceInfo));
  1369. return NULL;
  1370. }
  1371. PGROUP_ENTRY
  1372. BuildGroupEntry(
  1373. IN PDSM_CONTEXT DsmContext,
  1374. IN PDEVICE_INFO DeviceInfo
  1375. )
  1376. {
  1377. PGROUP_ENTRY group;
  1378. //
  1379. // Allocate the memory for the multi-path group.
  1380. //
  1381. group = ExAllocatePool(NonPagedPool, sizeof(GROUP_ENTRY));
  1382. if (group == NULL) {
  1383. return NULL;
  1384. }
  1385. RtlZeroMemory(group, sizeof(GROUP_ENTRY));
  1386. //
  1387. // Add it to the list of multi-path groups.
  1388. //
  1389. ExInterlockedInsertTailList(&DsmContext->GroupList,
  1390. &group->ListEntry,
  1391. &DsmContext->SpinLock);
  1392. group->GroupNumber = InterlockedIncrement(&DsmContext->NumberGroups);
  1393. ASSERT(group->GroupNumber >= 1);
  1394. return group;
  1395. }
  1396. NTSTATUS
  1397. AddDeviceEntry(
  1398. IN PDSM_CONTEXT DsmContext,
  1399. IN PGROUP_ENTRY Group,
  1400. IN PDEVICE_INFO DeviceInfo,
  1401. IN ULONG DeviceState
  1402. )
  1403. {
  1404. ULONG numberDevices;
  1405. ULONG i;
  1406. KIRQL irql;
  1407. //
  1408. // Ensure that this is a valid config - namely, it hasn't
  1409. // exceeded the number of paths supported.
  1410. //
  1411. numberDevices = Group->NumberDevices;
  1412. if (numberDevices >= MAX_PATHS) {
  1413. return STATUS_UNSUCCESSFUL;
  1414. }
  1415. KeAcquireSpinLock(&DsmContext->SpinLock, &irql);
  1416. #if DBG
  1417. //
  1418. // Ensure that this isn't a second copy of the same pdo.
  1419. //
  1420. for (i = 0; i < numberDevices; i++) {
  1421. if (Group->DeviceList[i]->PortPdo == DeviceInfo->PortPdo) {
  1422. DebugPrint((0,
  1423. "DsmAddDeviceEntry: Received same PDO twice\n"));
  1424. DbgBreakPoint();
  1425. }
  1426. }
  1427. #endif
  1428. //
  1429. // Indicate one device is present in
  1430. // this group.
  1431. //
  1432. Group->DeviceList[numberDevices] = DeviceInfo;
  1433. //
  1434. // Indicate one more in the list.
  1435. //
  1436. Group->NumberDevices++;
  1437. //
  1438. // Set-up this device's group id.
  1439. //
  1440. DeviceInfo->Group = Group;
  1441. //
  1442. // Set-up whether this is an active/passive member of the
  1443. // group.
  1444. //
  1445. DeviceInfo->State = DeviceState;
  1446. //
  1447. // One more deviceInfo entry.
  1448. //
  1449. DsmContext->NumberDevices++;
  1450. //
  1451. // Finally, add it to the global list of devices.
  1452. //
  1453. InsertTailList(&DsmContext->DeviceList,
  1454. &DeviceInfo->ListEntry);
  1455. KeReleaseSpinLock(&DsmContext->SpinLock, irql);
  1456. return STATUS_SUCCESS;
  1457. }
  1458. VOID
  1459. RemoveDeviceEntry(
  1460. IN PDSM_CONTEXT DsmContext,
  1461. IN PGROUP_ENTRY Group,
  1462. IN PDEVICE_INFO DeviceInfo
  1463. )
  1464. {
  1465. KIRQL irql;
  1466. NTSTATUS status;
  1467. ULONG i;
  1468. ULONG j;
  1469. BOOLEAN freeGroup = FALSE;
  1470. KeAcquireSpinLock(&DsmContext->SpinLock, &irql);
  1471. //
  1472. // Find it's offset in the array of devices.
  1473. //
  1474. for (i = 0; i < Group->NumberDevices; i++) {
  1475. if (Group->DeviceList[i] == DeviceInfo) {
  1476. //
  1477. // Zero out it's entry.
  1478. //
  1479. Group->DeviceList[i] = NULL;
  1480. //
  1481. // Reduce the number in the group.
  1482. //
  1483. Group->NumberDevices--;
  1484. //
  1485. // Collapse the array.
  1486. //
  1487. // BUGBUG: If any requests come in during this time, it's
  1488. // possible to either bugcheck or get an incorrect deviceInfo
  1489. // structure.
  1490. //
  1491. for (j = i; j < Group->NumberDevices; j++) {
  1492. //
  1493. // Shuffle all entries down to fill the hole.
  1494. //
  1495. Group->DeviceList[j] = Group->DeviceList[j + 1];
  1496. }
  1497. //
  1498. // Zero out the last one.
  1499. //
  1500. Group->DeviceList[j] = NULL;
  1501. break;
  1502. }
  1503. }
  1504. //
  1505. // See if anything is left in the Group.
  1506. //
  1507. if (Group->NumberDevices == 0) {
  1508. //
  1509. // Yank it from the Group list.
  1510. //
  1511. RemoveEntryList(&Group->ListEntry);
  1512. DsmContext->NumberGroups--;
  1513. //
  1514. // Zero it.
  1515. //
  1516. RtlZeroMemory(Group,
  1517. sizeof(GROUP_ENTRY));
  1518. freeGroup = TRUE;
  1519. }
  1520. //
  1521. // Yank the device out of the Global list.
  1522. //
  1523. RemoveEntryList(&DeviceInfo->ListEntry);
  1524. DsmContext->NumberDevices--;
  1525. //
  1526. // Zero it.
  1527. //
  1528. RtlZeroMemory(DeviceInfo,
  1529. sizeof(DEVICE_INFO));
  1530. KeReleaseSpinLock(&DsmContext->SpinLock, irql);
  1531. //
  1532. // Free the allocation.
  1533. //
  1534. ExFreePool(DeviceInfo);
  1535. if (freeGroup) {
  1536. //
  1537. // Free the allocation.
  1538. //
  1539. ExFreePool(Group);
  1540. }
  1541. }
  1542. PFAILOVER_GROUP
  1543. FindFOGroup(
  1544. IN PDSM_CONTEXT DsmContext,
  1545. IN PVOID PathId
  1546. )
  1547. {
  1548. PFAILOVER_GROUP failOverGroup;
  1549. PLIST_ENTRY entry;
  1550. ULONG i;
  1551. //
  1552. // Run through the list of Fail-Over Groups
  1553. //
  1554. entry = DsmContext->FailGroupList.Flink;
  1555. for (i = 0; i < DsmContext->NumberFOGroups; i++, entry = entry->Flink) {
  1556. //
  1557. // Extract the fail-over group structure.
  1558. //
  1559. failOverGroup = CONTAINING_RECORD(entry, FAILOVER_GROUP, ListEntry);
  1560. ASSERT(failOverGroup);
  1561. //
  1562. // Check for a match of the PathId.
  1563. //
  1564. if (failOverGroup->PathId == PathId) {
  1565. return failOverGroup;
  1566. }
  1567. }
  1568. return NULL;
  1569. }
  1570. PFAILOVER_GROUP
  1571. BuildFOGroup(
  1572. IN PDSM_CONTEXT DsmContext,
  1573. IN PDEVICE_INFO DeviceInfo,
  1574. IN PVOID PathId
  1575. )
  1576. {
  1577. PFAILOVER_GROUP failOverGroup;
  1578. KIRQL irql;
  1579. ULONG numberGroups;
  1580. //
  1581. // Allocate an entry.
  1582. //
  1583. failOverGroup = ExAllocatePool(NonPagedPool, sizeof(FAILOVER_GROUP));
  1584. if (failOverGroup == NULL) {
  1585. return NULL;
  1586. }
  1587. RtlZeroMemory(failOverGroup, sizeof(FAILOVER_GROUP));
  1588. KeAcquireSpinLock(&DsmContext->SpinLock, &irql);
  1589. //
  1590. // Get the current number of groups, and add the one that's
  1591. // being created.
  1592. //
  1593. numberGroups = DsmContext->NumberFOGroups++;
  1594. //
  1595. // Set the PathId - All devices on the same PathId will
  1596. // failover together.
  1597. //
  1598. failOverGroup->PathId = PathId;
  1599. //
  1600. // Set the initial state to NORMAL.
  1601. //
  1602. failOverGroup->State = FG_NORMAL;
  1603. //
  1604. // Add it to the global list.
  1605. //
  1606. InsertTailList(&DsmContext->FailGroupList,
  1607. &failOverGroup->ListEntry);
  1608. KeReleaseSpinLock(&DsmContext->SpinLock, irql);
  1609. return failOverGroup;
  1610. }
  1611. NTSTATUS
  1612. UpdateFOGroup(
  1613. IN PDSM_CONTEXT DsmContext,
  1614. IN PFAILOVER_GROUP FailGroup,
  1615. IN PDEVICE_INFO DeviceInfo
  1616. )
  1617. {
  1618. PGROUP_ENTRY group;
  1619. ULONG count;
  1620. KIRQL irql;
  1621. KeAcquireSpinLock(&DsmContext->SpinLock, &irql);
  1622. //
  1623. // Add the device to the list of devices that are on this path.
  1624. //
  1625. count = FailGroup->Count++;
  1626. FailGroup->DeviceList[count] = DeviceInfo;
  1627. //
  1628. // Get the MultiPath group for this device.
  1629. //
  1630. group = DeviceInfo->Group;
  1631. //
  1632. // Indicate that the L.B. policy needs to be updated.
  1633. // The next call to LBGetPath will cause the re-shuffle to
  1634. // take place.
  1635. //
  1636. group->LoadBalanceInit = FALSE;
  1637. DeviceInfo->NeedsVerification = TRUE;
  1638. //
  1639. // Set the device's F.O. Group.
  1640. //
  1641. DeviceInfo->FailGroup = FailGroup;
  1642. KeReleaseSpinLock(&DsmContext->SpinLock, irql);
  1643. return STATUS_SUCCESS;
  1644. }
  1645. VOID
  1646. RemoveDeviceFailGroup(
  1647. IN PDSM_CONTEXT DsmContext,
  1648. IN PFAILOVER_GROUP FailGroup,
  1649. IN PDEVICE_INFO DeviceInfo
  1650. )
  1651. {
  1652. ULONG count;
  1653. KIRQL irql;
  1654. ULONG i;
  1655. ULONG j;
  1656. KeAcquireSpinLock(&DsmContext->SpinLock, &irql);
  1657. //
  1658. // Find it's offset in the array of devices.
  1659. //
  1660. for (i = 0; i < FailGroup->Count; i++) {
  1661. if (FailGroup->DeviceList[i] == DeviceInfo) {
  1662. //
  1663. // Zero out it's entry.
  1664. //
  1665. FailGroup->DeviceList[i] = NULL;
  1666. //
  1667. // Reduce the number in the group.
  1668. //
  1669. FailGroup->Count--;
  1670. //
  1671. // Collapse the array.
  1672. //
  1673. for (j = i; j < FailGroup->Count; j++) {
  1674. //
  1675. // Shuffle all entries down to fill the hole.
  1676. //
  1677. FailGroup->DeviceList[j] = FailGroup->DeviceList[j + 1];
  1678. }
  1679. //
  1680. // Zero out the last one.
  1681. //
  1682. FailGroup->DeviceList[j] = NULL;
  1683. break;
  1684. }
  1685. }
  1686. KeReleaseSpinLock(&DsmContext->SpinLock, irql);
  1687. return;
  1688. }
  1689. PFAILOVER_GROUP
  1690. SetNewPath(
  1691. IN PDSM_CONTEXT DsmContext,
  1692. IN PGROUP_ENTRY Group,
  1693. IN PDEVICE_INFO FailingDevice,
  1694. IN PFAILOVER_GROUP SelectedPath
  1695. )
  1696. {
  1697. PFAILOVER_GROUP failGroup;
  1698. PGROUP_ENTRY group;
  1699. PDEVICE_INFO device;
  1700. ULONG i;
  1701. NTSTATUS status;
  1702. BOOLEAN matched = FALSE;
  1703. if (SelectedPath) {
  1704. //
  1705. // This indicates that a new path has already been selected
  1706. // for at least one device in the Fail-Over Group.
  1707. // Run the list of new devices and find the matching
  1708. // multi-path group.
  1709. //
  1710. for (i = 0; i < SelectedPath->Count; i++) {
  1711. //
  1712. // Get the device from the newly selected Path.
  1713. //
  1714. device = SelectedPath->DeviceList[i];
  1715. //
  1716. // Determine if the device's group matches the failing
  1717. // device's group.
  1718. //
  1719. if (device->Group == Group) {
  1720. //
  1721. // The new device should be either ACTIVE or PASSIVE
  1722. //
  1723. if ((device->State == DEV_ACTIVE) ||
  1724. (device->State == DEV_PASSIVE)) {
  1725. //
  1726. // Set it to ACTIVE.
  1727. //
  1728. device->State = DEV_ACTIVE;
  1729. //
  1730. // Ensure that it's ready.
  1731. //
  1732. status = DsmSendTUR(device->TargetObject);
  1733. ASSERT(status == STATUS_SUCCESS);
  1734. matched = TRUE;
  1735. break;
  1736. }
  1737. }
  1738. }
  1739. //
  1740. // When the first call was made and a path selected, all devices
  1741. // on the path were checked for validity.
  1742. //
  1743. ASSERT(matched == TRUE);
  1744. //
  1745. // Just return the SelectedPath
  1746. //
  1747. failGroup = SelectedPath;
  1748. } else {
  1749. //
  1750. // Go through Group, looking for an available device.
  1751. //
  1752. for (i = 0; i < Group->NumberDevices; i++) {
  1753. //
  1754. // Look for any that are Passive. They are the best
  1755. // choice. This would indicate either an ActiveN/PassiveN arrangement.
  1756. //
  1757. device = Group->DeviceList[i];
  1758. if (device->State == DEV_PASSIVE) {
  1759. matched = TRUE;
  1760. break;
  1761. }
  1762. }
  1763. if (matched) {
  1764. //
  1765. // Mark the device as active.
  1766. //
  1767. device->State = DEV_ACTIVE;
  1768. //
  1769. // Ensure that it's ready.
  1770. //
  1771. status = DsmSendTUR(device->TargetObject);
  1772. ASSERT(status == STATUS_SUCCESS);
  1773. //
  1774. // Get the Fail-Over group from the selected device.
  1775. //
  1776. failGroup = device->FailGroup;
  1777. } else {
  1778. //
  1779. // No passive devices. This indicates either an Active/Active arrangement,
  1780. // or everything is failed.
  1781. // Look for active devices.
  1782. //
  1783. for (i = 0; i < Group->NumberDevices; i++) {
  1784. device = Group->DeviceList[i];
  1785. if (device->State == DEV_ACTIVE) {
  1786. matched = TRUE;
  1787. break;
  1788. }
  1789. }
  1790. if (matched) {
  1791. //
  1792. // The device is already active, just return the
  1793. // new path info.
  1794. //
  1795. failGroup = device->FailGroup;
  1796. } else {
  1797. //
  1798. // Everything has failed. Should try to do something?? TODO
  1799. //
  1800. failGroup = NULL;
  1801. }
  1802. }
  1803. if (failGroup) {
  1804. //
  1805. // Run through all the devices to ensure that they are
  1806. // in a reasonable state.
  1807. //
  1808. for (i = 0; i < failGroup->Count; i++) {
  1809. device = failGroup->DeviceList[i];
  1810. if ((device->State != DEV_ACTIVE) &&
  1811. (device->State != DEV_PASSIVE)) {
  1812. //
  1813. // Really need to find a new fail-over group.
  1814. // TODO.
  1815. // This isn't necessarily a valid assert. If static lb is in
  1816. // effect and this is one of the first to fail-over, others
  1817. // could be considered bad.
  1818. //
  1819. ASSERT(device->State == DEV_ACTIVE);
  1820. }
  1821. }
  1822. }
  1823. }
  1824. return failGroup;
  1825. }
  1826. VOID
  1827. LBInit(
  1828. IN PDSM_CONTEXT DsmContext,
  1829. IN PGROUP_ENTRY Group
  1830. )
  1831. {
  1832. PFAILOVER_GROUP failGroup;
  1833. PDEVICE_INFO device;
  1834. PLIST_ENTRY entry;
  1835. ULONG numberPaths;
  1836. ULONG assignedPath;
  1837. ULONG i;
  1838. BOOLEAN found;
  1839. //
  1840. // TODO: Once the Wmi support is here, this will be configurable
  1841. // Need to add code to handle each of the different policies.
  1842. //
  1843. //
  1844. // Doing 'static' LB. Out of each Multi-Path Group, one
  1845. // device will be active and assigned to a particular path.
  1846. // The assignment is based on the group ordinal modulus the total
  1847. // number of paths.
  1848. //
  1849. numberPaths = DsmContext->NumberFOGroups;
  1850. assignedPath = Group->GroupNumber % numberPaths;
  1851. DebugPrint((0,
  1852. "DsmLBInit: NumberFOGs (%x), Group Number (%x), assignedPath (%x)\n",
  1853. DsmContext->NumberFOGroups,
  1854. Group->GroupNumber,
  1855. assignedPath));
  1856. //
  1857. // Get the Fail-Over Group with the correct path.
  1858. //
  1859. i = 0;
  1860. found = FALSE;
  1861. //
  1862. // Get the first entry.
  1863. //
  1864. entry = DsmContext->FailGroupList.Flink;
  1865. do {
  1866. //
  1867. // Extract the F.O. Group entry.
  1868. //
  1869. failGroup = CONTAINING_RECORD(entry, FAILOVER_GROUP, ListEntry);
  1870. ASSERT(failGroup);
  1871. DebugPrint((0,
  1872. "DsmLBInit: Trying %x. at (%x) of (%x)\n",
  1873. failGroup,
  1874. i,
  1875. assignedPath));
  1876. if (i == assignedPath) {
  1877. //
  1878. // This is the one.
  1879. //
  1880. found = TRUE;
  1881. } else {
  1882. //
  1883. // Advance to the next entry.
  1884. //
  1885. entry = entry->Flink;
  1886. i++;
  1887. }
  1888. //
  1889. // BUGBUG: Need to terminate this loop based on #of FG's.
  1890. //
  1891. } while (found == FALSE);
  1892. DebugPrint((0,
  1893. "DsmLBInit: Using FOG (%x)\n",
  1894. failGroup));
  1895. //
  1896. // It may occur that though there are multiple paths/groups, not
  1897. // all devices have been put into the DeviceList.
  1898. // If there is only 1, special case this. It will get fixed up
  1899. // when the second device arrives.
  1900. //
  1901. if (Group->NumberDevices == 1) {
  1902. //
  1903. // LOG. Indicates something "might" be wrong - definitely
  1904. // not multi-pathing this device, so could lead to disaster
  1905. //
  1906. //
  1907. // Grab device 0 and set it active.
  1908. //
  1909. device = Group->DeviceList[0];
  1910. device->State = DEV_ACTIVE;
  1911. //
  1912. // Go ahead state that this is init'ed. If/when another
  1913. // device shows up, we will re-do this.
  1914. //
  1915. Group->LoadBalanceInit = TRUE;
  1916. Group->LoadBalanceType = LB_STATIC;
  1917. DebugPrint((0,
  1918. "DsmLBInit: Only One Device (%x) currently in group. Setting it Active\n",
  1919. device));
  1920. return;
  1921. }
  1922. //
  1923. // Find the device with the same F.O. Group
  1924. // in the mulit-path group.
  1925. //
  1926. for (i = 0; i < Group->NumberDevices; i++) {
  1927. //
  1928. // Get the device info.
  1929. //
  1930. device = Group->DeviceList[i];
  1931. //
  1932. // See if there is a match.
  1933. //
  1934. if (device->FailGroup == failGroup) {
  1935. //
  1936. // Set the device to active.
  1937. //
  1938. device->State = DEV_ACTIVE;
  1939. DebugPrint((0,
  1940. "DsmLBInit: Marking (%x) as active device\n",
  1941. device));
  1942. //
  1943. // Done setting up this multi-path group.
  1944. // Indicate that it's so, and that we are using
  1945. // STATIC Load-Balancing.
  1946. //
  1947. Group->LoadBalanceInit = TRUE;
  1948. Group->LoadBalanceType = LB_STATIC;
  1949. return;
  1950. } else {
  1951. DebugPrint((0,
  1952. "DsmLBInit: Marking (%x) as stand-by device\n",
  1953. device));
  1954. device->State = DEV_PASSIVE;
  1955. }
  1956. }
  1957. }
  1958. VOID
  1959. DsmDebugPrint(
  1960. ULONG DebugPrintLevel,
  1961. PCCHAR DebugMessage,
  1962. ...
  1963. )
  1964. /*++
  1965. Routine Description:
  1966. Debug print for the DSM
  1967. Arguments:
  1968. Return Value:
  1969. None
  1970. --*/
  1971. {
  1972. va_list ap;
  1973. va_start(ap, DebugMessage);
  1974. if (DebugPrintLevel <= HPDSMDebug) {
  1975. _vsnprintf(DebugBuffer, DEBUG_BUFFER_LENGTH, DebugMessage, ap);
  1976. DbgPrint(DebugBuffer);
  1977. }
  1978. va_end(ap);
  1979. }