/* * Copyright(c) 2010 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Maintained at www.Open-FCoE.org */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "net_types.h" #include "fc_types.h" #include "fc_scsi.h" #include "fcoeadm_display.h" #include "fcoe_utils.h" #include "fcoemon_utils.h" #include "libopenfcoe.h" #include "sysfs_hba.h" /* Define FC4 Type */ #define FC_TYPE_FCP 0x08 /* SCSI FCP */ /* Constant defined in fcoe_def.h of fcoe driver */ #define FCOE_WORD_TO_BYTE 4 /* Minimum byte size of the received inquiry data */ #define MIN_INQ_DATA_SIZE 36 #define FCP_TARG_STR "FCP Target" #define SYSFS_HOST_DIR "/sys/class/fc_host" /* * Options for displaying target/LUN info. */ struct target_info_arguments { char *ifname; enum disp_style style; }; struct sa_nameval port_states[] = { { "Not Present", HBA_PORTSTATE_UNKNOWN }, { "Online", HBA_PORTSTATE_ONLINE }, { "Offline", HBA_PORTSTATE_OFFLINE }, { "Blocked", HBA_PORTSTATE_UNKNOWN }, { "Bypassed", HBA_PORTSTATE_BYPASSED }, { "Diagnostics", HBA_PORTSTATE_DIAGNOSTICS }, { "Linkdown", HBA_PORTSTATE_LINKDOWN }, { "Error", HBA_PORTSTATE_ERROR }, { "Loopback", HBA_PORTSTATE_LOOPBACK }, { "Deleted", HBA_PORTSTATE_UNKNOWN }, { NULL, 0 } }; /* * table of /sys port speed strings to HBA-API values. */ struct sa_nameval port_speeds[] = { { "Unknown", HBA_PORTSPEED_UNKNOWN }, { "1 Gbit", HBA_PORTSPEED_1GBIT }, { "2 Gbit", HBA_PORTSPEED_2GBIT }, { "4 Gbit", HBA_PORTSPEED_4GBIT }, { "10 Gbit", HBA_PORTSPEED_10GBIT }, { "8 Gbit", HBA_PORTSPEED_8GBIT }, { "16 Gbit", HBA_PORTSPEED_16GBIT }, { "32 Gbit", HBA_PORTSPEED_32GBIT }, { "20 Gbit", HBA_PORTSPEED_20GBIT }, { "40 Gbit", HBA_PORTSPEED_40GBIT }, { "Not Negotiated", HBA_PORTSPEED_NOT_NEGOTIATED }, { NULL, 0 } }; static int is_fcp_target(struct port_attributes *rp_info) { if (!strncmp(rp_info->roles, FCP_TARG_STR, strlen(FCP_TARG_STR))) return 0; return -EINVAL; } static void show_hba_info(struct hba_info *hba_info) { printf(" Description: %s\n", hba_info->model_description); printf(" Revision: %s\n", hba_info->hardware_version); printf(" Manufacturer: %s\n", hba_info->manufacturer); printf(" Serial Number: %s\n", hba_info->serial_number); printf(" Driver: %s %s\n", hba_info->driver_name, hba_info->driver_version); printf(" Number of Ports: %d\n", hba_info->nports); printf("\n"); } static void show_port_info(struct port_attributes *lp_info) { printf(" Symbolic Name: %s\n", lp_info->symbolic_name); printf(" OS Device Name: %s\n", lp_info->device_name); printf(" Node Name: %s\n", lp_info->node_name); printf(" Port Name: %s\n", lp_info->port_name); printf(" Fabric Name: %s\n", lp_info->fabric_name); printf(" Speed: %s\n", lp_info->speed); printf(" Supported Speed: %s\n", lp_info->supported_speeds); printf(" MaxFrameSize: %s\n", lp_info->maxframe_size); printf(" FC-ID (Port ID): %s\n", lp_info->port_id); printf(" State: %s\n", lp_info->port_state); /* TODO: Display PortSupportedFc4Types and PortActiveFc4Types */ } static void show_target_info(const char *symbolic_name, struct port_attributes *rp_info) { char *ifname; ifname = get_ifname_from_symbolic_name(symbolic_name); printf(" Interface: %s\n", ifname); printf(" Roles: %s\n", rp_info->roles); printf(" Node Name: %s\n", rp_info->node_name); printf(" Port Name: %s\n", rp_info->port_name); printf(" Target ID: %s\n", rp_info->scsi_target_id); printf(" MaxFrameSize: %s\n", rp_info->maxframe_size); printf(" OS Device Name: %s\n", rp_info->device_name); printf(" FC-ID (Port ID): %s\n", rp_info->port_id); printf(" State: %s\n", rp_info->port_state); printf("\n"); } static void show_short_lun_info_header(void) { printf(" LUN ID Device Name Capacity " "Block Size Description\n"); printf(" ------ ----------- ---------- ---------- " "----------------------------\n"); } static void sa_dir_crawl(char *dir_name, void (*func)(char *dirname, enum disp_style style), enum disp_style style) { DIR *dir; struct dirent *dp; void (*f)(char *dirname, enum disp_style style); char path[1024]; f = func; dir = opendir(dir_name); if (!dir) return; while ((dp = readdir(dir)) != NULL) { if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) continue; snprintf(path, sizeof(path), "%s/%s", dir_name, dp->d_name); f(path, style); } closedir(dir); } static char *format_capstr(uint64_t size, unsigned int blksize) { double cap_abbr; char *capstr; uint64_t cap; char *abbr; int ret; cap = size * blksize; cap_abbr = cap / (1024.0 * 1024.0); abbr = "MiB"; if (cap_abbr >= 1024) { cap_abbr /= 1024.0; abbr = "GiB"; } if (cap_abbr >= 1024) { cap_abbr /= 1024.0; abbr = "TiB"; } if (cap_abbr >= 1024) { cap_abbr /= 1024.0; abbr = "PiB"; } ret = asprintf(&capstr, "%0.2f %s", cap_abbr, abbr); if (ret == -1) return "Unknown"; return capstr; } static void show_full_lun_info(unsigned int hba, unsigned int port, unsigned int tgt, unsigned int lun) { char vendor[256]; char model[256]; char rev[256]; char *osname; char *capstr; uint64_t lba = 0; uint32_t blksize = 0; char path[1024]; char npath[1024]; DIR *dir; struct dirent *dp; struct port_attributes *rport_attrs; struct port_attributes *port_attrs; int rc; snprintf(path, sizeof(path), "/sys/class/scsi_device/%u:%u:%u:%u", hba, port, tgt, lun); rport_attrs = get_rport_attribs_by_device(path); if (!rport_attrs) return; port_attrs = get_port_attribs_by_device(path); if (!port_attrs) goto free_rport; strncat(path, "/device/", sizeof(path) - strlen(path) - 1); sa_sys_read_line(path, "rev", rev, sizeof(rev)); sa_sys_read_line(path, "model", model, sizeof(model)); sa_sys_read_line(path, "vendor", vendor, sizeof(vendor)); strncat(path, "block", sizeof(path) - strlen(path) - 1); dir = opendir(path); if (!dir) goto free_port; while ((dp = readdir(dir)) != NULL) { if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) continue; osname = dp->d_name; rc = snprintf(npath, sizeof(npath), "%s/%s/", path, osname); if (rc < 0 || rc >= sizeof(npath)) { /* error or truncation, bailing out */ return; } sa_sys_read_u64(npath, "size", &lba); rc = snprintf(npath, sizeof(npath), "%s/%s/queue/", path, osname); if (rc < 0 || rc >= sizeof(npath)) { /* error or truncation, bailing out */ return; } sa_sys_read_u32(npath, "hw_sector_size", &blksize); } closedir(dir); /* Show lun info */ printf(" LUN #%d Information:\n", lun); printf(" OS Device Name: %s\n", osname); printf(" Description: %s %s (rev %s)\n", vendor, model, rev); printf(" Ethernet Port FCID: %s\n", port_attrs->port_id); printf(" Target FCID: %s\n", rport_attrs->port_id); if (tgt == 0xFFFFFFFFU) printf(" Target ID: (None)\n"); else printf(" Target ID: %u\n", tgt); printf(" LUN ID: %d\n", lun); capstr = format_capstr(lba, blksize); printf(" Capacity: %s\n", capstr); printf(" Capacity in Blocks: %" PRIu64 "\n", lba); printf(" Block Size: %" PRIu32 " bytes\n", blksize); printf(" Status: Attached\n"); printf("\n"); free_rport: free(rport_attrs); free_port: free(port_attrs); } static void show_short_lun_info(unsigned int hba, unsigned int port, unsigned int tgt, unsigned int lun) { struct dirent *dp; char vendor[256]; char path[1024]; char npath[1024]; char model[256]; char rev[256]; DIR *dir; uint32_t blksize = 0; char *capstr = "Unknown"; char *osname = "Unknown"; uint64_t size; int rc; snprintf(path, sizeof(path), "/sys/class/scsi_device/%u:%u:%u:%u/device/", hba, port, tgt, lun); sa_sys_read_line(path, "rev", rev, sizeof(rev)); sa_sys_read_line(path, "model", model, sizeof(model)); sa_sys_read_line(path, "vendor", vendor, sizeof(vendor)); strncat(path, "block", sizeof(path) - strlen(path) - 1); dir = opendir(path); if (!dir) return; while ((dp = readdir(dir)) != NULL) { if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) continue; osname = dp->d_name; rc = snprintf(npath, sizeof(npath), "%s/%s/", path, osname); if (rc < 0 || rc >= sizeof(npath)) { /* error or truncation, bailing out */ return; } sa_sys_read_u64(npath, "size", &size); rc = snprintf(npath, sizeof(npath), "%s/%s/queue/", path, osname); if (rc < 0 || rc >= sizeof(npath)) { /* error or truncation, bailing out */ return; } sa_sys_read_u32(npath, "hw_sector_size", &blksize); } closedir(dir); capstr = format_capstr(size, blksize); /* Show the LUN info */ printf("%10d %-11s %10s %7d %s %s (rev %s)\n", lun, osname, capstr, blksize, vendor, model, rev); free(capstr); return; } static void list_scsi_device(char *d_name, enum disp_style style) { unsigned int port; unsigned int hba; unsigned int tgt; unsigned int lun; char *last; last = strrchr(d_name, '/'); if (sscanf(last, "/%u:%u:%u:%u", &hba, &port, &tgt, &lun) != 4) return; if (style == DISP_TARG) show_short_lun_info(hba, port, tgt, lun); else show_full_lun_info(hba, port, tgt, lun); } static void search_rport_targets(char *d_name, enum disp_style style) { if (!strstr(d_name, "target")) return; sa_dir_crawl(d_name, list_scsi_device, style); } static void list_luns_by_rport(char *rport, enum disp_style style) { char path[1024]; char link[1024]; char *substr; int len; int ret; snprintf(path, sizeof(path), "/sys/class/fc_remote_ports/%s", rport); ret = readlink(path, link, sizeof(link)); if (ret == -1) return; if (link[ret] != '\0') link[ret] = '\0'; substr = strstr(link, "net"); snprintf(path, sizeof(path), "/sys/class/%s", substr); substr = strstr(path, "fc_remote_ports"); len = strlen(path) - strlen(substr); path[len] = '\0'; sa_dir_crawl(path, search_rport_targets, style); } static void scan_device_map(char *port, char *rport, enum disp_style style) { if (style == DISP_TARG) show_short_lun_info_header(); list_luns_by_rport(rport, style); /* Newline at the end of the short lun report */ if (style == DISP_TARG) printf("\n"); } static void show_port_stats_header(const char *ifname, int interval) { printf("\n"); printf("%-7s interval: %-2d Err Inv " "IvTx Link Cntl Input Input Output Output\n", ifname, interval); printf("Seconds TxFrames TxBytes RxFrames RxBytes " "Frms CRC Byte Fail Reqs Requests MBytes " "Requests MBytes\n"); printf("------- --------- ------------ --------- -------------- " "---- ---- ---- ---- ---- --------- --------- " "--------- ---------\n"); } static void show_port_stats_in_row(uint64_t start_time, struct port_statistics *port_stats) { printf("%-7"PRIu64" ", port_stats->seconds_since_last_reset - start_time); printf("%-9"PRIu64" ", port_stats->tx_frames); printf("%-12"PRIu64" ", port_stats->tx_words * FCOE_WORD_TO_BYTE); printf("%-9"PRIu64" ", port_stats->rx_frames); printf("%-14"PRIu64" ", port_stats->rx_words * FCOE_WORD_TO_BYTE); printf("%-4"PRIu64" ", port_stats->error_frames); printf("%-4"PRIu64" ", port_stats->invalid_crc_count); printf("%-4"PRIu64" ", port_stats->invalid_tx_word_count * FCOE_WORD_TO_BYTE); printf("%-4"PRIu64" ", port_stats->link_failure_count); printf("%-4"PRIu64" ", port_stats->fcp_control_requests); printf("%-9"PRIu64" ", port_stats->fcp_input_requests); printf("%-9"PRIu64" ", port_stats->fcp_input_megabytes); printf("%-9"PRIu64" ", port_stats->fcp_output_requests); printf("%-9"PRIu64" ", port_stats->fcp_output_megabytes); printf("\n"); } enum fcoe_status display_port_stats(const char *ifname, int interval) { struct port_statistics *port_stats; enum fcoe_status rc = EINTERR; uint64_t start_time = 0; char *host; int i, num_hbas; num_hbas = get_number_of_adapters(); if (num_hbas < 0) return rc; host = get_host_from_netdev(ifname); if (!host) return rc; i = 0; while (1) { unsigned int secs_left; port_stats = get_port_statistics(host); if (!port_stats) goto free_host; if (!start_time) start_time = port_stats->seconds_since_last_reset; if (!(i % 52)) show_port_stats_header(ifname, interval); show_port_stats_in_row(start_time, port_stats); i++; /* wait for the requested time interval in seconds */ secs_left = interval; do { secs_left = sleep(secs_left); } while (secs_left); } rc = SUCCESS; free(port_stats); free_host: free(host); return rc; } static int get_host_from_vport(struct dirent *dp, void *arg) { if (!strncmp(dp->d_name, "host", strlen("host"))) { struct port_attributes *port_attrs; port_attrs = get_port_attribs(dp->d_name); if (!port_attrs) return 0; printf("\n"); show_port_info(port_attrs); free(port_attrs); } return 0; } static int crawl_vports(struct dirent *dp, void *arg) { char *oldpath = arg; if (!strncmp(dp->d_name, "vport", strlen("vport"))) { char path[1024]; snprintf(path, sizeof(path), "%s/%s", oldpath, dp->d_name); sa_dir_read(path, get_host_from_vport, NULL); } return 0; } static void show_host_vports(const char *host) { char path[1024]; snprintf(path, sizeof(path), "%s/%s/device/", SYSFS_HOST_DIR, host); sa_dir_read(path, crawl_vports, path); } static enum fcoe_status display_one_adapter_info(char *ifname) { struct port_attributes *port_attrs; struct hba_info *hba_info; enum fcoe_status rc = EINTERR; char *pcidev; char *host; pcidev = get_pci_dev_from_netdev(ifname); if (!pcidev) return rc; host = get_host_from_netdev(ifname); if (!host) goto free_pcidev; hba_info = get_hbainfo_by_pcidev(pcidev); if (!hba_info) goto free_host; port_attrs = get_port_attribs(host); if (!port_attrs) goto free_hba_info; /* * Display the adapter header. */ show_hba_info(hba_info); show_port_info(port_attrs); show_host_vports(host); rc = SUCCESS; free(port_attrs); free_hba_info: free(hba_info); free_host: free(host); free_pcidev: free(pcidev); return rc; } static int search_fc_adapter(struct dirent *dp, void *arg) { display_one_adapter_info(dp->d_name); return 0; } enum fcoe_status display_adapter_info(char *ifname) { enum fcoe_status rc = SUCCESS; int num_hbas; int err; if (ifname) return display_one_adapter_info(ifname); num_hbas = get_number_of_adapters(); if (!num_hbas) return ENOACTION; err = sa_dir_read("/sys/class/net/", search_fc_adapter, NULL); if (err) return EINTERR; return rc; } static char *get_ifname_from_rport(char *rport) { char link[1024]; char ifname[32]; ssize_t ret; char *path; char *offs; int err; int i = 0; err = asprintf(&path, "%s/%s", "/sys/class/fc_remote_ports", rport); if (err == -1) return NULL; ret = readlink(path, link, sizeof(link)); free(path); if (ret == -1) return NULL; if (link[ret] != '\0') link[ret] = '\0'; offs = strstr(link, "/net/"); if (!offs) return NULL; offs = offs + 5; for (i = 0; offs[i] != '\0'; i++) if (offs[i] == '/') break; strncpy(ifname, offs, i); if (ifname[i] != '\0') ifname[i] = '\0'; return strdup(ifname); } static enum fcoe_status display_one_target_info(char *ifname, char *rport, enum disp_style style) { struct port_attributes *rport_attrs; struct port_attributes *port_attrs; enum fcoe_status rc = SUCCESS; char *host; rport_attrs = get_rport_attribs(rport); if (!rport_attrs) return EINTERR; /* * Skip any targets that are not FCP targets */ if (is_fcp_target(rport_attrs)) goto free_rport_attribs; rc = EINTERR; host = get_host_from_netdev(ifname); if (!host) goto free_rport_attribs; port_attrs = get_port_attribs(host); if (!port_attrs) goto free_host; show_target_info(port_attrs->symbolic_name, rport_attrs); if (strncmp(port_attrs->port_state, "Online", 6)) goto free_port_attribs; /* * This will print the LUN table * under the target. */ scan_device_map(ifname, rport, style); free_port_attribs: free(port_attrs); free_host: free(host); free_rport_attribs: free(rport_attrs); return rc; } static bool rport_is_child(const char *rport, const char *ifname) { char link[1024]; ssize_t ret; char *path; char *offs; int err; err = asprintf(&path, "%s/%s", "/sys/class/fc_remote_ports", rport); if (err == -1) return false; ret = readlink(path, link, sizeof(link)); free(path); if (ret == -1) return false; offs = strstr(link, ifname); return offs ? true : false; } static int search_rports(struct dirent *dp, void *arg) { struct target_info_arguments *ta; bool allocated = false; /* ifname is malloc()ed? */ char *ifname; char *rport; ta = arg; rport = dp->d_name; ifname = ta->ifname; if (ifname) { bool child; child = rport_is_child(rport, ifname); if (!child) return 0; } else { ifname = get_ifname_from_rport(rport); if (!ifname) return 0; allocated = true; } display_one_target_info(ifname, rport, ta->style); if (allocated) free(ifname); return 0; } enum fcoe_status display_target_info(char *ifname, enum disp_style style) { struct target_info_arguments args; args.ifname = ifname; args.style = style; sa_dir_read("/sys/class/fc_remote_ports/", search_rports, (void *) &args); return SUCCESS; } static struct sa_table fcoe_ctlr_table; static void print_fcoe_fcf_device(void *ep, UNUSED void *arg) { struct fcoe_fcf_device *fcf = (struct fcoe_fcf_device *)ep; char temp[MAX_STR_LEN]; char mac[MAX_STR_LEN]; int len = sizeof(temp); const char *buf; printf("\n"); printf(" FCF #%u Information\n", fcf->index); buf = sa_enum_decode(temp, len, fcf_state_table, fcf->state); if (!buf) buf = temp; printf(" Connection Mode: %s\n", buf); printf(" Fabric Name: 0x%016" PRIx64 "\n", fcf->fabric_name); printf(" Switch Name 0x%016" PRIx64 "\n", fcf->switch_name); mac2str(fcf->mac, mac, MAX_STR_LEN); printf(" MAC Address: %s\n", mac); printf(" FCF Priority: %u\n", fcf->priority); printf(" FKA Period: %u seconds\n", fcf->fka_period); printf(" Selected: "); (fcf->selected == 1) ? printf("Yes\n") : printf("No\n"); printf(" VLAN ID: %u\n", fcf->vlan_id); printf("\n"); } static void print_interface_fcoe_fcf_device(void *ep, void *arg) { struct fcoe_ctlr_device *ctlr = (struct fcoe_ctlr_device *)ep; const char *ifname = arg; const char *buf; char temp[MAX_STR_LEN]; int len = sizeof(temp); if (!ifname || !strncmp(ifname, ctlr->ifname, IFNAMSIZ)) { printf(" Interface: %s\n", ctlr->ifname); buf = sa_enum_decode(temp, len, fip_conn_type_table, ctlr->mode); if (!buf) buf = temp; printf(" Connection Type: %s\n", buf); sa_table_iterate(&ctlr->fcfs, print_fcoe_fcf_device, NULL); } } /* * NULL ifname indicates to dispaly all fcfs */ enum fcoe_status display_fcf_info(const char *ifname) { enum fcoe_status rc = SUCCESS; sa_table_init(&fcoe_ctlr_table); read_fcoe_ctlr(&fcoe_ctlr_table); sa_table_iterate(&fcoe_ctlr_table, print_interface_fcoe_fcf_device, (void *)ifname); sa_table_iterate(&fcoe_ctlr_table, free_fcoe_ctlr_device, NULL); return rc; } static void print_interface_fcoe_lesb_stats(void *ep, void *arg) { struct fcoe_ctlr_device *ctlr = (struct fcoe_ctlr_device *)ep; const char *ifname = arg; if (!ifname || !strncmp(ifname, ctlr->ifname, IFNAMSIZ)) { printf("%-8u ", ctlr->lesb_link_fail); printf("%-9u ", ctlr->lesb_vlink_fail); printf("%-7u ", ctlr->lesb_miss_fka); printf("%-7u ", ctlr->lesb_symb_err); printf("%-9u ", ctlr->lesb_err_block); printf("%-9u ", ctlr->lesb_fcs_error); printf("\n"); } } static void print_interface_fcoe_lesb_stats_header(const char *ifname, int interval) { printf("\n"); printf("%-7s interval: %-2d\n", ifname, interval); printf("LinkFail VLinkFail MissFKA SymbErr ErrBlkCnt FCSErrCnt\n"); printf("-------- --------- ------- ------- --------- ---------\n"); } enum fcoe_status display_port_lesb_stats(const char *ifname, int interval) { enum fcoe_status rc = SUCCESS; int i = 0; while (1) { unsigned int secs_left; sa_table_init(&fcoe_ctlr_table); read_fcoe_ctlr(&fcoe_ctlr_table); if (!(i % 52)) print_interface_fcoe_lesb_stats_header(ifname, interval); sa_table_iterate(&fcoe_ctlr_table, print_interface_fcoe_lesb_stats, (void *)ifname); sa_table_iterate(&fcoe_ctlr_table, free_fcoe_ctlr_device, NULL); i++; secs_left = interval; do { secs_left = sleep(secs_left); } while (secs_left); } return rc; }