/*
* 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);
}