+static const struct ln_key_list discover_err_props_list = {
+ .lkl_maxattr = LNET_ERR_ATTR_MAX,
+ .lkl_list = {
+ [LNET_ERR_ATTR_HDR] = {
+ .lkp_value = "manage",
+ .lkp_key_format = LNKF_SEQUENCE | LNKF_MAPPING,
+ .lkp_data_type = NLA_NUL_STRING,
+ },
+ [LNET_ERR_ATTR_TYPE] = {
+ .lkp_value = "discover",
+ .lkp_data_type = NLA_STRING,
+ },
+ [LNET_ERR_ATTR_ERRNO] = {
+ .lkp_value = "errno",
+ .lkp_data_type = NLA_S16,
+ },
+ [LNET_ERR_ATTR_DESCR] = {
+ .lkp_value = "descr",
+ .lkp_data_type = NLA_STRING,
+ },
+ },
+};
+
+static const struct ln_key_list discover_props_list = {
+ .lkl_maxattr = LNET_PING_ATTR_MAX,
+ .lkl_list = {
+ [LNET_PING_ATTR_HDR] = {
+ .lkp_value = "discover",
+ .lkp_key_format = LNKF_SEQUENCE | LNKF_MAPPING,
+ .lkp_data_type = NLA_NUL_STRING,
+ },
+ [LNET_PING_ATTR_PRIMARY_NID] = {
+ .lkp_value = "primary nid",
+ .lkp_data_type = NLA_STRING
+ },
+ [LNET_PING_ATTR_ERRNO] = {
+ .lkp_value = "errno",
+ .lkp_data_type = NLA_S16
+ },
+ [LNET_PING_ATTR_MULTIRAIL] = {
+ .lkp_value = "Multi-Rail",
+ .lkp_data_type = NLA_FLAG
+ },
+ [LNET_PING_ATTR_PEER_NI_LIST] = {
+ .lkp_value = "peer_ni",
+ .lkp_key_format = LNKF_SEQUENCE | LNKF_MAPPING,
+ .lkp_data_type = NLA_NESTED
+ },
+ },
+};
+
+static int lnet_ping_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+ const struct ln_key_list *all[] = {
+ &discover_props_list, &ping_peer_ni_list, NULL
+ };
+ struct nlmsghdr *nlh = nlmsg_hdr(skb);
+ struct genlmsghdr *gnlh = nlmsg_data(nlh);
+ struct nlattr *params = genlmsg_data(gnlh);
+ struct lnet_genl_ping_list dlists;
+ int msg_len, rem, rc = 0, i;
+ bool clear_hdr = false;
+ struct sk_buff *reply;
+ struct nlattr *attr;
+ void *hdr = NULL;
+
+ msg_len = genlmsg_len(gnlh);
+ if (!msg_len) {
+ GENL_SET_ERR_MSG(info, "no configuration");
+ return -ENOMSG;
+ }
+
+ if (!(info->nlhdr->nlmsg_flags & NLM_F_CREATE)) {
+ GENL_SET_ERR_MSG(info, "only NLM_F_CREATE setting is allowed");
+ return -EINVAL;
+ }
+
+ reply = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!reply) {
+ GENL_SET_ERR_MSG(info,
+ "fail to allocate reply");
+ return -ENOMEM;
+ }
+
+ genradix_init(&dlists.lgpl_failed);
+ dlists.lgpl_failed_count = 0;
+ genradix_init(&dlists.lgpl_list);
+ dlists.lgpl_list_count = 0;
+
+ rc = lnet_genl_send_scalar_list(reply, info->snd_portid,
+ info->snd_seq, &lnet_family,
+ NLM_F_CREATE | NLM_F_MULTI,
+ LNET_CMD_PING, all);
+ if (rc < 0) {
+ GENL_SET_ERR_MSG(info,
+ "failed to send key table");
+ GOTO(report_err, rc);
+ }
+
+ nla_for_each_attr(attr, params, msg_len, rem) {
+ struct nlattr *nids;
+ int rem2;
+
+ /* We only care about the NID list to discover with */
+ if (nla_type(attr) != LN_SCALAR_ATTR_LIST)
+ continue;
+
+ nla_for_each_nested(nids, attr, rem2) {
+ char nid[LNET_NIDSTR_SIZE + 1];
+ struct lnet_processid id;
+ struct nlattr *nid_list;
+ struct lnet_peer *lp;
+ ssize_t len;
+
+ if (nla_type(nids) != LN_SCALAR_ATTR_VALUE)
+ continue;
+
+ memset(nid, 0, sizeof(nid));
+ rc = nla_strscpy(nid, nids, sizeof(nid));
+ if (rc < 0) {
+ GENL_SET_ERR_MSG(info,
+ "failed to get NID");
+ GOTO(report_err, rc);
+ }
+
+ len = libcfs_strid(&id, strim(nid));
+ if (len < 0) {
+ struct lnet_fail_ping *fail;
+
+ fail = genradix_ptr_alloc(&dlists.lgpl_failed,
+ dlists.lgpl_failed_count++,
+ GFP_KERNEL);
+ if (!fail) {
+ GENL_SET_ERR_MSG(info,
+ "failed to allocate improper NID");
+ GOTO(report_err, rc = -ENOMEM);
+ }
+ memset(fail->lfp_msg, '\0', sizeof(fail->lfp_msg));
+ snprintf(fail->lfp_msg, sizeof(fail->lfp_msg),
+ "cannot parse NID '%s'", strim(nid));
+ fail->lfp_id = id;
+ fail->lfp_errno = len;
+ continue;
+ }
+
+ if (LNET_NID_IS_ANY(&id.nid))
+ continue;
+
+ rc = lnet_discover(&id,
+ info->nlhdr->nlmsg_flags & NLM_F_EXCL,
+ &dlists);
+ if (rc < 0) {
+ struct lnet_fail_ping *fail;
+
+ fail = genradix_ptr_alloc(&dlists.lgpl_failed,
+ dlists.lgpl_failed_count++,
+ GFP_KERNEL);
+ if (!fail) {
+ GENL_SET_ERR_MSG(info,
+ "failed to allocate failed NID");
+ GOTO(report_err, rc = -ENOMEM);
+ }
+ memset(fail->lfp_msg, '\0', sizeof(fail->lfp_msg));
+ snprintf(fail->lfp_msg, sizeof(fail->lfp_msg),
+ "failed to discover %s",
+ libcfs_nidstr(&id.nid));
+ fail->lfp_id = id;
+ fail->lfp_errno = rc;
+ continue;
+ }
+
+ /* create the genetlink message header */
+ hdr = genlmsg_put(reply, info->snd_portid, info->snd_seq,
+ &lnet_family, NLM_F_MULTI, LNET_CMD_PING);
+ if (!hdr) {
+ GENL_SET_ERR_MSG(info,
+ "failed to allocate hdr");
+ GOTO(report_err, rc = -ENOMEM);
+ }
+
+ if (!clear_hdr) {
+ nla_put_string(reply, LNET_PING_ATTR_HDR, "");
+ clear_hdr = true;
+ }
+
+ lp = lnet_find_peer(&id.nid);
+ if (lp) {
+ nla_put_string(reply, LNET_PING_ATTR_PRIMARY_NID,
+ libcfs_nidstr(&lp->lp_primary_nid));
+ if (lnet_peer_is_multi_rail(lp))
+ nla_put_flag(reply, LNET_PING_ATTR_MULTIRAIL);
+ lnet_peer_decref_locked(lp);
+ }
+
+ nid_list = nla_nest_start(reply, LNET_PING_ATTR_PEER_NI_LIST);
+ for (i = 0; i < dlists.lgpl_list_count; i++) {
+ struct lnet_processid *found;
+ struct nlattr *nid_attr;
+ char *idstr;
+
+ found = genradix_ptr(&dlists.lgpl_list, i);
+ if (nid_is_lo0(&found->nid))
+ continue;
+
+ nid_attr = nla_nest_start(reply, i + 1);
+ if (id.pid == LNET_PID_LUSTRE)
+ idstr = libcfs_nidstr(&found->nid);
+ else
+ idstr = libcfs_idstr(found);
+ nla_put_string(reply, LNET_PING_PEER_NI_ATTR_NID, idstr);
+ nla_nest_end(reply, nid_attr);
+ }
+ nla_nest_end(reply, nid_list);
+
+ genlmsg_end(reply, hdr);
+ }
+ }
+
+ if (dlists.lgpl_failed_count) {
+ int flags = NLM_F_CREATE | NLM_F_REPLACE | NLM_F_MULTI;
+ const struct ln_key_list *fail[] = {
+ &discover_err_props_list, NULL
+ };
+
+ rc = lnet_genl_send_scalar_list(reply, info->snd_portid,
+ info->snd_seq, &lnet_family,
+ flags, LNET_CMD_PING, fail);
+ if (rc < 0) {
+ GENL_SET_ERR_MSG(info,
+ "failed to send new key table");
+ GOTO(report_err, rc);
+ }
+
+ for (i = 0; i < dlists.lgpl_failed_count; i++) {
+ struct lnet_fail_ping *fail;
+
+ hdr = genlmsg_put(reply, info->snd_portid, info->snd_seq,
+ &lnet_family, NLM_F_MULTI, LNET_CMD_PING);
+ if (!hdr) {
+ GENL_SET_ERR_MSG(info,
+ "failed to send failed values");
+ GOTO(report_err, rc = -ENOMSG);
+ }
+
+ fail = genradix_ptr(&dlists.lgpl_failed, i);
+ if (i == 0)
+ nla_put_string(reply, LNET_ERR_ATTR_HDR, "");
+
+ nla_put_string(reply, LNET_ERR_ATTR_TYPE, "\n");
+ nla_put_s16(reply, LNET_ERR_ATTR_ERRNO,
+ fail->lfp_errno);
+ nla_put_string(reply, LNET_ERR_ATTR_DESCR,
+ fail->lfp_msg);
+ genlmsg_end(reply, hdr);
+ }
+ }
+
+ nlh = nlmsg_put(reply, info->snd_portid, info->snd_seq, NLMSG_DONE, 0,
+ NLM_F_MULTI);
+ if (!nlh) {
+ genlmsg_cancel(reply, hdr);
+ GENL_SET_ERR_MSG(info,
+ "failed to finish message");
+ GOTO(report_err, rc = -EMSGSIZE);
+ }
+
+report_err:
+ genradix_free(&dlists.lgpl_failed);
+ genradix_free(&dlists.lgpl_list);
+
+ if (rc < 0) {
+ genlmsg_cancel(reply, hdr);
+ nlmsg_free(reply);
+ } else {
+ rc = genlmsg_reply(reply, info);
+ }
+
+ return rc;
+}
+