diff options
Diffstat (limited to 'usr/src/cmd/dlutil/dltraninfo.c')
-rw-r--r-- | usr/src/cmd/dlutil/dltraninfo.c | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/usr/src/cmd/dlutil/dltraninfo.c b/usr/src/cmd/dlutil/dltraninfo.c new file mode 100644 index 0000000000..af7f8c80c2 --- /dev/null +++ b/usr/src/cmd/dlutil/dltraninfo.c @@ -0,0 +1,458 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2017, Joyent, Inc. + */ + +/* + * Private utility to dump transceiver information for each physical datalink. + * Something like this should eventually be a part of dladm or similar. + */ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <strings.h> +#include <errno.h> +#include <ctype.h> +#include <unistd.h> +#include <limits.h> +#include <stdarg.h> +#include <libgen.h> + +#include <libdladm.h> +#include <libdllink.h> +#include <sys/dld.h> +#include <sys/dld_ioc.h> +#include <sys/dls_mgmt.h> +#include <libsff.h> + +#define DLTRAN_KIND_LEN 64 + +static dladm_handle_t dltran_hdl; +static char dltran_dlerrmsg[DLADM_STRSIZE]; +static const char *dltran_progname; +static boolean_t dltran_verbose; +static boolean_t dltran_hex; +static boolean_t dltran_write; +static int dltran_outfd; +static int dltran_errors; + +/* + * This routine basically assumes that we'll have 16 byte aligned output to + * print out the human readable output. + */ +static void +dltran_dump_page(uint8_t *buf, size_t nbytes, uint_t page) +{ + size_t i; + static boolean_t first = B_TRUE; + + if (first) { + (void) printf("page %*s 0", 4, ""); + for (i = 1; i < 16; i++) { + if (i % 4 == 0 && i % 16 != 0) { + (void) printf(" "); + } + + (void) printf("%2x", i); + } + (void) printf(" v123456789abcdef\n"); + first = B_FALSE; + } + for (i = 0; i < nbytes; i++) { + + if (i % 16 == 0) { + (void) printf("0x%02x %04x: ", page, i); + } + + if (i % 4 == 0 && i % 16 != 0) { + (void) printf(" "); + } + + + (void) printf("%02x", buf[i]); + + if (i % 16 == 15) { + int j; + (void) printf(" "); + for (j = i - (i % 16); j <= i; j++) { + if (!isprint(buf[j])) { + (void) printf("."); + } else { + (void) printf("%c", buf[j]); + } + } + (void) printf("\n"); + } + } +} + +static int +dltran_read_page(datalink_id_t link, uint_t tranid, uint_t page, uint8_t *bufp, + size_t *buflen) +{ + dld_ioc_tranio_t dti; + + bzero(bufp, *buflen); + bzero(&dti, sizeof (dti)); + + dti.dti_linkid = link; + dti.dti_tran_id = tranid; + dti.dti_page = page; + dti.dti_nbytes = *buflen; + dti.dti_off = 0; + dti.dti_buf = (uintptr_t)(void *)bufp; + + if (ioctl(dladm_dld_fd(dltran_hdl), DLDIOC_READTRAN, &dti) != 0) { + (void) fprintf(stderr, "failed to read transceiver page " + "0x%2x: %s\n", page, strerror(errno)); + return (1); + } + + *buflen = dti.dti_nbytes; + return (0); +} + +static boolean_t +dltran_is_8472(uint8_t *buf) +{ + switch (buf[0]) { + case 0xc: + case 0xd: + case 0x11: + /* + * Catch cases that refer explicitly to QSFP and newer. + */ + return (B_FALSE); + default: + break; + } + + /* + * Check the byte that indicates compliance with SFF 8472. Use this to + * know if we can read page 0xa2 or not. + */ + if (buf[94] == 0) + return (B_FALSE); + + return (B_TRUE); +} + +static void +dltran_hex_dump(datalink_id_t linkid, uint_t tranid) +{ + uint8_t buf[256]; + size_t buflen = sizeof (buf); + + if (dltran_read_page(linkid, tranid, 0xa0, buf, &buflen) != 0) { + dltran_errors++; + return; + } + + dltran_dump_page(buf, buflen, 0xa0); + + if (!dltran_is_8472(buf)) { + return; + } + + buflen = sizeof (buf); + if (dltran_read_page(linkid, tranid, 0xa2, buf, &buflen) != 0) { + dltran_errors++; + return; + } + + dltran_dump_page(buf, buflen, 0xa2); +} + +static void +dltran_write_page(datalink_id_t linkid, uint_t tranid) +{ + uint8_t buf[256]; + size_t buflen = sizeof (buf); + off_t off; + + if (dltran_read_page(linkid, tranid, 0xa0, buf, &buflen) != 0) { + dltran_errors++; + return; + } + + off = 0; + while (buflen > 0) { + ssize_t ret; + + ret = write(dltran_outfd, buf + off, buflen); + if (ret == -1) { + (void) fprintf(stderr, "failed to write data " + "to output file: %s\n", strerror(errno)); + dltran_errors++; + return; + } + + off += ret; + buflen -= ret; + } +} + +static void +dltran_verbose_dump(datalink_id_t linkid, uint_t tranid) +{ + uint8_t buf[256]; + size_t buflen = sizeof (buf); + int ret; + nvlist_t *nvl; + + if (dltran_read_page(linkid, tranid, 0xa0, buf, &buflen) != 0) { + dltran_errors++; + return; + } + + ret = libsff_parse(buf, buflen, 0xa0, &nvl); + if (ret == 0) { + dump_nvlist(nvl, 8); + nvlist_free(nvl); + } else { + fprintf(stderr, "failed to parse sfp data: %s\n", + strerror(ret)); + dltran_errors++; + } +} + +static int +dltran_dump_transceivers(dladm_handle_t hdl, datalink_id_t linkid, void *arg) +{ + dladm_status_t status; + char name[MAXLINKNAMELEN]; + dld_ioc_gettran_t gt; + uint_t count, i, tranid = UINT_MAX; + boolean_t tran_found = B_FALSE; + uint_t *tranidp = arg; + + if (tranidp != NULL) + tranid = *tranidp; + + if ((status = dladm_datalink_id2info(hdl, linkid, NULL, NULL, NULL, + name, sizeof (name))) != DLADM_STATUS_OK) { + (void) fprintf(stderr, "failed to get datalink name for link " + "%d: %s", linkid, dladm_status2str(status, + dltran_dlerrmsg)); + dltran_errors++; + return (DLADM_WALK_CONTINUE); + } + + bzero(>, sizeof (gt)); + gt.dgt_linkid = linkid; + gt.dgt_tran_id = DLDIOC_GETTRAN_GETNTRAN; + + if (ioctl(dladm_dld_fd(hdl), DLDIOC_GETTRAN, >) != 0) { + if (errno != ENOTSUP) { + (void) fprintf(stderr, "failed to get transceiver " + "count for device %s: %s\n", + name, strerror(errno)); + dltran_errors++; + } + return (DLADM_WALK_CONTINUE); + } + + count = gt.dgt_tran_id; + (void) printf("%s: discovered %d transceiver%s\n", name, count, + count > 1 ? "s" : ""); + for (i = 0; i < count; i++) { + if (tranid != UINT_MAX && i != tranid) + continue; + if (tranid != UINT_MAX) + tran_found = B_TRUE; + bzero(>, sizeof (gt)); + gt.dgt_linkid = linkid; + gt.dgt_tran_id = i; + + if (ioctl(dladm_dld_fd(hdl), DLDIOC_GETTRAN, >) != 0) { + (void) fprintf(stderr, "failed to get tran info for " + "%s: %s\n", name, strerror(errno)); + dltran_errors++; + return (DLADM_WALK_CONTINUE); + } + + if (dltran_hex && !gt.dgt_present) + continue; + if (!dltran_hex && !dltran_write) { + (void) printf("\ttransceiver %d present: %s\n", i, + gt.dgt_present ? "yes" : "no"); + if (!gt.dgt_present) + continue; + (void) printf("\ttransceiver %d usable: %s\n", i, + gt.dgt_usable ? "yes" : "no"); + } + + if (dltran_verbose) { + dltran_verbose_dump(linkid, i); + } + + if (dltran_write) { + if (!gt.dgt_present) { + (void) fprintf(stderr, "warning: no " + "transceiver present in port %d, not " + "writing\n", i); + dltran_errors++; + continue; + } + dltran_write_page(linkid, i); + } + + if (dltran_hex) { + printf("transceiver %d data:\n", i); + dltran_hex_dump(linkid, i); + } + } + + if (tranid != UINT_MAX && !tran_found) { + dltran_errors++; + (void) fprintf(stderr, "failed to find transceiver %d on " + "link %s\n", tranid, name); + } + + return (DLADM_WALK_CONTINUE); +} + + +static void +dltran_usage(const char *fmt, ...) +{ + if (fmt != NULL) { + va_list ap; + + (void) fprintf(stderr, "%s: ", dltran_progname); + va_start(ap, fmt); + (void) vfprintf(stderr, fmt, ap); + va_end(ap); + } + + (void) fprintf(stderr, "Usage: %s [-x | -v | -w file] [tran]...\n" + "\n" + "\t-v display all transceiver information\n" + "\t-w write transceiver data page 0xa0 to file\n" + "\t-x dump raw hexadecimal for transceiver\n", + dltran_progname); +} + +int +main(int argc, char *argv[]) +{ + int c; + dladm_status_t status; + const char *outfile = NULL; + uint_t count = 0; + + dltran_progname = basename(argv[0]); + + while ((c = getopt(argc, argv, ":xvw:")) != -1) { + switch (c) { + case 'v': + dltran_verbose = B_TRUE; + break; + case 'x': + dltran_hex = B_TRUE; + break; + case 'w': + dltran_write = B_TRUE; + outfile = optarg; + break; + case ':': + dltran_usage("option -%c requires an " + "operand\n", optopt); + return (2); + case '?': + default: + dltran_usage("unknown option: -%c\n", optopt); + return (2); + } + } + + argc -= optind; + argv += optind; + + if (dltran_verbose) + count++; + if (dltran_hex) + count++; + if (dltran_write) + count++; + if (count > 1) { + (void) fprintf(stderr, "only one of -v, -w, and -x may be " + "specified\n"); + return (2); + } + + if (dltran_write) { + if ((dltran_outfd = open(outfile, O_RDWR | O_TRUNC | O_CREAT, + 0644)) < 0) { + (void) fprintf(stderr, "failed to open output file " + "%s: %s\n", outfile, strerror(errno)); + return (1); + } + } + + if ((status = dladm_open(&dltran_hdl)) != DLADM_STATUS_OK) { + (void) fprintf(stderr, "failed to open /dev/dld: %s\n", + dladm_status2str(status, dltran_dlerrmsg)); + return (1); + } + + if (argc == 0) { + (void) dladm_walk_datalink_id(dltran_dump_transceivers, + dltran_hdl, NULL, DATALINK_CLASS_PHYS, + DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); + } else { + int i; + char *c; + + for (i = 0; i < argc; i++) { + uint_t tran; + uint_t *tranidp = NULL; + datalink_id_t linkid; + + if ((c = strrchr(argv[i], '/')) != NULL) { + unsigned long u; + char *eptr; + + c++; + errno = 0; + u = strtoul(c, &eptr, 10); + if (errno != 0 || *eptr != '\0' || + u >= UINT_MAX) { + (void) fprintf(stderr, "failed to " + "parse link/transceiver: %s\n", + argv[i]); + return (1); + } + c--; + *c = '\0'; + tran = (uint_t)u; + tranidp = &tran; + } + + if ((status = dladm_name2info(dltran_hdl, argv[i], + &linkid, NULL, NULL, NULL)) != DLADM_STATUS_OK) { + (void) fprintf(stderr, "failed to get link " + "id for link %s: %s\n", argv[i], + dladm_status2str(status, dltran_dlerrmsg)); + return (1); + } + + (void) dltran_dump_transceivers(dltran_hdl, linkid, + tranidp); + } + } + + return (dltran_errors != 0 ? 1 : 0); +} |