Sends the ELS_RLS (Read Link Status) command to the target rport to retrieve
the Link Error Status Block (LESB) as described by the FC-BB-5 Rev2. The
target rport is assumed to be already PLOGI-ed from where this command is
issued.

This tool is based on existing fcping/fcnsq tool, however as ELS_RLS must
be sent to a logged-in port, fcrls sends this command via FC_BSG_RPT_ELS,
not FC_BSG_HST_ELS_NOLOGIN.

Signed-off-by: Yi Zou <[email protected]>
---

 Makefile.am |    2 
 fcrls.c     |  488 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 489 insertions(+), 1 deletions(-)
 create mode 100644 fcrls.c

diff --git a/Makefile.am b/Makefile.am
index 3f8ac7f..0c28462 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,5 +1,5 @@
 ## target programs, to be built and installed in $(prefix)/sbin
-sbin_PROGRAMS = fcoeadm fcping fipvlan fcnsq
+sbin_PROGRAMS = fcoeadm fcping fipvlan fcnsq fcrls
 if WITH_DCB
 sbin_PROGRAMS += fcoemon
 endif
diff --git a/fcrls.c b/fcrls.c
new file mode 100644
index 0000000..233df20
--- /dev/null
+++ b/fcrls.c
@@ -0,0 +1,488 @@
+/*
+ * 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
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <linux/types.h>
+typedef __u8 u8;
+typedef __u16 u16;
+typedef __u32 u32;
+typedef __u64 u64;
+#include <linux/bsg.h>
+#include <scsi/sg.h>
+#include <scsi/fc/fc_els.h>
+#include <scsi/scsi_bsg_fc.h>
+
+#define ntoh24(n) (u32) ((n)[0] << 16 | (n)[1] << 8  | (n)[2])
+#define hton24(h) { (h) >> 16 & 0xff, (h) >> 8 & 0xff, (h) & 0xff }
+
+#define SYSFS_FC_RPORTS "/sys/class/fc_remote_ports"
+
+struct rport_info {
+       int host_no;
+       int channel;
+       u32 number;
+       u32 port_id;
+       bool found;
+       bool online;
+};
+
+static char *rjt_reason[] = {
+       [1] = "Invalid command code",
+       [2] = "Invalid version level",
+       [3] = "Logical error",
+       [4] = "Invalid CT_IU size",
+       [5] = "Logical busy",
+       [7] = "Protocol error",
+       [9] = "Unable to perform command request",
+       [0xB] = "Command not supported",
+       [0xD] = "Server not available",
+       [0xE] = "Session could not be established",
+       [0xFF] = "Vendor specific",
+       [0x100] = "N/A",
+};
+
+static char *rjt_explan[] = {
+       [0] = "No additional explanation",
+       [1] = "Port Identifier not registered",
+       [2] = "Port Name not registered",
+       [3] = "Node Name not registered",
+       [4] = "Class of Service not registered",
+       [6] = "Initial Process Associator not registered",
+       [7] = "FC-4 Types not registered",
+       [8] = "Symbolic Port Name not registered",
+       [9] = "Symbolic Node Name not registered",
+       [0xA] = "Port Type not registered",
+       [0xC] = "Fabric Port Name not registered",
+       [0xD] = "Hard Address not registered",
+       [0xF] = "FC-4 Features not registered",
+       [0x10] = "Access denied",
+       [0x11] = "Unacceptable Port Identifier",
+       [0x12] = "Data base empty",
+       [0x13] = "No object registered in the specified scope",
+       [0x14] = "Domain ID not present",
+       [0x15] = "Port number not present",
+       [0x16] = "No device attached",
+       [0x17] = "invalid OX_ID-RX_ID combination",
+       [0x19] = "Request already in progress",
+       [0x1e] = "N_Port login required",
+       [0x29] = "insufficient resources",
+       [0x2a] = "unable to supply requested data",
+       [0x2c] = "Request not supported",
+       [0x2d] = "Invalid payload length",
+       [0x44] = "Invalid Port/Node_Name",
+       [0x46] = "Login Extension not supported",
+       [0x48] = "Authentication required",
+       [0x50] = "Periodic Scan Value not allowed",
+       [0x51] = "Periodic Scanning not supported",
+       [0x60] = "MAC addressing mode not supported",
+       [0x61] = "Proposed MAC address incorrectly formed",
+       [0xf0] = "Authorization Exception",
+       [0xf1] = "Authentication Exception",
+       [0xf2] = "Data base full",
+       [0xf3] = "Data base empty",
+       [0xf4] = "Processing request",
+       [0xf5] = "Unable to verify connection",
+       [0xf6] = "Devices not in a common zone",
+       [0x100] = "N/A",
+
+};
+
+enum commands {
+       NONE = 0,
+       RLS_PORT,
+       RLS_FCID,
+       RLS_QUIET,
+       RLS_USAGE,
+};
+
+/* RLS_QUIET */
+static bool quiet;
+
+/* : - has arg
+ * :: - has optional arg
+ * ;  - arg is long opt
+ */
+static const struct option lopt[] = {
+       { "port", required_argument, NULL, RLS_PORT },
+       { "fcid", required_argument, NULL, RLS_FCID },
+       { "quiet", no_argument, NULL, RLS_QUIET },
+       { "usage", no_argument, NULL, RLS_USAGE },
+       { NULL, 0, NULL, 0 },
+};
+
+static const char *lopt_usage[] = {
+       "rport bsg name, e.g., rport-7:0-1.",
+       "rport port FC_ID, e.g., 0xce000d.",
+       "disable verbose output.",
+       "print useage information.",
+       NULL,
+};
+
+#define bsg_error(format...)           \
+({                                     \
+       fprintf(stderr, "ERROR: " format);      \
+})
+
+#define bsg_debug(format...)           \
+({                                     \
+       if (!quiet)                     \
+               printf("DEBUG: " format);               \
+})
+
+static char *els_rjt2str(int type, int code)
+{
+       char **str;
+
+       str = (type == 0) ? rjt_reason : rjt_explan;
+
+       if (code > 0xff)
+               code = 0x100;
+
+       if (!str[code])
+               code = 0x100;
+
+       return str[code];
+}
+
+static int els_print_lesb(struct fc_els_lesb_bb5 *lesb)
+{
+       printf("RLS request accepted (LS_ACC), dumping status counters:\n"
+               "\tLink Failure Count                   = %u\n"
+               "\tVirtual Link Failure Count           = %u\n"
+               "\tMissed Discovery Advertisement Count = %u\n"
+               "\tSymbol Error During Carrier Count    = %u\n"
+               "\tErrored Block Count                  = %u\n"
+               "\tFrame Check Sequence Error Count     = %u\n",
+               ntohl(lesb->lesb_link_fail),
+               ntohl(lesb->lesb_vlink_fail),
+               ntohl(lesb->lesb_miss_fip),
+               ntohl(lesb->lesb_symb_err),
+               ntohl(lesb->lesb_err_block),
+               ntohl(lesb->lesb_fcs_error));
+
+       return 0;
+}
+
+static int els_print_rjt(struct fc_els_ls_rjt *rjt)
+{
+       printf("RLS request rejected (LS_RJT), check reason code below:\n"
+               "\tReason Code  = 0x%02x, %s.\n"
+               "\tExplain Code = 0x%02x, %s.\n",
+               rjt->er_reason, els_rjt2str(0, rjt->er_reason),
+               rjt->er_explan, els_rjt2str(1, rjt->er_explan));
+       if (rjt->er_reason == ELS_RJT_VENDOR)
+               printf("\tVendor Code  = 0x%02x (check with your vendor).\n",
+                      rjt->er_vendor);
+       return 0;
+}
+
+static int els_print_rsp(struct fc_els_rls_resp *rsp)
+{
+       if (rsp->rls_cmd == ELS_LS_ACC) {
+               struct fc_els_lesb_bb5 *lesb;
+
+               lesb = (struct fc_els_lesb_bb5 *)&rsp->rls_lesb;
+               return  els_print_lesb(lesb);
+       }
+       if (rsp->rls_cmd == ELS_LS_RJT) {
+               struct fc_els_ls_rjt *rjt;
+
+               rjt = (struct fc_els_ls_rjt *)rsp;
+               return els_print_rjt(rjt);
+       }
+       bsg_error("Unknow response!\n");
+       return EIO;
+}
+
+static int bsg_rport_els(int bsg, u8 els_code,
+       void *req, int req_len, void *rsp, int rsp_len)
+{
+       int rc;
+       char sense[96];
+       struct fc_bsg_reply *reply = (struct fc_bsg_reply *)sense;
+       struct fc_bsg_request cdb = {
+               .msgcode        = FC_BSG_RPT_ELS,
+               .rqst_data.r_els = {
+                       .els_code = els_code,
+               }
+       };
+
+       struct sg_io_v4 sgio = {
+               .guard                  = 'Q',
+               .protocol               = BSG_PROTOCOL_SCSI,
+               .subprotocol            = BSG_SUB_PROTOCOL_SCSI_TRANSPORT,
+               .request_len            = sizeof(cdb),
+               .request                = (u64) &cdb,
+               .dout_xfer_len          = req_len,
+               .dout_xferp             = (u64) req,
+               .din_xfer_len           = rsp_len,
+               .din_xferp              = (u64) rsp,
+               .max_response_len       = sizeof(sense),
+               .response               = (u64) &sense,
+               .timeout                = 1000,
+       };
+       memset(sense, 0, sizeof(sense));
+       rc = ioctl(bsg, SG_IO, &sgio);
+       bsg_debug("ioctl returned %d: bsg_reply result=%d\n",
+                rc, reply->result);
+       return rc;
+}
+
+static int bsg_rport_els_rls(int bsg, struct rport_info *rpi)
+{
+       int rc = EOPNOTSUPP;
+       struct fc_els_rls rls = {
+               .rls_cmd = ELS_RLS,
+               .rls_port_id = hton24(rpi->port_id),
+       };
+       struct fc_els_rls_resp rsp;
+
+       memset(&rsp, 0, sizeof(rsp));
+       rc = bsg_rport_els(bsg, ELS_RLS, &rls, sizeof(rls), &rsp, sizeof(rsp));
+       if (rc) {
+               bsg_error("bsg_rport_els(ELS_RLS) failed\n");
+               return rc;
+       }
+       return els_print_rsp(&rsp);
+}
+
+static int rport_getid(struct rport_info *rpi)
+{
+       FILE *f;
+       char rp_sysfs[256];
+
+       if (rpi->found)
+               return 0;
+       snprintf(rp_sysfs, sizeof(rp_sysfs), "%s/rport-%d:%d-%d/port_id",
+               SYSFS_FC_RPORTS, rpi->host_no, rpi->channel, rpi->number);
+       f = fopen(rp_sysfs, "ro");
+       if (!f) {
+               bsg_error("failed to fopen(%s)!\n", rp_sysfs);
+               return -ENODEV;
+       }
+       if (1 != fscanf(f, "0x%6x", &rpi->port_id)) {
+               bsg_error("failed to fscanf(%s)\n", rp_sysfs);
+               fclose(f);
+               return -ENODEV;
+       }
+       if (rpi->port_id & 0xff000000) {
+               bsg_error("rport %s:invalid fcid 0x%x\n", rp_sysfs,
+                         rpi->port_id);
+               rpi->port_id = 0;
+               fclose(f);
+               return -ENODEV;
+       }
+       fclose(f);
+       return 0;
+}
+
+/*
+ * parse a string in format of rport-%d:%d-%d, and get the
+ * corresponding rport info.
+ * rport-%d:%d-%d
+ */
+static int rport_parse(const char *s, struct rport_info *rpi)
+{
+       if (!s)
+               return -EINVAL;
+       memset(rpi, 0, sizeof(*rpi));
+       if (3 != sscanf(s, "rport-%d:%d-%d", &rpi->host_no, &rpi->channel,
+                       &rpi->number))
+               return -ENODEV;
+       if (rport_getid(rpi))
+               return -ENODEV;
+       return 0;
+}
+
+#define RPORT_ONLINE   "Online"
+static int rport_check_state(struct rport_info *rpi)
+{
+       FILE *f;
+       char rp_sysfs[256];
+       char rp_state[256];
+
+       rpi->online = false;
+       if (!rpi->found)
+               return -EINVAL;
+
+       snprintf(rp_sysfs, sizeof(rp_sysfs), "%s/rport-%d:%d-%d/port_state",
+               SYSFS_FC_RPORTS, rpi->host_no, rpi->channel, rpi->number);
+
+       f = fopen(rp_sysfs, "ro");
+       if (!f) {
+               bsg_error("failed to fopen(%s)!\n", rp_sysfs);
+               return -ENODEV;
+       }
+       if (!fgets(rp_state, sizeof(rp_state), f)) {
+               bsg_error("failed to fgets(%s)!\n", rp_sysfs);
+               fclose(f);
+               return -ENODEV;
+       }
+       if (strncmp(rp_state, RPORT_ONLINE, strlen(RPORT_ONLINE))) {
+               bsg_error("rport 0x%x %s:must be %s\n", rpi->port_id,
+                       rp_state, RPORT_ONLINE);
+               fclose(f);
+               return -ENODEV;
+       }
+       rpi->online = true;
+       fclose(f);
+       return 0;
+}
+/* locate rport by fcid */
+static int rport_find(struct rport_info *rpi)
+{
+       int n;
+       struct dirent **namelist;
+       struct rport_info rpii;
+
+       if (rpi->found)
+               return 0;
+
+       if (!rpi->port_id)
+               return -ENODEV;
+
+       n = scandir(SYSFS_FC_RPORTS, &namelist, 0, alphasort);
+       if (n < 0) {
+               bsg_error("failed to scandir %s\n", SYSFS_FC_RPORTS);
+               return -ENODEV;
+       }
+       while (n--) {
+               if (namelist[n]->d_type != DT_DIR)
+                       goto free_name;
+               if (rport_parse(namelist[n]->d_name, &rpii))
+                       goto free_name;
+               if (rpi->port_id != rpii.port_id)
+                       goto free_name;
+               rpii.found = true;
+               memcpy(rpi, &rpii, sizeof(rpii));
+               bsg_debug("found rport 0x%06x as rport-%d:%d-%d\n",
+                         rpi->port_id, rpi->host_no, rpi->channel,
+                         rpi->number);
+free_name:
+               free(namelist[n]);
+       }
+       free(namelist);
+       return 0;
+}
+
+static void bsg_usage(int status)
+{
+       int i, n;
+
+       if (status)
+               bsg_error("Failed! %s (Errno %d)!\n", strerror(status), status);
+
+       n = sizeof(lopt)/sizeof(struct option) - 1;
+       printf("Usage: fcrls\n");
+       for (i = 0; i < n; i++)
+               printf("\t--%s: %s\n", lopt[i].name, lopt_usage[i]);
+       exit(status);
+}
+
+
+int main(int argc, char *argv[])
+{
+       int rc = -ENODEV;
+       int opt;
+       int bsg_dev;
+       char *endptr;
+       char *bsg_name = NULL;
+       struct rport_info rpi;
+
+       while ((opt = getopt_long(argc, argv, "", lopt, NULL)) != -1) {
+               switch (opt) {
+               case RLS_PORT:
+                       if (rport_parse(optarg, &rpi)) {
+                               bsg_error("%s format incorect, must be:"
+                                       "rport-host:channel-number\n", optarg);
+                               bsg_usage(-EINVAL);
+                       }
+                       rpi.found = true;
+                       break;
+               case RLS_FCID:
+                       rpi.found = false;
+                       rpi.port_id = strtoull(optarg, &endptr, 16);
+                       if (*endptr != '\0') {
+                               bsg_error("%s has no valid FCID\n", optarg);
+                               bsg_usage(-EINVAL);
+                       }
+                       if (rport_find(&rpi)) {
+                               bsg_error("%s is not a rport\n", optarg);
+                               bsg_usage(-ENODEV);
+                       }
+                       break;
+               case RLS_QUIET:
+                       quiet = true;
+                       break;
+               case RLS_USAGE:
+                       bsg_usage(0);
+                       break;
+               }
+       }
+       /* bsg device name */
+       if (!rpi.found)
+               bsg_usage(rc);
+
+       if (asprintf(&bsg_name, "/dev/bsg/rport-%d:%d-%d",
+               rpi.host_no, rpi.channel, rpi.number) < 0) {
+               rc = ENOMEM;
+               bsg_error("not enough memory!\n");
+               goto out_error;
+       }
+       /* open bsg device */
+       bsg_dev = open(bsg_name, O_RDWR);
+       if (bsg_dev < 0) {
+               bsg_error("failed top bsg device %s!\n", bsg_name);
+               goto out_free;
+       }
+       /* check port state */
+       if ((rport_check_state(&rpi)) || (!rpi.online)) {
+               bsg_error("rport 0x%x is not online!\n", rpi.port_id);
+               goto out_close;
+       }
+       /* send rls */
+       rc = bsg_rport_els_rls(bsg_dev, &rpi);
+       if (rc) {
+               bsg_error("Faild to bsg_rport_els_rls\n");
+               goto out_close;
+       }
+       rc = 0;
+
+out_close:
+       close(bsg_dev);
+out_free:
+       free(bsg_name);
+out_error:
+       return rc;
+}

_______________________________________________
devel mailing list
[email protected]
http://www.open-fcoe.org/mailman/listinfo/devel

Reply via email to