Whamcloud - gitweb
LU-10003 lnet: use Netlink to support old and new NI APIs. 14/48814/13
authorJames Simmons <jsimmons@infradead.org>
Fri, 28 Oct 2022 18:02:17 +0000 (14:02 -0400)
committerOleg Drokin <green@whamcloud.com>
Tue, 8 Nov 2022 08:49:53 +0000 (08:49 +0000)
The LNet layer uses two different sets of ioctls. One ioctl set is
for Multi-Rail and the other is an older API. Both are in heavy
use and with the upcoming support for IPv6 we are looking at an
explosion of ioctls. The solution is to move the LNet layer to
Netlink which can easily handle all the differences between the
APIs. This also resolves a long standing issue of the user land
API constantly changing in a non-compatible way with previous
versions.

This patch unifies the handling the LNet NI to use Netlink and is
fully aware of the new large NID addressing.

Test-Parameters: trivial testlist=sanity-lnet
Signed-off-by: James Simmons <jsimmons@infradead.org>
Change-Id: Idf3384fe7cd0f593f149fd5d8f3a101e8bd8a7f6
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/48814
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Serguei Smirnov <ssmirnov@whamcloud.com>
Reviewed-by: Neil Brown <neilb@suse.de>
Reviewed-by: Frank Sehr <fsehr@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
20 files changed:
lnet/include/lnet/lib-lnet.h
lnet/include/lnet/lib-types.h
lnet/include/uapi/linux/lnet/libcfs_ioctl.h
lnet/include/uapi/linux/lnet/lnet-dlc.h
lnet/include/uapi/linux/lnet/lnet-types.h
lnet/klnds/gnilnd/gnilnd.c
lnet/klnds/kfilnd/kfilnd.c
lnet/klnds/kfilnd/kfilnd.h
lnet/klnds/o2iblnd/o2iblnd.c
lnet/klnds/o2iblnd/o2iblnd.h
lnet/klnds/socklnd/socklnd.c
lnet/klnds/socklnd/socklnd.h
lnet/lnet/api-ni.c
lnet/lnet/config.c
lnet/lnet/module.c
lnet/utils/Makefile.am
lnet/utils/lnetconfig/liblnetconfig_netlink.c
lnet/utils/lnetctl.c
lustre/tests/sanity-lnet.sh
lustre/utils/portals.c

