#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;
* @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,
LNET_CMD_ROUTES = 4,
LNET_CMD_CONNS = 5,
LNET_CMD_PING = 6,
+ LNET_CMD_CPT_OF_NID = 7,
__LNET_CMD_MAX_PLUS_ONE
};
}
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.
*/
{ .name = "route", },
{ .name = "ping", },
{ .name = "discover", },
+ { .name = "cpt-of-nid", },
};
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 = {
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)
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.
*
{"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 }
};
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;
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;