Whamcloud - gitweb
LU-8915 lnet: migrate LNet selftest session handling to Netlink 98/43298/24
authorJames Simmons <jsimmons@infradead.org>
Mon, 17 Oct 2022 14:37:46 +0000 (10:37 -0400)
committerOleg Drokin <green@whamcloud.com>
Mon, 14 Nov 2022 08:24:28 +0000 (08:24 +0000)
The currently LNet selftest ioctl interface has a few issues which
can be resolved using Netlink. The first is the current API using
struct list_head is disliked by the Linux VFS maintainers. While
we technically don't need to use the struct list_head directly
its still confusing and passing pointers from userland to kernel
space is also frowned on.

Second issue that is exposed with debug kernels is that ioctl
handling done with the lstcon_ioctl_handler can easily end up
in a might_sleep state.

The new Netlink work is also needed for the IPv6 support. Update
the session handling to work with large NIDs. Internally use
struct lst_session_id which supports large NIDs instead of
struct lst_sid.

Lastly we have been wanting YAMl handling with LNet selftest
(LU-10975) which comes naturally with this work.

Test-Parameters: trivial testlist=lnet-selftest
Change-Id: I6ac56e51c6bfcf651a28388129020249166a7034
Signed-off-by: James Simmons <jsimmons@infradead.org>
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/43298
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Serguei Smirnov <ssmirnov@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
Reviewed-by: Frank Sehr <fsehr@whamcloud.com>
lnet/include/uapi/linux/lnet/lnetst.h
lnet/selftest/conctl.c
lnet/selftest/conrpc.c
lnet/selftest/console.c
lnet/selftest/console.h
lnet/selftest/framework.c
lnet/selftest/selftest.h
lnet/utils/Makefile.am
lnet/utils/lst.c

index 8749f8a..36668ae 100644 (file)
@@ -78,8 +78,6 @@ struct lst_sid {
        __s64           ses_stamp;      /* time stamp in milliseconds */
 };                                     /*** session id */
 
-extern struct lst_sid LST_INVALID_SID;
-
 struct lst_bid {
        __u64           bat_id;         /* unique id in session */
 };
@@ -534,4 +532,23 @@ struct sfw_counters {
        __u32 ping_errors;
 } __attribute__((packed));
 
+#define LNET_SELFTEST_GENL_NAME                "lnet_selftest"
+#define LNET_SELFTEST_GENL_VERSION     0x1
+
+/* enum lnet_selftest_commands       - Supported core LNet Selftest Netlink
+ *                                     commands
+ *
+ * @LNET_SELFTEST_CMD_UNSPEC:          unspecified command to catch errors
+ * @LNET_SELFTEST_CMD_SESSIONS:                command to manage sessions
+ */
+enum lnet_selftest_commands {
+       LNET_SELFTEST_CMD_UNSPEC        = 0,
+
+       LNET_SELFTEST_CMD_SESSIONS      = 1,
+
+       __LNET_SELFTEST_CMD_MAX_PLUS_ONE,
+};
+
+#define LNET_SELFTEST_CMD_MAX (__LNET_SELFTEST_CMD_MAX_PLUS_ONE - 1)
+
 #endif
index 9afbdae..185813e 100644 (file)
  * Author: Liang Zhen <liangzhen@clusterfs.com>
  */
 
+#include <libcfs/linux/linux-net.h>
 #include <libcfs/libcfs.h>
 #include <lnet/lib-lnet.h>
 #include "console.h"
 
 static int
