[lustre-devel] [PATCH 38/42] lnet: use Netlink to support LNet ping commands

James Simmons <[email protected]>
Newsgroups org.lustre.lists.lustre-devel
Message-ID <[email protected]>
Completely replace the old pre-MR ping command ioctl using
Netlink which will also handle large NIDs. We do update
IOC_LIBCFS_PING_PEER, which only supports only small NIDs,
so older tools will keep working.

WC-bug-id: https://jira.whamcloud.com/browse/LU-10003
Lustre-commit: d137e9823ca1e97fc ("LU-10003 lnet: use Netlink to support LNet ping commands")
Signed-off-by: James Simmons <[email protected]>
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/49360
Reviewed-by: Cyril Bordage <[email protected]>
Reviewed-by: Chris Horn <[email protected]>
Reviewed-by: Oleg Drokin <[email protected]>
---
 include/linux/lnet/lib-lnet.h          |   1 +
 include/linux/lnet/lib-types.h         |  46 +++
 include/uapi/linux/lnet/libcfs_ioctl.h |   2 +-
 include/uapi/linux/lnet/lnet-dlc.h     |   2 +
 net/lnet/lnet/api-ni.c                 | 491 +++++++++++++++++++++----
 net/lnet/lnet/nidstrings.c             |  24 ++
 net/lnet/lnet/peer.c                   |   2 +-
 7 files changed, 495 insertions(+), 73 deletions(-)

