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.

2807 lines
67 KiB

  1. /*++
  2. Copyright (C) 2000 Microsoft Corporation
  3. Module Name:
  4. dsm.c
  5. Abstract:
  6. This driver is the generic DSM for FC disks and exports behaviours that
  7. mpctl.sys will use to determine how to multipath these devices.
  8. Author:
  9. Environment:
  10. kernel mode only
  11. Notes:
  12. Revision History:
  13. --*/
  14. #include <ntddk.h>
  15. #include <stdio.h>
  16. #include <stdarg.h>
  17. #include "dsm.h"
  18. #include "gendsm.h"
  19. #include "wmi.h"
  20. #define USE_BINARY_MOF_QUERY
  21. //
  22. // MOF data can be reported by a device driver via a resource attached to
  23. // the device drivers image file or in response to a query on the binary
  24. // mof data guid. As the mpio pdo handles these requests partially for the DSM
  25. // it is easier to handle via a Query-response.
  26. //
  27. #ifdef ALLOC_DATA_PRAGMA
  28. #pragma data_seg("PAGED")
  29. #endif
  30. UCHAR DsmBinaryMofData[] =
  31. {
  32. #include "dsm.x"
  33. };
  34. #ifdef ALLOC_DATA_PRAGMA
  35. #pragma data_seg()
  36. #endif
  37. //
  38. // Define symbolic names for the guid indexes
  39. //
  40. #define GENDSM_CONFIGINFOGuidIndex 0
  41. #define BinaryMofGuidIndex 1
  42. //
  43. // List of guids supported
  44. //
  45. GUID GENDSM_CONFIGINFOGUID = GENDSM_CONFIGINFOGuid;
  46. GUID DsmBinaryMofGUID = BINARY_MOF_GUID;
  47. WMIGUIDREGINFO DsmGuidList[] =
  48. {
  49. {
  50. &GENDSM_CONFIGINFOGUID,
  51. 1,
  52. 0
  53. },
  54. {
  55. &DsmBinaryMofGUID,
  56. 1,
  57. 0
  58. }
  59. };
  60. #define DsmGuidCount (sizeof(DsmGuidList) / sizeof(WMIGUIDREGINFO))
  61. NTSTATUS
  62. DsmGetDeviceList(
  63. IN PDSM_CONTEXT Context
  64. );
  65. NTSTATUS
  66. DriverEntry(
  67. IN PDRIVER_OBJECT DriverObject,
  68. IN PUNICODE_STRING RegistryPath
  69. )
  70. /*++
  71. Routine Description:
  72. This routine is called when the driver loads loads.
  73. Arguments:
  74. DriverObject - Supplies the driver object.
  75. RegistryPath - Supplies the registry path.
  76. Return Value:
  77. NTSTATUS
  78. --*/
  79. {
  80. DSM_INIT_DATA initData;
  81. WCHAR dosDeviceName[40];
  82. UNICODE_STRING mpUnicodeName;
  83. PDEVICE_OBJECT deviceObject;
  84. PFILE_OBJECT fileObject;
  85. NTSTATUS status;
  86. PDSM_CONTEXT dsmContext;
  87. PDSM_MPIO_CONTEXT mpctlContext;
  88. PVOID buffer;
  89. //
  90. // Build the init data structure.
  91. //
  92. dsmContext = ExAllocatePool(NonPagedPool, sizeof(DSM_CONTEXT));
  93. if (dsmContext == NULL) {
  94. return STATUS_INSUFFICIENT_RESOURCES;
  95. }
  96. RtlZeroMemory(dsmContext, sizeof(DSM_CONTEXT));
  97. buffer = &initData;
  98. //
  99. // Set-up the init data
  100. //
  101. initData.DsmContext = (PVOID)dsmContext;
  102. initData.InitDataSize = sizeof(DSM_INIT_DATA);
  103. initData.DsmInquireDriver = DsmInquire;
  104. initData.DsmCompareDevices = DsmCompareDevices;
  105. initData.DsmSetDeviceInfo = DsmSetDeviceInfo;
  106. initData.DsmGetControllerInfo = DsmGetControllerInfo;
  107. initData.DsmIsPathActive = DsmIsPathActive;
  108. initData.DsmPathVerify = DsmPathVerify;
  109. initData.DsmInvalidatePath = DsmInvalidatePath;
  110. initData.DsmMoveDevice = DsmMoveDevice;
  111. initData.DsmRemovePending = DsmRemovePending;
  112. initData.DsmRemoveDevice = DsmRemoveDevice;
  113. initData.DsmRemovePath = DsmRemovePath;
  114. initData.DsmReenablePath = DsmBringPathOnLine;
  115. initData.DsmCategorizeRequest = DsmCategorizeRequest;
  116. initData.DsmBroadcastSrb = DsmBroadcastRequest;
  117. initData.DsmSrbDeviceControl = DsmSrbDeviceControl;
  118. initData.DsmSetCompletion = DsmSetCompletion;
  119. initData.DsmLBGetPath = DsmLBGetPath;
  120. initData.DsmInterpretError = DsmInterpretError;
  121. initData.DsmUnload = DsmUnload;
  122. //
  123. // Set-up the WMI Info.
  124. //
  125. DsmWmiInitialize(&initData.DsmWmiInfo);
  126. //
  127. // Set the DriverObject. Used by MPIO for Unloading.
  128. //
  129. initData.DriverObject = DriverObject;
  130. RtlInitUnicodeString(&initData.DisplayName, L"Generic Device-Specific Module");
  131. //
  132. // Initialize the context objects.
  133. //
  134. KeInitializeSpinLock(&dsmContext->SpinLock);
  135. InitializeListHead(&dsmContext->GroupList);
  136. InitializeListHead(&dsmContext->DeviceList);
  137. InitializeListHead(&dsmContext->FailGroupList);
  138. ExInitializeNPagedLookasideList(&dsmContext->ContextList,
  139. NULL,
  140. NULL,
  141. 0,
  142. sizeof(COMPLETION_CONTEXT),
  143. 'MSDG',
  144. 0);
  145. //
  146. // Build the mpctl name.
  147. //
  148. swprintf(dosDeviceName, L"\\DosDevices\\MPathControl");
  149. RtlInitUnicodeString(&mpUnicodeName, dosDeviceName);
  150. //
  151. // Get mpctl's deviceObject.
  152. //
  153. status = IoGetDeviceObjectPointer(&mpUnicodeName,
  154. FILE_READ_ATTRIBUTES,
  155. &fileObject,
  156. &deviceObject);
  157. if (NT_SUCCESS(status)) {
  158. KEVENT event;
  159. PIRP irp;
  160. IO_STATUS_BLOCK ioStatus;
  161. //
  162. // Send the IOCTL to mpctl.sys to register ourselves.
  163. //
  164. DsmSendDeviceIoControlSynchronous(IOCTL_MPDSM_REGISTER,
  165. deviceObject,
  166. &initData,
  167. &initData,
  168. sizeof(DSM_INIT_DATA),
  169. sizeof(DSM_MPIO_CONTEXT),
  170. TRUE,
  171. &ioStatus);
  172. status = ioStatus.Status;
  173. ObDereferenceObject(fileObject);
  174. }
  175. if (status == STATUS_SUCCESS) {
  176. //
  177. // Grab the context value passed back by mpctl.
  178. //
  179. mpctlContext = buffer;
  180. dsmContext->MPIOContext = mpctlContext->MPIOContext;
  181. //
  182. // Query the registry to find out what devices are being supported
  183. // on this machine.
  184. //
  185. DsmGetDeviceList(dsmContext);
  186. } else {
  187. //
  188. // Need to LOG this.
  189. //
  190. }
  191. return status;
  192. }
  193. PUCHAR
  194. DsmGetSerialNumber(
  195. IN PDEVICE_OBJECT DeviceObject
  196. )
  197. {
  198. IO_STATUS_BLOCK ioStatus;
  199. PSCSI_PASS_THROUGH passThrough;
  200. PVPD_SERIAL_NUMBER_PAGE serialPage;
  201. ULONG length;
  202. PCDB cdb;
  203. PUCHAR serialNumber;
  204. ULONG serialNumberOffset;
  205. //
  206. // Build an inquiry command with EVPD and pagecode of 0x80 (serial number).
  207. //
  208. length = sizeof(SCSI_PASS_THROUGH) + SENSE_BUFFER_SIZE + 0xFF;
  209. passThrough = ExAllocatePool(NonPagedPool, length);
  210. if (passThrough == NULL) {
  211. return NULL;
  212. }
  213. RtlZeroMemory(passThrough, length);
  214. //
  215. // build the cdb.
  216. //
  217. cdb = (PCDB)passThrough->Cdb;
  218. cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY;
  219. cdb->CDB6INQUIRY3.EnableVitalProductData = 1;
  220. cdb->CDB6INQUIRY3.PageCode = VPD_SERIAL_NUMBER;
  221. cdb->CDB6INQUIRY3.AllocationLength = 255;
  222. passThrough->Length = sizeof(SCSI_PASS_THROUGH);
  223. passThrough->CdbLength = 6;
  224. passThrough->SenseInfoLength = SENSE_BUFFER_SIZE;
  225. passThrough->DataIn = 1;
  226. passThrough->DataTransferLength = 0xFF;
  227. passThrough->TimeOutValue = 20;
  228. passThrough->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH);
  229. passThrough->DataBufferOffset = sizeof(SCSI_PASS_THROUGH) + 18;
  230. DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH,
  231. DeviceObject,
  232. passThrough,
  233. passThrough,
  234. length,
  235. length,
  236. FALSE,
  237. &ioStatus);
  238. if ((passThrough->ScsiStatus) || (ioStatus.Status != STATUS_SUCCESS)) {
  239. DebugPrint((0,
  240. "DsmGetSerialNumber: Status (%x) ScsiStatus (%x)\n",
  241. ioStatus.Status,
  242. passThrough->ScsiStatus));
  243. ExFreePool(passThrough);
  244. return NULL;
  245. } else {
  246. ULONG i;
  247. DebugPrint((0,
  248. "GetDeviceDescriptor: Got the serial number page\n"));
  249. //
  250. // Get the returned data.
  251. //
  252. (ULONG_PTR)serialPage = (ULONG_PTR)passThrough;
  253. (ULONG_PTR)serialPage += passThrough->DataBufferOffset;
  254. //
  255. // Allocate a buffer to hold just the serial number.
  256. //
  257. serialNumber = ExAllocatePool(NonPagedPool, serialPage->PageLength + 1);
  258. RtlZeroMemory(serialNumber, serialPage->PageLength + 1);
  259. //
  260. // Copy it over.
  261. //
  262. RtlCopyMemory(serialNumber,
  263. serialPage->SerialNumber,
  264. serialPage->PageLength);
  265. //
  266. // Convert 0x00 to spaces.
  267. //
  268. for (i = 0; i < serialPage->PageLength; i++) {
  269. if (serialNumber[i] == '\0') {
  270. serialNumber[i] = ' ';
  271. }
  272. }
  273. //
  274. // Free the passthrough + data buffer.
  275. //
  276. ExFreePool(passThrough);
  277. //
  278. // Return the sn.
  279. //
  280. return serialNumber;
  281. }
  282. }
  283. NTSTATUS
  284. DsmQueryCallBack(
  285. IN PWSTR ValueName,
  286. IN ULONG Type,
  287. IN PVOID Data,
  288. IN ULONG Length,
  289. IN PVOID Context,
  290. IN PVOID EntryContext
  291. )
  292. {
  293. PVOID *value = EntryContext;
  294. if (Type == REG_MULTI_SZ) {
  295. *value = ExAllocatePool(PagedPool, Length);
  296. if (*value) {
  297. RtlMoveMemory(*value, Data, Length);
  298. return STATUS_SUCCESS;
  299. }
  300. }
  301. return STATUS_UNSUCCESSFUL;
  302. }
  303. NTSTATUS
  304. DsmGetDeviceList(
  305. IN PDSM_CONTEXT Context
  306. )
  307. {
  308. RTL_QUERY_REGISTRY_TABLE queryTable[2];
  309. WCHAR registryKeyName[56];
  310. UNICODE_STRING inquiryStrings;
  311. WCHAR defaultIDs[] = { L"\0" };
  312. NTSTATUS status;
  313. RtlZeroMemory(registryKeyName, 56);
  314. RtlZeroMemory(&queryTable, sizeof(queryTable));
  315. RtlInitUnicodeString(&inquiryStrings, NULL);
  316. swprintf(registryKeyName, L"gendsm\\parameters");
  317. //
  318. // The query table has two entries. One for the supporteddeviceList and
  319. // the second which is the 'NULL' terminator.
  320. //
  321. queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND;
  322. queryTable[0].Name = L"SupportedDeviceList";
  323. queryTable[0].EntryContext = &Context->SupportedDevices;
  324. queryTable[0].DefaultType = REG_MULTI_SZ;
  325. queryTable[0].DefaultData = defaultIDs;
  326. queryTable[0].DefaultLength = sizeof(defaultIDs);
  327. status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
  328. registryKeyName,
  329. queryTable,
  330. NULL,
  331. NULL);
  332. return status;
  333. }
  334. BOOLEAN
  335. DsmFindSupportedDevice(
  336. IN PUNICODE_STRING DeviceName,
  337. IN PUNICODE_STRING SupportedDevices
  338. )
  339. {
  340. PWSTR devices = SupportedDevices->Buffer;
  341. UNICODE_STRING unicodeString;
  342. LONG compare;
  343. while (devices[0]) {
  344. //
  345. // Make the current entry into a unicode string.
  346. //
  347. RtlInitUnicodeString(&unicodeString, devices);
  348. //
  349. // Compare this one with the current device.
  350. //
  351. compare = RtlCompareUnicodeString(&unicodeString, DeviceName, TRUE);
  352. if (compare == 0) {
  353. return TRUE;
  354. }
  355. //
  356. // Advance to next entry in the MULTI_SZ.
  357. //
  358. devices += (unicodeString.MaximumLength / sizeof(WCHAR));
  359. }
  360. return FALSE;
  361. }
  362. BOOLEAN
  363. DsmDeviceSupported(
  364. IN PDSM_CONTEXT Context,
  365. IN PUCHAR VendorId,
  366. IN PUCHAR ProductId
  367. )
  368. {
  369. UNICODE_STRING deviceName;
  370. UNICODE_STRING productName;
  371. ANSI_STRING ansiVendor;
  372. ANSI_STRING ansiProduct;
  373. NTSTATUS status;
  374. BOOLEAN supported = FALSE;
  375. if (Context->SupportedDevices.MaximumLength == 0) {
  376. return FALSE;
  377. }
  378. //
  379. // Convert the inquiry fields into ansi strings.
  380. //
  381. RtlInitAnsiString(&ansiVendor, VendorId);
  382. RtlInitAnsiString(&ansiProduct, ProductId);
  383. //
  384. // Allocate the deviceName buffer. Needs to be 8+16 plus NULL.
  385. // (productId length + vendorId length + NULL).
  386. //
  387. deviceName.MaximumLength = 25 * sizeof(WCHAR);
  388. deviceName.Buffer = ExAllocatePool(PagedPool, deviceName.MaximumLength);
  389. //
  390. // Convert the vendorId to unicode.
  391. //
  392. RtlAnsiStringToUnicodeString(&deviceName, &ansiVendor, FALSE);
  393. //
  394. // Convert the productId to unicode.
  395. //
  396. RtlAnsiStringToUnicodeString(&productName, &ansiProduct, TRUE);
  397. //
  398. // 'cat' them.
  399. //
  400. status = RtlAppendUnicodeStringToString(&deviceName, &productName);
  401. if (status == STATUS_SUCCESS) {
  402. //
  403. // Run the list of supported devices that was captured from the registry
  404. // and see if this one is in the list.
  405. //
  406. supported = DsmFindSupportedDevice(&deviceName,
  407. &Context->SupportedDevices);
  408. }
  409. return supported;
  410. }
  411. NTSTATUS
  412. DsmInquire (
  413. IN PVOID DsmContext,
  414. IN PDEVICE_OBJECT TargetDevice,
  415. IN PDEVICE_OBJECT PortObject,
  416. IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor,
  417. IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceIdList,
  418. OUT PVOID *DsmIdentifier
  419. )
  420. {
  421. PDSM_CONTEXT dsmContext = DsmContext;
  422. PDEVICE_INFO deviceInfo;
  423. PGROUP_ENTRY group;
  424. NTSTATUS status;
  425. ULONG deviceState;
  426. ULONG allocationLength;
  427. PDSM_IDS controllerObjects;
  428. PUCHAR vendorId = "SEAGATE ";
  429. PUCHAR productId = "ST39102FC";
  430. PUCHAR vendorIndex;
  431. PUCHAR productIndex;
  432. PUCHAR serialNumber;
  433. BOOLEAN supported;
  434. vendorIndex = (PUCHAR)Descriptor;
  435. productIndex = (PUCHAR)Descriptor;
  436. (ULONG_PTR)vendorIndex += Descriptor->VendorIdOffset;
  437. (ULONG_PTR)productIndex += Descriptor->ProductIdOffset;
  438. supported = DsmDeviceSupported((PDSM_CONTEXT)DsmContext,
  439. vendorIndex,
  440. productIndex);
  441. if (supported == FALSE) {
  442. return STATUS_NOT_SUPPORTED;
  443. }
  444. #if 0
  445. //
  446. // Determine if the device is supported.
  447. //
  448. if ((!RtlEqualMemory(vendorId, vendorIndex, 8)) ||
  449. (!RtlEqualMemory(productId, productIndex, 9))) {
  450. return STATUS_NOT_SUPPORTED;
  451. }
  452. #endif
  453. //
  454. // Ensure that the device's serial number is present. If not, can't claim
  455. // support for this drive.
  456. //
  457. if ((Descriptor->SerialNumberOffset == (ULONG)-1) ||
  458. (Descriptor->SerialNumberOffset == 0)) {
  459. PUCHAR index;
  460. //
  461. // The port driver currently doesn't get the VPD page 0x80, if the
  462. // device doesn't support GET_SUPPORTED_PAGES. Check to see whether
  463. // there actually is a serial number.
  464. //
  465. serialNumber = DsmGetSerialNumber(TargetDevice);
  466. if (serialNumber == NULL) {
  467. return STATUS_NOT_SUPPORTED;
  468. }
  469. DebugPrint((0,"SerialNumber: "));
  470. index = serialNumber;
  471. while (*index) {
  472. DebugPrint((0,"%02x", *index));
  473. index++;
  474. }
  475. DebugPrint((0,"\n"));
  476. } else {
  477. //
  478. // Get a pointer to the embedded serial number info.
  479. //
  480. serialNumber = (PUCHAR)Descriptor;
  481. (ULONG_PTR)serialNumber += Descriptor->SerialNumberOffset;
  482. }
  483. //
  484. // Allocate the descriptor. This is also used as DsmId.
  485. //
  486. allocationLength = sizeof(DEVICE_INFO);
  487. allocationLength += Descriptor->Size - sizeof(STORAGE_DEVICE_DESCRIPTOR);
  488. deviceInfo = ExAllocatePool(NonPagedPool, allocationLength);
  489. if (deviceInfo == NULL) {
  490. return STATUS_INSUFFICIENT_RESOURCES;
  491. }
  492. RtlZeroMemory(deviceInfo, allocationLength);
  493. //
  494. // Copy over the StorageDescriptor.
  495. //
  496. RtlCopyMemory(&deviceInfo->Descriptor,
  497. Descriptor,
  498. Descriptor->Size);
  499. //
  500. // As an example - if the storage enclosure contains controller-type devices (or others)
  501. // they can be found via this routine.
  502. //
  503. controllerObjects = DsmGetAssociatedDevice(dsmContext->MPIOContext,
  504. PortObject,
  505. 0x0C);
  506. if (controllerObjects) {
  507. //
  508. // Currently not used by this driver, so just free the memory.
  509. //
  510. ExFreePool(controllerObjects);
  511. }
  512. //
  513. // Set the serial number.
  514. //
  515. deviceInfo->SerialNumber = serialNumber;
  516. //
  517. // Save the PortPdo Object.
  518. //
  519. deviceInfo->PortPdo = TargetDevice;
  520. //
  521. // Set the signature.
  522. //
  523. deviceInfo->DeviceSig = DSM_DEVICE_SIG;
  524. //
  525. // See if there is an existing Muli-path group to which this belongs.
  526. // (same serial number).
  527. //
  528. group = DsmFindDevice(DsmContext,
  529. deviceInfo);
  530. if (group == NULL) {
  531. //
  532. // Build a multi-path group entry.
  533. //
  534. group = DsmBuildGroupEntry(DsmContext,
  535. deviceInfo);
  536. if (group == NULL) {
  537. ExFreePool(deviceInfo);
  538. return STATUS_INSUFFICIENT_RESOURCES;
  539. }
  540. //
  541. // This is the first in the group, so make it the active
  542. // device. The actual active/passive devices will be set-up
  543. // later when the first call to LBGetPath is made.
  544. //
  545. deviceState = DEV_ACTIVE;
  546. } else {
  547. //
  548. // Already something active, this will be the fail-over
  549. // device until the load-balance groups are set-up.
  550. //
  551. deviceState = DEV_PASSIVE;
  552. }
  553. //
  554. // Add it to the list.
  555. //
  556. status = DsmAddDeviceEntry(DsmContext,
  557. group,
  558. deviceInfo,
  559. deviceState);
  560. *DsmIdentifier = deviceInfo;
  561. return status;
  562. }
  563. BOOLEAN
  564. DsmCompareDevices(
  565. IN PVOID DsmContext,
  566. IN PVOID DsmId1,
  567. IN PVOID DsmId2
  568. )
  569. {
  570. PDEVICE_INFO deviceInfo = DsmId1;
  571. PDEVICE_INFO comparedDevice = DsmId2;
  572. ULONG length;
  573. PUCHAR serialNumber;
  574. PUCHAR comparedSerialNumber;
  575. //
  576. // Get the two serial numbers.
  577. // They were either embedded in the STORAGE_DEVICE_DESCRIPTOR or built
  578. // by directly issuing the VPD request.
  579. //
  580. serialNumber = deviceInfo->SerialNumber;
  581. comparedSerialNumber = comparedDevice->SerialNumber;
  582. //
  583. // Get the length of the base-device Serial Number.
  584. //
  585. length = strlen(serialNumber);
  586. //
  587. // If the lengths match, compare the contents.
  588. //
  589. if (length == strlen(comparedSerialNumber)) {
  590. if (RtlEqualMemory(serialNumber,
  591. comparedSerialNumber,
  592. length)) {
  593. return TRUE;
  594. }
  595. }
  596. return FALSE;
  597. }
  598. VOID
  599. DsmSetupAlternatePath(
  600. IN PDSM_CONTEXT DsmContext,
  601. IN PGROUP_ENTRY Group
  602. )
  603. {
  604. PFAILOVER_GROUP currentGroup;
  605. PFAILOVER_GROUP failGroup;
  606. PDEVICE_INFO deviceInfo;
  607. PDEVICE_INFO currentDevInfo;
  608. ULONG i;
  609. ULONG j;
  610. BOOLEAN pathSet = FALSE;
  611. //
  612. // Check for single-pathed groups.
  613. //
  614. if (Group->NumberDevices > 2) {
  615. return;
  616. }
  617. for (i = 0; i < Group->NumberDevices; i++) {
  618. //
  619. // Get the device.
  620. //
  621. deviceInfo = Group->DeviceList[i];
  622. //
  623. // Get it's FOG.
  624. //
  625. currentGroup = deviceInfo->FailGroup;
  626. if (currentGroup == NULL) {
  627. //
  628. // This deviceInfo isn't fully intitialised yet.
  629. //
  630. continue;
  631. }
  632. //
  633. // Run through all the devices again.
  634. //
  635. for (j = 0; j < Group->NumberDevices; j++) {
  636. currentDevInfo = Group->DeviceList[j];
  637. if (currentDevInfo == deviceInfo) {
  638. //
  639. // Find one that's a different path.
  640. //
  641. continue;
  642. }
  643. failGroup = currentDevInfo->FailGroup;
  644. if (failGroup) {
  645. if (failGroup->PathId) {
  646. DebugPrint((0,
  647. "SetAlternatePath: FOG (%x) using (%x) as Alt\n",
  648. currentGroup,
  649. failGroup));
  650. currentGroup->AlternatePath = failGroup->PathId;
  651. pathSet = TRUE;
  652. }
  653. }
  654. }
  655. if (pathSet == FALSE) {
  656. DebugPrint((0,
  657. "SetAlternatePath: No alternate set for (%x)\n",
  658. currentGroup));
  659. } else {
  660. pathSet = FALSE;
  661. }
  662. }
  663. }
  664. NTSTATUS
  665. DsmSetDeviceInfo(
  666. IN PVOID DsmContext,
  667. IN PDEVICE_OBJECT TargetObject,
  668. IN PVOID DsmId,
  669. IN OUT PVOID *PathId
  670. )
  671. {
  672. PDEVICE_INFO deviceInfo = DsmId;
  673. PGROUP_ENTRY group = deviceInfo->Group;
  674. PFAILOVER_GROUP failGroup;
  675. NTSTATUS status;
  676. //
  677. // TargetObject is the destination for any requests created by this driver.
  678. // Save this for future reference.
  679. //
  680. deviceInfo->TargetObject = TargetObject;
  681. //
  682. // PathId indicates the path on which this device resides. Meaning
  683. // that when a Fail-Over occurs all device's on the same path fail together.
  684. // Search for a matching F.O. Group
  685. //
  686. failGroup = DsmFindFOGroup(DsmContext,
  687. *PathId);
  688. //
  689. // if not found, create a new f.o. group
  690. //
  691. if (failGroup == NULL) {
  692. failGroup = DsmBuildFOGroup(DsmContext,
  693. DsmId,
  694. *PathId);
  695. if (failGroup == NULL) {
  696. return STATUS_INSUFFICIENT_RESOURCES;
  697. }
  698. }
  699. //
  700. // add this deviceInfo to the f.o. group.
  701. //
  702. status = DsmUpdateFOGroup(DsmContext,
  703. failGroup,
  704. deviceInfo);
  705. DsmSetupAlternatePath(DsmContext,
  706. group);
  707. return status;
  708. }
  709. NTSTATUS
  710. DsmGetControllerInfo(
  711. IN PVOID DsmContext,
  712. IN PVOID DsmId,
  713. IN ULONG Flags,
  714. IN OUT PCONTROLLER_INFO *ControllerInfo
  715. )
  716. {
  717. PDSM_CONTEXT dsmContext = DsmContext;
  718. PCONTROLLER_INFO controllerInfo;
  719. LARGE_INTEGER time;
  720. if (!dsmContext->ControllerId) {
  721. //
  722. // Make one up.
  723. //
  724. KeQuerySystemTime(&time);
  725. //
  726. // Use only the bottom 32-bits.
  727. //
  728. dsmContext->ControllerId = time.LowPart;
  729. }
  730. if (Flags & DSM_CNTRL_FLAGS_ALLOCATE) {
  731. controllerInfo = ExAllocatePool(NonPagedPool, sizeof(CONTROLLER_INFO));
  732. if (controllerInfo == NULL) {
  733. return STATUS_INSUFFICIENT_RESOURCES;
  734. }
  735. RtlZeroMemory(controllerInfo, sizeof(CONTROLLER_INFO));
  736. //
  737. // Indicate that there are no specific controllers.
  738. //
  739. controllerInfo->State = DSM_CONTROLLER_NO_CNTRL;
  740. //
  741. // Set the identifier to the value generated earlier.
  742. //
  743. controllerInfo->ControllerIdentifier = (ULONGLONG)dsmContext->ControllerId;
  744. *ControllerInfo = controllerInfo;
  745. } else {
  746. controllerInfo = *ControllerInfo;
  747. //
  748. // If the enclosures supported by this DSM actually had controllers, there would
  749. // be a list of them and a search based on ControllerIdentifier would be made.
  750. //
  751. controllerInfo->State = DSM_CONTROLLER_NO_CNTRL;
  752. }
  753. return STATUS_SUCCESS;
  754. }
  755. BOOLEAN
  756. DsmIsPathActive(
  757. IN PVOID DsmContext,
  758. IN PVOID PathId
  759. )
  760. {
  761. PFAILOVER_GROUP group;
  762. //
  763. // NOTE: Internal callers of this assume certain behaviours. If it's changed,
  764. // those functions need to be updated appropriately.
  765. //
  766. //
  767. // Get the F.O. Group information.
  768. //
  769. group = DsmFindFOGroup(DsmContext,
  770. PathId);
  771. //
  772. // If there are any devices on this path, and
  773. // it's not in a failed state: it's capable of handling requests
  774. // so it's active.
  775. //
  776. if ((group->Count >= 1) && (group->State == FG_NORMAL)) {
  777. return TRUE;
  778. }
  779. return FALSE;
  780. }
  781. NTSTATUS
  782. DsmPathVerify(
  783. IN PVOID DsmContext,
  784. IN PVOID DsmId,
  785. IN PVOID PathId
  786. )
  787. {
  788. PDEVICE_INFO deviceInfo = DsmId;
  789. PFAILOVER_GROUP group;
  790. NTSTATUS status;
  791. ULONG i;
  792. //
  793. // Get the F.O. group
  794. //
  795. group = DsmFindFOGroup(DsmContext,
  796. PathId);
  797. if (group == NULL) {
  798. DbgBreakPoint();
  799. return STATUS_DEVICE_NOT_CONNECTED;
  800. }
  801. //
  802. // Check the Path state to ensure all is normal.
  803. // Should be in FAILBACK state. This indicates that either
  804. // an admin utility told us we are O.K. or the AutoRecovery detected
  805. // the error was transitory.
  806. // BUGBUG: Need to implement both of the above assumptions.
  807. //
  808. if ((group->Count >= 1) && group->State == FG_FAILBACK) {
  809. //
  810. // Ensure that the device is still there
  811. //
  812. for (i = 0; i < group->Count; i++) {
  813. if (group->DeviceList[i] == deviceInfo) {
  814. //
  815. // Send it a TUR.
  816. //
  817. status = DsmSendTUR(deviceInfo->TargetObject);
  818. }
  819. }
  820. } else {
  821. status = STATUS_UNSUCCESSFUL;
  822. //
  823. // Find the device.
  824. //
  825. for (i = 0; i < group->Count; i++) {
  826. if (group->DeviceList[i] == deviceInfo) {
  827. //
  828. // Issue a TUR to see if it's OK.
  829. //
  830. status = DsmSendTUR(deviceInfo->TargetObject);
  831. }
  832. }
  833. #if DBG
  834. if (status == STATUS_SUCCESS) {
  835. DebugPrint((2,
  836. "DsmPathVerify: Successful TUR to (%x)\n",
  837. deviceInfo));
  838. } else {
  839. //
  840. // Either the device is not in the group, or the TUR was not successful.
  841. //
  842. if (i == group->Count) {
  843. DebugPrint((0,
  844. "PathVerify: (%x) not in group (%x)\n",
  845. deviceInfo,
  846. group));
  847. } else {
  848. DebugPrint((0,
  849. "PathVerify: TUR to (%x) failed. (%x)\n",
  850. deviceInfo,
  851. status));
  852. }
  853. }
  854. #endif
  855. }
  856. //
  857. // Update the group State, depending upon the outcome.
  858. // TODO
  859. //
  860. if (status == STATUS_SUCCESS) {
  861. //
  862. // This lets the LBInit run to properly set-up this device.
  863. //
  864. deviceInfo->NeedsVerification = FALSE;
  865. }
  866. return status;
  867. }
  868. NTSTATUS
  869. DsmInvalidatePath(
  870. IN PVOID DsmContext,
  871. IN ULONG ErrorMask,
  872. IN PVOID PathId,
  873. IN OUT PVOID *NewPathId
  874. )
  875. {
  876. PFAILOVER_GROUP failGroup;
  877. PFAILOVER_GROUP hintPath;
  878. PGROUP_ENTRY group;
  879. PDEVICE_INFO deviceInfo;
  880. NTSTATUS status;
  881. ULONG i;
  882. ASSERT(ErrorMask & DSM_FATAL_ERROR);
  883. //
  884. // Get the fail-over group corresponding to the PathId.
  885. //
  886. failGroup = DsmFindFOGroup(DsmContext,
  887. PathId);
  888. //
  889. // Mark the path as failed.
  890. //
  891. failGroup->State = FG_FAILED;
  892. DebugPrint((0,
  893. "DsmInvalidatePath: Path (%x) FOG (%x) failing\n",
  894. PathId,
  895. failGroup));
  896. //
  897. // First interation, the hint will be NULL. This allows the
  898. // GetNewPath routine the opportunity to select the best new path
  899. // Subsequent calls will be fed the updated value.
  900. //
  901. hintPath = NULL;
  902. if (failGroup->Count == 0) {
  903. //
  904. // This indicates that all of the devices have already
  905. // been removed. Just return the alternate path.
  906. //
  907. *NewPathId = failGroup->AlternatePath;
  908. return STATUS_SUCCESS;
  909. }
  910. //
  911. // Process each device in the fail-over group
  912. //
  913. for (i = 0; i < failGroup->Count; i++) {
  914. //
  915. // Get the deviceInfo.
  916. //
  917. deviceInfo = failGroup->DeviceList[i];
  918. //
  919. // Set the state of the Failing Devicea
  920. //
  921. deviceInfo->State = DEV_FAILED;
  922. //
  923. // Get it's Multi-Path Group entry.
  924. //
  925. group = deviceInfo->Group;
  926. //
  927. // Get a new path for this failed device.
  928. //
  929. hintPath = DsmSetNewPath(DsmContext,
  930. group,
  931. deviceInfo,
  932. hintPath);
  933. }
  934. if (hintPath == NULL) {
  935. //
  936. // This indicates that no acceptable paths
  937. // were found. Return the error to mpctl.
  938. //
  939. status = STATUS_NO_SUCH_DEVICE;
  940. *NewPathId = NULL;
  941. } else {
  942. //
  943. // return the new path.
  944. //
  945. *NewPathId = hintPath->PathId;
  946. DebugPrint((0,
  947. "DsmInvalidatePath: Returning (%x) as newPath\n",
  948. hintPath->PathId));
  949. status = STATUS_SUCCESS;
  950. }
  951. return status;
  952. }
  953. NTSTATUS
  954. DsmMoveDevice(
  955. IN PVOID DsmContext,
  956. IN PDSM_IDS DsmIds,
  957. IN PVOID MPIOPath,
  958. IN PVOID SuggestedPath,
  959. IN ULONG Flags
  960. )
  961. {
  962. return STATUS_SUCCESS;
  963. }
  964. NTSTATUS
  965. DsmRemovePending(
  966. IN PVOID DsmContext,
  967. IN PVOID DsmId
  968. )
  969. {
  970. PDSM_CONTEXT dsmContext = DsmContext;
  971. PDEVICE_INFO deviceInfo = DsmId;
  972. KIRQL irql;
  973. DebugPrint((0,
  974. "RemovePending: Marking %x as PENDING_REMOVED\n",
  975. deviceInfo));
  976. KeAcquireSpinLock(&dsmContext->SpinLock, &irql);
  977. //
  978. // Mark the device as being unavailable, as a remove will be
  979. // coming shortly.
  980. //
  981. deviceInfo->State = DEV_PENDING_REMOVE;
  982. KeReleaseSpinLock(&dsmContext->SpinLock, irql);
  983. return STATUS_SUCCESS;
  984. }
  985. NTSTATUS
  986. DsmRemoveDevice(
  987. IN PVOID DsmContext,
  988. IN PVOID DsmId,
  989. IN PVOID PathId
  990. )
  991. {
  992. PDSM_CONTEXT dsmContext = DsmContext;
  993. PDEVICE_INFO deviceInfo;
  994. PFAILOVER_GROUP failGroup;
  995. PGROUP_ENTRY group;
  996. ULONG state;
  997. WCHAR buffer[64];
  998. DebugPrint((0,
  999. "DsmRemoveDevice: Removing %x\n",
  1000. DsmId));
  1001. //
  1002. // DsmId is our deviceInfo structure.
  1003. //
  1004. deviceInfo = DsmId;
  1005. //
  1006. // Get it's Multi-Path Group entry.
  1007. //
  1008. group = deviceInfo->Group;
  1009. //
  1010. // Get the Fail-over group.
  1011. //
  1012. failGroup = deviceInfo->FailGroup;
  1013. //
  1014. // If it's active, need to 'Fail-Over' to another device in
  1015. // the group.
  1016. //
  1017. state = deviceInfo->State;
  1018. //
  1019. // Set the state of the Failing Devicea
  1020. //
  1021. deviceInfo->State = DEV_FAILED;
  1022. if (state == DEV_ACTIVE) {
  1023. //
  1024. // Find the next available device.
  1025. // This is basically a fail-over for just
  1026. // this device.
  1027. //
  1028. DsmSetNewPath(DsmContext,
  1029. group,
  1030. deviceInfo,
  1031. NULL);
  1032. }
  1033. //
  1034. // Remove it's entry from the Fail-Over Group.
  1035. //
  1036. DsmRemoveDeviceFailGroup(DsmContext,
  1037. failGroup,
  1038. deviceInfo);
  1039. //
  1040. // Remove it from it's multi-path group. This has the side-effect
  1041. // of cleaning up the Group if the number of devices goes to zero.
  1042. //
  1043. DsmRemoveDeviceEntry(DsmContext,
  1044. group,
  1045. deviceInfo);
  1046. swprintf(buffer, L"Removing Device (%ws)", L"It's Name");
  1047. DsmWriteEvent(dsmContext->MPIOContext,
  1048. L"GenDsm",
  1049. buffer,
  1050. 2);
  1051. return STATUS_SUCCESS;
  1052. }
  1053. NTSTATUS
  1054. DsmRemovePath(
  1055. IN PDSM_CONTEXT DsmContext,
  1056. IN PVOID PathId
  1057. )
  1058. {
  1059. PFAILOVER_GROUP failGroup;
  1060. KIRQL irql;
  1061. failGroup = DsmFindFOGroup(DsmContext,
  1062. PathId);
  1063. if (failGroup == NULL) {
  1064. //
  1065. // It's already been removed.
  1066. // LOG though.
  1067. //
  1068. return STATUS_SUCCESS;
  1069. }
  1070. //
  1071. // The claim is that a path won't be removed, until all
  1072. // the devices on it are.
  1073. //
  1074. ASSERT(failGroup->Count == 0);
  1075. KeAcquireSpinLock(&DsmContext->SpinLock, &irql);
  1076. //
  1077. // Need to find any other FOG's using this as their alternate path and
  1078. // update them to use something else (if available).
  1079. // BUGBUG: that it's not done.
  1080. //
  1081. //
  1082. // Yank it from the list.
  1083. //
  1084. RemoveEntryList(&failGroup->ListEntry);
  1085. DsmContext->NumberFOGroups--;
  1086. //
  1087. // Zero the entry.
  1088. //
  1089. RtlZeroMemory(failGroup, sizeof(FAILOVER_GROUP));
  1090. KeReleaseSpinLock(&DsmContext->SpinLock, irql);
  1091. //
  1092. // Free the allocation.
  1093. //
  1094. ExFreePool(failGroup);
  1095. return STATUS_SUCCESS;
  1096. }
  1097. NTSTATUS
  1098. DsmBringPathOnLine(
  1099. IN PVOID DsmContext,
  1100. IN PVOID PathId,
  1101. OUT PULONG DSMError
  1102. )
  1103. {
  1104. PFAILOVER_GROUP failGroup;
  1105. //
  1106. // PathVerify has been called already, so if
  1107. // it came back successfully, then this is O.K.
  1108. //
  1109. failGroup = DsmFindFOGroup(DsmContext,
  1110. PathId);
  1111. if (failGroup == NULL) {
  1112. //
  1113. // LOG
  1114. //
  1115. *DSMError = 0;
  1116. return STATUS_DEVICE_NOT_CONNECTED;
  1117. }
  1118. //
  1119. // Should be in FG_PENDING
  1120. //
  1121. ASSERT(failGroup->State == FG_PENDING);
  1122. //
  1123. // Indicate that it's ready to go.
  1124. //
  1125. failGroup->State = FG_NORMAL;
  1126. return STATUS_SUCCESS;
  1127. }
  1128. PVOID
  1129. DsmLBGetPath(
  1130. IN PVOID DsmContext,
  1131. IN PSCSI_REQUEST_BLOCK Srb,
  1132. IN PDSM_IDS DsmList,
  1133. IN PVOID CurrentPath,
  1134. OUT NTSTATUS *Status
  1135. )
  1136. {
  1137. PDSM_CONTEXT dsmContext = DsmContext;
  1138. PDEVICE_INFO deviceInfo;
  1139. PGROUP_ENTRY group;
  1140. PFAILOVER_GROUP failGroup = NULL;
  1141. ULONG i;
  1142. KIRQL irql;
  1143. KeAcquireSpinLock(&dsmContext->SpinLock, &irql);
  1144. //
  1145. // Up-front checking to minimally validate
  1146. // the list of DsmId's being passed in.
  1147. //
  1148. ASSERT(DsmList->Count);
  1149. ASSERT(DsmList->IdList[0]);
  1150. //
  1151. // Grab the first device from the list.
  1152. //
  1153. deviceInfo = DsmList->IdList[0];
  1154. ASSERT(deviceInfo->DeviceSig == DSM_DEVICE_SIG);
  1155. //
  1156. // Get the multi-path group.
  1157. //
  1158. group = deviceInfo->Group;
  1159. //
  1160. // See if Load-Balancing has been initialized.
  1161. //
  1162. if (group->LoadBalanceInit == FALSE) {
  1163. PDEVICE_INFO lbDevice;
  1164. BOOLEAN doInit = TRUE;
  1165. //
  1166. // Check to see whether we are really ready to run
  1167. // the LBInit. If any of the list aren't verified, then
  1168. // we will hold off.
  1169. //
  1170. for (i = 0; i < DsmList->Count; i++) {
  1171. lbDevice = DsmList->IdList[i];
  1172. //
  1173. // Check to see whether pathVerify has been invoked
  1174. // on this device. Due to how PnP builds the device stacks
  1175. // there is a period of time between the PDO showing up, and
  1176. // when the FDO (mpdev.sys) gets loaded and registers.
  1177. //
  1178. if (lbDevice->NeedsVerification) {
  1179. DebugPrint((0,
  1180. "LBGetPath: (%x) needs verify\n",
  1181. lbDevice));
  1182. doInit = FALSE;
  1183. break;
  1184. }
  1185. }
  1186. if (doInit) {
  1187. //
  1188. // Set-up the load-balancing. This routine
  1189. // builds a static assignment of multi-path group to
  1190. // a particular path.
  1191. //
  1192. DsmLBInit(DsmContext,
  1193. group);
  1194. }
  1195. }
  1196. #if DBG
  1197. //
  1198. // Ensure that mpctl and this dsm are in sync.
  1199. //
  1200. if (DsmList->Count != group->NumberDevices) {
  1201. BOOLEAN doAssert = TRUE;
  1202. for (i = 0; i <group->NumberDevices; i++) {
  1203. deviceInfo = group->DeviceList[i];
  1204. if ((deviceInfo->State == DEV_PENDING_REMOVE) ||
  1205. (deviceInfo->State == DEV_FAILED)) {
  1206. //
  1207. // The reason the lists are off is that this one
  1208. // has been marked for removal. mpio has already
  1209. // adjusted it's structures to show it not being used.
  1210. //
  1211. doAssert = FALSE;
  1212. }
  1213. }
  1214. if (doAssert) {
  1215. ASSERT(DsmList->Count == group->NumberDevices);
  1216. }
  1217. }
  1218. #endif
  1219. //
  1220. // Find the active device.
  1221. //
  1222. //for (i = 0; i < group->NumberDevices; i++) {
  1223. for (i = 0; i < DsmList->Count; i++) {
  1224. //
  1225. // Get each of the DsmId's, in reality the deviceInfo.
  1226. //
  1227. deviceInfo = DsmList->IdList[i];
  1228. ASSERT(deviceInfo->DeviceSig == DSM_DEVICE_SIG);
  1229. //
  1230. // Ensure that the device is in our list.
  1231. //
  1232. ASSERT(DsmFindDevice(DsmContext, deviceInfo));
  1233. //
  1234. // NOTE: This assumes 'static' Load-Balancing. Once others
  1235. // are implemented, this section will have to be updated.
  1236. //
  1237. // Return the path on which the ACTIVE device resides.
  1238. //
  1239. if (deviceInfo->State == DEV_ACTIVE) {
  1240. //
  1241. // Get the F.O.Group, as it contains the
  1242. // correct PathId for this device.
  1243. //
  1244. failGroup = deviceInfo->FailGroup;
  1245. *Status = STATUS_SUCCESS;
  1246. KeReleaseSpinLock(&dsmContext->SpinLock, irql);
  1247. return failGroup->PathId;
  1248. }
  1249. }
  1250. KeReleaseSpinLock(&dsmContext->SpinLock, irql);
  1251. //
  1252. // Should never have gotten here.
  1253. //
  1254. DebugPrint((0,
  1255. "LBGetPath: Returning STATUS_DEVICE_NOT_CONNECTED\n"));
  1256. DbgBreakPoint();
  1257. ASSERT(failGroup);
  1258. *Status = STATUS_DEVICE_NOT_CONNECTED;
  1259. return NULL;
  1260. }
  1261. ULONG
  1262. DsmCategorizeRequest(
  1263. IN PVOID DsmContext,
  1264. IN PDSM_IDS DsmIds,
  1265. IN PIRP Irp,
  1266. IN PSCSI_REQUEST_BLOCK Srb,
  1267. IN PVOID CurrentPath,
  1268. OUT PVOID *PathId,
  1269. OUT NTSTATUS *Status
  1270. )
  1271. {
  1272. ULONG dsmStatus;
  1273. NTSTATUS status;
  1274. //
  1275. // Requests to broadcast
  1276. // Reset
  1277. // Reserve
  1278. // Release
  1279. //
  1280. // Requests to Handle
  1281. // None for now.
  1282. //
  1283. //
  1284. // For all other requests, punt it back to the bus-driver.
  1285. // Need to get a path for the request first, so call the Load-Balance
  1286. // function.
  1287. //
  1288. *PathId = DsmLBGetPath(DsmContext,
  1289. Srb,
  1290. DsmIds,
  1291. CurrentPath,
  1292. &status);
  1293. if (NT_SUCCESS(status)) {
  1294. //
  1295. // Indicate that the path is updated, and mpctl should handle the request.
  1296. //
  1297. dsmStatus = DSM_PATH_SET;
  1298. } else {
  1299. //
  1300. // Indicate the error back to mpctl.
  1301. //
  1302. dsmStatus = DSM_ERROR;
  1303. //
  1304. // Mark-up the Srb to show that a failure has occurred.
  1305. // This value is really only for this DSM to know what to do
  1306. // in the InterpretError routine - Fatal Error.
  1307. // It could be something more meaningful.
  1308. //
  1309. Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
  1310. }
  1311. //
  1312. // Pass back status info to mpctl.
  1313. //
  1314. *Status = status;
  1315. return dsmStatus;
  1316. }
  1317. NTSTATUS
  1318. DsmBroadcastRequest(
  1319. IN PVOID DsmContext,
  1320. IN PDSM_IDS DsmIds,
  1321. IN PIRP Irp,
  1322. IN PSCSI_REQUEST_BLOCK Srb,
  1323. IN PKEVENT Event
  1324. )
  1325. {
  1326. PDSM_CONTEXT dsmContext = DsmContext;
  1327. KIRQL irql;
  1328. KeAcquireSpinLock(&dsmContext->SpinLock, &irql);
  1329. //
  1330. // BUGBUG: Need to handle Reset, Reserve, and Release.
  1331. //
  1332. KeReleaseSpinLock(&dsmContext->SpinLock, irql);
  1333. return STATUS_INVALID_DEVICE_REQUEST;
  1334. }
  1335. NTSTATUS
  1336. DsmSrbDeviceControl(
  1337. IN PVOID DsmContext,
  1338. IN PDSM_IDS DsmIds,
  1339. IN PIRP Irp,
  1340. IN PSCSI_REQUEST_BLOCK Srb,
  1341. IN PKEVENT Event
  1342. )
  1343. {
  1344. PDSM_CONTEXT dsmContext = DsmContext;
  1345. KIRQL irql;
  1346. KeAcquireSpinLock(&dsmContext->SpinLock, &irql);
  1347. //
  1348. // BUGBUG: Need to handle ??
  1349. //
  1350. KeReleaseSpinLock(&dsmContext->SpinLock, irql);
  1351. return STATUS_INVALID_DEVICE_REQUEST;
  1352. }
  1353. VOID
  1354. DsmXCompletion(
  1355. IN PVOID DsmId,
  1356. IN PIRP Irp,
  1357. IN PSCSI_REQUEST_BLOCK Srb,
  1358. IN PVOID DsmContext
  1359. )
  1360. {
  1361. PCOMPLETION_CONTEXT completionContext = DsmContext;
  1362. PDEVICE_INFO deviceInfo;
  1363. PDSM_CONTEXT dsmContext;
  1364. UCHAR opCode;
  1365. //
  1366. // If it's read or write, save stats.
  1367. // Categorize set-up the Context to have path, target info.
  1368. // TODO
  1369. //
  1370. ASSERT(DsmContext);
  1371. dsmContext = completionContext->DsmContext;
  1372. deviceInfo = completionContext->DeviceInfo;
  1373. opCode = Srb->Cdb[0];
  1374. //
  1375. // Indicate one less request on this device.
  1376. //
  1377. InterlockedDecrement(&deviceInfo->Requests);
  1378. //
  1379. // TODO: Use the timestamp.
  1380. // Path/Device up-time, ave. time/request...
  1381. //
  1382. //
  1383. // If it's a read or a write, update the stats.
  1384. //
  1385. if (opCode == SCSIOP_READ) {
  1386. deviceInfo->Stats.NumberReads++;
  1387. deviceInfo->Stats.BytesRead.QuadPart += Srb->DataTransferLength;
  1388. } else if (opCode == SCSIOP_WRITE) {
  1389. deviceInfo->Stats.NumberWrites++;
  1390. deviceInfo->Stats.BytesWritten.QuadPart += Srb->DataTransferLength;
  1391. }
  1392. //
  1393. // Release the allocation.
  1394. //
  1395. ExFreeToNPagedLookasideList(&dsmContext->ContextList,
  1396. DsmContext);
  1397. }
  1398. VOID
  1399. DsmSetCompletion(
  1400. IN PVOID DsmContext,
  1401. IN PVOID DsmId,
  1402. IN PIRP Irp,
  1403. IN PSCSI_REQUEST_BLOCK Srb,
  1404. IN OUT PDSM_COMPLETION_INFO DsmCompletion
  1405. )
  1406. {
  1407. PCOMPLETION_CONTEXT completionContext;
  1408. PDSM_CONTEXT dsmContext = DsmContext;
  1409. PDEVICE_INFO deviceInfo = DsmId;
  1410. //
  1411. // Save the DeviceInfo as being the target for this request.
  1412. // Get a timestamp
  1413. // TODO Determine other data.
  1414. //
  1415. completionContext = ExAllocateFromNPagedLookasideList(&dsmContext->ContextList);
  1416. if (completionContext == NULL) {
  1417. //
  1418. // LOG
  1419. //
  1420. }
  1421. //
  1422. // Time stamp this.
  1423. //
  1424. KeQueryTickCount(&completionContext->TickCount);
  1425. //
  1426. // Indicate the target for this request.
  1427. //
  1428. completionContext->DeviceInfo = deviceInfo;
  1429. completionContext->DsmContext = DsmContext;
  1430. //
  1431. // Indicate one more request on this device.
  1432. // LB may use this.
  1433. //
  1434. InterlockedIncrement(&deviceInfo->Requests);
  1435. DsmCompletion->DsmCompletionRoutine = DsmXCompletion;
  1436. DsmCompletion->DsmContext = completionContext;
  1437. return;
  1438. }
  1439. ULONG
  1440. DsmInterpretError(
  1441. IN PVOID DsmContext,
  1442. IN PVOID DsmId,
  1443. IN PSCSI_REQUEST_BLOCK Srb,
  1444. IN OUT NTSTATUS *Status,
  1445. OUT PBOOLEAN Retry
  1446. )
  1447. {
  1448. ULONG errorMask = 0;
  1449. PSENSE_DATA senseData = Srb->SenseInfoBuffer;
  1450. BOOLEAN failover = FALSE;
  1451. BOOLEAN retry = FALSE;
  1452. BOOLEAN handled = FALSE;
  1453. //
  1454. // Check the NT Status first.
  1455. // Several are clearly failover conditions.
  1456. //
  1457. switch (*Status) {
  1458. case STATUS_DEVICE_NOT_CONNECTED:
  1459. case STATUS_DEVICE_DOES_NOT_EXIST:
  1460. case STATUS_NO_SUCH_DEVICE:
  1461. //
  1462. // The port pdo has either been removed or is
  1463. // very broken. A fail-over is necessary.
  1464. //
  1465. handled = TRUE;
  1466. failover = TRUE;
  1467. break;
  1468. case STATUS_IO_DEVICE_ERROR:
  1469. //
  1470. // See if it's a unit attention.
  1471. //
  1472. if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) {
  1473. if (senseData->SenseKey == SCSI_SENSE_UNIT_ATTENTION) {
  1474. retry = TRUE;
  1475. handled = TRUE;
  1476. }
  1477. }
  1478. break;
  1479. default:
  1480. break;
  1481. }
  1482. if (handled == FALSE) {
  1483. if (Srb) {
  1484. //
  1485. // The ntstatus didn't indicate a fail-over condition, but
  1486. // check various srb status for failover-class error.
  1487. //
  1488. switch (Srb->SrbStatus) {
  1489. case SRB_STATUS_SELECTION_TIMEOUT:
  1490. case SRB_STATUS_INVALID_LUN:
  1491. case SRB_STATUS_INVALID_TARGET_ID:
  1492. case SRB_STATUS_NO_DEVICE:
  1493. case SRB_STATUS_NO_HBA:
  1494. case SRB_STATUS_INVALID_PATH_ID:
  1495. //
  1496. // All of these are fatal.
  1497. //
  1498. failover = TRUE;
  1499. break;
  1500. default:
  1501. break;
  1502. }
  1503. }
  1504. }
  1505. if (failover) {
  1506. DebugPrint((0,
  1507. "InterpretError: Marking Fatal. Srb (%x). *Status (%x)\n",
  1508. Srb,
  1509. *Status));
  1510. errorMask = DSM_FATAL_ERROR;
  1511. }
  1512. //
  1513. // TODO: Gather a list of status that indicate a retry is necessary.
  1514. // Look at InterpretSenseInfo.
  1515. //
  1516. *Retry = retry;
  1517. return errorMask;
  1518. }
  1519. NTSTATUS
  1520. DsmUnload(
  1521. IN PVOID DsmContext
  1522. )
  1523. {
  1524. //
  1525. // It's the responsibility of the mpio bus driver to have already
  1526. // destroyed all devices and paths.
  1527. // As those functions free allocations for the objects, the only thing
  1528. // needed here is to free the DsmContext.
  1529. //
  1530. ExFreePool(DsmContext);
  1531. return STATUS_SUCCESS;
  1532. }
  1533. //
  1534. // Utility functions.
  1535. //
  1536. PGROUP_ENTRY
  1537. DsmFindDevice(
  1538. IN PDSM_CONTEXT DsmContext,
  1539. IN PDEVICE_INFO DeviceInfo
  1540. )
  1541. {
  1542. PDEVICE_INFO deviceInfo;
  1543. PLIST_ENTRY entry;
  1544. ULONG i;
  1545. //
  1546. // Run through the DeviceInfo List
  1547. //
  1548. entry = DsmContext->DeviceList.Flink;
  1549. for (i = 0; i < DsmContext->NumberDevices; i++, entry = entry->Flink) {
  1550. //
  1551. // Extract the deviceInfo structure.
  1552. //
  1553. deviceInfo = CONTAINING_RECORD(entry, DEVICE_INFO, ListEntry);
  1554. ASSERT(deviceInfo);
  1555. //
  1556. // Call the Serial Number compare routine.
  1557. //
  1558. if (DsmCompareDevices(DsmContext,
  1559. DeviceInfo,
  1560. deviceInfo)) {
  1561. return deviceInfo->Group;
  1562. }
  1563. }
  1564. DebugPrint((0,
  1565. "DsmFindDevice: DsmContext (%x), DeviceInfo (%x)\n",
  1566. DsmContext,
  1567. DeviceInfo));
  1568. return NULL;
  1569. }
  1570. PGROUP_ENTRY
  1571. DsmBuildGroupEntry(
  1572. IN PDSM_CONTEXT DsmContext,
  1573. IN PDEVICE_INFO DeviceInfo
  1574. )
  1575. {
  1576. PGROUP_ENTRY group;
  1577. //
  1578. // Allocate the memory for the multi-path group.
  1579. //
  1580. group = ExAllocatePool(NonPagedPool, sizeof(GROUP_ENTRY));
  1581. if (group == NULL) {
  1582. return NULL;
  1583. }
  1584. RtlZeroMemory(group, sizeof(GROUP_ENTRY));
  1585. //
  1586. // Add it to the list of multi-path groups.
  1587. //
  1588. ExInterlockedInsertTailList(&DsmContext->GroupList,
  1589. &group->ListEntry,
  1590. &DsmContext->SpinLock);
  1591. group->GroupNumber = InterlockedIncrement(&DsmContext->NumberGroups);
  1592. group->GroupSig = DSM_GROUP_SIG;
  1593. ASSERT(group->GroupNumber >= 1);
  1594. return group;
  1595. }
  1596. NTSTATUS
  1597. DsmAddDeviceEntry(
  1598. IN PDSM_CONTEXT DsmContext,
  1599. IN PGROUP_ENTRY Group,
  1600. IN PDEVICE_INFO DeviceInfo,
  1601. IN ULONG DeviceState
  1602. )
  1603. {
  1604. ULONG numberDevices;
  1605. ULONG i;
  1606. KIRQL irql;
  1607. //
  1608. // Ensure that this is a valid config - namely, it hasn't
  1609. // exceeded the number of paths supported.
  1610. //
  1611. numberDevices = Group->NumberDevices;
  1612. if (numberDevices >= MAX_PATHS) {
  1613. return STATUS_UNSUCCESSFUL;
  1614. }
  1615. KeAcquireSpinLock(&DsmContext->SpinLock, &irql);
  1616. #if DBG
  1617. //
  1618. // Ensure that this isn't a second copy of the same pdo.
  1619. //
  1620. for (i = 0; i < numberDevices; i++) {
  1621. if (Group->DeviceList[i]->PortPdo == DeviceInfo->PortPdo) {
  1622. DebugPrint((0,
  1623. "DsmAddDeviceEntry: Received same PDO twice\n"));
  1624. DbgBreakPoint();
  1625. }
  1626. }
  1627. #endif
  1628. //
  1629. // Indicate one device is present in
  1630. // this group.
  1631. //
  1632. Group->DeviceList[numberDevices] = DeviceInfo;
  1633. //
  1634. // Indicate one more in the list.
  1635. //
  1636. Group->NumberDevices++;
  1637. //
  1638. // Set-up this device's group id.
  1639. //
  1640. DeviceInfo->Group = Group;
  1641. //
  1642. // Set-up whether this is an active/passive member of the
  1643. // group.
  1644. //
  1645. DeviceInfo->State = DeviceState;
  1646. //
  1647. // One more deviceInfo entry.
  1648. //
  1649. DsmContext->NumberDevices++;
  1650. //
  1651. // Finally, add it to the global list of devices.
  1652. //
  1653. InsertTailList(&DsmContext->DeviceList,
  1654. &DeviceInfo->ListEntry);
  1655. KeReleaseSpinLock(&DsmContext->SpinLock, irql);
  1656. return STATUS_SUCCESS;
  1657. }
  1658. VOID
  1659. DsmRemoveDeviceEntry(
  1660. IN PDSM_CONTEXT DsmContext,
  1661. IN PGROUP_ENTRY Group,
  1662. IN PDEVICE_INFO DeviceInfo
  1663. )
  1664. {
  1665. KIRQL irql;
  1666. NTSTATUS status;
  1667. ULONG i;
  1668. ULONG j;
  1669. BOOLEAN freeGroup = FALSE;
  1670. KeAcquireSpinLock(&DsmContext->SpinLock, &irql);
  1671. //
  1672. // Find it's offset in the array of devices.
  1673. //
  1674. for (i = 0; i < Group->NumberDevices; i++) {
  1675. if (Group->DeviceList[i] == DeviceInfo) {
  1676. //
  1677. // Zero out it's entry.
  1678. //
  1679. Group->DeviceList[i] = NULL;
  1680. //
  1681. // Reduce the number in the group.
  1682. //
  1683. Group->NumberDevices--;
  1684. //
  1685. // Collapse the array.
  1686. //
  1687. // BUGBUG: If any requests come in during this time, it's
  1688. // possible to either bugcheck or get an incorrect deviceInfo
  1689. // structure.
  1690. //
  1691. for (j = i; j < Group->NumberDevices; j++) {
  1692. //
  1693. // Shuffle all entries down to fill the hole.
  1694. //
  1695. Group->DeviceList[j] = Group->DeviceList[j + 1];
  1696. }
  1697. //
  1698. // Zero out the last one.
  1699. //
  1700. Group->DeviceList[j] = NULL;
  1701. break;
  1702. }
  1703. }
  1704. //
  1705. // See if anything is left in the Group.
  1706. //
  1707. if (Group->NumberDevices == 0) {
  1708. //
  1709. // Yank it from the Group list.
  1710. //
  1711. RemoveEntryList(&Group->ListEntry);
  1712. DsmContext->NumberGroups--;
  1713. //
  1714. // Zero it.
  1715. //
  1716. RtlZeroMemory(Group,
  1717. sizeof(GROUP_ENTRY));
  1718. freeGroup = TRUE;
  1719. }
  1720. //
  1721. // Yank the device out of the Global list.
  1722. //
  1723. RemoveEntryList(&DeviceInfo->ListEntry);
  1724. DsmContext->NumberDevices--;
  1725. //
  1726. // Zero it.
  1727. //
  1728. RtlZeroMemory(DeviceInfo,
  1729. sizeof(DEVICE_INFO));
  1730. KeReleaseSpinLock(&DsmContext->SpinLock, irql);
  1731. //
  1732. // Free the allocation.
  1733. //
  1734. ExFreePool(DeviceInfo);
  1735. if (freeGroup) {
  1736. //
  1737. // Free the allocation.
  1738. //
  1739. ExFreePool(Group);
  1740. }
  1741. }
  1742. PFAILOVER_GROUP
  1743. DsmFindFOGroup(
  1744. IN PDSM_CONTEXT DsmContext,
  1745. IN PVOID PathId
  1746. )
  1747. {
  1748. PFAILOVER_GROUP failOverGroup;
  1749. PLIST_ENTRY entry;
  1750. ULONG i;
  1751. //
  1752. // Run through the list of Fail-Over Groups
  1753. //
  1754. entry = DsmContext->FailGroupList.Flink;
  1755. for (i = 0; i < DsmContext->NumberFOGroups; i++, entry = entry->Flink) {
  1756. //
  1757. // Extract the fail-over group structure.
  1758. //
  1759. failOverGroup = CONTAINING_RECORD(entry, FAILOVER_GROUP, ListEntry);
  1760. ASSERT(failOverGroup);
  1761. //
  1762. // Check for a match of the PathId.
  1763. //
  1764. if (failOverGroup->PathId == PathId) {
  1765. return failOverGroup;
  1766. }
  1767. }
  1768. return NULL;
  1769. }
  1770. PFAILOVER_GROUP
  1771. DsmBuildFOGroup(
  1772. IN PDSM_CONTEXT DsmContext,
  1773. IN PDEVICE_INFO DeviceInfo,
  1774. IN PVOID PathId
  1775. )
  1776. {
  1777. PFAILOVER_GROUP failOverGroup;
  1778. KIRQL irql;
  1779. ULONG numberGroups;
  1780. //
  1781. // Allocate an entry.
  1782. //
  1783. failOverGroup = ExAllocatePool(NonPagedPool, sizeof(FAILOVER_GROUP));
  1784. if (failOverGroup == NULL) {
  1785. return NULL;
  1786. }
  1787. RtlZeroMemory(failOverGroup, sizeof(FAILOVER_GROUP));
  1788. KeAcquireSpinLock(&DsmContext->SpinLock, &irql);
  1789. //
  1790. // Get the current number of groups, and add the one that's
  1791. // being created.
  1792. //
  1793. numberGroups = DsmContext->NumberFOGroups++;
  1794. //
  1795. // Set the PathId - All devices on the same PathId will
  1796. // failover together.
  1797. //
  1798. failOverGroup->PathId = PathId;
  1799. //
  1800. // Set the initial state to NORMAL.
  1801. //
  1802. failOverGroup->State = FG_NORMAL;
  1803. failOverGroup->FailOverSig = DSM_FOG_SIG;
  1804. //
  1805. // Add it to the global list.
  1806. //
  1807. InsertTailList(&DsmContext->FailGroupList,
  1808. &failOverGroup->ListEntry);
  1809. KeReleaseSpinLock(&DsmContext->SpinLock, irql);
  1810. return failOverGroup;
  1811. }
  1812. NTSTATUS
  1813. DsmUpdateFOGroup(
  1814. IN PDSM_CONTEXT DsmContext,
  1815. IN PFAILOVER_GROUP FailGroup,
  1816. IN PDEVICE_INFO DeviceInfo
  1817. )
  1818. {
  1819. PGROUP_ENTRY group;
  1820. ULONG count;
  1821. ULONG i;
  1822. KIRQL irql;
  1823. KeAcquireSpinLock(&DsmContext->SpinLock, &irql);
  1824. //
  1825. // Add the device to the list of devices that are on this path.
  1826. //
  1827. count = FailGroup->Count++;
  1828. FailGroup->DeviceList[count] = DeviceInfo;
  1829. //
  1830. // Get the MultiPath group for this device.
  1831. //
  1832. group = DeviceInfo->Group;
  1833. //
  1834. // Indicate that the L.B. policy needs to be updated.
  1835. // The next call to LBGetPath will cause the re-shuffle to
  1836. // take place.
  1837. //
  1838. group->LoadBalanceInit = FALSE;
  1839. //
  1840. // Indicate the need to wait for PathVerify
  1841. // This just eliminates the need to handle unit attentions
  1842. // on this device when LoadBalancing is set-up.
  1843. //
  1844. DeviceInfo->NeedsVerification = TRUE;
  1845. //
  1846. // Set the device's F.O. Group.
  1847. //
  1848. DeviceInfo->FailGroup = FailGroup;
  1849. KeReleaseSpinLock(&DsmContext->SpinLock, irql);
  1850. return STATUS_SUCCESS;
  1851. }
  1852. VOID
  1853. DsmRemoveDeviceFailGroup(
  1854. IN PDSM_CONTEXT DsmContext,
  1855. IN PFAILOVER_GROUP FailGroup,
  1856. IN PDEVICE_INFO DeviceInfo
  1857. )
  1858. {
  1859. ULONG count;
  1860. KIRQL irql;
  1861. ULONG i;
  1862. ULONG j;
  1863. KeAcquireSpinLock(&DsmContext->SpinLock, &irql);
  1864. //
  1865. // Find it's offset in the array of devices.
  1866. //
  1867. for (i = 0; i < FailGroup->Count; i++) {
  1868. if (FailGroup->DeviceList[i] == DeviceInfo) {
  1869. //
  1870. // Zero out it's entry.
  1871. //
  1872. FailGroup->DeviceList[i] = NULL;
  1873. //
  1874. // Reduce the number in the group.
  1875. //
  1876. FailGroup->Count--;
  1877. //
  1878. // Collapse the array.
  1879. //
  1880. for (j = i; j < FailGroup->Count; j++) {
  1881. //
  1882. // Shuffle all entries down to fill the hole.
  1883. //
  1884. FailGroup->DeviceList[j] = FailGroup->DeviceList[j + 1];
  1885. }
  1886. //
  1887. // Zero out the last one.
  1888. //
  1889. FailGroup->DeviceList[j] = NULL;
  1890. break;
  1891. }
  1892. }
  1893. KeReleaseSpinLock(&DsmContext->SpinLock, irql);
  1894. return;
  1895. }
  1896. PFAILOVER_GROUP
  1897. DsmSetNewPath(
  1898. IN PDSM_CONTEXT DsmContext,
  1899. IN PGROUP_ENTRY Group,
  1900. IN PDEVICE_INFO FailingDevice,
  1901. IN PFAILOVER_GROUP SelectedPath
  1902. )
  1903. {
  1904. PFAILOVER_GROUP failGroup;
  1905. PGROUP_ENTRY group;
  1906. PDEVICE_INFO device;
  1907. ULONG i;
  1908. NTSTATUS status;
  1909. BOOLEAN matched = FALSE;
  1910. if (SelectedPath) {
  1911. //
  1912. // This indicates that a new path has already been selected
  1913. // for at least one device in the Fail-Over Group.
  1914. // Run the list of new devices and find the matching
  1915. // multi-path group.
  1916. //
  1917. for (i = 0; i < SelectedPath->Count; i++) {
  1918. //
  1919. // Get the device from the newly selected Path.
  1920. //
  1921. device = SelectedPath->DeviceList[i];
  1922. //
  1923. // Determine if the device's group matches the failing
  1924. // device's group.
  1925. //
  1926. if (device->Group == Group) {
  1927. //
  1928. // The new device should be either ACTIVE or PASSIVE
  1929. //
  1930. if ((device->State == DEV_ACTIVE) ||
  1931. (device->State == DEV_PASSIVE)) {
  1932. //
  1933. // Set it to ACTIVE.
  1934. //
  1935. device->State = DEV_ACTIVE;
  1936. //
  1937. // Ensure that it's ready.
  1938. //
  1939. status = DsmSendTUR(device->TargetObject);
  1940. ASSERT(status == STATUS_SUCCESS);
  1941. matched = TRUE;
  1942. break;
  1943. }
  1944. }
  1945. }
  1946. //
  1947. // When the first call was made and a path selected, all devices
  1948. // on the path were checked for validity.
  1949. //
  1950. ASSERT(matched == TRUE);
  1951. //
  1952. // Just return the SelectedPath
  1953. //
  1954. failGroup = SelectedPath;
  1955. } else {
  1956. //
  1957. // Go through Group, looking for an available device.
  1958. //
  1959. for (i = 0; i < Group->NumberDevices; i++) {
  1960. //
  1961. // Look for any that are Passive. They are the best
  1962. // choice. This would indicate either an ActiveN/PassiveN arrangement.
  1963. //
  1964. device = Group->DeviceList[i];
  1965. if (device->State == DEV_PASSIVE) {
  1966. matched = TRUE;
  1967. break;
  1968. }
  1969. }
  1970. if (matched) {
  1971. //
  1972. // Mark the device as active.
  1973. //
  1974. device->State = DEV_ACTIVE;
  1975. //
  1976. // Ensure that it's ready.
  1977. //
  1978. status = DsmSendTUR(device->TargetObject);
  1979. if (status != STATUS_SUCCESS) {
  1980. DebugPrint((0,
  1981. "SetNewPath: SendTUR (%x) (%x)\n",
  1982. status,
  1983. device->TargetObject));
  1984. }
  1985. ASSERT(status == STATUS_SUCCESS);
  1986. //
  1987. // Get the Fail-Over group from the selected device.
  1988. //
  1989. failGroup = device->FailGroup;
  1990. } else {
  1991. //
  1992. // No passive devices. This indicates either an Active/Active arrangement,
  1993. // or everything is failed.
  1994. // Look for active devices.
  1995. //
  1996. for (i = 0; i < Group->NumberDevices; i++) {
  1997. device = Group->DeviceList[i];
  1998. if (device->State == DEV_ACTIVE) {
  1999. matched = TRUE;
  2000. break;
  2001. }
  2002. }
  2003. if (matched) {
  2004. //
  2005. // The device is already active, just return the
  2006. // new path info.
  2007. //
  2008. failGroup = device->FailGroup;
  2009. //
  2010. // Ensure that it's ready.
  2011. //
  2012. status = DsmSendTUR(device->TargetObject);
  2013. } else {
  2014. //
  2015. // Everything has failed. Should try to do something?? TODO
  2016. //
  2017. failGroup = NULL;
  2018. }
  2019. }
  2020. if (failGroup) {
  2021. //
  2022. // Run through all the devices to ensure that they are
  2023. // in a reasonable state.
  2024. //
  2025. for (i = 0; i < failGroup->Count; i++) {
  2026. device = failGroup->DeviceList[i];
  2027. if ((device->State != DEV_ACTIVE) &&
  2028. (device->State != DEV_PASSIVE)) {
  2029. //
  2030. // Really need to find a new fail-over group.
  2031. // TODO.
  2032. // This isn't necessarily a valid assert. If static lb is in
  2033. // effect and this is one of the first to fail-over, others
  2034. // could be considered bad.
  2035. //
  2036. ASSERT(device->State == DEV_ACTIVE);
  2037. }
  2038. }
  2039. }
  2040. }
  2041. return failGroup;
  2042. }
  2043. VOID
  2044. DsmLBInit(
  2045. IN PDSM_CONTEXT DsmContext,
  2046. IN PGROUP_ENTRY Group
  2047. )
  2048. {
  2049. PFAILOVER_GROUP failGroup;
  2050. PDEVICE_INFO device;
  2051. PLIST_ENTRY entry;
  2052. ULONG numberPaths;
  2053. ULONG assignedPath;
  2054. ULONG i;
  2055. BOOLEAN found;
  2056. //
  2057. // TODO: Once the Wmi support is here, this will be configurable
  2058. // Need to add code to handle each of the different policies.
  2059. //
  2060. //
  2061. // Doing 'static' LB. Out of each Multi-Path Group, one
  2062. // device will be active and assigned to a particular path.
  2063. // The assignment is based on the group ordinal modulus the total
  2064. // number of paths.
  2065. //
  2066. numberPaths = DsmContext->NumberFOGroups;
  2067. assignedPath = Group->GroupNumber % numberPaths;
  2068. // assignedPath = 0;
  2069. DebugPrint((2,
  2070. "DsmLBInit: NumberFOGs (%x), Group Number (%x), assignedPath (%x)\n",
  2071. DsmContext->NumberFOGroups,
  2072. Group->GroupNumber,
  2073. assignedPath));
  2074. //
  2075. // Get the Fail-Over Group with the correct path.
  2076. //
  2077. i = 0;
  2078. found = FALSE;
  2079. //
  2080. // Get the first entry.
  2081. //
  2082. entry = DsmContext->FailGroupList.Flink;
  2083. do {
  2084. //
  2085. // Extract the F.O. Group entry.
  2086. //
  2087. failGroup = CONTAINING_RECORD(entry, FAILOVER_GROUP, ListEntry);
  2088. ASSERT(failGroup);
  2089. if (i == assignedPath) {
  2090. //
  2091. // This is the one.
  2092. //
  2093. found = TRUE;
  2094. } else {
  2095. //
  2096. // Advance to the next entry.
  2097. //
  2098. entry = entry->Flink;
  2099. i++;
  2100. }
  2101. //
  2102. // BUGBUG: Need to terminate this loop based on #of FG's.
  2103. //
  2104. } while (found == FALSE);
  2105. //
  2106. // It may occur that though there are multiple paths/groups, not
  2107. // all devices have been put into the DeviceList.
  2108. // If there is only 1, special case this. It will get fixed up
  2109. // when the second device arrives.
  2110. //
  2111. if (Group->NumberDevices == 1) {
  2112. //
  2113. // LOG. Indicates something "might" be wrong - definitely
  2114. // not multi-pathing this device, so could lead to disaster
  2115. //
  2116. //
  2117. // Grab device 0 and set it active.
  2118. //
  2119. device = Group->DeviceList[0];
  2120. device->State = DEV_ACTIVE;
  2121. //
  2122. // Go ahead state that this is init'ed. If/when another
  2123. // device shows up, we will re-do this.
  2124. //
  2125. Group->LoadBalanceInit = TRUE;
  2126. Group->LoadBalanceType = LB_STATIC;
  2127. DebugPrint((0,
  2128. "DsmLBInit: Only One Device (%x) currently in group. Setting it Active\n",
  2129. device));
  2130. return;
  2131. }
  2132. //
  2133. // Find the device with the same F.O. Group
  2134. // in the mulit-path group.
  2135. //
  2136. for (i = 0; i < Group->NumberDevices; i++) {
  2137. //
  2138. // Get the device info.
  2139. //
  2140. device = Group->DeviceList[i];
  2141. //
  2142. // See if there is a match.
  2143. //
  2144. if (device->FailGroup == failGroup) {
  2145. //
  2146. // Set the device to active.
  2147. //
  2148. device->State = DEV_ACTIVE;
  2149. //
  2150. // Done setting up this multi-path group.
  2151. // Indicate that it's so, and that we are using
  2152. // STATIC Load-Balancing.
  2153. //
  2154. Group->LoadBalanceInit = TRUE;
  2155. Group->LoadBalanceType = LB_STATIC;
  2156. return;
  2157. } else {
  2158. //
  2159. // This makes the assumption, once again that Static LB is in
  2160. // effect. As this entire routine would need to be changes for
  2161. // any other policy, it's OK.
  2162. //
  2163. if (device->State == DEV_ACTIVE) {
  2164. device->State = DEV_PASSIVE;
  2165. } else {
  2166. //
  2167. // Don't muck with the state. It could be REMOVE_PENDING or FAILED
  2168. // and just waiting for the cleanup.
  2169. //
  2170. NOTHING;
  2171. }
  2172. }
  2173. }
  2174. }
  2175. NTSTATUS
  2176. DsmQueryData(
  2177. IN PVOID DsmContext,
  2178. IN PIRP Irp,
  2179. IN ULONG GuidIndex,
  2180. IN ULONG InstanceIndex,
  2181. IN ULONG InstanceCount,
  2182. IN OUT PULONG InstanceLengthArray,
  2183. IN ULONG BufferAvail,
  2184. OUT PUCHAR Buffer,
  2185. OUT PULONG DataLength
  2186. )
  2187. {
  2188. PDSM_CONTEXT context = DsmContext;
  2189. NTSTATUS status = STATUS_UNSUCCESSFUL;
  2190. ULONG dataLength;
  2191. switch(GuidIndex) {
  2192. case GENDSM_CONFIGINFOGuidIndex: {
  2193. PGENDSM_CONFIGINFO configInfo;
  2194. dataLength = sizeof(GENDSM_CONFIGINFO);
  2195. if (dataLength > BufferAvail) {
  2196. //
  2197. // Buffer is too small. Indicate this back
  2198. // to the mpio driver.
  2199. //
  2200. *DataLength = dataLength;
  2201. status = STATUS_BUFFER_TOO_SMALL;
  2202. } else {
  2203. //
  2204. // Get the buffer.
  2205. //
  2206. configInfo = (PGENDSM_CONFIGINFO)Buffer;
  2207. //
  2208. // Set-up the necessary info.
  2209. //
  2210. configInfo->NumberFOGroups = context->NumberFOGroups;
  2211. configInfo->NumberMPGroups = context->NumberGroups;
  2212. configInfo->LoadBalancePolicy = DSM_LB_STATIC;
  2213. //
  2214. // Indicate the size of returned data to
  2215. // WMI and MPIO.
  2216. //
  2217. *DataLength = dataLength;
  2218. *InstanceLengthArray = dataLength;
  2219. status = STATUS_SUCCESS;
  2220. }
  2221. break;
  2222. }
  2223. case BinaryMofGuidIndex: {
  2224. //
  2225. // Check that the buffer size can handle the binary
  2226. // mof data.
  2227. //
  2228. dataLength = sizeof(DsmBinaryMofData);
  2229. if (dataLength > BufferAvail) {
  2230. //
  2231. // Buffer is too small.
  2232. // Indicate such with status.
  2233. //
  2234. status = STATUS_BUFFER_TOO_SMALL;
  2235. //
  2236. // Update DataLength, so that the mpio pdo knows
  2237. // the correct size to report back to Wmi.
  2238. //
  2239. *DataLength = dataLength;
  2240. } else {
  2241. RtlCopyMemory(Buffer, DsmBinaryMofData, dataLength);
  2242. //
  2243. // Set both of these on all successful operations.
  2244. // InstanceLengthArray gives wMI it's info and DataLength
  2245. // gives the mpio pdo it's info.
  2246. //
  2247. *InstanceLengthArray = dataLength;
  2248. *DataLength = dataLength;
  2249. status = STATUS_SUCCESS;
  2250. }
  2251. break;
  2252. }
  2253. default:
  2254. status = STATUS_WMI_GUID_NOT_FOUND;
  2255. break;
  2256. }
  2257. return status;
  2258. }
  2259. VOID
  2260. DsmWmiInitialize(
  2261. IN PDSM_WMILIB_CONTEXT WmiInfo
  2262. )
  2263. {
  2264. RtlZeroMemory(WmiInfo, sizeof(DSM_WMILIB_CONTEXT));
  2265. //
  2266. // This will jam in the entry points and guids for
  2267. // supported WMI operations.
  2268. //
  2269. WmiInfo->GuidCount = DsmGuidCount;
  2270. WmiInfo->GuidList = DsmGuidList;
  2271. WmiInfo->QueryWmiDataBlock = DsmQueryData;
  2272. //
  2273. // SetDataBlock and Item, Execute, and FunctionControl are currently
  2274. // not needed, so leave them set to zero.
  2275. //
  2276. return;
  2277. }
  2278. VOID
  2279. DsmDebugPrint(
  2280. ULONG DebugPrintLevel,
  2281. PCCHAR DebugMessage,
  2282. ...
  2283. )
  2284. /*++
  2285. Routine Description:
  2286. Debug print for the DSM
  2287. Arguments:
  2288. Return Value:
  2289. None
  2290. --*/
  2291. {
  2292. va_list ap;
  2293. va_start(ap, DebugMessage);
  2294. if (DebugPrintLevel <= GenDSMDebug) {
  2295. _vsnprintf(DebugBuffer, DEBUG_BUFFER_LENGTH, DebugMessage, ap);
  2296. DbgPrint(DebugBuffer);
  2297. }
  2298. va_end(ap);
  2299. }