index f3caecd..e2f3560 100644 (file)
@@ -505,6 +505,7 @@ lnet_ni_alloc(struct lnet_net *net, struct cfs_expr_list *el,
 struct lnet_ni *
 lnet_ni_alloc_w_cpt_array(struct lnet_net *net, __u32 *cpts, __u32 ncpts,
                          char *iface);
+int lnet_ni_add_interface(struct lnet_ni *ni, char *iface);
 
 static inline int
 lnet_nid2peerhash(struct lnet_nid *nid)
@@ -672,8 +673,9 @@ void lnet_rtr_transfer_to_peer(struct lnet_peer *src,
 struct lnet_remotenet *lnet_find_rnet_locked(__u32 net);
 int lnet_dyn_add_net(struct lnet_ioctl_config_data *conf);
 int lnet_dyn_del_net(__u32 net);
-int lnet_dyn_add_ni(struct lnet_ioctl_config_ni *conf);
-int lnet_dyn_del_ni(struct lnet_ioctl_config_ni *conf);
+int lnet_dyn_add_ni(struct lnet_ioctl_config_ni *conf, u32 net,
+                   struct lnet_ioctl_config_lnd_tunables *tun);
+int lnet_dyn_del_ni(struct lnet_nid *nid);
 int lnet_clear_lazy_portal(struct lnet_ni *ni, int portal, char *reason);
 struct lnet_net *lnet_get_net_locked(__u32 net_id);
 void lnet_net_clr_pref_rtrs(struct lnet_net *net);
index 4b15d07..2aa823a 100644 (file)
@@ -334,6 +334,11 @@ struct lnet_lnd {
        /* get dma_dev priority */
        unsigned int (*lnd_get_dev_prio)(struct lnet_ni *ni,
                                         unsigned int dev_idx);
+
+       /* Handle LND specific Netlink handling */
+       int (*lnd_nl_set)(int cmd, struct nlattr *attr, int type, void *data);
+
+       const struct ln_key_list *lnd_keys;
 };
 
 struct lnet_tx_queue {
@@ -455,6 +460,104 @@ struct lnet_net {
        struct list_head        net_rtr_pref_nids;
 };
 
+/* Normally Netlink atttributes are defined in UAPI headers but Lustre is
+ * different in that the ABI is in a constant state of change unlike other
+ * Netlink interfaces. LNet sends a special header to help user land handle
+ * the differences.
+ */
+
+/** enum lnet_net_attrs                      - LNet NI netlink properties
+ *                                     attributes that describe LNet 'NI'
+ *                                     These values are used to piece together
+ *                                     messages for sending and receiving.
+ *
+ * @LNET_NET_ATTR_UNSPEC:              unspecified attribute to catch errors
+ *
+ * @LNET_NET_ATTR_HDR:                 grouping for LNet net data (NLA_NESTED)
+ * @LNET_NET_ATTR_TYPE:                        LNet net this NI belongs to (NLA_STRING)
+ * @LNET_NET_ATTR_LOCAL:               Local NI information (NLA_NESTED)
+ */
+enum lnet_net_attrs {
+       LNET_NET_ATTR_UNSPEC = 0,
+
+       LNET_NET_ATTR_HDR,
+       LNET_NET_ATTR_TYPE,
+       LNET_NET_ATTR_LOCAL,
+
+       __LNET_NET_ATTR_MAX_PLUS_ONE,
+};
+
+#define LNET_NET_ATTR_MAX (__LNET_NET_ATTR_MAX_PLUS_ONE - 1)
+
+/** enum lnet_net_local_ni_attrs      - LNet local NI netlink properties
+ *                                     attributes that describe local NI
+ *
+ * @LNET_NET_LOCAL_NI_ATTR_UNSPEC:     unspecified attribute to catch errors
+ *
+ * @LNET_NET_LOCAL_NI_ATTR_NID:                NID that represents this NI (NLA_STRING)
+ * @LNET_NET_LOCAL_NI_ATTR_STATUS:     State of this NI (NLA_STRING)
+ * @LNET_NET_LOCAL_NI_ATTR_INTERFACE:  Defines physical devices (NLA_NESTED)
+ *                                     Used to be many devices but no longer.
+ */
+enum lnet_net_local_ni_attrs {
+       LNET_NET_LOCAL_NI_ATTR_UNSPEC = 0,
+
+       LNET_NET_LOCAL_NI_ATTR_NID,
+       LNET_NET_LOCAL_NI_ATTR_STATUS,
+       LNET_NET_LOCAL_NI_ATTR_INTERFACE,
+
+       __LNET_NET_LOCAL_NI_ATTR_MAX_PLUS_ONE,
+};
+
+#define LNET_NET_LOCAL_NI_ATTR_MAX (__LNET_NET_LOCAL_NI_ATTR_MAX_PLUS_ONE - 1)
+
+/** enum lnet_net_local_ni_intf_attrs - LNet NI device netlink properties
+ *                                     attribute that reports the device
+ *                                     in use
+ *
+ * @LNET_NET_LOCAL_NI_INTF_ATTR_UNSPEC:        unspecified attribute to catch errors
+ *
+ * @LNET_NET_LOCAL_NI_INTF_ATTR_TYPE:  Physcial device interface (NLA_STRING)
+ */
+enum lnet_net_local_ni_intf_attrs {
+       LNET_NET_LOCAL_NI_INTF_ATTR_UNSPEC = 0,
+
+       LNET_NET_LOCAL_NI_INTF_ATTR_TYPE,
+
+       __LNET_NET_LOCAL_NI_INTF_ATTR_MAX_PLUS_ONE,
+};
+
+#define LNET_NET_LOCAL_NI_INTF_ATTR_MAX (__LNET_NET_LOCAL_NI_INTF_ATTR_MAX_PLUS_ONE - 1)
+
+/** enum lnet_net_local_ni_tunables_attrs            - LNet NI tunables
+ *                                                     netlink properties.
+ *                                                     Performance options
+ *                                                     for your NI.
+ *
+ * @LNET_NET_LOCAL_NI_TUNABLES_ATTR_UNSPEC:            unspecified attribute
+ *                                                     to catch errors
+ *
+ * @LNET_NET_LOCAL_NI_TUNABLES_ATTR_PEER_TIMEOUT:      Timeout for LNet peer.
+ *                                                     (NLA_S32)
+ * @LNET_NET_LOCAL_NI_TUNABLES_ATTR_PEER_CREDITS:      Credits for LNet peer.
+ *                                                     (NLA_S32)
+ * @LNET_NET_LOCAL_NI_TUNABLES_ATTR_PEER_BUFFER_CREDITS: Buffer credits for
+ *                                                      LNet peer. (NLA_S32)
+ * @LNET_NET_LOCAL_NI_TUNABLES_ATTR_CREDITS:           Credits for LNet peer
+ *                                                     TX. (NLA_S32)
+ */
+enum lnet_net_local_ni_tunables_attr {
+       LNET_NET_LOCAL_NI_TUNABLES_ATTR_UNSPEC = 0,
+
+       LNET_NET_LOCAL_NI_TUNABLES_ATTR_PEER_TIMEOUT,
+       LNET_NET_LOCAL_NI_TUNABLES_ATTR_PEER_CREDITS,
+       LNET_NET_LOCAL_NI_TUNABLES_ATTR_PEER_BUFFER_CREDITS,
+       LNET_NET_LOCAL_NI_TUNABLES_ATTR_CREDITS,
+       __LNET_NET_LOCAL_NI_TUNABLES_ATTR_MAX_PLUS_ONE,
+};
+
+#define LNET_NET_LOCAL_NI_TUNABLES_ATTR_MAX (__LNET_NET_LOCAL_NI_TUNABLES_ATTR_MAX_PLUS_ONE - 1)
+
 struct lnet_ni {
        /* chain on the lnet_net structure */
        struct list_head        ni_netlist;
index 1bcf47b..d1a94bb 100644 (file)
@@ -91,7 +91,7 @@ struct libcfs_ioctl_data {
 #define IOC_LIBCFS_MARK_DEBUG              _IOWR('e', 32, IOCTL_LIBCFS_TYPE)
 /* IOC_LIBCFS_MEMHOG obsolete in 2.8.0, was _IOWR('e', 36, IOCTL_LIBCFS_TYPE) */
 /* lnet ioctls */
-#define IOC_LIBCFS_GET_NI                 _IOWR('e', 50, IOCTL_LIBCFS_TYPE)
+/* IOC_LIBCFS_GET_NI obsolete in 2.16, was _IOWR('e', 50, IOCTL_LIBCFS_TYPE) */
 #define IOC_LIBCFS_FAIL_NID               _IOWR('e', 51, IOCTL_LIBCFS_TYPE)
 #define IOC_LIBCFS_NOTIFY_ROUTER          _IOWR('e', 55, IOCTL_LIBCFS_TYPE)
 #define IOC_LIBCFS_UNCONFIGURE            _IOWR('e', 56, IOCTL_LIBCFS_TYPE)
index 9b6cbe3..9f9e50f 100644 (file)
 #define __user
 #endif
 
+#define LNET_GENL_NAME         "lnet"
+#define LNET_GENL_VERSION      0x05
+
+/* enum lnet_commands        - Supported core LNet Netlink commands
+ *
+ *  @LNET_CMD_UNSPEC:          unspecified command to catch errors
+ *
+ *  @LNET_CMD_NETS:            command to manage the LNet networks
+ */
+enum lnet_commands {
+       LNET_CMD_UNSPEC         = 0,
+
+       LNET_CMD_CONFIGURE      = 1,
+       LNET_CMD_NETS           = 2,
+       LNET_CMD_PEERS          = 3,
+       LNET_CMD_ROUTES         = 4,
+       LNET_CMD_CONNS          = 5,
+
+       __LNET_CMD_MAX_PLUS_ONE
+};
+
+#define LNET_CMD_MAX (__LNET_CMD_MAX_PLUS_ONE - 1)
+
 /*
  * To allow for future enhancements to extend the tunables
  * add a hdr to this structure, so that the version can be set
index 5a2a2a0..50377d8 100644 (file)
 #ifndef __UAPI_LNET_TYPES_H__
 #define __UAPI_LNET_TYPES_H__
 
+#include <linux/types.h>
 #include <linux/string.h>
 #include <asm/byteorder.h>
+#ifndef __KERNEL__
+#include <stdbool.h>
+#endif
 
 /** \addtogroup lnet
  * @{ */
@@ -106,6 +110,17 @@ static inline __u32 LNET_MKNET(__u32 type, __u32 num)
 
 #define LNET_NET_ANY LNET_NIDNET(LNET_NID_ANY)
 
+/* check for address set */
+static inline bool nid_addr_is_set(const struct lnet_nid *nid)
+{
+       int sum = 0, i;
+
+       for (i = 0; i < NID_ADDR_BYTES(nid); i++)
+               sum |= nid->nid_addr[i];
+
+       return sum ? true : false;
+}
+
 static inline int nid_is_nid4(const struct lnet_nid *nid)
 {
        return NID_ADDR_BYTES(nid) == 4;
index 857dc20..c9880f9 100644 (file)
@@ -2615,6 +2615,12 @@ kgnilnd_startup(struct lnet_ni *ni)
                        *kgnilnd_tunables.kgn_peer_credits;
        }
 
+       if (!ni->ni_interface) {
+               rc = lnet_ni_add_interface(ni, "ipogif0");
+               if (rc < 0)
+                       CWARN("gnilnd failed to allocate ni_interface\n");
+       }
+
        if (*kgnilnd_tunables.kgn_peer_health) {
                int     fudge;
                int     timeout;
index e8547a1..5213a65 100644 (file)
@@ -327,6 +327,51 @@ static int kfilnd_recv(struct lnet_ni *ni, void *private, struct lnet_msg *msg,
        return rc;
 }
 
+static const struct ln_key_list kfilnd_tunables_keys = {
+       .lkl_maxattr                    = LNET_NET_KFILND_TUNABLES_ATTR_MAX,
+       .lkl_list                       = {
+               [LNET_NET_KFILND_TUNABLES_ATTR_PROV_MAJOR]      = {
+                       .lkp_value      = "prov_major_version",
+                       .lkp_data_type  = NLA_S32
+               },
+               [LNET_NET_KFILND_TUNABLES_ATTR_PROV_MINOR]  = {
+                       .lkp_value      = "prov_minor_version",
+                       .lkp_data_type  = NLA_S32
+               },
+               [LNET_NET_KFILND_TUNABLES_ATTR_AUTH_KEY]  = {
+                       .lkp_value      = "auth_key",
+                       .lkp_data_type  = NLA_S32
+               },
+       },
+};
+
+static int
+kfilnd_nl_set(int cmd, struct nlattr *attr, int type, void *data)
+{
+       struct lnet_lnd_tunables *tunables = data;
+       int rc = 0;
+
+       if (cmd != LNET_CMD_NETS)
+               return -EOPNOTSUPP;
+
+       switch (type) {
+       case LNET_NET_KFILND_TUNABLES_ATTR_PROV_MAJOR:
+               tunables->lnd_tun_u.lnd_kfi.lnd_prov_major_version = nla_get_s64(attr);
+               break;
+       case LNET_NET_KFILND_TUNABLES_ATTR_PROV_MINOR:
+               tunables->lnd_tun_u.lnd_kfi.lnd_prov_minor_version = nla_get_s64(attr);
+               break;
+       case LNET_NET_KFILND_TUNABLES_ATTR_AUTH_KEY:
+               tunables->lnd_tun_u.lnd_kfi.lnd_auth_key = nla_get_s64(attr);
+               break;
+       default:
+               rc = -EINVAL;
+               break;
+       }
+
+       return rc;
+}
+
 static int kfilnd_startup(struct lnet_ni *ni);
 
 static const struct lnet_lnd the_kfilnd = {
@@ -335,6 +380,8 @@ static const struct lnet_lnd the_kfilnd = {
        .lnd_shutdown   = kfilnd_shutdown,
        .lnd_send       = kfilnd_send,
        .lnd_recv       = kfilnd_recv,
+       .lnd_nl_set     = kfilnd_nl_set,
+       .lnd_keys       = &kfilnd_tunables_keys,
 };
 
 static int kfilnd_startup(struct lnet_ni *ni)
index a1a47a4..49781e5 100644 (file)
@@ -125,6 +125,17 @@ enum kfilnd_object_states {
        KFILND_STATE_SHUTTING_DOWN
 };
 
+enum kfilnd_ni_lnd_tunables_attr {
+       LNET_NET_KFILND_TUNABLES_ATTR_UNSPEC = 0,
+
+       LNET_NET_KFILND_TUNABLES_ATTR_PROV_MAJOR,
+       LNET_NET_KFILND_TUNABLES_ATTR_PROV_MINOR,
+       LNET_NET_KFILND_TUNABLES_ATTR_AUTH_KEY,
+       __LNET_NET_KFILND_TUNABLES_ATTR_MAX_PLUS_ONE,
+};
+
+#define LNET_NET_KFILND_TUNABLES_ATTR_MAX (__LNET_NET_KFILND_TUNABLES_ATTR_MAX_PLUS_ONE - 1)
+
 extern struct dentry *kfilnd_debug_dir;
 extern const struct file_operations kfilnd_initiator_state_stats_file_ops;
 extern const struct file_operations kfilnd_target_state_stats_file_ops;
index 30cadfa..f6c2d58 100644 (file)
@@ -1216,6 +1216,86 @@ kiblnd_ctl(struct lnet_ni *ni, unsigned int cmd, void *arg)
         return rc;
 }
 
+static const struct ln_key_list kiblnd_tunables_keys = {
+       .lkl_maxattr                    = LNET_NET_O2IBLND_TUNABLES_ATTR_MAX,
+       .lkl_list                       = {
+               [LNET_NET_O2IBLND_TUNABLES_ATTR_HIW_PEER_CREDITS]  = {
+                       .lkp_value      = "peercredits_hiw",
+                       .lkp_data_type  = NLA_U32
+               },
+               [LNET_NET_O2IBLND_TUNABLES_ATTR_MAP_ON_DEMAND]  = {
+                       .lkp_value      = "map_on_demand",
+                       .lkp_data_type  = NLA_FLAG
+               },
+               [LNET_NET_O2IBLND_TUNABLES_ATTR_CONCURRENT_SENDS]  = {
+                       .lkp_value      = "concurrent_sends",
+                       .lkp_data_type  = NLA_U32
+               },
+               [LNET_NET_O2IBLND_TUNABLES_ATTR_FMR_POOL_SIZE]  = {
+                       .lkp_value      = "fmr_pool_size",
+                       .lkp_data_type  = NLA_U32
+               },
+               [LNET_NET_O2IBLND_TUNABLES_ATTR_FMR_FLUSH_TRIGGER]  = {
+                       .lkp_value      = "fmr_flush_trigger",
+                       .lkp_data_type  = NLA_U32
+               },
+               [LNET_NET_O2IBLND_TUNABLES_ATTR_FMR_CACHE]  = {
+                       .lkp_value      = "fmr_cache",
+                       .lkp_data_type  = NLA_U32
+               },
+               [LNET_NET_O2IBLND_TUNABLES_ATTR_NTX]  = {
+                       .lkp_value      = "ntx",
+                       .lkp_data_type  = NLA_U16
+               },
+               [LNET_NET_O2IBLND_TUNABLES_ATTR_CONNS_PER_PEER]  = {
+                       .lkp_value      = "conns_per_peer",
+                       .lkp_data_type  = NLA_U16
+               },
+       },
+};
+
+static int
+kiblnd_nl_set(int cmd, struct nlattr *attr, int type, void *data)
+{
+       struct lnet_lnd_tunables *tunables = data;
+
+       if (cmd != LNET_CMD_NETS)
+               return -EOPNOTSUPP;
+
+       if (nla_type(attr) != LN_SCALAR_ATTR_INT_VALUE)
+               return -EINVAL;
+
+       switch (type) {
+       case LNET_NET_O2IBLND_TUNABLES_ATTR_HIW_PEER_CREDITS:
+               tunables->lnd_tun_u.lnd_o2ib.lnd_peercredits_hiw = nla_get_s64(attr);
+               break;
+       case LNET_NET_O2IBLND_TUNABLES_ATTR_MAP_ON_DEMAND:
+               tunables->lnd_tun_u.lnd_o2ib.lnd_map_on_demand = nla_get_s64(attr);
+               break;
+       case LNET_NET_O2IBLND_TUNABLES_ATTR_CONCURRENT_SENDS:
+               tunables->lnd_tun_u.lnd_o2ib.lnd_concurrent_sends = nla_get_s64(attr);
+               break;
+       case LNET_NET_O2IBLND_TUNABLES_ATTR_FMR_POOL_SIZE:
+               tunables->lnd_tun_u.lnd_o2ib.lnd_fmr_pool_size = nla_get_s64(attr);
+               break;
+       case LNET_NET_O2IBLND_TUNABLES_ATTR_FMR_FLUSH_TRIGGER:
+               tunables->lnd_tun_u.lnd_o2ib.lnd_fmr_flush_trigger = nla_get_s64(attr);
+               break;
+       case LNET_NET_O2IBLND_TUNABLES_ATTR_FMR_CACHE:
+               tunables->lnd_tun_u.lnd_o2ib.lnd_fmr_cache = nla_get_s64(attr);
+               break;
+       case LNET_NET_O2IBLND_TUNABLES_ATTR_NTX:
+               tunables->lnd_tun_u.lnd_o2ib.lnd_ntx = nla_get_s64(attr);
+               break;
+       case LNET_NET_O2IBLND_TUNABLES_ATTR_CONNS_PER_PEER:
+               tunables->lnd_tun_u.lnd_o2ib.lnd_conns_per_peer = nla_get_s64(attr);
+       default:
+               break;
+       }
+
+       return 0;
+}
+
 static void
 kiblnd_free_pages(struct kib_pages *p)
 {
@@ -3557,7 +3637,11 @@ kiblnd_startup(struct lnet_ni *ni)
 
        net->ibn_dev = ibdev;
        ni->ni_nid.nid_addr[0] = cpu_to_be32(ibdev->ibd_ifip);
-
+       if (!ni->ni_interface) {
+               rc = lnet_ni_add_interface(ni, ifaces[i].li_name);
+               if (rc < 0)
+                       CWARN("ko2iblnd failed to allocate ni_interface\n");
+       }
        ni->ni_dev_cpt = ifaces[i].li_cpt;
 
        rc = kiblnd_dev_start_threads(ibdev, newdev, ni->ni_cpts, ni->ni_ncpts);
@@ -3603,6 +3687,8 @@ static const struct lnet_lnd the_o2iblnd = {
        .lnd_send       = kiblnd_send,
        .lnd_recv       = kiblnd_recv,
        .lnd_get_dev_prio = kiblnd_get_dev_prio,
+       .lnd_nl_set     = kiblnd_nl_set,
+       .lnd_keys       = &kiblnd_tunables_keys,
 };
 
 static void ko2inlnd_assert_wire_constants(void)
index d685610..9908b22 100644 (file)
@@ -55,7 +55,6 @@
 #define HAVE_NLA_PARSE_6_PARAMS 1
 #define HAVE_NETLINK_EXTACK 1
 
-
 /* MOFED has its own bitmap_alloc backport */
 #define HAVE_BITMAP_ALLOC 1
 
 #include <lnet/lnet_rdma.h>
 #include "o2iblnd-idl.h"
 
+enum kiblnd_ni_lnd_tunables_attr {
+       LNET_NET_O2IBLND_TUNABLES_ATTR_UNSPEC = 0,
+
+       LNET_NET_O2IBLND_TUNABLES_ATTR_HIW_PEER_CREDITS,
+       LNET_NET_O2IBLND_TUNABLES_ATTR_CONCURRENT_SENDS,
+       LNET_NET_O2IBLND_TUNABLES_ATTR_MAP_ON_DEMAND,
+       LNET_NET_O2IBLND_TUNABLES_ATTR_FMR_POOL_SIZE,
+       LNET_NET_O2IBLND_TUNABLES_ATTR_FMR_FLUSH_TRIGGER,
+       LNET_NET_O2IBLND_TUNABLES_ATTR_FMR_CACHE,
+       LNET_NET_O2IBLND_TUNABLES_ATTR_NTX,
+       LNET_NET_O2IBLND_TUNABLES_ATTR_CONNS_PER_PEER,
+       __LNET_NET_O2IBLND_TUNABLES_ATTR_MAX_PLUS_ONE,
+};
+
+#define LNET_NET_O2IBLND_TUNABLES_ATTR_MAX (__LNET_NET_O2IBLND_TUNABLES_ATTR_MAX_PLUS_ONE - 1)
+
 #define IBLND_PEER_HASH_BITS           7       /* log2 of # peer_ni lists */
 #define IBLND_N_SCHED                  2
 #define IBLND_N_SCHED_HIGH             4
index 6e800f1..2c844c0 100644 (file)
@@ -841,6 +841,33 @@ ksocknal_accept(struct lnet_ni *ni, struct socket *sock)
        return 0;
 }
 
+static const struct ln_key_list ksocknal_tunables_keys = {
+       .lkl_maxattr                    = LNET_NET_SOCKLND_TUNABLES_ATTR_MAX,
+       .lkl_list                       = {
+               [LNET_NET_SOCKLND_TUNABLES_ATTR_CONNS_PER_PEER]  = {
+                       .lkp_value      = "conns_per_peer",
+                       .lkp_data_type  = NLA_S32
+               },
+       },
+};
+
+static int
+ksocknal_nl_set(int cmd, struct nlattr *attr, int type, void *data)
+{
+       struct lnet_lnd_tunables *tunables = data;
+
+       if (cmd != LNET_CMD_NETS)
+               return -EOPNOTSUPP;
+
+       if (type != LNET_NET_SOCKLND_TUNABLES_ATTR_CONNS_PER_PEER ||
+           nla_type(attr) != LN_SCALAR_ATTR_INT_VALUE)
+               return -EINVAL;
+
+       tunables->lnd_tun_u.lnd_sock.lnd_conns_per_peer = nla_get_s64(attr);
+
+       return 0;
+}
+
 static int
 ksocknal_connecting(struct ksock_conn_cb *conn_cb, struct sockaddr *sa)
 {
@@ -2506,16 +2533,20 @@ ksocknal_startup(struct lnet_ni *ni)
 
        /* Use the first discovered interface or look in the list */
        if (ni->ni_interface) {
-               for (i = 0; i < rc; i++)
+               for (i = 0; i < rc; i++) {
                        if (strcmp(ifaces[i].li_name, ni->ni_interface) == 0)
                                break;
-
+               }
                /* ni_interfaces doesn't contain the interface we want */
                if (i == rc) {
                        CERROR("ksocklnd: failed to find interface %s\n",
                               ni->ni_interface);
                        goto fail_1;
                }
+       } else {
+               rc = lnet_ni_add_interface(ni, ifaces[i].li_name);
+               if (rc < 0)
+                       CWARN("ksocklnd failed to allocate ni_interface\n");
        }
 
        ni->ni_dev_cpt = ifaces[i].li_cpt;
@@ -2576,6 +2607,8 @@ static const struct lnet_lnd the_ksocklnd = {
        .lnd_recv               = ksocknal_recv,
        .lnd_notify_peer_down   = ksocknal_notify_gw_down,
        .lnd_accept             = ksocknal_accept,
+       .lnd_nl_set             = ksocknal_nl_set,
+       .lnd_keys               = &ksocknal_tunables_keys,
 };
 
 static int __init ksocklnd_init(void)
index c9a9fdb..823fd30 100644 (file)
 # define SOCKNAL_RISK_KMAP_DEADLOCK  1
 #endif
 
+enum ksocklnd_ni_lnd_tunables_attr {
+       LNET_NET_SOCKLND_TUNABLES_ATTR_UNSPEC = 0,
+
+       LNET_NET_SOCKLND_TUNABLES_ATTR_CONNS_PER_PEER,
+       __LNET_NET_SOCKLND_TUNABLES_ATTR_MAX_PLUS_ONE,
+};
+
+#define LNET_NET_SOCKLND_TUNABLES_ATTR_MAX (__LNET_NET_SOCKLND_TUNABLES_ATTR_MAX_PLUS_ONE - 1)
+
 /* per scheduler state */
 struct ksock_sched {
        /* serialise */
index 036c035..2c5683c 100644 (file)
@@ -39,6 +39,9 @@
 #ifdef HAVE_SCHED_HEADERS
 #include <linux/sched/signal.h>
 #endif
+#include <net/genetlink.h>
+
+#include <libcfs/linux/linux-net.h>
 #include <lnet/udsp.h>
 #include <lnet/lib-lnet.h>
 
@@ -2580,6 +2583,36 @@ failed0:
        return rc;
 }
 
+static const struct lnet_lnd *lnet_load_lnd(u32 lnd_type)
+{
+       const struct lnet_lnd *lnd;
+       int rc = 0;
+
+       mutex_lock(&the_lnet.ln_lnd_mutex);
+       lnd = lnet_find_lnd_by_type(lnd_type);
+       if (!lnd) {
+               mutex_unlock(&the_lnet.ln_lnd_mutex);
+               rc = request_module("%s", libcfs_lnd2modname(lnd_type));
+               mutex_lock(&the_lnet.ln_lnd_mutex);
+
+               lnd = lnet_find_lnd_by_type(lnd_type);
+               if (!lnd) {
+                       mutex_unlock(&the_lnet.ln_lnd_mutex);
+                       CERROR("Can't load LND %s, module %s, rc=%d\n",
+                       libcfs_lnd2str(lnd_type),
+                       libcfs_lnd2modname(lnd_type), rc);
+#ifndef HAVE_MODULE_LOADING_SUPPORT
+                       LCONSOLE_ERROR_MSG(0x104,
+                                          "Your kernel must be compiled with kernel module loading support.");
+#endif
+                       return ERR_PTR(-EINVAL);
+               }
+       }
+       mutex_unlock(&the_lnet.ln_lnd_mutex);
+
+       return lnd;
+}
+
 static int
 lnet_startup_lndnet(struct lnet_net *net, struct lnet_lnd_tunables *tun)
 {
@@ -2607,32 +2640,14 @@ lnet_startup_lndnet(struct lnet_net *net, struct lnet_lnd_tunables *tun)
        if (lnet_net_unique(net->net_id, &the_lnet.ln_nets, &net_l)) {
                lnd_type = LNET_NETTYP(net->net_id);
 
-               mutex_lock(&the_lnet.ln_lnd_mutex);
-               lnd = lnet_find_lnd_by_type(lnd_type);
-
-               if (lnd == NULL) {
-                       mutex_unlock(&the_lnet.ln_lnd_mutex);
-                       rc = request_module("%s", libcfs_lnd2modname(lnd_type));
-                       mutex_lock(&the_lnet.ln_lnd_mutex);
-
-                       lnd = lnet_find_lnd_by_type(lnd_type);
-                       if (lnd == NULL) {
-                               mutex_unlock(&the_lnet.ln_lnd_mutex);
-                               CERROR("Can't load LND %s, module %s, rc=%d\n",
-                               libcfs_lnd2str(lnd_type),
-                               libcfs_lnd2modname(lnd_type), rc);
-#ifndef HAVE_MODULE_LOADING_SUPPORT
-                               LCONSOLE_ERROR_MSG(0x104, "Your kernel must be "
-                                               "compiled with kernel module "
-                                               "loading support.");
-#endif
-                               rc = -EINVAL;
-                               goto failed0;
-                       }
+               lnd = lnet_load_lnd(lnd_type);
+               if (IS_ERR(lnd)) {
+                       rc = PTR_ERR(lnd);
+                       goto failed0;
                }
 
+               mutex_lock(&the_lnet.ln_lnd_mutex);
                net->net_lnd = lnd;
-
                mutex_unlock(&the_lnet.ln_lnd_mutex);
 
                net_l = net;
@@ -2852,6 +2867,8 @@ canceled:
 }
 EXPORT_SYMBOL(lnet_genl_send_scalar_list);
 
+static struct genl_family lnet_family;
+
 /**
  * Initialize LNet library.
  *
@@ -2890,6 +2907,13 @@ int lnet_lib_init(void)
                return rc;
        }
 
+       rc = genl_register_family(&lnet_family);
+       if (rc != 0) {
+               lnet_destroy_locks();
+               CERROR("Can't register LNet netlink family: %d\n", rc);
+               return rc;
+       }
+
        the_lnet.ln_refcount = 0;
        INIT_LIST_HEAD(&the_lnet.ln_net_zombie);
        INIT_LIST_HEAD(&the_lnet.ln_msg_resend);
@@ -2929,6 +2953,7 @@ void lnet_lib_exit(void)
        for (i = 0; i < NUM_LNDS; i++)
                LASSERT(!the_lnet.ln_lnds[i]);
        lnet_destroy_locks();
+       genl_unregister_family(&lnet_family);
 }
 
 /**
@@ -3612,31 +3637,24 @@ out:
        return rc;
 }
 
-int lnet_dyn_add_ni(struct lnet_ioctl_config_ni *conf)
+int lnet_dyn_add_ni(struct lnet_ioctl_config_ni *conf, u32 net_id,
+                   struct lnet_ioctl_config_lnd_tunables *tun)
 {
        struct lnet_net *net;
        struct lnet_ni *ni;
-       struct lnet_ioctl_config_lnd_tunables *tun = NULL;
        int rc, i;
-       __u32 net_id, lnd_type;
-
-       /* get the tunables if they are available */
-       if (conf->lic_cfg_hdr.ioc_len >=
-           sizeof(*conf) + sizeof(*tun))
-               tun = (struct lnet_ioctl_config_lnd_tunables *)
-                       conf->lic_bulk;
+       u32 lnd_type;
 
        /* handle legacy ip2nets from DLC */
        if (conf->lic_legacy_ip2nets[0] != '\0')
                return lnet_handle_legacy_ip2nets(conf->lic_legacy_ip2nets,
                                                  tun);
 
-       net_id = LNET_NIDNET(conf->lic_nid);
        lnd_type = LNET_NETTYP(net_id);
 
        if (!libcfs_isknown_lnd(lnd_type)) {
                CERROR("No valid net and lnd information provided\n");
-               return -EINVAL;
+               return -ENOENT;
        }
 
        net = lnet_net_alloc(net_id, NULL);
@@ -3646,7 +3664,7 @@ int lnet_dyn_add_ni(struct lnet_ioctl_config_ni *conf)
        for (i = 0; i < conf->lic_ncpts; i++) {
                if (conf->lic_cpts[i] >= LNET_CPT_NUMBER) {
                        lnet_net_free(net);
-                       return -EINVAL;
+                       return -ERANGE;
                }
        }
 
@@ -3675,16 +3693,15 @@ int lnet_dyn_add_ni(struct lnet_ioctl_config_ni *conf)
        return rc;
 }
 
-int lnet_dyn_del_ni(struct lnet_ioctl_config_ni *conf)
+int lnet_dyn_del_ni(struct lnet_nid *nid)
 {
        struct lnet_net *net;
        struct lnet_ni *ni;
-       __u32 net_id = LNET_NIDNET(conf->lic_nid);
+       u32 net_id = LNET_NID_NET(nid);
        struct lnet_ping_buffer *pbuf;
        struct lnet_handle_md ping_mdh;
        int net_bytes, rc;
        bool net_empty;
-       u32 addr;
 
        /* don't allow userspace to shutdown the LOLND */
        if (LNET_NETTYP(net_id) == LOLND)
@@ -3706,8 +3723,7 @@ int lnet_dyn_del_ni(struct lnet_ioctl_config_ni *conf)
                goto unlock_net;
        }
 
-       addr = LNET_NIDADDR(conf->lic_nid);
-       if (addr == 0) {
+       if (!nid_addr_is_set(nid)) {
                /* remove the entire net */
                net_bytes = lnet_get_net_ni_bytes_locked(net);
 
@@ -3730,10 +3746,9 @@ int lnet_dyn_del_ni(struct lnet_ioctl_config_ni *conf)
                goto unlock_api_mutex;
        }
 
-       ni = lnet_nid2ni_locked(conf->lic_nid, 0);
+       ni = lnet_nid_to_ni_locked(nid, 0);
        if (!ni) {
-               CERROR("nid %s not found\n",
-                      libcfs_nid2str(conf->lic_nid));
+               CERROR("nid %s not found\n", libcfs_nidstr(nid));
                rc = -ENOENT;
                goto unlock_net;
        }
@@ -4032,8 +4047,6 @@ LNetCtl(unsigned int cmd, void *arg)
 {
        struct libcfs_ioctl_data *data = arg;
        struct lnet_ioctl_config_data *config;
-       struct lnet_process_id    id4 = {};
-       struct lnet_processid     id = {};
        struct lnet_ni           *ni;
        struct lnet_nid           nid;
        int                       rc;
@@ -4042,11 +4055,6 @@ LNetCtl(unsigned int cmd, void *arg)
                     sizeof(struct lnet_ioctl_config_data) > LIBCFS_IOC_DATA_MAX);
 
        switch (cmd) {
-       case IOC_LIBCFS_GET_NI:
-               rc = LNetGetId(data->ioc_count, &id);
-               data->ioc_nid = lnet_nid_to_nid4(&id.nid);
-               return rc;
-
        case IOC_LIBCFS_FAIL_NID:
                return lnet_fail_nid(data->ioc_nid, data->ioc_count);
 
@@ -4428,6 +4436,7 @@ LNetCtl(unsigned int cmd, void *arg)
                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;
@@ -4637,6 +4646,696 @@ LNetCtl(unsigned int cmd, void *arg)
 }
 EXPORT_SYMBOL(LNetCtl);
 
+static const struct ln_key_list net_props_list = {
+       .lkl_maxattr                    = LNET_NET_ATTR_MAX,
+       .lkl_list                       = {
+               [LNET_NET_ATTR_HDR]             = {
+                       .lkp_value              = "net",
+                       .lkp_key_format         = LNKF_SEQUENCE | LNKF_MAPPING,
+                       .lkp_data_type          = NLA_NUL_STRING,
+               },
+               [LNET_NET_ATTR_TYPE]            = {
+                       .lkp_value              = "net type",
+                       .lkp_data_type          = NLA_STRING
+               },
+               [LNET_NET_ATTR_LOCAL]           = {
+                       .lkp_value              = "local NI(s)",
+                       .lkp_key_format         = LNKF_SEQUENCE | LNKF_MAPPING,
+                       .lkp_data_type          = NLA_NESTED
+               },
+       },
+};
+
+static struct ln_key_list local_ni_list = {
+       .lkl_maxattr                    = LNET_NET_LOCAL_NI_ATTR_MAX,
+       .lkl_list                       = {
+               [LNET_NET_LOCAL_NI_ATTR_NID]    = {
+                       .lkp_value              = "nid",
+                       .lkp_data_type          = NLA_STRING
+               },
+               [LNET_NET_LOCAL_NI_ATTR_STATUS] = {
+                       .lkp_value              = "status",
+                       .lkp_data_type          = NLA_STRING
+               },
+               [LNET_NET_LOCAL_NI_ATTR_INTERFACE] = {
+                       .lkp_value              = "interfaces",
+                       .lkp_key_format         = LNKF_MAPPING,
+                       .lkp_data_type          = NLA_NESTED
+               },
+       },
+};
+
+static const struct ln_key_list local_ni_interfaces_list = {
+       .lkl_maxattr                    = LNET_NET_LOCAL_NI_INTF_ATTR_MAX,
+       .lkl_list                       = {
+               [LNET_NET_LOCAL_NI_INTF_ATTR_TYPE] = {
+                       .lkp_value      = "0",
+                       .lkp_data_type  = NLA_STRING
+               },
+       },
+};
+
+/* Use an index since the traversal is across LNet nets and ni collections */
+struct lnet_genl_net_list {
+       unsigned int    lngl_net_id;
+       unsigned int    lngl_idx;
+};
+
+static inline struct lnet_genl_net_list *
+lnet_net_dump_ctx(struct netlink_callback *cb)
+{
+       return (struct lnet_genl_net_list *)cb->args[0];
+}
+
+static int lnet_net_show_done(struct netlink_callback *cb)
+{
+       struct lnet_genl_net_list *nlist = lnet_net_dump_ctx(cb);
+
+       if (nlist) {
+               LIBCFS_FREE(nlist, sizeof(*nlist));
+               cb->args[0] = 0;
+       }
+
+       return 0;
+}
+
+/* LNet net ->start() handler for GET requests */
+static int lnet_net_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_net_list *nlist;
+       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
+       if (the_lnet.ln_refcount == 0) {
+               NL_SET_ERR_MSG(extack, "LNet stack down");
+               return -ENETDOWN;
+       }
+
+       LIBCFS_ALLOC(nlist, sizeof(*nlist));
+       if (!nlist)
+               return -ENOMEM;
+
+       nlist->lngl_net_id = LNET_NET_ANY;
+       nlist->lngl_idx = 0;
+       cb->args[0] = (long)nlist;
+
+       if (!msg_len)
+               return 0;
+
+       params = genlmsg_data(gnlh);
+       nla_for_each_attr(top, params, msg_len, rem) {
+               struct nlattr *net;
+               int rem2;
+
+               nla_for_each_nested(net, top, rem2) {
+                       char filter[LNET_NIDSTR_SIZE];
+
+                       if (nla_type(net) != LN_SCALAR_ATTR_VALUE ||
+                           nla_strcmp(net, "name") != 0)
+                               continue;
+
+                       net = nla_next(net, &rem2);
+                       if (nla_type(net) != LN_SCALAR_ATTR_VALUE) {
+                               NL_SET_ERR_MSG(extack, "invalid config param");
+                               GOTO(report_err, rc = -EINVAL);
+                       }
+
+                       rc = nla_strscpy(filter, net, sizeof(filter));
+                       if (rc < 0) {
+                               NL_SET_ERR_MSG(extack, "failed to get param");
+                               GOTO(report_err, rc);
+                       }
+                       rc = 0;
+
+                       nlist->lngl_net_id = libcfs_str2net(filter);
+                       if (nlist->lngl_net_id == LNET_NET_ANY) {
+                               NL_SET_ERR_MSG(extack, "cannot parse net");
+                               GOTO(report_err, rc = -ENOENT);
+                       }
+               }
+       }
+report_err:
+       if (rc < 0)
+               lnet_net_show_done(cb);
+
+       return rc;
+}
+
+static int lnet_net_show_dump(struct sk_buff *msg,
+                             struct netlink_callback *cb)
+{
+       struct lnet_genl_net_list *nlist = lnet_net_dump_ctx(cb);
+#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;
+       struct lnet_net *net;
+       int idx = 0, rc = 0;
+       bool found = false;
+       void *hdr = NULL;
+
+#ifdef HAVE_NL_DUMP_WITH_EXT_ACK
+       extack = cb->extack;
+#endif
+       if (!nlist->lngl_idx) {
+               const struct ln_key_list *all[] = {
+                       &net_props_list, &local_ni_list,
+                       &local_ni_interfaces_list,
+                       NULL
+               };
+
+               rc = lnet_genl_send_scalar_list(msg, portid, seq,
+                                               &lnet_family,
+                                               NLM_F_CREATE | NLM_F_MULTI,
+                                               LNET_CMD_NETS, all);
+               if (rc < 0) {
+                       NL_SET_ERR_MSG(extack, "failed to send key table");
+                       GOTO(send_error, rc);
+               }
+       }
+
+       lnet_net_lock(LNET_LOCK_EX);
+
+       list_for_each_entry(net, &the_lnet.ln_nets, net_list) {
+               struct lnet_ni *ni;
+
+               if (nlist->lngl_net_id != LNET_NET_ANY &&
+                   nlist->lngl_net_id != net->net_id)
+                       continue;
+
+               list_for_each_entry(ni, &net->net_ni_list, ni_netlist) {
+                       struct nlattr *local_ni, *ni_attr;
+                       char *status = "up";
+
+                       if (idx++ < nlist->lngl_idx)
+                               continue;
+
+                       hdr = genlmsg_put(msg, portid, seq, &lnet_family,
+                                         NLM_F_MULTI, LNET_CMD_NETS);
+                       if (!hdr) {
+                               NL_SET_ERR_MSG(extack, "failed to send values");
+                               GOTO(net_unlock, rc = -EMSGSIZE);
+                       }
+
+                       if (idx == 1)
+                               nla_put_string(msg, LNET_NET_ATTR_HDR, "");
+
+                       nla_put_string(msg, LNET_NET_ATTR_TYPE,
+                                      libcfs_net2str(net->net_id));
+                       found = true;
+
+                       local_ni = nla_nest_start(msg, LNET_NET_ATTR_LOCAL);
+                       ni_attr = nla_nest_start(msg, idx - 1);
+
+                       lnet_ni_lock(ni);
+                       nla_put_string(msg, LNET_NET_LOCAL_NI_ATTR_NID,
+                                      libcfs_nidstr(&ni->ni_nid));
+                       if (nid_is_lo0(&ni->ni_nid) &&
+                           *ni->ni_status != LNET_NI_STATUS_UP)
+                               status = "down";
+                       nla_put_string(msg, LNET_NET_LOCAL_NI_ATTR_STATUS, "up");
+
+                       if (!nid_is_lo0(&ni->ni_nid) && ni->ni_interface) {
+                               struct nlattr *intf_nest, *intf_attr;
+
+                               intf_nest = nla_nest_start(msg,
+                                                          LNET_NET_LOCAL_NI_ATTR_INTERFACE);
+                               intf_attr = nla_nest_start(msg, 0);
+                               nla_put_string(msg,
+                                              LNET_NET_LOCAL_NI_INTF_ATTR_TYPE,
+                                              ni->ni_interface);
+                               nla_nest_end(msg, intf_attr);
+                               nla_nest_end(msg, intf_nest);
+                       }
+
+                       lnet_ni_unlock(ni);
+                       nla_nest_end(msg, ni_attr);
+                       nla_nest_end(msg, local_ni);
+
+                       genlmsg_end(msg, hdr);
+               }
+       }
+
+       if (!found) {
+               struct nlmsghdr *nlh = nlmsg_hdr(msg);
+
+               nlmsg_cancel(msg, nlh);
+               NL_SET_ERR_MSG(extack, "Network is down");
+               rc = -ESRCH;
+       }
+net_unlock:
+       lnet_net_unlock(LNET_LOCK_EX);
+send_error:
+       nlist->lngl_idx = idx;
+
+       return lnet_nl_send_error(cb->skb, portid, seq, rc);
+}
+
+#ifndef HAVE_NETLINK_CALLBACK_START
+static int lnet_old_net_show_dump(struct sk_buff *msg,
+                                  struct netlink_callback *cb)
+{
+       if (!cb->args[0]) {
+               int rc = lnet_net_show_start(cb);
+
+               if (rc < 0)
+                       return rc;
+       }
+
+       return lnet_net_show_dump(msg, cb);
+}
+#endif
+
+static int lnet_genl_parse_tunables(struct nlattr *settings,
+                                   struct lnet_ioctl_config_lnd_tunables *tun)
+{
+       struct nlattr *param;
+       int rem, rc = 0;
+
+       nla_for_each_nested(param, settings, rem) {
+               int type = LNET_NET_LOCAL_NI_TUNABLES_ATTR_UNSPEC;
+               s64 num;
+
+               if (nla_type(param) != LN_SCALAR_ATTR_VALUE)
+                       continue;
+
+               if (nla_strcmp(param, "peer_timeout") == 0)
+                       type = LNET_NET_LOCAL_NI_TUNABLES_ATTR_PEER_TIMEOUT;
+               else if (nla_strcmp(param, "peer_credits") == 0)
+                       type = LNET_NET_LOCAL_NI_TUNABLES_ATTR_PEER_CREDITS;
+               else if (nla_strcmp(param, "peer_buffer_credits") == 0)
+                       type = LNET_NET_LOCAL_NI_TUNABLES_ATTR_PEER_BUFFER_CREDITS;
+               else if (nla_strcmp(param, "credits") == 0)
+                       type = LNET_NET_LOCAL_NI_TUNABLES_ATTR_CREDITS;
+
+               param = nla_next(param, &rem);
+               if (nla_type(param) != LN_SCALAR_ATTR_INT_VALUE)
+                       return -EINVAL;
+
+               num = nla_get_s64(param);
+               switch (type) {
+               case LNET_NET_LOCAL_NI_TUNABLES_ATTR_PEER_TIMEOUT:
+                       tun->lt_cmn.lct_peer_timeout = num;
+                       break;
+               case LNET_NET_LOCAL_NI_TUNABLES_ATTR_PEER_CREDITS:
+                       tun->lt_cmn.lct_peer_tx_credits = num;
+                       break;
+               case LNET_NET_LOCAL_NI_TUNABLES_ATTR_PEER_BUFFER_CREDITS:
+                       tun->lt_cmn.lct_peer_rtr_credits = num;
+                       break;
+               case LNET_NET_LOCAL_NI_TUNABLES_ATTR_CREDITS:
+                       tun->lt_cmn.lct_max_tx_credits = num;
+                       break;
+               default:
+                       rc = -EINVAL;
+                       break;
+               }
+       }
+       return rc;
+}
+
+static int
+lnet_genl_parse_lnd_tunables(struct nlattr *settings,
+                            struct lnet_ioctl_config_lnd_tunables *tun,
+                            const struct lnet_lnd *lnd)
+{
+       const struct ln_key_list *list = lnd->lnd_keys;
+       struct nlattr *param;
+       int rem, rc = 0;
+       int i = 1;
+
+       if (!list)
+               return 0;
+
+       if (!lnd->lnd_nl_set)
+               return -EOPNOTSUPP;
+
+       if (!list->lkl_maxattr)
+               return -ERANGE;
+
+       nla_for_each_nested(param, settings, rem) {
+               if (nla_type(param) != LN_SCALAR_ATTR_VALUE)
+                       continue;
+
+               for (i = 1; i <= list->lkl_maxattr; i++) {
+                       if (!list->lkl_list[i].lkp_value ||
+                           nla_strcmp(param, list->lkl_list[i].lkp_value) != 0)
+                               continue;
+
+                       param = nla_next(param, &rem);
+                       rc = lnd->lnd_nl_set(LNET_CMD_NETS, param, i, tun);
+                       if (rc < 0)
+                               return rc;
+               }
+       }
+
+       return rc;
+}
+
+static int
+lnet_genl_parse_local_ni(struct nlattr *entry, struct genl_info *info,
+                        int net_id, struct lnet_ioctl_config_ni *conf,
+                        struct lnet_ioctl_config_lnd_tunables *tun,
+                        bool *ni_list)
+{
+       struct nlattr *settings;
+       int rem3, rc = 0;
+
+       nla_for_each_nested(settings, entry, rem3) {
+               if (nla_type(settings) != LN_SCALAR_ATTR_VALUE)
+                       continue;
+
+               if (nla_strcmp(settings, "interfaces") == 0) {
+                       struct nlattr *intf;
+                       int rem4;
+
+                       settings = nla_next(settings, &rem3);
+                       if (nla_type(settings) !=
+                           LN_SCALAR_ATTR_LIST) {
+                               GENL_SET_ERR_MSG(info,
+                                                "invalid interfaces");
+                               GOTO(out, rc = -EINVAL);
+                       }
+
+                       nla_for_each_nested(intf, settings, rem4) {
+                               intf = nla_next(intf, &rem4);
+                               if (nla_type(intf) !=
+                                   LN_SCALAR_ATTR_VALUE) {
+                                       GENL_SET_ERR_MSG(info,
+                                                        "0 key is invalid");
+                                       GOTO(out, rc = -EINVAL);
+                               }
+
+                               rc = nla_strscpy(conf->lic_ni_intf, intf,
+                                                sizeof(conf->lic_ni_intf));
+                               if (rc < 0) {
+                                       GENL_SET_ERR_MSG(info,
+                                                        "failed to parse interfaces");
+                                       GOTO(out, rc);
+                               }
+                       }
+                       *ni_list = true;
+               } else if (nla_strcmp(settings, "tunables") == 0) {
+                       settings = nla_next(settings, &rem3);
+                       if (nla_type(settings) !=
+                           LN_SCALAR_ATTR_LIST) {
+                               GENL_SET_ERR_MSG(info,
+                                                "invalid tunables");
+                               GOTO(out, rc = -EINVAL);
+                       }
+
+                       rc = lnet_genl_parse_tunables(settings, tun);
+                       if (rc < 0) {
+                               GENL_SET_ERR_MSG(info,
+                                                "failed to parse tunables");
+                               GOTO(out, rc);
+                       }
+               } else if ((nla_strcmp(settings, "lnd tunables") == 0)) {
+                       const struct lnet_lnd *lnd;
+
+                       lnd = lnet_load_lnd(LNET_NETTYP(net_id));
+                       if (IS_ERR(lnd)) {
+                               GENL_SET_ERR_MSG(info,
+                                                "LND type not supported");
+                               GOTO(out, rc = PTR_ERR(lnd));
+                       }
+
+                       settings = nla_next(settings, &rem3);
+                       if (nla_type(settings) !=
+                           LN_SCALAR_ATTR_LIST) {
+                               GENL_SET_ERR_MSG(info,
+                                                "lnd tunables should be list\n");
+                               GOTO(out, rc = -EINVAL);
+                       }
+
+                       rc = lnet_genl_parse_lnd_tunables(settings,
+                                                         tun, lnd);
+                       if (rc < 0) {
+                               GENL_SET_ERR_MSG(info,
+                                                "failed to parse lnd tunables");
+                               GOTO(out, rc);
+                       }
+               } else if (nla_strcmp(settings, "CPT") == 0) {
+                       struct nlattr *cpt;
+                       int rem4;
+
+                       settings = nla_next(settings, &rem3);
+                       if (nla_type(settings) != LN_SCALAR_ATTR_LIST) {
+                               GENL_SET_ERR_MSG(info,
+                                                "CPT should be list");
+                               GOTO(out, rc = -EINVAL);
+                       }
+
+                       nla_for_each_nested(cpt, settings, rem4) {
+                               s64 core;
+
+                               if (nla_type(cpt) !=
+                                   LN_SCALAR_ATTR_INT_VALUE) {
+                                       GENL_SET_ERR_MSG(info,
+                                                        "invalid CPT config");
+                                       GOTO(out, rc = -EINVAL);
+                               }
+
+                               core = nla_get_s64(cpt);
+                               if (core >= LNET_CPT_NUMBER) {
+                                       GENL_SET_ERR_MSG(info,
+                                                        "invalid CPT value");
+                                       GOTO(out, rc = -ERANGE);
+                               }
+
+                               conf->lic_cpts[conf->lic_ncpts] = core;
+                               conf->lic_ncpts++;
+                       }
+               }
+       }
+out:
+       return rc;
+}
+
+static int lnet_net_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nlmsghdr *nlh = nlmsg_hdr(skb);
+       struct genlmsghdr *gnlh = nlmsg_data(nlh);
+       struct nlattr *params = genlmsg_data(gnlh);
+       int msg_len, rem, rc = 0;
+       struct nlattr *attr;
+
+       msg_len = genlmsg_len(gnlh);
+       if (!msg_len) {
+               GENL_SET_ERR_MSG(info, "no configuration");
+               return -ENOMSG;
+       }
+
+       nla_for_each_attr(attr, params, msg_len, rem) {
+               struct lnet_ioctl_config_ni conf;
+               u32 net_id = LNET_NET_ANY;
+               struct nlattr *entry;
+               bool ni_list = false;
+               int rem2;
+
+               if (nla_type(attr) != LN_SCALAR_ATTR_LIST)
+                       continue;
+
+               nla_for_each_nested(entry, attr, rem2) {
+                       switch (nla_type(entry)) {
+                       case LN_SCALAR_ATTR_VALUE: {
+                               size_t len;
+
+                               memset(&conf, 0, sizeof(conf));
+                               if (nla_strcmp(entry, "ip2net") == 0) {
+                                       entry = nla_next(entry, &rem2);
+                                       if (nla_type(entry) !=
+                                           LN_SCALAR_ATTR_VALUE) {
+                                               GENL_SET_ERR_MSG(info,
+                                                                "ip2net has invalid key");
+                                               GOTO(out, rc = -EINVAL);
+                                       }
+
+                                       len = nla_strscpy(conf.lic_legacy_ip2nets,
+                                                         entry,
+                                                         sizeof(conf.lic_legacy_ip2nets));
+                                       if (len < 0) {
+                                               GENL_SET_ERR_MSG(info,
+                                                                "ip2net key string is invalid");
+                                               GOTO(out, rc = len);
+                                       }
+                                       ni_list = true;
+                               } else if (nla_strcmp(entry, "net type") == 0) {
+                                       char tmp[LNET_NIDSTR_SIZE];
+
+                                       entry = nla_next(entry, &rem2);
+                                       if (nla_type(entry) !=
+                                           LN_SCALAR_ATTR_VALUE) {
+                                               GENL_SET_ERR_MSG(info,
+                                                                "net type has invalid key");
+                                               GOTO(out, rc = -EINVAL);
+                                       }
+
+                                       len = nla_strscpy(tmp, entry,
+                                                         sizeof(tmp));
+                                       if (len < 0) {
+                                               GENL_SET_ERR_MSG(info,
+                                                                "net type key string is invalid");
+                                               GOTO(out, rc = len);
+                                       }
+
+                                       net_id = libcfs_str2net(tmp);
+                                       if (!net_id) {
+                                               GENL_SET_ERR_MSG(info,
+                                                                "cannot parse net");
+                                               GOTO(out, rc = -ENODEV);
+                                       }
+                                       if (LNET_NETTYP(net_id) == LOLND) {
+                                               GENL_SET_ERR_MSG(info,
+                                                                "setting @lo not allowed");
+                                               GOTO(out, rc = -ENODEV);
+                                       }
+                                       conf.lic_legacy_ip2nets[0] = '\0';
+                                       conf.lic_ni_intf[0] = '\0';
+                                       ni_list = false;
+                               }
+                               if (rc < 0)
+                                       GOTO(out, rc);
+                               break;
+                       }
+                       case LN_SCALAR_ATTR_LIST: {
+                               bool create = info->nlhdr->nlmsg_flags &
+                                             NLM_F_CREATE;
+                               struct lnet_ioctl_config_lnd_tunables tun;
+
+                               memset(&tun, 0, sizeof(tun));
+                               tun.lt_cmn.lct_peer_timeout = -1;
+                               conf.lic_ncpts = 0;
+
+                               rc = lnet_genl_parse_local_ni(entry, info,
+                                                             net_id, &conf,
+                                                             &tun, &ni_list);
+                               if (rc < 0)
+                                       GOTO(out, rc);
+
+                               if (!create) {
+                                       struct lnet_net *net;
+                                       struct lnet_ni *ni;
+
+                                       rc = -ENODEV;
+                                       if (!strlen(conf.lic_ni_intf)) {
+                                               GENL_SET_ERR_MSG(info,
+                                                                "interface is missing");
+                                               GOTO(out, rc);
+                                       }
+
+                                       lnet_net_lock(LNET_LOCK_EX);
+                                       net = lnet_get_net_locked(net_id);
+                                       if (!net) {
+                                               GENL_SET_ERR_MSG(info,
+                                                                "LNet net doesn't exist");
+                                               GOTO(out, rc);
+                                       }
+                                       list_for_each_entry(ni, &net->net_ni_list,
+                                                           ni_netlist) {
+                                               if (!ni->ni_interface ||
+                                                   strncmp(ni->ni_interface,
+                                                           conf.lic_ni_intf,
+                                                           strlen(conf.lic_ni_intf)) != 0) {
+                                                       ni = NULL;
+                                                       continue;
+                                               }
+
+                                               lnet_net_unlock(LNET_LOCK_EX);
+                                               rc = lnet_dyn_del_ni(&ni->ni_nid);
+                                               lnet_net_lock(LNET_LOCK_EX);
+                                               if (rc < 0) {
+                                                       GENL_SET_ERR_MSG(info,
+                                                                        "cannot del LNet NI");
+                                                       GOTO(out, rc);
+                                               }
+                                               break;
+                                       }
+
+                                       lnet_net_unlock(LNET_LOCK_EX);
+                               } else {
+                                       rc = lnet_dyn_add_ni(&conf, net_id, &tun);
+                                       switch (rc) {
+                                       case -ENOENT:
+                                               GENL_SET_ERR_MSG(info,
+                                                                "cannot parse net");
+                                               break;
+                                       case -ERANGE:
+                                               GENL_SET_ERR_MSG(info,
+                                                                "invalid CPT set");
+                                       fallthrough;
+                                       default:
+                                               GENL_SET_ERR_MSG(info,
+                                                                "cannot add LNet NI");
+                                       case 0:
+                                               break;
+                                       }
+                                       if (rc < 0)
+                                               GOTO(out, rc);
+                               }
+                               break;
+                       }
+                       /* it is possible a newer version of the user land send
+                        * values older kernels doesn't handle. So silently
+                        * ignore these values
+                        */
+                       default:
+                               break;
+                       }
+               }
+
+               /* Handle case of just sent NET with no list of NIDs */
+               if (!(info->nlhdr->nlmsg_flags & NLM_F_CREATE) && !ni_list) {
+                       rc = lnet_dyn_del_net(net_id);
+                       if (rc < 0) {
+                               GENL_SET_ERR_MSG(info,
+                                                "cannot del network");
+                       }
+               }
+       }
+out:
+       return rc;
+}
+
+static const struct genl_multicast_group lnet_mcast_grps[] = {
+       { .name =       "ip2net",       },
+       { .name =       "net",          },
+};
+
+static const struct genl_ops lnet_genl_ops[] = {
+       {
+               .cmd            = LNET_CMD_NETS,
+#ifdef HAVE_NETLINK_CALLBACK_START
+               .start          = lnet_net_show_start,
+               .dumpit         = lnet_net_show_dump,
+#else
+               .dumpit         = lnet_old_net_show_dump,
+#endif
+               .done           = lnet_net_show_done,
+               .doit           = lnet_net_cmd,
+       },
+};
+
+static struct genl_family lnet_family = {
+       .name           = LNET_GENL_NAME,
+       .version        = LNET_GENL_VERSION,
+       .module         = THIS_MODULE,
+       .netnsok        = true,
+       .ops            = lnet_genl_ops,
+       .n_ops          = ARRAY_SIZE(lnet_genl_ops),
+       .mcgrps         = lnet_mcast_grps,
+       .n_mcgrps       = ARRAY_SIZE(lnet_mcast_grps),
+};
+
 void LNetDebugPeer(struct lnet_processid *id)
 {
        lnet_debug_peer(lnet_nid_to_nid4(&id->nid));
index d01bfb2..5dc820e 100644 (file)
@@ -378,8 +378,7 @@ lnet_net_alloc(__u32 net_id, struct list_head *net_list)
        return net;
 }
 
-static int
-lnet_ni_add_interface(struct lnet_ni *ni, char *iface)
+int lnet_ni_add_interface(struct lnet_ni *ni, char *iface)
 {
        size_t iface_len = strlen(iface) + 1;
 
@@ -410,6 +409,7 @@ lnet_ni_add_interface(struct lnet_ni *ni, char *iface)
 
        return 0;
 }
+EXPORT_SYMBOL(lnet_ni_add_interface);
 
 static struct lnet_ni *
 lnet_ni_alloc_common(struct lnet_net *net, char *iface)
index fba972c..5076a2c 100644 (file)
@@ -40,8 +40,7 @@ MODULE_PARM_DESC(config_on_load, "configure network at module load");
 
 static DEFINE_MUTEX(lnet_config_mutex);
 
-static int
-lnet_configure(void *arg)
+int lnet_configure(void *arg)
 {
        /* 'arg' only there so I can be passed to cfs_create_thread() */
        int    rc = 0;
@@ -68,10 +67,9 @@ out:
        return rc;
 }
 
-static int
-lnet_unconfigure (void)
+int lnet_unconfigure(void)
 {
-       int   refcount;
+       int refcount;
 
        mutex_lock(&lnet_config_mutex);
 
@@ -135,16 +133,26 @@ lnet_dyn_configure_ni(struct libcfs_ioctl_hdr *hdr)
 {
        struct lnet_ioctl_config_ni *conf =
          (struct lnet_ioctl_config_ni *)hdr;
-       int                           rc;
+       int rc = -EINVAL;
 
        if (conf->lic_cfg_hdr.ioc_len < sizeof(*conf))
-               return -EINVAL;
+               return rc;
 
        mutex_lock(&lnet_config_mutex);
-       if (the_lnet.ln_niinit_self)
-               rc = lnet_dyn_add_ni(conf);
-       else
-               rc = -EINVAL;
+       if (the_lnet.ln_niinit_self) {
+               struct lnet_ioctl_config_lnd_tunables *tun = NULL;
+               struct lnet_nid nid;
+               u32 net_id;
+
+               /* get the tunables if they are available */
+               if (conf->lic_cfg_hdr.ioc_len >=
+                   sizeof(*conf) + sizeof(*tun))
+                       tun = (struct lnet_ioctl_config_lnd_tunables *) conf->lic_bulk;
+
+               lnet_nid4_to_nid(conf->lic_nid, &nid);
+               net_id = LNET_NID_NET(&nid);
+               rc = lnet_dyn_add_ni(conf, net_id, tun);
+       }
        mutex_unlock(&lnet_config_mutex);
 
        return rc;
@@ -155,14 +163,17 @@ lnet_dyn_unconfigure_ni(struct libcfs_ioctl_hdr *hdr)
 {
        struct lnet_ioctl_config_ni *conf =
          (struct lnet_ioctl_config_ni *) hdr;
-       int                           rc;
+       struct lnet_nid nid;
+       int rc = -EINVAL;
 
-       if (conf->lic_cfg_hdr.ioc_len < sizeof(*conf))
-               return -EINVAL;
+       if (conf->lic_cfg_hdr.ioc_len < sizeof(*conf) ||
+           !the_lnet.ln_niinit_self)
+               return rc;
 
+       lnet_nid4_to_nid(conf->lic_nid, &nid);
        mutex_lock(&lnet_config_mutex);
        if (the_lnet.ln_niinit_self)
-               rc = lnet_dyn_del_ni(conf);
+               rc = lnet_dyn_del_ni(&nid);
        else
                rc = -EINVAL;
        mutex_unlock(&lnet_config_mutex);
index a51993c..664534a 100644 (file)
@@ -55,5 +55,5 @@ endif # TESTS
 
 lnetctl_SOURCES = lnetctl.c
 lnetctl_LDADD = $(top_builddir)/lnet/utils/lnetconfig/liblnetconfig.la \
-               $(LIBEFENCE)
+               $(LIBNL3_LIBS) -lyaml $(LIBEFENCE)
 endif # UTILS
index 802a294..aac8a16 100644 (file)
@@ -313,6 +313,7 @@ struct yaml_netlink_input {
        yaml_parser_t           *parser;
        void                    *buffer;
        const char              *errmsg;
+       int                     error;
        struct nl_sock          *nl;
        bool                    complete;
        bool                    async;
@@ -535,7 +536,7 @@ static void yaml_parse_value_list(struct yaml_netlink_input *data, int *size,
 
                if (keys[i].lkp_data_type != NLA_NUL_STRING &&
                    keys[i].lkp_data_type != NLA_NESTED) {
-                       if (!attr)
+                       if (!attr && keys[i].lkp_data_type != NLA_FLAG)
                                continue;
 
                        if (!(mapping & LNKF_FLOW)) {
@@ -685,6 +686,11 @@ not_first:
                                       nla_get_string(attr));
                        break;
 
+               case NLA_FLAG:
+                       len = snprintf(data->buffer, *size, "%s",
+                                      attr ? "true" : "false");
+                       break;
+
                case NLA_U16:
                        len = snprintf(data->buffer, *size, "%hu",
                                       nla_get_u16(attr));
@@ -837,6 +843,7 @@ static int yaml_netlink_msg_error(struct sockaddr_nl *who,
                }
 #endif /* HAVE_USRSPC_NLMSGERR */
                data->errmsg = errstr;
+               data->error = errmsg->error;
                data->parser->error = YAML_READER_ERROR;
                data->complete = true;
        }
@@ -960,6 +967,7 @@ yaml_parser_get_reader_error(yaml_parser_t *parser)
        if (!buf)
                return NULL;
 
+       errno = buf->error;
        return buf->errmsg;
 }
 
@@ -1127,10 +1135,16 @@ static int yaml_fill_scalar_data(struct nl_msg *msg,
                                 enum lnet_nl_key_format fmt,
                                 char *line)
 {
-       char *sep = strchr(line, ':');
+       char *sep = strstr(line, ": "); /* handle mappings */
        int rc = 0;
        long num;
 
+       if (!sep) {
+               char *tmp = strchr(line, ':');
+
+               if (tmp && strlen(tmp) == 1) /* handle simple scalar */
+                       sep = tmp;
+       }
        if (sep)
                *sep++ = '\0';
 
@@ -1193,7 +1207,7 @@ static int yaml_create_nested_list(struct yaml_netlink_output *out,
        }
 
        if (fmt & LNKF_FLOW) {
-               char *tmp = strchr(*hdr, '{');
+               char *tmp = strchr(*hdr, '{'), *split = NULL;
                bool format = false;
 
                if (!tmp) {
@@ -1224,6 +1238,7 @@ static int yaml_create_nested_list(struct yaml_netlink_output *out,
 
                        /* Flow can be splt across lines by libyaml library. */
                        if (strchr(line, ',')) {
+                               split = line;
                                *hdr = line;
                                continue;
                        }
@@ -1242,7 +1257,10 @@ static int yaml_create_nested_list(struct yaml_netlink_output *out,
 
                        /* Move to next YAML line */
                        if (format) {
-                               strsep(entry, "\n");
+                               if (!split)
+                                       line = *entry;
+                               else
+                                       *entry = NULL;
                                break;
                        }
                }
@@ -1254,6 +1272,9 @@ static int yaml_create_nested_list(struct yaml_netlink_output *out,
                        goto nla_put_failure;
                }
 
+               if (line && line[0] == '-')
+                       *indent = 0;
+
                nla_nest_end(msg, list);
        } else {
                do {
@@ -1282,9 +1303,9 @@ have_next_line:
                        }
                } while (strcmp(*entry, ""));
 
-               if (line && line[0] == '-'  && !*indent) {
-                       line[0] = ' ';
-                       *indent = 2;
+               if (line && line[*indent] == '-') {
+                       line[*indent] = ' ';
+                       *indent += 2;
                        goto have_next_line;
                }
                if (*entry && !strlen(*entry))
index 380d60d..2338230 100644 (file)
@@ -928,6 +928,135 @@ static int jt_set_max_recovery_ping_interval(int argc, char **argv)
        return rc;
 }
 
+static void yaml_lnet_print_error(char *flag, char *cmd, const char *errstr)
+{
+       yaml_emitter_t log;
+       yaml_event_t event;
+       char errcode[23];
+       int rc;
+
+       snprintf(errcode, sizeof(errcode), "%d", errno);
+
+       yaml_emitter_initialize(&log);
+       yaml_emitter_set_output_file(&log, stdout);
+
+       yaml_emitter_open(&log);
+       yaml_document_start_event_initialize(&event, NULL, NULL, NULL, 0);
+       rc = yaml_emitter_emit(&log, &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(&log, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       yaml_scalar_event_initialize(&event, NULL,
+                                    (yaml_char_t *)YAML_STR_TAG,
+                                    (yaml_char_t *)flag,
+                                    strlen(flag), 1, 0,
+                                    YAML_PLAIN_SCALAR_STYLE);
+       rc = yaml_emitter_emit(&log, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       yaml_sequence_start_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_SEQ_TAG,
+                                            1, YAML_ANY_SEQUENCE_STYLE);
+       rc = yaml_emitter_emit(&log, &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(&log, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       yaml_scalar_event_initialize(&event, NULL,
+                                    (yaml_char_t *)YAML_STR_TAG,
+                                    (yaml_char_t *)cmd,
+                                    strlen(cmd),
+                                    1, 0, YAML_PLAIN_SCALAR_STYLE);
+       rc = yaml_emitter_emit(&log, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       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(&log, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       yaml_scalar_event_initialize(&event, NULL,
+                                    (yaml_char_t *)YAML_STR_TAG,
+                                    (yaml_char_t *)"errno",
+                                    strlen("errno"), 1, 0,
+                                    YAML_PLAIN_SCALAR_STYLE);
+       rc = yaml_emitter_emit(&log, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       yaml_scalar_event_initialize(&event, NULL,
+                                    (yaml_char_t *)YAML_INT_TAG,
+                                    (yaml_char_t *)errcode,
+                                    strlen(errcode), 1, 0,
+                                    YAML_PLAIN_SCALAR_STYLE);
+       rc = yaml_emitter_emit(&log, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+
+       yaml_scalar_event_initialize(&event, NULL,
+                                    (yaml_char_t *)YAML_STR_TAG,
+                                    (yaml_char_t *)"descr",
+                                    strlen("descr"), 1, 0,
+                                    YAML_PLAIN_SCALAR_STYLE);
+       rc = yaml_emitter_emit(&log, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       yaml_scalar_event_initialize(&event, NULL,
+                                    (yaml_char_t *)YAML_INT_TAG,
+                                    (yaml_char_t *)errstr,
+                                    strlen(errstr), 1, 0,
+                                    YAML_PLAIN_SCALAR_STYLE);
+       rc = yaml_emitter_emit(&log, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       yaml_mapping_end_event_initialize(&event);
+       rc = yaml_emitter_emit(&log, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       yaml_sequence_end_event_initialize(&event);
+       rc = yaml_emitter_emit(&log, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       yaml_mapping_end_event_initialize(&event);
+       rc = yaml_emitter_emit(&log, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       yaml_document_end_event_initialize(&event, 0);
+       rc = yaml_emitter_emit(&log, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       rc = yaml_emitter_close(&log);
+emitter_error:
+       if (rc == 0)
+               yaml_emitter_log_error(&log, stdout);
+       yaml_emitter_delete(&log);
+}
 
 static int jt_config_lnet(int argc, char **argv)
 {
@@ -1057,6 +1186,475 @@ static int jt_add_route(int argc, char **argv)
        return rc;
 }
 
+static int yaml_add_ni_tunables(yaml_emitter_t *output,
+                               struct lnet_ioctl_config_lnd_tunables *tunables,
+                               int cpp)
+{
+       yaml_event_t event;
+       char num[23];
+       int rc;
+
+       yaml_scalar_event_initialize(&event, NULL,
+                                    (yaml_char_t *)YAML_STR_TAG,
+                                    (yaml_char_t *)"tunables",
+                                    strlen("tunables"), 1, 0,
+                                    YAML_PLAIN_SCALAR_STYLE);
+       rc = yaml_emitter_emit(output, &event);
+       if (rc == 0)
+               goto error;
+
+       yaml_mapping_start_event_initialize(&event, NULL,
+                                           (yaml_char_t *)YAML_MAP_TAG,
+                                           1, YAML_ANY_MAPPING_STYLE);
+       rc = yaml_emitter_emit(output, &event);
+       if (rc == 0)
+               goto error;
+
+       if (tunables->lt_cmn.lct_peer_timeout >= 0) {
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_STR_TAG,
+                                            (yaml_char_t *)"peer_timeout",
+                                            strlen("peer_timeout"), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto error;
+
+               snprintf(num, sizeof(num), "%u",
+                        tunables->lt_cmn.lct_peer_timeout);
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_INT_TAG,
+                                            (yaml_char_t *)num,
+                                            strlen(num), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto error;
+       }
+
+       if (tunables->lt_cmn.lct_peer_tx_credits > 0) {
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_STR_TAG,
+                                            (yaml_char_t *)"peer_credits",
+                                            strlen("peer_credits"), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto error;
+
+               snprintf(num, sizeof(num), "%u",
+                        tunables->lt_cmn.lct_peer_tx_credits);
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_INT_TAG,
+                                            (yaml_char_t *)num,
+                                            strlen(num), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto error;
+       }
+
+       if (tunables->lt_cmn.lct_peer_rtr_credits > 0) {
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_STR_TAG,
+                                            (yaml_char_t *)"peer_buffer_credits",
+                                            strlen("peer_buffer_credits"), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto error;
+
+               snprintf(num, sizeof(num), "%u",
+                        tunables->lt_cmn.lct_peer_rtr_credits);
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_INT_TAG,
+                                            (yaml_char_t *)num,
+                                            strlen(num), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto error;
+       }
+
+       if (tunables->lt_cmn.lct_max_tx_credits > 0) {
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_STR_TAG,
+                                            (yaml_char_t *)"credits",
+                                            strlen("credits"), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto error;
+
+               snprintf(num, sizeof(num), "%u",
+                        tunables->lt_cmn.lct_max_tx_credits);
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_INT_TAG,
+                                            (yaml_char_t *)num,
+                                            strlen(num), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto error;
+       }
+
+       yaml_mapping_end_event_initialize(&event);
+       rc = yaml_emitter_emit(output, &event);
+       if (rc == 0)
+               goto error;
+
+       if (cpp > 0 || tunables->lt_tun.lnd_tun_u.lnd_kfi.lnd_auth_key > 0) {
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_STR_TAG,
+                                            (yaml_char_t *)"lnd tunables",
+                                            strlen("lnd tunables"), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto error;
+
+               yaml_mapping_start_event_initialize(&event, NULL,
+                                                   (yaml_char_t *)YAML_MAP_TAG,
+                                                   1, YAML_ANY_MAPPING_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto error;
+
+               if (tunables->lt_tun.lnd_tun_u.lnd_kfi.lnd_auth_key > 0) {
+                       yaml_scalar_event_initialize(&event, NULL,
+                                                    (yaml_char_t *)YAML_STR_TAG,
+                                                    (yaml_char_t *)"auth_key",
+                                                    strlen("auth_key"), 1, 0,
+                                                    YAML_PLAIN_SCALAR_STYLE);
+                       rc = yaml_emitter_emit(output, &event);
+                       if (rc == 0)
+                               goto error;
+
+                       snprintf(num, sizeof(num), "%u",
+                                tunables->lt_tun.lnd_tun_u.lnd_kfi.lnd_auth_key);
+               } else {
+                       yaml_scalar_event_initialize(&event, NULL,
+                                                    (yaml_char_t *)YAML_STR_TAG,
+                                                    (yaml_char_t *)"conns_per_peer",
+                                                    strlen("conns_per_peer"), 1, 0,
+                                                    YAML_PLAIN_SCALAR_STYLE);
+                       rc = yaml_emitter_emit(output, &event);
+                       if (rc == 0)
+                               goto error;
+
+                       snprintf(num, sizeof(num), "%u", cpp);
+               }
+
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_INT_TAG,
+                                            (yaml_char_t *)num,
+                                            strlen(num), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto error;
+
+               yaml_mapping_end_event_initialize(&event);
+               rc = yaml_emitter_emit(output, &event);
+               if (rc == 0)
+                       goto error;
+       }
+error:
+       return rc;
+}
+
+static int yaml_lnet_config_ni(char *net_id, char *ip2net,
+                              struct lnet_dlc_network_descr *nw_descr,
+                              struct lnet_ioctl_config_lnd_tunables *tunables,
+                              int cpp, struct cfs_expr_list *global_cpts,
+                              int flags)
+{
+       struct lnet_dlc_intf_descr *intf;
+       struct nl_sock *sk = NULL;
+       yaml_emitter_t output;
+       yaml_parser_t reply;
+       yaml_event_t event;
+       int rc;
+
+       if (!ip2net && (!nw_descr || nw_descr->nw_id == 0 ||
+           (list_empty(&nw_descr->nw_intflist)))) {
+               fprintf(stdout, "missing mandatory parameters in NI config: '%s'",
+                       (!nw_descr) ? "network , interface" :
+                       (nw_descr->nw_id == 0) ? "network" : "interface");
+               return -EINVAL;
+       }
+
+       /* 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(&output);
+       if (rc == 0)
+               goto free_reply;
+
+       rc = yaml_emitter_set_output_netlink(&output, sk, LNET_GENL_NAME,
+                                            LNET_GENL_VERSION, LNET_CMD_NETS,
+                                            flags);
+       if (rc == 0)
+               goto emitter_error;
+
+       yaml_emitter_open(&output);
+       yaml_document_start_event_initialize(&event, NULL, NULL, NULL, 0);
+       rc = yaml_emitter_emit(&output, &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(&output, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       yaml_scalar_event_initialize(&event, NULL,
+                                    (yaml_char_t *)YAML_STR_TAG,
+                                    (yaml_char_t *)"net",
+                                    strlen("net"), 1, 0,
+                                    YAML_PLAIN_SCALAR_STYLE);
+       rc = yaml_emitter_emit(&output, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       if (net_id || ip2net) {
+               char *key = net_id ? "net type" : "ip2net";
+               char *value = net_id ? net_id : ip2net;
+
+               yaml_sequence_start_event_initialize(&event, NULL,
+                                                    (yaml_char_t *)YAML_SEQ_TAG,
+                                                    1, YAML_ANY_SEQUENCE_STYLE);
+               rc = yaml_emitter_emit(&output, &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(&output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_STR_TAG,
+                                            (yaml_char_t *)key,
+                                            strlen(key),
+                                            1, 0, YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(&output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_STR_TAG,
+                                            (yaml_char_t *)value,
+                                            strlen(value), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(&output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+       } else {
+               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(&output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+
+               goto no_net_id;
+       }
+
+       if (list_empty(&nw_descr->nw_intflist))
+               goto skip_intf;
+
+       yaml_scalar_event_initialize(&event, NULL,
+                                    (yaml_char_t *)YAML_STR_TAG,
+                                    (yaml_char_t *)"local NI(s)",
+                                    strlen("local NI(s)"), 1, 0,
+                                    YAML_PLAIN_SCALAR_STYLE);
+       rc = yaml_emitter_emit(&output, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       yaml_sequence_start_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_SEQ_TAG,
+                                            1, YAML_ANY_SEQUENCE_STYLE);
+       rc = yaml_emitter_emit(&output, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       list_for_each_entry(intf, &nw_descr->nw_intflist,
+                           intf_on_network) {
+               yaml_mapping_start_event_initialize(&event, NULL,
+                                                   (yaml_char_t *)YAML_MAP_TAG,
+                                                   1, YAML_ANY_MAPPING_STYLE);
+               rc = yaml_emitter_emit(&output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_STR_TAG,
+                                            (yaml_char_t *)"interfaces",
+                                            strlen("interfaces"), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(&output, &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(&output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_STR_TAG,
+                                            (yaml_char_t *)"0",
+                                            strlen("0"), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(&output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+
+               yaml_scalar_event_initialize(&event, NULL,
+                                            (yaml_char_t *)YAML_STR_TAG,
+                                            (yaml_char_t *)intf->intf_name,
+                                            strlen(intf->intf_name), 1, 0,
+                                            YAML_PLAIN_SCALAR_STYLE);
+               rc = yaml_emitter_emit(&output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+
+               yaml_mapping_end_event_initialize(&event);
+               rc = yaml_emitter_emit(&output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+
+               if (tunables) {
+                       rc = yaml_add_ni_tunables(&output, tunables, cpp);
+                       if (rc == 0)
+                               goto emitter_error;
+               }
+
+               if (global_cpts) {
+                       __u32 *cpt_array;
+                       int count, i;
+
+                       yaml_scalar_event_initialize(&event, NULL,
+                                                    (yaml_char_t *)YAML_STR_TAG,
+                                                    (yaml_char_t *)"CPT",
+                                                    strlen("CPT"), 1, 0,
+                                                    YAML_PLAIN_SCALAR_STYLE);
+                       rc = yaml_emitter_emit(&output, &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(&output, &event);
+                       if (rc == 0)
+                               goto emitter_error;
+
+                       count = cfs_expr_list_values(global_cpts,
+                                                    LNET_MAX_SHOW_NUM_CPT,
+                                                    &cpt_array);
+                       for (i = 0; i < count; i++) {
+                               char core[23];
+
+                               snprintf(core, sizeof(core), "%u", cpt_array[i]);
+                               yaml_scalar_event_initialize(&event, NULL,
+                                                            (yaml_char_t *)YAML_STR_TAG,
+                                                            (yaml_char_t *)core,
+                                                            strlen(core), 1, 0,
+                                                            YAML_PLAIN_SCALAR_STYLE);
+                               rc = yaml_emitter_emit(&output, &event);
+                               if (rc == 0)
+                                       goto emitter_error;
+                       }
+
+                       yaml_sequence_end_event_initialize(&event);
+                       rc = yaml_emitter_emit(&output, &event);
+                       if (rc == 0)
+                               goto emitter_error;
+
+                       cfs_expr_list_free(global_cpts);
+                       free(cpt_array);
+               }
+
+               yaml_mapping_end_event_initialize(&event);
+               rc = yaml_emitter_emit(&output, &event);
+               if (rc == 0)
+                       goto emitter_error;
+       }
+
+       yaml_sequence_end_event_initialize(&event);
+       rc = yaml_emitter_emit(&output, &event);
+       if (rc == 0)
+               goto emitter_error;
+skip_intf:
+       yaml_mapping_end_event_initialize(&event);
+       rc = yaml_emitter_emit(&output, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       yaml_sequence_end_event_initialize(&event);
+       rc = yaml_emitter_emit(&output, &event);
+       if (rc == 0)
+               goto emitter_error;
+no_net_id:
+       yaml_mapping_end_event_initialize(&event);
+       rc = yaml_emitter_emit(&output, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       yaml_document_end_event_initialize(&event, 0);
+       rc = yaml_emitter_emit(&output, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       rc = yaml_emitter_close(&output);
+emitter_error:
+       if (rc == 0) {
+               yaml_emitter_log_error(&output, stderr);
+               rc = -EINVAL;
+       } else {
+               yaml_document_t errmsg;
+
+               rc = yaml_parser_load(&reply, &errmsg);
+       }
+       yaml_emitter_delete(&output);
+free_reply:
+       if (rc == 0) {
+               yaml_lnet_print_error(flags ? "add" : "del", "net",
+                                     yaml_parser_get_reader_error(&reply));
+               rc = -EINVAL;
+       }
+       yaml_parser_delete(&reply);
+       nl_socket_free(sk);
+
+       return rc == 1 ? 0 : rc;
+}
+
 static int jt_add_ni(int argc, char **argv)
 {
        char *ip2net = NULL;
@@ -1068,10 +1666,6 @@ static int jt_add_ni(int argc, char **argv)
        struct lnet_ioctl_config_lnd_tunables tunables;
        bool found = false;
        bool skip_mr_route_setup = false;
-
-       memset(&tunables, 0, sizeof(tunables));
-       lustre_lnet_init_nw_descr(&nw_descr);
-
        const char *const short_options = "a:b:c:i:k:m:n:p:r:s:t:";
        static const struct option long_options[] = {
        { .name = "auth-key",     .has_arg = required_argument, .val = 'a' },
@@ -1089,6 +1683,10 @@ static int jt_add_ni(int argc, char **argv)
        { .name = "cpt",          .has_arg = required_argument, .val = 's' },
        { .name = "peer-timeout", .has_arg = required_argument, .val = 't' },
        { .name = NULL } };
+       char *net_id = NULL;
+
+       memset(&tunables, 0, sizeof(tunables));
+       lustre_lnet_init_nw_descr(&nw_descr);
 
        rc = check_cmd(net_cmds, "net", "add", 0, argc, argv);
        if (rc)
@@ -1144,6 +1742,7 @@ static int jt_add_ni(int argc, char **argv)
 
                case 'n':
                        nw_descr.nw_id = libcfs_str2net(optarg);
+                       net_id = optarg;
                        break;
                case 'p':
                        ip2net = optarg;
@@ -1192,6 +1791,19 @@ static int jt_add_ni(int argc, char **argv)
        if (found && LNET_NETTYP(nw_descr.nw_id) == O2IBLND)
                tunables.lt_tun.lnd_tun_u.lnd_o2ib.lnd_map_on_demand = UINT_MAX;
 
+       rc = yaml_lnet_config_ni(net_id, ip2net, &nw_descr,
+                                found ? &tunables : NULL, cpp,
+                                (cpt_rc == 0) ? global_cpts : NULL,
+                                NLM_F_CREATE);
+       if (rc <= 0) {
+               if (global_cpts != NULL)
+                       cfs_expr_list_free(global_cpts);
+               if (rc == 0 && !skip_mr_route_setup)
+                       rc = lustre_lnet_setup_mrrouting(&err_rc);
+               if (rc != -EOPNOTSUPP)
+                       return rc;
+       }
+
        rc = lustre_lnet_config_ni(&nw_descr,
                                   (cpt_rc == 0) ? global_cpts: NULL,
                                   ip2net, (found) ? &tunables : NULL,
@@ -1266,24 +1878,25 @@ static int jt_del_ni(int argc, char **argv)
        struct cYAML *err_rc = NULL;
        int rc, opt;
        struct lnet_dlc_network_descr nw_descr;
-
-       lustre_lnet_init_nw_descr(&nw_descr);
-
        const char *const short_options = "n:i:";
        static const struct option long_options[] = {
        { .name = "net",        .has_arg = required_argument,   .val = 'n' },
        { .name = "if",         .has_arg = required_argument,   .val = 'i' },
        { .name = NULL } };
+       char *net_id = NULL;
 
        rc = check_cmd(net_cmds, "net", "del", 0, argc, argv);
        if (rc)
                return rc;
 
+       lustre_lnet_init_nw_descr(&nw_descr);
+
        while ((opt = getopt_long(argc, argv, short_options,
                                   long_options, NULL)) != -1) {
                switch (opt) {
                case 'n':
                        nw_descr.nw_id = libcfs_str2net(optarg);
+                       net_id = optarg;
                        break;
                case 'i':
                        rc = lustre_lnet_parse_interfaces(optarg, &nw_descr);
@@ -1301,8 +1914,13 @@ static int jt_del_ni(int argc, char **argv)
                }
        }
 
-       rc = lustre_lnet_del_ni(&nw_descr, -1, &err_rc);
+       rc = yaml_lnet_config_ni(net_id, NULL, &nw_descr, NULL, -1, NULL, 0);
+       if (rc <= 0) {
+               if (rc != -EOPNOTSUPP)
+                       return rc;
+       }
 
+       rc = lustre_lnet_del_ni(&nw_descr, -1, &err_rc);
 out:
        if (rc != LUSTRE_CFG_RC_NO_ERR)
                cYAML_print_tree2file(stderr, err_rc);
index 1336d13..27c40a8 100755 (executable)
@@ -1720,7 +1720,7 @@ test_208() {
 
        echo "$LCTL net up should fail"
        $LCTL net up &&
-               error "LNet bringup should have failed"
+               error "LNet bring up should have failed"
 
        cleanup_lnet
 }
index 1c54f3c..972819d 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/lnet/lnetctl.h>
 #include <linux/lnet/nidstr.h>
 #include <linux/lnet/socklnd.h>
+#include <lnetconfig/liblnetconfig.h>
 #include <lustre/lustreapi.h>
 
 unsigned int libcfs_debug;
@@ -345,13 +346,20 @@ int jt_ptl_network(int argc, char **argv)
        return 0;
 }
 
+#define IOC_LIBCFS_GET_NI      _IOWR('e', 50, IOCTL_LIBCFS_TYPE)
+
 int
 jt_ptl_list_nids(int argc, char **argv)
 {
        struct libcfs_ioctl_data data;
        int all = 0, return_nid = 0;
+       yaml_emitter_t request;
+       yaml_parser_t reply;
+       yaml_event_t event;
+       struct nl_sock *sk;
+       bool done = false;
+       int rc = 0;
        int count;
-       int rc;
 
        all = (argc == 2) && (strcmp(argv[1], "all") == 0);
        /* Hack to pass back value */
@@ -362,6 +370,174 @@ jt_ptl_list_nids(int argc, char **argv)
                return 0;
        }
 
+       sk = nl_socket_alloc();
+       if (!sk)
+               goto old_api;
+
+       /* Setup parser to receive Netlink packets */
+       rc = yaml_parser_initialize(&reply);
+       if (rc == 0) {
+               yaml_parser_log_error(&reply, stderr, NULL);
+               goto old_api;
+       }
+
+       rc = yaml_parser_set_input_netlink(&reply, sk, false);
+       if (rc == 0) {
+               yaml_parser_log_error(&reply, stderr, NULL);
+               yaml_parser_delete(&reply);
+               goto old_api;
+       }
+
+       /* Create Netlink emitter to send request to kernel */
+       rc = yaml_emitter_initialize(&request);
+       if (rc == 0) {
+               yaml_parser_log_error(&reply, stderr, NULL);
+               yaml_parser_delete(&reply);
+               goto old_api;
+       }
+
+       rc = yaml_emitter_set_output_netlink(&request, sk, LNET_GENL_NAME, 1,
+                                            LNET_CMD_NETS, NLM_F_DUMP);
+       if (rc == 0) {
+               yaml_emitter_log_error(&request, stderr);
+               yaml_emitter_delete(&request);
+               yaml_parser_delete(&reply);
+               goto old_api;
+       }
+
+       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 *)"net",
+                                    strlen("net"), 1, 0,
+                                    YAML_PLAIN_SCALAR_STYLE);
+       rc = yaml_emitter_emit(&request, &event);
+       if (rc == 0)
+               goto emitter_error;
+
+       /* no net_id */
+       if (!g_net_set || g_net == LNET_NET_ANY) {
+               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;
+       } else {
+               char *net_id = libcfs_net2str(g_net);
+
+               yaml_sequence_start_event_initialize(&event, NULL,
+                                                    (yaml_char_t *)YAML_SEQ_TAG,
+                                                    1, YAML_ANY_SEQUENCE_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 *)"net type",
+                                            strlen("net type"),
+                                            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 *)net_id,
+                                            strlen(net_id), 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;
+
+               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_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;
+       }
+       yaml_emitter_delete(&request);
+
+       while (!done) {
+               rc = yaml_parser_parse(&reply, &event);
+               if (rc == 0)
+                       break;
+
+               if (event.type == YAML_SCALAR_EVENT &&
+                   strcmp((char *)event.data.scalar.value, "nid") == 0) {
+                       char *tmp;
+
+                       yaml_event_delete(&event);
+                       rc = yaml_parser_parse(&reply, &event);
+                       if (rc == 0) {
+                               yaml_event_delete(&event);
+                               break;
+                       }
+
+                       tmp = (char *)event.data.scalar.value;
+                       if (all || strcmp(tmp, "0@lo") != 0) {
+                               printf("%s\n", tmp);
+                               if (return_nid) {
+                                       *(__u64 *)(argv[1]) = libcfs_str2nid(tmp);
+                                       return_nid--;
+                               }
+                       }
+               }
+               done = (event.type == YAML_STREAM_END_EVENT);
+               yaml_event_delete(&event);
+       }
+
+       if (rc == 0)
+               yaml_parser_log_error(&reply, stderr, NULL);
+       yaml_parser_delete(&reply);
+old_api:
+       if (sk)
+               nl_socket_free(sk);
+       if (rc == 1)
+               return 0;
+
        for (count = 0;; count++) {
                LIBCFS_IOC_INIT(data);
                data.ioc_count = count;