This is the implementation of fcping by invoking the FCoE ELS/CT pass-through interface in the kernel. fcping is added as one of the command tools in fcoe-utils. Pinging is to send an ECHO ELS from a local port of an HBA to a remote FCoE destination(e.g. target, directory server, etc.). Issue fcping -h to see a summary of the command usage. libHBAAPI and libhbalinux libraries are used by fcping to find HBA information.
Signed-off-by: Steve Ma <[email protected]> --- Makefile.am | 10 + fcping.c | 919 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 928 insertions(+), 1 deletions(-) create mode 100644 fcping.c diff --git a/Makefile.am b/Makefile.am index 5d896bf..6c812d9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ ## target programs, to be built and installed in $(prefix)/sbin -sbin_PROGRAMS = fcoeadm +sbin_PROGRAMS = fcoeadm fcping if WITH_DCB sbin_PROGRAMS += fcoemon endif @@ -19,6 +19,14 @@ include/fc_scsi.h include/fc_types.h include/net_types.h fcoeadm_CFLAGS = $(HBAAPI_CFLAGS) fcoeadm_LDFLAGS = $(HBAAPI_LIBS) +## rules for building fcping +## only listed sources get packaged, so must list all headers too +fcping_SOURCES = fcping.c + +## fcping uses HBAAPI, so get the right flags for compiling and linking +fcping_CFLAGS = $(HBAAPI_CFLAGS) +fcping_LDFLAGS = $(HBAAPI_LIBS) -lrt + ## rules for building fcoemon ## only listed sources get packaged, so must list all headers too fcoemon_SOURCES = fcoemon_utils.c fcoemon.c fcoemon.h fcoemon_utils.h \ diff --git a/fcping.c b/fcping.c new file mode 100644 index 0000000..cb268ea --- /dev/null +++ b/fcping.c @@ -0,0 +1,919 @@ +/* + * Copyright(c) 2009 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 + */ + +/* + * FCPing - FC fabric diagnostic. + */ +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdarg.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <time.h> +#include <fcntl.h> +#include <malloc.h> +#include <limits.h> +#include <signal.h> +#include <libgen.h> +#include <assert.h> +#include <syslog.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <scsi/sg.h> +#include <net/ethernet.h> +#include <netinet/ether.h> +#include <hbaapi.h> +#include <vendorhbaapi.h> +#include <linux/types.h> +#include <linux/bsg.h> +#include "net_types.h" +#include "fc_types.h" +#include <scsi/fc/fc_ns.h> +#include <scsi/fc/fc_gs.h> +#include <scsi/fc/fc_els.h> +#include <scsi/scsi_bsg_fc.h> + +static const char *cmdname; + +static void +fp_usage() +{ + fprintf(stderr, + "Usage: %s [-fqx] -i <interval> -c <count> -h <hba> -s <size> \\\n" + " [ -F <FC-ID> | -P <WWPN> | -N <WWNN>]\n" + " flags: \n" + " -f: Flood ping\n" + " -q: Quiet! just print summary\n" + " -x: Hex dump of responses\n" + " -i <interval>: Wait <interval> seconds between each ping\n" + " -c <count>: Stop after sending <count> pings\n" + " -h <hba>: eth<n>, MAC address, WWPN, or FC-ID of the HBA\n" + " -s <size>: Byte-length of ping request payload\n" + " -F <FC-ID>: Destination port ID\n" + " -P <WWPN>: Destination world-wide port name\n" + " -N <WWNN>: Destination world-wide node name\n", + cmdname); + exit(1); +} + +#define FC_MAX_PAYLOAD 2112U +#define MAX_SENSE_LEN 96 /* SCSI_SENSE_BUFFERSIZE */ +#define MAX_HBA_COUNT 128 +#define FP_LEN_DEF 32 /* default ping payload length */ +#define FP_LEN_PAD 32 /* extra length for response */ +#define FP_MIN_INTVL 0.001 /* minimum interval in seconds */ +#define FP_DEF_INTVL 1.000 /* default sending interval in seconds */ + +static fc_fid_t fp_did; +static fc_wwn_t fp_port_wwn; +static fc_wwn_t fp_node_wwn; +static int fp_count = -1; /* send indefinitely by default */ +static uint32_t fp_len = FP_LEN_DEF; +static int fp_flood; /* send as fast as possible */ +static uint32_t fp_interval = FP_DEF_INTVL * 1000; /* in milliseconds */ +static int fp_quiet; +static int fp_hex; +static char *fp_hba; /* name of interface to be used */ +static int fp_hba_type; +#define FP_HBA_FCID_TYPE 1 +#define FP_HBA_WWPN_TYPE 2 +#define FP_HBA_HOST_TYPE 3 +#define FP_HBA_ETH_TYPE 4 +static char fp_dev[64]; +static int fp_fd; /* file descriptor for openfc ioctls */ +static void *fp_buf; /* sending buffer */ +static int fp_debug; + +struct fp_stats { + uint32_t fp_tx_frames; + uint32_t fp_rx_frames; + uint32_t fp_rx_errors; + uint64_t fp_transit_time_us; /* total transit time in microseconds */ + uint32_t fp_rx_times; /* valid times on receive */ +}; +static struct fp_stats fp_stats; + +#define hton24(p, v) \ +do { \ + p[0] = (((v) >> 16) & 0xFF); \ + p[1] = (((v) >> 8) & 0xFF); \ + p[2] = ((v) & 0xFF); \ +} while (0) + +#define hton64(p, v) \ +do { \ + p[0] = (u_char) ((v) >> 56) & 0xFF; \ + p[1] = (u_char) ((v) >> 48) & 0xFF; \ + p[2] = (u_char) ((v) >> 40) & 0xFF; \ + p[3] = (u_char) ((v) >> 32) & 0xFF; \ + p[4] = (u_char) ((v) >> 24) & 0xFF; \ + p[5] = (u_char) ((v) >> 16) & 0xFF; \ + p[6] = (u_char) ((v) >> 8) & 0xFF; \ + p[7] = (u_char) (v) & 0xFF; \ +} while (0) + +static void sa_log_func(const char *func, const char *format, ...); +static void sa_log_err(int, const char *func, const char *format, ...); +static void sa_log_output(const char *buf); + +/* + * Log message. + */ +#define SA_LOG(...) \ + do { sa_log_func(__func__, __VA_ARGS__); } while (0) + +#define SA_LOG_ERR(error, ...) \ + do { sa_log_err(error, NULL, __VA_ARGS__); } while (0) + +/* + * Logging exits. + */ +#define SA_LOG_EXIT(...) \ + do { sa_log_func(__func__, __VA_ARGS__); \ + if (fp_debug) \ + sa_log_func(__func__, \ + "Exiting at %s:%d", __FILE__, __LINE__); \ + exit(1); \ + } while (0) + +#define SA_LOG_ERR_EXIT(error, ...) \ + do { sa_log_func(__func__, __VA_ARGS__); \ + if (fp_debug) \ + sa_log_err(error, __func__, \ + "Exiting at %s:%d", __FILE__, __LINE__); \ + else \ + sa_log_err(error, NULL, NULL); \ + exit(1); \ + } while (0) + +#define SA_LOG_BUF_LEN 200 /* on-stack line buffer size */ + +/* + * log with a variable argument list. + */ +static void +sa_log_va(const char *func, const char *format, va_list arg) +{ + size_t len; + size_t flen; + int add_newline; + char sa_buf[SA_LOG_BUF_LEN]; + char *bp; + + /* + * If the caller didn't provide a newline at the end, we will. + */ + len = strlen(format); + add_newline = 0; + if (!len || format[len - 1] != '\n') + add_newline = 1; + bp = sa_buf; + len = sizeof(sa_buf); + if (func) { + flen = snprintf(bp, len, "%s: ", func); + len -= flen; + bp += flen; + } + flen = vsnprintf(bp, len, format, arg); + if (add_newline && flen < len) { + bp += flen; + *bp++ = '\n'; + *bp = '\0'; + } + sa_log_output(sa_buf); +} + +/* + * log with function name. + */ +static void +sa_log_func(const char *func, const char *format, ...) +{ + va_list arg; + + va_start(arg, format); + if (fp_debug) + sa_log_va(func, format, arg); + else + sa_log_va(NULL, format, arg); + va_end(arg); +} + +/* + * log with error number. + */ +static void +sa_log_err(int error, const char *func, const char *format, ...) +{ + va_list arg; + char buf[SA_LOG_BUF_LEN]; + + sa_log_func(func, "errno=%d %s", error, + strerror_r(error, buf, sizeof(buf))); + if (format) { + va_start(arg, format); + sa_log_va(func, format, arg); + va_end(arg); + } +} + +static void +sa_log_output(const char *buf) +{ + fprintf(stderr, "%s", buf); + fflush(stderr); +} + +static char * +sa_hex_format(char *buf, size_t buflen, + const unsigned char *data, size_t data_len, + unsigned int group_len, char *inter_group_sep) +{ + size_t rlen, tlen; + char *bp, *sep; + unsigned int i; + + rlen = buflen; + bp = buf; + sep = ""; + for (i = 0; rlen > 0 && i < data_len; ) { + tlen = snprintf(bp, rlen, "%s%2.2x", sep, data[i]); + rlen -= tlen; + bp += tlen; + i++; + sep = (i % group_len) ? "" : inter_group_sep; + } + return buf; +} + +/* + * Hex dump buffer to file. + */ +static void sa_hex_dump(unsigned char *bp, size_t len, FILE *fp) +{ + char lbuf[120]; + size_t tlen; + uint32_t offset = 0; + + while (len > 0) { + tlen = 16; /* bytes per line */ + if (tlen > len) + tlen = len; + sa_hex_format(lbuf, sizeof(lbuf), bp, tlen, 4, " "); + fprintf(fp, "%6x %s\n", offset, lbuf); + offset += tlen; + len -= tlen; + bp += tlen; + } +} + +/* + * Convert 48-bit IEEE MAC address to 64-bit FC WWN. + */ +fc_wwn_t +fc_wwn_from_mac(uint64_t mac, uint32_t scheme, uint32_t port) +{ + fc_wwn_t wwn; + + assert(mac < (1ULL << 48)); + wwn = mac | ((fc_wwn_t) scheme << 60); + switch (scheme) { + case 1: + assert(port == 0); + break; + case 2: + assert(port < 0xfff); + wwn |= (fc_wwn_t) port << 48; + break; + default: + assert(1); + break; + } + return wwn; +} + +/* + * Handle WWN/MAC arguments + */ +static fc_wwn_t +fp_parse_wwn(const char *arg, char *msg, uint32_t scheme, uint32_t port) +{ + char *endptr; + fc_wwn_t wwn; + fc_wwn_t oui; + struct ether_addr mac; + + wwn = strtoull(arg, &endptr, 16); + if (*endptr != '\0') { + if (ether_aton_r(arg, &mac) == NULL && + ether_hostton(arg, &mac) != 0) { + SA_LOG_EXIT("invalid %s WWN or MAC addr %s", msg, arg); + } + oui = net48_get((net48_t *)mac.ether_addr_octet); + wwn = fc_wwn_from_mac(oui, scheme, port); + } + return wwn; +} + +/* + * Handle options. + */ +static void +fp_options(int argc, char *argv[]) +{ + int opt; + char *endptr; + float sec; + int targ_spec = 0; + + cmdname = basename(argv[0]); + if (argc <= 1) + fp_usage(); + + while ((opt = getopt(argc, argv, "c:fi:h:qs:xF:P:N:")) != -1) { + switch (opt) { + case 'c': + fp_count = (int) strtoul(optarg, &endptr, 10); + if (*endptr != '\0') + SA_LOG_EXIT("bad count %s\n", optarg); + break; + case 'f': + fp_flood = 1; + break; + case 'i': + if (sscanf(optarg, "%f", &sec) != 1 || + sec < FP_MIN_INTVL) + SA_LOG_EXIT("bad interval %s\n", optarg); + fp_interval = sec * 1000; + break; + case 'h': + fp_hba = optarg; + break; + case 'q': + fp_quiet = 1; + break; + case 's': + fp_len = strtoul(optarg, &endptr, 0); + if (*endptr != '\0' || fp_len > FC_MAX_PAYLOAD) + SA_LOG_EXIT("bad size %s max %d\n", + optarg, FC_MAX_PAYLOAD); + if (fp_len < 4) + SA_LOG_EXIT("bad size %s min %d\n", + optarg, 4); + break; + case 'x': + fp_hex = 1; + break; + + /* + * -F specifies the target FC_ID. + */ + case 'F': + fp_did = strtoull(optarg, &endptr, 16); + if (*endptr != '\0') + SA_LOG_EXIT("bad target FC_ID %s\n", optarg); + targ_spec++; + break; + + /* + * The -P and -N flags take a world-wide name in hex, + * or an ethernet addr, or an etherhost entry from /etc/ethers. + */ + case 'N': + fp_node_wwn = fp_parse_wwn(optarg, "Node", 1, 0); + targ_spec++; + break; + + case 'P': + fp_port_wwn = fp_parse_wwn(optarg, "Port", 2, 0); + targ_spec++; + break; + + case '?': + default: + fp_usage(); /* exits */ + break; + } + } + argc -= optind; + argv += optind; + + if (fp_hba == NULL) + SA_LOG_EXIT("FCoE interface not specified"); + + if (targ_spec == 0) + SA_LOG_EXIT("no target specified"); + + if (targ_spec > 1) + SA_LOG_EXIT("too many targets specified;" + " only one is allowed."); + + return; +} + +/* + * Lookup specified adapter using HBAAPI. + */ +static int +fp_find_hba(void) +{ + HBA_STATUS retval; + HBA_UINT32 hba_cnt; + HBA_HANDLE hba_handle = 0; + HBA_ADAPTERATTRIBUTES hba_attrs; + HBA_PORTATTRIBUTES port_attrs; + HBA_UINT32 fcid = 0; + char namebuf[1028]; + fc_wwn_t wwn = 0; + HBA_WWN wwpn; + char *endptr; + int i, found = 0; + + /* + * Parse HBA spec. if there is one. + * These formats are tried: + * host<n> = match the index <n>. + * eth<n> or some other name - strtoull fails. + * Try matching the name. + * otherwise, try parsing as a wwn and match that. + */ + if (strstr(fp_hba, "eth") == fp_hba) { + i = strtoul(fp_hba + 3, &endptr, 10); + if (*endptr != '\0') + SA_LOG_EXIT("invalid hba name %s", fp_hba); + fp_hba_type = FP_HBA_ETH_TYPE; + } else if (strstr(fp_hba, "host") == fp_hba) { + i = strtoul(fp_hba + 4, &endptr, 10); + if (*endptr != '\0') + SA_LOG_EXIT("invalid hba name %s", fp_hba); + fp_hba_type = FP_HBA_HOST_TYPE; + } else if (strstr(fp_hba, ":")) { + wwn = fp_parse_wwn(fp_hba, "HBA", 2, 0); + hton64(wwpn.wwn, wwn); + fp_hba_type = FP_HBA_WWPN_TYPE; + } else { + wwn = strtoull(fp_hba, &endptr, 16); + if (wwn < 0x1000000) { + fcid = wwn; + fp_hba_type = FP_HBA_FCID_TYPE; + } else { + if (*endptr != '\0') + SA_LOG_EXIT("unsupported hba name"); + wwn = fp_parse_wwn(fp_hba, "HBA", 2, 0); + hton64(wwpn.wwn, wwn); + fp_hba_type = FP_HBA_WWPN_TYPE; + } + } + + hba_cnt = HBA_GetNumberOfAdapters(); + if (!hba_cnt) + SA_LOG_EXIT("No FCoE interfaces created"); + + for (i = 0; i < hba_cnt; i++) { + retval = HBA_GetAdapterName(i, namebuf); + if (retval != HBA_STATUS_OK) { + SA_LOG("HBA_GetAdapterName" + " failed, retval=%d", retval); + continue; + } + + hba_handle = HBA_OpenAdapter(namebuf); + if (!hba_handle) { + SA_LOG("HBA_OpenAdapter failed"); + continue; + } + + retval = HBA_GetAdapterAttributes(hba_handle, &hba_attrs); + if (retval != HBA_STATUS_OK) { + SA_LOG("HBA_GetAdapterAttributes" + " failed, retval=%d", retval); + HBA_CloseAdapter(hba_handle); + continue; + } + + retval = HBA_GetAdapterPortAttributes( + hba_handle, 0, &port_attrs); + if (retval != HBA_STATUS_OK) { + SA_LOG("HBA_GetAdapterPortAttributes" + " failed, retval=%d", retval); + HBA_CloseAdapter(hba_handle); + continue; + } + + switch (fp_hba_type) { + case FP_HBA_FCID_TYPE: + if (port_attrs.PortFcId != fcid) { + HBA_CloseAdapter(hba_handle); + continue; + } + break; + case FP_HBA_WWPN_TYPE: + if (memcmp(&port_attrs.PortWWN, &wwpn, sizeof(wwpn))) { + HBA_CloseAdapter(hba_handle); + continue; + } + break; + case FP_HBA_HOST_TYPE: + if (!strstr(port_attrs.OSDeviceName, fp_hba)) { + HBA_CloseAdapter(hba_handle); + continue; + } + break; + default: + if (!strstr(port_attrs.PortSymbolicName, fp_hba)) { + HBA_CloseAdapter(hba_handle); + continue; + } + break; + } + + snprintf(fp_dev, sizeof(fp_dev), + "fc_%s", port_attrs.OSDeviceName); + found = 1; + break; + } + if (!found) + SA_LOG("FCoE interface %s not found", fp_hba); + + return found; +} + +static void +fp_report(void) +{ + double loss; + struct fp_stats *sp = &fp_stats; + + loss = 100.0 * (sp->fp_tx_frames - sp->fp_rx_frames) / sp->fp_tx_frames; + printf("%d frames sent, %d received %d errors, %.3f%% loss, " + "avg. rt time %.3f ms\n", + sp->fp_tx_frames, sp->fp_rx_frames, sp->fp_rx_errors, loss, + sp->fp_rx_times ? sp->fp_transit_time_us * 1.0 / + (1000.0 * sp->fp_rx_times) : 0.0); +} + +/* + * Lookup ID from port name or node name. + */ +static int +fp_ns_get_id(uint32_t op, fc_wwn_t wwn, char *response, size_t *resp_len) +{ + struct ct_get_id { + struct fc_ct_hdr hdr; + net64_t wwn; + } ct; + struct fc_bsg_request cdb; + struct sg_io_v4 sg_io; + char sense[MAX_SENSE_LEN]; + size_t actual_len; + int cmd, rc = 0; + + memset((char *)&cdb, 0, sizeof(cdb)); + memset(&ct, 0, sizeof(ct)); + ct.hdr.ct_rev = FC_CT_REV; + hton24(ct.hdr.ct_in_id, 0xfffffc); + ct.hdr.ct_fs_type = FC_FST_DIR; + ct.hdr.ct_fs_subtype = FC_NS_SUBTYPE; + ct.hdr.ct_options = 0; + ct.hdr.ct_cmd = op; + ct.hdr.ct_mr_size = *resp_len; + net64_put(&ct.wwn, wwn); + + cdb.msgcode = FC_BSG_HST_CT; + + sg_io.guard = 'Q'; + sg_io.protocol = BSG_PROTOCOL_SCSI; + sg_io.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT; + sg_io.request_len = sizeof(cdb); + sg_io.request = (__u64)&cdb; + sg_io.dout_xfer_len = sizeof(ct); + sg_io.dout_xferp = (__u64)&ct; + sg_io.din_xfer_len = *resp_len; + sg_io.din_xferp = (__u64)response; + sg_io.max_response_len = sizeof(sense); + sg_io.response = (__u64)sense; + sg_io.timeout = 1000; /* millisecond */ + memset(sense, 0, sizeof(sense)); + memset(response, 0, sizeof(response)); + + rc = ioctl(fp_fd, SG_IO, &sg_io); + if (rc < 0) { + perror("ioctl"); + return rc; + } + + cmd = ((response[8]<<8) | response[9]) & 0xffff; + if (cmd != FC_FS_ACC) { + errno = ENOENT; + return -1; + } + + actual_len = (size_t)(sg_io.din_xfer_len - sg_io.din_resid); + if (actual_len < *resp_len) + *resp_len = actual_len; + + return 0; +} + +static int +fp_lookup_target() +{ + char response[32]; + size_t resp_len; + int rc; + + if (fp_did != 0) + return 0; + + if (fp_port_wwn != 0) { + resp_len = sizeof(response); + memset(&response, 0, sizeof(response)); + rc = fp_ns_get_id(FC_NS_GID_PN, fp_port_wwn, + response, &resp_len); + if (rc == 0) { + fp_did = ((response[17] << 16) & 0xff0000) | + ((response[18] << 8) & 0x00ff00) | + (response[19] & 0x0000ff); + return 0; + } + SA_LOG("cannot find fcid of destination @ wwpn 0x%llX", + fp_port_wwn); + } + if (fp_node_wwn != 0) { + resp_len = sizeof(response); + memset(&response, 0, sizeof(response)); + rc = fp_ns_get_id(FC_NS_GID_NN, fp_node_wwn, + response, &resp_len); + if (rc == 0) { + fp_did = ((response[17] << 16) & 0xff0000) | + ((response[18] << 8) & 0x00ff00) | + (response[19] & 0x0000ff); + return 0; + } + SA_LOG("cannot find fcid of destination @ wwnn 0x%llX", + fp_node_wwn); + } + return 1; +} + +/* + * ELS_ECHO request format being used. + * Put a sequence number in the payload, followed by the pattern. + */ +struct fcping_echo { + net8_t fe_op; /* opcode */ + net24_t fe_resvd; /* reserved, must be zero */ + net32_t fe_seq; /* sequence number */ +}; + +/* + * Setup buffer to be sent. + */ +static void +fp_buf_setup(void) +{ + struct fcping_echo *ep; + net8_t *pp; + int len; + int i; + + /* + * Alloc extra in case of odd len or shorter than minimum. + */ + len = fp_len + sizeof(*ep) + sizeof(net32_t); + ep = calloc(1, len); + if (ep == NULL) + SA_LOG_ERR_EXIT(errno, "calloc %d bytes failed", len); + ep->fe_op = ELS_ECHO; + net32_put(&ep->fe_seq, 1); /* starting sequence number */ + i = 0; + for (pp = (net8_t *) (ep + 1); pp < (net8_t *) ep + fp_len; pp++) + *pp = i++; + fp_buf = ep; +} + +static unsigned long long +fp_get_time_usec(void) +{ +#ifdef _POSIX_TIMERS + struct timespec ts; + int rc; + + rc = clock_gettime(CLOCK_MONOTONIC, &ts); + if (rc) + SA_LOG_ERR_EXIT(errno, "clock_gettime error"); + return ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000; +#else +#warning no _POSIX_TIMERS + struct timeval ts; + + gettimeofday(&ts, NULL); + return ts.tv_sec * 1000000ULL + ts.tv_usec; +#endif /* _POSIX_TIMERS */ +} + +static int +send_els_echo(int fp_fd, void *fp_buf, uint32_t fp_len, + unsigned char *resp, uint32_t *resp_len, fc_fid_t fp_did) +{ + struct fc_bsg_request cdb; + char sense[MAX_SENSE_LEN]; + struct sg_io_v4 sg_io; + int rc; + + cdb.msgcode = FC_BSG_HST_ELS_NOLOGIN; + cdb.rqst_data.h_els.command_code = ELS_ECHO; + hton24(cdb.rqst_data.h_els.port_id, fp_did); + + sg_io.guard = 'Q'; + sg_io.protocol = BSG_PROTOCOL_SCSI; + sg_io.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT; + sg_io.request_len = sizeof(cdb); + sg_io.request = (__u64)&cdb; + sg_io.dout_xfer_len = fp_len; + sg_io.dout_xferp = (__u64)fp_buf; + sg_io.din_xfer_len = *resp_len; + sg_io.din_xferp = (__u64)resp; + sg_io.max_response_len = sizeof(sense); + sg_io.response = (__u64)sense; + sg_io.timeout = 20000; + memset(sense, 0, sizeof(sense)); + + rc = ioctl(fp_fd, SG_IO, &sg_io); + if (rc < 0) + return 1; + + *resp_len = sg_io.din_xfer_len - sg_io.din_resid; + return 0; +} + +/* + * Send ELS ECHO. + */ +static int fp_send_ping(void) +{ + struct fp_stats *sp = &fp_stats; + struct fcping_echo *ep; + int rc; + uint32_t resp_len; + unsigned char *resp; + unsigned long long tx_time; + unsigned long long usec; + char msg[80]; + char time_msg[80]; + + resp_len = fp_len + FP_LEN_PAD; /* for odd-byte padding and then some */ + resp = calloc(1, resp_len); + if (resp == NULL) + SA_LOG_EXIT("calloc %d bytes failed", resp_len); + + sp->fp_tx_frames++; + if (fp_len >= sizeof(*ep)) { + ep = (struct fcping_echo *) fp_buf; + net32_put(&ep->fe_seq, sp->fp_tx_frames); + } + tx_time = fp_get_time_usec(); + + /* send ELS ECHO frame and receive */ + rc = send_els_echo(fp_fd, fp_buf, fp_len, resp, &resp_len, fp_did); + if (rc) { + sp->fp_rx_errors++; + printf("echo %4d error: %s\n", + sp->fp_tx_frames, strerror(errno)); + } else { + usec = fp_get_time_usec(); + sp->fp_rx_frames++; + ep = (struct fcping_echo *) resp; + if (usec < tx_time) { + snprintf(time_msg, sizeof(time_msg), + "time unknown now %llx old %llx", + usec, tx_time); + usec = 0; /* as if time went backwards */ + } else { + usec = usec - tx_time; + snprintf(time_msg, sizeof(time_msg), + "%6.3f ms", usec / 1000.0); + sp->fp_transit_time_us += usec; + sp->fp_rx_times++; + } + if (ep->fe_op == ELS_LS_ACC) { + if (memcmp((char *) ep + 1, + (char *) fp_buf + 1, fp_len - 1) == 0) + snprintf(msg, sizeof(msg), "accepted"); + else { + sp->fp_rx_errors++; + snprintf(msg, sizeof(msg), + "accept data mismatches"); + } + } else if (ep->fe_op == ELS_LS_RJT) { + sp->fp_rx_errors++; + snprintf(msg, sizeof(msg), "REJECT received"); + } else { + sp->fp_rx_errors++; + snprintf(msg, sizeof(msg), + "op %x received", ep->fe_op); + } + if (fp_quiet == 0) + printf("echo %4d %-30s %s\n", + sp->fp_tx_frames, msg, time_msg); + } + if (fp_hex) { + printf("response length %u\n", resp_len); + sa_hex_dump(resp, resp_len, stdout); + printf("\n"); + } + free(resp); + return rc; +} + +static void +fp_signal_handler(int sig) +{ + /* + * Allow graceful termination of the + * for loop in fp_start. + */ + fp_count = 0; +} + +/* + * Main loop. + */ +static void fp_start(void) +{ + struct sigaction act; + int i; + int rc; + + memset(&act, 0, sizeof(act)); + act.sa_handler = fp_signal_handler; + act.sa_flags = 0; + + sigaction(SIGTERM, &act, NULL); /* Signal 15: kill <pid> */ + sigaction(SIGQUIT, &act, NULL); /* Signal 3: Ctrl-\ */ + sigaction(SIGINT, &act, NULL); /* Signal 2: Ctrl-C */ + + printf("sending echo to 0x%X\n", fp_did); + for (i = 0; fp_count == -1 || i < fp_count; i++) { + rc = fp_send_ping(); + if (rc != 0 && errno == EMSGSIZE) + break; + if (fp_flood == 0) + usleep(fp_interval * 1000); + if (!fp_count) + break; + } +} + +/* + * Main. + */ +int main(int argc, char *argv[]) +{ + char bsg_dev[80]; + int rc = 1; + + fp_options(argc, argv); + + if (HBA_LoadLibrary() != HBA_STATUS_OK) + SA_LOG_ERR_EXIT(errno, "HBA_LoadLibrary failed"); + + if (fp_find_hba()) { + sprintf(bsg_dev, "/dev/bsg/%s", fp_dev); + fp_fd = open(bsg_dev, O_RDWR); + if (fp_fd < 0) + SA_LOG_ERR_EXIT(errno, + "open of %s failed", bsg_dev); + + if (!fp_lookup_target()) { + fp_buf_setup(); + fp_start(); + fp_report(); + rc = 0; + } + close(fp_fd); + } + + HBA_FreeLibrary(); + return rc; +} _______________________________________________ devel mailing list [email protected] http://www.open-fcoe.org/mailman/listinfo/devel
