From 466e25a6a35f140a34d14ddc75076872e2b35a0c Mon Sep 17 00:00:00 2001 From: Chris Horn Date: Tue, 29 Aug 2023 10:46:13 -0600 Subject: [PATCH] LU-17054 lnet: Change cpt-of-nid to get result from kernel The lnetctl cpt-of-nid command leverages a userspace implementation of the kernel hash_long() function to compute the CPT for a given NID. However, the kernel hash_long() function has changed over time such that the userspace version may give a different result than the kernel version. Since Lustre supports such a wide range of kernels we cannot simply update the userspace implementation of hash_long() to match newer kernel. Address this by re-implementing lnetctl cpt-of-nid to call into kernel space to compute the CPT and return the result to userspace. lnetctl cpt-of-nid now works with extended NIDs (e.g., IPv6). lnetctl cpt-of-nid no longer accepts the --ncpt argument because the kernel functions for computing the cpt do not support this. lnetctl cpt-of-nid no longer accepts the --nid argument. Instead, the command now takes a space separated list of nids. Example: $ lnetctl cpt-of-nid 867@kfi 5.3.0.9@tcp cpt-of-nid: - nid: 867@kfi cpt: 0 - nid: 5.3.0.9@tcp cpt: 1 $ Because the old implementation could return a wrong result it is completely removed. HPE-bug-id: LUS-11785 Test-Parameters: trivial Signed-off-by: Chris Horn Change-Id: I7c2bc48c5c0da7da8a4425d319c0b99207814ae1 Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/52502 Tested-by: jenkins Tested-by: Maloo Reviewed-by: James Simmons Reviewed-by: Serguei Smirnov Reviewed-by: Oleg Drokin --- lnet/include/lnet/lib-types.h | 23 +++ lnet/include/uapi/linux/lnet/lnet-dlc.h | 2 + lnet/lnet/api-ni.c | 249 ++++++++++++++++++++++++++++++++ lnet/utils/lnetconfig/liblnetconfig.c | 66 --------- lnet/utils/lnetconfig/liblnetconfig.h | 6 - lnet/utils/lnetctl.c | 249 +++++++++++++++++++++++++------- 6 files changed, 471 insertions(+), 124 deletions(-) diff --git a/lnet/include/lnet/lib-types.h b/lnet/include/lnet/lib-types.h index d68ee29..6083ecb 100644 --- a/lnet/include/lnet/lib-types.h +++ b/lnet/include/lnet/lib-types.h @@ -1049,6 +1049,29 @@ enum lnet_udsp_info_pref_nids_attr { #define LNET_UDSP_INFO_PREF_NIDS_ATTR_MAX (__LNET_UDSP_INFO_PREF_NIDS_ATTR_MAX_PLUS_ONE - 1) +/** enum lnet_cpt_of_nid_attr - Attributes to support + * lnetctl cpt-of-nid command + * + * @LNET_CPT_OF_NID_ATTR_UNSPEC unspecified attribute to catch + * errors + * @LNET_CPT_OF_NID_ATTR_HDR Grouping for cpt-of-nid + * (NLA_NUL_STRING) + * @LNET_CPT_OF_NID_ATTR_NID The NID whose CPT we want to + * calculate (NLA_STRING) + * LNET_CPT_OF_NID_ATTR_CPT The CPT for the specified NID + * (NLA_U32) + */ +enum lnet_cpt_of_nid_attr { + LNET_CPT_OF_NID_ATTR_UNSPEC = 0, + + LNET_CPT_OF_NID_ATTR_HDR, + LNET_CPT_OF_NID_ATTR_NID, + LNET_CPT_OF_NID_ATTR_CPT, + __LNET_CPT_OF_NID_ATTR_MAX_PLUS_ONE, +}; + +#define LNET_CPT_OF_NID_ATTR_MAX (__LNET_CPT_OF_NID_ATTR_MAX_PLUS_ONE - 1) + struct lnet_ni { /* chain on the lnet_net structure */ struct list_head ni_netlist; diff --git a/lnet/include/uapi/linux/lnet/lnet-dlc.h b/lnet/include/uapi/linux/lnet/lnet-dlc.h index 47ac566..aefcc88 100644 --- a/lnet/include/uapi/linux/lnet/lnet-dlc.h +++ b/lnet/include/uapi/linux/lnet/lnet-dlc.h @@ -59,6 +59,7 @@ * @LNET_CMD_NETS: command to manage the LNet networks * @LNET_CMD_ROUTES: command to manage LNet routes * @LNET_CMD_PING: command to send pings to LNet connections + * @LNET_CMD_CPT_OF_NID: command to calculate the CPT of specified NIDs */ enum lnet_commands { LNET_CMD_UNSPEC = 0, @@ -69,6 +70,7 @@ enum lnet_commands { LNET_CMD_ROUTES = 4, LNET_CMD_CONNS = 5, LNET_CMD_PING = 6, + LNET_CMD_CPT_OF_NID = 7, __LNET_CMD_MAX_PLUS_ONE }; diff --git a/lnet/lnet/api-ni.c b/lnet/lnet/api-ni.c index 18159a3..d1b98df 100644 --- a/lnet/lnet/api-ni.c +++ b/lnet/lnet/api-ni.c @@ -4785,6 +4785,244 @@ report_discover_err: } EXPORT_SYMBOL(LNetCtl); +struct lnet_nid_cpt { + struct lnet_nid lnc_nid; + unsigned int lnc_cpt; +}; + +struct lnet_genl_nid_cpt_list { + unsigned int lgncl_index; + unsigned int lgncl_list_count; + GENRADIX(struct lnet_nid_cpt) lgncl_lnc_list; +}; + +static inline struct lnet_genl_nid_cpt_list * +lnet_cpt_of_nid_dump_ctx(struct netlink_callback *cb) +{ + return (struct lnet_genl_nid_cpt_list *)cb->args[0]; +} + +static int lnet_cpt_of_nid_show_done(struct netlink_callback *cb) +{ + struct lnet_genl_nid_cpt_list *lgncl; + + lgncl = lnet_cpt_of_nid_dump_ctx(cb); + + if (lgncl) { + genradix_free(&lgncl->lgncl_lnc_list); + LIBCFS_FREE(lgncl, sizeof(*lgncl)); + cb->args[0] = 0; + } + + return 0; +} + +static int lnet_cpt_of_nid_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 lnet_genl_nid_cpt_list *lgncl; + int msg_len = genlmsg_len(gnlh); + struct nlattr *params, *top; + int rem, rc = 0; + +#ifdef HAVE_NL_DUMP_WITH_EXT_ACK + extack = cb->extack; +#endif + + mutex_lock(&the_lnet.ln_api_mutex); + if (the_lnet.ln_state != LNET_STATE_RUNNING) { + NL_SET_ERR_MSG(extack, "Network is down"); + mutex_unlock(&the_lnet.ln_api_mutex); + return -ENETDOWN; + } + + msg_len = genlmsg_len(gnlh); + if (!msg_len) { + NL_SET_ERR_MSG(extack, "Missing NID argument(s)"); + mutex_unlock(&the_lnet.ln_api_mutex); + return -ENOENT; + } + + LIBCFS_ALLOC(lgncl, sizeof(*lgncl)); + if (!lgncl) { + mutex_unlock(&the_lnet.ln_api_mutex); + return -ENOMEM; + } + + genradix_init(&lgncl->lgncl_lnc_list); + lgncl->lgncl_list_count = 0; + cb->args[0] = (long)lgncl; + + params = genlmsg_data(gnlh); + nla_for_each_attr(top, params, msg_len, rem) { + struct nlattr *nids; + int rem2; + + switch (nla_type(top)) { + case LN_SCALAR_ATTR_LIST: + nla_for_each_nested(nids, top, rem2) { + char nidstr[LNET_NIDSTR_SIZE + 1]; + struct lnet_nid_cpt *lnc; + + if (nla_type(nids) != LN_SCALAR_ATTR_VALUE) + continue; + + memset(nidstr, 0, sizeof(nidstr)); + rc = nla_strscpy(nidstr, nids, sizeof(nidstr)); + if (rc < 0) { + NL_SET_ERR_MSG(extack, + "failed to get NID"); + GOTO(report_err, rc); + } + + lnc = genradix_ptr_alloc(&lgncl->lgncl_lnc_list, + lgncl->lgncl_list_count++, + GFP_ATOMIC); + if (!lnc) { + NL_SET_ERR_MSG(extack, + "failed to allocate NID"); + GOTO(report_err, rc = -ENOMEM); + } + + rc = libcfs_strnid(&lnc->lnc_nid, strim(nidstr)); + if (rc < 0) { + NL_SET_ERR_MSG(extack, "invalid NID"); + GOTO(report_err, rc); + } + rc = 0; + CDEBUG(D_NET, "nid: %s\n", libcfs_nidstr(&lnc->lnc_nid)); + } + fallthrough; + default: + break; + } + } +report_err: + mutex_unlock(&the_lnet.ln_api_mutex); + + if (rc < 0) + lnet_cpt_of_nid_show_done(cb); + + return rc; +} + +static const struct ln_key_list cpt_of_nid_props_list = { + .lkl_maxattr = LNET_CPT_OF_NID_ATTR_MAX, + .lkl_list = { + [LNET_CPT_OF_NID_ATTR_HDR] = { + .lkp_value = "cpt-of-nid", + .lkp_key_format = LNKF_SEQUENCE | LNKF_MAPPING, + .lkp_data_type = NLA_NUL_STRING, + }, + [LNET_CPT_OF_NID_ATTR_NID] = { + .lkp_value = "nid", + .lkp_data_type = NLA_STRING, + }, + [LNET_CPT_OF_NID_ATTR_CPT] = { + .lkp_value = "cpt", + .lkp_data_type = NLA_U32, + }, + }, +}; + +static int lnet_cpt_of_nid_show_dump(struct sk_buff *msg, + struct netlink_callback *cb) +{ + struct lnet_genl_nid_cpt_list *lgncl; +#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; + int rc = 0; + bool need_hdr = true; + +#ifdef HAVE_NL_DUMP_WITH_EXT_ACK + extack = cb->extack; +#endif + + mutex_lock(&the_lnet.ln_api_mutex); + if (the_lnet.ln_state != LNET_STATE_RUNNING) { + NL_SET_ERR_MSG(extack, "Network is down"); + GOTO(send_error, rc = -ENETDOWN); + } + + lgncl = lnet_cpt_of_nid_dump_ctx(cb); + idx = lgncl->lgncl_index; + + if (!lgncl->lgncl_index) { + const struct ln_key_list *all[] = { + &cpt_of_nid_props_list, NULL, NULL + }; + + rc = lnet_genl_send_scalar_list(msg, portid, seq, &lnet_family, + NLM_F_CREATE | NLM_F_MULTI, + LNET_CMD_CPT_OF_NID, all); + if (rc < 0) { + NL_SET_ERR_MSG(extack, "failed to send key table"); + GOTO(send_error, rc); + } + } + + while (idx < lgncl->lgncl_list_count) { + struct lnet_nid_cpt *lnc; + void *hdr; + int cpt; + + lnc = genradix_ptr(&lgncl->lgncl_lnc_list, idx++); + + cpt = lnet_nid_cpt_hash(&lnc->lnc_nid, LNET_CPT_NUMBER); + + CDEBUG(D_NET, "nid: %s cpt: %d\n", libcfs_nidstr(&lnc->lnc_nid), cpt); + hdr = genlmsg_put(msg, portid, seq, &lnet_family, + NLM_F_MULTI, LNET_CMD_CPT_OF_NID); + if (!hdr) { + NL_SET_ERR_MSG(extack, "failed to send values"); + genlmsg_cancel(msg, hdr); + GOTO(send_error, rc = -EMSGSIZE); + } + + if (need_hdr) { + nla_put_string(msg, LNET_CPT_OF_NID_ATTR_HDR, ""); + need_hdr = false; + } + + nla_put_string(msg, LNET_CPT_OF_NID_ATTR_NID, + libcfs_nidstr(&lnc->lnc_nid)); + nla_put_u32(msg, LNET_CPT_OF_NID_ATTR_CPT, cpt); + + genlmsg_end(msg, hdr); + } + + genradix_free(&lgncl->lgncl_lnc_list); + rc = 0; + lgncl->lgncl_index = idx; + +send_error: + mutex_unlock(&the_lnet.ln_api_mutex); + + return lnet_nl_send_error(cb->skb, portid, seq, rc); +} + +#ifndef HAVE_NETLINK_CALLBACK_START +static int lnet_old_cpt_of_nid_show_dump(struct sk_buff *msg, + struct netlink_callback *cb) +{ + if (!cb->args[0]) { + int rc = lnet_cpt_of_nid_show_start(cb); + + if (rc < 0) + return rc; + } + + return lnet_cpt_of_nid_show_dump(msg, cb); +} +#endif + /* This is the keys for the UDSP info which is used by many * Netlink commands. */ @@ -8159,6 +8397,7 @@ static const struct genl_multicast_group lnet_mcast_grps[] = { { .name = "route", }, { .name = "ping", }, { .name = "discover", }, + { .name = "cpt-of-nid", }, }; static const struct genl_ops lnet_genl_ops[] = { @@ -8210,6 +8449,16 @@ static const struct genl_ops lnet_genl_ops[] = { .done = lnet_ping_show_done, .doit = lnet_ping_cmd, }, + { + .cmd = LNET_CMD_CPT_OF_NID, +#ifdef HAVE_NETLINK_CALLBACK_START + .start = lnet_cpt_of_nid_show_start, + .dumpit = lnet_cpt_of_nid_show_dump, +#else + .dumpit = lnet_old_cpt_of_nid_show_dump, +#endif + .done = lnet_cpt_of_nid_show_done, + }, }; static struct genl_family lnet_family = { diff --git a/lnet/utils/lnetconfig/liblnetconfig.c b/lnet/utils/lnetconfig/liblnetconfig.c index c759f7b..10b3b4f 100644 --- a/lnet/utils/lnetconfig/liblnetconfig.c +++ b/lnet/utils/lnetconfig/liblnetconfig.c @@ -4193,72 +4193,6 @@ out: return rc; } -static unsigned int -lnet_nid_cpt_hash(lnet_nid_t nid, long int number, unsigned int cpt_bits) -{ - __u64 key = nid; - __u16 lnd = LNET_NETTYP(LNET_NIDNET(nid)); - unsigned int cpt; - - if (number == 1) - return 0; - - if (lnd == KFILND || lnd == GNILND) { - cpt = hash_long(key, cpt_bits); - - /* NB: The number of CPTs needn't be a power of 2 */ - if (cpt >= number) - cpt = (key + cpt + (cpt >> 1)) % number; - } else { - __u64 pair_bits = 0x0001000100010001LLU; - __u64 mask = pair_bits * 0xFF; - __u64 pair_sum; - /* For ipv4 NIDs, use (sum-by-multiplication of nid bytes) mod - * (number of CPTs) to match nid to a CPT. - */ - pair_sum = (key & mask) + ((key >> 8) & mask); - pair_sum = (pair_sum * pair_bits) >> 48; - cpt = (unsigned int)(pair_sum) % number; - } - - return cpt; -} - -int lustre_lnet_calc_cpt_of_nid(char *nidc, long int ncpts) -{ - int rc = LUSTRE_CFG_RC_BAD_PARAM; - lnet_nid_t nid; - unsigned int cpt_bits; - - if (!nidc) { - fprintf(stderr, "error:\n msg: \"no NID provided\"\n"); - return rc; - } - - if (ncpts < 0) { - fprintf(stderr, "error:\n msg: \"number of CPTs not provided\"\n"); - return rc; - } - - if (ncpts < 1) { - fprintf(stderr, "error:\n msg: \"number of CPTs must be >= 1\"\n"); - return rc; - } - - cpt_bits = 1; - while ((1 << cpt_bits) < ncpts) - cpt_bits++; - - nid = libcfs_str2nid(nidc); - if (nid == LNET_NID_ANY) { - fprintf(stderr, "error:\n msg: \"bad NID provided %s\"\n", - nidc); - return rc; - } - - return (int)lnet_nid_cpt_hash(nid, ncpts, cpt_bits); -} - static int show_recovery_queue(enum lnet_health_type type, char *name, int seq_no, struct cYAML **show_rc, struct cYAML **err_rc) diff --git a/lnet/utils/lnetconfig/liblnetconfig.h b/lnet/utils/lnetconfig/liblnetconfig.h index d33b92b..48adc14 100644 --- a/lnet/utils/lnetconfig/liblnetconfig.h +++ b/lnet/utils/lnetconfig/liblnetconfig.h @@ -544,12 +544,6 @@ int lustre_lnet_calc_service_id(__u64 *service_id); int lustre_lnet_setup_mrrouting(struct cYAML **err_rc); /* - * lustre_lnet_calc_cpt_of_nid - * Return the cpt number of the NID provided - */ -int lustre_lnet_calc_cpt_of_nid(char *nidc, long int ncpts); - -/* * lustre_lnet_config_discovery * Enable or disable peer discovery. Peer discovery is enabled by default. * diff --git a/lnet/utils/lnetctl.c b/lnet/utils/lnetctl.c index df1ac30..2fe2347 100644 --- a/lnet/utils/lnetctl.c +++ b/lnet/utils/lnetctl.c @@ -117,9 +117,9 @@ command_t cmd_list[] = { {"udsp", jt_udsp, 0, "udsp {add | del | help}"}, {"setup-mrrouting", jt_setup_mrrouting, 0, "setup linux routing tables\n"}, - {"cpt-of-nid", jt_calc_cpt_of_nid, 0, "Calculate the CPT associated with NID\n" - "\t--nid: NID to calculate the CPT of\n" - "\t--ncpt: Number of CPTs to consider in the calculation\n"}, + {"cpt-of-nid", jt_calc_cpt_of_nid, 0, + "Calculate the CPTs associated with NIDs\n" + " usage:\n\tlnetctl cpt-of-nid nid[ nid ...]\n"}, { 0, 0, 0, NULL } }; @@ -436,55 +436,6 @@ static int jt_calc_service_id(int argc, char **argv) return rc; } -static int jt_calc_cpt_of_nid(int argc, char **argv) -{ - int rc, opt; - int cpt; - long int ncpts = -1; - char *nid = NULL; - struct cYAML *err_rc = NULL; - const char *const short_options = "n:c:h"; - static const struct option long_options[] = { - { .name = "nid", .has_arg = required_argument, .val = 'n' }, - { .name = "ncpt", .has_arg = required_argument, .val = 'c' }, - { .name = NULL } }; - - rc = check_cmd(cmd_list, "", "cpt-of-nid", 0, argc, argv); - if (rc) - return rc; - - while ((opt = getopt_long(argc, argv, short_options, - long_options, NULL)) != -1) { - switch (opt) { - case 'n': - nid = optarg; - break; - case 'c': - rc = parse_long(optarg, &ncpts); - if (rc != 0) { - cYAML_build_error(-1, -1, "cpt", "get", - "cannot parse input", &err_rc); - cYAML_print_tree2file(stderr, err_rc); - cYAML_free_tree(err_rc); - return -1; - } - break; - case '?': - print_help(cmd_list, "", "cpt-of-nid"); - default: - return 0; - } - } - - cpt = lustre_lnet_calc_cpt_of_nid(nid, ncpts); - if (cpt < 0) - return -1; - - printf("cpt:\n value: %d\n", cpt); - - return 0; -} - static int jt_set_recovery_limit(int argc, char **argv) { long int value; @@ -1080,6 +1031,200 @@ emitter_error: yaml_emitter_delete(&log); } +static int yaml_lnet_cpt_of_nid_display(yaml_parser_t *reply) +{ + yaml_document_t results; + yaml_emitter_t output; + int rc; + + rc = yaml_parser_load(reply, &results); + if (rc == 0) { + yaml_lnet_print_error(NLM_F_DUMP, "cpt-of-nid", + yaml_parser_get_reader_error(reply)); + yaml_document_delete(&results); + return -EINVAL; + } + + rc = yaml_emitter_initialize(&output); + if (rc == 1) { + yaml_emitter_set_output_file(&output, stdout); + + rc = yaml_emitter_dump(&output, &results); + } + + yaml_document_delete(&results); + if (rc == 0) { + yaml_emitter_log_error(&output, stderr); + rc = -EINVAL; + } + yaml_emitter_delete(&output); + + return 1; +} + +static int yaml_lnet_cpt_of_nid(int start, int end, char **nids) +{ + struct nl_sock *sk = NULL; + yaml_emitter_t request; + yaml_parser_t reply; + yaml_event_t event; + int i, rc; + + /* Create Netlink emitter to send request to kernel */ + sk = nl_socket_alloc(); + if (!sk) + return -EOPNOTSUPP; + + /* Setup parser to receive 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 free_reply; + + /* Create Netlink emitter to send request to kernel */ + rc = yaml_emitter_initialize(&request); + if (rc == 0) + goto free_reply; + + rc = yaml_emitter_set_output_netlink(&request, sk, LNET_GENL_NAME, + LNET_GENL_VERSION, + LNET_CMD_CPT_OF_NID, NLM_F_DUMP); + 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_ANY_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 *)"cpt-of-nid", + strlen("cpt-of-nid"), 1, 0, + YAML_PLAIN_SCALAR_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_ANY_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 *)"nids", + strlen("nids"), 1, 0, + YAML_PLAIN_SCALAR_STYLE); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + + yaml_sequence_start_event_initialize(&event, NULL, + (yaml_char_t *)YAML_SEQ_TAG, + 1, YAML_FLOW_SEQUENCE_STYLE); + rc = yaml_emitter_emit(&request, &event); + if (rc == 0) + goto emitter_error; + + for (i = start; i < end; i++) { + yaml_scalar_event_initialize(&event, NULL, + (yaml_char_t *)YAML_STR_TAG, + (yaml_char_t *)nids[i], + strlen(nids[i]), 1, 0, + YAML_PLAIN_SCALAR_STYLE); + 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; + + yaml_mapping_end_event_initialize(&event); + 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); +emitter_error: + if (rc == 0) { + yaml_emitter_log_error(&request, stderr); + rc = -EINVAL; + } else { + rc = yaml_lnet_cpt_of_nid_display(&reply); + } + yaml_emitter_delete(&request); +free_reply: + if (rc == 0) { + yaml_lnet_print_error(NLM_F_DUMP, "cpt-of-nid", + yaml_parser_get_reader_error(&reply)); + rc = -EINVAL; + } + + yaml_parser_delete(&reply); + nl_socket_free(sk); + + return rc == 1 ? 0 : rc; +} + +static int jt_calc_cpt_of_nid(int argc, char **argv) +{ + int rc, opt; + const char *const short_options = "h"; + static const struct option long_options[] = { + { .name = "help", .val = 'h' }, + { .name = NULL } }; + + rc = check_cmd(cmd_list, "", "cpt-of-nid", 2, argc, argv); + if (rc) + return rc; + + while ((opt = getopt_long(argc, argv, short_options, + long_options, NULL)) != -1) { + switch (opt) { + case 'h': + case '?': + print_help(cmd_list, "", "cpt-of-nid"); + default: + return 0; + } + } + + rc = yaml_lnet_cpt_of_nid(optind, argc, argv); + if (rc == -EOPNOTSUPP) + printf("Operation not supported\n"); + + return rc; +} + static int jt_config_lnet(int argc, char **argv) { struct cYAML *err_rc = NULL; -- 1.8.3.1