Whamcloud - gitweb
LU-9667 lnet: Userspace control to show lnet ni stats
authorSonia Sharma <sharmaso@whamcloud.com>
Thu, 5 Jul 2018 00:02:59 +0000 (20:02 -0400)
committerSonia Sharma <sharmaso@whamcloud.com>
Wed, 29 May 2019 07:08:15 +0000 (03:08 -0400)
Adding lustre_lnet_stats_ni() function to show
lnet NI's statistics.

Adding helper functions in liblnetconfig.c to traverse
the lnet ni sysfs structure and show their statistics.
With this change, lnetctl utility can be used to run
below example commands to show the NI stats.

lnetctl net stats
lnetctl net stats --nid <nid1>,<nid2>,<nid3>
lnetctl net stats --nid 192.168.56.[101-103]@tcp
lnetctl net stats --net tcp

Change-Id: I2b4e7e95f738fcf573cddef3f743283149325750
Test-Parameters: forbuildonly
Signed-off-by: Sonia Sharma <sharmaso@whamcloud.com>
Reviewed-on: https://review.whamcloud.com/32786
Reviewed-by: James Simmons <uja.ornl@yahoo.com>
Reviewed-by: Amir Shehata <ashehata@whamcloud.com>
Tested-by: Jenkins
lnet/utils/lnetconfig/liblnetconfig.c
lnet/utils/lnetconfig/liblnetconfig.h
lnet/utils/lnetctl.c

index bdcda35..ff77957 100644 (file)
@@ -48,6 +48,8 @@
 #include "liblnd.h"
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <mntent.h>
+#include <dirent.h>
 #include <fcntl.h>
 #include <ifaddrs.h>
 #include "liblnetconfig.h"
@@ -87,6 +89,39 @@ struct lustre_lnet_ip2nets {
        struct list_head ip2nets_ip_ranges;
 };
 
+static char *find_mount_point(const char *filesys)
+{
+       FILE *fp;
+       struct mntent *ent;
+
+       fp = setmntent("/proc/mounts", "r");
+       if (fp == NULL)
+               return NULL;
+
+       while ((ent = getmntent(fp)) != NULL) {
+               if (strncmp(ent->mnt_fsname, filesys, strlen(ent->mnt_fsname))
+                   == 0)
+                       break;
+       }
+       endmntent(fp);
+
+       return ent->mnt_dir;
+}
+
+static char *concat_dir(const char *str1, const char *str2)
+{
+       char *result;
+
+       result = malloc(strlen(str1) + strlen(str2) + 2);
+       if (result == NULL)
+               return NULL;
+
+       strcpy(result, str1);
+       strcat(result, str2);
+       strcat(result, "/");
+       return result;
+}
+
 int open_sysfs_file(const char *path, const char *attr, const int mode)
 {
        int fd;
@@ -3876,6 +3911,795 @@ out:
        return rc;
 }
 
