[Open-FCoE] [RESUBMIT PATCH] fcoe-utils: fcping implementation via FC pass-through interface

James Smart James.Smart at Emulex.Com
Thu Jul 9 17:18:10 UTC 2009


This is cool.

Brings out one comment in my mind though...  It uses hbaapi to find the 
adapter, but then reverts to sg_io for the echo ELS.  Seems odd and 
makes this very linux-centric.  Why wouldn't we extend the hbaapi 
interface to have a generic send_els routine (it has a bunch of like 
functions, but they are specific to an ELS), and wrapper the els-sending 
in the library (which is very linux-centric in implementation) ? 

-- james


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.
>
> VLAN Handling:
>
> Because the VLAN interface can have any name, we cannot
> just check the name to see if it is "ethxxx". The patch
> is to check in /sys/class/net to see if the interface
> name (VLAN or non-VLAN) exists in this directory as a
> symbolic link. If yes, then pass the name to libhbalinux
> to check if it is an FCoE interface. If no, proceed to
> check if it is other form of representation of the
> interface (VLAN or non-VLAN).
>
> Also updated the INSTALL document and the QUICKSTART document
> for fcping.
>
> Signed-off-by: Steve Ma <steve.ma at intel.com>
> ---
>
>  INSTALL     |   18 +
>  Makefile.am |   10 +
>  QUICKSTART  |   18 +
>  fcping.c    |  928 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 971 insertions(+), 3 deletions(-)
>  create mode 100644 fcping.c
>
> diff --git a/INSTALL b/INSTALL
> index 9974167..c974282 100644
> --- a/INSTALL
> +++ b/INSTALL
> @@ -17,7 +17,23 @@ DEPENDENCIES
>  * automake
>  * libtool
>
> -1) Bootstrap, configure, make and make install
> +1) Ensure correct header files
> +   - fcping requires certain kernel header files to be exported
> +     or it cannot be compiled. These header files were included
> +     in the 2.6.31 Linux kernel. If you are using a distribution
> +     kernel that is >= 2.6.31 then you shouldn't have to take
> +     this step. If you are building your own kernel or are using
> +     a pre-2.6.31 kernel you will need to take this step to
> +     export the appropriate header files.
> +
> +   # pushd <kernel-src>
> +   # make headers_install
> +   # PWD=`pwd`
> +   # ln -s $PWD/usr/include/scsi/scsi_bsg_fc.h /usr/include/scsi/
> +   # ln -s $PWD/usr/include/scsi/fc /usr/include/scsi/fc
> +   # popd
> +
> +2) Bootstrap, configure, make and make install
>     # ./bootstrap.sh
>     # rpm --eval "%configure" | sh
>     # make
> 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/QUICKSTART b/QUICKSTART
> index a711390..e2f91c2 100644
> --- a/QUICKSTART
> +++ b/QUICKSTART
> @@ -117,7 +117,23 @@ DEPENDENCIES
>
>  PROCESS
>
> -1) Bootstrap, configure, make and make install
> +1) Ensure correct header files
> +   - fcping requires certain kernel header files to be exported
> +     or it cannot be compiled. These header files were included
> +     in the 2.6.31 Linux kernel. If you are using a distribution
> +     kernel that is >= 2.6.31 then you shouldn't have to take
> +     this step. If you are building your own kernel or are using
> +     a pre-2.6.31 kernel you will need to take this step to
> +     export the appropriate header files.
> +
> +   # pushd <kernel-src>
> +   # make headers_install
> +   # PWD=`pwd`
> +   # ln -s $PWD/usr/include/scsi/scsi_bsg_fc.h /usr/include/scsi/
> +   # ln -s $PWD/usr/include/scsi/fc /usr/include/scsi/fc
> +   # popd
> +
> +2) Bootstrap, configure, make and make install
>     # ./bootstrap.sh
>     # rpm --eval "%configure" | sh
>     # make
> diff --git a/fcping.c b/fcping.c
> new file mode 100644
> index 0000000..4e3fa68
> --- /dev/null
> +++ b/fcping.c
> @@ -0,0 +1,928 @@
> +/*
> + * 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 <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"
> +typedef uint8_t u8;
> +#include <scsi/sg.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;
> +
> +#define FC_MAX_PAYLOAD  (2112U - sizeof(net32_t))
> +#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 */
> +#define SYSFS_HBA_DIR   "/sys/class/net"
> +
> +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 (max %ld)\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, FC_MAX_PAYLOAD);
> +       exit(1);
> +}
> +
> +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];
> +
> +       strerror_r(error, buf, sizeof(buf));
> +       sa_log_func(func, "errno=%d %s", error, 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:
> +                       fprintf(stderr, "FC_MAX_PAYLOAD=%lu\n", FC_MAX_PAYLOAD);
> +                       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];
> +       char hba_dir[256];
> +       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:
> +        *    If pass in an interface name, it does not need
> +        *    to be validated here. The interface name can be
> +        *    anything. It will have to be found via HBAAPI
> +        *    library. It fails if not found.
> +        *    host<n> = match the index <n>.
> +        *    mac address xx:xx:xx:xx:xx:xx
> +        *    otherwise, try parsing as a wwn and match that.
> +        */
> +
> +       snprintf(hba_dir, sizeof(hba_dir), SYSFS_HBA_DIR "/%s", fp_hba);
> +       if (readlink(hba_dir, namebuf, sizeof(namebuf) - 1)) {
> +               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 = (unsigned long)&cdb;
> +       sg_io.dout_xfer_len = sizeof(ct);
> +       sg_io.dout_xferp = (unsigned long)&ct;
> +       sg_io.din_xfer_len = *resp_len;
> +       sg_io.din_xferp = (unsigned long)response;
> +       sg_io.max_response_len = sizeof(sense);
> +       sg_io.response = (unsigned long)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) {
> +               if (op == FC_NS_GID_PN)
> +                       printf("GID_PN error: %s\n", strerror(errno));
> +               if (op == FC_NS_GID_NN)
> +                       printf("GID_NN error: %s\n", strerror(errno));
> +               return rc;
> +       }
> +
> +       cmd = ((response[8]<<8) | response[9]) & 0xffff;
> +       if (cmd != FC_FS_ACC)
> +               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 = (unsigned long)&cdb;
> +       sg_io.dout_xfer_len = fp_len;
> +       sg_io.dout_xferp = (unsigned long)fp_buf;
> +       sg_io.din_xfer_len = *resp_len;
> +       sg_io.din_xferp = (unsigned long)resp;
> +       sg_io.max_response_len = sizeof(sense);
> +       sg_io.response = (unsigned long)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 (rc != 0 && errno == ECONNABORTED)
> +                       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
> devel at open-fcoe.org
> http://www.open-fcoe.org/mailman/listinfo/devel
>
>   



More information about the devel mailing list