|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* Copyright(c) 2010 Intel Corporation. All rights reserved.
|
|
Packit |
a132db |
*
|
|
Packit |
a132db |
* This program is free software; you can redistribute it and/or modify it
|
|
Packit |
a132db |
* under the terms and conditions of the GNU General Public License,
|
|
Packit |
a132db |
* version 2, as published by the Free Software Foundation.
|
|
Packit |
a132db |
*
|
|
Packit |
a132db |
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
Packit |
a132db |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
Packit |
a132db |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
Packit |
a132db |
* more details.
|
|
Packit |
a132db |
*
|
|
Packit |
a132db |
* You should have received a copy of the GNU General Public License along with
|
|
Packit |
a132db |
* this program; if not, write to the Free Software Foundation, Inc.,
|
|
Packit |
a132db |
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
|
Packit |
a132db |
*
|
|
Packit |
a132db |
* Maintained at www.Open-FCoE.org
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* FCPing - FC fabric diagnostic.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
#include <stdio.h>
|
|
Packit |
a132db |
#include <stdlib.h>
|
|
Packit |
a132db |
#include <stddef.h>
|
|
Packit |
a132db |
#include <stdarg.h>
|
|
Packit |
a132db |
#include <unistd.h>
|
|
Packit |
a132db |
#include <inttypes.h>
|
|
Packit |
a132db |
#include <errno.h>
|
|
Packit |
a132db |
#include <string.h>
|
|
Packit |
a132db |
#include <time.h>
|
|
Packit |
a132db |
#include <fcntl.h>
|
|
Packit |
a132db |
#include <malloc.h>
|
|
Packit |
a132db |
#include <limits.h>
|
|
Packit |
a132db |
#include <signal.h>
|
|
Packit |
a132db |
#include <libgen.h>
|
|
Packit |
a132db |
#include <assert.h>
|
|
Packit |
a132db |
#include <syslog.h>
|
|
Packit |
a132db |
#include <sys/stat.h>
|
|
Packit |
a132db |
#include <sys/param.h>
|
|
Packit |
a132db |
#include <sys/ioctl.h>
|
|
Packit |
a132db |
#include <sys/time.h>
|
|
Packit |
a132db |
#include <net/ethernet.h>
|
|
Packit |
a132db |
#include <netinet/ether.h>
|
|
Packit |
a132db |
#include <linux/types.h>
|
|
Packit |
a132db |
#include <linux/bsg.h>
|
|
Packit |
a132db |
#include "net_types.h"
|
|
Packit |
a132db |
#include "fc_types.h"
|
|
Packit |
a132db |
#include "fcoe_utils.h"
|
|
Packit |
a132db |
typedef uint8_t u8;
|
|
Packit |
a132db |
#include <scsi/sg.h>
|
|
Packit |
a132db |
#include "fc_ns.h"
|
|
Packit |
a132db |
#include "fc_gs.h"
|
|
Packit |
a132db |
#include "fc_els.h"
|
|
Packit |
a132db |
#include "scsi_bsg_fc.h"
|
|
Packit |
a132db |
|
|
Packit |
a132db |
#include "sysfs_hba.h"
|
|
Packit |
a132db |
|
|
Packit |
a132db |
static const char *cmdname;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
#define FC_MAX_PAYLOAD 2112UL /* Max FC payload */
|
|
Packit |
a132db |
#define MAX_SENSE_LEN 96 /* SCSI_SENSE_BUFFERSIZE */
|
|
Packit |
a132db |
#define MAX_HBA_COUNT 128
|
|
Packit |
a132db |
/* FC ELS ECHO Command takes 4 bytes */
|
|
Packit |
a132db |
#define FP_LEN_ECHO sizeof(net32_t)
|
|
Packit |
a132db |
/* default max ping data length, excluding 4 bytes of ELS ECHO command */
|
|
Packit |
a132db |
#define FP_LEN_MAX (FC_MAX_PAYLOAD - FP_LEN_ECHO)
|
|
Packit |
a132db |
#define FP_LEN_MIN 4 /* fcping needs 4 bytes as sequence number */
|
|
Packit |
a132db |
#define FP_LEN_DEF 32 /* default ping payload length */
|
|
Packit |
a132db |
#define FP_LEN_PAD 32 /* extra length for response */
|
|
Packit |
a132db |
#define FP_MIN_INTVL 0.001 /* minimum interval in seconds */
|
|
Packit |
a132db |
#define FP_DEF_INTVL 1.000 /* default sending interval in seconds */
|
|
Packit |
a132db |
#define SYSFS_HBA_DIR "/sys/class/net"
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/* Check if it is WKA accoriding to FC-FS-3 Rev 1.00 Clause 11 Table 30 */
|
|
Packit |
a132db |
#define FCID_IS_WKA(i) ((((i) >= 0xfffc01) && ((i) <= 0xfffcfe)) || \
|
|
Packit |
a132db |
(((i) >= 0xfffff0) && ((i) <= 0xffffff)))
|
|
Packit |
a132db |
|
|
Packit |
a132db |
#define FC_WKA_FABRIC_CONTROLLER ((fc_fid_t)0xfffffd)
|
|
Packit |
a132db |
#define FC_WKA_DIRECTORY_SERVICE ((fc_fid_t)0xfffffc)
|
|
Packit |
a132db |
|
|
Packit |
a132db |
static void fp_usage(void)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
fprintf(stderr,
|
|
Packit |
a132db |
"Usage: %s [ -fqx ] [ -i <interval> ] [ -c <count> ] -h <hba> "
|
|
Packit |
a132db |
"[ -s <size> ] { -F <FC-ID> | -P <WWPN> | -N <WWNN> }\n"
|
|
Packit |
a132db |
" flags:\n"
|
|
Packit |
a132db |
" -f: Flood ping\n"
|
|
Packit |
a132db |
" -q: Quiet! just print summary\n"
|
|
Packit |
a132db |
" -x: Hex dump of responses\n"
|
|
Packit |
a132db |
" -i <interval>: Wait <interval> seconds between each ping\n"
|
|
Packit |
a132db |
" -c <count>: Stop after sending <count> pings\n"
|
|
Packit |
a132db |
" -h <hba>: eth<n>, MAC address, WWPN, or FC-ID of the HBA\n"
|
|
Packit |
a132db |
" -s <size>: Byte-length of ping request payload (max %lu)\n"
|
|
Packit |
a132db |
" -F <FC-ID>: Destination port ID\n"
|
|
Packit |
a132db |
" -P <WWPN>: Destination world-wide port name\n"
|
|
Packit |
a132db |
" -N <WWNN>: Destination world-wide node name\n",
|
|
Packit |
a132db |
cmdname, FP_LEN_MAX);
|
|
Packit |
a132db |
|
|
Packit |
a132db |
fprintf(stderr, "\nNote that the default maximum FC payload allowed "
|
|
Packit |
a132db |
"is %lu bytes and the default maximum fcping payload, "
|
|
Packit |
a132db |
"i.e., the FC ELS ECHO data, allowed is %lu "
|
|
Packit |
a132db |
"bytes.\n",
|
|
Packit |
a132db |
FC_MAX_PAYLOAD, FP_LEN_MAX);
|
|
Packit |
a132db |
|
|
Packit |
a132db |
exit(1);
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
static fc_fid_t fp_did;
|
|
Packit |
a132db |
static fc_wwn_t fp_port_wwn;
|
|
Packit |
a132db |
static fc_wwn_t fp_node_wwn;
|
|
Packit |
a132db |
static int fp_count = -1; /* send indefinitely by default */
|
|
Packit |
a132db |
static uint32_t fp_len = FP_LEN_DEF + FP_LEN_ECHO;
|
|
Packit |
a132db |
static int fp_flood; /* send as fast as possible */
|
|
Packit |
a132db |
static uint32_t fp_interval = FP_DEF_INTVL * 1000; /* in milliseconds */
|
|
Packit |
a132db |
static int fp_quiet;
|
|
Packit |
a132db |
static int fp_hex;
|
|
Packit |
a132db |
static char *fp_hba; /* name of interface to be used */
|
|
Packit |
a132db |
static char fp_dev[64];
|
|
Packit |
a132db |
static int fp_fd; /* file descriptor for openfc ioctls */
|
|
Packit |
a132db |
static void *fp_buf; /* sending buffer */
|
|
Packit |
a132db |
static int fp_debug;
|
|
Packit |
a132db |
static char *host;
|
|
Packit |
a132db |
static struct port_attributes *port_attrs;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
struct fp_stats {
|
|
Packit |
a132db |
uint32_t fp_tx_frames;
|
|
Packit |
a132db |
uint32_t fp_rx_frames;
|
|
Packit |
a132db |
uint32_t fp_rx_errors;
|
|
Packit |
a132db |
uint64_t fp_transit_time_us; /* total transit time in microseconds */
|
|
Packit |
a132db |
uint32_t fp_rx_times; /* valid times on receive */
|
|
Packit |
a132db |
};
|
|
Packit |
a132db |
static struct fp_stats fp_stats;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
#define hton24(p, v) \
|
|
Packit |
a132db |
do { \
|
|
Packit |
a132db |
p[0] = (((v) >> 16) & 0xFF); \
|
|
Packit |
a132db |
p[1] = (((v) >> 8) & 0xFF); \
|
|
Packit |
a132db |
p[2] = ((v) & 0xFF); \
|
|
Packit |
a132db |
} while (0)
|
|
Packit |
a132db |
|
|
Packit |
a132db |
#define hton64(p, v) \
|
|
Packit |
a132db |
do { \
|
|
Packit |
a132db |
p[0] = (u_char) ((v) >> 56) & 0xFF; \
|
|
Packit |
a132db |
p[1] = (u_char) ((v) >> 48) & 0xFF; \
|
|
Packit |
a132db |
p[2] = (u_char) ((v) >> 40) & 0xFF; \
|
|
Packit |
a132db |
p[3] = (u_char) ((v) >> 32) & 0xFF; \
|
|
Packit |
a132db |
p[4] = (u_char) ((v) >> 24) & 0xFF; \
|
|
Packit |
a132db |
p[5] = (u_char) ((v) >> 16) & 0xFF; \
|
|
Packit |
a132db |
p[6] = (u_char) ((v) >> 8) & 0xFF; \
|
|
Packit |
a132db |
p[7] = (u_char) (v) & 0xFF; \
|
|
Packit |
a132db |
} while (0)
|
|
Packit |
a132db |
|
|
Packit |
a132db |
__attribute__((__format__(__printf__, 2, 3)))
|
|
Packit |
a132db |
static void sa_log_func(const char *func, const char *format, ...);
|
|
Packit |
a132db |
__attribute__((__format__(__printf__, 3, 4)))
|
|
Packit |
a132db |
static void sa_log_err(int, const char *func, const char *format, ...);
|
|
Packit |
a132db |
static void sa_log_output(const char *buf);
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* Log message.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
#define SA_LOG(...) \
|
|
Packit |
a132db |
do { sa_log_func(__func__, __VA_ARGS__); } while (0)
|
|
Packit |
a132db |
|
|
Packit |
a132db |
#define SA_LOG_ERR(error, ...) \
|
|
Packit |
a132db |
do { sa_log_err(error, NULL, __VA_ARGS__); } while (0)
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* Logging exits.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
#define SA_LOG_EXIT(...) \
|
|
Packit |
a132db |
do { sa_log_func(__func__, __VA_ARGS__); \
|
|
Packit |
a132db |
if (fp_debug) \
|
|
Packit |
a132db |
sa_log_func(__func__, \
|
|
Packit |
a132db |
"Exiting at %s:%d", __FILE__, __LINE__); \
|
|
Packit |
a132db |
exit(1); \
|
|
Packit |
a132db |
} while (0)
|
|
Packit |
a132db |
|
|
Packit |
a132db |
#define SA_LOG_ERR_EXIT(error, ...) \
|
|
Packit |
a132db |
do { sa_log_func(__func__, __VA_ARGS__); \
|
|
Packit |
a132db |
if (fp_debug) \
|
|
Packit |
a132db |
sa_log_err(error, __func__, \
|
|
Packit |
a132db |
"Exiting at %s:%d", __FILE__, __LINE__); \
|
|
Packit |
a132db |
else \
|
|
Packit |
a132db |
sa_log_err(error, NULL, NULL); \
|
|
Packit |
a132db |
exit(1); \
|
|
Packit |
a132db |
} while (0)
|
|
Packit |
a132db |
|
|
Packit |
a132db |
#define SA_LOG_BUF_LEN 200 /* on-stack line buffer size */
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* log with a variable argument list.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
static void
|
|
Packit |
a132db |
sa_log_va(const char *func, const char *format, va_list arg)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
size_t len;
|
|
Packit |
a132db |
size_t flen;
|
|
Packit |
a132db |
int add_newline;
|
|
Packit |
a132db |
char sa_buf[SA_LOG_BUF_LEN];
|
|
Packit |
a132db |
char *bp;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* If the caller didn't provide a newline at the end, we will.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
len = strlen(format);
|
|
Packit |
a132db |
add_newline = 0;
|
|
Packit |
a132db |
if (!len || format[len - 1] != '\n')
|
|
Packit |
a132db |
add_newline = 1;
|
|
Packit |
a132db |
bp = sa_buf;
|
|
Packit |
a132db |
len = sizeof(sa_buf);
|
|
Packit |
a132db |
if (func) {
|
|
Packit |
a132db |
flen = snprintf(bp, len, "%s: ", func);
|
|
Packit |
a132db |
len -= flen;
|
|
Packit |
a132db |
bp += flen;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
flen = vsnprintf(bp, len, format, arg);
|
|
Packit |
a132db |
if (add_newline && flen < len) {
|
|
Packit |
a132db |
bp += flen;
|
|
Packit |
a132db |
*bp++ = '\n';
|
|
Packit |
a132db |
*bp = '\0';
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
sa_log_output(sa_buf);
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* log with function name.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
static void
|
|
Packit |
a132db |
sa_log_func(const char *func, const char *format, ...)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
va_list arg;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
va_start(arg, format);
|
|
Packit |
a132db |
if (fp_debug)
|
|
Packit |
a132db |
sa_log_va(func, format, arg);
|
|
Packit |
a132db |
else
|
|
Packit |
a132db |
sa_log_va(NULL, format, arg);
|
|
Packit |
a132db |
va_end(arg);
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* log with error number.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
static void
|
|
Packit |
a132db |
sa_log_err(int error, const char *func, const char *format, ...)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
va_list arg;
|
|
Packit |
a132db |
char buf[SA_LOG_BUF_LEN];
|
|
Packit |
a132db |
|
|
Packit |
a132db |
strerror_r(error, buf, sizeof(buf));
|
|
Packit |
a132db |
sa_log_func(func, "errno=%d %s", error, buf);
|
|
Packit |
a132db |
if (format) {
|
|
Packit |
a132db |
va_start(arg, format);
|
|
Packit |
a132db |
sa_log_va(func, format, arg);
|
|
Packit |
a132db |
va_end(arg);
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
static void
|
|
Packit |
a132db |
sa_log_output(const char *buf)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
fprintf(stderr, "%s", buf);
|
|
Packit |
a132db |
fflush(stderr);
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
static char *
|
|
Packit |
a132db |
sa_hex_format(char *buf, size_t buflen,
|
|
Packit |
a132db |
const unsigned char *data, size_t data_len,
|
|
Packit |
a132db |
unsigned int group_len, char *inter_group_sep)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
size_t rlen, tlen;
|
|
Packit |
a132db |
char *bp, *sep;
|
|
Packit |
a132db |
unsigned int i;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
rlen = buflen;
|
|
Packit |
a132db |
bp = buf;
|
|
Packit |
a132db |
sep = "";
|
|
Packit |
a132db |
for (i = 0; rlen > 0 && i < data_len; ) {
|
|
Packit |
a132db |
tlen = snprintf(bp, rlen, "%s%2.2x", sep, data[i]);
|
|
Packit |
a132db |
rlen -= tlen;
|
|
Packit |
a132db |
bp += tlen;
|
|
Packit |
a132db |
i++;
|
|
Packit |
a132db |
sep = (i % group_len) ? "" : inter_group_sep;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
return buf;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* Hex dump buffer to file.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
static void sa_hex_dump(unsigned char *bp, size_t len, FILE *fp)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
char lbuf[120];
|
|
Packit |
a132db |
size_t tlen;
|
|
Packit |
a132db |
uint32_t offset = 0;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
while (len > 0) {
|
|
Packit |
a132db |
tlen = 16; /* bytes per line */
|
|
Packit |
a132db |
if (tlen > len)
|
|
Packit |
a132db |
tlen = len;
|
|
Packit |
a132db |
sa_hex_format(lbuf, sizeof(lbuf), bp, tlen, 4, " ");
|
|
Packit |
a132db |
fprintf(fp, "%6x %s\n", offset, lbuf);
|
|
Packit |
a132db |
offset += tlen;
|
|
Packit |
a132db |
len -= tlen;
|
|
Packit |
a132db |
bp += tlen;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* Convert 48-bit IEEE MAC address to 64-bit FC WWN.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
fc_wwn_t
|
|
Packit |
a132db |
fc_wwn_from_mac(uint64_t mac, uint32_t scheme, uint32_t port)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
fc_wwn_t wwn;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
assert(mac < (1ULL << 48));
|
|
Packit |
a132db |
wwn = mac | ((fc_wwn_t) scheme << 60);
|
|
Packit |
a132db |
switch (scheme) {
|
|
Packit |
a132db |
case 1:
|
|
Packit |
a132db |
assert(port == 0);
|
|
Packit |
a132db |
break;
|
|
Packit |
a132db |
case 2:
|
|
Packit |
a132db |
assert(port < 0xfff);
|
|
Packit |
a132db |
wwn |= (fc_wwn_t) port << 48;
|
|
Packit |
a132db |
break;
|
|
Packit |
a132db |
default:
|
|
Packit |
a132db |
assert(1);
|
|
Packit |
a132db |
break;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
return wwn;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* Handle WWN/MAC arguments
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
static fc_wwn_t
|
|
Packit |
a132db |
fp_parse_wwn(const char *arg, char *msg, uint32_t scheme, uint32_t port)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
char *endptr;
|
|
Packit |
a132db |
fc_wwn_t wwn;
|
|
Packit |
a132db |
fc_wwn_t oui;
|
|
Packit |
a132db |
struct ether_addr mac;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
wwn = strtoull(arg, &endptr, 16);
|
|
Packit |
a132db |
if (*endptr != '\0') {
|
|
Packit |
a132db |
if (ether_aton_r(arg, &mac) == NULL &&
|
|
Packit |
a132db |
ether_hostton(arg, &mac) != 0) {
|
|
Packit |
a132db |
SA_LOG_EXIT("invalid %s WWN or MAC addr %s", msg, arg);
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
oui = net48_get((net48_t *)mac.ether_addr_octet);
|
|
Packit |
a132db |
wwn = fc_wwn_from_mac(oui, scheme, port);
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
return wwn;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* Handle options.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
static void
|
|
Packit |
a132db |
fp_options(int argc, char *argv[])
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
int opt;
|
|
Packit |
a132db |
char *endptr;
|
|
Packit |
a132db |
float sec;
|
|
Packit |
a132db |
int targ_spec = 0;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
cmdname = basename(argv[0]);
|
|
Packit |
a132db |
if (argc <= 1)
|
|
Packit |
a132db |
fp_usage();
|
|
Packit |
a132db |
|
|
Packit |
a132db |
while ((opt = getopt(argc, argv, "?c:fi:h:qs:xF:P:N:")) != -1) {
|
|
Packit |
a132db |
switch (opt) {
|
|
Packit |
a132db |
case 'c':
|
|
Packit |
a132db |
fp_count = (int) strtoul(optarg, &endptr, 10);
|
|
Packit |
a132db |
if (*endptr != '\0')
|
|
Packit |
a132db |
SA_LOG_EXIT("bad count %s\n", optarg);
|
|
Packit |
a132db |
break;
|
|
Packit |
a132db |
case 'f':
|
|
Packit |
a132db |
fp_flood = 1;
|
|
Packit |
a132db |
break;
|
|
Packit |
a132db |
case 'i':
|
|
Packit |
a132db |
if (sscanf(optarg, "%f", &sec) != 1 ||
|
|
Packit |
a132db |
sec < FP_MIN_INTVL)
|
|
Packit |
a132db |
SA_LOG_EXIT("bad interval %s\n", optarg);
|
|
Packit |
a132db |
fp_interval = sec * 1000;
|
|
Packit |
a132db |
break;
|
|
Packit |
a132db |
case 'h':
|
|
Packit |
a132db |
fp_hba = optarg;
|
|
Packit |
a132db |
break;
|
|
Packit |
a132db |
case 'q':
|
|
Packit |
a132db |
fp_quiet = 1;
|
|
Packit |
a132db |
break;
|
|
Packit |
a132db |
case 's':
|
|
Packit |
a132db |
/* maximum ECHO data allowed is 2108 */
|
|
Packit |
a132db |
fp_len = strtoul(optarg, &endptr, 0);
|
|
Packit |
a132db |
if (*endptr != '\0' || fp_len > FP_LEN_MAX)
|
|
Packit |
a132db |
SA_LOG_EXIT("Bad size %s for FC ELS ECHO "
|
|
Packit |
a132db |
"data, max %lu bytes allowed.\n",
|
|
Packit |
a132db |
optarg, FP_LEN_MAX);
|
|
Packit |
a132db |
if (fp_len < FP_LEN_MIN)
|
|
Packit |
a132db |
SA_LOG_EXIT("Bad size %s for FC ELS ECHO "
|
|
Packit |
a132db |
"data, min %d bytes allowed.\n",
|
|
Packit |
a132db |
optarg, FP_LEN_MIN);
|
|
Packit |
a132db |
/* add 4 bytes for the ECHO command */
|
|
Packit |
a132db |
fp_len += FP_LEN_ECHO;
|
|
Packit |
a132db |
break;
|
|
Packit |
a132db |
case 'x':
|
|
Packit |
a132db |
fp_hex = 1;
|
|
Packit |
a132db |
break;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* -F specifies the target FC_ID.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
case 'F':
|
|
Packit |
a132db |
fp_did = strtoull(optarg, &endptr, 16);
|
|
Packit |
a132db |
if (*endptr != '\0')
|
|
Packit |
a132db |
SA_LOG_EXIT("bad target FC_ID %s\n", optarg);
|
|
Packit |
a132db |
targ_spec++;
|
|
Packit |
a132db |
break;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* The -P and -N flags take a world-wide name
|
|
Packit |
a132db |
* in hex, or an ethernet addr, or an etherhost
|
|
Packit |
a132db |
* entry from /etc/ethers.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
case 'N':
|
|
Packit |
a132db |
fp_node_wwn = fp_parse_wwn(optarg, "Node", 1, 0);
|
|
Packit |
a132db |
targ_spec++;
|
|
Packit |
a132db |
break;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
case 'P':
|
|
Packit |
a132db |
fp_port_wwn = fp_parse_wwn(optarg, "Port", 2, 0);
|
|
Packit |
a132db |
targ_spec++;
|
|
Packit |
a132db |
break;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
case '?':
|
|
Packit |
a132db |
default:
|
|
Packit |
a132db |
fp_usage(); /* exits */
|
|
Packit |
a132db |
break;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
argc -= optind;
|
|
Packit |
a132db |
argv += optind;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
if (fp_hba == NULL)
|
|
Packit |
a132db |
SA_LOG_EXIT("FCoE interface not specified");
|
|
Packit |
a132db |
|
|
Packit |
a132db |
if (targ_spec == 0)
|
|
Packit |
a132db |
SA_LOG_EXIT("no target specified");
|
|
Packit |
a132db |
|
|
Packit |
a132db |
if (targ_spec > 1)
|
|
Packit |
a132db |
SA_LOG_EXIT("too many targets specified;"
|
|
Packit |
a132db |
" only one is allowed.");
|
|
Packit |
a132db |
|
|
Packit |
a132db |
return;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* Lookup specified adapter using HBAAPI.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
static int
|
|
Packit |
a132db |
fp_find_hba(void)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
struct stat statbuf;
|
|
Packit |
a132db |
char hba_dir[256];
|
|
Packit |
a132db |
fc_wwn_t wwn = 0;
|
|
Packit |
a132db |
struct hba_wwn wwpn;
|
|
Packit |
a132db |
char *endptr;
|
|
Packit |
a132db |
uint32_t fcid;
|
|
Packit |
a132db |
int hba_cnt;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
hba_cnt = get_number_of_adapters();
|
|
Packit |
a132db |
if (!hba_cnt)
|
|
Packit |
a132db |
SA_LOG_EXIT("No FCoE interfaces created");
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* Parse HBA spec. if there is one.
|
|
Packit |
a132db |
* These formats are tried:
|
|
Packit |
a132db |
* If pass in an interface name, it does not need
|
|
Packit |
a132db |
* to be validated here. The interface name can be
|
|
Packit |
a132db |
* anything. It will have to be found via HBAAPI
|
|
Packit |
a132db |
* library. It fails if not found.
|
|
Packit |
a132db |
* host<n> = match the index <n>.
|
|
Packit |
a132db |
* mac address xx:xx:xx:xx:xx:xx
|
|
Packit |
a132db |
* otherwise, try parsing as a wwn and match that.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
snprintf(hba_dir, sizeof(hba_dir), SYSFS_HBA_DIR "/%s", fp_hba);
|
|
Packit |
a132db |
if (!stat(hba_dir, &statbuf)) {
|
|
Packit |
a132db |
host = get_host_from_netdev(fp_hba);
|
|
Packit |
a132db |
} else if (strstr(fp_hba, "host") == fp_hba) {
|
|
Packit |
a132db |
(void) strtoul(fp_hba + strlen("host"), &endptr, 10);
|
|
Packit |
a132db |
if (*endptr != '\0')
|
|
Packit |
a132db |
SA_LOG_EXIT("invalid hba name %s", fp_hba);
|
|
Packit |
a132db |
host = strdup(fp_hba);
|
|
Packit |
a132db |
} else if (strstr(fp_hba, ":")) {
|
|
Packit |
a132db |
if (strlen(fp_hba) == strlen("xx:yy:aa:bb:cc:dd:ee:ff")) {
|
|
Packit |
a132db |
fc_wwn_t wwn1;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
wwn1 = fp_parse_wwn(fp_hba, "HBA", 2, 0);
|
|
Packit |
a132db |
wwn1 &= 0xffff000000000000;
|
|
Packit |
a132db |
wwn = fp_parse_wwn(&fp_hba[6], "HBA", 2, 0);
|
|
Packit |
a132db |
wwn &= 0x0000ffffffffffff;
|
|
Packit |
a132db |
wwn |= wwn1;
|
|
Packit |
a132db |
} else if (strlen(fp_hba) == strlen("aa:bb:cc:dd:ee:ff")) {
|
|
Packit |
a132db |
wwn = fp_parse_wwn(fp_hba, "HBA", 2, 0);
|
|
Packit |
a132db |
} else {
|
|
Packit |
a132db |
SA_LOG_EXIT("invalid WWPN or MAC address %s", fp_hba);
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
hton64(wwpn.wwn, wwn);
|
|
Packit |
a132db |
host = get_host_by_wwpn(wwpn);
|
|
Packit |
a132db |
} else {
|
|
Packit |
a132db |
wwn = strtoull(fp_hba, &endptr, 16);
|
|
Packit |
a132db |
if (wwn < 0x1000000) {
|
|
Packit |
a132db |
fcid = wwn;
|
|
Packit |
a132db |
host = get_host_by_fcid(fcid);
|
|
Packit |
a132db |
} else {
|
|
Packit |
a132db |
if (*endptr != '\0')
|
|
Packit |
a132db |
SA_LOG_EXIT("unsupported hba name");
|
|
Packit |
a132db |
wwn = fp_parse_wwn(fp_hba, "HBA", 2, 0);
|
|
Packit |
a132db |
hton64(wwpn.wwn, wwn);
|
|
Packit |
a132db |
host = get_host_by_wwpn(wwpn);
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
if (!host) {
|
|
Packit |
a132db |
SA_LOG("FCoE interface %s not found", fp_hba);
|
|
Packit |
a132db |
return 0;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
snprintf(fp_dev, sizeof(fp_dev), "fc_%s", host);
|
|
Packit |
a132db |
|
|
Packit |
a132db |
port_attrs = get_port_attribs(host);
|
|
Packit |
a132db |
if (!port_attrs) {
|
|
Packit |
a132db |
free(host);
|
|
Packit |
a132db |
return 0;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
return 1;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
static void
|
|
Packit |
a132db |
fp_report(void)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
double loss;
|
|
Packit |
a132db |
struct fp_stats *sp = &fp_stats;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
loss = 100.0 * (sp->fp_tx_frames - sp->fp_rx_frames) / sp->fp_tx_frames;
|
|
Packit |
a132db |
printf("%d frames sent, %d received %d errors, %.3f%% loss, "
|
|
Packit |
a132db |
"avg. rt time %.3f ms\n",
|
|
Packit |
a132db |
sp->fp_tx_frames, sp->fp_rx_frames, sp->fp_rx_errors, loss,
|
|
Packit |
a132db |
sp->fp_rx_times ? sp->fp_transit_time_us * 1.0 /
|
|
Packit |
a132db |
(1000.0 * sp->fp_rx_times) : 0.0);
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* Lookup ID from port name or node name.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
static int
|
|
Packit |
a132db |
fp_ns_get_id(uint32_t op, fc_wwn_t wwn, char *response, size_t *resp_len)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
struct ct_get_id {
|
|
Packit |
a132db |
struct fc_ct_hdr hdr;
|
|
Packit |
a132db |
net64_t wwn;
|
|
Packit |
a132db |
} ct;
|
|
Packit |
a132db |
struct fc_bsg_request cdb;
|
|
Packit |
a132db |
struct fc_bsg_reply reply;
|
|
Packit |
a132db |
struct sg_io_v4 sg_io;
|
|
Packit |
a132db |
size_t actual_len;
|
|
Packit |
a132db |
int cmd, rc = 0;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
memset((char *)&cdb, 0, sizeof(cdb));
|
|
Packit |
a132db |
memset(&ct, 0, sizeof(ct));
|
|
Packit |
a132db |
ct.hdr.ct_rev = FC_CT_REV;
|
|
Packit |
a132db |
hton24(ct.hdr.ct_in_id, 0xfffffc);
|
|
Packit |
a132db |
ct.hdr.ct_fs_type = FC_FST_DIR;
|
|
Packit |
a132db |
ct.hdr.ct_fs_subtype = FC_NS_SUBTYPE;
|
|
Packit |
a132db |
ct.hdr.ct_options = 0;
|
|
Packit |
a132db |
ct.hdr.ct_cmd = htons(op);
|
|
Packit |
a132db |
ct.hdr.ct_mr_size = *resp_len;
|
|
Packit |
a132db |
net64_put(&ct.wwn, wwn);
|
|
Packit |
a132db |
|
|
Packit |
a132db |
cdb.msgcode = FC_BSG_HST_CT;
|
|
Packit |
a132db |
hton24(cdb.rqst_data.h_ct.port_id, 0xfffffc);
|
|
Packit |
a132db |
memcpy(&cdb.rqst_data.h_ct.preamble_word0, &ct.hdr,
|
|
Packit |
a132db |
3 * sizeof(uint32_t));
|
|
Packit |
a132db |
|
|
Packit |
a132db |
sg_io.guard = 'Q';
|
|
Packit |
a132db |
sg_io.protocol = BSG_PROTOCOL_SCSI;
|
|
Packit |
a132db |
sg_io.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT;
|
|
Packit |
a132db |
sg_io.request_len = sizeof(cdb);
|
|
Packit |
a132db |
sg_io.request = (uintptr_t)&cdb;
|
|
Packit |
a132db |
sg_io.dout_xfer_len = sizeof(ct);
|
|
Packit |
a132db |
sg_io.dout_xferp = (uintptr_t)&ct;
|
|
Packit |
a132db |
sg_io.din_xfer_len = *resp_len;
|
|
Packit |
a132db |
sg_io.din_xferp = (uintptr_t)response;
|
|
Packit |
a132db |
sg_io.max_response_len = sizeof(reply);
|
|
Packit |
a132db |
sg_io.response = (uintptr_t)&reply;
|
|
Packit |
a132db |
sg_io.timeout = 1000; /* millisecond */
|
|
Packit |
a132db |
memset(&reply, 0, sizeof(reply));
|
|
Packit |
a132db |
memset(response, 0, *resp_len);
|
|
Packit |
a132db |
|
|
Packit |
a132db |
rc = ioctl(fp_fd, SG_IO, &sg_io);
|
|
Packit |
a132db |
if (rc < 0) {
|
|
Packit |
a132db |
if (op == FC_NS_GID_PN)
|
|
Packit |
a132db |
printf("GID_PN error: %s\n", strerror(errno));
|
|
Packit |
a132db |
if (op == FC_NS_GID_NN)
|
|
Packit |
a132db |
printf("GID_NN error: %s\n", strerror(errno));
|
|
Packit |
a132db |
return rc;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
cmd = ((response[8]<<8) | response[9]) & 0xffff;
|
|
Packit |
a132db |
if (cmd != FC_FS_ACC)
|
|
Packit |
a132db |
return -1;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
actual_len = reply.reply_payload_rcv_len;
|
|
Packit |
a132db |
if (actual_len < *resp_len)
|
|
Packit |
a132db |
*resp_len = actual_len;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
return 0;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
static int fp_lookup_target(void)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
char response[32];
|
|
Packit |
a132db |
size_t resp_len;
|
|
Packit |
a132db |
int rc;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
if (fp_did != 0)
|
|
Packit |
a132db |
return 0;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
if (fp_port_wwn != 0) {
|
|
Packit |
a132db |
resp_len = sizeof(response);
|
|
Packit |
a132db |
memset(&response, 0, sizeof(response));
|
|
Packit |
a132db |
rc = fp_ns_get_id(FC_NS_GID_PN, fp_port_wwn,
|
|
Packit |
a132db |
response, &resp_len);
|
|
Packit |
a132db |
if (rc == 0) {
|
|
Packit |
a132db |
fp_did = ((response[17] << 16) & 0xff0000) |
|
|
Packit |
a132db |
((response[18] << 8) & 0x00ff00) |
|
|
Packit |
a132db |
(response[19] & 0x0000ff);
|
|
Packit |
a132db |
return 0;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
SA_LOG("cannot find fcid of destination @ wwpn 0x%llX",
|
|
Packit |
a132db |
fp_port_wwn);
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
if (fp_node_wwn != 0) {
|
|
Packit |
a132db |
resp_len = sizeof(response);
|
|
Packit |
a132db |
memset(&response, 0, sizeof(response));
|
|
Packit |
a132db |
rc = fp_ns_get_id(FC_NS_GID_NN, fp_node_wwn,
|
|
Packit |
a132db |
response, &resp_len);
|
|
Packit |
a132db |
if (rc == 0) {
|
|
Packit |
a132db |
fp_did = ((response[17] << 16) & 0xff0000) |
|
|
Packit |
a132db |
((response[18] << 8) & 0x00ff00) |
|
|
Packit |
a132db |
(response[19] & 0x0000ff);
|
|
Packit |
a132db |
return 0;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
SA_LOG("cannot find fcid of destination @ wwnn 0x%llX",
|
|
Packit |
a132db |
fp_node_wwn);
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
return 1;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* fp_get_max_data_len - get the maximum ECHO data size by FCID
|
|
Packit |
a132db |
* @fcid: the fcid
|
|
Packit |
a132db |
*
|
|
Packit |
a132db |
* Returns the maximum allowed ECHO data size. The ECHO data plus the 4 bytes
|
|
Packit |
a132db |
* ECHO ELS command is the maximum payload allowed.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
static uint32_t fp_get_max_data_len(fc_fid_t fcid)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
struct port_attributes *rport_attrs;
|
|
Packit |
a132db |
uint32_t dlen = FP_LEN_DEF;
|
|
Packit |
a132db |
uint32_t maxframe_size;
|
|
Packit |
a132db |
char *rport;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/* not found from disovered ports, if it's one of the
|
|
Packit |
a132db |
* WKA from FC-LS Table 30, use FC_MAX_PAYLOAD */
|
|
Packit |
a132db |
if (FCID_IS_WKA(fcid)) {
|
|
Packit |
a132db |
dlen = FP_LEN_MAX;
|
|
Packit |
a132db |
goto out;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
rport = get_rport_by_fcid(fcid);
|
|
Packit |
a132db |
if (!rport)
|
|
Packit |
a132db |
goto out;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
rport_attrs = get_rport_attribs(rport);
|
|
Packit |
a132db |
if (!rport_attrs)
|
|
Packit |
a132db |
goto free_rport;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
maxframe_size = strtoul(rport_attrs->maxframe_size, NULL, 16);
|
|
Packit |
a132db |
dlen = maxframe_size - FP_LEN_ECHO;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
free(rport_attrs);
|
|
Packit |
a132db |
free_rport:
|
|
Packit |
a132db |
free(rport);
|
|
Packit |
a132db |
|
|
Packit |
a132db |
out:
|
|
Packit |
a132db |
/* returns maximum allowed ECHO data length, excluding the 4
|
|
Packit |
a132db |
* bytes ECHO command in the payload */
|
|
Packit |
a132db |
return dlen;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* fp_check_data_len - figure out maximum allowed ECHO data size
|
|
Packit |
a132db |
*
|
|
Packit |
a132db |
* From FC-LS 4.2.4, for maximum allowed payload when Login exists
|
|
Packit |
a132db |
*
|
|
Packit |
a132db |
* "If a Login with the destination Nx_Port exists, the ECHO data field size
|
|
Packit |
a132db |
* is limited by 4 less than the smallest Receive Data_Field Size supported by
|
|
Packit |
a132db |
* the destination Nx_Port, the Fabric, and the source Nx_Port for the class
|
|
Packit |
a132db |
* of service being use."
|
|
Packit |
a132db |
*
|
|
Packit |
a132db |
* So, here we figure out the minimum of the source PortMaxFrameSize, the target
|
|
Packit |
a132db |
* PortMaxFraemSize, and the Domain Controller (Fabric) PortMaxFrameSize
|
|
Packit |
a132db |
* (default to be FC_MAX_PAYLOAD). For any FCID that is in FC-LS Table 30 WKA,
|
|
Packit |
a132db |
* use FP_LEN_MAX for ECHO data, i.e., FC_MAX_PAYLOAD - 4.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
static void fp_check_data_len(void)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
fc_fid_t sid;
|
|
Packit |
a132db |
uint32_t slen = 0;
|
|
Packit |
a132db |
uint32_t dlen = 0;
|
|
Packit |
a132db |
uint32_t flen = 0;
|
|
Packit |
a132db |
uint32_t plen = FP_LEN_DEF;
|
|
Packit |
a132db |
uint32_t maxframe_size;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/* find out maximum payload supported by the fabric */
|
|
Packit |
a132db |
flen = fp_get_max_data_len(FC_WKA_FABRIC_CONTROLLER);
|
|
Packit |
a132db |
if (!flen) {
|
|
Packit |
a132db |
flen = fp_get_max_data_len(FC_WKA_DIRECTORY_SERVICE);
|
|
Packit |
a132db |
if (!flen)
|
|
Packit |
a132db |
flen = FP_LEN_MAX;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/* find out maximum payload supported by the target */
|
|
Packit |
a132db |
dlen = fp_get_max_data_len(fp_did);
|
|
Packit |
a132db |
if (!dlen)
|
|
Packit |
a132db |
dlen = FP_LEN_DEF;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
|
|
Packit |
a132db |
maxframe_size = strtoul(port_attrs->maxframe_size, NULL, 16);
|
|
Packit |
a132db |
sid = strtoul(port_attrs->port_id, NULL, 16);
|
|
Packit |
a132db |
|
|
Packit |
a132db |
slen = maxframe_size - FP_LEN_ECHO;
|
|
Packit |
a132db |
plen = MIN(flen, MIN(slen, dlen));
|
|
Packit |
a132db |
|
|
Packit |
a132db |
printf("Maximum ECHO data allowed by the Fabric (0x%06x) : %d bytes.\n"
|
|
Packit |
a132db |
"Maximum ECHO data allowed by the Source (0x%06x) : %d bytes.\n"
|
|
Packit |
a132db |
"Maximum ECHO data allowed by the Target (0x%06x) : %d bytes.\n"
|
|
Packit |
a132db |
"Maximum ECHO data requested from user input (-s) : %" PRIu32 " "
|
|
Packit |
a132db |
"(default %d) bytes.\n",
|
|
Packit |
a132db |
FC_WKA_FABRIC_CONTROLLER, flen, sid, slen, fp_did, dlen,
|
|
Packit |
a132db |
(uint32_t)(fp_len - FP_LEN_ECHO), FP_LEN_DEF);
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/* fp_len is the total payload, including 4 bytes for ECHO command */
|
|
Packit |
a132db |
fp_len = MIN(fp_len, plen + FP_LEN_ECHO);
|
|
Packit |
a132db |
printf("Actual FC ELS ECHO data size used : %" PRIu32 " bytes.\n"
|
|
Packit |
a132db |
"Actual FC ELS ECHO payload size used : %d bytes "
|
|
Packit |
a132db |
"(including %zu bytes ECHO command).\n",
|
|
Packit |
a132db |
(uint32_t)(fp_len - FP_LEN_ECHO), fp_len, FP_LEN_ECHO);
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* ELS_ECHO request format being used.
|
|
Packit |
a132db |
* Put a sequence number in the payload, followed by the pattern.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
struct fcping_echo {
|
|
Packit |
a132db |
net8_t fe_op; /* opcode */
|
|
Packit |
a132db |
net24_t fe_resvd; /* reserved, must be zero */
|
|
Packit |
a132db |
net32_t fe_seq; /* sequence number */
|
|
Packit |
a132db |
};
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* Setup buffer to be sent.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
static void
|
|
Packit |
a132db |
fp_buf_setup(void)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
struct fcping_echo *ep;
|
|
Packit |
a132db |
net8_t *pp;
|
|
Packit |
a132db |
int len;
|
|
Packit |
a132db |
int i;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* Alloc extra in case of odd len or shorter than minimum.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
len = fp_len + sizeof(*ep) + sizeof(net32_t);
|
|
Packit |
a132db |
ep = calloc(1, len);
|
|
Packit |
a132db |
if (ep == NULL)
|
|
Packit |
a132db |
SA_LOG_ERR_EXIT(errno, "calloc %d bytes failed", len);
|
|
Packit |
a132db |
ep->fe_op = ELS_ECHO;
|
|
Packit |
a132db |
net32_put(&ep->fe_seq, 1); /* starting sequence number */
|
|
Packit |
a132db |
i = 0;
|
|
Packit |
a132db |
for (pp = (net8_t *) (ep + 1); pp < (net8_t *) ep + fp_len; pp++)
|
|
Packit |
a132db |
*pp = i++;
|
|
Packit |
a132db |
fp_buf = ep;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
static unsigned long long
|
|
Packit |
a132db |
fp_get_time_usec(void)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
#ifdef _POSIX_TIMERS
|
|
Packit |
a132db |
struct timespec ts;
|
|
Packit |
a132db |
int rc;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
rc = clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
Packit |
a132db |
if (rc)
|
|
Packit |
a132db |
SA_LOG_ERR_EXIT(errno, "clock_gettime error");
|
|
Packit |
a132db |
return ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000;
|
|
Packit |
a132db |
#else
|
|
Packit |
a132db |
#warning no _POSIX_TIMERS
|
|
Packit |
a132db |
struct timeval ts;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
gettimeofday(&ts, NULL);
|
|
Packit |
a132db |
return ts.tv_sec * 1000000ULL + ts.tv_usec;
|
|
Packit |
a132db |
#endif /* _POSIX_TIMERS */
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
static int
|
|
Packit |
a132db |
send_els_echo(int fp_fd, void *fp_buf, uint32_t fp_len,
|
|
Packit |
a132db |
unsigned char *resp, uint32_t *resp_len, fc_fid_t fp_did)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
struct fc_bsg_request cdb;
|
|
Packit |
a132db |
char sense[MAX_SENSE_LEN];
|
|
Packit |
a132db |
struct sg_io_v4 sg_io;
|
|
Packit |
a132db |
int rc;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
cdb.msgcode = FC_BSG_HST_ELS_NOLOGIN;
|
|
Packit |
a132db |
cdb.rqst_data.h_els.command_code = ELS_ECHO;
|
|
Packit |
a132db |
hton24(cdb.rqst_data.h_els.port_id, fp_did);
|
|
Packit |
a132db |
|
|
Packit |
a132db |
sg_io.guard = 'Q';
|
|
Packit |
a132db |
sg_io.protocol = BSG_PROTOCOL_SCSI;
|
|
Packit |
a132db |
sg_io.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT;
|
|
Packit |
a132db |
sg_io.request_len = sizeof(cdb);
|
|
Packit |
a132db |
sg_io.request = (unsigned long)&cdb;
|
|
Packit |
a132db |
sg_io.dout_xfer_len = fp_len;
|
|
Packit |
a132db |
sg_io.dout_xferp = (unsigned long)fp_buf;
|
|
Packit |
a132db |
sg_io.din_xfer_len = *resp_len;
|
|
Packit |
a132db |
sg_io.din_xferp = (unsigned long)resp;
|
|
Packit |
a132db |
sg_io.max_response_len = sizeof(sense);
|
|
Packit |
a132db |
sg_io.response = (unsigned long)sense;
|
|
Packit |
a132db |
sg_io.timeout = 20000;
|
|
Packit |
a132db |
memset(sense, 0, sizeof(sense));
|
|
Packit |
a132db |
|
|
Packit |
a132db |
rc = ioctl(fp_fd, SG_IO, &sg_io);
|
|
Packit |
a132db |
if (rc < 0)
|
|
Packit |
a132db |
return 1;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
*resp_len = sg_io.din_xfer_len - sg_io.din_resid;
|
|
Packit |
a132db |
return 0;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* Send ELS ECHO.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
static int fp_send_ping(void)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
struct fp_stats *sp = &fp_stats;
|
|
Packit |
a132db |
struct fcping_echo *ep;
|
|
Packit |
a132db |
int rc;
|
|
Packit |
a132db |
uint32_t resp_len;
|
|
Packit |
a132db |
unsigned char *resp;
|
|
Packit |
a132db |
unsigned long long tx_time;
|
|
Packit |
a132db |
unsigned long long usec;
|
|
Packit |
a132db |
char msg[80];
|
|
Packit |
a132db |
char time_msg[80];
|
|
Packit |
a132db |
|
|
Packit |
a132db |
resp_len = fp_len + FP_LEN_PAD; /* for odd-byte padding and then some */
|
|
Packit |
a132db |
resp = calloc(1, resp_len);
|
|
Packit |
a132db |
if (resp == NULL)
|
|
Packit |
a132db |
SA_LOG_EXIT("calloc %d bytes failed", resp_len);
|
|
Packit |
a132db |
|
|
Packit |
a132db |
sp->fp_tx_frames++;
|
|
Packit |
a132db |
if (fp_len >= sizeof(*ep)) {
|
|
Packit |
a132db |
ep = (struct fcping_echo *) fp_buf;
|
|
Packit |
a132db |
net32_put(&ep->fe_seq, sp->fp_tx_frames);
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
tx_time = fp_get_time_usec();
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/* send ELS ECHO frame and receive */
|
|
Packit |
a132db |
rc = send_els_echo(fp_fd, fp_buf, fp_len, resp, &resp_len, fp_did);
|
|
Packit |
a132db |
if (rc) {
|
|
Packit |
a132db |
sp->fp_rx_errors++;
|
|
Packit |
a132db |
printf("echo %4d error: %s\n",
|
|
Packit |
a132db |
sp->fp_tx_frames, strerror(errno));
|
|
Packit |
a132db |
} else {
|
|
Packit |
a132db |
usec = fp_get_time_usec();
|
|
Packit |
a132db |
sp->fp_rx_frames++;
|
|
Packit |
a132db |
ep = (struct fcping_echo *) resp;
|
|
Packit |
a132db |
if (usec < tx_time) {
|
|
Packit |
a132db |
snprintf(time_msg, sizeof(time_msg),
|
|
Packit |
a132db |
"time unknown now %llx old %llx",
|
|
Packit |
a132db |
usec, tx_time);
|
|
Packit |
a132db |
usec = 0; /* as if time went backwards */
|
|
Packit |
a132db |
} else {
|
|
Packit |
a132db |
usec = usec - tx_time;
|
|
Packit |
a132db |
snprintf(time_msg, sizeof(time_msg),
|
|
Packit |
a132db |
"%6.3f ms", usec / 1000.0);
|
|
Packit |
a132db |
sp->fp_transit_time_us += usec;
|
|
Packit |
a132db |
sp->fp_rx_times++;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
if (ep->fe_op == ELS_LS_ACC) {
|
|
Packit |
a132db |
if (memcmp((char *) ep + 1,
|
|
Packit |
a132db |
(char *) fp_buf + 1, fp_len - 1) == 0)
|
|
Packit |
a132db |
snprintf(msg, sizeof(msg), "accepted");
|
|
Packit |
a132db |
else {
|
|
Packit |
a132db |
sp->fp_rx_errors++;
|
|
Packit |
a132db |
snprintf(msg, sizeof(msg),
|
|
Packit |
a132db |
"accept data mismatches");
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
} else if (ep->fe_op == ELS_LS_RJT) {
|
|
Packit |
a132db |
sp->fp_rx_errors++;
|
|
Packit |
a132db |
snprintf(msg, sizeof(msg), "REJECT received");
|
|
Packit |
a132db |
} else {
|
|
Packit |
a132db |
sp->fp_rx_errors++;
|
|
Packit |
a132db |
snprintf(msg, sizeof(msg),
|
|
Packit |
a132db |
"op %x received", ep->fe_op);
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
if (fp_quiet == 0)
|
|
Packit |
a132db |
printf("echo %4d %-30s %s\n",
|
|
Packit |
a132db |
sp->fp_tx_frames, msg, time_msg);
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
if (fp_hex) {
|
|
Packit |
a132db |
printf("response length %u\n", resp_len);
|
|
Packit |
a132db |
sa_hex_dump(resp, resp_len, stdout);
|
|
Packit |
a132db |
printf("\n");
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
free(resp);
|
|
Packit |
a132db |
return rc;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
static void fp_signal_handler(UNUSED int sig)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* Allow graceful termination of the
|
|
Packit |
a132db |
* for loop in fp_start.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
fp_count = 0;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* Main loop.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
static void fp_start(void)
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
struct sigaction act;
|
|
Packit |
a132db |
int i;
|
|
Packit |
a132db |
int rc;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
memset(&act, 0, sizeof(act));
|
|
Packit |
a132db |
act.sa_handler = fp_signal_handler;
|
|
Packit |
a132db |
act.sa_flags = 0;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
sigaction(SIGTERM, &act, NULL); /* Signal 15: kill <pid> */
|
|
Packit |
a132db |
sigaction(SIGQUIT, &act, NULL); /* Signal 3: Ctrl-\ */
|
|
Packit |
a132db |
sigaction(SIGINT, &act, NULL); /* Signal 2: Ctrl-C */
|
|
Packit |
a132db |
|
|
Packit |
a132db |
printf("Sending FC ELS ECHO from %s (%s) to 0x%X:\n",
|
|
Packit |
a132db |
port_attrs->port_id, fp_dev, fp_did);
|
|
Packit |
a132db |
|
|
Packit |
a132db |
for (i = 0; fp_count == -1 || i < fp_count; i++) {
|
|
Packit |
a132db |
rc = fp_send_ping();
|
|
Packit |
a132db |
if (rc != 0 && errno == EMSGSIZE)
|
|
Packit |
a132db |
break;
|
|
Packit |
a132db |
if (rc != 0 && errno == ECONNABORTED)
|
|
Packit |
a132db |
break;
|
|
Packit |
a132db |
if (fp_flood == 0)
|
|
Packit |
a132db |
usleep(fp_interval * 1000);
|
|
Packit |
a132db |
if (!fp_count)
|
|
Packit |
a132db |
break;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
/*
|
|
Packit |
a132db |
* Main.
|
|
Packit |
a132db |
*/
|
|
Packit |
a132db |
int main(int argc, char *argv[])
|
|
Packit |
a132db |
{
|
|
Packit |
a132db |
char bsg_dev[80];
|
|
Packit |
a132db |
int rc = 1;
|
|
Packit |
a132db |
|
|
Packit |
a132db |
fp_options(argc, argv);
|
|
Packit |
a132db |
if (fp_find_hba()) {
|
|
Packit |
a132db |
sprintf(bsg_dev, "/dev/bsg/%s", fp_dev);
|
|
Packit |
a132db |
fp_fd = open(bsg_dev, O_RDWR);
|
|
Packit |
a132db |
if (fp_fd < 0)
|
|
Packit |
a132db |
SA_LOG_ERR_EXIT(errno,
|
|
Packit |
a132db |
"open of %s failed", bsg_dev);
|
|
Packit |
a132db |
|
|
Packit |
a132db |
if (!fp_lookup_target()) {
|
|
Packit |
a132db |
fp_check_data_len();
|
|
Packit |
a132db |
fp_buf_setup();
|
|
Packit |
a132db |
fp_start();
|
|
Packit |
a132db |
fp_report();
|
|
Packit |
a132db |
rc = 0;
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
free(port_attrs);
|
|
Packit |
a132db |
free(host);
|
|
Packit |
a132db |
close(fp_fd);
|
|
Packit |
a132db |
}
|
|
Packit |
a132db |
|
|
Packit |
a132db |
return rc;
|
|
Packit |
a132db |
}
|