+static int build_ni_stats_tree(char *path, struct cYAML *root, char *nid,
+                              int detail, char *err_str)
+{
+       struct cYAML *send = NULL, *recv = NULL, *drop = NULL, *health = NULL,
+                    *message = NULL, *ni_node = NULL;
+       char *stats_path = NULL;
+       char val[LNET_MAX_STR_LEN];
+       int rc = LUSTRE_CFG_RC_OUT_OF_MEM;
+       int buf;
+
+       stats_path = concat_dir(path, "stats");
+       if (!stats_path)
+               return rc;
+
+       ni_node = cYAML_create_seq_item(root);
+       if (!ni_node)
+               goto out;
+
+       if (!cYAML_create_string(ni_node, "nid", nid))
+               goto out;
+
+       message = cYAML_create_object(ni_node, "message");
+       if (!message)
+               goto out;
+
+       if (!detail) {
+               if (!read_sysfs_file(stats_path, "total_send_count", val, 1,
+                                    sizeof(val))) {
+                       buf = atoi(val);
+                       if (!cYAML_create_number(message, "send", buf))
+                               goto out;
+               }
+               memset(&val[0], 0, sizeof(val));
+
+               if (!read_sysfs_file(stats_path, "total_recv_count", val, 1,
+                                    sizeof(val))) {
+                       buf = atoi(val);
+                       if (!cYAML_create_number(message, "receive", buf))
+                               goto out;
+               }
+               memset(&val[0], 0, sizeof(val));
+
+               if (!read_sysfs_file(stats_path, "total_drop_count", val, 1,
+                                    sizeof(val))) {
+                       buf = atoi(val);
+                       if (!cYAML_create_number(message, "drop", buf))
+                               goto out;
+               }
+               memset(&val[0], 0, sizeof(val));
+
+               goto health_stats;
+       }
+
+       send = cYAML_create_object(message, "send");
+       if (!send)
+               goto out;
+
+       if (!read_sysfs_file(stats_path, "get_send_count", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(send, "get", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       if (!read_sysfs_file(stats_path, "put_send_count", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(send, "put", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       if (!read_sysfs_file(stats_path, "reply_send_count", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(send, "reply", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       if (!read_sysfs_file(stats_path, "ack_send_count", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(send, "ack", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       if (!read_sysfs_file(stats_path, "hello_send_count", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(send, "hello", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       recv = cYAML_create_object(message, "receive");
+       if (!recv)
+               goto out;
+
+       if (!read_sysfs_file(stats_path, "get_recv_count", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(recv, "get", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       if (!read_sysfs_file(stats_path, "put_recv_count", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(recv, "put", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       if (!read_sysfs_file(stats_path, "reply_recv_count", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(recv, "reply", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       if (!read_sysfs_file(stats_path, "ack_recv_count", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(recv, "ack", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       if (!read_sysfs_file(stats_path, "hello_recv_count", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(recv, "hello", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       drop = cYAML_create_object(message, "drop");
+       if (!drop)
+               goto out;
+
+       if (!read_sysfs_file(stats_path, "get_drop_count", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(drop, "get", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       if (!read_sysfs_file(stats_path, "put_drop_count", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(drop, "put", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       if (!read_sysfs_file(stats_path, "reply_drop_count", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(drop, "reply", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       if (!read_sysfs_file(stats_path, "ack_drop_count", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(drop, "ack", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       if (!read_sysfs_file(stats_path, "hello_drop_count", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(drop, "hello", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+health_stats:
+       health = cYAML_create_object(ni_node, "health");
+       if (!health)
+               goto out;
+
+       if (!read_sysfs_file(stats_path, "local_interrupt", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(health, "interrupt", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       if (!read_sysfs_file(stats_path, "local_dropped", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(health, "dropped", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       if (!read_sysfs_file(stats_path, "local_aborted", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(health, "aborted", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       if (!read_sysfs_file(stats_path, "local_no_route", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(health, "no_route", buf))
+                       goto out;
+       }
+
+       if (!read_sysfs_file(stats_path, "local_timeout", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(health, "timeout", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       if (!read_sysfs_file(stats_path, "local_error", val, 1,
+                            sizeof(val))) {
+               buf = atoi(val);
+               if (!cYAML_create_number(health, "error", buf))
+                       goto out;
+       }
+       memset(&val[0], 0, sizeof(val));
+
+       rc = LUSTRE_CFG_RC_NO_ERR;
+
+out:
+       free(stats_path);
+       return rc;
+}
+
+static int traverse_sysfs_intf(const char *path, char *nw, char *nid,
+                              struct cYAML *root, int detail, char *err_str)
+{
+       struct dirent *dent;
+       DIR *srcdir = opendir(path);
+       char *intfpath = NULL;
+       int rc = LUSTRE_CFG_RC_BAD_PARAM;
+
+       if (srcdir == NULL)
+               return rc;
+
+       while ((dent = readdir(srcdir)) != NULL) {
+               struct stat st;
+
+               if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..")
+                   == 0)
+                       continue;
+
+               if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0)
+                       continue;
+
+               if (S_ISDIR(st.st_mode)) {
+                       struct lnet_dlc_intf_descr *intf;
+                       struct list_head intf_list;
+                       char *ni;
+                       __u32 ip;
+
+                       INIT_LIST_HEAD(&intf_list);
+
+                       intfpath = concat_dir(path, dent->d_name);
+                       if (!intfpath) {
+                               closedir(srcdir);
+                               return LUSTRE_CFG_RC_OUT_OF_MEM;
+                       }
+
+                       rc = lustre_lnet_add_intf_descr(&intf_list, dent->d_name,
+                                                       strlen(dent->d_name));
+                       if (rc != LUSTRE_CFG_RC_NO_ERR)
+                               goto out;
+
+                       list_for_each_entry(intf, &intf_list, intf_on_network) {
+                               rc = lustre_lnet_queryip(intf, &ip);
+                               if (rc != LUSTRE_CFG_RC_NO_ERR) {
+                                       snprintf(err_str, LNET_MAX_STR_LEN,
+                                                "\"couldn't query intf %s\"",
+                                                intf->intf_name);
+                                       goto out;
+                               }
+                       }
+
+                       ni = libcfs_nid2str(LNET_MKNID(libcfs_str2net(nw), ip));
+
+                       if (nid == NULL || (strcmp(nid, ni) == 0)) {
+                               rc = build_ni_stats_tree(intfpath, root, ni,
+                                                        detail, err_str);
+                               if (rc != LUSTRE_CFG_RC_NO_ERR)
+                                       goto out;
+
+                               free(intfpath);
+
+                               if (nid)
+                                       break;
+                       } else {
+                               free(intfpath);
+                               continue;
+                       }
+               }
+       }
+
+       closedir(srcdir);
+       return rc;
+out:
+       free(intfpath);
+
+       if (srcdir)
+               closedir(srcdir);
+       return rc;
+}
+
+static int traverse_sysfs_net_path(const char *path, char *nw, char *nid,
+                                  __u32 *netlist, int net_count,
+                                  struct cYAML *root, int detail,
+                                  char *err_str)
+{
+       struct cYAML *item = NULL, *tmp = NULL;
+       char *netpath = NULL;
+       int rc = LUSTRE_CFG_RC_OUT_OF_MEM;
+       int i = 0;
+       bool found = false;
+
+       for (i = 0; i < net_count; i++) {
+               char *net = libcfs_net2str(netlist[i]);
+               if (nw == NULL || (nw && strcmp(nw, net) == 0)) {
+                       found = true;
+                       netpath = concat_dir(path, net);
+
+                       item = cYAML_create_seq_item(root);
+                       if (!item)
+                               goto out;
+
+                       if (!cYAML_create_string(item, "net type", net))
+                               goto out;
+
+                       tmp = cYAML_create_seq(item, "local NI(s)");
+                       if (!tmp)
+                               goto out;
+
+                       rc = traverse_sysfs_intf(netpath, net, nid, tmp,
+                                                detail, err_str);
+                       if (rc != LUSTRE_CFG_RC_NO_ERR)
+                               goto out;
+
+                       free(netpath);
+               } else
+                       continue;
+       }
+
+       if (!found) {
+               rc = LUSTRE_CFG_RC_BAD_PARAM;
+               snprintf(err_str, LNET_MAX_STR_LEN, "\"Bad parameter - %s\"",
+                        nw);
+       }
+
+       return rc;
+
+out:
+       free(netpath);
+       return rc;
+}
+
+static int check_ip_in_range(char *nidstr, int count, char **config_nids,
+                            char **rnids, char *err_str)
+{
+       struct lustre_lnet_ip_range_descr *ipr;
+       struct lustre_lnet_ip2nets ip2nets;
+       char *start, *delimit;
+       char ip_range[LNET_MAX_STR_LEN];
+       __u32 net, ip;
+       int rc = LUSTRE_CFG_RC_BAD_PARAM;
+       int i = 0, rcount = 0, ip_range_len = 0;
+
+       /* initialize all lists */
+       INIT_LIST_HEAD(&ip2nets.ip2nets_ip_ranges);
+       INIT_LIST_HEAD(&ip2nets.ip2nets_net.network_on_rule);
+       INIT_LIST_HEAD(&ip2nets.ip2nets_net.nw_intflist);
+
+       start = nidstr;
+       delimit = strchr(nidstr, '@');
+       if (!delimit) {
+               snprintf(err_str, LNET_MAX_STR_LEN, "\"cannot parse NID %s\"",
+                        nidstr);
+               return rc;
+       }
+
+       ip_range_len = delimit - start;
+
+       delimit++;
+       net = libcfs_str2net(delimit);
+       /* expression support is for o2iblnd and socklnd only */
+       if (LNET_NETTYP(net) != O2IBLND && LNET_NETTYP(net) != SOCKLND)
+               return LUSTRE_CFG_RC_SKIP;
+
+       strncpy(ip_range, start, ip_range_len);
+       ip_range[ip_range_len] = '\0';
+       ip2nets.ip2nets_net.nw_id = net;
+
+       rc = lustre_lnet_add_ip_range(&ip2nets.ip2nets_ip_ranges, ip_range);
+       if (rc != LUSTRE_CFG_RC_NO_ERR) {
+               snprintf(err_str, LNET_MAX_STR_LEN,
+                        "\"cannot parse ip_range '%s'\"", ip_range);
+               goto out;
+       }
+
+       list_for_each_entry(ipr, &ip2nets.ip2nets_ip_ranges, ipr_entry) {
+               for (i = 0; i < count; i++) {
+                       ip = LNET_NIDADDR(libcfs_str2nid(config_nids[i]));
+                       rc = cfs_ip_addr_match(ip, &ipr->ipr_expr);
+                       if (rc) {
+                               rnids[rcount] = config_nids[i];
+                               rcount++;
+                       }
+               }
+       }
+       rc = rcount;
+out:
+       lustre_lnet_clean_ip2nets(&ip2nets);
+       return rc;
+}
+
+static int add_nid_to_tree(const char *path, char *nid, struct cYAML *root,
+                          int detail, char *err_str)
+{
+       struct cYAML *tree = NULL, *net_node = NULL, *tmp = NULL,
+                    *ni_node = NULL, *local_nis = NULL, *item = NULL,
+                    *item1 = NULL;
+       char *net, *delimit;
+       char *netpath = NULL;
+       int rc = LUSTRE_CFG_RC_BAD_PARAM;
+
+       delimit = strchr(nid, '@');
+       if (delimit == NULL) {
+               snprintf(err_str, LNET_MAX_STR_LEN, "\"Cannot parse NID - %s\"",
+                        nid);
+               return rc;
+       }
+
+       delimit++;
+       net = delimit;
+
+       tree = cYAML_get_object_item(root, "net");
+       if (!tree && !cYAML_is_sequence(tree)) {
+               snprintf(err_str, LNET_MAX_STR_LEN, "\"yaml tree root not\""
+                        " found\"");
+               return rc;
+       }
+
+       /* check if the sub-tree for the net-type exists or not.
+        * if exists, then add the nid to the subtree else create
+        * one for this net type.
+        */
+       while (cYAML_get_next_seq_item(tree, &item) != NULL) {
+               net_node = cYAML_get_object_item(item, "net type");
+               /* if no net exists yet, then configure the first */
+               if (!net_node)
+                       goto create_net_tree;
+
+               if (strcmp(net_node->cy_valuestring, net) == 0) {
+                       local_nis = cYAML_get_object_item(item, "local NI(s)");
+                       if (local_nis == NULL || !cYAML_is_sequence(local_nis))
+                               return LUSTRE_CFG_RC_BAD_PARAM;
+
+                       while (cYAML_get_next_seq_item(local_nis,
+                                                      &item1) != NULL) {
+                               ni_node = cYAML_get_object_item(item1, "nid");
+                               if (!ni_node)
+                                       return LUSTRE_CFG_RC_MISSING_PARAM;
+
+                               if (strcmp(ni_node->cy_valuestring, nid) == 0)
+                                       return LUSTRE_CFG_RC_NO_ERR;
+                       }
+
+                       netpath = concat_dir(path, net);
+                       rc = traverse_sysfs_intf(netpath, net, nid, local_nis,
+                                                detail, err_str);
+                       free(netpath);
+                       return rc;
+               }
+       }
+
+create_net_tree:
+       rc = LUSTRE_CFG_RC_OUT_OF_MEM;
+       netpath = concat_dir(path, net);
+
+       item = cYAML_create_seq_item(tree);
+       if (!item)
+               goto out;
+
+       if (cYAML_create_string(tree, "net type", net) == NULL)
+               goto out;
+
+       tmp = cYAML_create_seq(tree, "local NI(s)");
+       if (!tmp)
+               goto out;
+
+       rc = traverse_sysfs_intf(netpath, net, nid, tmp, detail, err_str);
+
+out:
+       free(netpath);
+       return rc;
+}
+
+static int lnet_get_config_ni_list(char *path, char *net, char **config_nids,
+                                  int *count)
+{
+       struct dirent *dent;
+       DIR *srcdir = opendir(path);
+       __u32 nw = libcfs_str2net(net);
+       int rc = LUSTRE_CFG_RC_BAD_PARAM;
+
+       if (!srcdir)
+               return rc;
+
+       while ((dent = readdir(srcdir)) != NULL) {
+               struct stat st;
+
+               if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..")
+                   == 0)
+                       continue;
+
+               if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0)
+                       continue;
+
+               if (S_ISDIR(st.st_mode)) {
+                       struct lnet_dlc_intf_descr *intf;
+                       struct list_head intf_list;
+                       __u32 ip;
+
+                       INIT_LIST_HEAD(&intf_list);
+
+                       rc = lustre_lnet_add_intf_descr(&intf_list,
+                                                       dent->d_name,
+                                                       strlen(dent->d_name));
+                       if (rc != LUSTRE_CFG_RC_NO_ERR)
+                               goto out;
+
+                       list_for_each_entry(intf, &intf_list, intf_on_network) {
+                               rc = lustre_lnet_queryip(intf, &ip);
+                               if (rc != LUSTRE_CFG_RC_NO_ERR)
+                                       goto out;
+                       }
+                       config_nids[*count] = libcfs_nid2str(LNET_MKNID(nw,
+                                                                       ip));
+                       (*count)++;
+               }
+       }
+
+out:
+       closedir(srcdir);
+       return *count;
+}
+
+static int lnet_get_config_net_list(char *path, int *ni_count,
+                                   __u32 *config_nets, char **config_nids)
+{
+       struct dirent *dent;
+       DIR *srcdir = opendir(path);
+       char *netpath = NULL;
+       int net_count = 0;
+       int rc = LUSTRE_CFG_RC_BAD_PARAM;
+
+       if (!srcdir)
+               return rc;
+
+       /* loop over all the subdirs(net) under path
+        * and add them to array config_nets[].
+        * Here we have info for all configured nids
+        * as well in config_nids[].
+        */
+       while ((dent = readdir(srcdir)) != NULL) {
+               struct stat st;
+
+               if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..")
+                   == 0)
+                       continue;
+
+               if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0)
+                       continue;
+
+               if (S_ISDIR(st.st_mode)) {
+                       config_nets[net_count] = libcfs_str2net(dent->d_name);
+                       netpath = concat_dir(path, dent->d_name);
+                       rc = lnet_get_config_ni_list(netpath, dent->d_name,
+                                                    config_nids, ni_count);
+                       if (rc < 0) {
+                               free(netpath);
+                               return rc;
+                       }
+                       net_count++;
+               }
+               free(netpath);
+       }
+
+       closedir(srcdir);
+       return net_count;
+}
+
+int lustre_lnet_stats_ni(char **nid, int num_nids, char *nw, int detail,
+                        int seq_no, struct cYAML **show_rc,
+                        struct cYAML **err_rc)
+{
+       struct cYAML *root = NULL, *net_root = NULL, *first_seq = NULL;
+       char err_str[LNET_MAX_STR_LEN];
+       char *mountdir = NULL, *path = NULL;
+       __u32 config_nets[MAX_NUM_IPS];
+       char *config_nids[MAX_NUM_IPS];
+       char *rnids[MAX_NUM_IPS];
+       int net_count = 0, ni_count = 0, rcount = 0;
+       int rc = LUSTRE_CFG_RC_OUT_OF_MEM;
+       int rc2 = LUSTRE_CFG_RC_NO_ERR;
+       int i, j, k;
+       bool found = false;
+
+       snprintf(err_str, sizeof(err_str),
+                "\"out of memory\"");
+
+       /* create struct cYAML root object */
+       root = cYAML_create_object(NULL, NULL);
+       if (!root)
+               goto out;
+
+       net_root = cYAML_create_seq(root, "net");
+       if (!net_root)
+               goto out;
+
+       mountdir = find_mount_point("sysfs");
+       if (!mountdir) {
+               snprintf(err_str, sizeof(err_str),
+                        "\"cannot find mount point for sysfs\"");
+               goto out;
+       }
+
+       path = concat_dir(mountdir, "/fs/lnet/net");
+       if (!path)
+               goto out;
+
+       if (!first_seq)
+               first_seq = net_root;
+
+       if (nw && (libcfs_str2net(nw) == LNET_NIDNET(LNET_NID_ANY))) {
+               snprintf(err_str, sizeof(err_str),
+                        "\"cannot parse net '%s'\"", nw);
+               rc = LUSTRE_CFG_RC_BAD_PARAM;
+               cYAML_build_error(rc, seq_no, "stats", "net", err_str, err_rc);
+               goto out;
+       }
+
+       /* get the list of the configured nets and NIs on the node */
+       rc = lnet_get_config_net_list(path, &ni_count, config_nets,
+                                     config_nids);
+       if (rc < 0) {
+               snprintf(err_str, sizeof(err_str),
+                        "\"could not find the configured sysfs structure\"");
+               goto out;
+       } else if (rc == 0) {
+               snprintf(err_str, sizeof(err_str),
+                        "\" No NIs configured. Nothing to show stats for\"");
+               cYAML_build_error(rc, seq_no, "stats", "net", err_str, err_rc);
+               rc = LUSTRE_CFG_RC_NO_MATCH;
+               goto out;
+       }
+
+       net_count = rc;
+
+       /* if no NID is given to filter stats for */
+       if (nid == NULL) {
+               found = true;
+               rc = traverse_sysfs_net_path(path, nw, NULL, config_nets,
+                                            net_count, net_root, detail,
+                                            err_str);
+               if (rc != LUSTRE_CFG_RC_NO_ERR) {
+                       cYAML_build_error(rc, seq_no, "stats", "net", err_str,
+                                         err_rc);
+                       goto out;
+               }
+               goto success;
+       }
+
+       /* if the stats are to be filtered for the input NIDs */
+       for (i = 0; i < num_nids ; i++) {
+               found = false;
+               rc2 = rc;
+
+               /*
+                * if the input nid is a range then check if
+                * any of the configured NIDs belongs to that range.
+                */
+               if (strchr(nid[i], '[')) {
+                       rcount = check_ip_in_range(nid[i], ni_count,
+                                                  config_nids, rnids, err_str);
+                       if (rcount > 0) {
+                               found = true;
+                               for (j = 0; j < rcount; j++) {
+                                       rc = add_nid_to_tree(path, rnids[j],
+                                                            root, detail,
+                                                            err_str);
+                                       if (rc == LUSTRE_CFG_RC_OUT_OF_MEM)
+                                               goto out;
+                                       else if (rc < 0)
+                                               cYAML_build_error(rc, seq_no,
+                                                                 "stats", "net",
+                                                                 err_str,
+                                                                 err_rc);
+                               }
+                       }
+               } else {
+                       /* if the input nid is not a range then
+                        * check the if the input nid is among the
+                        * list of configured nids.
+                        */
+                       for (k = 0; k < ni_count; k++) {
+                               if (strcmp(nid[i], config_nids[k]) == 0) {
+                                       found = true;
+                                       rc = add_nid_to_tree(path, nid[i],
+                                                            root, detail,
+                                                            err_str);
+                                       if (rc == LUSTRE_CFG_RC_OUT_OF_MEM)
+                                               goto out;
+                                       else if (rc < 0)
+                                               cYAML_build_error(rc, seq_no,
+                                                                 "stats", "net",
+                                                                 err_str,
+                                                                 err_rc);
+                                       break;
+                               }
+                       }
+               }
+
+               if (found == false) {
+                       snprintf(err_str, sizeof(err_str),
+                                "\"NID not configured '%s'\"", nid[i]);
+                       rc = LUSTRE_CFG_RC_BAD_PARAM;
+                       cYAML_build_error(rc, seq_no, "stats", "net", err_str,
+                                         err_rc);
+                       continue;
+               }
+       }
+
+success:
+       if (rc2 == LUSTRE_CFG_RC_NO_ERR && rc == LUSTRE_CFG_RC_NO_ERR)
+               snprintf(err_str, sizeof(err_str), "\"success\"");
+
+out:
+       free(path);
+
+       if (show_rc == NULL || !found ||
+           (rc != LUSTRE_CFG_RC_NO_ERR && rc2 != LUSTRE_CFG_RC_NO_ERR)) {
+               cYAML_free_tree(root);
+       } else if (show_rc != NULL && *show_rc != NULL) {
+               struct cYAML *show_node;
+               /* find the net node, if one doesn't exist then
+                * insert one.  Otherwise add to the one there
+                */
+               show_node = cYAML_get_object_item(*show_rc, "net");
+               if (show_node != NULL && cYAML_is_sequence(show_node)) {
+                       cYAML_insert_child(show_node, first_seq);
+                       free(net_root);
+                       free(root);
+               } else if (show_node == NULL) {
+                       cYAML_insert_sibling((*show_rc)->cy_child, net_root);
+                       free(root);
+               } else {
+                       cYAML_free_tree(root);
+               }
+       } else {
+               *show_rc = root;
+       }
+
+       if (rc != LUSTRE_CFG_RC_NO_ERR)
+               return rc;
+       else if (rc2 != LUSTRE_CFG_RC_NO_ERR)
+               return rc2;
+
+       return rc;
+}
+
 typedef int (*cmd_handler_t)(struct cYAML *tree,
                             struct cYAML **show_rc,
                             struct cYAML **err_rc);
index 8a5e6ac..921e6f7 100644 (file)
@@ -185,6 +185,25 @@ int lustre_lnet_show_net(char *nw, int detail, int seq_no,
                         bool backup);
 
 /*
+ * lustre_lnet_ni_stats
+ *   Show the stats for the NIs specified with nid option.
+ *   If no nids are specified then stats for all the
+ *   configured NIs are shown. The stats can be filtered
+ *   per network by specifying network with option --net.
+ *
+ *   nid - list of nids.
+ *   num_nids - number of nids in the nid array
+ *   nw - network to filter the stats for
+ *   detail - display detailed statistics.
+ *   seq_no - sequence number of the command
+ *   show_rc - YAML structure of the resultant show
+ *   err_rc - YAML structure of the resultant return code.
+ */
+int lustre_lnet_stats_ni(char **nid, int num_nids, char *nw, int detail,
+                        int seq_no, struct cYAML **show_rc,
+                        struct cYAML **err_rc);
+
+/*
  * lustre_lnet_enable_routing
  *   Send down an IOCTL to enable or diable routing
  *
index c503223..34f5638 100644 (file)
@@ -50,6 +50,7 @@ static int jt_show_stats(int argc, char **argv);
 static int jt_show_peer(int argc, char **argv);
 static int jt_show_recovery(int argc, char **argv);
 static int jt_show_global(int argc, char **argv);
+static int jt_stats_net(int argc, char **argv);
 static int jt_set_tiny(int argc, char **argv);
 static int jt_set_small(int argc, char **argv);
 static int jt_set_large(int argc, char **argv);
@@ -147,6 +148,10 @@ command_t net_cmds[] = {
         "\t--net: net name (e.g. tcp0) to filter on\n"
         "\t--verbose: display detailed output per network."
                       " Optional argument of '2' outputs more stats\n"},
+       {"stats", jt_stats_net, 0, "show NI statistics\n"
+        "\t--net: net name (e.g. tcp) to filter the stats for\n"
+        "\t--nid: nids (e.g. 10.211.55.[10-13]@tcp) to filter stats for\n"
+        "\t  show stats for all nids if none of the option is used\n"},
        {"set", jt_set_ni_value, 0, "set local NI specific parameter\n"
         "\t--nid: NI NID to set the\n"
         "\t--health: specify health value to set\n"
@@ -1198,6 +1203,110 @@ static int jt_show_net(int argc, char **argv)
        return rc;
 }
 
+static int jt_stats_net(int argc, char **argv)
+{
+       char **nids = NULL, **nids2 = NULL;
+       char *net_type = NULL;
+       struct cYAML *show_rc = NULL, *err_rc = NULL;
+       long int detail = 0;
+       int size = 0;
+       int rc = LUSTRE_CFG_RC_NO_ERR, opt, i;
+       bool nid_list = false, net_present = false;
+
+       const char *const short_opts = "ht:n:v";
+       const struct option long_opts[] = {
+       { .name = "help",       .has_arg = no_argument,         .val = 'h' },
+       { .name = "net",        .has_arg = required_argument,   .val = 't' },
+       { .name = "nid",        .has_arg = required_argument,   .val = 'n' },
+       { .name = "verbose",    .has_arg = optional_argument,   .val = 'v' },
+       { .name = NULL } };
+
+       rc = check_cmd(stats_cmds, "net", "stats", 0, argc, argv);
+       if (rc)
+               return rc;
+
+       while ((opt = getopt_long(argc, argv, short_opts,
+                                 long_opts, NULL)) != -1) {
+               switch (opt) {
+               case 't':
+                       net_present = true;
+                       if (nid_list) {
+                               cYAML_build_error(-1, -1, "net", "stats",
+                                                 " net type can not be"
+                                                 " specified along side nid.",
+                                                 &err_rc);
+                               rc = LUSTRE_CFG_RC_BAD_PARAM;
+                               goto failed;
+                       }
+                       net_type = optarg;
+                       break;
+               case 'n':
+                       nid_list = true;
+                       if (net_present) {
+                               cYAML_build_error(-1, -1, "net", "stats",
+                                                 " nids can not be specified"
+                                                 " along side net type param.",
+                                                 &err_rc);
+                               rc = LUSTRE_CFG_RC_BAD_PARAM;
+                               goto failed;
+                       }
+                       size = lustre_lnet_parse_nids(optarg, NULL, size,
+                                                     &nids2);
+                       if (nids2 == NULL)
+                               goto failed;
+                       nids = nids2;
+                       rc = LUSTRE_CFG_RC_OUT_OF_MEM;
+                       break;
+               case 'v':
+                       if ((!optarg) && (argv[optind] != NULL) &&
+                           (argv[optind][0] != '-')) {
+                               if (parse_long(argv[optind++], &detail) != 0)
+                                       detail = 1;
+                       } else {
+                               detail = 1;
+                       }
+                       break;
+               case '?':
+                       print_help(net_cmds, "net", "stats");
+               default:
+                       return 0;
+               }
+       }
+
+       if (nid_list) {
+               for (; optind < argc; optind++) {
+                       size = lustre_lnet_parse_nids(argv[optind], nids,
+                                                     size, &nids2);
+                       if (nids2 == NULL)
+                               goto failed;
+                       nids = nids2;
+               }
+       }
+
+       rc = lustre_lnet_stats_ni(nids, size, net_type, (int) detail, -1,
+                                 &show_rc, &err_rc);
+
+       if (show_rc)
+               cYAML_print_tree(show_rc);
+
+       cYAML_free_tree(show_rc);
+
+failed:
+       if (nids) {
+               /* free the array of nids */
+               for (i = 0; i < size; i++)
+                       free(nids[i]);
+               free(nids);
+       }
+
+       if (rc != LUSTRE_CFG_RC_NO_ERR || err_rc)
+               cYAML_print_tree(err_rc);
+
+       cYAML_free_tree(err_rc);
+
+       return rc;
+}
+
 static int jt_show_routing(int argc, char **argv)
 {
        struct cYAML *err_rc = NULL, *show_rc = NULL;