From a6c2d277d09ce9b33d5407dd08da7ffc42066de0 Mon Sep 17 00:00:00 2001 From: James Simmons Date: Wed, 14 Dec 2022 14:31:26 -0500 Subject: [PATCH] LU-8915 lnet: migrate LNet selftest group handling to Netlink Replace the LSTIO_GROUP_LIST and LSTIO_GROUP_INFO ioctls with a Netlink backend. Make this transitition transparent to the user. Be aware this newer version of lnet_selftest.ko doesn't support older versions of the lst tool. While the old interface allows only setting one group up at a time the Netlink interface can be used to setup many groups at one time. Currently we don't change the interface to handle larger NIDs but this new interface will allow us to use the new NID format in a follow on patch. Change-Id: I18f07b380d353425c6e127e4fbd0f30e41f66944 Test-Parameters: trivial testlist=lnet-selftest Signed-off-by: James Simmons Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/49314 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Serguei Smirnov Reviewed-by: Chris Horn Reviewed-by: Oleg Drokin --- lnet/include/uapi/linux/lnet/lnetst.h | 2 + lnet/selftest/conctl.c | 432 +++++++++++++++++++++++++++------- lnet/selftest/console.c | 27 +-- lnet/selftest/console.h | 4 +- lnet/selftest/selftest.h | 44 ++++ lnet/utils/lst.c | 425 ++++++++++++++++++++++++++++++++- 6 files changed, 822 insertions(+), 112 deletions(-) diff --git a/lnet/include/uapi/linux/lnet/lnetst.h b/lnet/include/uapi/linux/lnet/lnetst.h index 36668ae..52d6fe0 100644 --- a/lnet/include/uapi/linux/lnet/lnetst.h +++ b/lnet/include/uapi/linux/lnet/lnetst.h @@ -540,11 +540,13 @@ struct sfw_counters { * * @LNET_SELFTEST_CMD_UNSPEC: unspecified command to catch errors * @LNET_SELFTEST_CMD_SESSIONS: command to manage sessions + * @LNET_SELFTEST_CMD_GROUPS: command to manage selftest groups */ enum lnet_selftest_commands { LNET_SELFTEST_CMD_UNSPEC = 0, LNET_SELFTEST_CMD_SESSIONS = 1, + LNET_SELFTEST_CMD_GROUPS = 2, __LNET_SELFTEST_CMD_MAX_PLUS_ONE, }; diff --git a/lnet/selftest/conctl.c b/lnet/selftest/conctl.c index 185813e..ff13527 100644 --- a/lnet/selftest/conctl.c +++ b/lnet/selftest/conctl.c @@ -35,6 +35,7 @@ * Author: Liang Zhen */ +#include #include #include #include @@ -291,86 +292,6 @@ lst_nodes_add_ioctl(struct lstio_group_nodes_args *args) } static int -lst_group_list_ioctl(struct lstio_group_list_args *args) -{ - if (args->lstio_grp_key != console_session.ses_key) - return -EACCES; - - if (args->lstio_grp_idx < 0 || - args->lstio_grp_namep == NULL || - args->lstio_grp_nmlen <= 0 || - args->lstio_grp_nmlen > LST_NAME_SIZE) - return -EINVAL; - - return lstcon_group_list(args->lstio_grp_idx, - args->lstio_grp_nmlen, - args->lstio_grp_namep); -} - -static int -lst_group_info_ioctl(struct lstio_group_info_args *args) -{ - char *name; - int ndent; - int index; - int rc; - - if (args->lstio_grp_key != console_session.ses_key) - return -EACCES; - - if (args->lstio_grp_namep == NULL || - args->lstio_grp_nmlen <= 0 || - args->lstio_grp_nmlen > LST_NAME_SIZE) - return -EINVAL; - - if (args->lstio_grp_entp == NULL && /* output: group entry */ - args->lstio_grp_dentsp == NULL) /* output: node entry */ - return -EINVAL; - - if (args->lstio_grp_dentsp != NULL) { /* have node entry */ - if (args->lstio_grp_idxp == NULL || /* node index */ - args->lstio_grp_ndentp == NULL) /* # of node entry */ - return -EINVAL; - - if (copy_from_user(&ndent, args->lstio_grp_ndentp, - sizeof(ndent)) || - copy_from_user(&index, args->lstio_grp_idxp, - sizeof(index))) - return -EFAULT; - - if (ndent <= 0 || index < 0) - return -EINVAL; - } - - LIBCFS_ALLOC(name, args->lstio_grp_nmlen + 1); - if (name == NULL) - return -ENOMEM; - - if (copy_from_user(name, args->lstio_grp_namep, - args->lstio_grp_nmlen)) { - LIBCFS_FREE(name, args->lstio_grp_nmlen + 1); - return -EFAULT; - } - - name[args->lstio_grp_nmlen] = 0; - - rc = lstcon_group_info(name, args->lstio_grp_entp, - &index, &ndent, args->lstio_grp_dentsp); - - LIBCFS_FREE(name, args->lstio_grp_nmlen + 1); - - if (rc != 0) - return rc; - - if (args->lstio_grp_dentsp != NULL && - (copy_to_user(args->lstio_grp_idxp, &index, sizeof(index)) || - copy_to_user(args->lstio_grp_ndentp, &ndent, sizeof(ndent)))) - return -EFAULT; - - return 0; -} - -static int lst_batch_add_ioctl(struct lstio_batch_add_args *args) { int rc; @@ -815,10 +736,9 @@ lstcon_ioctl_entry(struct notifier_block *nb, rc = lst_nodes_add_ioctl((struct lstio_group_nodes_args *)buf); break; case LSTIO_GROUP_LIST: - rc = lst_group_list_ioctl((struct lstio_group_list_args *)buf); - break; + fallthrough; case LSTIO_GROUP_INFO: - rc = lst_group_info_ioctl((struct lstio_group_info_args *)buf); + rc = -EOPNOTSUPP; break; case LSTIO_BATCH_ADD: rc = lst_batch_add_ioctl((struct lstio_batch_add_args *)buf); @@ -1101,8 +1021,344 @@ out_unlock: return rc; } +static char *lst_node_state2str(int state) +{ + if (state == LST_NODE_ACTIVE) + return "Active"; + if (state == LST_NODE_BUSY) + return "Busy"; + if (state == LST_NODE_DOWN) + return "Down"; + + return "Unknown"; +} + +int lst_node_str2state(char *str) +{ + int state = 0; + + if (strcasecmp(str, "Active") == 0) + state = LST_NODE_ACTIVE; + else if (strcasecmp(str, "Busy") == 0) + state = LST_NODE_BUSY; + else if (strcasecmp(str, "Down") == 0) + state = LST_NODE_DOWN; + else if (strcasecmp(str, "Unknown") == 0) + state = LST_NODE_UNKNOWN; + else if (strcasecmp(str, "Invalid") == 0) + state = LST_NODE_UNKNOWN | LST_NODE_DOWN | LST_NODE_BUSY; + return state; +} + +struct lst_genl_group_prop { + struct lstcon_group *lggp_grp; + int lggp_state_filter; +}; + +struct lst_genl_group_list { + GENRADIX(struct lst_genl_group_prop) lggl_groups; + unsigned int lggl_count; + unsigned int lggl_index; + bool lggl_verbose; +}; + +static inline struct lst_genl_group_list * +lst_group_dump_ctx(struct netlink_callback *cb) +{ + return (struct lst_genl_group_list *)cb->args[0]; +} + +static int lst_groups_show_done(struct netlink_callback *cb) +{ + struct lst_genl_group_list *glist = lst_group_dump_ctx(cb); + + if (glist) { + int i; + + for (i = 0; i < glist->lggl_count; i++) { + struct lst_genl_group_prop *prop; + + prop = genradix_ptr(&glist->lggl_groups, i); + if (!prop || !prop->lggp_grp) + continue; + lstcon_group_decref(prop->lggp_grp); + } + genradix_free(&glist->lggl_groups); + LIBCFS_FREE(glist, sizeof(*glist)); + } + cb->args[0] = 0; + + return 0; +} + +/* LNet selftest groups ->start() handler for GET requests */ +static int lst_groups_show_start(struct netlink_callback *cb) +{ + struct genlmsghdr *gnlh = nlmsg_data(cb->nlh); +#ifdef HAVE_NL_PARSE_WITH_EXT_ACK + struct netlink_ext_ack *extack = NULL; +#endif + struct nlattr *params = genlmsg_data(gnlh); + struct lst_genl_group_list *glist; + int msg_len = genlmsg_len(gnlh); + struct lstcon_group *grp; + struct nlattr *groups; + int rem, rc = 0; + + LIBCFS_ALLOC(glist, sizeof(*glist)); + if (!glist) + return -ENOMEM; + + genradix_init(&glist->lggl_groups); + cb->args[0] = (long)glist; + + if (!msg_len) { + list_for_each_entry(grp, &console_session.ses_grp_list, + grp_link) { + struct lst_genl_group_prop *prop; + + prop = genradix_ptr_alloc(&glist->lggl_groups, + glist->lggl_count++, + GFP_ATOMIC); + if (!prop) { + NL_SET_ERR_MSG(extack, + "failed to allocate group info"); + GOTO(report_err, rc = -ENOMEM); + } + lstcon_group_addref(grp); /* +1 ref for caller */ + prop->lggp_grp = grp; + } + + if (!glist->lggl_count) { + NL_SET_ERR_MSG(extack, "No groups found"); + rc = -ENOENT; + } + GOTO(report_err, rc); + } + glist->lggl_verbose = true; +#ifdef HAVE_NL_DUMP_WITH_EXT_ACK + extack = cb->extack; +#endif + nla_for_each_attr(groups, params, msg_len, rem) { + struct lst_genl_group_prop *prop = NULL; + struct nlattr *group; + int rem2; + + if (nla_type(groups) != LN_SCALAR_ATTR_LIST) + continue; + + nla_for_each_nested(group, groups, rem2) { + if (nla_type(group) == LN_SCALAR_ATTR_VALUE) { + char name[LST_NAME_SIZE]; + + prop = genradix_ptr_alloc(&glist->lggl_groups, + glist->lggl_count++, + GFP_ATOMIC); + if (!prop) { + NL_SET_ERR_MSG(extack, + "failed to allocate group info"); + GOTO(report_err, rc = -ENOMEM); + } + + rc = nla_strscpy(name, group, sizeof(name)); + if (rc < 0) { + NL_SET_ERR_MSG(extack, + "failed to get name"); + GOTO(report_err, rc); + } + rc = lstcon_group_find(name, &prop->lggp_grp); + if (rc < 0) { + /* don't stop reporting groups if one + * doesn't exist. + */ + CWARN("LNet selftest group %s does not exit\n", + name); + rc = 0; + } + } else if (nla_type(group) == LN_SCALAR_ATTR_LIST) { + struct nlattr *attr; + int rem3; + + if (!prop) { + NL_SET_ERR_MSG(extack, + "missing group information"); + GOTO(report_err, rc = -EINVAL); + } + + nla_for_each_nested(attr, group, rem3) { + char tmp[16]; + + if (nla_type(attr) != LN_SCALAR_ATTR_VALUE || + nla_strcmp(attr, "status") != 0) + continue; + + attr = nla_next(attr, &rem3); + if (nla_type(attr) != + LN_SCALAR_ATTR_VALUE) { + NL_SET_ERR_MSG(extack, + "invalid config param"); + GOTO(report_err, rc = -EINVAL); + } + + rc = nla_strscpy(tmp, attr, sizeof(tmp)); + if (rc < 0) { + NL_SET_ERR_MSG(extack, + "failed to get prop attr"); + GOTO(report_err, rc); + } + rc = 0; + prop->lggp_state_filter |= + lst_node_str2state(tmp); + } + } + } + } + if (!glist->lggl_count) { + NL_SET_ERR_MSG(extack, "No groups found"); + rc = -ENOENT; + } +report_err: + if (rc < 0) + lst_groups_show_done(cb); + + return rc; +} + +static const struct ln_key_list lst_group_keys = { + .lkl_maxattr = LNET_SELFTEST_GROUP_MAX, + .lkl_list = { + [LNET_SELFTEST_GROUP_ATTR_HDR] = { + .lkp_value = "groups", + .lkp_key_format = LNKF_SEQUENCE, + .lkp_data_type = NLA_NUL_STRING, + }, + [LNET_SELFTEST_GROUP_ATTR_NAME] = { + .lkp_data_type = NLA_STRING, + }, + [LNET_SELFTEST_GROUP_ATTR_NODELIST] = { + .lkp_key_format = LNKF_MAPPING | LNKF_SEQUENCE, + .lkp_data_type = NLA_NESTED, + }, + }, +}; + +static const struct ln_key_list lst_group_nodelist_keys = { + .lkl_maxattr = LNET_SELFTEST_GROUP_NODELIST_PROP_MAX, + .lkl_list = { + [LNET_SELFTEST_GROUP_NODELIST_PROP_ATTR_NID] = { + .lkp_value = "nid", + .lkp_data_type = NLA_STRING, + }, + [LNET_SELFTEST_GROUP_NODELIST_PROP_ATTR_STATUS] = { + .lkp_value = "status", + .lkp_data_type = NLA_STRING, + }, + }, +}; + +static int lst_groups_show_dump(struct sk_buff *msg, + struct netlink_callback *cb) +{ + struct lst_genl_group_list *glist = lst_group_dump_ctx(cb); +#ifdef HAVE_NL_PARSE_WITH_EXT_ACK + struct netlink_ext_ack *extack = NULL; +#endif + int portid = NETLINK_CB(cb->skb).portid; + int seq = cb->nlh->nlmsg_seq; + int idx = 0, rc = 0; + +#ifdef HAVE_NL_DUMP_WITH_EXT_ACK + extack = cb->extack; +#endif + if (!glist->lggl_index) { + const struct ln_key_list *all[] = { + &lst_group_keys, &lst_group_nodelist_keys, NULL + }; + + rc = lnet_genl_send_scalar_list(msg, portid, seq, &lst_family, + NLM_F_CREATE | NLM_F_MULTI, + LNET_SELFTEST_CMD_GROUPS, all); + if (rc < 0) { + NL_SET_ERR_MSG(extack, "failed to send key table"); + GOTO(send_error, rc); + } + } + + for (idx = glist->lggl_index; idx < glist->lggl_count; idx++) { + struct lst_genl_group_prop *group; + struct lstcon_ndlink *ndl; + struct nlattr *nodelist; + unsigned int count = 1; + void *hdr; + + group = genradix_ptr(&glist->lggl_groups, idx); + if (!group) + continue; + + hdr = genlmsg_put(msg, portid, seq, &lst_family, + NLM_F_MULTI, LNET_SELFTEST_CMD_GROUPS); + if (!hdr) { + NL_SET_ERR_MSG(extack, "failed to send values"); + GOTO(send_error, rc = -EMSGSIZE); + } + + if (idx == 0) + nla_put_string(msg, LNET_SELFTEST_GROUP_ATTR_HDR, ""); + + nla_put_string(msg, LNET_SELFTEST_GROUP_ATTR_NAME, + group->lggp_grp->grp_name); + + if (!glist->lggl_verbose) + goto skip_details; + + nodelist = nla_nest_start(msg, + LNET_SELFTEST_GROUP_ATTR_NODELIST); + list_for_each_entry(ndl, &group->lggp_grp->grp_ndl_list, + ndl_link) { + struct nlattr *node = nla_nest_start(msg, count); + char *ndstate; + + if (group->lggp_state_filter && + !(group->lggp_state_filter & ndl->ndl_node->nd_state)) + continue; + + nla_put_string(msg, + LNET_SELFTEST_GROUP_NODELIST_PROP_ATTR_NID, + libcfs_id2str(ndl->ndl_node->nd_id)); + + ndstate = lst_node_state2str(ndl->ndl_node->nd_state); + nla_put_string(msg, + LNET_SELFTEST_GROUP_NODELIST_PROP_ATTR_STATUS, + ndstate); + nla_nest_end(msg, node); + } + nla_nest_end(msg, nodelist); +skip_details: + genlmsg_end(msg, hdr); + } + glist->lggl_index = idx; +send_error: + return lnet_nl_send_error(cb->skb, portid, seq, rc); +} + +#ifndef HAVE_NETLINK_CALLBACK_START +static int lst_old_groups_show_dump(struct sk_buff *msg, + struct netlink_callback *cb) +{ + if (!cb->args[0]) { + int rc = lst_groups_show_start(cb); + + if (rc < 0) + return rc; + } + + return lst_groups_show_dump(msg, cb); +} +#endif + static const struct genl_multicast_group lst_mcast_grps[] = { { .name = "sessions", }, + { .name = "groups", }, }; static const struct genl_ops lst_genl_ops[] = { @@ -1111,6 +1367,16 @@ static const struct genl_ops lst_genl_ops[] = { .dumpit = lst_sessions_show_dump, .doit = lst_sessions_cmd, }, + { + .cmd = LNET_SELFTEST_CMD_GROUPS, +#ifdef HAVE_NETLINK_CALLBACK_START + .start = lst_groups_show_start, + .dumpit = lst_groups_show_dump, +#else + .dumpit = lst_old_groups_show_dump, +#endif + .done = lst_groups_show_done, + }, }; static struct genl_family lst_family = { diff --git a/lnet/selftest/console.c b/lnet/selftest/console.c index 1251960..ac26ad2 100644 --- a/lnet/selftest/console.c +++ b/lnet/selftest/console.c @@ -222,8 +222,7 @@ lstcon_group_alloc(char *name, struct lstcon_group **grpp) return 0; } -static void -lstcon_group_addref(struct lstcon_group *grp) +void lstcon_group_addref(struct lstcon_group *grp) { grp->grp_ref++; } @@ -243,8 +242,7 @@ lstcon_group_drain(struct lstcon_group *grp, int keep) } } -static void -lstcon_group_decref(struct lstcon_group *grp) +void lstcon_group_decref(struct lstcon_group *grp) { int i; @@ -263,8 +261,7 @@ lstcon_group_decref(struct lstcon_group *grp) grp_ndl_hash[LST_NODE_HASHSIZE])); } -static int -lstcon_group_find(const char *name, struct lstcon_group **grpp) +int lstcon_group_find(const char *name, struct lstcon_group **grpp) { struct lstcon_group *grp; @@ -715,24 +712,6 @@ lstcon_group_refresh(char *name, struct list_head __user *result_up) return rc; } -int -lstcon_group_list(int index, int len, char __user *name_up) -{ - struct lstcon_group *grp; - - LASSERT(index >= 0); - LASSERT(name_up != NULL); - - list_for_each_entry(grp, &console_session.ses_grp_list, grp_link) { - if (index-- == 0) { - return copy_to_user(name_up, grp->grp_name, len) ? - -EFAULT : 0; - } - } - - return -ENOENT; -} - static int lstcon_nodes_getent(struct list_head *head, int *index_p, int *count_p, struct lstcon_node_ent __user *dents_up) diff --git a/lnet/selftest/console.h b/lnet/selftest/console.h index fce9f19..dd01b94 100644 --- a/lnet/selftest/console.h +++ b/lnet/selftest/console.h @@ -212,6 +212,9 @@ extern int lstcon_nodes_debug(int timeout, int nnd, struct list_head __user *result_up); extern int lstcon_group_add(char *name); extern int lstcon_group_del(char *name); +void lstcon_group_addref(struct lstcon_group *grp); +void lstcon_group_decref(struct lstcon_group *grp); +int lstcon_group_find(const char *name, struct lstcon_group **grpp); extern int lstcon_group_clean(char *name, int args); extern int lstcon_group_refresh(char *name, struct list_head __user *result_up); extern int lstcon_nodes_add(char *name, int nnd, @@ -224,7 +227,6 @@ extern int lstcon_nodes_remove(char *name, int nnd, extern int lstcon_group_info(char *name, struct lstcon_ndlist_ent __user *gent_up, int *index_p, int *ndent_p, struct lstcon_node_ent __user *ndents_up); -extern int lstcon_group_list(int idx, int len, char __user *name_up); extern int lstcon_batch_add(char *name); extern int lstcon_batch_run(char *name, int timeout, struct list_head __user *result_up); diff --git a/lnet/selftest/selftest.h b/lnet/selftest/selftest.h index 94c4afe..0b609ad 100644 --- a/lnet/selftest/selftest.h +++ b/lnet/selftest/selftest.h @@ -83,6 +83,50 @@ enum lnet_selftest_session_attrs { #define LNET_SELFTEST_SESSION_MAX (__LNET_SELFTEST_SESSION_MAX_PLUS_ONE - 1) +/* enum lnet_selftest_group_attrs - LNet selftest group Netlink attributes + * + * @LNET_SELFTEST_GROUP_ATTR_UNSPEC: unspecified attribute to catch errors + * + * @LENT_SELFTEST_GROUP_ATTR_HDR: Netlink group this data is for + * (NLA_NUL_STRING) + * @LNET_SELFTEST_GROUP_ATTR_NAME: name of this group (NLA_STRING) + * @LNET_SELFTEST_GROUP_ATTR_NODELIST: List of nodes belonging to the group + * (NLA_NESTED) + */ +enum lnet_selftest_group_attrs { + LNET_SELFTEST_GROUP_ATTR_UNSPEC = 0, + + LNET_SELFTEST_GROUP_ATTR_HDR, + LNET_SELFTEST_GROUP_ATTR_NAME, + LNET_SELFTEST_GROUP_ATTR_NODELIST, + + __LNET_SELFTEST_GROUP_MAX_PLUS_ONE, +}; + +#define LNET_SELFTEST_GROUP_MAX (__LNET_SELFTEST_GROUP_MAX_PLUS_ONE - 1) + +/* enum lnet_selftest_group_nodelist_prop_attrs - Netlink attributes for + * the properties of the + * nodes that belong to a + * group + * + * @LNET_SELFTEST_GROUP_NODELIST_PROP_ATTR_UNSPEC: unspecified attribute + * to catch errors + * + * @LENT_SELFTEST_GROUP_NODELIST_PROP_ATTR_NID: Nodes's NID (NLA_STRING) + * @LNET_SELFTEST_GROUP_NODELIST_PROP_ATTR_STATUS: Status of the node + * (NLA_STRING) + */ +enum lnet_selftest_group_nodelist_prop_attrs { + LNET_SELFTEST_GROUP_NODELIST_PROP_ATTR_UNSPEC = 0, + + LNET_SELFTEST_GROUP_NODELIST_PROP_ATTR_NID, + LNET_SELFTEST_GROUP_NODELIST_PROP_ATTR_STATUS, + __LNET_SELFTEST_GROUP_NODELIST_PROP_MAX_PLUS_ONE, +}; + +#define LNET_SELFTEST_GROUP_NODELIST_PROP_MAX (__LNET_SELFTEST_GROUP_NODELIST_PROP_MAX_PLUS_ONE - 1) + #define SWI_STATE_NEWBORN 0 #define SWI_STATE_REPLY_SUBMITTED 1 #define SWI_STATE_REPLY_SENT 2 diff --git a/lnet/utils/lst.c b/lnet/utils/lst.c index efdbb50..3ce64ac 100644 --- a/lnet/utils/lst.c +++ b/lnet/utils/lst.c @@ -986,6 +986,393 @@ finish: return rc; } +#define LST_NODES_TITLE "\tACTIVE\tBUSY\tDOWN\tUNKNOWN\tTOTAL\n" + +static int lst_yaml_display_groups(yaml_parser_t *reply, char *group, + int states, bool print) +{ + yaml_event_t event; + int rc; + + if (states) { + char name[LST_NAME_SIZE] = {}; + bool done = false; + int count = 0; + + if (print) + fprintf(stdout, LST_NODES_TITLE); + + while (!done) { + rc = yaml_parser_parse(reply, &event); + if (rc == 0) + goto parser_error; + + if (event.type == YAML_MAPPING_START_EVENT) { + char *value = NULL; + yaml_event_t next; + + rc = yaml_parser_parse(reply, &next); + if (rc == 0) + goto parser_error; + + if (next.type != YAML_SCALAR_EVENT) { + yaml_event_delete(&next); + continue; + } + + value = (char *)next.data.scalar.value; + if (strcmp(value, "groups") == 0) { + yaml_event_delete(&next); + } else if (strcmp(value, "nid") == 0) { + yaml_event_t next; + char *tmp; + + rc = yaml_parser_parse(reply, &next); + if (rc == 0) + goto parser_error; + + fprintf(stdout, "\t%s: ", + (char *)next.data.scalar.value); + + rc = yaml_parser_parse(reply, &next); + if (rc == 0) + goto parser_error; + + tmp = (char *)next.data.scalar.value; + if (strcmp(tmp, "status") == 0) { + yaml_event_t state; + + rc = yaml_parser_parse(reply, &state); + if (rc == 0) + goto parser_error; + + fprintf(stdout, "%s\n", + (char *)state.data.scalar.value); + count++; + } + } else { + yaml_event_t next; + + rc = yaml_parser_parse(reply, &next); + if (rc == 0) + goto parser_error; + + strncpy(name, value, sizeof(name) - 1); + fprintf(stdout, "Group [ %s ]\n", name); + count = 0; + + if (next.type != YAML_SEQUENCE_START_EVENT) { + fprintf(stdout, + "No nodes found [ %s ]\n", + name); + } + } + } else if (event.type == YAML_SEQUENCE_END_EVENT && + count) { + fprintf(stdout, "Total %d nodes [ %s ]\n", + count, name); + count = 0; + } + + done = (event.type == YAML_STREAM_END_EVENT); + yaml_event_delete(&event); + } + } else if (group) { + int active = 0, busy = 0, down = 0, unknown = 0; + char group[LST_NAME_SIZE]; + bool done = false; + + while (!done) { + rc = yaml_parser_parse(reply, &event); + if (rc == 0) + goto parser_error; + + if (event.type == YAML_SCALAR_EVENT) { + char *value = (char *)event.data.scalar.value; + yaml_event_t next; + + value = (char *)event.data.scalar.value; + if (strcmp(value, "groups") == 0) { + yaml_event_delete(&event); + continue; + } + + rc = yaml_parser_parse(reply, &next); + if (rc == 0) + goto parser_error; + + if (next.type == YAML_SCALAR_EVENT) { + int status; + + status = lst_node_str2state((char *)next.data.scalar.value); + switch (status) { + case LST_NODE_ACTIVE: + active++; + break; + case LST_NODE_BUSY: + busy++; + break; + case LST_NODE_DOWN: + down++; + break; + case LST_NODE_UNKNOWN: + unknown++; + default: + break; + } + } else if (next.type == YAML_SEQUENCE_START_EVENT) { + strncpy(group, value, sizeof(group) - 1); + active = 0; + busy = 0; + down = 0; + unknown = 0; + } + yaml_event_delete(&next); + } else if (event.type == YAML_SEQUENCE_END_EVENT) { + if (strlen(group)) { + fprintf(stdout, "\t%d\t%d\t%d\t%d\t%d\t%s\n", + active, busy, down, unknown, + active + busy + down + unknown, group); + } + memset(group, 0, sizeof(group)); + } + done = (event.type == YAML_STREAM_END_EVENT); + yaml_event_delete(&event); + } + } else { + bool done = false; + unsigned int i = 1; + + while (!done) { + rc = yaml_parser_parse(reply, &event); + if (rc == 0) + goto parser_error; + + if (event.type == YAML_SCALAR_EVENT) { + char *value; + + value = (char *)event.data.scalar.value; + if (strlen(value) && + strcmp(value, "groups") != 0) { + if (print) + fprintf(stdout, "%d) %s\n", + i, value); + i++; + } + } + done = (event.type == YAML_STREAM_END_EVENT); + + yaml_event_delete(&event); + } + if (print) + fprintf(stdout, "Total %d groups\n", i - 1); + else + rc = i - 1; + } +parser_error: + return rc; +} + +#define LST_NODES_TITLE "\tACTIVE\tBUSY\tDOWN\tUNKNOWN\tTOTAL\n" + +static int lst_yaml_groups(int nlflags, char *name, int states, bool print) +{ + yaml_emitter_t request; + yaml_parser_t reply; + yaml_event_t event; + struct nl_sock *sk; + int rc; + + sk = nl_socket_alloc(); + if (!sk) + return -EOPNOTSUPP; + + /* Setup reply parser to recieve Netlink packets */ + rc = yaml_parser_initialize(&reply); + if (rc == 0) { + nl_socket_free(sk); + return -EOPNOTSUPP; + } + + rc = yaml_parser_set_input_netlink(&reply, sk, false); + if (rc == 0) + goto parser_error; + + /* Create Netlink emitter to send request to kernel */ + yaml_emitter_initialize(&request); + rc = yaml_emitter_set_output_netlink(&request, sk, + LNET_SELFTEST_GENL_NAME, + LNET_SELFTEST_GENL_VERSION, + LNET_SELFTEST_CMD_GROUPS, + nlflags); + if (rc == 0) + goto emitter_error; + + yaml_emitter_open(&request); + yaml_document_start_event_initialize(&event, NULL, NULL, NULL, 0); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + + yaml_mapping_start_event_initialize(&event, NULL, + (yaml_char_t *)YAML_MAP_TAG, + 1, YAML_BLOCK_MAPPING_STYLE); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + + yaml_scalar_event_initialize(&event, NULL, + (yaml_char_t *)YAML_STR_TAG, + (yaml_char_t *)"groups", + strlen("groups"), 1, 0, + YAML_PLAIN_SCALAR_STYLE); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + + if (name) { + yaml_sequence_start_event_initialize(&event, NULL, + (yaml_char_t *)YAML_SEQ_TAG, + 1, YAML_BLOCK_SEQUENCE_STYLE); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + + yaml_mapping_start_event_initialize(&event, NULL, + (yaml_char_t *)YAML_MAP_TAG, + 1, YAML_BLOCK_MAPPING_STYLE); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + + yaml_scalar_event_initialize(&event, NULL, + (yaml_char_t *)YAML_STR_TAG, + (yaml_char_t *)name, + strlen(name), 1, 0, + YAML_PLAIN_SCALAR_STYLE); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + + if (states) { + int max = ffs(LST_NODE_UNKNOWN) + 1, i; + char *state; + + yaml_sequence_start_event_initialize(&event, NULL, + (yaml_char_t *)YAML_SEQ_TAG, + 1, YAML_BLOCK_SEQUENCE_STYLE); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + + + for (i = 0; i < max; i++) { + int mask = states & (1 << i); + + state = lst_node_state2str(mask); + if (mask != LST_NODE_UNKNOWN && strcmp(state, "Unknown") == 0) + continue; + + yaml_mapping_start_event_initialize(&event, NULL, + (yaml_char_t *)YAML_MAP_TAG, + 1, YAML_BLOCK_MAPPING_STYLE); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + + yaml_scalar_event_initialize(&event, NULL, + (yaml_char_t *)YAML_STR_TAG, + (yaml_char_t *)"status", + strlen("status"), 1, 0, + YAML_PLAIN_SCALAR_STYLE); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + + yaml_scalar_event_initialize(&event, NULL, + (yaml_char_t *)YAML_STR_TAG, + (yaml_char_t *)state, + strlen(state), 1, 0, + YAML_PLAIN_SCALAR_STYLE); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + + yaml_mapping_end_event_initialize(&event); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + } + + yaml_sequence_end_event_initialize(&event); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + } else { + yaml_scalar_event_initialize(&event, NULL, + (yaml_char_t *)YAML_STR_TAG, + (yaml_char_t *)"", + strlen(""), 1, 0, + YAML_PLAIN_SCALAR_STYLE); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + } + + yaml_mapping_end_event_initialize(&event); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + + yaml_sequence_end_event_initialize(&event); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + } else { + yaml_scalar_event_initialize(&event, NULL, + (yaml_char_t *)YAML_STR_TAG, + (yaml_char_t *)"", + strlen(""), 1, 0, + YAML_PLAIN_SCALAR_STYLE); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + } + yaml_mapping_end_event_initialize(&event); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + + yaml_document_end_event_initialize(&event, 0); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + + rc = yaml_emitter_close(&request); + if (rc == 0) { +emitter_error: + yaml_emitter_log_error(&request, stderr); + yaml_emitter_delete(&request); + rc = -EINVAL; + goto parser_error; + } + yaml_emitter_delete(&request); + + /* display output */ + if (nlflags == NLM_F_DUMP) + rc = lst_yaml_display_groups(&reply, name, states, print); +parser_error: + if (rc == 0) + yaml_parser_log_error(&reply, stderr, NULL); + yaml_parser_delete(&reply); + nl_socket_free(sk); + + if (print) + rc = rc == 1 ? 0 : -EINVAL; + + return rc; +} + int lst_ping_ioctl(char *str, int type, int timeout, int count, struct lnet_process_id *ids, struct list_head *head) @@ -1030,7 +1417,13 @@ lst_get_node_count(int type, char *str, int *countp, break; case LST_OPC_GROUP: - rc = lst_info_group_ioctl(str, entp, NULL, NULL, NULL); + rc = lst_yaml_groups(NLM_F_DUMP, NULL, 0, false); + if (rc == -EOPNOTSUPP) { + rc = lst_info_group_ioctl(str, entp, NULL, NULL, NULL); + } else if (rc > 0) { + entp->nle_nnode = rc; + rc = 0; + } break; case LST_OPC_NODES: @@ -1609,13 +2002,12 @@ lst_list_group_all(void) return 0; } -#define LST_NODES_TITLE "\tACTIVE\tBUSY\tDOWN\tUNKNOWN\tTOTAL\n" - int jt_lst_list_group(int argc, char **argv) { struct lstcon_ndlist_ent gent; struct lstcon_node_ent *dents; + int states = 0; int optidx = 0; int verbose = 0; int active = 0; @@ -1629,7 +2021,6 @@ jt_lst_list_group(int argc, char **argv) int j; int c; int rc = 0; - static const struct option list_group_opts[] = { { .name = "active", .has_arg = no_argument, .val = 'a' }, { .name = "busy", .has_arg = no_argument, .val = 'b' }, @@ -1654,21 +2045,27 @@ jt_lst_list_group(int argc, char **argv) switch (c) { case 'a': verbose = active = 1; + states |= LST_NODE_ACTIVE; all = 0; break; case 'b': verbose = busy = 1; + states |= LST_NODE_BUSY; all = 0; break; case 'd': verbose = down = 1; + states |= LST_NODE_DOWN; all = 0; break; case 'u': verbose = unknown = 1; + states |= LST_NODE_UNKNOWN; all = 0; break; case 'l': + states |= LST_NODE_ACTIVE | LST_NODE_BUSY | + LST_NODE_DOWN | LST_NODE_UNKNOWN; verbose = all = 1; break; default: @@ -1677,6 +2074,26 @@ jt_lst_list_group(int argc, char **argv) } } + if (optind == argc) { + rc = lst_yaml_groups(NLM_F_DUMP, NULL, 0, true); + if (rc <= 0) { + if (rc == -EOPNOTSUPP) + goto old_api; + return rc; + } + } else { + for (i = optind; i < argc; i++) { + rc = lst_yaml_groups(NLM_F_DUMP, argv[i], states, + i == optind ? true : false); + if (rc < 0) { + if (rc == -EOPNOTSUPP) + goto old_api; + return rc; + } + } + return 0; + } +old_api: if (optind == argc) { /* no group is specified, list name of all groups */ rc = lst_list_group_all(); -- 1.8.3.1