diff --git a/include/linux/lnet/lib-lnet.h b/include/linux/lnet/lib-lnet.h
index 25289f5bba39..ed28af6fe8d5 100644
--- a/include/linux/lnet/lib-lnet.h
+++ b/include/linux/lnet/lib-lnet.h
@@ -895,6 +895,7 @@ struct lnet_ping_iter {
 u32 *ping_iter_first(struct lnet_ping_iter *pi, struct lnet_ping_buffer *pbuf,
 		     struct lnet_nid *nid);
 u32 *ping_iter_next(struct lnet_ping_iter *pi, struct lnet_nid *nid);
+int ping_info_count_entries(struct lnet_ping_buffer *pbuf);
 
 static inline int lnet_push_target_resize_needed(void)
 {
diff --git a/include/linux/lnet/lib-types.h b/include/linux/lnet/lib-types.h
index 73d962f18e06..eb54e754ad95 100644
--- a/include/linux/lnet/lib-types.h
+++ b/include/linux/lnet/lib-types.h
@@ -62,6 +62,7 @@ static inline char *libcfs_nidstr(const struct lnet_nid *nid)
 
 int libcfs_strnid(struct lnet_nid *nid, const char *str);
 char *libcfs_idstr(struct lnet_processid *id);
+int libcfs_strid(struct lnet_processid *id, const char *str);
 
 int cfs_match_nid_net(struct lnet_nid *nid, u32 net,
 		      struct list_head *net_num_list,
@@ -567,6 +568,51 @@ enum lnet_net_local_ni_tunables_attr {
 
 #define LNET_NET_LOCAL_NI_TUNABLES_ATTR_MAX (__LNET_NET_LOCAL_NI_TUNABLES_ATTR_MAX_PLUS_ONE - 1)
 
+/** LNet netlink ping API */
+
+/** enum lnet_ping_atts				      - LNet ping netlink properties
+ *							attributes to describe ping format
+ *							These values are used to piece together
+ *							messages for sending and receiving.
+ *
+ * @LNET_PING_ATTR_UNSPEC:				unspecified attribute to catch errors
+ *
+ * @LNET_PING_ATTR_HDR:					grouping for LNet ping data (NLA_NUL_STRING)
+ * @LNET_PING_ATTR_PRIMARY_NID:				Source NID for ping request (NLA_STRING)
+ * @LNET_PING_ATTR_ERRNO:				error code if we fail to ping (NLA_S16)
+ * @LNET_PING_ATTR_MULTIRAIL:				Report if MR is supported (NLA_FLAG)
+ * @LNET_PING_ATTR_PEER_NI_LIST:			List of peer NI's (NLA_NESTED)
+ */
+enum lnet_ping_attr {
+	LNET_PING_ATTR_UNSPEC = 0,
+
+	LNET_PING_ATTR_HDR,
+	LNET_PING_ATTR_PRIMARY_NID,
+	LNET_PING_ATTR_ERRNO,
+	LNET_PING_ATTR_MULTIRAIL,
+	LNET_PING_ATTR_PEER_NI_LIST,
+	__LNET_PING_ATTR_MAX_PLUS_ONE,
+};
+
+#define LNET_PING_ATTR_MAX (__LNET_PING_ATTR_MAX_PLUS_ONE - 1)
+
+/** enium lnet_ping_peer_ni_attr		      - LNet peer ni information reported by
+ *							ping command. A list of these are
+ *							returned with a ping request.
+ *
+ * @LNET_PING_PEER_NI_ATTR_UNSPEC:			unspecified attribute to catch errrors
+ *
+ * @LNET_PING_PEER_NI_ATTR_NID:				NID address of peer NI. (NLA_STRING)
+ */
+enum lnet_ping_peer_ni_attr {
+	LNET_PING_PEER_NI_ATTR_UNSPEC = 0,
+
+	LNET_PING_PEER_NI_ATTR_NID,
+	__LNET_PING_PEER_NI_ATTR_MAX_PLUS_ONE,
+};
+
+#define LNET_PING_PEER_NI_ATTR_MAX (__LNET_PING_PEER_NI_ATTR_MAX_PLUS_ONE - 1)
+
 struct lnet_ni {
 	spinlock_t		ni_lock;
 	/* chain on the lnet_net structure */
diff --git a/include/uapi/linux/lnet/libcfs_ioctl.h b/include/uapi/linux/lnet/libcfs_ioctl.h
index 89ac0758c1b1..e012532fc88a 100644
--- a/include/uapi/linux/lnet/libcfs_ioctl.h
+++ b/include/uapi/linux/lnet/libcfs_ioctl.h
@@ -102,7 +102,7 @@ struct libcfs_ioctl_data {
 #define IOC_LIBCFS_LNET_DIST		_IOWR('e', 58, IOCTL_LIBCFS_TYPE)
 #define IOC_LIBCFS_CONFIGURE		_IOWR('e', 59, IOCTL_LIBCFS_TYPE)
 #define IOC_LIBCFS_TESTPROTOCOMPAT	_IOWR('e', 60, IOCTL_LIBCFS_TYPE)
-#define IOC_LIBCFS_PING			_IOWR('e', 61, IOCTL_LIBCFS_TYPE)
+/* IOC_LIBCFS_PING obsolete in 2.16, was _IOWR('e', 61, IOCTL_LIBCFS_TYPE) */
 #define IOC_LIBCFS_PING_PEER		_IOWR('e', 62, IOCTL_LIBCFS_TYPE)
 #define IOC_LIBCFS_LNETST		_IOWR('e', 63, IOCTL_LIBCFS_TYPE)
 #define IOC_LIBCFS_LNET_FAULT		_IOWR('e', 64, IOCTL_LIBCFS_TYPE)
diff --git a/include/uapi/linux/lnet/lnet-dlc.h b/include/uapi/linux/lnet/lnet-dlc.h
index 58697c134d6a..63578a04a286 100644
--- a/include/uapi/linux/lnet/lnet-dlc.h
+++ b/include/uapi/linux/lnet/lnet-dlc.h
@@ -57,6 +57,7 @@
  *  @LNET_CMD_UNSPEC:		unspecified command to catch errors
  *
  *  @LNET_CMD_NETS:		command to manage the LNet networks
+ *  @LNET_CMD_PING:		command to send pings to LNet connections
  */
 enum lnet_commands {
 	LNET_CMD_UNSPEC		= 0,
@@ -66,6 +67,7 @@ enum lnet_commands {
 	LNET_CMD_PEERS		= 3,
 	LNET_CMD_ROUTES		= 4,
 	LNET_CMD_CONNS		= 5,
+	LNET_CMD_PING		= 6,
 
 	__LNET_CMD_MAX_PLUS_ONE
 };
diff --git a/net/lnet/lnet/api-ni.c b/net/lnet/lnet/api-ni.c
index 18dc3e7cccc6..2c7f5211bbee 100644
--- a/net/lnet/lnet/api-ni.c
+++ b/net/lnet/lnet/api-ni.c
@@ -31,6 +31,9 @@
  */
 
 #define DEBUG_SUBSYSTEM S_LNET
+
+#include <linux/ctype.h>
+#include <linux/generic-radix-tree.h>
 #include <linux/log2.h>
 #include <linux/ktime.h>
 #include <linux/moduleparam.h>
@@ -223,8 +226,23 @@ static void lnet_set_lnd_timeout(void)
  */
 static atomic_t lnet_dlc_seq_no = ATOMIC_INIT(0);
 
-static int lnet_ping(struct lnet_process_id id4, struct lnet_nid *src_nid,
-		     signed long timeout, struct lnet_process_id __user *ids,
+struct lnet_fail_ping {
+	struct lnet_processid		lfp_id;
+	int				lfp_errno;
+};
+
+struct lnet_genl_ping_list {
+	unsigned int			lgpl_index;
+	unsigned int			lgpl_list_count;
+	unsigned int			lgpl_failed_count;
+	signed long			lgpl_timeout;
+	struct lnet_nid			lgpl_src_nid;
+	GENRADIX(struct lnet_fail_ping)	lgpl_failed;
+	GENRADIX(struct lnet_processid)	lgpl_list;
+};
+
+static int lnet_ping(struct lnet_processid *id, struct lnet_nid *src_nid,
+		     signed long timeout, struct lnet_genl_ping_list *plist,
 		     int n_ids);
 
 static int lnet_discover(struct lnet_process_id id, u32 force,
@@ -4367,35 +4385,15 @@ LNetCtl(unsigned int cmd, void *arg)
 	case IOC_LIBCFS_LNET_FAULT:
 		return lnet_fault_ctl(data->ioc_flags, data);
 
-	case IOC_LIBCFS_PING: {
-		struct lnet_process_id id4;
-		signed long timeout;
-
-		id4.nid = data->ioc_nid;
-		id4.pid = data->ioc_u32[0];
-
-		/* If timeout is negative then set default of 3 minutes */
-		if (((s32)data->ioc_u32[1] <= 0) ||
-		    data->ioc_u32[1] > (DEFAULT_PEER_TIMEOUT * MSEC_PER_SEC))
-			timeout = DEFAULT_PEER_TIMEOUT * HZ;
-		else
-			timeout = msecs_to_jiffies(data->ioc_u32[1]);
-
-		rc = lnet_ping(id4, &LNET_ANY_NID, timeout, data->ioc_pbuf1,
-			       data->ioc_plen1 / sizeof(struct lnet_process_id));
-
-		if (rc < 0)
-			return rc;
-
-		data->ioc_count = rc;
-		return 0;
-	}
-
 	case IOC_LIBCFS_PING_PEER: {
 		struct lnet_ioctl_ping_data *ping = arg;
+		struct lnet_process_id __user *ids = ping->ping_buf;
 		struct lnet_nid src_nid = LNET_ANY_NID;
+		struct lnet_genl_ping_list plist;
+		struct lnet_processid id;
 		struct lnet_peer *lp;
 		signed long timeout;
+		int count, i;
 
 		/* Check if the supplied ping data supports source nid
 		 * NB: This check is sufficient if lnet_ioctl_ping_data has
@@ -4416,15 +4414,30 @@ LNetCtl(unsigned int cmd, void *arg)
 		else
 			timeout = msecs_to_jiffies(ping->op_param);
 
-		rc = lnet_ping(ping->ping_id, &src_nid, timeout,
-			       ping->ping_buf,
+		id.pid = ping->ping_id.pid;
+		lnet_nid4_to_nid(ping->ping_id.nid, &id.nid);
+		rc = lnet_ping(&id, &src_nid, timeout, &plist,
 			       ping->ping_count);
 		if (rc < 0)
-			return rc;
+			goto report_ping_err;
+		count = rc;
+
+		for (i = 0; i < count; i++) {
+			struct lnet_processid *result;
+			struct lnet_process_id tmpid;
+
+			result = genradix_ptr(&plist.lgpl_list, i);
+			memset(&tmpid, 0, sizeof(tmpid));
+			tmpid.pid = result->pid;
+			tmpid.nid = lnet_nid_to_nid4(&result->nid);
+			if (copy_to_user(&ids[i], &tmpid, sizeof(tmpid))) {
+				rc = -EFAULT;
+				goto report_ping_err;
+			}
+		}
 
 		mutex_lock(&the_lnet.ln_api_mutex);
-		lnet_nid4_to_nid(ping->ping_id.nid, &nid);
-		lp = lnet_find_peer(&nid);
+		lp = lnet_find_peer(&id.nid);
 		if (lp) {
 			ping->ping_id.nid =
 				lnet_nid_to_nid4(&lp->lp_primary_nid);
@@ -4433,8 +4446,10 @@ LNetCtl(unsigned int cmd, void *arg)
 		}
 		mutex_unlock(&the_lnet.ln_api_mutex);
 
-		ping->ping_count = rc;
-		return 0;
+		ping->ping_count = count;
+report_ping_err:
+		genradix_free(&plist.lgpl_list);
+		return rc;
 	}
 
 	case IOC_LIBCFS_DISCOVER: {
@@ -5229,9 +5244,339 @@ static int lnet_net_cmd(struct sk_buff *skb, struct genl_info *info)
 	return rc;
 }
 
+static inline struct lnet_genl_ping_list *
+lnet_ping_dump_ctx(struct netlink_callback *cb)
+{
+	return (struct lnet_genl_ping_list *)cb->args[0];
+}
+
+static int lnet_ping_show_done(struct netlink_callback *cb)
+{
+	struct lnet_genl_ping_list *plist = lnet_ping_dump_ctx(cb);
+
+	if (plist) {
+		genradix_free(&plist->lgpl_failed);
+		genradix_free(&plist->lgpl_list);
+		kfree(plist);
+		cb->args[0] = 0;
+	}
+
+	return 0;
+}
+
+/* LNet ping ->start() handler for GET requests */
+static int lnet_ping_show_start(struct netlink_callback *cb)
+{
+	struct genlmsghdr *gnlh = nlmsg_data(cb->nlh);
+	struct netlink_ext_ack *extack = cb->extack;
+	struct lnet_genl_ping_list *plist;
+	int msg_len = genlmsg_len(gnlh);
+	struct nlattr *params, *top;
+	int rem, rc = 0;
+
+	if (the_lnet.ln_refcount == 0) {
+		NL_SET_ERR_MSG(extack, "Network is down");
+		return -ENETDOWN;
+	}
+
+	if (!msg_len) {
+		NL_SET_ERR_MSG(extack, "Ping needs NID targets");
+		return -ENOENT;
+	}
+
+	plist = kzalloc(sizeof(*plist), GFP_KERNEL);
+	if (!plist) {
+		NL_SET_ERR_MSG(extack, "failed to setup ping list");
+		return -ENOMEM;
+	}
+	genradix_init(&plist->lgpl_list);
+	plist->lgpl_timeout = DEFAULT_PEER_TIMEOUT * HZ;
+	plist->lgpl_src_nid = LNET_ANY_NID;
+	plist->lgpl_index = 0;
+	plist->lgpl_list_count = 0;
+	cb->args[0] = (long)plist;
+
+	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_VALUE:
+			if (nla_strcmp(top, "timeout") == 0) {
+				s64 timeout;
+
+				top = nla_next(top, &rem);
+				if (nla_type(top) != LN_SCALAR_ATTR_INT_VALUE) {
+					NL_SET_ERR_MSG(extack,
+						       "invalid timeout param");
+					rc = -EINVAL;
+					goto report_err;
+				}
+
+				/* If timeout is negative then set default of
+				 * 3 minutes
+				 */
+				timeout = nla_get_s64(top);
+				if (timeout > 0 &&
+				    timeout < (DEFAULT_PEER_TIMEOUT * MSEC_PER_SEC))
+					plist->lgpl_timeout =
+						nsecs_to_jiffies(timeout * NSEC_PER_MSEC);
+			} else if (nla_strcmp(top, "source") == 0) {
+				char nidstr[LNET_NIDSTR_SIZE + 1];
+
+				top = nla_next(top, &rem);
+				if (nla_type(top) != LN_SCALAR_ATTR_VALUE) {
+					NL_SET_ERR_MSG(extack,
+						       "invalid source param");
+					rc = -EINVAL;
+					goto report_err;
+				}
+
+				rc = nla_strlcpy(nidstr, top, sizeof(nidstr));
+				if (rc < 0) {
+					NL_SET_ERR_MSG(extack,
+						       "failed to parse source nid");
+					goto report_err;
+				}
+
+				rc = libcfs_strnid(&plist->lgpl_src_nid,
+						   strim(nidstr));
+				if (rc < 0) {
+					NL_SET_ERR_MSG(extack,
+						       "invalid source nid");
+					goto report_err;
+				}
+				rc = 0;
+			}
+			break;
+		case LN_SCALAR_ATTR_LIST:
+			nla_for_each_nested(nids, top, rem2) {
+				char nid[LNET_NIDSTR_SIZE + 1];
+				struct lnet_processid *id;
+
+				if (nla_type(nids) != LN_SCALAR_ATTR_VALUE)
+					continue;
+
+				memset(nid, 0, sizeof(nid));
+				rc = nla_strlcpy(nid, nids, sizeof(nid));
+				if (rc < 0) {
+					NL_SET_ERR_MSG(extack,
+						       "failed to get NID");
+					goto report_err;
+				}
+
+				id = genradix_ptr_alloc(&plist->lgpl_list,
+							plist->lgpl_list_count++,
+							GFP_ATOMIC);
+				if (!id) {
+					NL_SET_ERR_MSG(extack,
+						       "failed to allocate NID");
+					rc = -ENOMEM;
+					goto report_err;
+				}
+
+				rc = libcfs_strid(id, strim(nid));
+				if (rc < 0) {
+					NL_SET_ERR_MSG(extack, "invalid NID");
+					goto report_err;
+				}
+				rc = 0;
+			}
+			fallthrough;
+		default:
+			break;
+		}
+	}
+report_err:
+	if (rc < 0)
+		lnet_ping_show_done(cb);
+
+	return rc;
+}
+
+static const struct ln_key_list ping_props_list = {
+	.lkl_maxattr			= LNET_PING_ATTR_MAX,
+	.lkl_list			= {
+		[LNET_PING_ATTR_HDR]            = {
+			.lkp_value              = "ping",
+			.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 struct ln_key_list ping_peer_ni_list = {
+	.lkl_maxattr			= LNET_PING_PEER_NI_ATTR_MAX,
+	.lkl_list                       = {
+		[LNET_PING_PEER_NI_ATTR_NID]	= {
+			.lkp_value		= "nid",
+			.lkp_data_type		= NLA_STRING
+		},
+	},
+};
+
+static int lnet_ping_show_dump(struct sk_buff *msg,
+			       struct netlink_callback *cb)
+{
+	struct lnet_genl_ping_list *plist = lnet_ping_dump_ctx(cb);
+	struct genlmsghdr *gnlh = nlmsg_data(cb->nlh);
+	struct netlink_ext_ack *extack = cb->extack;
+	int portid = NETLINK_CB(cb->skb).portid;
+	int seq = cb->nlh->nlmsg_seq;
+	int idx = plist->lgpl_index;
+	int rc = 0, i = 0;
+
+	if (!plist->lgpl_index) {
+		const struct ln_key_list *all[] = {
+			&ping_props_list, &ping_peer_ni_list, NULL
+		};
+
+		rc = lnet_genl_send_scalar_list(msg, portid, seq,
+						&lnet_family,
+						NLM_F_CREATE | NLM_F_MULTI,
+						LNET_CMD_PING, all);
+		if (rc < 0) {
+			NL_SET_ERR_MSG(extack, "failed to send key table");
+			goto send_error;
+		}
+
+		genradix_init(&plist->lgpl_failed);
+	}
+
+	while (idx < plist->lgpl_list_count) {
+		struct lnet_nid primary_nid = LNET_ANY_NID;
+		struct lnet_genl_ping_list peers;
+		struct lnet_processid *id;
+		struct nlattr *nid_list;
+		struct lnet_peer *lp;
+		bool mr_flag = false;
+		unsigned int count;
+		void *hdr = NULL;
+
+		id = genradix_ptr(&plist->lgpl_list, idx++);
+		if (nid_is_lo0(&id->nid))
+			continue;
+
+		rc = lnet_ping(id, &plist->lgpl_src_nid, plist->lgpl_timeout,
+			       &peers, lnet_interfaces_max);
+		if (rc < 0) {
+			struct lnet_fail_ping *fail;
+
+			fail = genradix_ptr_alloc(&plist->lgpl_failed,
+						  plist->lgpl_failed_count++,
+						  GFP_ATOMIC);
+			if (!fail) {
+				NL_SET_ERR_MSG(extack,
+					       "failed to allocate failed NID");
+				goto send_error;
+			}
+			fail->lfp_id = *id;
+			fail->lfp_errno = rc;
+			goto cant_reach;
+		}
+
+		mutex_lock(&the_lnet.ln_api_mutex);
+		lp = lnet_find_peer(&id->nid);
+		if (lp) {
+			primary_nid = lp->lp_primary_nid;
+			mr_flag = lnet_peer_is_multi_rail(lp);
+			lnet_peer_decref_locked(lp);
+		}
+		mutex_unlock(&the_lnet.ln_api_mutex);
+
+		hdr = genlmsg_put(msg, portid, seq, &lnet_family,
+				  NLM_F_MULTI, LNET_CMD_PING);
+		if (!hdr) {
+			NL_SET_ERR_MSG(extack, "failed to send values");
+			genlmsg_cancel(msg, hdr);
+			rc = -EMSGSIZE;
+			goto send_error;
+		}
+
+		if (i++ == 0)
+			nla_put_string(msg, LNET_PING_ATTR_HDR, "");
+
+		nla_put_string(msg, LNET_PING_ATTR_PRIMARY_NID,
+			       libcfs_nidstr(&primary_nid));
+		if (mr_flag)
+			nla_put_flag(msg, LNET_PING_ATTR_MULTIRAIL);
+
+		nid_list = nla_nest_start(msg, LNET_PING_ATTR_PEER_NI_LIST);
+		for (count = 0; count < rc; count++) {
+			struct lnet_processid *result;
+			struct nlattr *nid_attr;
+			char *idstr;
+
+			result = genradix_ptr(&peers.lgpl_list, count);
+			if (nid_is_lo0(&result->nid))
+				continue;
+
+			nid_attr = nla_nest_start(msg, count + 1);
+			if (gnlh->version == 1)
+				idstr = libcfs_nidstr(&result->nid);
+			else
+				idstr = libcfs_idstr(result);
+			nla_put_string(msg, LNET_PING_PEER_NI_ATTR_NID, idstr);
+			nla_nest_end(msg, nid_attr);
+		}
+		nla_nest_end(msg, nid_list);
+		genlmsg_end(msg, hdr);
+cant_reach:
+		genradix_free(&peers.lgpl_list);
+	}
+
+	for (i = 0; i < plist->lgpl_failed_count; i++) {
+		struct lnet_fail_ping *fail;
+		void *hdr;
+
+		fail = genradix_ptr(&plist->lgpl_failed, i);
+
+		hdr = genlmsg_put(msg, portid, seq, &lnet_family,
+				  NLM_F_MULTI, LNET_CMD_PING);
+		if (!hdr) {
+			NL_SET_ERR_MSG(extack, "failed to send failed values");
+			genlmsg_cancel(msg, hdr);
+			rc = -EMSGSIZE;
+			goto send_error;
+		}
+
+		if (i == 0)
+			nla_put_string(msg, LNET_PING_ATTR_HDR, "");
+
+		nla_put_string(msg, LNET_PING_ATTR_PRIMARY_NID,
+			       libcfs_nidstr(&fail->lfp_id.nid));
+		nla_put_s16(msg, LNET_PING_ATTR_ERRNO, fail->lfp_errno);
+		genlmsg_end(msg, hdr);
+	}
+	rc = 0; /* don't treat it as an error */
+
+	plist->lgpl_index = idx;
+send_error:
+	return rc;
+}
+
 static const struct genl_multicast_group lnet_mcast_grps[] = {
 	{ .name	=	"ip2net",	},
 	{ .name =	"net",		},
+	{ .name	=	"ping",		},
 };
 
 static const struct genl_ops lnet_genl_ops[] = {
@@ -5242,6 +5587,12 @@ static const struct genl_ops lnet_genl_ops[] = {
 		.done		= lnet_net_show_done,
 		.doit		= lnet_net_cmd,
 	},
+	{
+		.cmd		= LNET_CMD_PING,
+		.start		= lnet_ping_show_start,
+		.dumpit		= lnet_ping_show_dump,
+		.done		= lnet_ping_show_done,
+	},
 };
 
 static struct genl_family lnet_family = {
@@ -5337,29 +5688,26 @@ lnet_ping_event_handler(struct lnet_event *event)
 		complete(&pd->completion);
 }
 
-/* lnet_ping() only works with nid4 nids, so we can calculate
- * size from number of nids
- */
-#define LNET_PING_INFO_SIZE(NNIDS) \
-	offsetof(struct lnet_ping_info, pi_ni[NNIDS])
-
-static int lnet_ping(struct lnet_process_id id4, struct lnet_nid *src_nid,
-		     signed long timeout, struct lnet_process_id __user *ids,
+static int lnet_ping(struct lnet_processid *id, struct lnet_nid *src_nid,
+		     signed long timeout, struct lnet_genl_ping_list *plist,
 		     int n_ids)
 {
+	int id_bytes = sizeof(struct lnet_ni_status); /* For 0@lo */
 	struct lnet_md md = { NULL };
 	struct ping_data pd = { 0 };
 	struct lnet_ping_buffer *pbuf;
-	struct lnet_process_id tmpid;
-	struct lnet_processid id;
-	int id_bytes;
-	int i;
+	struct lnet_processid pid;
+	struct lnet_ping_iter pi;
+	int i = 0;
+	u32 *st;
 	int nob;
 	int rc;
 	int rc2;
 
+	genradix_init(&plist->lgpl_list);
+
 	/* n_ids limit is arbitrary */
-	if (n_ids <= 0 || id4.nid == LNET_NID_ANY)
+	if (n_ids <= 0 || LNET_NID_IS_ANY(&id->nid))
 		return -EINVAL;
 
 	/* if the user buffer has more space than the lnet_interfaces_max
@@ -5368,10 +5716,10 @@ static int lnet_ping(struct lnet_process_id id4, struct lnet_nid *src_nid,
 	if (n_ids > lnet_interfaces_max)
 		n_ids = lnet_interfaces_max;
 
-	if (id4.pid == LNET_PID_ANY)
-		id4.pid = LNET_PID_LUSTRE;
+	if (id->pid == LNET_PID_ANY)
+		id->pid = LNET_PID_LUSTRE;
 
-	id_bytes = LNET_PING_INFO_SIZE(n_ids);
+	id_bytes += lnet_ping_sts_size(&id->nid) * n_ids;
 	pbuf = lnet_ping_buffer_alloc(id_bytes, GFP_NOFS);
 	if (!pbuf)
 		return -ENOMEM;
@@ -5393,8 +5741,7 @@ static int lnet_ping(struct lnet_process_id id4, struct lnet_nid *src_nid,
 		goto fail_ping_buffer_decref;
 	}
 
-	lnet_pid4_to_pid(id4, &id);
-	rc = LNetGet(src_nid, pd.mdh, &id, LNET_RESERVED_PORTAL,
+	rc = LNetGet(src_nid, pd.mdh, id, LNET_RESERVED_PORTAL,
 		     LNET_PROTO_PING_MATCHBITS, 0, false);
 	if (rc) {
 		/* Don't CERROR; this could be deliberate! */
@@ -5410,6 +5757,7 @@ static int lnet_ping(struct lnet_process_id id4, struct lnet_nid *src_nid,
 		LNetMDUnlink(pd.mdh);
 		wait_for_completion(&pd.completion);
 	}
+
 	if (!pd.replied) {
 		rc = pd.rc ?: -EIO;
 		goto fail_ping_buffer_decref;
@@ -5420,9 +5768,9 @@ static int lnet_ping(struct lnet_process_id id4, struct lnet_nid *src_nid,
 
 	rc = -EPROTO;		/* if I can't parse... */
 
-	if (nob < 8) {
+	if (nob < LNET_PING_INFO_HDR_SIZE) {
 		CERROR("%s: ping info too short %d\n",
-		       libcfs_idstr(&id), nob);
+		       libcfs_idstr(id), nob);
 		goto fail_ping_buffer_decref;
 	}
 
@@ -5430,54 +5778,55 @@ static int lnet_ping(struct lnet_process_id id4, struct lnet_nid *src_nid,
 		lnet_swap_pinginfo(pbuf);
 	} else if (pbuf->pb_info.pi_magic != LNET_PROTO_PING_MAGIC) {
 		CERROR("%s: Unexpected magic %08x\n",
-		       libcfs_idstr(&id), pbuf->pb_info.pi_magic);
+		       libcfs_idstr(id), pbuf->pb_info.pi_magic);
 		goto fail_ping_buffer_decref;
 	}
 
 	if (!(pbuf->pb_info.pi_features & LNET_PING_FEAT_NI_STATUS)) {
 		CERROR("%s: ping w/o NI status: 0x%x\n",
-		       libcfs_idstr(&id), pbuf->pb_info.pi_features);
+		       libcfs_idstr(id), pbuf->pb_info.pi_features);
 		goto fail_ping_buffer_decref;
 	}
 
 	/* Test if smaller than lnet_pinginfo with just one pi_ni status info.
 	 * That one might contain size when large nids are used.
 	 */
-	if (nob < LNET_PING_INFO_SIZE(1)) {
+	if (nob < offsetof(struct lnet_ping_info, pi_ni[1])) {
 		CERROR("%s: Short reply %d(%lu min)\n",
-		       libcfs_idstr(&id), nob, LNET_PING_INFO_SIZE(1));
+		       libcfs_idstr(id), nob,
+		       offsetof(struct lnet_ping_info, pi_ni[1]));
 		goto fail_ping_buffer_decref;
 	}
 
-	if (pbuf->pb_info.pi_nnis < n_ids) {
-		n_ids = pbuf->pb_info.pi_nnis;
+	if (ping_info_count_entries(pbuf) < n_ids) {
+		n_ids = ping_info_count_entries(pbuf);
 		id_bytes = lnet_ping_info_size(&pbuf->pb_info);
 	}
 
 	if (nob < id_bytes) {
 		CERROR("%s: Short reply %d(%d expected)\n",
-		       libcfs_idstr(&id), nob, id_bytes);
+		       libcfs_idstr(id), nob, id_bytes);
 		goto fail_ping_buffer_decref;
 	}
 
-	rc = -EFAULT;		/* if I segv in copy_to_user()... */
-
-	memset(&tmpid, 0, sizeof(tmpid));
-	for (i = 0; i < n_ids; i++) {
-		tmpid.pid = pbuf->pb_info.pi_pid;
-		tmpid.nid = pbuf->pb_info.pi_ni[i].ns_nid;
-		if (copy_to_user(&ids[i], &tmpid, sizeof(tmpid)))
+	for (st = ping_iter_first(&pi, pbuf, &pid.nid);
+	     st;
+	     st = ping_iter_next(&pi, &pid.nid)) {
+		id = genradix_ptr_alloc(&plist->lgpl_list, i++, GFP_ATOMIC);
+		if (!id) {
+			rc = -ENOMEM;
 			goto fail_ping_buffer_decref;
-	}
-	rc = pbuf->pb_info.pi_nnis;
+		}
 
+		id->pid = pbuf->pb_info.pi_pid;
+		id->nid = pid.nid;
+	}
+	rc = i;
 fail_ping_buffer_decref:
 	lnet_ping_buffer_decref(pbuf);
 	return rc;
 }
 
-#undef LNET_PING_INFO_SIZE
-
 static int
 lnet_discover(struct lnet_process_id id4, u32 force,
 	      struct lnet_process_id __user *ids,
diff --git a/net/lnet/lnet/nidstrings.c b/net/lnet/lnet/nidstrings.c
index ac2aa973a412..b5a585507d6a 100644
--- a/net/lnet/lnet/nidstrings.c
+++ b/net/lnet/lnet/nidstrings.c
@@ -1170,6 +1170,30 @@ libcfs_idstr(struct lnet_processid *id)
 }
 EXPORT_SYMBOL(libcfs_idstr);
 
+int
+libcfs_strid(struct lnet_processid *id, const char *str)
+{
+	char *tmp = strchr(str, '-');
+
+	id->pid = LNET_PID_LUSTRE;
+	if (tmp &&
+	    strncmp("LNET_PID_ANY-", str, tmp - str) != 0) {
+		char pid[LNET_NIDSTR_SIZE];
+		int rc;
+
+		strscpy(pid, str, tmp - str);
+		rc = kstrtou32(pid, 10, &id->pid);
+		if (rc < 0)
+			return rc;
+		tmp++;
+	} else {
+		tmp = (char *)str;
+	}
+
+	return libcfs_strnid(&id->nid, tmp);
+}
+EXPORT_SYMBOL(libcfs_strid);
+
 int
 libcfs_str2anynid(lnet_nid_t *nidp, const char *str)
 {
diff --git a/net/lnet/lnet/peer.c b/net/lnet/lnet/peer.c
index a9759860a5cd..da1f8d4d67a3 100644
--- a/net/lnet/lnet/peer.c
+++ b/net/lnet/lnet/peer.c
@@ -2955,7 +2955,7 @@ u32 *ping_iter_next(struct lnet_ping_iter *pi, struct lnet_nid *nid)
 	return NULL;
 }
 
-static int ping_info_count_entries(struct lnet_ping_buffer *pbuf)
+int ping_info_count_entries(struct lnet_ping_buffer *pbuf)
 {
 	struct lnet_ping_iter pi;
 	u32 *st;
-- 
2.27.0

_______________________________________________
lustre-devel mailing list
[email protected]
http://lists.lustre.org/listinfo.cgi/lustre-devel-lustre.org
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.