Steve Ma wrote:
> 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) {
> +
This will not work for virtual interfaces such as ethx.y.
> 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
>
Steve,
Please fix the stroul above so that it will work for virtual devices
(ethx.y). Currently, this will fail immediately here for virtual devices.
Thanks,
john.
_______________________________________________
devel mailing list
[email protected]
http://www.open-fcoe.org/mailman/listinfo/devel