summaryrefslogtreecommitdiff
path: root/usr/src/cmd/sasinfo/sasinfo-list.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/sasinfo/sasinfo-list.c')
-rw-r--r--usr/src/cmd/sasinfo/sasinfo-list.c2799
1 files changed, 2799 insertions, 0 deletions
diff --git a/usr/src/cmd/sasinfo/sasinfo-list.c b/usr/src/cmd/sasinfo/sasinfo-list.c
new file mode 100644
index 0000000000..57b39b8247
--- /dev/null
+++ b/usr/src/cmd/sasinfo/sasinfo-list.c
@@ -0,0 +1,2799 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <printAttrs.h>
+#include <smhbaapi.h>
+
+#define TABLEN 2
+typedef struct inputArgs {
+ int wwnCount;
+ char **wwn_argv;
+ uint64_t portWWN;
+ char *hbaName;
+ int pflag;
+ int *wwn_flag;
+} inputArg_t;
+
+typedef struct tgt_mapping {
+ SMHBA_SCSIENTRY tgtentry;
+ uchar_t inq_vid[8];
+ uchar_t inq_pid[16];
+ uchar_t inq_dtype;
+ struct tgt_mapping *next;
+}tgt_mapping;
+
+/*
+ * Remote port tree node structure.
+ */
+typedef struct smhba_rp_tree {
+ SMHBA_PORTATTRIBUTES portattr;
+ SMHBA_SAS_PORT sasattr;
+ tgt_mapping *first_entry;
+ int printed;
+ struct smhba_rp_tree *parent;
+ struct smhba_rp_tree *child;
+ struct smhba_rp_tree *sibling;
+}rp_tree_t;
+
+/*
+ * Report LUN data structure.
+ */
+struct lun {
+ uchar_t val[8];
+};
+
+typedef struct rep_luns_rsp {
+ uint32_t length;
+ uint32_t rsrvd;
+ struct lun lun[1];
+} rep_luns_rsp_t;
+
+/*
+ * The following flag is used for printing HBA header on-demand.
+ */
+static int g_printHBA = 0;
+
+/*
+ * The following structure is for sorted output of HBA and HBA Port.
+ */
+typedef struct _sas_elem {
+ char name[256];
+ int index;
+}sas_elem_t;
+
+/*
+ * The following two functions are for generating hierachy of expander
+ * subcommand.
+ */
+static int
+sas_rp_tree_insert(rp_tree_t **rproot, rp_tree_t *rpnode);
+static int
+sas_rp_tree_print(HBA_HANDLE handle, char *adapterName,
+ HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
+ rp_tree_t *rpnode, inputArg_t *input, int gident,
+ int *printPort);
+static int
+sas_rp_tree_print_desc(HBA_HANDLE handle, HBA_UINT32 portIndex,
+ SMHBA_PORTATTRIBUTES *port, rp_tree_t *desc,
+ inputArg_t *input, int lident, int gident);
+static int
+sas_print_rpnode(inputArg_t *input,
+ rp_tree_t *rpnode, int lident, int gident);
+static void sas_rp_tree_free(rp_tree_t *rproot);
+
+typedef int (*processPortFunc)(HBA_HANDLE handle, char *adapterName,
+ HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
+ SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input);
+
+static int processHBA(inputArg_t *input,
+ processPortFunc processPort);
+
+static int isPortWWNInArgv(inputArg_t *input, PHBA_WWN pWWN);
+static int isStringInArgv(inputArg_t *input, const char *adapterName);
+static boolean_t compareLUName(char *cmdArg, char *osName);
+static discoveredDevice *LUList = NULL;
+static targetPortList_t *gTargetPortList = NULL;
+
+/* processes for hanlding local HBA info */
+static int handleHBA(SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input,
+ int numberOfPorts, const char *adapterName);
+static int handleHBAPort(HBA_HANDLE handle, char *adapterName,
+ HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
+ SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input);
+static int processHBAPortPhyInfo(HBA_HANDLE handle, HBA_UINT32 portIndex,
+ SMHBA_PORTATTRIBUTES *port, int pflag);
+static int processHBAPortPhyStat(HBA_HANDLE handle, HBA_UINT32 portIndex,
+ int phyIndex, PSMHBA_SAS_PHY phyattrs, int pflag);
+
+/* process for handling expander info */
+static int handleExpander(HBA_HANDLE handle, char *adapterName,
+ HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
+ SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input);
+
+/* process for handling target port info */
+static int handleTargetPort(HBA_HANDLE handle, char *adapterName,
+ HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
+ SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input);
+
+/* process for handling logical unit info */
+static int handleLogicalUnit(HBA_HANDLE handle, char *adapterName,
+ HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
+ SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input);
+
+/* process for target port SCSI processing */
+static int
+searchTargetPortMappingData(HBA_HANDLE handle, HBA_UINT32 portIndex,
+ SMHBA_PORTATTRIBUTES *port, SMHBA_SAS_PORT *sasattr,
+ struct targetPortConfig *configData);
+
+/* process for target port config processing */
+static int searchTargetPort(HBA_HANDLE handle, HBA_UINT32 portIndex,
+ SMHBA_PORTATTRIBUTES *port, SMHBA_PORTATTRIBUTES *targetattr,
+ SMHBA_SAS_PORT *sasattr, int pflag);
+
+/* process for logical-unit config processing */
+static int
+searchDevice(PSMHBA_SCSIENTRY entryP, HBA_HANDLE handle, HBA_WWN hbaPortWWN,
+ HBA_WWN domainPortWWN, char *portName, int pflag);
+
+/* get domain port out of hba-port phy attr. */
+HBA_STATUS get_domainPort(HBA_HANDLE handle,
+ int portindex, PSMHBA_PORTATTRIBUTES port,
+ HBA_WWN *pdomainPort);
+
+static int
+sas_name_comp(const char *name1, const char *name2);
+static void
+sas_elem_sort(sas_elem_t *array, int nelem);
+
+/*
+ * function for hba subcommand
+ *
+ * Arguments:
+ * wwnCount - count of the number of WWNs in wwn_argv
+ * if wwnCount > 0, then we will only print information for
+ * the hba ports listed in wwn_argv
+ * if wwnCount == 0, then we will print information on all hba ports
+ * wwn_argv - argument array of hba port WWNs
+ * options - any options specified by the caller
+ *
+ * returns:
+ * 0 if successful
+ * >0 otherwise
+ */
+int
+sas_util_list_hba(int hbaCount, char **hba_argv, cmdOptions_t *options)
+{
+ HBA_STATUS status;
+ int processHBA_flags = 0;
+ inputArg_t input;
+ int err_cnt = 0;
+
+ /* process each of the options */
+ for (; options->optval; options++) {
+ switch (options->optval) {
+ case 'v':
+ processHBA_flags |= PRINT_VERBOSE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((status = HBA_LoadLibrary()) != HBA_STATUS_OK) {
+ (void *) fprintf(stderr, "%s %s\n",
+ gettext("Failed to load SM-HBA libraries."
+ "Reason:"), getHBAStatus(status));
+ err_cnt++;
+ return (err_cnt);
+ }
+
+ (void *) memset(&input, 0, sizeof (input));
+ /* utilize wwnCount and wwn_argv for hbaCount and hba_argv */
+ input.wwnCount = hbaCount;
+ input.wwn_argv = hba_argv;
+ input.pflag = processHBA_flags;
+
+ /*
+ * Process and filter for every local hba,
+ * when the hba is not specificed, print all hba(s).
+ */
+ err_cnt += processHBA(&input, NULL);
+
+ (void) HBA_FreeLibrary();
+
+ return (err_cnt);
+}
+
+/*
+ * function for hba-port subcommand
+ *
+ * Arguments:
+ * wwnCount - count of the number of WWNs in wwn_argv
+ * if wwnCount > 0, then we will only print information for
+ * the hba ports listed in wwn_argv
+ * if wwnCount == 0, then we will print information on all hba ports
+ * wwn_argv - argument array of hba port WWNs
+ * options - any options specified by the caller
+ *
+ * returns:
+ * 0 if successful
+ * >0 otherwise
+ */
+int
+sas_util_list_hbaport(int wwnCount, char **wwn_argv, cmdOptions_t *options)
+{
+ HBA_STATUS status;
+ int processHBA_flags = 0;
+ inputArg_t input;
+ int err_cnt = 0;
+ char hbaName[256] = {'\0'};
+
+ /* process each of the options */
+ for (; options->optval; options++) {
+ switch (options->optval) {
+ case 'a':
+ (void *) strlcpy(hbaName,
+ options->optarg, sizeof (hbaName));
+ break;
+ case 'y':
+ processHBA_flags |= PRINT_PHY;
+ break;
+ case 'l':
+ processHBA_flags |= PRINT_PHY_LINKSTAT;
+ break;
+ case 'v':
+ processHBA_flags |= PRINT_VERBOSE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((status = HBA_LoadLibrary()) != HBA_STATUS_OK) {
+ (void *) fprintf(stderr, "%s %s\n",
+ gettext("Failed to load SM-HBA libraries."
+ "Reason:"), getHBAStatus(status));
+ err_cnt++;
+ return (err_cnt);
+ }
+
+ (void *) memset(&input, 0, sizeof (input));
+ input.wwnCount = wwnCount;
+ input.wwn_argv = wwn_argv;
+ input.hbaName = hbaName;
+ input.pflag = processHBA_flags;
+
+ /*
+ * Process and filter for every local hba-port,
+ * when the hba-port is not specificed, print all hba-port(s).
+ */
+ err_cnt += processHBA(&input, handleHBAPort);
+
+ (void) HBA_FreeLibrary();
+
+ return (err_cnt);
+}
+
+/*
+ * function for expander subcommand
+ *
+ * Arguments:
+ * wwnCount - the number of Remote Port SAS Address in wwn_argv
+ * if wwnCount == 0, then print information on all
+ * expander devices.
+ * if wwnCount > 0, then print information for the exapnders
+ * given in wwn_argv.
+ * wwn_argv - array of WWNs
+ * options - options specified by the caller
+ *
+ * returns:
+ * 0 if successful
+ * >0 otherwise
+ */
+int
+sas_util_list_expander(int wwnCount, char **wwn_argv, cmdOptions_t *options)
+{
+ HBA_STATUS status;
+ int processHBA_flags = 0;
+ char hbaPort[MAXPATHLEN + 1] = {0};
+ inputArg_t input;
+ int err_cnt = 0;
+
+ /* process each of the options */
+ for (; options->optval; options++) {
+ switch (options->optval) {
+ case 'p':
+ (void) strlcpy(hbaPort, options->optarg,
+ sizeof (hbaPort));
+ break;
+ case 't':
+ processHBA_flags |= PRINT_TARGET_PORT;
+ break;
+ case 'v':
+ processHBA_flags |= PRINT_VERBOSE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((status = HBA_LoadLibrary()) != HBA_STATUS_OK) {
+ (void *) fprintf(stderr, "%s %s\n",
+ gettext("Failed to load SM-HBA libraries."
+ "Reason:"), getHBAStatus(status));
+ err_cnt++;
+ return (err_cnt);
+ }
+
+ (void *) memset(&input, 0, sizeof (input));
+ input.wwnCount = wwnCount;
+ input.wwn_argv = wwn_argv;
+ input.pflag = processHBA_flags;
+ input.hbaName = hbaPort;
+
+ /*
+ * Process and filter for every hba-port,
+ * when the hba-port is not specificed, print all hba-port(s).
+ */
+ err_cnt += processHBA(&input, handleExpander);
+
+ (void) HBA_FreeLibrary();
+
+ return (err_cnt);
+}
+
+/*
+ * function for target-port subcommand
+ *
+ * Arguments:
+ * wwnCount - the number of Remote Port SAS Address in wwn_argv
+ * if wwnCount == 0, then print information on all
+ * target ports.
+ * if wwnCount > 0, then print information for the target ports
+ * given in wwn_argv.
+ * wwn_argv - array of WWNs
+ * options - options specified by the caller
+ *
+ * returns:
+ * 0 if successful
+ * >0 otherwise
+ */
+int
+sas_util_list_targetport(int tpCount, char **tpArgv, cmdOptions_t *options)
+{
+ HBA_STATUS status;
+ int processHBA_flags = 0;
+ int tp, tpFound;
+ inputArg_t input;
+ targetPortList_t *tpListWalk;
+ int err_cnt = 0;
+ uint64_t tmpAddr;
+
+ /* process each of the options */
+ for (; options->optval; options++) {
+ switch (options->optval) {
+ case 's':
+ processHBA_flags |= PRINT_TARGET_SCSI;
+ break;
+ case 'v':
+ processHBA_flags |= PRINT_VERBOSE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((status = HBA_LoadLibrary()) != HBA_STATUS_OK) {
+ (void *) fprintf(stderr, "%s %s\n",
+ gettext("Failed to load SM-HBA libraries."
+ "Reason:"), getHBAStatus(status));
+ err_cnt++;
+ return (err_cnt);
+ }
+
+ (void *) memset(&input, 0, sizeof (input));
+ input.wwnCount = tpCount;
+ input.wwn_argv = tpArgv;
+ input.pflag = processHBA_flags;
+
+ /*
+ * Process and filter for every hba-port,
+ * when the hba-port is not specificed, print all hba-port(s).
+ */
+ err_cnt += processHBA(&input, handleTargetPort);
+
+ if (tpCount == 0) {
+ /* list all target port */
+ for (tpListWalk = gTargetPortList; tpListWalk != NULL;
+ tpListWalk = tpListWalk->next) {
+ err_cnt += printTargetPortInfo(tpListWalk, input.pflag);
+ }
+ } else {
+ /*
+ * When operands provided, we should set the error code
+ * only if there are issues related with the operands.
+ */
+ err_cnt = 0;
+ /*
+ * list any paths not found first
+ * this gives the user cleaner output
+ */
+ for (tp = 0; tp < tpCount; tp++) {
+ errno = 0;
+ tmpAddr = strtoull(tpArgv[tp], NULL, 16);
+ if ((tmpAddr == 0) && (errno != 0)) {
+ err_cnt++;
+ continue;
+ }
+ for (tpListWalk = gTargetPortList, tpFound = B_FALSE;
+ tpListWalk != NULL;
+ tpListWalk = tpListWalk->next) {
+ if (wwnConversion(tpListWalk->sasattr.
+ LocalSASAddress.wwn) == tmpAddr) {
+ tpFound = B_TRUE;
+ break;
+ }
+ }
+ if (tpFound == B_FALSE) {
+ (void *) fprintf(stderr,
+ "Error: Target Port %s Not Found \n",
+ tpArgv[tp]);
+ err_cnt++;
+ }
+ }
+ /* list all paths requested in order requested */
+ for (tp = 0; tp < tpCount; tp++) {
+ errno = 0;
+ tmpAddr = strtoull(tpArgv[tp], NULL, 16);
+ if ((tmpAddr == 0) && (errno != 0)) {
+ continue;
+ }
+ for (tpListWalk = gTargetPortList, tpFound = B_FALSE;
+ tpListWalk != NULL;
+ tpListWalk = tpListWalk->next) {
+ if (wwnConversion(tpListWalk->sasattr.
+ LocalSASAddress.wwn) == tmpAddr) {
+ err_cnt += printTargetPortInfo(
+ tpListWalk,
+ processHBA_flags);
+ }
+ }
+ }
+ }
+ (void) HBA_FreeLibrary();
+ return (err_cnt);
+}
+/*
+ * This function will enumerate all the hba and hba ports,
+ * call the callback function to proceed with futher process.
+ *
+ * Arguments:
+ * input - contains all the input parameters.
+ * processPort - a callback function when handling each port.
+ *
+ * Return Value:
+ * 0 sucessfully processed handle
+ * >0 error has occured
+ */
+static int
+processHBA(inputArg_t *input, processPortFunc processPort)
+{
+ int numAdapters = 0;
+ int matchedHBAs = 0;
+ int matchedHBAPorts = 0;
+ int hbaPortExist = 0;
+ HBA_STATUS status;
+ HBA_HANDLE handle;
+ HBA_UINT32 numberOfPorts = 0;
+ int portIndex = 0;
+ HBA_PORTTYPE porttype;
+ SMHBA_LIBRARYATTRIBUTES libattrs;
+ SMHBA_ADAPTERATTRIBUTES attrs;
+ SMHBA_PORTATTRIBUTES port;
+ SMHBA_SAS_PORT sasattrs;
+ int i, sum, ret = 0;
+ int remote_avail = 0;
+ int local_avail = 0;
+ sas_elem_t *adpt_array = NULL;
+ sas_elem_t *port_array = NULL;
+
+ numAdapters = HBA_GetNumberOfAdapters();
+ if (numAdapters == 0) {
+ (void *) fprintf(stderr, "%s\n",
+ gettext("Error: No Adapters Found."));
+ return (++ret);
+ }
+
+ /*
+ * To deal with mismatching HBA/HBA Port/Expander Port, we need an
+ * array of flags for each operands.
+ */
+ if (input->wwnCount && (processPort != handleTargetPort) &&
+ (processPort != handleLogicalUnit)) {
+ input->wwn_flag = calloc(input->wwnCount, sizeof (int));
+ if (input->wwn_flag == NULL) {
+ (void *) fprintf(stderr, "%s\n",
+ gettext("No enough memory on heap"));
+ return (++ret);
+ }
+ }
+
+ adpt_array = calloc(numAdapters, sizeof (sas_elem_t));
+ if (adpt_array == NULL) {
+ (void *) fprintf(stderr, "%s\n",
+ gettext("No enough memory on heap"));
+ if (input->wwn_flag) {
+ free(input->wwn_flag);
+ input->wwn_flag = NULL;
+ }
+ return (++ret);
+ }
+ for (i = 0; i < numAdapters; i++) {
+ status =
+ SMHBA_GetVendorLibraryAttributes(i, &libattrs);
+ /*
+ * If we get SAS incompatible library warning here,
+ * just skip the following steps.
+ */
+ if (status != 1) {
+ continue;
+ }
+ status = HBA_GetAdapterName(i, adpt_array[i].name);
+ if (status != HBA_STATUS_OK) {
+ (void *) fprintf(stderr, "%s %d %s %s\n",
+ gettext("Error: Failed to get the name for"
+ " HBA index"),
+ i, gettext("Reason:"),
+ getHBAStatus(status));
+ ret++;
+ continue;
+ }
+ adpt_array[i].index = i;
+ }
+ /* Sort the HBA Name in place. */
+ sas_elem_sort(adpt_array, numAdapters);
+
+ for (i = 0; i < numAdapters; i++) {
+ int times = 0;
+ if (adpt_array[i].name[0] != '\0') {
+ if ((handle = HBA_OpenAdapter(adpt_array[i].name))
+ == 0) {
+ (void *) fprintf(stderr, "%s %s.\n",
+ gettext("Error: Failed to open adapter"),
+ adpt_array[i].name);
+ ret++;
+ continue;
+ }
+ } else {
+ continue;
+ }
+
+ /*
+ * We need to support an adapter without hba port.
+ * So get attributes anyway.
+ */
+ (void *) memset(&attrs, 0, sizeof (attrs));
+ status = SMHBA_GetAdapterAttributes(handle, &attrs);
+ while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
+ status == HBA_STATUS_ERROR_BUSY) &&
+ times++ < HBA_MAX_RETRIES) {
+ (void) sleep(1);
+ status = SMHBA_GetAdapterAttributes(handle,
+ &attrs);
+ }
+ if (status != HBA_STATUS_OK) {
+ (void *) fprintf(stderr, "%s %s %s %s\n",
+ gettext("Error: Failed to get attributes"
+ " for HBA "), adpt_array[i].name,
+ gettext("Reason:"),
+ getHBAStatus(status));
+
+ HBA_CloseAdapter(handle);
+ ret++;
+ continue;
+ }
+
+ status = SMHBA_GetNumberOfPorts(handle, &numberOfPorts);
+ if (status != HBA_STATUS_OK) {
+ (void *) fprintf(stderr, "%s %s %s %s\n",
+ gettext("Error: Failed to get number of ports "
+ "for HBA"), adpt_array[i].name,
+ gettext("Reason:"),
+ getHBAStatus(status));
+ HBA_CloseAdapter(handle);
+ ret++;
+ continue;
+ }
+
+ /*
+ * Deal with each subcommand for hba filter here,
+ * processPort is NULL for hba subcommand.
+ */
+ if (processPort == NULL) {
+ matchedHBAs += handleHBA(&attrs, input,
+ numberOfPorts, adpt_array[i].name);
+ HBA_CloseAdapter(handle);
+ continue;
+ } else if (processPort == handleHBAPort) {
+ if (input->hbaName[0] != '\0') {
+ if (strcmp(input->hbaName,
+ adpt_array[i].name) == 0) {
+ matchedHBAs++;
+ } else {
+ continue;
+ }
+ } else {
+ matchedHBAs++;
+ }
+ } else {
+ matchedHBAs++;
+ }
+
+ /*
+ * In order to have a sorted output for HBA Port, we should
+ * do the sorting before moving on.
+ */
+ if (numberOfPorts) {
+ port_array = calloc(numberOfPorts, sizeof (sas_elem_t));
+ }
+ for (portIndex = 0; portIndex < numberOfPorts; portIndex++) {
+ if ((status = SMHBA_GetPortType(handle,
+ portIndex, &porttype)) != HBA_STATUS_OK) {
+ (void *) fprintf(stderr, "%s %s %s %s\n",
+ gettext("Failed to get adapter port type "
+ "for HBA"), adpt_array[i].name,
+ gettext("Reason:"),
+ getHBAStatus(status));
+ ret++;
+ continue;
+ }
+ if (porttype != HBA_PORTTYPE_SASDEVICE) {
+ /* skip any non-sas hba port */
+ continue;
+ }
+ (void *) memset(&port, 0, sizeof (port));
+ (void *) memset(&sasattrs, 0, sizeof (sasattrs));
+ port.PortSpecificAttribute.SASPort = &sasattrs;
+ if ((status = SMHBA_GetAdapterPortAttributes(
+ handle, portIndex, &port)) != HBA_STATUS_OK) {
+ /*
+ * Not able to get port attributes.
+ * print out error message and
+ * move on to the next port
+ */
+ (void *) fprintf(stderr, "%s %s %s %d %s %s\n",
+ gettext("Error: Failed to get port "
+ "attributes for HBA"), adpt_array[i].name,
+ gettext("port index"), portIndex,
+ gettext("Reason:"),
+ getHBAStatus(status));
+ ret++;
+ continue;
+ }
+ (void) strlcpy(port_array[portIndex].name,
+ port.OSDeviceName,
+ sizeof (port_array[portIndex].name));
+ port_array[portIndex].index = portIndex;
+ }
+ /* Sort the HBA Port Name here. */
+ if (port_array) {
+ sas_elem_sort(port_array, numberOfPorts);
+ }
+ /*
+ * Sum up the local hba ports available.
+ */
+ local_avail += numberOfPorts;
+
+ /*
+ * Clear g_printHBA flag for expander subcommand.
+ */
+ g_printHBA = 0;
+
+ /* process each port on the given adapter */
+ for (portIndex = 0;
+ portIndex < numberOfPorts;
+ portIndex++) {
+ /*
+ * We only handle the port which is valid.
+ */
+ if (port_array[portIndex].name[0] == '\0') {
+ continue;
+ }
+ (void *) memset(&port, 0, sizeof (port));
+ (void *) memset(&sasattrs, 0, sizeof (sasattrs));
+ port.PortSpecificAttribute.SASPort = &sasattrs;
+
+ (void) SMHBA_GetAdapterPortAttributes(handle,
+ port_array[portIndex].index, &port);
+
+ /*
+ * We have different things to do for the three
+ * sub-commands here.
+ */
+ if (processPort == handleHBAPort) {
+ /*
+ * For hba-port, we will check whether the
+ * specified hba port exist first.
+ * But if no hba port specified, we should
+ * by pass this check(just let hbaPortExist
+ * be 1).
+ */
+ if (input->wwnCount > 0) {
+ if (isStringInArgv(input,
+ port.OSDeviceName)) {
+ hbaPortExist = 1;
+ if (g_printHBA == 0) {
+ (void *) fprintf(stdout,
+ "%s %s\n",
+ "HBA Name:",
+ adpt_array[i].name);
+ g_printHBA = 1;
+ }
+ }
+ } else {
+ hbaPortExist = 1;
+ if (g_printHBA == 0) {
+ (void *) fprintf(stdout,
+ "%s %s\n",
+ "HBA Name:",
+ adpt_array[i].name);
+ g_printHBA = 1;
+ }
+ }
+ }
+
+ if (processPort == handleExpander) {
+ /*
+ * For expander device, input->hbaName is
+ * the hba port name specified on the
+ * command line(with -p option).
+ */
+ if (input->hbaName[0] != '\0') {
+ if (strcmp(input->hbaName,
+ port.OSDeviceName) == 0)
+ hbaPortExist = 1;
+ } else
+ hbaPortExist = 1;
+ }
+
+ if (processPort == handleTargetPort) {
+ /*
+ * For target port, we don't need to check the
+ * hba port address, so let it go here.
+ */
+ hbaPortExist = 1;
+ }
+
+ if (processPort == handleLogicalUnit) {
+ /*
+ * For lu, we don't need to check the hba
+ * port address, so let it go here.
+ */
+ hbaPortExist = 1;
+ }
+
+ if (hbaPortExist) {
+ if (port.PortSpecificAttribute.SASPort->
+ NumberofDiscoveredPorts) {
+ remote_avail++;
+ }
+ ret += (*processPort)(handle,
+ adpt_array[i].name,
+ port_array[portIndex].index, &port,
+ &attrs, input);
+ /*
+ * We should reset the hbaPortExist flag
+ * here for next round of check and count
+ * for the machedHBAPorts.
+ */
+ hbaPortExist = 0;
+ matchedHBAPorts++;
+ }
+ }
+ if (port_array) {
+ free(port_array);
+ port_array = NULL;
+ }
+ HBA_CloseAdapter(handle);
+ }
+ if (adpt_array) {
+ free(adpt_array);
+ adpt_array = NULL;
+ }
+
+ /*
+ * When we are here, we have traversed all the hba and hba ports.
+ */
+ if (matchedHBAs == 0) {
+ (void *) fprintf(stderr, "%s\n",
+ gettext("Error: Matching HBA not found."));
+ if (input->wwn_flag) {
+ free(input->wwn_flag);
+ input->wwn_flag = NULL;
+ }
+ return (++ret);
+ } else if (processPort == NULL) {
+ /*
+ * processPort == NULL signifies hba subcommand.
+ * If enter here, it means we have at least one matching
+ * hba, we need to check if there are mismatching ones.
+ */
+ for (i = 0; i < input->wwnCount; i++) {
+ if (input->wwn_flag[i] == 0) {
+ (void *) fprintf(stderr, "%s %s %s\n",
+ gettext("Error: HBA"),
+ input->wwn_argv[i],
+ gettext("not found."));
+ ret++;
+ }
+ }
+ } else {
+ if (local_avail > 0 && matchedHBAPorts == 0) {
+ (void *) fprintf(stderr, "%s\n",
+ gettext("Error: Matching HBA Port "
+ "not found."));
+ if (input->wwn_flag) {
+ free(input->wwn_flag);
+ input->wwn_flag = NULL;
+ }
+ return (++ret);
+ } else if (local_avail == 0) {
+ (void *) fprintf(stderr, "%s\n",
+ gettext("Error: No HBA Port Configured."));
+ if (input->wwn_flag) {
+ free(input->wwn_flag);
+ input->wwn_flag = NULL;
+ }
+ return (++ret);
+ } else if (processPort == handleHBAPort) {
+ /*
+ * If enter here, we have at least one HBA port
+ * matched. For hba-port subcommand, we shall check
+ * whether there are operands mismatching.
+ */
+ for (i = 0; i < input->wwnCount; i++) {
+ if (input->wwn_flag[i] == 0) {
+ (void *) fprintf(stderr, "%s %s %s\n",
+ gettext("Error: HBA Port"),
+ input->wwn_argv[i],
+ gettext("not found."));
+ ret++;
+ }
+ }
+ }
+ }
+
+ /*
+ * For expander subcommand, we need to check if the
+ * specified sas address(ese) exist (none/partial/all).
+ */
+ if (processPort == handleExpander) {
+ if (input->wwnCount > 0) {
+ sum = 0;
+ for (i = 0; i < input->wwnCount; i++) {
+ sum += input->wwn_flag[i];
+ }
+ /*
+ * If sum is zero, it means that for all the given
+ * operands matching count is zero. So none of the
+ * specified SAS address exist actually.
+ */
+ if (sum == 0) {
+ (void *) fprintf(stderr, gettext("Error: "
+ "Matching SAS Address not found.\n"));
+ free(input->wwn_flag);
+ input->wwn_flag = NULL;
+ return (++ret);
+ }
+
+ /*
+ * If we get here, it means that some of the specified
+ * sas address exist, we will know through looping the
+ * wwn_flag array.
+ */
+ for (i = 0; i < input->wwnCount; i++) {
+ if (input->wwn_flag[i] == 0) {
+ (void *) fprintf(stderr, "%s %s %s\n",
+ gettext("Error: SAS Address"),
+ input->wwn_argv[i],
+ gettext("not found."));
+ ret++;
+ }
+ }
+ }
+ /* even if no remote port is found it is not an error. */
+ }
+ if (input->wwn_flag) {
+ free(input->wwn_flag);
+ input->wwn_flag = NULL;
+ }
+ return (ret);
+}
+
+/*
+ * This function will handle the phy stuff for hba-port subcommand.
+ *
+ * Arguments:
+ * handle - handle to hba port.
+ * portIndex - the index of hba port currently being processed.
+ * port - pointer to hba port attributes.
+ * pflag - options user specified.
+ *
+ * Return Value:
+ * 0 sucessfully processed handle
+ * >0 error has occured
+ */
+static int
+processHBAPortPhyInfo(HBA_HANDLE handle, HBA_UINT32 portIndex,
+ SMHBA_PORTATTRIBUTES *port, int pflag)
+{
+ int phyIndex = 0, err_cnt = 0;
+ HBA_UINT32 numphys = 0;
+ HBA_STATUS status = 0;
+ SMHBA_SAS_PHY phyattrs;
+
+ if (port == NULL)
+ return (++err_cnt);
+
+ numphys = port->PortSpecificAttribute.SASPort->NumberofPhys;
+ if (numphys == 0)
+ return (0);
+
+ if ((pflag & PRINT_PHY) || (pflag & PRINT_PHY_LINKSTAT))
+ (void *) fprintf(stdout, "%s\n", " Phy Information:");
+ else
+ return (0);
+
+
+ for (phyIndex = 0; phyIndex < numphys; phyIndex++) {
+ (void *) memset(&phyattrs, 0, sizeof (phyattrs));
+ status = SMHBA_GetSASPhyAttributes(
+ handle, portIndex, phyIndex, &phyattrs);
+ if (status != HBA_STATUS_OK) {
+ (void *) fprintf(stderr, "%s %d %s %s\n",
+ gettext("Failed to get SAS Phy attributes"
+ "phyIndex"), phyIndex,
+ gettext("Reason:"),
+ getHBAStatus(status));
+ err_cnt++;
+ continue;
+ }
+ if (pflag & PRINT_PHY)
+ printHBAPortPhyInfo(&phyattrs);
+ if (pflag & PRINT_PHY_LINKSTAT)
+ err_cnt += processHBAPortPhyStat(handle,
+ portIndex, phyIndex, &phyattrs, pflag);
+ }
+ return (err_cnt);
+}
+
+/*
+ * This function will handle the phy stuff for hba-port subcommand.
+ *
+ * Arguments:
+ * handle - handle to hba port.
+ * portIndex - the index of hba port currently being processed.
+ * port - pointer to hba port attributes.
+ * pflag - options user specified.
+ *
+ * Return Value:
+ * 0 sucessfully processed handle
+ * >0 error has occured
+ */
+static int
+processHBAPortPhyStat(HBA_HANDLE handle, HBA_UINT32 portIndex, int phyIndex,
+ PSMHBA_SAS_PHY phyattrs, int pflag)
+{
+ HBA_STATUS status = 0;
+ SMHBA_PHYSTATISTICS phystat;
+ SMHBA_SASPHYSTATISTICS sasphystat;
+
+ if ((pflag & PRINT_PHY) == 0) {
+ (void *) fprintf(stdout, "%s %d\n",
+ " Identifier:", phyattrs->PhyIdentifier);
+ }
+
+ (void *) memset(&phystat, 0, sizeof (phystat));
+ (void *) memset(&sasphystat, 0, sizeof (sasphystat));
+ phystat.SASPhyStatistics = &sasphystat;
+ status = SMHBA_GetPhyStatistics(handle, portIndex, phyIndex, &phystat);
+ if (status != HBA_STATUS_OK) {
+ (void *) fprintf(stdout, "%s\n",
+ " Link Error Statistics:");
+ (void *) fprintf(stderr, "%s\n",
+ gettext(" Failed to retrieve Link "
+ "Error Statistics!"));
+ return (1);
+ }
+ printHBAPortPhyStatistics(phystat.SASPhyStatistics);
+ return (0);
+}
+
+/*
+ * Check whether the pWWN exist in the WWNs list which specified by user.
+ *
+ * Arguments:
+ * input - contains all the input parameters.
+ * pWWN - pointer to the hba port sas address.
+ *
+ * Return Value:
+ * 1 true, the pWWN exist in the sas address list specified.
+ * 0 false.
+ */
+static int
+isPortWWNInArgv(inputArg_t *input, PHBA_WWN pWWN)
+{
+ int port_wwn_counter = 0;
+ int portfound = 0;
+ uint64_t hbaWWN;
+
+ /* list only ports given in wwn_argv */
+ for (port_wwn_counter = 0;
+ port_wwn_counter < input->wwnCount;
+ port_wwn_counter++) {
+ hbaWWN = strtoull(input->wwn_argv[port_wwn_counter], NULL,
+ 16);
+ if (hbaWWN == 0 && errno != 0)
+ continue;
+ if (wwnConversion(pWWN->wwn) == hbaWWN) {
+ if (input->wwn_flag) {
+ input->wwn_flag[port_wwn_counter]++;
+ }
+ portfound = 1;
+ }
+ }
+ return (portfound);
+}
+
+/*
+ * Check whether the string value exists in the input list,
+ * which specified by user.
+ *
+ * Arguments:
+ * input - contains all the input parameters.
+ * stringName - could be hba adapter name
+ * hba-port name.
+ *
+ * Return Value:
+ * 1 true, the HBA exists in the list specified.
+ * 0 false.
+ */
+static int
+isStringInArgv(inputArg_t *input, const char *stringName)
+{
+ int counter = 0;
+ int found = 0;
+
+ /* list only hba(s) given in wwn_argv */
+ for (counter = 0;
+ counter < input->wwnCount;
+ counter++) {
+ if (strcmp(input->wwn_argv[counter],
+ stringName) == 0) {
+ if (input->wwn_flag)
+ input->wwn_flag[counter]++;
+ found = 1;
+ }
+ }
+ return (found);
+}
+
+/*
+ * Callback function for hba subcommand.
+ *
+ * Arguments:
+ * attrs - pointer to adapter attributes currently being processed.
+ * input - contains all the input parameters.
+ * numberOfPorts - number of ports of this HBA.
+ *
+ * Return Value:
+ * matching number
+ */
+static int handleHBA(SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input,
+ int numberOfPorts, const char *adapterName)
+{
+ int matchingHBA = 1;
+
+ if (input->wwnCount == 0) {
+ printHBAInfo(attrs, input->pflag, numberOfPorts, adapterName);
+ } else {
+ if (isStringInArgv(input, adapterName)) {
+ printHBAInfo(attrs,
+ input->pflag, numberOfPorts, adapterName);
+ } else {
+ matchingHBA = 0;
+ }
+ }
+
+ return (matchingHBA);
+}
+
+/*
+ * Callback function for hba-port subcommand.
+ *
+ * Arguments:
+ * handle - handle to hba port.
+ * portIndex - the index of hba port currently being processed.
+ * port - pointer to hba port attributes.
+ * attrs - pointer to adapter attributes currently being processed.
+ * input - contains all the input parameters.
+ *
+ * Return Value:
+ * 0 sucessfully processed handle
+ * >0 error has occured
+ */
+/*ARGSUSED*/
+static int handleHBAPort(HBA_HANDLE handle, char *adapterName,
+ HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
+ SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input)
+{
+ int ret = 0;
+ printHBAPortInfo(port, attrs, input->pflag);
+ ret = processHBAPortPhyInfo(handle, portIndex, port, input->pflag);
+ return (ret);
+}
+
+/*
+ * Callback function for expander subcommand.
+ *
+ * Arguments:
+ * handle - handle to hba port.
+ * portIndex - the index of hba port currently being processed.
+ * port - pointer to hba port attributes.
+ * attrs - pointer to adapter attributes currently being processed.
+ * input - contains all the input parameters.
+ *
+ * Return Value:
+ * 0 sucessfully processed handle
+ * >0 error has occured
+ */
+/*ARGSUSED*/
+static int handleExpander(HBA_HANDLE handle, char *adapterName,
+ HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
+ SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input)
+{
+ SMHBA_PORTATTRIBUTES attr;
+ SMHBA_SAS_PORT sasport;
+ HBA_STATUS status;
+ int ret = 0;
+ int i, numberOfRP;
+ rp_tree_t *rpnode;
+ rp_tree_t *rproot = NULL;
+ rp_tree_t *unsolved_head = NULL;
+ rp_tree_t *unsolved_tail = NULL;
+ rp_tree_t *unsolved_sentinel = NULL;
+ int printPort = 0;
+ int numberOfEXP = 0;
+ int unsolved_inserted = 0;
+ int unsolved_left = 0;
+ int disco_port_fail = 0;
+ boolean_t firstPrinted = B_FALSE;
+
+ (void *) memset(&attr, 0, sizeof (attr));
+ (void *) memset(&sasport, 0, sizeof (sasport));
+ attr.PortSpecificAttribute.SASPort = &sasport;
+
+ /*
+ * Retrive all expander device from this hba port first.
+ */
+ if ((numberOfRP = port->PortSpecificAttribute.SASPort->
+ NumberofDiscoveredPorts) == 0) {
+ /* no remote port. just return 0. */
+ return (ret);
+ }
+
+ for (i = 0; i < numberOfRP; i++) {
+ rpnode = calloc(1, sizeof (rp_tree_t));
+ rpnode->portattr.PortSpecificAttribute.SASPort =
+ &rpnode->sasattr;
+ status = SMHBA_GetDiscoveredPortAttributes(handle,
+ portIndex, i, &rpnode->portattr);
+ if (status != HBA_STATUS_OK) {
+ disco_port_fail++;
+ free(rpnode);
+ ret++;
+ continue;
+ }
+
+ if (rpnode->portattr.PortType == HBA_PORTTYPE_SASEXPANDER) {
+ numberOfEXP++;
+ }
+ /*
+ * We will try to insert this expander device and target
+ * ports into the topology tree. If we failed, we can chain
+ * them together and try again when we have all the
+ * discovered port information in hands.
+ */
+ if (rproot == NULL && memcmp(port->
+ PortSpecificAttribute.SASPort->LocalSASAddress.wwn,
+ rpnode->sasattr.AttachedSASAddress.wwn,
+ sizeof (HBA_WWN)) == 0) {
+ /*
+ * The root node of tree should
+ * be set up first.
+ */
+ rproot = rpnode;
+ } else {
+ /*
+ * If we can not set up the root node of
+ * the tree or we failed to insert
+ * the disocvered port node, queue it up then.
+ */
+ if (rproot == NULL ||
+ sas_rp_tree_insert(&rproot, rpnode) != 0) {
+ if (unsolved_head == NULL) {
+ unsolved_head = rpnode;
+ unsolved_tail = rpnode;
+ } else {
+ rpnode->sibling = unsolved_head;
+ unsolved_head = rpnode;
+ }
+ }
+ }
+ }
+
+ if (disco_port_fail) {
+ (void *) fprintf(stderr, "%s %d %s %s\n",
+ gettext("Error: Failed to get attributes for"),
+ disco_port_fail,
+ gettext("connected ports of HBA port"),
+ port->OSDeviceName);
+ }
+
+ /* no expander found. No need further processing. */
+ if (numberOfEXP == 0) {
+ while (unsolved_head) {
+ unsolved_tail =
+ unsolved_head->sibling;
+ free(unsolved_head);
+ unsolved_head = unsolved_tail;
+ }
+ if (rproot) sas_rp_tree_free(rproot);
+ return (ret);
+ }
+
+ /*
+ * When we're here, we should already have all information,
+ * now we try again to insert them into the topology tree.
+ * unsolved_head is the pointer which point to the head of
+ * unsolved rpnode linked list.
+ * unsolved_tail is the pointer which point to the tail of
+ * unsolved rpnode linked list.
+ * unsolved_sentinel is for insertion failure detection.
+ * When we're trying to insert the rpnodes from unsolved
+ * linked list, it may happen that some of the rpnodes can
+ * not be inserted no matter how many times we loop through
+ * this linked list. So we use unsolved_sentinel to identify
+ * the tail of last round of scanning, and unsolved_inserted
+ * which is a counter will be used to count how many rpnodes
+ * have been inserted from last round, if it is zero, which
+ * means that we can not insert rpnodes into rptree any more,
+ * and we should stop and deallocate the memory they occupied.
+ */
+ unsolved_sentinel = unsolved_tail;
+ while (unsolved_head) {
+ rpnode = unsolved_head;
+ unsolved_head = unsolved_head->sibling;
+ if (unsolved_head == NULL)
+ unsolved_tail = NULL;
+ rpnode->sibling = NULL;
+ if (sas_rp_tree_insert(&rproot, rpnode) != 0) {
+ unsolved_tail->sibling = rpnode;
+ unsolved_tail = rpnode;
+ if (rpnode == unsolved_sentinel) {
+ /*
+ * We just scanned one round for the
+ * unsolved list. Check to see whether we
+ * have nodes inserted, if none, we should
+ * break in case of an indefinite loop.
+ */
+ if (unsolved_inserted == 0) {
+ /*
+ * Indicate there is unhandled node.
+ * Chain free the whole unsolved
+ * list here.
+ */
+ unsolved_left++;
+ break;
+ } else {
+ unsolved_inserted = 0;
+ unsolved_sentinel = unsolved_tail;
+ }
+ }
+ } else {
+ /*
+ * We just inserted one rpnode, increment the
+ * unsolved_inserted counter. We will utilize this
+ * counter to detect an indefinite insertion loop.
+ */
+ unsolved_inserted++;
+ }
+ }
+
+ /* check if there is left out discovered ports. */
+ if (unsolved_left) {
+ ret++;
+ (void *) fprintf(stderr, "%s %s\n",
+ gettext("Error: Failed to establish expander topology on"),
+ port->OSDeviceName);
+ (void *) fprintf(stderr, "%s\n",
+ gettext(" Folowing port(s) are unresolved."));
+ while (unsolved_head) {
+ unsolved_tail =
+ unsolved_head->sibling;
+ (void *) fprintf(stderr, "%s%016llx ",
+ firstPrinted ? "" : "\t",
+ wwnConversion(unsolved_head->sasattr.
+ LocalSASAddress.wwn));
+ if (firstPrinted == B_FALSE) firstPrinted = B_TRUE;
+ free(unsolved_head);
+ unsolved_head = unsolved_tail;
+ }
+ (void *) fprintf(stderr, "\n");
+ /* still print what we have */
+ ret += sas_rp_tree_print(handle, adapterName, portIndex,
+ port, rproot, input, 2 * TABLEN, &printPort);
+ } else {
+ ret += sas_rp_tree_print(handle, adapterName, portIndex,
+ port, rproot, input, 2 * TABLEN, &printPort);
+ }
+
+ if (rproot) sas_rp_tree_free(rproot);
+
+ return (ret);
+}
+
+/*
+ * Callback function for target-port subcommand.
+ *
+ * Arguments:
+ * handle - handle to hba port.
+ * portIndex - the index of hba port currently being processed.
+ * port - pointer to hba port attributes.
+ * attrs - pointer to adapter attributes currently being processed.
+ * input - contains all the input parameters.
+ *
+ * Return Value:
+ * 0 sucessfully processed handle
+ * >0 error has occured
+ */
+/*ARGSUSED*/
+static int handleTargetPort(HBA_HANDLE handle, char *adapterName,
+ HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
+ SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input)
+{
+ HBA_STATUS status;
+ SMHBA_PORTATTRIBUTES targetattr;
+ SMHBA_SAS_PORT sasattr;
+ int i;
+ int ret = 0;
+ int disco_port_fail = 0;
+
+ targetattr.PortSpecificAttribute.SASPort = &sasattr;
+
+ for (i = 0; i < port->PortSpecificAttribute.SASPort->
+ NumberofDiscoveredPorts; i++) {
+ status = SMHBA_GetDiscoveredPortAttributes(handle,
+ portIndex, i, &targetattr);
+ if (status != HBA_STATUS_OK) {
+ disco_port_fail++;
+ } else {
+ /* skip expander device */
+ if (targetattr.PortType != HBA_PORTTYPE_SASEXPANDER) {
+ ret += searchTargetPort(handle, portIndex, port,
+ &targetattr, &sasattr, input->pflag);
+ }
+ }
+ }
+
+ if (disco_port_fail) {
+ ret++;
+ (void *) fprintf(stderr, "%s %d %s %s\n",
+ gettext("Error: Failed to get attributes for"),
+ disco_port_fail,
+ gettext("connected ports of HBA port"),
+ port->OSDeviceName);
+ }
+ return (ret);
+}
+
+/*
+ * ****************************************************************************
+ *
+ * compareLUName -
+ * compare names directly and also check if disk namees match with
+ * different slice number or /devices path are speicified and matches.
+ *
+ * cmdArg - first string to compare
+ * osName - os name from attributes
+ *
+ * returns B_TRUE if the strings match either directly or via devid
+ * B_FALSE otherwise
+ *
+ * ****************************************************************************
+ */
+static boolean_t
+compareLUName(char *cmdArg, char *osName)
+{
+
+ boolean_t isSame = B_FALSE;
+ char dev1[MAXPATHLEN], dev2[MAXPATHLEN];
+ char *ch1, *ch2;
+
+ if (strcmp(cmdArg, osName) == 0) {
+ isSame = B_TRUE;
+ } else {
+ /* user input didn't match, try to match the core of args. */
+ (void) strlcpy(dev1, cmdArg, MAXPATHLEN);
+ (void) strlcpy(dev2, osName, MAXPATHLEN);
+ /* is this /devices path */
+ if (((ch1 = strrchr(dev1, ',')) != NULL) &&
+ ((ch2 = strrchr(dev2, ',')) != NULL)) {
+ *ch1 = *ch2 = '\0';
+ if (strcmp(dev1, dev2) == 0) {
+ isSame = B_TRUE;
+ }
+ /* is this a /dev link */
+ } else if ((strncmp(dev1, "/dev/", 5) == 0) &&
+ (strncmp(dev2, "/dev/", 5) == 0)) {
+ if ((strstr(dev1, "dsk") != NULL) &&
+ ((strstr(dev2, "dsk") != NULL))) {
+ /* if it is disk link */
+ if (((ch1 = strrchr(dev1, 's')) != NULL) &&
+ ((ch2 = strrchr(dev2, 's')) != NULL)) {
+ *ch1 = *ch2 = '\0';
+ if (strcmp(dev1, dev2) == 0) {
+ isSame = B_TRUE;
+ }
+ }
+ } else {
+ /* other dev links */
+ if (strcmp(dev1, dev2) == 0) {
+ isSame = B_TRUE;
+ }
+ }
+ }
+ } /* compare */
+
+ return (isSame);
+}
+
+/*
+ * Process logical-unit(lu) subcommand.
+ *
+ * Arguments:
+ * luCount - number of OS device name(s) specified by user.
+ * luArgv - array of OS device name(s) specified by user.
+ * options - all the options specified by user.
+ *
+ * Return Value:
+ * 0 sucessfully processed handle
+ * >0 error has occured
+ */
+int
+sas_util_list_logicalunit(int luCount, char **luArgv, cmdOptions_t *options)
+{
+ HBA_STATUS status;
+ int processHBA_flags = 0;
+ int lu;
+ boolean_t pathFound;
+ boolean_t verbose;
+ inputArg_t input;
+ discoveredDevice *LUListWalk = NULL;
+ int err_cnt = 0;
+
+ for (; options->optval; options++) {
+ if (options->optval == 'v') {
+ processHBA_flags |= PRINT_VERBOSE;
+ }
+ }
+
+ /* HBA_LoadLibrary() */
+ if ((status = HBA_LoadLibrary()) != HBA_STATUS_OK) {
+ (void *) fprintf(stderr, "%s %s\n",
+ gettext("Failed to load SM-HBA libraries."
+ "Reason:"), getHBAStatus(status));
+ err_cnt++;
+ return (err_cnt);
+ }
+
+ (void *) memset(&input, 0, sizeof (input));
+ input.pflag = processHBA_flags;
+ input.wwnCount = luCount;
+ input.wwn_argv = luArgv;
+
+ err_cnt += processHBA(&input, handleLogicalUnit);
+ verbose = (input.pflag & PRINT_VERBOSE) ? B_TRUE : B_FALSE;
+
+ if (luCount == 0) {
+ /* list all paths */
+ for (LUListWalk = LUList; LUListWalk != NULL;
+ LUListWalk = LUListWalk->next) {
+ err_cnt += printOSDeviceNameInfo(LUListWalk, verbose);
+ }
+ } else {
+ /*
+ * When operands provided, we should set the error code
+ * only if there are issues related with the operands.
+ */
+ err_cnt = 0;
+ /*
+ * list any paths not found first
+ * this gives the user cleaner output
+ */
+ for (lu = 0; lu < luCount; lu++) {
+ for (LUListWalk = LUList, pathFound = B_FALSE;
+ LUListWalk != NULL;
+ LUListWalk = LUListWalk->next) {
+ if (compareLUName(luArgv[lu],
+ LUListWalk->OSDeviceName)) {
+ pathFound = B_TRUE;
+ break;
+ }
+ }
+ if (pathFound == B_FALSE) {
+ (void *) fprintf(stderr,
+ "Error: Logical Unit %s Not Found \n",
+ luArgv[lu]);
+ err_cnt++;
+ }
+ }
+ /* list all paths requested in order requested */
+ for (lu = 0; lu < luCount; lu++) {
+ for (LUListWalk = LUList; LUListWalk != NULL;
+ LUListWalk = LUListWalk->next) {
+ if (compareLUName(luArgv[lu],
+ LUListWalk->OSDeviceName)) {
+ err_cnt += printOSDeviceNameInfo(
+ LUListWalk,
+ verbose);
+ }
+ }
+ }
+ }
+ (void) HBA_FreeLibrary();
+ return (err_cnt);
+}
+
+/*
+ * Callback function for logical-unit(lu) subcommand.
+ *
+ * Arguments:
+ * handle - handle to hba port.
+ * portIndex - the index of hba port currently being processed.
+ * port - pointer to hba port attributes.
+ * attrs - pointer to adapter attributes currently being processed.
+ * input - contains all the input parameters.
+ *
+ * Return Value:
+ * 0 sucessfully processed handle
+ * >0 error has occured
+ */
+/*ARGSUSED*/
+static int handleLogicalUnit(HBA_HANDLE handle, char *adapterName,
+ HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
+ SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input)
+{
+ HBA_STATUS status;
+ SMHBA_TARGETMAPPING *map;
+ HBA_WWN hbaPortWWN, domainPortWWN;
+ char *portName = NULL;
+ int numentries;
+ int count = 0;
+ int ret = 0;
+
+ hbaPortWWN = port->PortSpecificAttribute.SASPort->LocalSASAddress;
+ portName = port->OSDeviceName;
+
+ status = get_domainPort(handle, portIndex, port, &domainPortWWN);
+ switch (status) {
+ case HBA_STATUS_OK:
+ break;
+ case HBA_STATUS_ERROR_NOT_SUPPORTED:
+ /* don't increase error flag for no phy configuration */
+ return (ret);
+ case HBA_STATUS_ERROR:
+ default:
+ return (++ret);
+ }
+
+ if ((map = calloc(1, sizeof (*map))) == NULL) {
+ (void *) fprintf(stderr, "%s\n",
+ gettext("No enough memory on heap."));
+ return (++ret);
+ }
+ map->NumberOfEntries = 1;
+
+ /*
+ * First, we need to get the target mapping data from this hba
+ * port.
+ */
+ status = SMHBA_GetTargetMapping(handle,
+ hbaPortWWN, domainPortWWN, map);
+
+ if (status == HBA_STATUS_ERROR_MORE_DATA) {
+ numentries = map->NumberOfEntries;
+ free(map);
+ map = calloc(1, sizeof (HBA_UINT32) +
+ (numentries * sizeof (SMHBA_SCSIENTRY)));
+ if (map == NULL) {
+ (void *) fprintf(stderr, "%s\n",
+ gettext("No enough memory on heap."));
+ return (++ret);
+ }
+ map->NumberOfEntries = numentries;
+ status = SMHBA_GetTargetMapping(handle,
+ hbaPortWWN, domainPortWWN, map);
+ }
+
+ if (status != HBA_STATUS_OK) {
+ (void *) fprintf(stderr, "%s %016llx %s %s\n",
+ gettext("Error: Failed to get SCSI mapping data for "
+ "the HBA port"), wwnConversion(hbaPortWWN.wwn),
+ gettext("Reason:"),
+ getHBAStatus(status));
+ free(map);
+ return (++ret);
+ }
+
+ /*
+ * By iterating each entry of the targetmapping data, we will
+ * construct a global list of logical unit.
+ */
+ for (count = 0; count < map->NumberOfEntries; count++) {
+ ret += searchDevice(
+ &(map->entry[count]), handle, hbaPortWWN, domainPortWWN,
+ portName, input->pflag);
+ }
+ free(map);
+ return (ret);
+}
+
+/*
+ * Search the matching targetmapping data for given target port and SAM LUN
+ * and return target mapping data if found.
+ *
+ * Arguments:
+ * handle - handle to hba port.
+ * portIndex - hba port index
+ * port - hba port attributes.
+ * targetportWWN - target port SAS address.
+ * domainportWWN - domain port SAS address.
+ * domainportttr - target port SAS attributes.
+ * samLUN - samLUN from report LUNs data.
+ * data - matching target mapping data.
+ *
+ * Return Value:
+ * 0 sucessfully processed handle
+ * >0 error has occured
+ */
+static int
+searchTargetPortMappingData(HBA_HANDLE handle, HBA_UINT32 portIndex,
+ SMHBA_PORTATTRIBUTES *port, SMHBA_SAS_PORT *sasattr,
+ struct targetPortConfig *configData)
+{
+ int ret = 0;
+ HBA_STATUS status;
+ SMHBA_TARGETMAPPING *map = NULL;
+ HBA_WWN hbaPortWWN, domainPortWWN;
+ int numentries, count;
+ targetPortMappingData_t *TPMapData;
+ struct scsi_inquiry inq;
+ struct scsi_extended_sense sense;
+ HBA_UINT32 responseSize, senseSize = 0;
+ uchar_t rawLUNs[DEFAULT_LUN_LENGTH], *lun_string;
+ HBA_UINT8 scsiStatus;
+ rep_luns_rsp_t *lun_resp;
+ int lunNum, numberOfLun, lunCount;
+ uint32_t lunlength, tmp_lunlength;
+ uint64_t sasLUN;
+ SMHBA_SCSILUN smhbaLUN;
+
+ hbaPortWWN = port->PortSpecificAttribute.SASPort->
+ LocalSASAddress;
+
+ status = get_domainPort(handle, portIndex, port, &domainPortWWN);
+ if (status == HBA_STATUS_OK) {
+ if ((map = calloc(1, sizeof (*map))) == NULL) {
+ (void *) fprintf(stderr, "%s\n",
+ gettext("No enough memory on heap."));
+ return (++ret);
+ }
+ map->NumberOfEntries = 1;
+
+ status = SMHBA_GetTargetMapping(handle, hbaPortWWN,
+ domainPortWWN, map);
+
+ if (status == HBA_STATUS_ERROR_MORE_DATA) {
+ numentries = map->NumberOfEntries;
+ free(map);
+ map = calloc(1, sizeof (HBA_UINT32) +
+ (numentries * sizeof (SMHBA_SCSIENTRY)));
+ if (map == NULL) {
+ (void *) fprintf(stderr, "%s\n",
+ gettext("No enough memory on heap."));
+ return (++ret);
+ }
+ map->NumberOfEntries = numentries;
+ status = SMHBA_GetTargetMapping(handle,
+ hbaPortWWN, domainPortWWN, map);
+ }
+
+ if (status != HBA_STATUS_OK) {
+ /* continue to build mapping data based SCSI info */
+ ret++;
+ free(map);
+ map = NULL;
+ }
+ }
+
+ /*
+ * Get report lun data.
+ */
+ responseSize = DEFAULT_LUN_LENGTH;
+ senseSize = sizeof (struct scsi_extended_sense);
+ (void) memset(&sense, 0, sizeof (sense));
+ status = SMHBA_ScsiReportLUNs(
+ handle,
+ hbaPortWWN,
+ sasattr->LocalSASAddress,
+ domainPortWWN,
+ (void *)rawLUNs,
+ &responseSize,
+ &scsiStatus,
+ (void *) &sense, &senseSize);
+
+ /*
+ * if HBA_STATUS_ERROR_NOT_A_TARGET is return, we can assume this is
+ * a remote HBA and move on
+ */
+ if (status != HBA_STATUS_OK) {
+ configData->reportLUNsFailed = B_TRUE;
+ if (map != NULL) {
+ /*
+ * Let's search mapping data and indicate that Report
+ * LUNs failed.
+ */
+ for (count = 0; count < map->NumberOfEntries; count++) {
+ if (memcmp(map->entry[count].PortLun.
+ PortWWN.wwn, sasattr->LocalSASAddress.wwn,
+ sizeof (HBA_WWN)) == 0) {
+ /* allocate mapping data for each LUN */
+ TPMapData = calloc(1,
+ sizeof (targetPortMappingData_t));
+ if (TPMapData == NULL) {
+ (void *) fprintf(stderr, "%s\n",
+ gettext("No enough "
+ "memory."));
+ free(map);
+ return (++ret);
+ }
+ TPMapData->mappingExist = B_TRUE;
+ TPMapData->osLUN =
+ map->entry[count].ScsiId.ScsiOSLun;
+ (void) strlcpy(TPMapData->osDeviceName,
+ map->entry[count].ScsiId.
+ OSDeviceName,
+ sizeof (TPMapData->osDeviceName));
+ TPMapData->inq_vid[0] = '\0';
+ TPMapData->inq_pid[0] = '\0';
+ TPMapData->inq_dtype = DTYPE_UNKNOWN;
+ if (configData->map == NULL) {
+ configData->map = TPMapData;
+ } else {
+ TPMapData->next =
+ configData->map->next;
+ configData->map = TPMapData;
+ }
+ }
+ }
+ }
+ (void) free(map);
+ return (++ret);
+ }
+ lun_resp = (rep_luns_rsp_t *)((void *)rawLUNs);
+ (void) memcpy(&tmp_lunlength, &(lun_resp->length),
+ sizeof (tmp_lunlength));
+ lunlength = ntohl(tmp_lunlength);
+ (void) memcpy(&numberOfLun, &lunlength, sizeof (numberOfLun));
+ for (lunCount = 0; lunCount < (numberOfLun / 8); lunCount++) {
+ /* allocate mapping data for each LUN */
+ TPMapData = calloc(1,
+ sizeof (targetPortMappingData_t));
+ if (TPMapData == NULL) {
+ (void *) fprintf(stderr, "%s\n",
+ gettext("No enough memory."));
+ free(map);
+ return (++ret);
+ }
+
+ (void) memcpy(&TPMapData->reportLUN, lun_resp->
+ lun[lunCount].val, sizeof (SMHBA_SCSILUN));
+
+ /*
+ * now issue standard inquiry to get Vendor
+ * and product information
+ */
+ responseSize = sizeof (struct scsi_inquiry);
+ senseSize = sizeof (struct scsi_extended_sense);
+ (void) memset(&inq, 0, sizeof (struct scsi_inquiry));
+ (void) memset(&sense, 0, sizeof (sense));
+ sasLUN = ntohll(wwnConversion(lun_resp->lun[lunCount].val));
+ (void) memcpy(&smhbaLUN, &sasLUN, sizeof (SMHBA_SCSILUN));
+ status = SMHBA_ScsiInquiry(
+ handle,
+ hbaPortWWN,
+ sasattr->LocalSASAddress,
+ domainPortWWN,
+ smhbaLUN,
+ 0,
+ 0,
+ (void *) &inq, &responseSize,
+ &scsiStatus,
+ (void *) &sense, &senseSize);
+ if (status != HBA_STATUS_OK) {
+ TPMapData->inq_vid[0] = '\0';
+ TPMapData->inq_pid[0] = '\0';
+ TPMapData->inq_dtype = DTYPE_UNKNOWN;
+ /* indicate that inquiry for this lun is failed */
+ TPMapData->inquiryFailed = B_TRUE;
+ } else {
+ (void *) memcpy(TPMapData->inq_vid, inq.inq_vid,
+ sizeof (TPMapData->inq_vid));
+ (void *) memcpy(TPMapData->inq_pid, inq.inq_pid,
+ sizeof (TPMapData->inq_pid));
+ TPMapData->inq_dtype = inq.inq_dtype;
+ }
+
+ if (map != NULL) {
+ for (count = 0; count < map->NumberOfEntries; count++) {
+ if ((memcmp(map->entry[count].PortLun.
+ PortWWN.wwn, sasattr->LocalSASAddress.wwn,
+ sizeof (HBA_WWN)) == 0) &&
+ (memcmp(&(map->entry[count].PortLun.
+ TargetLun), &smhbaLUN,
+ sizeof (SMHBA_SCSILUN))
+ == 0)) {
+ TPMapData->mappingExist = B_TRUE;
+ TPMapData->osLUN =
+ map->entry[count].ScsiId.ScsiOSLun;
+ (void) strlcpy(TPMapData->osDeviceName,
+ map->entry[count].ScsiId.
+ OSDeviceName,
+ sizeof (TPMapData->osDeviceName));
+ break;
+ }
+ }
+ if (count == map->NumberOfEntries) {
+ TPMapData->osDeviceName[0] = '\0';
+ lun_string = lun_resp->lun[lunCount].val;
+ lunNum = ((lun_string[0] & 0x3F) << 8) |
+ lun_string[1];
+ TPMapData->osLUN = lunNum;
+ }
+ } else {
+ /* Not able to get any target mapping information */
+ TPMapData->osDeviceName[0] = '\0';
+ lun_string = lun_resp->lun[lunCount].val;
+ lunNum = ((lun_string[0] & 0x3F) << 8) |
+ lun_string[1];
+ TPMapData->osLUN = lunNum;
+ }
+
+ if (configData->map == NULL) {
+ configData->map = TPMapData;
+ } else {
+ TPMapData->next = configData->map->next;
+ configData->map = TPMapData;
+ }
+ }
+ free(map);
+ return (ret);
+}
+
+/*
+ * Search the discovered LUs and construct the global LU list.
+ *
+ * Arguments:
+ * handle - handle to hba port.
+ * portIndex - hba port index
+ * port - hba port attributes.
+ * targetattr - target port attributes.
+ * sasattr - target port SAS attributes.
+ * pflag - options the user specified.
+ *
+ * Return Value:
+ * 0 sucessfully processed handle
+ * >0 error has occured
+ */
+static int
+searchTargetPort(HBA_HANDLE handle, HBA_UINT32 portIndex,
+ SMHBA_PORTATTRIBUTES *port, SMHBA_PORTATTRIBUTES *targetattr,
+ SMHBA_SAS_PORT *sasattr, int pflag)
+{
+ int ret = 0;
+ HBA_WWN expander;
+ HBA_WWN domainPortWWN;
+ targetPortList_t *discoveredTP, *newTP;
+ targetPortConfig_t *TPConfig, *newConfig, *prevConfig;
+ boolean_t foundTP = B_FALSE;
+ boolean_t foundConfig = B_FALSE;
+ int status;
+ SMHBA_PORTATTRIBUTES tgtattr;
+ SMHBA_SAS_PORT tgtsasport;
+ int expanderValid = 0;
+
+ status = get_domainPort(handle, portIndex, port, &domainPortWWN);
+ switch (status) {
+ case HBA_STATUS_OK:
+ break;
+ case HBA_STATUS_ERROR_NOT_SUPPORTED:
+ /* don't increase error flag for no phy configuration */
+ return (ret);
+ case HBA_STATUS_ERROR:
+ default:
+ return (++ret);
+ }
+
+ /*
+ * First, we will iterate the already constructed target port
+ * list to see whether there is a target port already exist with
+ * matching target port SAS address.
+ */
+ for (discoveredTP = gTargetPortList; discoveredTP != NULL;
+ discoveredTP = discoveredTP->next) {
+ if (memcmp((void *)sasattr->LocalSASAddress.wwn,
+ (void *)discoveredTP->sasattr.LocalSASAddress.wwn,
+ sizeof (HBA_WWN)) == 0) {
+ /*
+ * if the target port exist and
+ * verbose is not set, just return
+ */
+ if (((pflag & PRINT_VERBOSE) == 0) &&
+ ((pflag & PRINT_TARGET_SCSI) == 0)) {
+ return (ret);
+ }
+ foundTP = B_TRUE;
+ break;
+ }
+ }
+
+ if (foundTP == B_TRUE) {
+ /*
+ * If there is a target port already exist, we should
+ * add more information on the target port to construct the
+ * whole topology.
+ * Here we will check whether the current hba port name
+ * has already been added.
+ */
+ /* first get the expander SAS address compare */
+ if (memcmp((void *)port->PortSpecificAttribute.SASPort->
+ LocalSASAddress.wwn, (void *)sasattr->
+ AttachedSASAddress.wwn, sizeof (HBA_WWN)) == 0) {
+ /* NO expander */
+ (void) memset((void *)expander.wwn, 0,
+ sizeof (HBA_WWN));
+ expanderValid = 1;
+ } else {
+ if (wwnConversion(sasattr->AttachedSASAddress.wwn)
+ != 0) {
+ /* expander exist. We should verify it. */
+ (void) memcpy((void *)expander.wwn,
+ (void *)sasattr->AttachedSASAddress.wwn,
+ sizeof (HBA_WWN));
+
+ (void *) memset(&tgtattr, 0, sizeof (tgtattr));
+ (void *) memset(&tgtsasport, 0,
+ sizeof (tgtsasport));
+ tgtattr.PortSpecificAttribute.SASPort
+ = &tgtsasport;
+ status = SMHBA_GetPortAttributesByWWN(handle,
+ sasattr->AttachedSASAddress, domainPortWWN,
+ &tgtattr);
+ if (status == HBA_STATUS_OK && tgtattr.PortType
+ == HBA_PORTTYPE_SASEXPANDER) {
+ expanderValid = 1;
+ }
+ }
+ }
+
+ for (TPConfig = discoveredTP->configEntry,
+ foundConfig = B_FALSE; TPConfig != NULL;
+ TPConfig = TPConfig->next) {
+ if ((strcmp(TPConfig->hbaPortName,
+ port->OSDeviceName) == 0) &&
+ (memcmp((void *)expander.wwn, (void *)TPConfig->
+ expanderSASAddr.wwn,
+ sizeof (HBA_WWN)) == 0)) {
+ foundConfig = B_TRUE;
+ break;
+ }
+ }
+
+ /*
+ * If we get here, it means that it is a new hba port/exapnder
+ * sas address for this discovered target port.
+ */
+ if (foundConfig == B_FALSE) {
+ newConfig = (targetPortConfig_t *)calloc(1,
+ sizeof (targetPortConfig_t));
+ if (newConfig == NULL) {
+ (void *) fprintf(stderr,
+ "%s\n", strerror(errno));
+ return (++ret);
+ }
+
+ (void) strlcpy(newConfig->hbaPortName, port->
+ OSDeviceName, sizeof (newConfig->hbaPortName));
+ (void) memcpy((void *)newConfig->expanderSASAddr.wwn,
+ (void *)expander.wwn, sizeof (HBA_WWN));
+ newConfig->expanderValid = expanderValid;
+ if (discoveredTP->configEntry == NULL) {
+ discoveredTP->configEntry = newConfig;
+ } else {
+ TPConfig = discoveredTP->configEntry;
+ prevConfig = TPConfig;
+ while (TPConfig != NULL &&
+ sas_name_comp(newConfig->hbaPortName,
+ TPConfig->hbaPortName) > 0) {
+ prevConfig = TPConfig;
+ TPConfig = TPConfig->next;
+ }
+ if (TPConfig == prevConfig) {
+ /* Should be inserted in the head. */
+ newConfig->next = TPConfig;
+ discoveredTP->configEntry = newConfig;
+ } else {
+ newConfig->next = TPConfig;
+ prevConfig->next = newConfig;
+ }
+ }
+ /* if scsi option is not set return */
+ if ((pflag & PRINT_TARGET_SCSI) == 0) {
+ return (0);
+ } else {
+ return (searchTargetPortMappingData(
+ handle, portIndex, port,
+ sasattr, newConfig));
+ }
+ }
+ } else {
+ /*
+ * Here we got a new target port which has not ever exist
+ * in our global target port list. So add it to the list.
+ * list.
+ */
+ newTP = (targetPortList_t *)calloc(1,
+ sizeof (targetPortList_t));
+
+ if (newTP == NULL) {
+ (void *) fprintf(stderr, "%s\n", strerror(errno));
+ return (++ret);
+ }
+
+ (void) memcpy((void *)&newTP->targetattr, (void *)targetattr,
+ sizeof (SMHBA_PORTATTRIBUTES));
+ (void) memcpy((void *)&newTP->sasattr, (void *)sasattr,
+ sizeof (SMHBA_SAS_PORT));
+
+ newConfig = (targetPortConfig_t *)calloc(1,
+ sizeof (targetPortConfig_t));
+
+ if (newConfig == NULL) {
+ (void *) fprintf(stderr, "%s\n", strerror(errno));
+ free(newTP);
+ return (++ret);
+ }
+
+ (void) strlcpy(newConfig->hbaPortName, port->OSDeviceName,
+ sizeof (newConfig->hbaPortName));
+ if (memcmp((void *)port->PortSpecificAttribute.SASPort->
+ LocalSASAddress.wwn, (void *)sasattr->
+ AttachedSASAddress.wwn, sizeof (HBA_WWN)) == 0) {
+ /* NO expander */
+ (void) memset((void *)newConfig->expanderSASAddr.wwn,
+ 0, sizeof (HBA_WWN));
+ } else {
+ /* expander exist. We should verify it. */
+ (void) memcpy((void *)newConfig->expanderSASAddr.wwn,
+ (void *)sasattr->AttachedSASAddress.wwn,
+ sizeof (HBA_WWN));
+
+ (void *) memset(&tgtattr, 0, sizeof (tgtattr));
+ (void *) memset(&tgtsasport, 0, sizeof (tgtsasport));
+ tgtattr.PortSpecificAttribute.SASPort = &tgtsasport;
+ status = SMHBA_GetPortAttributesByWWN(handle,
+ sasattr->AttachedSASAddress, domainPortWWN,
+ &tgtattr);
+ if (status == HBA_STATUS_OK && tgtattr.PortType ==
+ HBA_PORTTYPE_SASEXPANDER) {
+ expanderValid = 1;
+ }
+ newConfig->expanderValid = expanderValid;
+ }
+
+ newTP->configEntry = newConfig;
+
+ newTP->next = gTargetPortList; /* insert at head */
+ gTargetPortList = newTP; /* set new head */
+
+ /* if scsi option is not set return */
+ if ((pflag & PRINT_TARGET_SCSI) == 0) {
+ return (0);
+ } else {
+ return (searchTargetPortMappingData(
+ handle, portIndex, port, sasattr, newConfig));
+ }
+ }
+ return (ret);
+}
+
+/*
+ * Search the discovered LUs and construct the global LU list.
+ *
+ * Arguments:
+ * entryP - one of the target mapping data.
+ * handle - handle to hba port.
+ * hbaPortWWN - hba port sas address.
+ * domainPortWWN - domain port WWN for this sas domain.
+ * portName - HBA port OS Device Name.
+ * pflag - options the user specified.
+ *
+ * Return Value:
+ * 0 sucessfully processed handle
+ * >0 error has occured
+ */
+static int
+searchDevice(PSMHBA_SCSIENTRY entryP,
+ HBA_HANDLE handle, HBA_WWN hbaPortWWN, HBA_WWN domainPortWWN,
+ char *portName, int pflag)
+{
+ HBA_STATUS status;
+ int ret = 0;
+ discoveredDevice *discoveredLU, *newDevice;
+ portList *portElem, *newPort, *prevElem;
+ tgtPortWWNList *newTgtWWN, *TgtWWNList;
+ boolean_t foundDevice = B_FALSE;
+ boolean_t foundPort = B_FALSE;
+ struct scsi_inquiry inq;
+ HBA_UINT32 responseSize, senseSize = 0;
+ HBA_UINT8 inq_status;
+ SMHBA_SCSILUN smhbaLUN;
+ struct scsi_extended_sense sense;
+
+ /* if OSDeviceName is not set, we don't need to search */
+ if (entryP->ScsiId.OSDeviceName[0] == '\0') {
+ return (ret);
+ }
+
+ /*
+ * First, we will iterate the already constructed discovered LU
+ * list to see whether there is a LU already exist with the same OS
+ * device name as current target mapping data entry.
+ */
+ for (discoveredLU = LUList; discoveredLU != NULL;
+ discoveredLU = discoveredLU->next) {
+ if (strcmp(entryP->ScsiId.OSDeviceName,
+ discoveredLU->OSDeviceName) == 0) {
+ /*
+ * if there is existing OS Device Name and
+ * verbose is not set, just return
+ */
+ if ((pflag & PRINT_VERBOSE) == 0) {
+ return (ret);
+ }
+ foundDevice = B_TRUE;
+ break;
+ }
+ }
+
+ if (foundDevice == B_TRUE) {
+ /*
+ * If there is a discovered LU already exist, we should
+ * add more information on this LU to construct the whole
+ * topology.
+ * Here we will check whether the current hba port has
+ * already been added.
+ */
+ for (portElem = discoveredLU->HBAPortList,
+ foundPort = B_FALSE; portElem != NULL;
+ portElem = portElem->next) {
+ if (strcmp(portElem->portName,
+ portName) == 0) {
+ foundPort = B_TRUE;
+ break;
+ }
+ }
+
+ /*
+ * If we get here, it means that it is a new hba port name
+ * for this discovered LU.
+ */
+ if (foundPort == B_FALSE) {
+ newPort = (portList *)calloc(1, sizeof (portList));
+ if (newPort == NULL) {
+ (void *) fprintf(stderr,
+ "%s\n", strerror(errno));
+ return (++ret);
+ }
+ (void) strlcpy(newPort->portName, portName,
+ sizeof (newPort->portName));
+
+ portElem = discoveredLU->HBAPortList;
+ prevElem = portElem;
+ while (portElem != NULL &&
+ sas_name_comp(newPort->portName, portElem->portName)
+ > 0) {
+ prevElem = portElem;
+ portElem = portElem->next;
+ }
+ if (portElem == prevElem) {
+ /* Insert in the head of list. */
+ newPort->next = portElem;
+ discoveredLU->HBAPortList = newPort;
+ } else {
+ newPort->next = portElem;
+ prevElem->next = newPort;
+ }
+ /* add Target Port */
+ newPort->tgtPortWWN = (tgtPortWWNList *)calloc(1,
+ sizeof (tgtPortWWNList));
+ if (newPort->tgtPortWWN == NULL) {
+ (void *) fprintf(stderr,
+ "%s\n", strerror(errno));
+ return (++ret);
+ }
+ (void *) memcpy((void *)&(newPort->tgtPortWWN->portWWN),
+ (void *)&(entryP->PortLun.PortWWN),
+ sizeof (HBA_WWN));
+ /* Set LUN data */
+ newPort->tgtPortWWN->scsiOSLun =
+ entryP->ScsiId.ScsiOSLun;
+ } else {
+ /*
+ * Otherwise, we just need to add the target port
+ * sas address information.
+ */
+ for (TgtWWNList = portElem->tgtPortWWN;
+ TgtWWNList != NULL;
+ TgtWWNList = TgtWWNList->next) {
+ if (memcmp(&TgtWWNList->portWWN,
+ &entryP->PortLun.PortWWN,
+ sizeof (HBA_WWN)) == 0)
+ return (0);
+ }
+ /* add it to existing */
+ newTgtWWN = (tgtPortWWNList *)calloc(1,
+ sizeof (tgtPortWWNList));
+ if (newTgtWWN == NULL) {
+ (void *) fprintf(stderr,
+ "%s\n", strerror(errno));
+ return (++ret);
+ }
+ /* insert at head */
+ newTgtWWN->next = portElem->tgtPortWWN;
+ portElem->tgtPortWWN = newTgtWWN;
+ (void *) memcpy((void *)&(newTgtWWN->portWWN),
+ (void *)&(entryP->PortLun.PortWWN),
+ sizeof (HBA_WWN));
+ /* Set LUN data */
+ newTgtWWN->scsiOSLun =
+ entryP->ScsiId.ScsiOSLun;
+ }
+ } else {
+ /*
+ * Here we got a new discovered LU which has not ever exist
+ * in our global LU list. So add it into our global LU
+ * list.
+ */
+ newDevice = (discoveredDevice *)calloc(1,
+ sizeof (discoveredDevice));
+
+ if (newDevice == NULL) {
+ (void *) fprintf(stderr, "%s\n", strerror(errno));
+ return (++ret);
+ }
+ newDevice->next = LUList; /* insert at head */
+ LUList = newDevice; /* set new head */
+
+ /* copy device name */
+ (void *) strlcpy(newDevice->OSDeviceName,
+ entryP->ScsiId.OSDeviceName,
+ sizeof (newDevice->OSDeviceName));
+
+ /* if verbose is not set return */
+ if ((pflag & PRINT_VERBOSE) == 0) {
+ return (0);
+ }
+
+ /* copy WWN data */
+ newDevice->HBAPortList = (portList *)calloc(1,
+ sizeof (portList));
+ if (newDevice->HBAPortList == NULL) {
+ (void *) fprintf(stderr, "%s\n", strerror(errno));
+ return (++ret);
+ }
+ (void) strlcpy(newDevice->HBAPortList->portName,
+ portName, sizeof (newDevice->HBAPortList->portName));
+
+ newDevice->HBAPortList->tgtPortWWN =
+ (tgtPortWWNList *)calloc(1, sizeof (tgtPortWWNList));
+ if (newDevice->HBAPortList->tgtPortWWN == NULL) {
+ (void *) fprintf(stderr, "%s\n", strerror(errno));
+ return (++ret);
+ }
+
+ (void *) memcpy((void *)&(newDevice->HBAPortList->\
+ tgtPortWWN->portWWN),
+ (void *)&(entryP->PortLun.PortWWN),
+ sizeof (HBA_WWN));
+ newDevice->HBAPortList->tgtPortWWN->scsiOSLun =
+ entryP->ScsiId.ScsiOSLun;
+
+ responseSize = sizeof (struct scsi_inquiry);
+ senseSize = sizeof (struct scsi_extended_sense);
+ (void *) memset(&inq, 0, sizeof (struct scsi_inquiry));
+ (void *) memset(&sense, 0, sizeof (sense));
+ (void *) memcpy(&smhbaLUN, &entryP->PortLun.TargetLun,
+ sizeof (smhbaLUN));
+
+ /*
+ * Retrieve the VPD data for the newly found discovered LU.
+ */
+ status = SMHBA_ScsiInquiry(
+ handle,
+ hbaPortWWN,
+ entryP->PortLun.PortWWN,
+ domainPortWWN,
+ smhbaLUN,
+ 0,
+ 0,
+ (void *) &inq, &responseSize,
+ &inq_status,
+ (void *) &sense, &senseSize);
+
+ if (status != HBA_STATUS_OK) {
+ /* init VID/PID/dType as '\0' */
+ newDevice->VID[0] = '\0';
+ newDevice->PID[0] = '\0';
+ newDevice->dType = DTYPE_UNKNOWN;
+ /* initialize inq status */
+ newDevice->inquiryFailed = B_TRUE;
+ ret++;
+ } else {
+ (void *) memcpy(newDevice->VID, inq.inq_vid,
+ sizeof (newDevice->VID));
+ (void *) memcpy(newDevice->PID, inq.inq_pid,
+ sizeof (newDevice->PID));
+ newDevice->dType = inq.inq_dtype;
+ /* initialize inq status */
+ newDevice->inquiryFailed = B_FALSE;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * Function we use to insert a newly discovered port.
+ * Return:
+ * 0 - success
+ * >0 - failed
+ */
+static int
+sas_rp_tree_insert(rp_tree_t **rproot,
+ rp_tree_t *rpnode)
+{
+ HBA_UINT8 *wwn1, *wwn2, *wwn3;
+ rp_tree_t *node_ptr;
+ int ret = 0;
+
+ if (rproot == NULL) {
+ (void *) fprintf(stderr, "%s\n",
+ gettext("Error: NULL rproot"));
+ return (1);
+ }
+
+ if (rpnode == NULL) {
+ (void *) fprintf(stderr, "%s\n",
+ gettext("Error: NULL rpnode"));
+ return (1);
+ }
+
+ if (*rproot == NULL) {
+ *rproot = rpnode;
+ return (0);
+ }
+
+ wwn1 = (*rproot)->sasattr.LocalSASAddress.wwn;
+ wwn2 = (*rproot)->sasattr.AttachedSASAddress.wwn;
+ wwn3 = rpnode->sasattr.AttachedSASAddress.wwn;
+
+ /*
+ * If the attched sas address is equal to the local sas address,
+ * then this should be a child node of current root node.
+ */
+ if (memcmp(wwn1, wwn3, sizeof (HBA_WWN)) == 0) {
+ (void) sas_rp_tree_insert(&(*rproot)->child, rpnode);
+ rpnode->parent = *rproot;
+ } else if (memcmp(wwn2, wwn3, sizeof (HBA_WWN)) == 0) {
+ /*
+ * If the attached sas address is equal to the attached sas
+ * address of current root node, then this should be a
+ * sibling node.
+ * Insert the SAS/SATA Device at the head of sibling list.
+ */
+ if (rpnode->portattr.PortType != HBA_PORTTYPE_SASEXPANDER) {
+ rpnode->sibling = *rproot;
+ *rproot = rpnode;
+ } else {
+ /*
+ * Insert the SAS Expander at the tail of sibling
+ * list.
+ */
+ node_ptr = *rproot;
+ while (node_ptr->sibling != NULL)
+ node_ptr = node_ptr->sibling;
+ node_ptr->sibling = rpnode;
+ }
+ rpnode->parent = (*rproot)->parent;
+ } else {
+ /*
+ * If enter here, we should first try to insert the discovered
+ * port node into the child sub-tree, then try to insert to the
+ * sibling sub-trees. If we failed to insert the discovered
+ * port node, return 1. The caller will queue this node
+ * up and retry insertion later.
+ */
+ if ((*rproot)->child) {
+ ret = sas_rp_tree_insert(&(*rproot)->child, rpnode);
+ }
+ if ((*rproot)->child == NULL || ret != 0) {
+ if ((*rproot)->sibling) {
+ ret = sas_rp_tree_insert(&(*rproot)->sibling,
+ rpnode);
+ } else
+ ret = 1;
+ }
+ return (ret);
+ }
+ return (0);
+}
+
+/*
+ * Function which will print out the whole disocvered port topology.
+ * Here we use the Preorder Traversal algorithm.
+ * The indentation rules are:
+ * 1 * TABLEN - for attributes
+ * 2 * TABLEN - for next tier target port/expander
+ */
+static int
+sas_rp_tree_print(HBA_HANDLE handle, char *adapterName,
+ HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
+ rp_tree_t *rpnode, inputArg_t *input,
+ int gident, int *printPort)
+{
+ int ret = 0, lident;
+
+ if (rpnode == NULL)
+ return (ret);
+ lident = gident;
+
+ /*
+ * We assume that all the nodes are disocvered ports(sas device or
+ * expander).
+ */
+ if (input->wwnCount > 0) {
+ /* Adjust local indentation if a discovered port specified. */
+ lident = 2 * TABLEN;
+ /*
+ * Check whether current node match one of the specified
+ * SAS addresses.
+ */
+ if ((rpnode->portattr.PortType != HBA_PORTTYPE_SASEXPANDER) ||
+ !isPortWWNInArgv(input,
+ &rpnode->sasattr.LocalSASAddress)) {
+ /*
+ * Step down to child tree first.
+ */
+ ret += sas_rp_tree_print(handle, adapterName,
+ portIndex, port, rpnode->child, input,
+ gident + 2 * TABLEN, printPort);
+ /*
+ * Then check the sibling tree.
+ */
+ ret += sas_rp_tree_print(handle, adapterName,
+ portIndex, port, rpnode->sibling, input,
+ gident, printPort);
+ return (ret);
+ }
+ }
+
+ if ((rpnode->portattr.PortType == HBA_PORTTYPE_SASEXPANDER) ||
+ (input->pflag & PRINT_TARGET_PORT)) {
+ /*
+ * We should print the header(HBA Name + HBA Port Name)
+ * on-demand. It means that, if we have expander device
+ * address specified on the command line, we should print
+ * the header once we find a matching one. Or we will
+ * print the header from the beginning of the output.
+ */
+ if (g_printHBA == 0) {
+ (void *) fprintf(stdout, "%s %s\n",
+ "HBA Name:", adapterName);
+ g_printHBA = 1;
+ }
+
+ if (*printPort == 0) {
+ (void *) fprintf(stdout, "%s%s %s\n",
+ getIndentSpaces(TABLEN),
+ "HBA Port Name:", port->OSDeviceName);
+ *printPort = 1;
+ }
+ ret += sas_print_rpnode(input, rpnode, lident, gident);
+ }
+
+ /*
+ * If operands provided with "-t" option specified, we will print
+ * the immediate child nodes information under the expander.
+ */
+ if (input->pflag & PRINT_TARGET_PORT) {
+ /* no operand. ignore the option. */
+ if (input->wwnCount > 0) {
+ if (rpnode->portattr.PortType ==
+ HBA_PORTTYPE_SASEXPANDER) {
+ ret += sas_rp_tree_print_desc(handle,
+ portIndex, port, rpnode->child,
+ input,
+ lident + 2 * TABLEN,
+ gident + 2 * TABLEN);
+ }
+ }
+ }
+
+ /*
+ * Here we use DFS(Depth First Search) algorithm to traverse the
+ * whole tree.
+ */
+ ret += sas_rp_tree_print(handle, adapterName,
+ portIndex, port, rpnode->child, input,
+ gident + 2 * TABLEN, printPort);
+ ret += sas_rp_tree_print(handle, adapterName,
+ portIndex, port, rpnode->sibling, input,
+ gident, printPort);
+ return (ret);
+}
+
+/*
+ * Function which will destroy the whole discovered port tree.
+ * Here we use the Postorder Traversal algorithm.
+ */
+static void sas_rp_tree_free(rp_tree_t *rproot)
+{
+ tgt_mapping *cur, *next;
+
+ if (rproot == NULL)
+ return;
+
+ /*
+ * Free child tree first.
+ */
+ if (rproot->child) {
+ sas_rp_tree_free(rproot->child);
+ }
+
+ /*
+ * Free sibling trees then.
+ */
+ if (rproot->sibling) {
+ sas_rp_tree_free(rproot->sibling);
+ }
+
+ /*
+ * Free root node at last.
+ */
+ cur = rproot->first_entry;
+ while (cur != NULL) {
+ next = cur->next;
+ free(cur);
+ cur = next;
+ }
+ free(rproot);
+}
+
+/*
+ * Function used to print out all the descendant nodes.
+ * handle - handle to HBA.
+ * port - port attributes of current HBA port.
+ * desc - the root node of a subtree which will be processed.
+ * input - input argument.
+ * lident - local indentation for shifting indentation.
+ * gident - global indentation, can also be used to obtain Tier number.
+ */
+/*ARGSUSED*/
+static int
+sas_rp_tree_print_desc(HBA_HANDLE handle, HBA_UINT32 portIndex,
+ SMHBA_PORTATTRIBUTES *port, rp_tree_t *desc,
+ inputArg_t *input, int lident, int gident)
+{
+ int ret = 0;
+ rp_tree_t *rp_node;
+
+ if (desc == NULL)
+ return (ret);
+ /*
+ * Walk through the subtree of desc by Pre-Order Traversal Algo.
+ */
+ for (rp_node = desc; rp_node != NULL; rp_node = rp_node->sibling) {
+ ret += sas_print_rpnode(input, rp_node, lident, gident);
+ }
+
+ return (ret);
+}
+
+/*
+ * Function used to print the information of specified SAS address.
+ * handle - handle to a HBA.
+ * port - port attributes of a HBA port.
+ * rpnode - discovered port which will be processed.
+ * lident - local indentation used for shifting indentation.
+ * gident - global indentation used for calculating "Tier" number.
+ */
+static int
+sas_print_rpnode(inputArg_t *input,
+ rp_tree_t *rpnode, int lident, int gident)
+{
+ int ret = 0;
+
+ if (rpnode->portattr.PortType == HBA_PORTTYPE_SASEXPANDER) {
+ (void *) fprintf(stdout, "%s%s(Tier %d): %016llx\n",
+ getIndentSpaces(lident),
+ "Expander SAS Address",
+ gident / (2 * TABLEN),
+ wwnConversion(rpnode->sasattr.LocalSASAddress.wwn));
+ } else {
+ (void *) fprintf(stdout, "%s%s %016llx\n",
+ getIndentSpaces(lident),
+ "Target Port SAS Address:",
+ wwnConversion(rpnode->sasattr.LocalSASAddress.wwn));
+ }
+ if (input->pflag & PRINT_VERBOSE) {
+ if (rpnode->portattr.PortType != HBA_PORTTYPE_SASEXPANDER) {
+ (void *) fprintf(stdout, "%s%s %s\n",
+ getIndentSpaces(TABLEN + lident),
+ "Type:",
+ getStateString(rpnode->portattr.PortType,
+ porttype_string));
+ } else {
+ (void *) fprintf(stdout, "%s%s %s\n",
+ getIndentSpaces(TABLEN + lident),
+ "OS Device Name:",
+ rpnode->portattr.OSDeviceName);
+ (void *) fprintf(stdout, "%s%s %s\n",
+ getIndentSpaces(TABLEN + lident),
+ "State: ",
+ getStateString(rpnode->portattr.PortState,
+ portstate_string));
+ }
+ }
+ rpnode->printed = 1;
+ return (ret);
+}
+
+/*
+ * Function used to get the correct domainPortWWN as needed by some of the
+ * SMHBA APIs.
+ * handle - handle to a HBA.
+ * portIndex - index to locate the port.
+ * port - pointer to the structure holding port attributes.
+ * pdomainPort - pointer to the buffer holding domainPortWWN.
+ */
+HBA_STATUS
+get_domainPort(HBA_HANDLE handle,
+ int portIndex, PSMHBA_PORTATTRIBUTES port,
+ HBA_WWN *pdomainPort)
+{
+ HBA_STATUS status;
+ PSMHBA_SAS_PORT sasport;
+ SMHBA_SAS_PHY phyattr;
+
+ sasport = port->PortSpecificAttribute.SASPort;
+ (void *) memset(pdomainPort, 0, sizeof (HBA_WWN));
+ /*
+ * Since iport can exist without any phys,
+ * sasinfo hba-port -v has indicated numberOfPhys;
+ * if there is no phys within the hba, just return OK.
+ */
+ if (sasport->NumberofPhys > 0) {
+ status = SMHBA_GetSASPhyAttributes(handle, portIndex,
+ 0, &phyattr);
+ if (status != HBA_STATUS_OK)
+ return (status);
+ (void *) memcpy(pdomainPort, &phyattr.domainPortWWN,
+ sizeof (HBA_WWN));
+ } else {
+ /* return not supported for no phy configured */
+ return (HBA_STATUS_ERROR_NOT_SUPPORTED);
+ }
+ return (HBA_STATUS_OK);
+}
+
+/*
+ * Comparison function for comparing names possibly ending with digits.
+ * Return:
+ * <0 - name1 is less than name2.
+ * 0 - name1 is equal with name2.
+ * >0 - name1 is more than name2.
+ */
+static int
+sas_name_comp(const char *name1, const char *name2)
+{
+ int i = 0;
+
+ if (name1 == name2)
+ return (0);
+
+ while ((name1[i] == name2[i]) && (name1[i] != '\0'))
+ i++;
+
+ /* If neither of name1[i] and name2[i] is '\0'. */
+ if (isdigit(name1[i]) && isdigit(name2[i]))
+ return (atoi(&name1[i]) - atoi(&name2[i]));
+
+ /* One of name1[i] and name2[i] is not digit. */
+ return (name1[i] - name2[i]);
+}
+/*
+ * Comparison function for sorting HBA/HBA Port.
+ * arg1 - first argument of type sas_elem_t.
+ * arg2 - second argument of type sas_elem_t.
+ * Return:
+ * <0 - arg1 is less than arg2.
+ * 0 - arg1 is equal with arg2.
+ * >0 - arg1 is more than arg2.
+ */
+static int
+sas_elem_compare(const void *arg1, const void *arg2)
+{
+ sas_elem_t *p1, *p2;
+ p1 = (sas_elem_t *)arg1;
+ p2 = (sas_elem_t *)arg2;
+ return (sas_name_comp(p1->name, p2->name));
+}
+
+/*
+ * Sorting function for HBA/HBA Port output.
+ * array - elements array of type sas_elem_t.
+ * nelem - number of elements in array of type sas_elem_t.
+ */
+static void
+sas_elem_sort(sas_elem_t *array, int nelem)
+{
+ qsort((void *)array, nelem, sizeof (sas_elem_t), sas_elem_compare);
+}