# # 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. # # Copyright (c) 2018, Joyent, Inc. include ../Makefile.cmd COMMONBASE = ../../common PROG = sasinfo PRODUCT = $(PROG) $(ROOT_PROG_LINK) : FILEMODE = 0555 LOCAL_OBJS = sasinfo.o sasinfo-list.o printAttrs.o COMMON_OBJS = cmdparse.o OBJS = $(LOCAL_OBJS) $(COMMON_OBJS) POFILE = sasinfo_all.po POFILES = $(LOCAL_OBJS:%.o=%.po) LDLIBS += -lSMHBAAPI CERRWARN += -Wno-unused-value SMOFF += indenting INCS += -I. INCS += -I$(SRC)/lib/smhba/common INCS += -I$(SRC)/lib/hbaapi/common INCS += -I$(COMMONBASE)/cmdparse CPPFLAGS += -D_LARGEFILE64_SOURCE=1 -D_REENTRANT $(INCS) $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG .KEEP_STATE: all: $(PROG) $(PROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) cmdparse.o: $(COMMONBASE)/cmdparse/cmdparse.c $(COMPILE.c) -o $@ $(COMMONBASE)/cmdparse/cmdparse.c $(POST_PROCESS_O) install: all $(ROOTUSRSBINPROG) $(POFILE): $(POFILES) $(RM) $@ cat $(POFILES) > $@ clean: $(RM) $(OBJS) check: $(CHKMANIFEST) include ../Makefile.targ /* * 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 #include static SAS_STATE hbastatus_string[] = { HBA_STATUS_OK, "Okay", HBA_STATUS_ERROR, "Error", HBA_STATUS_ERROR_NOT_SUPPORTED, "Not Supported", HBA_STATUS_ERROR_INVALID_HANDLE, "Invalid Handle", HBA_STATUS_ERROR_ARG, "Argument Error", HBA_STATUS_ERROR_ILLEGAL_WWN, "Illegal WWN", HBA_STATUS_ERROR_ILLEGAL_INDEX, "Illegal Index", HBA_STATUS_ERROR_MORE_DATA, "Not Enough Buffer for Data", HBA_STATUS_ERROR_STALE_DATA, "Stale Data", HBA_STATUS_SCSI_CHECK_CONDITION, "SCSI Check Condition", HBA_STATUS_ERROR_BUSY, "Busy", HBA_STATUS_ERROR_TRY_AGAIN, "Try Again", HBA_STATUS_ERROR_UNAVAILABLE, "Unavailable", HBA_STATUS_ERROR_ELS_REJECT, "ELS Reject", HBA_STATUS_ERROR_INVALID_LUN, "Invalid LUN", HBA_STATUS_ERROR_INCOMPATIBLE, "Request Incompatible", HBA_STATUS_ERROR_AMBIGUOUS_WWN, "Ambiguous WWN", HBA_STATUS_ERROR_LOCAL_BUS, "Local Bus Error", HBA_STATUS_ERROR_LOCAL_TARGET, "Local Target Error", HBA_STATUS_ERROR_LOCAL_LUN, "Local LUN Error", HBA_STATUS_ERROR_LOCAL_SCSIID_BOUND, "Local SCSIID Bound", HBA_STATUS_ERROR_TARGET_FCID, "Target FCID Error", HBA_STATUS_ERROR_TARGET_NODE_WWN, "Target Node WWN Error", HBA_STATUS_ERROR_TARGET_PORT_WWN, "Target Port WWN Error", HBA_STATUS_ERROR_TARGET_LUN, "Target LUN Error", HBA_STATUS_ERROR_TARGET_LUID, "Target LUID Error", HBA_STATUS_ERROR_NO_SUCH_BINDING, "No Such Binding", HBA_STATUS_ERROR_NOT_A_TARGET, "Not a Target", HBA_STATUS_ERROR_UNSUPPORTED_FC4, "Unsupported FC4", HBA_STATUS_ERROR_INCAPABLE, "Incapable", HBA_STATUS_ERROR_TARGET_BUSY, "Target Busy", HBA_STATUS_ERROR_NOT_LOADED, "Not Loaded", HBA_STATUS_ERROR_ALREADY_LOADED, "Alreday Loaded", HBA_STATUS_ERROR_ILLEGAL_FCID, "Illegal FCID", HBA_STATUS_ERROR_NOT_ASCSIDEVICE, "Not a SCSI Device", HBA_STATUS_ERROR_INVALID_PROTOCOL_TYPE, "Invalid Protocol Type", HBA_STATUS_ERROR_BAD_EVENT_TYPE, "Bad Event Type", -1, NULL }; SAS_STATE porttype_string[] = { HBA_PORTTYPE_UNKNOWN, "UNKNOWN", HBA_PORTTYPE_OTHER, "OTHER", HBA_PORTTYPE_NOTPRESENT, "NOT Present", HBA_PORTTYPE_SASDEVICE, "SAS Device", HBA_PORTTYPE_SATADEVICE, "SATA Device", HBA_PORTTYPE_SASEXPANDER, "SAS Expander", -1, NULL, }; SAS_STATE portstate_string[] = { HBA_PORTSTATE_UNKNOWN, "unknown", HBA_PORTSTATE_ONLINE, "online", HBA_PORTSTATE_OFFLINE, "offline", HBA_PORTSTATE_BYPASSED, "bypassed", HBA_PORTSTATE_DIAGNOSTICS, "diagnostics", HBA_PORTSTATE_LINKDOWN, "link Down", HBA_PORTSTATE_ERROR, "port Error", HBA_PORTSTATE_LOOPBACK, "loopback", HBA_PORTSTATE_DEGRADED, "degraded", -1, NULL, }; static SAS_STATE phystate_string[] = { HBA_SASSTATE_UNKNOWN, "unknown", HBA_SASSTATE_DISABLED, "disabled", HBA_SASSTATE_FAILED, "failed", HBA_SASSTATE_SATASPINUP, "sata-spinup", HBA_SASSTATE_SATAPORTSEL, "sata-portselector", HBA_SASSPEED_1_5GBIT, "1.5Gbit", HBA_SASSPEED_3GBIT, "3Gbit", HBA_SASSPEED_6GBIT, "6Gbit", HBA_SASSPEED_12GBIT, "12Gbit", -1, NULL, }; static SAS_STATE dtype_string[] = { DTYPE_DIRECT, "Disk Device", DTYPE_SEQUENTIAL, "Tape Device", DTYPE_PRINTER, "Printer Device", DTYPE_PROCESSOR, "Processor Device", DTYPE_WORM, "WORM Device", DTYPE_RODIRECT, "CD/DVD Device", DTYPE_SCANNER, "Scanner Device", DTYPE_OPTICAL, "Optical Memory Device", DTYPE_CHANGER, "Medium Changer Device", DTYPE_COMM, "Communications Device", DTYPE_ARRAY_CTRL, "Storage Array Controller Device", DTYPE_ESI, "Enclosure Services Device", DTYPE_RBC, "Simplified Direct-access Device", DTYPE_OCRW, "Optical Card Reader/Writer Device", DTYPE_BCC, "Bridge Controller Commands", DTYPE_OSD, "Object-based Storage Device", DTYPE_ADC, "Automation/Drive Interface", DTYPE_WELLKNOWN, "Well Known Logical Unit", DTYPE_UNKNOWN, "Unknown Device", -1, NULL }; static char *getPhyStateString(HBA_UINT32 key, phystat_type phyt); char * getIndentSpaces(int number) { int i = 0; /* the maximum indent with terminator '\0' */ static char ret[MAXINDENT+1]; if (number > MAXINDENT) number = MAXINDENT; for (i = 0; i < number; i++) { ret[i] = ' '; } ret[i] = '\0'; return (ret); } char * getStateString(HBA_UINT32 key, SAS_STATE *stat_string) { static char ret[64]; while (stat_string->key != -1) { if (stat_string->key == key) { return ((char *)stat_string->value); } stat_string++; } (void) sprintf(ret, "Undefined value (%d)", key); return (ret); } static char * getPhyStateString(HBA_UINT32 key, phystat_type phyt) { int i = 0, len = 0, match = 0; HBA_UINT32 physpeed[] = { HBA_SASSPEED_1_5GBIT, HBA_SASSPEED_3GBIT, HBA_SASSPEED_6GBIT, HBA_SASSPEED_12GBIT }; len = sizeof (physpeed) / sizeof (HBA_UINT32); for (i = 0; i < len; i++) { if (key == physpeed[i]) { match = 1; break; } } if (match == 1) { if (phyt == PHY_STATE) return ("enabled"); else return (getStateString(key, phystate_string)); } else { if (phyt == PHY_STATE) return (getStateString(key, phystate_string)); else return ("not available"); } } char * getHBAStatus(HBA_STATUS key) { return (getStateString(key, hbastatus_string)); } /* * return device type description * * Arguments: * dType - Device type returned from Standard INQUIRY * Returns: * char string description for device type */ char * getDTypeString(uchar_t dType) { return (getStateString((dType & DTYPE_MASK), dtype_string)); } uint64_t wwnConversion(uchar_t *wwn) { uint64_t tmp; (void) memcpy(&tmp, wwn, sizeof (uint64_t)); return (ntohll(tmp)); } /* * prints out HBA information */ void printHBAInfo(SMHBA_ADAPTERATTRIBUTES *attrs, int pflag, int numberOfPorts, const char *adapterName) { (void) fprintf(stdout, "%s %s\n", "HBA Name:", adapterName); if (pflag & PRINT_VERBOSE) { (void) fprintf(stdout, "%s%s %s\n", getIndentSpaces(4), "Manufacturer:", attrs->Manufacturer[0] == 0? "not available":attrs->Manufacturer); (void) fprintf(stdout, "%s%s %s\n", getIndentSpaces(4), "Model: ", attrs->Model[0] == 0? "not available":attrs->Model); (void) fprintf(stdout, "%s%s %s\n", getIndentSpaces(4), "Firmware Version:", attrs->FirmwareVersion[0] == 0? "not available": attrs->FirmwareVersion); (void) fprintf(stdout, "%s%s %s\n", getIndentSpaces(4), "FCode/BIOS Version:", attrs->OptionROMVersion[0] == 0? "not available": attrs->OptionROMVersion); (void) fprintf(stdout, "%s%s %s\n", getIndentSpaces(4), "Serial Number:", attrs->SerialNumber[0] == 0? "not available": attrs->SerialNumber); (void) fprintf(stdout, "%s%s %s\n", getIndentSpaces(4), "Driver Name:", attrs->DriverName[0] == 0? "not available": attrs->DriverName); (void) fprintf(stdout, "%s%s %s\n", getIndentSpaces(4), "Driver Version:", attrs->DriverVersion[0] == 0? "not available": attrs->DriverVersion); (void) fprintf(stdout, "%s%s %d\n", getIndentSpaces(4), "Number of HBA Ports:", numberOfPorts); } } /* * prints out all the HBA port information */ void printHBAPortInfo(SMHBA_PORTATTRIBUTES *port, SMHBA_ADAPTERATTRIBUTES *attrs, int pflag) { if ((port == NULL) || (attrs == NULL)) { return; } (void) fprintf(stdout, "%s%s %s\n", getIndentSpaces(2), "HBA Port Name:", port->OSDeviceName); if (!(pflag & PRINT_VERBOSE)) { return; } if (port->PortType != HBA_PORTTYPE_SASDEVICE) return; (void) fprintf(stdout, "%s%s %s\n", getIndentSpaces(4), "Type:", getStateString(port->PortType, porttype_string)); (void) fprintf(stdout, "%s%s %s\n", getIndentSpaces(4), "State:", getStateString(port->PortState, portstate_string)); (void) fprintf(stdout, "%s%s %016llx\n", getIndentSpaces(4), "Local SAS Address:", wwnConversion(port->PortSpecificAttribute.SASPort->\ LocalSASAddress.wwn)); (void) fprintf(stdout, "%s%s %016llx\n", getIndentSpaces(4), "Attached SAS Address:", wwnConversion(port->PortSpecificAttribute.SASPort->\ AttachedSASAddress.wwn)); (void) fprintf(stdout, "%s%s %d\n", getIndentSpaces(4), "Number of Phys:", port->PortSpecificAttribute.SASPort->NumberofPhys); } void printHBAPortPhyInfo(SMHBA_SAS_PHY *phyinfo) { if (phyinfo == NULL) return; (void) fprintf(stdout, "%s%s %u\n", getIndentSpaces(6), "Identifier:", phyinfo->PhyIdentifier); (void) fprintf(stdout, "%s%s %s\n", getIndentSpaces(8), "State: ", getPhyStateString(phyinfo->NegotiatedLinkRate, PHY_STATE)); (void) fprintf(stdout, "%s%s %s/%s\n", getIndentSpaces(8), "HardwareLinkRate(Min/Max):", getPhyStateString(phyinfo->HardwareMinLinkRate, PHY_SPEED), getPhyStateString(phyinfo->HardwareMaxLinkRate, PHY_SPEED)); (void) fprintf(stdout, "%s%s %s/%s\n", getIndentSpaces(8), "ProgrammedLinkRate(Min/Max):", getPhyStateString(phyinfo->ProgrammedMinLinkRate, PHY_SPEED), getPhyStateString(phyinfo->ProgrammedMaxLinkRate, PHY_SPEED)); (void) fprintf(stdout, "%s%s %s\n", getIndentSpaces(8), "NegotiatedLinkRate:", getPhyStateString(phyinfo->NegotiatedLinkRate, PHY_SPEED)); } void printHBAPortPhyStatistics(SMHBA_SASPHYSTATISTICS *phystat) { if (phystat == NULL) return; (void) fprintf(stdout, "%s%s\n", getIndentSpaces(8), "Link Error Statistics:"); (void) fprintf(stdout, "%s%s %llu\n", getIndentSpaces(12), "Invalid Dword:", phystat->InvalidDwordCount); (void) fprintf(stdout, "%s%s %llu\n", getIndentSpaces(12), "Running Disparity Error:", phystat->RunningDisparityErrorCount); (void) fprintf(stdout, "%s%s %llu\n", getIndentSpaces(12), "Loss of Dword Sync:", phystat->LossofDwordSyncCount); (void) fprintf(stdout, "%s%s %llu\n", getIndentSpaces(12), "Reset Problem:", phystat->PhyResetProblemCount); } /* * print the OS device name for the logical-unit object * * Arguments: * devListWalk - OS device path info * verbose - boolean indicating whether to display additional info * * returns: * 0 - we're good. * >0 - we met issues. */ int printTargetPortInfo(targetPortList_t *TPListWalk, int pflag) { targetPortConfig_t *configList; targetPortMappingData_t *mapList; int count, i; int ret = 0; (void) fprintf(stdout, "Target Port SAS Address: %016llx\n", wwnConversion(TPListWalk->sasattr.LocalSASAddress.wwn)); if ((pflag & PRINT_VERBOSE) || (pflag & PRINT_TARGET_SCSI)) { (void) fprintf(stdout, "%sType: %s\n", getIndentSpaces(4), getStateString(TPListWalk->targetattr.PortType, porttype_string)); for (configList = TPListWalk->configEntry; configList != NULL; configList = configList->next) { (void) fprintf(stdout, "%sHBA Port Name: %s\n", getIndentSpaces(4), configList->hbaPortName); if (wwnConversion(configList->expanderSASAddr.wwn) != 0) { if (configList->expanderValid) { (void) fprintf(stdout, "%sExpander Device SAS Address:" " %016llx", getIndentSpaces(8), wwnConversion(configList-> expanderSASAddr.wwn)); } else { (void) fprintf(stdout, "%sExpander Device SAS Address:" " %016llx (Failed to Validate" " Attached Port.)", getIndentSpaces(8), wwnConversion(configList-> expanderSASAddr.wwn)); ret++; } } else { if (configList->expanderValid) { (void) fprintf(stdout, "%sExpander Device SAS Address: %s", getIndentSpaces(8), "None (direct attached)"); } else { (void) fprintf(stdout, "%sExpander Device SAS Address: %s", getIndentSpaces(8), "None (Failed to Get" " Attached Port)"); } } (void) fprintf(stdout, "\n"); if (pflag & PRINT_TARGET_SCSI) { if (configList->reportLUNsFailed) { (void) fprintf(stdout, "%s %016llx\n", gettext("Error: Failed to get " "ReportLun Data on"), wwnConversion(TPListWalk-> sasattr.LocalSASAddress.wwn)); ret++; continue; } for (mapList = configList->map; mapList != NULL; mapList = mapList->next) { (void) fprintf(stdout, "%sLUN : %d\n", getIndentSpaces(12), mapList->osLUN); if (mapList->mappingExist) { (void) fprintf(stdout, "%sOS Device Name : %s\n", getIndentSpaces(14), (mapList->osDeviceName[0] == '\0') ? "Not avaialble" : mapList->osDeviceName); } else { (void) fprintf(stdout, "%sOS Device Name : %s\n", getIndentSpaces(14), "No " "matching OS Device " "found."); ret++; } /* indentation changed here */ if (mapList->inquiryFailed) { (void) fprintf(stdout, "%s %s LUN %d\n", gettext("Error: Failed to get Inquiry Data on"), mapList->osDeviceName, mapList->osLUN); ret++; } else { (void) fprintf(stdout, "%sVendor: ", getIndentSpaces(14)); for (count = sizeof (mapList->inq_vid), i = 0; i < count; i++) { if (isprint(mapList->inq_vid[i])) (void) fprintf(stdout, "%c", mapList->inq_vid[i]); } (void) fprintf(stdout, "\n%sProduct: ", getIndentSpaces(14)); for (count = sizeof (mapList->inq_pid), i = 0; i < count; i++) { if (isprint(mapList->inq_pid[i])) (void) fprintf(stdout, "%c", mapList->inq_pid[i]); } (void) fprintf(stdout, "\n%sDevice Type: %s\n", getIndentSpaces(14), getDTypeString(mapList->inq_dtype)); } /* indentation changed back */ } } } } return (ret); } /* * print the OS device name for the logical-unit object * * Arguments: * devListWalk - OS device path info * verbose - boolean indicating whether to display additional info * * returns: * 0 - we're good. * >0 - we met issues. */ int printOSDeviceNameInfo(discoveredDevice *devListWalk, boolean_t verbose) { portList *portElem; tgtPortWWNList *tgtWWNList; int i, count; int ret = 0; (void) fprintf(stdout, "OS Device Name: %s\n", devListWalk->OSDeviceName); if (verbose == B_TRUE) { for (portElem = devListWalk->HBAPortList; portElem != NULL; portElem = portElem->next) { (void) fprintf(stdout, "%sHBA Port Name: ", getIndentSpaces(4)); (void) fprintf(stdout, "%s", portElem->portName); for (tgtWWNList = portElem->tgtPortWWN; tgtWWNList != NULL; tgtWWNList = tgtWWNList->next) { (void) fprintf(stdout, "\n%sTarget Port SAS Address: ", getIndentSpaces(8)); (void) fprintf(stdout, "%016llx", wwnConversion(tgtWWNList->portWWN.wwn)); (void) fprintf(stdout, "\n%sLUN: %u", getIndentSpaces(12), tgtWWNList->scsiOSLun); } (void) fprintf(stdout, "\n"); } if (devListWalk->inquiryFailed) { (void) fprintf(stdout, "%s %s\n", gettext("Error: Failed to get Inquiry data " "on device"), devListWalk->OSDeviceName); ret++; } else { (void) fprintf(stdout, "%sVendor: ", getIndentSpaces(4)); for (count = sizeof (devListWalk->VID), i = 0; i < count; i++) { if (isprint(devListWalk->VID[i])) (void) fprintf(stdout, "%c", devListWalk->VID[i]); } (void) fprintf(stdout, "\n%sProduct: ", getIndentSpaces(4)); for (count = sizeof (devListWalk->PID), i = 0; i < count; i++) { if (isprint(devListWalk->PID[i])) (void) fprintf(stdout, "%c", devListWalk->PID[i]); } (void) fprintf(stdout, "\n%sDevice Type: %s\n", getIndentSpaces(4), getDTypeString(devListWalk->dType)); } } return (ret); } /* * 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. */ #ifndef _PRINTATTRS_H #define _PRINTATTRS_H #ifdef __cplusplus extern "C" { #endif #include typedef enum { PHY_STATE, PHY_SPEED } phystat_type; typedef struct state_string { int key; char *value; } SAS_STATE; extern SAS_STATE porttype_string[]; extern SAS_STATE portstate_string[]; #define MAXINDENT 64 char *getHBAStatus(HBA_STATUS hbaStatus); uint64_t wwnConversion(uchar_t *wwn); void printHBAInfo(SMHBA_ADAPTERATTRIBUTES *attrs, int pflag, int numberOfPorts, const char *adapterName); void printHBAPortInfo(SMHBA_PORTATTRIBUTES *port, SMHBA_ADAPTERATTRIBUTES *attrs, int pflag); void printHBAPortPhyInfo(SMHBA_SAS_PHY *phyinfo); void printHBAPortPhyStatistics(SMHBA_SASPHYSTATISTICS *phystat); extern void printLogicalUnit(int pflag, SMHBA_TARGETMAPPING *map); extern int printOSDeviceNameInfo(discoveredDevice *devListWalk, boolean_t verbose); extern int printTargetPortInfo(targetPortList_t *TPListWalk, int pflag); extern char *getStateString(HBA_UINT32 key, SAS_STATE *stat_string); extern char *getIndentSpaces(int number); extern char *getDTypeString(uchar_t dType); #ifdef __cplusplus } #endif #endif /* _PRINTATTRS_H */ /* * 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 #include #include #include #include #include #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); } /* * 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 #include #include #define VERSION_STRING_MAX_LEN 10 /* * Version number: * MAJOR - This should only change when there is an incompatible change made * to the interfaces or the output. * * MINOR - This should change whenever there is a new command or new feature * with no incompatible change. */ #define VERSION_STRING_MAJOR "1" #define VERSION_STRING_MINOR "0" /* globals */ static char *cmdName; /* forward declarations */ static int listHbaFunc(int, char **, cmdOptions_t *, void *); static int listHbaPortFunc(int, char **, cmdOptions_t *, void *); static int listExpanderFunc(int, char **, cmdOptions_t *, void *); static int listTargetPortFunc(int, char **, cmdOptions_t *, void *); static int listLogicalUnitFunc(int, char **, cmdOptions_t *, void *); static char *getExecBasename(char *); /* * Add new options here * * Optional option-arguments are not allowed by CLIP */ optionTbl_t sasinfolongOptions[] = { {"hba", required_argument, 'a', "HBA Name"}, {"hba-port", required_argument, 'p', "HBA Port Name"}, {"phy", no_argument, 'y', NULL}, {"phy-linkstat", no_argument, 'l', NULL}, {"scsi-target", no_argument, 's', NULL}, {"verbose", no_argument, 'v', NULL}, {"target", no_argument, 't', NULL}, {NULL, 0, 0} }; /* * Add new subcommands here */ subCommandProps_t sasinfosubcommands[] = { {"hba", listHbaFunc, "v", NULL, NULL, OPERAND_OPTIONAL_MULTIPLE, "HBA Name"}, {"hba-port", listHbaPortFunc, "ylva", NULL, NULL, OPERAND_OPTIONAL_MULTIPLE, "HBA Port Name"}, {"expander", listExpanderFunc, "ptv", NULL, NULL, OPERAND_OPTIONAL_MULTIPLE, "Expander Device SAS Address"}, {"target-port", listTargetPortFunc, "sv", NULL, "sv", OPERAND_OPTIONAL_MULTIPLE, "Target Port SAS Address"}, {"logical-unit", listLogicalUnitFunc, "v", NULL, NULL, OPERAND_OPTIONAL_MULTIPLE, "OS Device Name"}, {"lu", listLogicalUnitFunc, "v", NULL, NULL, OPERAND_OPTIONAL_MULTIPLE, "OS Device Name"}, {NULL, 0, NULL, NULL, NULL, 0, NULL, NULL} }; /* * Pass in options/arguments, rest of arguments */ /*ARGSUSED*/ static int listHbaFunc(int objects, char *argv[], cmdOptions_t *options, void *addArgs) { return (sas_util_list_hba(objects, argv, options)); } /*ARGSUSED*/ static int listHbaPortFunc(int objects, char *argv[], cmdOptions_t *options, void *addArgs) { return (sas_util_list_hbaport(objects, argv, options)); } /* * Pass in options/arguments, rest of arguments */ /*ARGSUSED*/ static int listExpanderFunc(int objects, char *argv[], cmdOptions_t *options, void *addArgs) { return (sas_util_list_expander(objects, argv, options)); } /*ARGSUSED*/ static int listTargetPortFunc(int objects, char *argv[], cmdOptions_t *options, void *addArgs) { return (sas_util_list_targetport(objects, argv, options)); } /* * Pass in options/arguments, rest of arguments */ /*ARGSUSED*/ static int listLogicalUnitFunc(int objects, char *argv[], cmdOptions_t *options, void *addArgs) { return (sas_util_list_logicalunit(objects, argv, options)); } /* * input: * execFullName - exec name of program (argv[0]) * * Returns: * command name portion of execFullName */ static char * getExecBasename(char *execFullname) { char *lastSlash, *execBasename; /* guard against '/' at end of command invocation */ for (;;) { lastSlash = strrchr(execFullname, '/'); if (lastSlash == NULL) { execBasename = execFullname; break; } else { execBasename = lastSlash + 1; if (*execBasename == '\0') { *lastSlash = '\0'; continue; } break; } } return (execBasename); } /* * main calls a parser that checks syntax of the input command against * various rules tables. * * The return value from the function is placed in funcRet */ int main(int argc, char *argv[]) { synTables_t synTables; char versionString[VERSION_STRING_MAX_LEN]; int ret; int funcRet; void *subcommandArgs = NULL; /* to support locale */ (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ #endif (void) textdomain(TEXT_DOMAIN); /* set global command name */ cmdName = getExecBasename(argv[0]); /* check if is global zone */ if (getzoneid() != GLOBAL_ZONEID) { (void) fprintf(stdout, "%s %s\n", cmdName, gettext("does not support non-global zone.")); return (1); } (void) snprintf(versionString, sizeof (versionString), "%s.%s", VERSION_STRING_MAJOR, VERSION_STRING_MINOR); synTables.versionString = versionString; synTables.longOptionTbl = &sasinfolongOptions[0]; synTables.subCommandPropsTbl = &sasinfosubcommands[0]; /* call the CLI parser */ ret = cmdParse(argc, argv, synTables, subcommandArgs, &funcRet); if (ret == 1) { (void) fprintf(stdout, "%s %s(8)\n", gettext("For more information, please see"), cmdName); return (1); } else if (ret == -1) { (void) fprintf(stderr, "%s %s\n", cmdName, strerror(errno)); return (1); } if (funcRet != 0) { return (1); } return (0); } /* * 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. */ #ifndef _SASINFO_H #define _SASINFO_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _BIG_ENDIAN #define htonll(x) (x) #define ntohll(x) (x) #else #define htonll(x) ((((unsigned long long)htonl(x)) << 32) + htonl(x >> 32)) #define ntohll(x) ((((unsigned long long)ntohl(x)) << 32) + ntohl(x >> 32)) #endif /* DEFINES */ #define DEFAULT_LUN_COUNT 1024 #define LUN_SIZE 8 #define LUN_HEADER_SIZE 8 #define LUN_LENGTH LUN_SIZE + LUN_HEADER_SIZE #define DEFAULT_LUN_LENGTH DEFAULT_LUN_COUNT * \ LUN_SIZE + \ LUN_HEADER_SIZE /* flags that are needed to be passed into porcessHBA */ #define PRINT_VERBOSE 0x00000001 #define PRINT_PHY 0x00000002 /* print phy addresses */ #define PRINT_PHY_LINKSTAT 0x00000004 /* print phy link statistics */ #define PRINT_TARGET_PORT 0x00000008 /* print target os deivce info */ #define PRINT_CHILD 0x00000010 /* print descendant nodes */ #define PRINT_TARGET_SCSI 0x00000020 /* print descendant nodes */ #define HBA_MAX_RETRIES 20 typedef struct _tgtPortWWNList { HBA_WWN portWWN; HBA_UINT32 scsiOSLun; struct _tgtPortWWNList *next; } tgtPortWWNList; typedef struct _portList { char portName[MAXPATHLEN]; tgtPortWWNList *tgtPortWWN; struct _portList *next; } portList; /* Discovered LU structure */ typedef struct _discoveredDevice { boolean_t inquiryFailed; char OSDeviceName[MAXPATHLEN]; portList *HBAPortList; char VID[8]; char PID[16]; uchar_t dType; struct _discoveredDevice *next; } discoveredDevice; typedef struct targetPortMappingData { boolean_t mappingExist; boolean_t inquiryFailed; HBA_UINT32 osLUN; SMHBA_SCSILUN reportLUN; char osDeviceName[256]; uchar_t inq_vid[8]; uchar_t inq_pid[16]; uchar_t inq_dtype; struct targetPortMappingData *next; } targetPortMappingData_t; typedef struct targetPortConfig { char hbaPortName[256]; HBA_WWN expanderSASAddr; int expanderValid; boolean_t reportLUNsFailed; struct targetPortMappingData *map; struct targetPortConfig *next; } targetPortConfig_t; typedef struct targetPortList { SMHBA_PORTATTRIBUTES targetattr; SMHBA_SAS_PORT sasattr; struct targetPortConfig *configEntry; struct targetPortList *next; } targetPortList_t; int sas_util_list_hba(int hbaCount, char **hba_argv, cmdOptions_t *options); int sas_util_list_hbaport(int wwnCount, char **wwn_argv, cmdOptions_t *options); int sas_util_list_expander(int wwnCount, char **wwn_argv, cmdOptions_t *options); int sas_util_list_targetport(int tpCount, char **tpArgv, cmdOptions_t *options); int sas_util_list_remoteport(int wwnCount, char **wwn_argv, cmdOptions_t *options); int sas_util_list_logicalunit(int luCount, char **luArgv, cmdOptions_t *options); #ifdef __cplusplus } #endif #endif /* _SASINFO_H */