/* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "sun_fc_version.h" #define DEFAULT_LUN_COUNT 1024 #define LUN_SIZE 8 #define LUN_HEADER_SIZE 8 #define DEFAULT_LUN_LENGTH DEFAULT_LUN_COUNT * \ LUN_SIZE + \ LUN_HEADER_SIZE struct lun_val { uchar_t val[8]; }; struct rep_luns_rsp { uint32_t length; uint32_t rsrvd; struct lun_val lun[1]; }; /* Extracted from the old scsi.h file */ struct capacity_data_struct { uint_t last_block_addr; uint_t block_size; }; /* Structure to handle the inq. page 0x80 serial number */ struct page80 { uchar_t inq_dtype; uchar_t inq_page_code; uchar_t reserved; uchar_t inq_page_len; uchar_t inq_serial[251]; }; extern char *dtype[]; extern int Options; extern const int OPTION_P; int skip_hba(int i); int find_supported_inq_page(HBA_HANDLE handle, HBA_WWN hwwn, HBA_WWN pwwn, uint64_t lun, int page_num); /* * The routines within this file operate against the T11 * HBA API interface. In some cases, proprietary Sun driver * interface are also called to add additional information * above what the standard library supports. */ uint64_t wwnConversion(uchar_t *wwn) { uint64_t tmp; (void) memcpy(&tmp, wwn, sizeof (uint64_t)); return (ntohll(tmp)); } void printStatus(HBA_STATUS status) { switch (status) { case HBA_STATUS_OK: printf(MSGSTR(2410, "OK")); return; case HBA_STATUS_ERROR: printf(MSGSTR(2411, "ERROR")); return; case HBA_STATUS_ERROR_NOT_SUPPORTED: printf(MSGSTR(2412, "NOT SUPPORTED")); return; case HBA_STATUS_ERROR_INVALID_HANDLE: printf(MSGSTR(2413, "INVALID HANDLE")); return; case HBA_STATUS_ERROR_ARG: printf(MSGSTR(2414, "ERROR ARG")); return; case HBA_STATUS_ERROR_ILLEGAL_WWN: printf(MSGSTR(2415, "ILLEGAL WWN")); return; case HBA_STATUS_ERROR_ILLEGAL_INDEX: printf(MSGSTR(2416, "ILLEGAL INDEX")); return; case HBA_STATUS_ERROR_MORE_DATA: printf(MSGSTR(2417, "MORE DATA")); return; case HBA_STATUS_ERROR_STALE_DATA: printf(MSGSTR(2418, "STALE DATA")); return; case HBA_STATUS_SCSI_CHECK_CONDITION: printf(MSGSTR(2419, "SCSI CHECK CONDITION")); return; case HBA_STATUS_ERROR_BUSY: printf(MSGSTR(2420, "BUSY")); return; case HBA_STATUS_ERROR_TRY_AGAIN: printf(MSGSTR(2421, "TRY AGAIN")); return; case HBA_STATUS_ERROR_UNAVAILABLE: printf(MSGSTR(2422, "UNAVAILABLE")); return; default: printf(MSGSTR(2423, "UNKNOWN ERROR TYPE %d"), status); return; } } uint32_t getNumberOfAdapters() { uint32_t count = HBA_GetNumberOfAdapters(); if (count == 0) { fprintf(stderr, MSGSTR(2405, "\nERROR: No Fibre Channel Adapters found.\n")); } return (count); } #define MAX_RETRIES 10 /* * Returns non-zero on failure (aka, HBA_STATUS_ERROR_* * Will handle retries if applicable. */ int getAdapterAttrs(HBA_HANDLE handle, char *name, HBA_ADAPTERATTRIBUTES *attrs) { int count = 0; HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */ /* Loop as long as we have a retryable error */ while ((status == HBA_STATUS_ERROR_TRY_AGAIN || status == HBA_STATUS_ERROR_BUSY) && count++ < MAX_RETRIES) { status = HBA_GetAdapterAttributes(handle, attrs); if (status == HBA_STATUS_OK) { break; } (void) sleep(1); } if (status != HBA_STATUS_OK) { /* We encountered a non-retryable error */ fprintf(stderr, MSGSTR(2501, "\nERROR: Unable to retrieve adapter port details (%s)"), name); printStatus(status); fprintf(stderr, "\n"); } return (status); } /* * Returns non-zero on failure (aka, HBA_STATUS_ERROR_* * Will handle retries if applicable. */ int getAdapterPortAttrs(HBA_HANDLE handle, char *name, int portIndex, HBA_PORTATTRIBUTES *attrs) { int count = 0; HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */ /* Loop as long as we have a retryable error */ while ((status == HBA_STATUS_ERROR_TRY_AGAIN || status == HBA_STATUS_ERROR_BUSY) && count++ < MAX_RETRIES) { status = HBA_GetAdapterPortAttributes(handle, portIndex, attrs); if (status == HBA_STATUS_OK) { break; } /* The odds of this occuring are very slim, but possible. */ if (status == HBA_STATUS_ERROR_STALE_DATA) { /* * If we hit a stale data scenario, * we'll just tell the user to try again. */ status = HBA_STATUS_ERROR_TRY_AGAIN; break; } sleep(1); } if (status != HBA_STATUS_OK) { /* We encountered a non-retryable error */ fprintf(stderr, MSGSTR(2501, "\nERROR: Unable to retrieve adapter port details (%s)"), name); printStatus(status); fprintf(stderr, "\n"); } return (status); } /* * Returns non-zero on failure (aka, HBA_STATUS_ERROR_* * Will handle retries if applicable. */ int getDiscPortAttrs(HBA_HANDLE handle, char *name, int portIndex, int discIndex, HBA_PORTATTRIBUTES *attrs) { int count = 0; HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */ /* Loop as long as we have a retryable error */ while ((status == HBA_STATUS_ERROR_TRY_AGAIN || status == HBA_STATUS_ERROR_BUSY) && count++ < MAX_RETRIES) { status = HBA_GetDiscoveredPortAttributes(handle, portIndex, discIndex, attrs); if (status == HBA_STATUS_OK) { break; } /* The odds of this occuring are very slim, but possible. */ if (status == HBA_STATUS_ERROR_STALE_DATA) { /* * If we hit a stale data scenario, we'll just tell the * user to try again. */ status = HBA_STATUS_ERROR_TRY_AGAIN; break; } sleep(1); } if (status != HBA_STATUS_OK) { /* We encountered a non-retryable error */ fprintf(stderr, MSGSTR(2504, "\nERROR: Unable to retrieve target port details (%s)"), name); printStatus(status); fprintf(stderr, "\n"); } return (status); } /*ARGSUSED*/ int fchba_display_port(int verbose) { int retval = 0; HBA_HANDLE handle; HBA_ADAPTERATTRIBUTES hbaAttrs; HBA_PORTATTRIBUTES portAttrs; HBA_STATUS status; int count, adapterIndex, portIndex; char name[256]; char *physical = NULL; char path[MAXPATHLEN]; if ((retval = loadLibrary())) { return (retval); } count = getNumberOfAdapters(); for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) { if (skip_hba(adapterIndex)) { continue; } status = HBA_GetAdapterName(adapterIndex, (char *)&name); if (status != HBA_STATUS_OK) { /* Just skip it, maybe it was DR'd */ continue; } handle = HBA_OpenAdapter(name); if (handle == 0) { /* Just skip it, maybe it was DR'd */ continue; } if (getAdapterAttrs(handle, name, &hbaAttrs)) { /* This should never happen, we'll just skip the adapter */ HBA_CloseAdapter(handle); continue; } for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts; portIndex++) { if (getAdapterPortAttrs(handle, name, portIndex, &portAttrs)) { continue; } physical = get_slash_devices_from_osDevName( portAttrs.OSDeviceName, STANDARD_DEVNAME_HANDLING); if (physical) { char *tmp = strstr(physical, ":fc"); if (tmp) { *tmp = '\0'; (void) snprintf(path, MAXPATHLEN, "%s:devctl", physical); } else { (void) snprintf(path, MAXPATHLEN, "%s", physical); } free(physical); physical = NULL; (void) printf("%-65s ", path); } else { (void) printf("%-65s ", portAttrs.OSDeviceName); } if (portAttrs.NumberofDiscoveredPorts > 0) { printf(MSGSTR(2233, "CONNECTED\n")); } else { printf(MSGSTR(2234, "NOT CONNECTED\n")); } } } (void) HBA_FreeLibrary(); return (retval); } /* * Internal routines/structure to deal with a path list * so we can ensure uniqueness */ struct path_entry { char path[MAXPATHLEN]; HBA_UINT8 wwn[8]; uchar_t dtype; struct path_entry *next; }; void add_path(struct path_entry *head, struct path_entry *cur) { struct path_entry *tmp; for (tmp = head; tmp->next != NULL; tmp = tmp->next) { } tmp->next = cur; } struct path_entry *is_duplicate_path(struct path_entry *head, char *path) { struct path_entry *tmp; for (tmp = head; tmp != NULL; tmp = tmp->next) { if (strncmp(tmp->path, path, sizeof (tmp->path)) == 0) { return (tmp); } } return (NULL); } void free_path_list(struct path_entry *head) { struct path_entry *tmp; struct path_entry *tmp2; for (tmp = head; tmp != NULL; ) { tmp2 = tmp->next; free(tmp); tmp = tmp2; } } int is_wwn(char *arg) { int i; if (strlen(arg) == 16) { for (i = 0; i < 16; i++) { if (!isxdigit(arg[i])) { return (0); } } return (1); } return (0); } int is_path(char *arg) { struct stat buf; if (stat(arg, &buf)) { return (0); } return (1); } /* We take a wild guess for our first get target mappings call */ #define MAP_GUESS 50 HBA_STATUS fetch_mappings(HBA_HANDLE handle, HBA_WWN pwwn, HBA_FCPTARGETMAPPINGV2 **map) { int loop = 0; int count = 0; HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */ *map = (HBA_FCPTARGETMAPPINGV2 *) calloc(1, (sizeof (HBA_FCPSCSIENTRYV2)* (MAP_GUESS-1)) + sizeof (HBA_FCPTARGETMAPPINGV2)); /* Loop as long as we have a retryable error */ while ((status == HBA_STATUS_ERROR_TRY_AGAIN || status == HBA_STATUS_ERROR_BUSY || status == HBA_STATUS_ERROR_MORE_DATA) && loop++ < MAX_RETRIES) { status = HBA_GetFcpTargetMappingV2(handle, pwwn, *map); if (status == HBA_STATUS_OK) { break; } else if (status == HBA_STATUS_ERROR_MORE_DATA) { count = (*map)->NumberOfEntries; free(*map); *map = (HBA_FCPTARGETMAPPINGV2 *) calloc(1, (sizeof (HBA_FCPSCSIENTRYV2)* (count-1)) + sizeof (HBA_FCPTARGETMAPPINGV2)); (*map)->NumberOfEntries = count; continue; } sleep(1); } if (status != HBA_STATUS_OK) { /* We encountered a non-retryable error */ fprintf(stderr, MSGSTR(2502, "\nERROR: Unable to retrieve SCSI device paths " "(HBA Port WWN %016llx)"), wwnConversion(pwwn.wwn)); printStatus(status); fprintf(stderr, "\n"); } return (status); } /* * Returns the index of the first match, or -1 if no match */ int match_mappings(char *compare, HBA_FCPTARGETMAPPINGV2 *map) { int mapIndex; char *physical = NULL; char *tmp; int wwnCompare = 0; uint64_t wwn; if (map == NULL || compare == NULL) { return (-1); } if (is_wwn(compare)) { wwnCompare = 1; (void) sscanf(compare, "%016llx", &wwn); } else { /* Convert the paths to phsyical paths */ physical = get_slash_devices_from_osDevName(compare, STANDARD_DEVNAME_HANDLING); } for (mapIndex = 0; mapIndex < map->NumberOfEntries; mapIndex ++) { if (wwnCompare) { if (wwn == wwnConversion( map->entry[mapIndex].FcpId.NodeWWN.wwn) || wwn == wwnConversion( map->entry[mapIndex].FcpId.PortWWN.wwn)) { return (mapIndex); } } else { if (physical != NULL) { tmp = get_slash_devices_from_osDevName( map->entry[mapIndex].ScsiId.OSDeviceName, STANDARD_DEVNAME_HANDLING); if ((tmp != NULL) && strncmp(physical, tmp, MAXPATHLEN) == 0) { free(physical); return (mapIndex); } } } } if (physical) { free(physical); } return (-1); } /* * returns non-zero on failure (aka HBA_STATUS_ERROR_* */ int loadLibrary() { int status = HBA_LoadLibrary(); if (status != HBA_STATUS_OK) { fprintf(stderr, MSGSTR(2505, "ERROR: Unable to load HBA API library: ")); printStatus(status); fprintf(stderr, "\n"); } return (status); } int fchba_non_encl_probe() { HBA_HANDLE handle; HBA_ADAPTERATTRIBUTES hbaAttrs; HBA_PORTATTRIBUTES portAttrs; HBA_FCPTARGETMAPPINGV2 *map; HBA_STATUS status; int count, adapterIndex, portIndex, mapIndex; char name[256]; struct path_entry *head = NULL; uint64_t lun = 0; L_inquiry inq; struct scsi_extended_sense sense; HBA_UINT8 scsiStatus; uint32_t inquirySize = sizeof (inq), senseSize = sizeof (sense); if (loadLibrary()) { return (-1); } count = getNumberOfAdapters(); /* Loop over all HBAs */ for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) { if (skip_hba(adapterIndex)) { continue; } status = HBA_GetAdapterName(adapterIndex, (char *)&name); if (status != HBA_STATUS_OK) { /* May have been DR'd */ continue; } handle = HBA_OpenAdapter(name); if (handle == 0) { /* May have been DR'd */ continue; } if (getAdapterAttrs(handle, name, &hbaAttrs)) { /* Should not happen, just skip it */ HBA_CloseAdapter(handle); continue; } /* Loop over all HBA Ports */ for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts; portIndex++) { if (getAdapterPortAttrs(handle, name, portIndex, &portAttrs)) { continue; } if (fetch_mappings(handle, portAttrs.PortWWN, &map)) { continue; } /* Loop over all target Mapping entries */ for (mapIndex = 0; mapIndex < map->NumberOfEntries; mapIndex ++) { struct path_entry *tmpPath = NULL; int doInquiry = 0; if (!head) { head = (struct path_entry *)calloc(1, sizeof (struct path_entry)); tmpPath = head; strncpy(head->path, map->entry[mapIndex].ScsiId.OSDeviceName, sizeof (map->entry[mapIndex].ScsiId.OSDeviceName)); (void) memcpy(tmpPath->wwn, map->entry[mapIndex].FcpId.NodeWWN.wwn, sizeof (HBA_UINT8) * 8); doInquiry = 1; } else if (tmpPath = is_duplicate_path(head, map->entry[mapIndex].ScsiId.OSDeviceName)) { if (tmpPath->dtype != 0x1f) { doInquiry = 0; } else { doInquiry = 1; } } else { tmpPath = (struct path_entry *) calloc(1, sizeof (struct path_entry)); strncpy(tmpPath->path, map->entry[mapIndex].ScsiId.OSDeviceName, sizeof (map->entry[mapIndex].ScsiId.OSDeviceName)); (void) memcpy(tmpPath->wwn, map->entry[mapIndex].FcpId.NodeWWN.wwn, sizeof (HBA_UINT8) * 8); add_path(head, tmpPath); doInquiry = 1; } if (doInquiry) { lun = map->entry[mapIndex].FcpId.FcpLun; memset(&inq, 0, sizeof (inq)); memset(&sense, 0, sizeof (sense)); status = HBA_ScsiInquiryV2(handle, portAttrs.PortWWN, map->entry[mapIndex].FcpId.PortWWN, lun, 0, 0, &inq, &inquirySize, &scsiStatus, &sense, &senseSize); if (status != HBA_STATUS_OK) { inq.inq_dtype = 0x1f; } tmpPath->dtype = inq.inq_dtype; } } } } if (head) { struct path_entry *tmp; printf(MSGSTR(2098, "\nFound Fibre Channel device(s):\n")); for (tmp = head; tmp != NULL; tmp = tmp->next) { printf(" "); printf(MSGSTR(90, "Node WWN:")); printf("%016llx ", wwnConversion(tmp->wwn)); fprintf(stdout, MSGSTR(35, "Device Type:")); (void) fflush(stdout); if ((tmp->dtype & DTYPE_MASK) < 0x10) { fprintf(stdout, "%s", dtype[tmp->dtype & DTYPE_MASK]); } else if ((tmp->dtype & DTYPE_MASK) < 0x1f) { fprintf(stdout, MSGSTR(2406, "Reserved")); } else { fprintf(stdout, MSGSTR(2407, "Unknown")); } printf("\n "); printf(MSGSTR(31, "Logical Path:%s"), tmp->path); printf("\n"); /* We probably shouldn't be using a g_fc interface here */ if (Options & OPTION_P) { char *phys_path = get_slash_devices_from_osDevName( tmp->path, STANDARD_DEVNAME_HANDLING); if (phys_path != NULL) { fprintf(stdout, " "); fprintf(stdout, MSGSTR(5, "Physical Path:")); fprintf(stdout, "\n %s\n", phys_path); free(phys_path); } } } free_path_list(head); } HBA_FreeLibrary(); return (0); } int fchba_inquiry(char **argv) { int path_index = 0, found = 0; uint64_t wwn; uint64_t lun = 0; HBA_HANDLE handle; HBA_ADAPTERATTRIBUTES hbaAttrs; HBA_PORTATTRIBUTES portAttrs; HBA_FCPTARGETMAPPINGV2 *map; HBA_STATUS status; int count, adapterIndex, portIndex, mapIndex; char name[256]; L_inquiry inq; struct page80 serial; uint32_t serialSize = sizeof (serial); struct scsi_extended_sense sense; HBA_UINT8 scsiStatus; uint32_t inquirySize = sizeof (inq), senseSize = sizeof (sense); boolean_t goodPath = B_FALSE; int matched = 0, wwnCompare = 0; char *tmp, *physical = NULL; int ret = 0; if (loadLibrary()) { return (-1); } for (path_index = 0; argv[path_index] != NULL; path_index++) { goodPath = B_FALSE; found = 0; if (is_wwn(argv[path_index])) { (void) sscanf(argv[path_index], "%016llx", &wwn); wwnCompare = 1; } else if (!is_path(argv[path_index])) { fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), argv[path_index]); fprintf(stderr, "\n"); ret = -1; continue; } if (!wwnCompare) { /* Convert the paths to phsyical paths */ physical = get_slash_devices_from_osDevName(argv[path_index], STANDARD_DEVNAME_HANDLING); if (!physical) { fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), argv[path_index]); fprintf(stderr, "\n"); ret = -1; continue; } } count = getNumberOfAdapters(); /* Loop over all HBAs */ for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) { if (skip_hba(adapterIndex)) { continue; } status = HBA_GetAdapterName(adapterIndex, (char *)&name); if (status != HBA_STATUS_OK) { /* May have been DR'd */ continue; } handle = HBA_OpenAdapter(name); if (handle == 0) { /* May have been DR'd */ continue; } if (getAdapterAttrs(handle, name, &hbaAttrs)) { /* Should never happen */ HBA_CloseAdapter(handle); continue; } /* Loop over all HBA Ports */ for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts; portIndex++) { if (getAdapterPortAttrs(handle, name, portIndex, &portAttrs)) { continue; } if (fetch_mappings(handle, portAttrs.PortWWN, &map)) { continue; } for (mapIndex = 0; mapIndex < map->NumberOfEntries; mapIndex ++) { matched = 0; if (wwnCompare) { if (wwn == wwnConversion( map->entry[mapIndex].FcpId.NodeWWN.wwn) || wwn == wwnConversion( map->entry[mapIndex].FcpId.PortWWN.wwn)) { lun = map->entry[mapIndex].FcpId.FcpLun; matched = 1; } } else { tmp = get_slash_devices_from_osDevName( map->entry[mapIndex].ScsiId.OSDeviceName, STANDARD_DEVNAME_HANDLING); if ((tmp != NULL) && (strncmp(physical, tmp, MAXPATHLEN) == 0)) { lun = map->entry[mapIndex].FcpId.FcpLun; matched = 1; free(tmp); } } if (matched) { memset(&inq, 0, sizeof (inq)); memset(&sense, 0, sizeof (sense)); status = HBA_ScsiInquiryV2(handle, portAttrs.PortWWN, map->entry[mapIndex].FcpId.PortWWN, lun, 0, 0, &inq, &inquirySize, &scsiStatus, &sense, &senseSize); if (status == HBA_STATUS_OK) { goodPath = B_TRUE; /* * Call the inquiry cmd on page 0x80 only if * the vendor supports page 0x80 */ memset(&serial, 0, sizeof (serial)); if ((find_supported_inq_page(handle, portAttrs.PortWWN, map->entry[mapIndex].FcpId.PortWWN, lun, 0x80))) { status = HBA_ScsiInquiryV2(handle, portAttrs.PortWWN, map->entry[mapIndex].FcpId.PortWWN, lun, 1, 0x80, &serial, &serialSize, &scsiStatus, &sense, &senseSize); if (status != HBA_STATUS_OK) { strncpy( (char *)serial.inq_serial, "Unavailable", sizeof (serial.inq_serial)); } } else { strncpy((char *)serial.inq_serial, "Unsupported", sizeof (serial.inq_serial)); } /* * we are adding serial number information * from 0x80. If length is less than 39, * then we want to increase length to 52 to * reflect the fact that we have serial number * information */ if (inq.inq_len < 39) { inq.inq_len = 52; } print_inq_data(argv[path_index], map->entry[mapIndex].ScsiId.OSDeviceName, inq, serial.inq_serial, sizeof (serial.inq_serial)); if (! wwnCompare) { found = 1; break; } } else { fprintf(stderr, MSGSTR(2430, "Error: I/O failure communicating with %s "), map->entry[mapIndex].ScsiId.OSDeviceName); printStatus(status); fprintf(stderr, "\n"); } } } if (found == 1) { break; } } if (found == 1) { break; } } if (physical) { free(physical); } if (!goodPath) { fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), argv[path_index]); fprintf(stderr, "\n"); ret = -1; } } return (ret); } int fchba_dump_map(char **argv) { int path_index = 0; uint64_t wwn; uint64_t lun = 0; HBA_HANDLE handle; HBA_ADAPTERATTRIBUTES hbaAttrs; HBA_PORTATTRIBUTES portAttrs; HBA_PORTATTRIBUTES discPortAttrs; HBA_FCPTARGETMAPPINGV2 *map; HBA_STATUS status; int count, adapterIndex, portIndex, mapIndex, discIndex; char name[256], *physical = NULL, *comp_phys = NULL; L_inquiry inq; struct scsi_extended_sense sense; HBA_UINT8 scsiStatus; int matched; int done; uint32_t inquirySize = sizeof (inq), senseSize = sizeof (sense); boolean_t goodPath = B_FALSE; int ret = 0; uint32_t responseSize = DEFAULT_LUN_LENGTH; uchar_t raw_luns[DEFAULT_LUN_LENGTH]; struct rep_luns_rsp *lun_resp; if (loadLibrary()) { return (-1); } for (path_index = 0; argv[path_index] != NULL; path_index++) { goodPath = B_FALSE; if (is_wwn(argv[path_index])) { (void) sscanf(argv[path_index], "%016llx", &wwn); } else if (!is_path(argv[path_index])) { fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), argv[path_index]); fprintf(stderr, "\n"); ret = -1; continue; } count = getNumberOfAdapters(); done = 0; /* Loop over all HBAs */ for (adapterIndex = 0; adapterIndex < count && !done; adapterIndex ++) { if (skip_hba(adapterIndex)) { continue; } status = HBA_GetAdapterName(adapterIndex, (char *)&name); if (status != HBA_STATUS_OK) { /* May have been DR'd */ continue; } handle = HBA_OpenAdapter(name); if (handle == 0) { /* May have been DR'd */ continue; } if (getAdapterAttrs(handle, name, &hbaAttrs)) { /* Should never happen */ HBA_CloseAdapter(handle); continue; } /* Loop over all HBA Ports */ for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts && !done; portIndex++) { if (getAdapterPortAttrs(handle, name, portIndex, &portAttrs)) { continue; } matched = 0; if (is_wwn(argv[path_index])) { if (wwn == wwnConversion( portAttrs.NodeWWN.wwn) || wwn == wwnConversion( portAttrs.PortWWN.wwn)) { matched = 1; } } else { if (is_path(argv[path_index]) && ((physical = get_slash_devices_from_osDevName( argv[path_index], STANDARD_DEVNAME_HANDLING)) != NULL) && ((comp_phys = get_slash_devices_from_osDevName( portAttrs.OSDeviceName, STANDARD_DEVNAME_HANDLING)) != NULL)) { char *tmp = strstr(physical, ":devctl"); if (tmp) { *tmp = '\0'; } else { tmp = strstr(physical, ":fc"); if (tmp) { *tmp = '\0'; } } if (strstr(comp_phys, physical)) { matched = 1; } } if (physical) { free(physical); physical = NULL; } if (comp_phys) { free(comp_phys); comp_phys = NULL; } } if (!fetch_mappings(handle, portAttrs.PortWWN, &map)) { mapIndex = match_mappings(argv[path_index], map); if (mapIndex >= 0) { matched = 1; } } else { continue; } if (matched) { goodPath = B_TRUE; printf(MSGSTR(2095, "Pos Port_ID Hard_Addr Port WWN" " Node WWN Type\n")); for (discIndex = 0; discIndex < portAttrs.NumberofDiscoveredPorts; discIndex++) { if (getDiscPortAttrs(handle, name, portIndex, discIndex, &discPortAttrs)) { /* Move on to the next target */ continue; } printf("%-4d %-6x %-6x %016llx %016llx", discIndex, discPortAttrs.PortFcId, 0, wwnConversion(discPortAttrs.PortWWN.wwn), wwnConversion(discPortAttrs.NodeWWN.wwn)); /* * devices are not all required to respond to * Scsi Inquiry calls sent to LUN 0. We must * fisrt issue a ReportLUN and then send the * SCSI Inquiry call to the first LUN Returned * from the ReportLUN call */ memset(&sense, 0, sizeof (sense)); status = HBA_ScsiReportLUNsV2(handle, portAttrs.PortWWN, discPortAttrs.PortWWN, (void *)raw_luns, &responseSize, &scsiStatus, (void *)&sense, &senseSize); if (status == HBA_STATUS_OK) { lun_resp = (struct rep_luns_rsp *) (unsigned long)raw_luns; lun = ntohll( wwnConversion(lun_resp->lun[0].val)); } else { /* * in case we are unable to retrieve report * LUN data, we will blindly try sending the * INQUIRY to lun 0. */ lun = 0; } memset(&sense, 0, sizeof (sense)); status = HBA_ScsiInquiryV2(handle, portAttrs.PortWWN, discPortAttrs.PortWWN, lun, 0, 0, &inq, &inquirySize, &scsiStatus, &sense, &senseSize); if (status != HBA_STATUS_OK) { inq.inq_dtype = 0x1f; } print_fabric_dtype_prop(portAttrs.PortWWN.wwn, map->entry[mapIndex].FcpId.PortWWN.wwn, inq.inq_dtype); } /* Now dump this HBA's stats */ printf("%-4d %-6x %-6x %016llx %016llx", discIndex, portAttrs.PortFcId, 0, wwnConversion(portAttrs.PortWWN.wwn), wwnConversion(portAttrs.NodeWWN.wwn)); print_fabric_dtype_prop(portAttrs.PortWWN.wwn, portAttrs.PortWWN.wwn, 0x1f); done = 1; } } } if (!goodPath) { fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), argv[path_index]); fprintf(stderr, "\n"); ret = -1; } } return (ret); } int fchba_display_link_status(char **argv) { int path_index = 0; uint64_t wwn; HBA_HANDLE handle; HBA_ADAPTERATTRIBUTES hbaAttrs; HBA_PORTATTRIBUTES portAttrs; HBA_PORTATTRIBUTES discPortAttrs; HBA_FCPTARGETMAPPINGV2 *map; HBA_STATUS status; int count, adapterIndex, portIndex, discIndex; char name[256], *physical = NULL, *comp_phys = NULL; int matched; struct fc_rls_acc_params rls; uint32_t rls_size = sizeof (rls); boolean_t goodPath = B_FALSE; int ret = 0; if (loadLibrary()) { return (-1); } for (path_index = 0; argv[path_index] != NULL; path_index++) { goodPath = B_FALSE; if (is_wwn(argv[path_index])) { (void) sscanf(argv[path_index], "%016llx", &wwn); } else if (!is_path(argv[path_index])) { fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), argv[path_index]); fprintf(stderr, "\n"); ret = -1; continue; } count = getNumberOfAdapters(); /* Loop over all HBAs */ for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) { if (skip_hba(adapterIndex)) { continue; } status = HBA_GetAdapterName(adapterIndex, (char *)&name); if (status != HBA_STATUS_OK) { /* May have been DR'd */ continue; } handle = HBA_OpenAdapter(name); if (handle == 0) { /* May have been DR'd */ continue; } if (getAdapterAttrs(handle, name, &hbaAttrs)) { /* Should never happen */ HBA_CloseAdapter(handle); continue; } /* Loop over all HBA Ports */ for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts; portIndex++) { if (getAdapterPortAttrs(handle, name, portIndex, &portAttrs)) { continue; } matched = 0; if (is_wwn(argv[path_index])) { if (wwn == wwnConversion( portAttrs.NodeWWN.wwn) || wwn == wwnConversion( portAttrs.PortWWN.wwn)) { matched = 1; } } else { if (is_path(argv[path_index]) && ((physical = get_slash_devices_from_osDevName( argv[path_index], STANDARD_DEVNAME_HANDLING)) != NULL) && ((comp_phys = get_slash_devices_from_osDevName( portAttrs.OSDeviceName, STANDARD_DEVNAME_HANDLING)) != NULL)) { char *tmp = strstr(physical, ":devctl"); if (tmp) { *tmp = '\0'; } else { tmp = strstr(physical, ":fc"); if (tmp) { *tmp = '\0'; } } if (strstr(comp_phys, physical)) { matched = 1; } } if (physical) { free(physical); physical = NULL; } if (comp_phys) { free(comp_phys); comp_phys = NULL; } } if (!matched) { if (fetch_mappings(handle, portAttrs.PortWWN, &map)) { continue; } } if (matched || match_mappings(argv[path_index], map) >= 0) { goodPath = B_TRUE; fprintf(stdout, MSGSTR(2007, "\nLink Error Status " "information for loop:%s\n"), argv[path_index]); fprintf(stdout, MSGSTR(2008, "al_pa lnk fail " " sync loss signal loss sequence err" " invalid word CRC\n")); for (discIndex = 0; discIndex < portAttrs.NumberofDiscoveredPorts; discIndex++) { if (getDiscPortAttrs(handle, name, portIndex, discIndex, &discPortAttrs)) { continue; } status = HBA_SendRLS(handle, portAttrs.PortWWN, discPortAttrs.PortWWN, &rls, &rls_size); if (status != HBA_STATUS_OK) { memset(&rls, 0xff, sizeof (rls)); } if ((rls.rls_link_fail == 0xffffffff) && (rls.rls_sync_loss == 0xffffffff) && (rls.rls_sig_loss == 0xffffffff) && (rls.rls_prim_seq_err == 0xffffffff) && (rls.rls_invalid_word == 0xffffffff) && (rls.rls_invalid_crc == 0xffffffff)) { fprintf(stdout, "%x\t%-12d%-12d%-14d%-15d%-15d%-12d\n", discPortAttrs.PortFcId, rls.rls_link_fail, rls.rls_sync_loss, rls.rls_sig_loss, rls.rls_prim_seq_err, rls.rls_invalid_word, rls.rls_invalid_crc); } else { fprintf(stdout, "%x\t%-12u%-12u%-14u%-15u%-15u%-12u\n", discPortAttrs.PortFcId, rls.rls_link_fail, rls.rls_sync_loss, rls.rls_sig_loss, rls.rls_prim_seq_err, rls.rls_invalid_word, rls.rls_invalid_crc); } } /* Now dump this HBA's stats */ status = HBA_SendRLS(handle, portAttrs.PortWWN, portAttrs.PortWWN, &rls, &rls_size); if (status != HBA_STATUS_OK) { memset(&rls, 0xff, sizeof (rls)); } if ((rls.rls_link_fail == 0xffffffff) && (rls.rls_sync_loss == 0xffffffff) && (rls.rls_sig_loss == 0xffffffff) && (rls.rls_prim_seq_err == 0xffffffff) && (rls.rls_invalid_word == 0xffffffff) && (rls.rls_invalid_crc == 0xffffffff)) { fprintf(stdout, "%x\t%-12d%-12d%-14d%-15d%-15d%-12d\n", portAttrs.PortFcId, rls.rls_link_fail, rls.rls_sync_loss, rls.rls_sig_loss, rls.rls_prim_seq_err, rls.rls_invalid_word, rls.rls_invalid_crc); } else { fprintf(stdout, "%x\t%-12u%-12u%-14u%-15u%-15u%-12u\n", portAttrs.PortFcId, rls.rls_link_fail, rls.rls_sync_loss, rls.rls_sig_loss, rls.rls_prim_seq_err, rls.rls_invalid_word, rls.rls_invalid_crc); } } } } if (!goodPath) { fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), argv[path_index]); fprintf(stderr, "\n"); ret = -1; } } (void) fprintf(stdout, MSGSTR(2009, "NOTE: These LESB counts are not" " cleared by a reset, only power cycles.\n" "These counts must be compared" " to previously read counts.\n")); return (ret); } typedef struct _PathInformation { char pathClass[MAXPATHLEN]; char pathState[MAXPATHLEN]; int32_t pathInfoState; int32_t pathInfoExternalState; } PathInformation; struct lun_tracking { HBA_FCPSCSIENTRYV2 map; HBA_WWN hba_pwwn; char hba_path[MAXPATHLEN]; PathInformation info; /* Points to another lun_tracking instance with the same map->LUID */ struct lun_tracking *next_path; /* Points to next lun_tracking with a different map->LUID */ struct lun_tracking *next_lun; }; static const char VHCI_COMPONENT[] = "scsi_vhci"; static void scsi_vhci_details(struct lun_tracking *lun) { HBA_FCPSCSIENTRYV2 entry = lun->map; int retval = 0; int pathcnt, i, count, found = 0; char temppath[MAXPATHLEN]; char buf[MAXPATHLEN]; char *path_state[5]; char *phys_path = get_slash_devices_from_osDevName( entry.ScsiId.OSDeviceName, STANDARD_DEVNAME_HANDLING); char *devPath = NULL; char *trailingCruft = NULL; char devaddr[MAXPATHLEN]; sv_iocdata_t ioc; int prop_buf_size = SV_PROP_MAX_BUF_SIZE; char *path_class_val = NULL; char client_path[MAXPATHLEN]; char phci_path[MAXPATHLEN]; /* Only proceed if we are an mpxio path */ if (phys_path == NULL || strstr(phys_path, VHCI_COMPONENT) == NULL) { return; } path_state[0] = MSGSTR(2400, "INIT"); path_state[1] = MSGSTR(2401, "ONLINE"); path_state[2] = MSGSTR(2402, "STANDBY"); path_state[3] = MSGSTR(2403, "FAULT"); path_state[4] = MSGSTR(2404, "OFFLINE"); sprintf(devaddr, "%016llx,%x", wwnConversion( entry.FcpId.PortWWN.wwn), entry.ScsiId.ScsiOSLun); /* First get the controller path */ sprintf(temppath, "/dev/cfg/c%d", entry.ScsiId.ScsiBusNumber); if ((count = readlink(temppath, buf, sizeof (buf)))) { buf[count] = '\0'; /* Now skip over the leading "../.." */ devPath = strstr(buf, "/devices/"); if (devPath == NULL) { strcpy(lun->info.pathClass, "Unavailable"); strcpy(lun->info.pathState, "Unavailable"); free(phys_path); return; } /* Now chop off the trailing ":xxx" portion if present */ trailingCruft = strrchr(buf, ':'); if (trailingCruft) { trailingCruft[0] = '\0'; } } else { strcpy(lun->info.pathClass, "Unavailable"); strcpy(lun->info.pathState, "Unavailable"); free(phys_path); return; } ioc.client = client_path; ioc.phci = phci_path; retval = get_scsi_vhci_pathinfo(phys_path, &ioc, &pathcnt); if (retval != 0) { print_errString(retval, NULL); exit(-1); } for (i = 0; i < pathcnt; i++) { nvlist_t *nvl; if (strstr(devPath, ioc.ret_buf[i].device.ret_phci)) { /* This could break someday if MPxIO changes devaddr */ if (strstr(ioc.ret_buf[i].ret_addr, devaddr)) { retval = nvlist_unpack(ioc.ret_buf[i].ret_prop.buf, prop_buf_size, &nvl, 0); if (retval != 0) { strcpy(lun->info.pathClass, "UNKNOWN PROB"); } else { strcpy(lun->info.pathState, path_state[ioc.ret_buf[i].ret_state]); lun->info.pathInfoState = ioc.ret_buf[i].ret_state; lun->info.pathInfoExternalState = ioc.ret_buf[i].ret_ext_state; if (nvlist_lookup_string(nvl, "path-class", &path_class_val) == 0) { strcpy(lun->info.pathClass, path_class_val); } else { strcpy(lun->info.pathClass, "UNKNOWN"); } } nvlist_free(nvl); found++; break; } } } if (!found) { strcpy(lun->info.pathClass, "Unavailable"); strcpy(lun->info.pathState, "Unavailable"); } free(phys_path); /* free everything we alloced */ for (i = 0; i < ioc.buf_elem; i++) { free(ioc.ret_buf[i].ret_prop.buf); free(ioc.ret_buf[i].ret_prop.ret_buf_size); } free(ioc.ret_buf); } /* Utility routine to add new entries to the list (ignores dups) */ static void add_lun_path(struct lun_tracking *head, HBA_FCPSCSIENTRYV2 *map, HBA_WWN pwwn, char *path) { struct lun_tracking *tmp = NULL, *cmp = NULL; for (tmp = head; tmp != NULL; tmp = tmp->next_lun) { if (memcmp(&tmp->map.LUID, &map->LUID, sizeof (HBA_LUID)) == 0) { /* Ensure this isn't a duplicate */ for (cmp = tmp; cmp->next_path != NULL; cmp = cmp->next_path) { if (memcmp(&cmp->map, map, sizeof (cmp->map)) == 0) { return; } } if (memcmp(&cmp->map, map, sizeof (cmp->map)) == 0) { return; } /* We have a new entry to add */ cmp->next_path = (struct lun_tracking *)calloc(1, sizeof (struct lun_tracking)); cmp = cmp->next_path; (void) memcpy(&cmp->map, map, sizeof (cmp->map)); (void) memcpy(&cmp->hba_pwwn, &pwwn, sizeof (cmp->hba_pwwn)); (void) snprintf(cmp->hba_path, MAXPATHLEN, path); scsi_vhci_details(cmp); return; } } /* Append a new LUN at the end of the list */ for (tmp = head; tmp->next_lun != NULL; tmp = tmp->next_lun) {} tmp->next_lun = (struct lun_tracking *)calloc(1, sizeof (struct lun_tracking)); tmp = tmp->next_lun; (void) memcpy(&tmp->map, map, sizeof (tmp->map)); (void) memcpy(&tmp->hba_pwwn, &pwwn, sizeof (tmp->hba_pwwn)); (void) snprintf(tmp->hba_path, MAXPATHLEN, path); scsi_vhci_details(tmp); } /*ARGSUSED*/ int fchba_display_config(char **argv, int option_t_input, int argc) { int path_index = 0; uint64_t wwn; uint64_t lun = 0; HBA_HANDLE handle; HBA_ADAPTERATTRIBUTES hbaAttrs; HBA_PORTATTRIBUTES portAttrs; HBA_FCPTARGETMAPPINGV2 *map; HBA_STATUS status; int count, adapterIndex, portIndex; char name[256]; L_inquiry inq; struct scsi_extended_sense sense; struct page80 serial; HBA_UINT8 scsiStatus; uint32_t inquirySize = sizeof (inq), senseSize = sizeof (sense); uint32_t serialSize = sizeof (serial); struct mode_page *pg_hdr; uchar_t *pg_buf; float lunMbytes; struct capacity_data_struct cap_data; uint32_t cap_data_size = sizeof (cap_data); struct mode_header_g1 *mode_header_ptr; int offset; char *phys_path = NULL; int mpxio = 0; int wwnCompare = 0; char *physical = NULL; struct lun_tracking *head = NULL; boolean_t goodPath = B_FALSE; int ret = 0; if ((status = loadLibrary())) { return (-1); } for (path_index = 0; argv[path_index] != NULL; path_index++) { goodPath = B_FALSE; if (is_wwn(argv[path_index])) { (void) sscanf(argv[path_index], "%016llx", &wwn); wwnCompare = 1; } else if (!is_path(argv[path_index])) { fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), argv[path_index]); fprintf(stderr, "\n"); ret = -1; continue; } if (!wwnCompare) { /* Convert the paths to phsyical paths */ physical = get_slash_devices_from_osDevName(argv[path_index], STANDARD_DEVNAME_HANDLING); if (!physical) { fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), argv[path_index]); fprintf(stderr, "\n"); ret = -1; continue; } } count = getNumberOfAdapters(); /* * We have to loop twice to ensure we don't miss any * extra paths for other targets in a multi-target device */ /* First check WWN/path comparisons */ for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) { if (skip_hba(adapterIndex)) { continue; } status = HBA_GetAdapterName(adapterIndex, (char *)&name); if (status != HBA_STATUS_OK) { /* May have been DR'd */ continue; } handle = HBA_OpenAdapter(name); if (handle == 0) { /* May have been DR'd */ continue; } if (getAdapterAttrs(handle, name, &hbaAttrs)) { /* Should never happen */ HBA_CloseAdapter(handle); continue; } /* Loop over all HBA Ports */ for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts; portIndex++) { int matched = 0; int mapIndex; char *tmp; if (getAdapterPortAttrs(handle, name, portIndex, &portAttrs)) { continue; } if (fetch_mappings(handle, portAttrs.PortWWN, &map)) { continue; } for (mapIndex = 0; mapIndex < map->NumberOfEntries; mapIndex ++) { matched = 0; if (wwnCompare) { if (wwn == wwnConversion( map->entry[mapIndex].FcpId.NodeWWN.wwn) || wwn == wwnConversion( map->entry[mapIndex].FcpId.PortWWN.wwn)) { matched = 1; } } else { tmp = get_slash_devices_from_osDevName( map->entry[mapIndex].ScsiId.OSDeviceName, STANDARD_DEVNAME_HANDLING); if ((tmp != NULL) && (strncmp(physical, tmp, MAXPATHLEN) == 0)) { matched = 1; free(tmp); } } if (matched && head == NULL) { goodPath = B_TRUE; head = (struct lun_tracking *)calloc(1, sizeof (struct lun_tracking)); (void) memcpy(&head->map, &map->entry[mapIndex], sizeof (head->map)); (void) memcpy(&head->hba_pwwn, &portAttrs.PortWWN, sizeof (head->hba_pwwn)); (void) snprintf(head->hba_path, MAXPATHLEN, portAttrs.OSDeviceName); scsi_vhci_details(head); } else if (matched) { goodPath = B_TRUE; add_lun_path(head, &map->entry[mapIndex], portAttrs.PortWWN, portAttrs.OSDeviceName); } } } } if (physical) { free(physical); } /* Now do it again and look for matching LUIDs (aka GUIDs) */ for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) { if (skip_hba(adapterIndex)) { continue; } status = HBA_GetAdapterName(adapterIndex, (char *)&name); if (status != HBA_STATUS_OK) { /* May have been DR'd */ continue; } handle = HBA_OpenAdapter(name); if (handle == 0) { /* May have been DR'd */ continue; } if (getAdapterAttrs(handle, name, &hbaAttrs)) { /* Should never happen */ HBA_CloseAdapter(handle); continue; } /* Loop over all HBA Ports */ for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts; portIndex++) { int matched = 0; int mapIndex; if (getAdapterPortAttrs(handle, name, portIndex, &portAttrs)) { continue; } if (fetch_mappings(handle, portAttrs.PortWWN, &map)) { continue; } for (mapIndex = 0; mapIndex < map->NumberOfEntries; mapIndex ++) { struct lun_tracking *outer; matched = 0; for (outer = head; outer != NULL; outer = outer->next_lun) { struct lun_tracking *inner; for (inner = outer; inner != NULL; inner = inner->next_path) { if (memcmp(&inner->map.LUID, &map->entry[mapIndex].LUID, sizeof (HBA_LUID)) == 0) { matched = 1; break; } } if (matched) { break; } } if (matched && head == NULL) { goodPath = B_TRUE; head = (struct lun_tracking *)calloc(1, sizeof (struct lun_tracking)); (void) memcpy(&head->map, &map->entry[mapIndex], sizeof (head->map)); (void) memcpy(&head->hba_pwwn, &portAttrs.PortWWN, sizeof (head->hba_pwwn)); (void) snprintf(head->hba_path, MAXPATHLEN, portAttrs.OSDeviceName); scsi_vhci_details(head); } else if (matched) { goodPath = B_TRUE; add_lun_path(head, &map->entry[mapIndex], portAttrs.PortWWN, portAttrs.OSDeviceName); } } } } if (!goodPath) { fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), argv[path_index]); fprintf(stderr, "\n"); ret = -1; /* Just bomb out instead of going on */ return (ret); } } /* Now display all the LUNs that we found that matched */ { struct lun_tracking *first_time; struct lun_tracking *tmp_path; for (first_time = head; first_time != NULL; first_time = first_time->next_lun) { struct lun_tracking *path; phys_path = get_slash_devices_from_osDevName( first_time->map.ScsiId.OSDeviceName, STANDARD_DEVNAME_HANDLING); /* Change behavior if this is an MPxIO device */ if (phys_path != NULL) { if (strstr(phys_path, VHCI_COMPONENT) != NULL) { mpxio = 1; } } for (tmp_path = first_time; tmp_path != NULL; tmp_path = tmp_path->next_path) { if (mpxio && (strncmp(tmp_path->info.pathState, "ONLINE", strlen(tmp_path->info.pathState)))) { /* continue to next online path */ continue; } status = HBA_OpenAdapterByWWN(&handle, tmp_path->hba_pwwn); if (status != HBA_STATUS_OK) { fprintf(stderr, MSGSTR(2431, "Error: Failed to get handle for %s "), tmp_path->hba_path); printStatus(status); fprintf(stderr, "\n"); /* continue to next path */ continue; } lun = tmp_path->map.FcpId.FcpLun; memset(&inq, 0, sizeof (inq)); memset(&sense, 0, sizeof (sense)); status = HBA_ScsiInquiryV2(handle, tmp_path->hba_pwwn, tmp_path->map.FcpId.PortWWN, lun, 0, 0, &inq, &inquirySize, &scsiStatus, &sense, &senseSize); if (status == HBA_STATUS_OK) { break; } HBA_CloseAdapter(handle); } if (tmp_path == NULL) { fprintf(stderr, MSGSTR(2430, "Error: I/O failure communicating with %s "), first_time->map.ScsiId.OSDeviceName); printStatus(status); fprintf(stderr, "\n"); continue; } switch ((inq.inq_dtype & DTYPE_MASK)) { case DTYPE_DIRECT: fprintf(stdout, MSGSTR(121, "DEVICE PROPERTIES for disk: %s\n"), first_time->map.ScsiId.OSDeviceName); break; case DTYPE_SEQUENTIAL: /* Tape */ fprintf(stdout, MSGSTR(2249, "DEVICE PROPERTIES for tape: %s\n"), first_time->map.ScsiId.OSDeviceName); break; default: fprintf(stdout, MSGSTR(2250, "DEVICE PROPERTIES for: %s\n"), first_time->map.ScsiId.OSDeviceName); break; } fprintf(stdout, " "); fprintf(stdout, MSGSTR(3, "Vendor:")); fprintf(stdout, "\t\t"); print_chars(inq.inq_vid, sizeof (inq.inq_vid), 0); fprintf(stdout, MSGSTR(2115, "\n Product ID:\t\t")); print_chars(inq.inq_pid, sizeof (inq.inq_pid), 0); fprintf(stdout, "\n "); fprintf(stdout, MSGSTR(2119, "Revision:")); fprintf(stdout, "\t\t"); print_chars(inq.inq_revision, sizeof (inq.inq_revision), 0); fprintf(stdout, "\n "); fprintf(stdout, MSGSTR(17, "Serial Num:")); fprintf(stdout, "\t\t"); (void) fflush(stdout); /* * Call the inquiry cmd on page 0x80 only if the vendor * supports page 0x80. */ if ((find_supported_inq_page(handle, first_time->hba_pwwn, first_time->map.FcpId.PortWWN, lun, 0x80))) { memset(&serial, 0, sizeof (serial)); status = HBA_ScsiInquiryV2(handle, first_time->hba_pwwn, first_time->map.FcpId.PortWWN, lun, 1, 0x80, &serial, &serialSize, &scsiStatus, &sense, &senseSize); if (status == HBA_STATUS_OK) { print_chars(serial.inq_serial, sizeof (serial.inq_serial), 0); } else { fprintf(stdout, MSGSTR(2506, "Unsupported")); } } else { fprintf(stdout, MSGSTR(2506, "Unsupported")); } HBA_CloseAdapter(handle); if ((inq.inq_dtype & DTYPE_MASK) == DTYPE_DIRECT) { /* Read capacity wont work on standby paths, so try till OK */ for (tmp_path = first_time; tmp_path != NULL; tmp_path = tmp_path->next_path) { if (mpxio && (strncmp(tmp_path->info.pathState, "ONLINE", strlen(tmp_path->info.pathState)))) { /* continue to next online path */ continue; } status = HBA_OpenAdapterByWWN(&handle, tmp_path->hba_pwwn); if (status != HBA_STATUS_OK) { /* continue to next path */ continue; } status = HBA_ScsiReadCapacityV2(handle, tmp_path->hba_pwwn, tmp_path->map.FcpId.PortWWN, tmp_path->map.FcpId.FcpLun, &cap_data, &cap_data_size, &scsiStatus, &sense, &senseSize); if (status == HBA_STATUS_OK) { break; } else if (status == HBA_STATUS_SCSI_CHECK_CONDITION && sense.es_key == KEY_UNIT_ATTENTION) { /* * retry for check-condition state when unit attention * condition has been established */ status = HBA_ScsiReadCapacityV2(handle, tmp_path->hba_pwwn, tmp_path->map.FcpId.PortWWN, tmp_path->map.FcpId.FcpLun, &cap_data, &cap_data_size, &scsiStatus, &sense, &senseSize); if (status == HBA_STATUS_OK) { break; } } HBA_CloseAdapter(handle); } } if (handle != HBA_HANDLE_INVALID) { HBA_CloseAdapter(handle); } if (status != HBA_STATUS_OK) { /* Make sure we don't display garbage */ cap_data.block_size = 0; cap_data.last_block_addr = 0; } if (cap_data.block_size > 0 && cap_data.last_block_addr > 0) { lunMbytes = ntohl(cap_data.last_block_addr) + 1; lunMbytes *= ntohl(cap_data.block_size); lunMbytes /= (float)(1024*1024); fprintf(stdout, "\n "); fprintf(stdout, MSGSTR(60, "Unformatted capacity:\t%6.3f MBytes"), lunMbytes); } fprintf(stdout, "\n"); /* * get mode page information for FC device. * do not do mode sense if this is a tape device. * mode sense will rewind the tape */ if ((inq.inq_dtype & DTYPE_MASK) != DTYPE_SEQUENTIAL) { if (get_mode_page(first_time->map.ScsiId.OSDeviceName, &pg_buf) == 0) { mode_header_ptr = (struct mode_header_g1 *) (void *)pg_buf; offset = sizeof (struct mode_header_g1) + ntohs(mode_header_ptr->bdesc_length); pg_hdr = (struct mode_page *)&pg_buf[offset]; while (offset < (ntohs(mode_header_ptr->length) + sizeof (mode_header_ptr->length))) { if (pg_hdr->code == MODEPAGE_CACHING) { struct mode_caching *pg8_buf; pg8_buf = (struct mode_caching *) (void *)pg_hdr; if (pg8_buf->wce) { fprintf(stdout, MSGSTR(2122, " Write Cache:\t\t" "Enabled\n")); } if (pg8_buf->rcd == 0) { fprintf(stdout, MSGSTR(2123, " Read Cache:\t\t" "Enabled\n")); fprintf(stdout, MSGSTR(2509, " Minimum prefetch:\t0x%x\n" " Maximum prefetch:\t0x%x\n"), pg8_buf->min_prefetch, pg8_buf->max_prefetch); } break; } offset += pg_hdr->length + sizeof (struct mode_page); pg_hdr = (struct mode_page *)&pg_buf[offset]; } } } fprintf(stdout, " %s\t\t", MSGSTR(35, "Device Type:")); if ((inq.inq_dtype & DTYPE_MASK) < 0x10) { fprintf(stdout, "%s\n", dtype[inq.inq_dtype & DTYPE_MASK]); } else if ((inq.inq_dtype & DTYPE_MASK) < 0x1f) { fprintf(stdout, MSGSTR(2432, "Reserved")); } else { /* dtype of 0x1f is returned */ fprintf(stdout, MSGSTR(2433, "Unknown")); } fprintf(stdout, MSGSTR(2128, " Path(s):\n")); fprintf(stdout, "\n"); fprintf(stdout, " %s\n", first_time->map.ScsiId.OSDeviceName); if (phys_path != NULL) { fprintf(stdout, " %s\n", phys_path); } /* Now display all paths to this LUN */ for (path = first_time; path != NULL; path = path->next_path) { /* Display the controller information */ fprintf(stdout, MSGSTR(2303, " Controller \t%s\n"), path->hba_path); fprintf(stdout, MSGSTR(2507, " Device Address\t\t%016llx,%x\n"), wwnConversion( path->map.FcpId.PortWWN.wwn), path->map.ScsiId.ScsiOSLun); fprintf(stdout, MSGSTR(2508, " Host controller port WWN\t%016llx\n"), wwnConversion(path->hba_pwwn.wwn)); if (mpxio) { fprintf(stdout, MSGSTR(2305, " Class\t\t\t%s\n"), path->info.pathClass); fprintf(stdout, MSGSTR(2306, " State\t\t\t%s\n"), path->info.pathState); } if (phys_path != NULL) { free(phys_path); phys_path = NULL; } } printf("\n"); } } return (ret); } /* * handle expert-mode hotplug commands * * return 0 iff all is okay */ int fchba_hotplug_e(int todo, char **argv, int verbose_flag, int force_flag) { char *path_phys = NULL; int exit_code; devctl_hdl_t dcp; if (todo != DEV_ONLINE && todo != DEV_OFFLINE) { fprintf(stderr, "%s\n", strerror(ENOTSUP)); return (-1); } /* Convert the paths to phsyical paths */ path_phys = get_slash_devices_from_osDevName(argv[0], NOT_IGNORE_DANGLING_LINK); if (!path_phys) { fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"), argv[0]); fprintf(stderr, "\n"); return (-1); } if (verbose_flag) { (void) fprintf(stdout, MSGSTR(5516, "phys path = \"%s\"\n"), path_phys); } /* acquire rights to hack on device */ if ((dcp = devctl_device_acquire(path_phys, force_flag ? 0 : DC_EXCL)) == NULL) { (void) fprintf(stderr, MSGSTR(5517, "Error: can't acquire \"%s\": %s\n"), path_phys, strerror(errno)); return (1); } switch (todo) { case DEV_ONLINE: exit_code = devctl_device_online(dcp); break; case DEV_OFFLINE: exit_code = devctl_device_offline(dcp); break; default: exit_code = 0; } if (exit_code != 0) { perror(MSGSTR(5518, "devctl")); } /* all done now -- release device */ devctl_release(dcp); if (path_phys) { free(path_phys); } return (exit_code); } /* * Returns non zero if we should use FC-HBA. * For x86, luxadm uses FC-HBA. */ int use_fchba() { #ifdef __x86 return (1); #else return (0); #endif } /* * Returns non-zero if we should skip the HBA at index "i" */ int skip_hba(int i) { HBA_LIBRARYATTRIBUTES lib_attrs; (void) HBA_GetVendorLibraryAttributes(i, &lib_attrs); if (strncmp(lib_attrs.VName, VSL_NAME, sizeof (lib_attrs.VName)) == 0) { return (0); } return (1); } /* * Function to determine if the given page is supported by vendor. */ int find_supported_inq_page(HBA_HANDLE handle, HBA_WWN hwwn, HBA_WWN pwwn, uint64_t lun, int page_num) { struct scsi_extended_sense sense; L_inquiry00 inq00; uchar_t *data; HBA_STATUS status = HBA_STATUS_ERROR; int index; HBA_UINT8 scsiStatus; uint32_t inqSize = sizeof (inq00); uint32_t senseSize = sizeof (sense); status = HBA_ScsiInquiryV2(handle, hwwn, pwwn, lun, 1, 0x00, &inq00, &inqSize, &scsiStatus, &sense, &senseSize); if (status == HBA_STATUS_OK) { data = (uchar_t *)&inq00; for (index = 4; (index <= inq00.len+3)&& (data[index] <= page_num); index ++) { if (data[index] == page_num) { return (1); } } } return (0); }