-lst_session_new_ioctl(struct lstio_session_new_args *args)
-{
-       char *name;
-       int rc;
-
-       if (args->lstio_ses_idp == NULL || /* address for output sid */
-           args->lstio_ses_key == 0 || /* no key is specified */
-           args->lstio_ses_namep == NULL || /* session name */
-           args->lstio_ses_nmlen <= 0 ||
-           args->lstio_ses_nmlen > LST_NAME_SIZE)
-               return -EINVAL;
-
-       LIBCFS_ALLOC(name, args->lstio_ses_nmlen + 1);
-       if (name == NULL)
-               return -ENOMEM;
-
-       if (copy_from_user(name, args->lstio_ses_namep,
-                          args->lstio_ses_nmlen)) {
-               LIBCFS_FREE(name, args->lstio_ses_nmlen + 1);
-               return -EFAULT;
-       }
-
-       name[args->lstio_ses_nmlen] = 0;
-
-       rc = lstcon_session_new(name,
-                               args->lstio_ses_key,
-                               args->lstio_ses_feats,
-                               args->lstio_ses_timeout,
-                               args->lstio_ses_force,
-                               args->lstio_ses_idp);
-
-       LIBCFS_FREE(name, args->lstio_ses_nmlen + 1);
-       return rc;
-}
-
-static int
-lst_session_end_ioctl(struct lstio_session_end_args *args)
-{
-       if (args->lstio_ses_key != console_session.ses_key)
-               return -EACCES;
-
-       return lstcon_session_end();
-}
-
-static int
-lst_session_info_ioctl(struct lstio_session_info_args *args)
-{
-       /* no checking of key */
-
-       if (args->lstio_ses_idp == NULL || /* address for ouput sid */
-           args->lstio_ses_keyp == NULL || /* address for ouput key */
-           args->lstio_ses_featp == NULL || /* address for ouput features */
-           args->lstio_ses_ndinfo == NULL || /* address for output ndinfo */
-           args->lstio_ses_namep == NULL || /* address for ouput name */
-           args->lstio_ses_nmlen <= 0 ||
-           args->lstio_ses_nmlen > LST_NAME_SIZE)
-               return -EINVAL;
-
-       return lstcon_session_info(args->lstio_ses_idp,
-                                  args->lstio_ses_keyp,
-                                  args->lstio_ses_featp,
-                                  args->lstio_ses_ndinfo,
-                                  args->lstio_ses_namep,
-                                  args->lstio_ses_nmlen);
-}
-
-static int
 lst_debug_ioctl(struct lstio_debug_args *args)
 {
        char *name = NULL;
@@ -859,13 +793,11 @@ lstcon_ioctl_entry(struct notifier_block *nb,
 
        switch (opc) {
        case LSTIO_SESSION_NEW:
-               rc = lst_session_new_ioctl((struct lstio_session_new_args *)buf);
-               break;
+               fallthrough;
        case LSTIO_SESSION_END:
-               rc = lst_session_end_ioctl((struct lstio_session_end_args *)buf);
-               break;
+               fallthrough;
        case LSTIO_SESSION_INFO:
-               rc = lst_session_info_ioctl((struct lstio_session_info_args *)buf);
+               rc = -EOPNOTSUPP;
                break;
        case LSTIO_DEBUG:
                rc = lst_debug_ioctl((struct lstio_debug_args *)buf);
@@ -927,3 +859,277 @@ out_free_buf:
 err:
        return notifier_from_ioctl_errno(rc);
 }
+
+static struct genl_family lst_family;
+
+static const struct ln_key_list lst_session_keys = {
+       .lkl_maxattr                    = LNET_SELFTEST_SESSION_MAX,
+       .lkl_list                       = {
+               [LNET_SELFTEST_SESSION_HDR]     = {
+                       .lkp_value              = "session",
+                       .lkp_key_format         = LNKF_MAPPING,
+                       .lkp_data_type          = NLA_NUL_STRING,
+               },
+               [LNET_SELFTEST_SESSION_NAME]    = {
+                       .lkp_value              = "name",
+                       .lkp_data_type          = NLA_STRING,
+               },
+               [LNET_SELFTEST_SESSION_KEY]     = {
+                       .lkp_value              = "key",
+                       .lkp_data_type          = NLA_U32,
+               },
+               [LNET_SELFTEST_SESSION_TIMESTAMP] = {
+                       .lkp_value              = "timestamp",
+                       .lkp_data_type          = NLA_S64,
+               },
+               [LNET_SELFTEST_SESSION_NID]     = {
+                       .lkp_value              = "nid",
+                       .lkp_data_type          = NLA_STRING,
+               },
+               [LNET_SELFTEST_SESSION_NODE_COUNT] = {
+                       .lkp_value              = "nodes",
+                       .lkp_data_type          = NLA_U16,
+               },
+       },
+};
+
+static int lst_sessions_show_dump(struct sk_buff *msg,
+                                 struct netlink_callback *cb)
+{
+       const struct ln_key_list *all[] = {
+               &lst_session_keys, NULL
+       };
+#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;
+       unsigned int node_count = 0;
+       struct lstcon_ndlink *ndl;
+       int flag = NLM_F_MULTI;
+       int rc = 0;
+       void *hdr;
+
+#ifdef HAVE_NL_DUMP_WITH_EXT_ACK
+       extack = cb->extack;
+#endif
+       if (console_session.ses_state != LST_SESSION_ACTIVE) {
+               NL_SET_ERR_MSG(extack, "session is not active");
+               GOTO(out_unlock, rc = -ESRCH);
+       }
+
+       list_for_each_entry(ndl, &console_session.ses_ndl_list, ndl_link)
+               node_count++;
+
+       rc = lnet_genl_send_scalar_list(msg, portid, seq, &lst_family,
+                                       NLM_F_CREATE | NLM_F_MULTI,
+                                       LNET_SELFTEST_CMD_SESSIONS, all);
+       if (rc < 0) {
+               NL_SET_ERR_MSG(extack, "failed to send key table");
+               GOTO(out_unlock, rc);
+       }
+
+       if (console_session.ses_force)
+               flag |= NLM_F_REPLACE;
+
+       hdr = genlmsg_put(msg, portid, seq, &lst_family, flag,
+                         LNET_SELFTEST_CMD_SESSIONS);
+       if (!hdr) {
+               NL_SET_ERR_MSG(extack, "failed to send values");
+               genlmsg_cancel(msg, hdr);
+               GOTO(out_unlock, rc = -EMSGSIZE);
+       }
+
+       nla_put_string(msg, LNET_SELFTEST_SESSION_NAME,
+                      console_session.ses_name);
+       nla_put_u32(msg, LNET_SELFTEST_SESSION_KEY,
+                   console_session.ses_key);
+       nla_put_u64_64bit(msg, LNET_SELFTEST_SESSION_TIMESTAMP,
+                         console_session.ses_id.ses_stamp,
+                         LNET_SELFTEST_SESSION_PAD);
+       nla_put_string(msg, LNET_SELFTEST_SESSION_NID,
+                      libcfs_nidstr(&console_session.ses_id.ses_nid));
+       nla_put_u16(msg, LNET_SELFTEST_SESSION_NODE_COUNT,
+                   node_count);
+       genlmsg_end(msg, hdr);
+out_unlock:
+       return lnet_nl_send_error(cb->skb, portid, seq, rc);
+}
+
+static int lst_sessions_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+       struct sk_buff *msg = NULL;
+       int rc = 0;
+
+       mutex_lock(&console_session.ses_mutex);
+
+       console_session.ses_laststamp = ktime_get_real_seconds();
+
+       if (console_session.ses_shutdown) {
+               GENL_SET_ERR_MSG(info, "session is shutdown");
+               GOTO(out_unlock, rc = -ESHUTDOWN);
+       }
+
+       if (console_session.ses_expired)
+               lstcon_session_end();
+
+       if (!(info->nlhdr->nlmsg_flags & NLM_F_CREATE) &&
+           console_session.ses_state == LST_SESSION_NONE) {
+               GENL_SET_ERR_MSG(info, "session is not active");
+               GOTO(out_unlock, rc = -ESRCH);
+       }
+
+       memset(&console_session.ses_trans_stat, 0,
+              sizeof(struct lstcon_trans_stat));
+
+       if (!(info->nlhdr->nlmsg_flags & NLM_F_CREATE)) {
+               lstcon_session_end();
+               GOTO(out_unlock, rc);
+       }
+
+       if (info->attrs[LN_SCALAR_ATTR_LIST]) {
+               struct genlmsghdr *gnlh = nlmsg_data(info->nlhdr);
+               const struct ln_key_list *all[] = {
+                       &lst_session_keys, NULL
+               };
+               char name[LST_NAME_SIZE];
+               struct nlmsghdr *nlh;
+               struct nlattr *item;
+               bool force = false;
+               s64 timeout = 300;
+               void *hdr;
+               int rem;
+
+               if (info->nlhdr->nlmsg_flags & NLM_F_REPLACE)
+                       force = true;
+
+               nla_for_each_nested(item, info->attrs[LN_SCALAR_ATTR_LIST],
+                                   rem) {
+                       if (nla_type(item) != LN_SCALAR_ATTR_VALUE)
+                               continue;
+
+                       if (nla_strcmp(item, "name") == 0) {
+                               ssize_t len;
+
+                               item = nla_next(item, &rem);
+                               if (nla_type(item) != LN_SCALAR_ATTR_VALUE)
+                                       GOTO(err_conf, rc = -EINVAL);
+
+                               len = nla_strscpy(name, item, sizeof(name));
+                               if (len < 0)
+                                       rc = len;
+                       } else if (nla_strcmp(item, "timeout") == 0) {
+                               item = nla_next(item, &rem);
+                               if (nla_type(item) !=
+                                   LN_SCALAR_ATTR_INT_VALUE)
+                                       GOTO(err_conf, rc = -EINVAL);
+
+                               timeout = nla_get_s64(item);
+                               if (timeout < 0)
+                                       rc = -ERANGE;
+                       }
+                       if (rc < 0) {
+err_conf:
+                               GENL_SET_ERR_MSG(info,
+                                                "failed to get config");
+                               GOTO(out_unlock, rc);
+                       }
+               }
+
+               rc = lstcon_session_new(name, info->nlhdr->nlmsg_pid,
+                                       gnlh->version, timeout,
+                                       force);
+               if (rc < 0) {
+                       GENL_SET_ERR_MSG(info, "new session creation failed");
+                       lstcon_session_end();
+                       GOTO(out_unlock, rc);
+               }
+
+               msg = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+               if (!msg) {
+                       GENL_SET_ERR_MSG(info, "msg allocation failed");
+                       GOTO(out_unlock, rc = -ENOMEM);
+               }
+
+               rc = lnet_genl_send_scalar_list(msg, info->snd_portid,
+                                               info->snd_seq, &lst_family,
+                                               NLM_F_CREATE | NLM_F_MULTI,
+                                               LNET_SELFTEST_CMD_SESSIONS,
+                                               all);
+               if (rc < 0) {
+                       GENL_SET_ERR_MSG(info, "failed to send key table");
+                       GOTO(out_unlock, rc);
+               }
+
+               hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
+                                 &lst_family, NLM_F_MULTI,
+                                 LNET_SELFTEST_CMD_SESSIONS);
+               if (!hdr) {
+                       GENL_SET_ERR_MSG(info, "failed to send values");
+                       genlmsg_cancel(msg, hdr);
+                       GOTO(out_unlock, rc = -EMSGSIZE);
+               }
+
+               nla_put_string(msg, LNET_SELFTEST_SESSION_NAME,
+                              console_session.ses_name);
+               nla_put_u32(msg, LNET_SELFTEST_SESSION_KEY,
+                           console_session.ses_key);
+               nla_put_u64_64bit(msg, LNET_SELFTEST_SESSION_TIMESTAMP,
+                                 console_session.ses_id.ses_stamp,
+                                 LNET_SELFTEST_SESSION_PAD);
+               nla_put_string(msg, LNET_SELFTEST_SESSION_NID,
+                              libcfs_nidstr(&console_session.ses_id.ses_nid));
+               nla_put_u16(msg, LNET_SELFTEST_SESSION_NODE_COUNT, 0);
+
+               genlmsg_end(msg, hdr);
+
+               nlh = nlmsg_put(msg, info->snd_portid, info->snd_seq,
+                               NLMSG_DONE, 0, NLM_F_MULTI);
+               if (!nlh) {
+                       GENL_SET_ERR_MSG(info, "failed to complete message");
+                       genlmsg_cancel(msg, hdr);
+                       GOTO(out_unlock, rc = -ENOMEM);
+               }
+               rc = genlmsg_reply(msg, info);
+               if (rc)
+                       GENL_SET_ERR_MSG(info, "failed to send reply");
+       }
+out_unlock:
+       if (rc < 0 && msg)
+               nlmsg_free(msg);
+       mutex_unlock(&console_session.ses_mutex);
+       return rc;
+}
+
+static const struct genl_multicast_group lst_mcast_grps[] = {
+       { .name = "sessions",           },
+};
+
+static const struct genl_ops lst_genl_ops[] = {
+       {
+               .cmd            = LNET_SELFTEST_CMD_SESSIONS,
+               .dumpit         = lst_sessions_show_dump,
+               .doit           = lst_sessions_cmd,
+       },
+};
+
+static struct genl_family lst_family = {
+       .name           = LNET_SELFTEST_GENL_NAME,
+       .version        = LNET_SELFTEST_GENL_VERSION,
+       .maxattr        = LN_SCALAR_MAX,
+       .module         = THIS_MODULE,
+       .ops            = lst_genl_ops,
+       .n_ops          = ARRAY_SIZE(lst_genl_ops),
+       .mcgrps         = lst_mcast_grps,
+       .n_mcgrps       = ARRAY_SIZE(lst_mcast_grps),
+};
+
+int lstcon_init_netlink(void)
+{
+       return genl_register_family(&lst_family);
+}
+
+void lstcon_fini_netlink(void)
+{
+       genl_unregister_family(&lst_family);
+}
index d2147e6..927d099 100644 (file)
@@ -600,8 +600,10 @@ lstcon_sesrpc_prep(struct lstcon_node *nd, int transop,
                         return rc;
 
                 msrq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.mksn_reqst;
-                msrq->mksn_sid     = console_session.ses_id;
-                msrq->mksn_force   = console_session.ses_force;
+               msrq->mksn_sid.ses_stamp = console_session.ses_id.ses_stamp;
+               msrq->mksn_sid.ses_nid =
+                       lnet_nid_to_nid4(&console_session.ses_id.ses_nid);
+               msrq->mksn_force = console_session.ses_force;
                strlcpy(msrq->mksn_name, console_session.ses_name,
                        sizeof(msrq->mksn_name));
                 break;
@@ -612,9 +614,11 @@ lstcon_sesrpc_prep(struct lstcon_node *nd, int transop,
                 if (rc != 0)
                         return rc;
 
-                rsrq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.rmsn_reqst;
-                rsrq->rmsn_sid = console_session.ses_id;
-                break;
+               rsrq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.rmsn_reqst;
+               rsrq->rmsn_sid.ses_stamp = console_session.ses_id.ses_stamp;
+               rsrq->rmsn_sid.ses_nid =
+                       lnet_nid_to_nid4(&console_session.ses_id.ses_nid);
+               break;
 
         default:
                 LBUG();
@@ -636,7 +640,9 @@ lstcon_dbgrpc_prep(struct lstcon_node *nd, unsigned int feats,
 
         drq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.dbg_reqst;
 
-        drq->dbg_sid   = console_session.ses_id;
+       drq->dbg_sid.ses_stamp = console_session.ses_id.ses_stamp;
+       drq->dbg_sid.ses_nid =
+               lnet_nid_to_nid4(&console_session.ses_id.ses_nid);
         drq->dbg_flags = 0;
 
         return rc;
@@ -656,7 +662,9 @@ lstcon_batrpc_prep(struct lstcon_node *nd, int transop, unsigned int feats,
 
         brq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.bat_reqst;
 
-        brq->bar_sid     = console_session.ses_id;
+       brq->bar_sid.ses_stamp = console_session.ses_id.ses_stamp;
+       brq->bar_sid.ses_nid =
+               lnet_nid_to_nid4(&console_session.ses_id.ses_nid);
         brq->bar_bid     = tsb->tsb_id;
         brq->bar_testidx = tsb->tsb_index;
         brq->bar_opc     = transop == LST_TRANS_TSBRUN ? SRPC_BATCH_OPC_RUN :
@@ -688,7 +696,10 @@ lstcon_statrpc_prep(struct lstcon_node *nd, unsigned int feats,
 
         srq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.stat_reqst;
 
-        srq->str_sid  = console_session.ses_id;
+
+       srq->str_sid.ses_stamp = console_session.ses_id.ses_stamp;
+       srq->str_sid.ses_nid =
+               lnet_nid_to_nid4(&console_session.ses_id.ses_nid);
         srq->str_type = 0; /* XXX remove it */
 
         return 0;
@@ -878,7 +889,9 @@ lstcon_testrpc_prep(struct lstcon_node *nd, int transop, unsigned int feats,
                 trq->tsr_loop  = test->tes_loop;
        }
 
-        trq->tsr_sid        = console_session.ses_id;
+       trq->tsr_sid.ses_stamp = console_session.ses_id.ses_stamp;
+       trq->tsr_sid.ses_nid =
+               lnet_nid_to_nid4(&console_session.ses_id.ses_nid);
         trq->tsr_bid        = test->tes_hdr.tsb_id;
         trq->tsr_concur     = test->tes_concur;
         trq->tsr_is_client  = (transop == LST_TRANS_TSBCLIADD) ? 1 : 0;
@@ -1259,7 +1272,9 @@ lstcon_rpc_pinger(void *arg)
 
                 drq = &crpc->crp_rpc->crpc_reqstmsg.msg_body.dbg_reqst;
 
-                drq->dbg_sid   = console_session.ses_id;
+               drq->dbg_sid.ses_stamp = console_session.ses_id.ses_stamp;
+               drq->dbg_sid.ses_nid =
+                       lnet_nid_to_nid4(&console_session.ses_id.ses_nid);
                 drq->dbg_flags = 0;
 
                 lstcon_rpc_trans_addreq(trans, crpc);
index 25de1f2..1251960 100644 (file)
@@ -1684,27 +1684,32 @@ lstcon_nodes_debug(int timeout, int count,
 }
 
 int
-lstcon_session_match(struct lst_sid sid)
+lstcon_session_match(struct lst_sid id)
 {
-        return (console_session.ses_id.ses_nid   == sid.ses_nid &&
-                console_session.ses_id.ses_stamp == sid.ses_stamp) ?  1: 0;
+       struct lst_session_id sid;
+
+       sid.ses_stamp = id.ses_stamp;
+       lnet_nid4_to_nid(id.ses_nid, &sid.ses_nid);
+
+       return (nid_same(&console_session.ses_id.ses_nid, &sid.ses_nid) &&
+               console_session.ses_id.ses_stamp == sid.ses_stamp) ?  1 : 0;
 }
 
 static void
-lstcon_new_session_id(struct lst_sid *sid)
+lstcon_new_session_id(struct lst_session_id *sid)
 {
        struct lnet_processid id;
 
        LASSERT(console_session.ses_state == LST_SESSION_NONE);
 
        LNetGetId(1, &id);
-       sid->ses_nid = lnet_nid_to_nid4(&id.nid);
+       sid->ses_nid = id.nid;
        sid->ses_stamp = div_u64(ktime_get_ns(), NSEC_PER_MSEC);
 }
 
 int
 lstcon_session_new(char *name, int key, unsigned feats,
-                  int timeout, int force, struct lst_sid __user *sid_up)
+                  int timeout, int force)
 {
         int     rc = 0;
         int     i;
@@ -1736,7 +1741,6 @@ lstcon_session_new(char *name, int key, unsigned feats,
        lstcon_new_session_id(&console_session.ses_id);
 
        console_session.ses_key     = key;
-       console_session.ses_state   = LST_SESSION_ACTIVE;
        console_session.ses_force   = !!force;
        console_session.ses_features = feats;
        console_session.ses_feats_updated = 0;
@@ -1762,52 +1766,12 @@ lstcon_session_new(char *name, int key, unsigned feats,
                 return rc;
         }
 
-       if (copy_to_user(sid_up, &console_session.ses_id,
-                            sizeof(struct lst_sid)) == 0)
-                return rc;
-
-        lstcon_session_end();
-
-        return -EFAULT;
-}
-
-int
-lstcon_session_info(struct lst_sid __user *sid_up, int __user *key_up,
-                   unsigned __user *featp,
-                   struct lstcon_ndlist_ent __user *ndinfo_up,
-                   char __user *name_up, int len)
-{
-       struct lstcon_ndlist_ent *entp;
-       struct lstcon_ndlink *ndl;
-       int rc = 0;
+       console_session.ses_state = LST_SESSION_ACTIVE;
 
-        if (console_session.ses_state != LST_SESSION_ACTIVE)
-                return -ESRCH;
-
-        LIBCFS_ALLOC(entp, sizeof(*entp));
-        if (entp == NULL)
-                return -ENOMEM;
-
-       list_for_each_entry(ndl, &console_session.ses_ndl_list, ndl_link)
-               LST_NODE_STATE_COUNTER(ndl->ndl_node, entp);
-
-       if (copy_to_user(sid_up, &console_session.ses_id,
-                        sizeof(struct lst_sid)) ||
-           copy_to_user(key_up, &console_session.ses_key,
-                            sizeof(*key_up)) ||
-           copy_to_user(featp, &console_session.ses_features,
-                            sizeof(*featp)) ||
-           copy_to_user(ndinfo_up, entp, sizeof(*entp)) ||
-           copy_to_user(name_up, console_session.ses_name, len))
-                rc = -EFAULT;
-
-        LIBCFS_FREE(entp, sizeof(*entp));
-
-        return rc;
+       return rc;
 }
 
-int
-lstcon_session_end(void)
+int lstcon_session_end(void)
 {
        struct lstcon_rpc_trans *trans;
        struct lstcon_group *grp;
@@ -1915,9 +1879,10 @@ lstcon_acceptor_handle(struct srpc_server_rpc *rpc)
 
        mutex_lock(&console_session.ses_mutex);
 
-        jrep->join_sid = console_session.ses_id;
+       jrep->join_sid.ses_stamp = console_session.ses_id.ses_stamp;
+       jrep->join_sid.ses_nid = lnet_nid_to_nid4(&console_session.ses_id.ses_nid);
 
-        if (console_session.ses_id.ses_nid == LNET_NID_ANY) {
+       if (LNET_NID_IS_ANY(&console_session.ses_id.ses_nid)) {
                 jrep->join_status = ESRCH;
                 goto out;
         }
@@ -2049,13 +2014,20 @@ lstcon_console_init(void)
                 goto out;
         }
 
+       rc = lstcon_init_netlink();
+       if (rc < 0)
+               goto out;
+
        rc = blocking_notifier_chain_register(&libcfs_ioctl_list,
                                              &lstcon_ioctl_handler);
-       if (rc == 0) {
-               lstcon_rpc_module_init();
-               return 0;
+       if (rc < 0) {
+               lstcon_fini_netlink();
+               goto out;
        }
 
+       lstcon_rpc_module_init();
+       return 0;
+
 out:
        srpc_shutdown_service(&lstcon_acceptor_service);
        srpc_remove_service(&lstcon_acceptor_service);
@@ -2074,6 +2046,7 @@ lstcon_console_fini(void)
 
        blocking_notifier_chain_unregister(&libcfs_ioctl_list,
                                           &lstcon_ioctl_handler);
+       lstcon_fini_netlink();
 
        mutex_lock(&console_session.ses_mutex);
 
index 13144cc..fce9f19 100644 (file)
@@ -148,8 +148,8 @@ struct lstcon_test {
 
 struct lstcon_session {
        struct mutex            ses_mutex;      /* only 1 thread in session */
-       struct lst_sid          ses_id;         /* global session id */
-        int                     ses_key;        /* local session key */
+       struct lst_session_id   ses_id;         /* global session id */
+       u32                     ses_key;        /* local session key */
         int                     ses_state;      /* state of session */
         int                     ses_timeout;    /* timeout in seconds */
        time64_t                ses_laststamp;  /* last operation stamp (seconds) */
@@ -198,11 +198,7 @@ lstcon_id2hash(struct lnet_process_id id, struct list_head *hash)
 
 extern int lstcon_session_match(struct lst_sid sid);
 extern int lstcon_session_new(char *name, int key, unsigned version,
-                             int timeout, int flags, struct lst_sid __user *sid_up);
-extern int lstcon_session_info(struct lst_sid __user *sid_up, int __user *key,
-                              unsigned __user *verp,
-                              struct lstcon_ndlist_ent __user *entp,
-                              char __user *name_up, int len);
+                             int timeout, int flags);
 extern int lstcon_session_end(void);
 extern int lstcon_session_debug(int timeout,
                                struct list_head __user *result_up);
@@ -256,7 +252,11 @@ extern int lstcon_test_add(char *batch_name, int type, int loop,
 
 int lstcon_ioctl_entry(struct notifier_block *nb,
                       unsigned long cmd, void *vdata);
+
 int lstcon_console_init(void);
 int lstcon_console_fini(void);
 
+int lstcon_init_netlink(void);
+void lstcon_fini_netlink(void);
+
 #endif
index 7e048ad..4b65968 100644 (file)
@@ -38,7 +38,7 @@
 
 #include "selftest.h"
 
-struct lst_sid LST_INVALID_SID = { .ses_nid = LNET_NID_ANY, .ses_stamp = -1};
+struct lst_session_id LST_INVALID_SID = { .ses_nid = LNET_ANY_NID, .ses_stamp = -1};
 
 static int session_timeout = 100;
 module_param(session_timeout, int, 0444);
@@ -252,7 +252,7 @@ sfw_session_expired (void *data)
         LASSERT (sn == sfw_data.fw_session);
 
        CWARN ("Session expired! sid: %s-%llu, name: %s\n",
-               libcfs_nid2str(sn->sn_id.ses_nid),
+              libcfs_nidstr(&sn->sn_id.ses_nid),
                sn->sn_id.ses_stamp, &sn->sn_name[0]);
 
         sn->sn_timer_active = 0;
@@ -276,7 +276,8 @@ sfw_init_session(struct sfw_session *sn, struct lst_sid sid,
        strlcpy(&sn->sn_name[0], name, sizeof(sn->sn_name));
 
        sn->sn_timer_active = 0;
-       sn->sn_id = sid;
+       sn->sn_id.ses_stamp = sid.ses_stamp;
+       lnet_nid4_to_nid(sid.ses_nid, &sn->sn_id.ses_nid);
        sn->sn_features = features;
        sn->sn_timeout = session_timeout;
        sn->sn_started = ktime_get();
@@ -368,6 +369,18 @@ sfw_bid2batch(struct lst_bid bid)
        return bat;
 }
 
+static struct lst_sid get_old_sid(struct sfw_session *sn)
+{
+       struct lst_sid sid = { .ses_nid = LNET_NID_ANY, .ses_stamp = -1 };
+
+       if (sn) {
+               sid.ses_stamp = sn->sn_id.ses_stamp;
+               sid.ses_nid = lnet_nid_to_nid4(&sn->sn_id.ses_nid);
+       }
+
+       return sid;
+}
+
 static int
 sfw_get_stats(struct srpc_stat_reqst *request, struct srpc_stat_reply *reply)
 {
@@ -375,7 +388,7 @@ sfw_get_stats(struct srpc_stat_reqst *request, struct srpc_stat_reply *reply)
        struct sfw_counters *cnt = &reply->str_fw;
        struct sfw_batch *bat;
 
-        reply->str_sid = (sn == NULL) ? LST_INVALID_SID : sn->sn_id;
+       reply->str_sid = get_old_sid(sn);
 
         if (request->str_sid.ses_nid == LNET_NID_ANY) {
                 reply->str_status = EINVAL;
@@ -416,14 +429,14 @@ sfw_make_session(struct srpc_mksn_reqst *request, struct srpc_mksn_reply *reply)
        int cplen = 0;
 
         if (request->mksn_sid.ses_nid == LNET_NID_ANY) {
-                reply->mksn_sid = (sn == NULL) ? LST_INVALID_SID : sn->sn_id;
+               reply->mksn_sid = get_old_sid(sn);
                 reply->mksn_status = EINVAL;
                 return 0;
         }
 
         if (sn != NULL) {
                 reply->mksn_status  = 0;
-                reply->mksn_sid     = sn->sn_id;
+               reply->mksn_sid = get_old_sid(sn);
                 reply->mksn_timeout = sn->sn_timeout;
 
                 if (sfw_sid_equal(request->mksn_sid, sn->sn_id)) {
@@ -471,7 +484,7 @@ sfw_make_session(struct srpc_mksn_reqst *request, struct srpc_mksn_reply *reply)
        spin_unlock(&sfw_data.fw_lock);
 
        reply->mksn_status  = 0;
-       reply->mksn_sid     = sn->sn_id;
+       reply->mksn_sid = get_old_sid(sn);
        reply->mksn_timeout = sn->sn_timeout;
        return 0;
 }
@@ -482,7 +495,7 @@ sfw_remove_session(struct srpc_rmsn_reqst *request,
 {
        struct sfw_session *sn = sfw_data.fw_session;
 
-        reply->rmsn_sid = (sn == NULL) ? LST_INVALID_SID : sn->sn_id;
+       reply->rmsn_sid = get_old_sid(sn);
 
         if (request->rmsn_sid.ses_nid == LNET_NID_ANY) {
                 reply->rmsn_status = EINVAL;
@@ -504,7 +517,7 @@ sfw_remove_session(struct srpc_rmsn_reqst *request,
        spin_unlock(&sfw_data.fw_lock);
 
        reply->rmsn_status = 0;
-       reply->rmsn_sid    = LST_INVALID_SID;
+       reply->rmsn_sid = get_old_sid(NULL);
        LASSERT(sfw_data.fw_session == NULL);
        return 0;
 }
@@ -517,12 +530,12 @@ sfw_debug_session(struct srpc_debug_reqst *request,
 
         if (sn == NULL) {
                 reply->dbg_status = ESRCH;
-                reply->dbg_sid    = LST_INVALID_SID;
+               reply->dbg_sid = get_old_sid(NULL);
                 return 0;
        }
 
        reply->dbg_status  = 0;
-       reply->dbg_sid     = sn->sn_id;
+       reply->dbg_sid = get_old_sid(sn);
        reply->dbg_timeout = sn->sn_timeout;
        if (strlcpy(reply->dbg_name, &sn->sn_name[0], sizeof(reply->dbg_name))
            >= sizeof(reply->dbg_name))
@@ -1126,7 +1139,7 @@ sfw_add_test(struct srpc_server_rpc *rpc)
        struct sfw_batch *bat;
 
         request = &rpc->srpc_reqstbuf->buf_msg.msg_body.tes_reqst;
-        reply->tsr_sid = (sn == NULL) ? LST_INVALID_SID : sn->sn_id;
+       reply->tsr_sid = get_old_sid(sn);
 
         if (request->tsr_loop == 0 ||
             request->tsr_concur == 0 ||
@@ -1194,7 +1207,7 @@ sfw_control_batch(struct srpc_batch_reqst *request,
         int            rc = 0;
        struct sfw_batch *bat;
 
-        reply->bar_sid = (sn == NULL) ? LST_INVALID_SID : sn->sn_id;
+       reply->bar_sid = get_old_sid(sn);
 
         if (sn == NULL || !sfw_sid_equal(request->bar_sid, sn->sn_id)) {
                 reply->bar_status = ESRCH;
@@ -1274,7 +1287,9 @@ sfw_handle_server_rpc(struct srpc_server_rpc *rpc)
                                "features of current session: %x/%x\n",
                                request->msg_ses_feats, sn->sn_features);
                        reply->msg_body.reply.status = EPROTO;
-                       reply->msg_body.reply.sid    = sn->sn_id;
+                       reply->msg_body.reply.sid.ses_stamp = sn->sn_id.ses_stamp;
+                       reply->msg_body.reply.sid.ses_nid =
+                               lnet_nid_to_nid4(&sn->sn_id.ses_nid);
                        goto out;
                }
 
index f19685d..94c4afe 100644 (file)
 #define MADE_WITHOUT_COMPROMISE
 #endif
 
+/* enum lnet_selftest_session_attrs   - LNet selftest session Netlink
+ *                                     attributes
+ *
+ *  @LNET_SELFTEST_SESSION_UNSPEC:     unspecified attribute to catch errors
+ *  @LNET_SELFTEST_SESSION_PAD:                padding for 64-bit attributes, ignore
+ *
+ *  @LENT_SELFTEST_SESSION_HDR:                Netlink group this data is for
+ *                                     (NLA_NUL_STRING)
+ *  @LNET_SELFTEST_SESSION_NAME:       name of this session (NLA_STRING)
+ *  @LNET_SELFTEST_SESSION_KEY:                key used to represent the session
+ *                                     (NLA_U32)
+ *  @LNET_SELFTEST_SESSION_TIMESTAMP:  timestamp when the session was created
+ *                                     (NLA_S64)
+ *  @LNET_SELFTEST_SESSION_NID:                NID of the node selftest ran on
+ *                                     (NLA_STRING)
+ *  @LNET_SELFTEST_SESSION_NODE_COUNT: Number of nodes in use (NLA_U16)
+ */
+enum lnet_selftest_session_attrs {
+       LNET_SELFTEST_SESSION_UNSPEC = 0,
+       LNET_SELFTEST_SESSION_PAD = LNET_SELFTEST_SESSION_UNSPEC,
+
+       LNET_SELFTEST_SESSION_HDR,
+       LNET_SELFTEST_SESSION_NAME,
+       LNET_SELFTEST_SESSION_KEY,
+       LNET_SELFTEST_SESSION_TIMESTAMP,
+       LNET_SELFTEST_SESSION_NID,
+       LNET_SELFTEST_SESSION_NODE_COUNT,
+
+       __LNET_SELFTEST_SESSION_MAX_PLUS_ONE,
+};
+
+#define LNET_SELFTEST_SESSION_MAX      (__LNET_SELFTEST_SESSION_MAX_PLUS_ONE - 1)
 
 #define SWI_STATE_NEWBORN                  0
 #define SWI_STATE_REPLY_SUBMITTED          1
@@ -318,10 +350,17 @@ struct srpc_service {
        int              (*sv_bulk_ready)(struct srpc_server_rpc *, int);
 };
 
+struct lst_session_id {
+       s64                     ses_stamp;      /* time stamp in milliseconds */
+       struct lnet_nid         ses_nid;        /* nid of console node */
+};                                             /*** session id (large addr) */
+
+extern struct lst_session_id LST_INVALID_SID;
+
 struct sfw_session {
        /* chain on fw_zombie_sessions */
        struct list_head        sn_list;
-       struct lst_sid          sn_id;          /* unique identifier */
+       struct lst_session_id   sn_id;          /* unique identifier */
        /* # seconds' inactivity to expire */
        unsigned int            sn_timeout;
        int                     sn_timer_active;
@@ -335,8 +374,16 @@ struct sfw_session {
        ktime_t                 sn_started;
 };
 
-#define sfw_sid_equal(sid0, sid1)     ((sid0).ses_nid == (sid1).ses_nid && \
-                                       (sid0).ses_stamp == (sid1).ses_stamp)
+static inline int sfw_sid_equal(struct lst_sid sid0,
+                               struct lst_session_id sid1)
+{
+       struct lnet_nid ses_nid;
+
+       lnet_nid4_to_nid(sid0.ses_nid, &ses_nid);
+
+       return ((sid0.ses_stamp == sid1.ses_stamp) &&
+               nid_same(&ses_nid, &sid1.ses_nid));
+}
 
 struct sfw_batch {
        struct list_head        bat_list;       /* chain on sn_batches */
index 664534a..a5c4386 100644 (file)
@@ -45,7 +45,7 @@ routerstat_LDADD = $(top_builddir)/lnet/utils/lnetconfig/liblnetconfig.la
 lst_SOURCES = lst.c
 lst_CFLAGS = -fPIC -D_LINUX_TIME_H -D_GNU_SOURCE $(LIBNL3_CFLAGS)
 lst_LDADD = $(top_builddir)/lnet/utils/lnetconfig/liblnetconfig.la \
-           $(LIBEFENCE)
+           $(LIBEFENCE) $(LIBNL3_LIBS) -lyaml
 
 if TESTS
 sbin_PROGRAMS += wirecheck
index 97eeb9f..1017b4e 100644 (file)
 #include "lnetconfig/liblnetconfig.h"
 
 struct lst_sid LST_INVALID_SID = { .ses_nid = LNET_NID_ANY, .ses_stamp = -1 };
-static struct lst_sid session_id;
-static int                 session_key;
+static unsigned int session_key;
 static int lst_list_commands(int argc, char **argv);
 
 /* All nodes running 2.6.50 or later understand feature LST_FEAT_BULK_LEN */
-static unsigned                session_features = LST_FEATS_MASK;
+static unsigned int session_features = LST_FEATS_MASK;
 static struct lstcon_trans_stat        trans_stat;
 
 typedef struct list_string {
@@ -516,6 +515,265 @@ lst_ioctl(unsigned int opc, void *buf, int len)
         return 0;
 }
 
+int lst_yaml_session(const char *label, const char *timeout, int nlflags,
+                    const char *errmsg)
+{
+       struct lstcon_ndlist_ent ndinfo = { };
+       struct lst_sid sid = LST_INVALID_SID;
+       /* nlflags being zero means we are destroying the session.
+        * No parsing of reply needed.
+        */
+       bool done = nlflags ? false : true;
+       char nid[LNET_NIDSTR_SIZE];
+       char name[LST_NAME_SIZE];
+       unsigned int key = 0;
+       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 -1;
+
+       /* Note: NL_AUTO_PID == zero which we use by default for the
+        * session_key when creating a new session. This is considered
+        * an invalid key so we need to get the real session key from
+        * the yaml parser yet to be created. If the user did request
+        * a specific session key then set the socket's port id to this
+        * value.
+        */
+       if (session_key)
+               nl_socket_set_local_port(sk, session_key);
+
+       /* Setup reply parser to recieve Netlink packets */
+       rc = yaml_parser_initialize(&reply);
+       if (rc == 0) {
+               nl_socket_free(sk);
+               return -1;
+       }
+
+       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_SESSIONS,
+                                            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 *)"sessions",
+                                    strlen("sessions"), 1, 0,
+                                    YAML_PLAIN_SCALAR_STYLE);
+       rc = yaml_emitter_emit(&request, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       if (!label) {
+               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;
+               goto skip_params;
+       }
+
+       /* sessions: { name: 'name', timeout: 300 }
+        * or
+        * sessions:
+        *   name: 'name'
+        *   timeout: 300
+        */
+       yaml_mapping_start_event_initialize(&event, NULL,
+                                           (yaml_char_t *)YAML_MAP_TAG,
+                                           1, YAML_FLOW_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;
+
+       yaml_scalar_event_initialize(&event, NULL,
+                                    (yaml_char_t *)YAML_STR_TAG,
+                                    (yaml_char_t *)label,
+                                    strlen(label), 1, 0,
+                                    YAML_PLAIN_SCALAR_STYLE);
+       rc = yaml_emitter_emit(&request, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       if (timeout) {
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_STR_TAG,
+                                            (yaml_char_t *)"timeout",
+                                            strlen("timeout"), 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 *)timeout,
+                                            strlen(timeout), 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;
+
+skip_params:
+       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);
+               errmsg = NULL;
+               goto parser_error;
+       }
+       yaml_emitter_delete(&request);
+
+       while (!done) {
+               rc = yaml_parser_parse(&reply, &event);
+               if (rc == 0)
+                       goto parser_error;
+
+               if (event.type == YAML_SCALAR_EVENT) {
+                       char *tmp, *endp = NULL;
+
+                       if (strcmp((char *)event.data.scalar.value,
+                                  "name") == 0) {
+                               yaml_event_delete(&event);
+                               rc = yaml_parser_parse(&reply, &event);
+                               if (rc == 0)
+                                       goto parser_error;
+
+                               strncpy(name, (char *)event.data.scalar.value,
+                                       sizeof(name) - 1);
+                       }
+
+                       if (strcmp((char *)event.data.scalar.value,
+                                  "key") == 0) {
+                               yaml_event_delete(&event);
+                               rc = yaml_parser_parse(&reply, &event);
+                               if (rc == 0)
+                                       goto parser_error;
+
+                               tmp = (char *)event.data.scalar.value;
+                               key = strtoul(tmp, &endp, 10);
+                               if (endp == tmp)
+                                       goto parser_error;
+                       }
+
+                       if (strcmp((char *)event.data.scalar.value,
+                                  "timestamp") == 0) {
+                               yaml_event_delete(&event);
+                               rc = yaml_parser_parse(&reply, &event);
+                               if (rc == 0)
+                                       goto parser_error;
+
+                               tmp = (char *)event.data.scalar.value;
+                               sid.ses_stamp = strtoll(tmp, &endp, 10);
+                               if (endp == tmp)
+                                       goto parser_error;
+                       }
+
+                       if (strcmp((char *)event.data.scalar.value,
+                                  "nid") == 0) {
+                               yaml_event_delete(&event);
+                               rc = yaml_parser_parse(&reply, &event);
+                               if (rc == 0)
+                                       goto parser_error;
+
+                               strncpy(nid, (char *)event.data.scalar.value,
+                                       sizeof(nid) - 1);
+                       }
+
+                       if (strcmp((char *)event.data.scalar.value,
+                                  "nodes") == 0) {
+                               yaml_event_delete(&event);
+                               rc = yaml_parser_parse(&reply, &event);
+                               if (rc == 0)
+                                       goto parser_error;
+
+                               tmp = (char *)event.data.scalar.value;
+                               ndinfo.nle_nnode = strtoul(tmp, &endp, 10);
+                               if (endp == tmp)
+                                       goto parser_error;
+                       }
+               }
+
+               done = (event.type == YAML_STREAM_END_EVENT);
+
+               yaml_event_delete(&event);
+       }
+
+       if (nlflags & NLM_F_CREATE) {
+               session_features = yaml_parser_get_reader_proto_version(&reply);
+               session_key = key;
+       }
+parser_error:
+       if (rc == 0 && errmsg)
+               yaml_parser_log_error(&reply, stderr, errmsg);
+       yaml_parser_delete(&reply);
+       nl_socket_free(sk);
+
+       if (((nlflags & NLM_F_DUMP) == NLM_F_DUMP) && rc != 0) {
+               fprintf(stdout,
+                       "%s ID: %ju@%s, KEY: %u FEATURES: %x NODES: %d\n",
+                       name, (uintmax_t)sid.ses_stamp, nid,
+                       key, session_features, ndinfo.nle_nnode);
+       }
+
+       return rc == 0 ? -1 : 0;
+}
+
 int
 lst_new_session_ioctl(char *name, int timeout, int force, struct lst_sid *sid)
 {
@@ -536,7 +794,9 @@ int
 jt_lst_new_session(int argc, char **argv)
 {
        char  buf[LST_NAME_SIZE * 2 + 1];
-       char *name;
+       char *name, *timeout_s = NULL;
+       int nlflags = NLM_F_CREATE;
+       struct lst_sid session_id;
        int   optidx = 0;
        int   timeout = 300;
        int   force = 0;
@@ -548,26 +808,19 @@ jt_lst_new_session(int argc, char **argv)
                { .name = "force",   .has_arg = no_argument,       .val = 'f' },
                { .name = NULL } };
 
-        if (session_key == 0) {
-                fprintf(stderr,
-                        "Can't find env LST_SESSION or value is not valid\n");
-                return -1;
-        }
-
         while (1) {
-
                 c = getopt_long(argc, argv, "ft:",
                                 session_opts, &optidx);
-
                 if (c == -1)
                         break;
 
                 switch (c) {
                 case 'f':
+                       nlflags |= NLM_F_REPLACE;
                         force = 1;
                         break;
                 case 't':
-                        timeout = atoi(optarg);
+                       timeout_s = optarg;
                         break;
                 default:
                         lst_print_usage(argv[0]);
@@ -575,10 +828,13 @@ jt_lst_new_session(int argc, char **argv)
                 }
         }
 
-        if (timeout <= 0) {
-                fprintf(stderr, "Invalid timeout value\n");
-                return -1;
-        }
+       if (timeout_s) {
+               timeout = atoi(timeout_s);
+               if (timeout <= 0) {
+                       fprintf(stderr, "Invalid timeout value\n");
+                       return -1;
+               }
+       }
 
         if (optind == argc - 1) {
                 name = argv[optind ++];
@@ -587,7 +843,6 @@ jt_lst_new_session(int argc, char **argv)
                                 LST_NAME_SIZE - 1);
                         return -1;
                 }
-
         } else if (optind == argc) {
                 char           user[LST_NAME_SIZE];
                 char           host[LST_NAME_SIZE];
@@ -604,19 +859,28 @@ jt_lst_new_session(int argc, char **argv)
 
                snprintf(buf, sizeof(buf), "%s@%s", user, host);
                 name = buf;
-
         } else {
                 lst_print_usage(argv[0]);
                 return -1;
         }
 
+       rc = lst_yaml_session(name, timeout_s, nlflags, "new session");
+       if (rc == 0)
+               goto success;
+
+       if (session_key == 0) {
+               fprintf(stderr,
+                       "Can't find env LST_SESSION or value is not valid\n");
+               return -1;
+       }
+
         rc = lst_new_session_ioctl(name, timeout, force, &session_id);
         if (rc != 0) {
                 lst_print_error("session", "Failed to create session: %s\n",
                                 strerror(errno));
                 return rc;
         }
-
+success:
        fprintf(stdout, "SESSION: %s FEATURES: %x TIMEOUT: %d FORCE: %s\n",
                name, session_features, timeout, force ? "Yes" : "No");
        return 0;
@@ -643,10 +907,14 @@ jt_lst_show_session(int argc, char **argv)
 {
        struct lstcon_ndlist_ent ndinfo;
        struct lst_sid sid;
-        char                name[LST_NAME_SIZE];
-       unsigned            feats;
-       int                 key;
-       int                 rc;
+       char name[LST_NAME_SIZE];
+       unsigned int feats;
+       int key;
+       int rc;
+
+       rc = lst_yaml_session(NULL, NULL, NLM_F_DUMP, "show session");
+       if (rc == 0)
+               return 0;
 
        rc = lst_session_info_ioctl(name, sizeof(name), &key,
                                    &feats, &sid, &ndinfo);
@@ -684,6 +952,10 @@ jt_lst_end_session(int argc, char **argv)
                 return -1;
         }
 
+       rc = lst_yaml_session(NULL, NULL, 0, "end session");
+       if (rc == 0)
+               goto finish;
+
         rc = lst_end_session_ioctl();
 
         if (rc == 0) {
@@ -696,7 +968,7 @@ jt_lst_end_session(int argc, char **argv)
                                 strerror(errno));
                 return rc;
         }
-
+finish:
         if (trans_stat.trs_rpc_errno != 0) {
                 fprintf(stderr,
                         "[RPC] Failed to send %d session RPCs: %s\n",