diff options
Diffstat (limited to 'usr/src/cmd/sasinfo/sasinfo-list.c')
-rw-r--r-- | usr/src/cmd/sasinfo/sasinfo-list.c | 2799 |
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); +} |