Whamcloud - gitweb
LU-17705 ptlrpc: replace synchronize_rcu() with rcu_barrier()
[fs/lustre-release.git] / lnet / klnds / socklnd / socklnd.c
index 1b77ddf..d187969 100644 (file)
@@ -27,7 +27,6 @@
  */
 /*
  * This file is part of Lustre, http://www.lustre.org/
- * Lustre is a trademark of Sun Microsystems, Inc.
  *
  * lnet/klnds/socklnd/socklnd.c
  *
  * Author: Eric Barton <eric@bartonsoftware.com>
  */
 
+#include <linux/ethtool.h>
 #include <linux/inetdevice.h>
-#include "socklnd.h"
+#include <linux/kernel.h>
 #include <linux/sunrpc/addr.h>
+#include <net/addrconf.h>
+#include "socklnd.h"
 
 static const struct lnet_lnd the_ksocklnd;
 struct ksock_nal_data ksocknal_data;
 
-static struct ksock_interface *
-ksocknal_ip2iface(struct lnet_ni *ni, struct sockaddr *addr)
-{
-       struct ksock_net *net = ni->ni_data;
-       struct ksock_interface *iface;
-
-       iface = &net->ksnn_interface;
-
-       if (rpc_cmp_addr((struct sockaddr *)&iface->ksni_addr, addr))
-               return iface;
-
-       return NULL;
-}
-
-static struct ksock_interface *
-ksocknal_index2iface(struct lnet_ni *ni, int index)
-{
-       struct ksock_net *net = ni->ni_data;
-       struct ksock_interface *iface;
-
-       iface = &net->ksnn_interface;
-
-       if (iface->ksni_index == index)
-               return iface;
-
-       return NULL;
-}
-
-static int ksocknal_ip2index(struct sockaddr *addr, struct lnet_ni *ni)
+static int ksocknal_ip2index(struct sockaddr *addr, struct lnet_ni *ni,
+                            int *dev_status)
 {
        struct net_device *dev;
        int ret = -1;
        DECLARE_CONST_IN_IFADDR(ifa);
 
-       if (addr->sa_family != AF_INET)
-               /* No IPv6 support yet */
+       *dev_status = -1;
+
+       if (addr->sa_family != AF_INET && addr->sa_family != AF_INET6)
                return ret;
 
        rcu_read_lock();
@@ -93,20 +69,52 @@ static int ksocknal_ip2index(struct sockaddr *addr, struct lnet_ni *ni)
                if (!(flags & IFF_UP))
                        continue;
 
-               in_dev = __in_dev_get_rcu(dev);
-               if (!in_dev)
-                       continue;
+               switch (addr->sa_family) {
+               case AF_INET:
+                       in_dev = __in_dev_get_rcu(dev);
+                       if (!in_dev)
+                               continue;
 
-               in_dev_for_each_ifa_rcu(ifa, in_dev) {
-                       if (ifa->ifa_local ==
-                           ((struct sockaddr_in *)addr)->sin_addr.s_addr)
-                               ret = dev->ifindex;
+                       in_dev_for_each_ifa_rcu(ifa, in_dev) {
+                               if (ifa->ifa_local ==
+                                   ((struct sockaddr_in *)addr)->sin_addr.s_addr)
+                                       ret = dev->ifindex;
+                       }
+                       endfor_ifa(in_dev);
+                       break;
+#if IS_ENABLED(CONFIG_IPV6)
+               case AF_INET6: {
+                       struct inet6_dev *in6_dev;
+                       const struct inet6_ifaddr *ifa6;
+                       struct sockaddr_in6 *addr6 = (struct sockaddr_in6*)addr;
+
+                       in6_dev = __in6_dev_get(dev);
+                       if (!in6_dev)
+                               continue;
+
+                       list_for_each_entry_rcu(ifa6, &in6_dev->addr_list, if_list) {
+                               if (ipv6_addr_cmp(&ifa6->addr,
+                                                &addr6->sin6_addr) == 0)
+                                       ret = dev->ifindex;
+                       }
+                       break;
+                       }
+#endif /* IS_ENABLED(CONFIG_IPV6) */
                }
-               endfor_ifa(in_dev);
                if (ret >= 0)
                        break;
        }
+
        rcu_read_unlock();
+       if (ret >= 0)
+               *dev_status = 1;
+
+       if ((ret == -1) ||
+           ((dev->reg_state == NETREG_UNREGISTERING) ||
+            ((dev->operstate != IF_OPER_UP) &&
+             (dev->operstate != IF_OPER_UNKNOWN))) ||
+           (lnet_get_link_status(dev) == 0))
+               *dev_status = 0;
 
        return ret;
 }
@@ -126,12 +134,16 @@ ksocknal_create_conn_cb(struct sockaddr *addr)
        rpc_copy_addr((struct sockaddr *)&conn_cb->ksnr_addr, addr);
        rpc_set_port((struct sockaddr *)&conn_cb->ksnr_addr,
                     rpc_get_port(addr));
-       conn_cb->ksnr_myiface = -1;
        conn_cb->ksnr_scheduled = 0;
        conn_cb->ksnr_connecting = 0;
        conn_cb->ksnr_connected = 0;
        conn_cb->ksnr_deleted = 0;
        conn_cb->ksnr_conn_count = 0;
+       conn_cb->ksnr_ctrl_conn_count = 0;
+       conn_cb->ksnr_blki_conn_count = 0;
+       conn_cb->ksnr_blko_conn_count = 0;
+       conn_cb->ksnr_max_conns = 0;
+       conn_cb->ksnr_busy_retry_count = 0;
 
        return conn_cb;
 }
@@ -148,14 +160,14 @@ ksocknal_destroy_conn_cb(struct ksock_conn_cb *conn_cb)
 }
 
 static struct ksock_peer_ni *
-ksocknal_create_peer(struct lnet_ni *ni, struct lnet_process_id id)
+ksocknal_create_peer(struct lnet_ni *ni, struct lnet_processid *id)
 {
-       int cpt = lnet_cpt_of_nid(id.nid, ni);
+       int cpt = lnet_nid2cpt(&id->nid, ni);
        struct ksock_net *net = ni->ni_data;
        struct ksock_peer_ni *peer_ni;
 
-       LASSERT(id.nid != LNET_NID_ANY);
-       LASSERT(id.pid != LNET_PID_ANY);
+       LASSERT(!LNET_NID_IS_ANY(&id->nid));
+       LASSERT(id->pid != LNET_PID_ANY);
        LASSERT(!in_interrupt());
 
        if (!atomic_inc_unless_negative(&net->ksnn_npeers)) {
@@ -170,7 +182,7 @@ ksocknal_create_peer(struct lnet_ni *ni, struct lnet_process_id id)
        }
 
        peer_ni->ksnp_ni = ni;
-       peer_ni->ksnp_id = id;
+       peer_ni->ksnp_id = *id;
        refcount_set(&peer_ni->ksnp_refcount, 1); /* 1 ref for caller */
        peer_ni->ksnp_closing = 0;
        peer_ni->ksnp_accepting = 0;
@@ -193,7 +205,7 @@ ksocknal_destroy_peer(struct ksock_peer_ni *peer_ni)
        struct ksock_net *net = peer_ni->ksnp_ni->ni_data;
 
        CDEBUG (D_NET, "peer_ni %s %p deleted\n",
-               libcfs_id2str(peer_ni->ksnp_id), peer_ni);
+               libcfs_idstr(&peer_ni->ksnp_id), peer_ni);
 
        LASSERT(refcount_read(&peer_ni->ksnp_refcount) == 0);
        LASSERT(peer_ni->ksnp_accepting == 0);
@@ -214,23 +226,24 @@ ksocknal_destroy_peer(struct ksock_peer_ni *peer_ni)
 }
 
 struct ksock_peer_ni *
-ksocknal_find_peer_locked(struct lnet_ni *ni, struct lnet_process_id id)
+ksocknal_find_peer_locked(struct lnet_ni *ni, struct lnet_processid *id)
 {
        struct ksock_peer_ni *peer_ni;
+       unsigned long hash = nidhash(&id->nid);
 
        hash_for_each_possible(ksocknal_data.ksnd_peers, peer_ni,
-                              ksnp_list, id.nid) {
+                              ksnp_list, hash) {
                LASSERT(!peer_ni->ksnp_closing);
 
                if (peer_ni->ksnp_ni != ni)
                        continue;
 
-               if (peer_ni->ksnp_id.nid != id.nid ||
-                   peer_ni->ksnp_id.pid != id.pid)
+               if (!nid_same(&peer_ni->ksnp_id.nid, &id->nid) ||
+                   peer_ni->ksnp_id.pid != id->pid)
                        continue;
 
                CDEBUG(D_NET, "got peer_ni [%p] -> %s (%d)\n",
-                      peer_ni, libcfs_id2str(id),
+                      peer_ni, libcfs_idstr(id),
                       refcount_read(&peer_ni->ksnp_refcount));
                return peer_ni;
        }
@@ -238,7 +251,7 @@ ksocknal_find_peer_locked(struct lnet_ni *ni, struct lnet_process_id id)
 }
 
 struct ksock_peer_ni *
-ksocknal_find_peer(struct lnet_ni *ni, struct lnet_process_id id)
+ksocknal_find_peer(struct lnet_ni *ni, struct lnet_processid *id)
 {
        struct ksock_peer_ni *peer_ni;
 
@@ -248,33 +261,12 @@ ksocknal_find_peer(struct lnet_ni *ni, struct lnet_process_id id)
                ksocknal_peer_addref(peer_ni);
        read_unlock(&ksocknal_data.ksnd_global_lock);
 
-        return (peer_ni);
+       return peer_ni;
 }
 
 static void
 ksocknal_unlink_peer_locked(struct ksock_peer_ni *peer_ni)
 {
-       int i;
-       struct ksock_interface *iface;
-
-       for (i = 0; i < peer_ni->ksnp_n_passive_ips; i++) {
-               struct sockaddr_in sa = { .sin_family = AF_INET };
-               LASSERT(i < LNET_INTERFACES_NUM);
-               sa.sin_addr.s_addr = htonl(peer_ni->ksnp_passive_ips[i]);
-
-               iface = ksocknal_ip2iface(peer_ni->ksnp_ni,
-                                         (struct sockaddr *)&sa);
-               /*
-                * All IPs in peer_ni->ksnp_passive_ips[] come from the
-                * interface list, therefore the call must succeed.
-                */
-               LASSERT(iface != NULL);
-
-               CDEBUG(D_NET, "peer_ni=%p iface=%p ksni_nroutes=%d\n",
-                      peer_ni, iface, iface->ksni_nroutes);
-               iface->ksni_npeers--;
-       }
-
        LASSERT(list_empty(&peer_ni->ksnp_conns));
        LASSERT(peer_ni->ksnp_conn_cb == NULL);
        LASSERT(!peer_ni->ksnp_closing);
@@ -284,16 +276,62 @@ ksocknal_unlink_peer_locked(struct ksock_peer_ni *peer_ni)
        ksocknal_peer_decref(peer_ni);
 }
 
+
+static void
+ksocknal_dump_peer_debug_info(struct ksock_peer_ni *peer_ni)
+{
+       struct ksock_conn *conn;
+       struct list_head *ctmp;
+       struct list_head *txtmp;
+       int ccount = 0;
+       int txcount = 0;
+
+       list_for_each(ctmp, &peer_ni->ksnp_conns) {
+               conn = list_entry(ctmp, struct ksock_conn, ksnc_list);
+
+               if (!list_empty(&conn->ksnc_tx_queue))
+                       list_for_each(txtmp, &conn->ksnc_tx_queue) txcount++;
+
+               CDEBUG(D_CONSOLE, "Conn %d [type, closing, crefcnt, srefcnt]: %d, %d, %d, %d\n",
+                      ccount,
+                      conn->ksnc_type,
+                      conn->ksnc_closing,
+                      refcount_read(&conn->ksnc_conn_refcount),
+                      refcount_read(&conn->ksnc_sock_refcount));
+               CDEBUG(D_CONSOLE, "Conn %d rx [scheduled, ready, state]: %d, %d, %d\n",
+                      ccount,
+                      conn->ksnc_rx_scheduled,
+                      conn->ksnc_rx_ready,
+                      conn->ksnc_rx_state);
+               CDEBUG(D_CONSOLE, "Conn %d tx [txqcnt, scheduled, last_post, ready, deadline]: %d, %d, %lld, %d, %lld\n",
+                      ccount,
+                      txcount,
+                      conn->ksnc_tx_scheduled,
+                      conn->ksnc_tx_last_post,
+                      conn->ksnc_rx_ready,
+                      conn->ksnc_rx_deadline);
+
+               if (conn->ksnc_scheduler)
+                       CDEBUG(D_CONSOLE, "Conn %d sched [nconns, cpt]: %d, %d\n",
+                              ccount,
+                              conn->ksnc_scheduler->kss_nconns,
+                              conn->ksnc_scheduler->kss_cpt);
+
+               txcount = 0;
+               ccount++;
+       }
+}
+
 static int
 ksocknal_get_peer_info(struct lnet_ni *ni, int index,
-                      struct lnet_process_id *id, __u32 *myip, __u32 *peer_ip,
+                      struct lnet_processid *id, __u32 *myip, __u32 *peer_ip,
                       int *port, int *conn_count, int *share_count)
 {
        struct ksock_peer_ni *peer_ni;
        struct ksock_conn_cb *conn_cb;
        int i;
-       int j;
        int rc = -ENOENT;
+       struct ksock_net *net;
 
        read_lock(&ksocknal_data.ksnd_global_lock);
 
@@ -301,53 +339,32 @@ ksocknal_get_peer_info(struct lnet_ni *ni, int index,
 
                if (peer_ni->ksnp_ni != ni)
                        continue;
+               if (index-- > 0)
+                       continue;
 
-               if (peer_ni->ksnp_n_passive_ips == 0 &&
-                   peer_ni->ksnp_conn_cb == NULL) {
-                       if (index-- > 0)
-                               continue;
-
-                       *id = peer_ni->ksnp_id;
+               *id = peer_ni->ksnp_id;
+               conn_cb = peer_ni->ksnp_conn_cb;
+               if (conn_cb == NULL) {
                        *myip = 0;
                        *peer_ip = 0;
                        *port = 0;
                        *conn_count = 0;
                        *share_count = 0;
                        rc = 0;
-                       goto out;
-               }
-
-               for (j = 0; j < peer_ni->ksnp_n_passive_ips; j++) {
-                       if (index-- > 0)
-                               continue;
-
-                       *id = peer_ni->ksnp_id;
-                       *myip = peer_ni->ksnp_passive_ips[j];
-                       *peer_ip = 0;
-                       *port = 0;
-                       *conn_count = 0;
-                       *share_count = 0;
-                       rc = 0;
-                       goto out;
-               }
-
-               if (peer_ni->ksnp_conn_cb) {
-                       if (index-- > 0)
-                               continue;
-
-                       conn_cb = peer_ni->ksnp_conn_cb;
+               } else {
+                       ksocknal_dump_peer_debug_info(peer_ni);
 
-                       *id = peer_ni->ksnp_id;
                        if (conn_cb->ksnr_addr.ss_family == AF_INET) {
                                struct sockaddr_in *sa =
                                        (void *)&conn_cb->ksnr_addr;
-
+                               net = ni->ni_data;
                                rc = choose_ipv4_src(myip,
-                                                    conn_cb->ksnr_myiface,
+                                                    net->ksnn_interface.ksni_index,
                                                     ntohl(sa->sin_addr.s_addr),
                                                     ni->ni_net_ns);
                                *peer_ip = ntohl(sa->sin_addr.s_addr);
                                *port = ntohs(sa->sin_port);
+
                        } else {
                                *myip = 0xFFFFFFFF;
                                *peer_ip = 0xFFFFFFFF;
@@ -356,57 +373,113 @@ ksocknal_get_peer_info(struct lnet_ni *ni, int index,
                        }
                        *conn_count = conn_cb->ksnr_conn_count;
                        *share_count = 1;
-                       goto out;
                }
+               break;
        }
-out:
        read_unlock(&ksocknal_data.ksnd_global_lock);
        return rc;
 }
 
+static unsigned int
+ksocknal_get_conns_per_peer(struct ksock_peer_ni *peer_ni)
+{
+       struct lnet_ni *ni = peer_ni->ksnp_ni;
+       struct lnet_ioctl_config_socklnd_tunables *tunables;
+
+       LASSERT(ni);
+
+       tunables = &ni->ni_lnd_tunables.lnd_tun_u.lnd_sock;
+
+       return tunables->lnd_conns_per_peer;
+}
+
+static void
+ksocknal_incr_conn_count(struct ksock_conn_cb *conn_cb,
+                        int type)
+{
+       conn_cb->ksnr_conn_count++;
+
+       /* check if all connections of the given type got created */
+       switch (type) {
+       case SOCKLND_CONN_CONTROL:
+               conn_cb->ksnr_ctrl_conn_count++;
+               /* there's a single control connection per peer,
+                * two in case of loopback
+                */
+               conn_cb->ksnr_connected |= BIT(type);
+               break;
+       case SOCKLND_CONN_BULK_IN:
+               conn_cb->ksnr_blki_conn_count++;
+               if (conn_cb->ksnr_blki_conn_count >= conn_cb->ksnr_max_conns)
+                       conn_cb->ksnr_connected |= BIT(type);
+               break;
+       case SOCKLND_CONN_BULK_OUT:
+               conn_cb->ksnr_blko_conn_count++;
+               if (conn_cb->ksnr_blko_conn_count >= conn_cb->ksnr_max_conns)
+                       conn_cb->ksnr_connected |= BIT(type);
+               break;
+       case SOCKLND_CONN_ANY:
+               if (conn_cb->ksnr_conn_count >= conn_cb->ksnr_max_conns)
+                       conn_cb->ksnr_connected |= BIT(type);
+               break;
+       default:
+               LBUG();
+               break;
+       }
+
+       CDEBUG(D_NET, "Add conn type %d, ksnr_connected %x ksnr_max_conns %d\n",
+              type, conn_cb->ksnr_connected, conn_cb->ksnr_max_conns);
+}
+
+
+static void
+ksocknal_decr_conn_count(struct ksock_conn_cb *conn_cb,
+                        int type)
+{
+       conn_cb->ksnr_conn_count--;
+
+       /* check if all connections of the given type got created */
+       switch (type) {
+       case SOCKLND_CONN_CONTROL:
+               conn_cb->ksnr_ctrl_conn_count--;
+               /* there's a single control connection per peer,
+                * two in case of loopback
+                */
+               if (conn_cb->ksnr_ctrl_conn_count == 0)
+                       conn_cb->ksnr_connected &= ~BIT(type);
+               break;
+       case SOCKLND_CONN_BULK_IN:
+               conn_cb->ksnr_blki_conn_count--;
+               if (conn_cb->ksnr_blki_conn_count == 0)
+                       conn_cb->ksnr_connected &= ~BIT(type);
+               break;
+       case SOCKLND_CONN_BULK_OUT:
+               conn_cb->ksnr_blko_conn_count--;
+               if (conn_cb->ksnr_blko_conn_count == 0)
+                       conn_cb->ksnr_connected &= ~BIT(type);
+               break;
+       case SOCKLND_CONN_ANY:
+               if (conn_cb->ksnr_conn_count == 0)
+                       conn_cb->ksnr_connected &= ~BIT(type);
+               break;
+       default:
+               LBUG();
+               break;
+       }
+
+       CDEBUG(D_NET, "Del conn type %d, ksnr_connected %x ksnr_max_conns %d\n",
+              type, conn_cb->ksnr_connected, conn_cb->ksnr_max_conns);
+}
+
 static void
 ksocknal_associate_cb_conn_locked(struct ksock_conn_cb *conn_cb,
                                  struct ksock_conn *conn)
 {
-       struct ksock_peer_ni *peer_ni = conn_cb->ksnr_peer;
        int type = conn->ksnc_type;
-       struct ksock_interface *iface;
-       int conn_iface;
 
-       conn_iface = ksocknal_ip2index((struct sockaddr *)&conn->ksnc_myaddr,
-                                      peer_ni->ksnp_ni);
        conn->ksnc_conn_cb = conn_cb;
        ksocknal_conn_cb_addref(conn_cb);
-
-       if (conn_cb->ksnr_myiface != conn_iface) {
-               if (conn_cb->ksnr_myiface < 0) {
-                       /* route wasn't bound locally yet (the initial route) */
-                       CDEBUG(D_NET, "Binding %s %pIS to interface %d\n",
-                              libcfs_id2str(peer_ni->ksnp_id),
-                              &conn_cb->ksnr_addr,
-                              conn_iface);
-               } else {
-                       CDEBUG(D_NET,
-                              "Rebinding %s %pIS from interface %d to %d\n",
-                              libcfs_id2str(peer_ni->ksnp_id),
-                              &conn_cb->ksnr_addr,
-                              conn_cb->ksnr_myiface,
-                              conn_iface);
-
-                       iface = ksocknal_index2iface(peer_ni->ksnp_ni,
-                                                    conn_cb->ksnr_myiface);
-                       if (iface)
-                               iface->ksni_nroutes--;
-               }
-               conn_cb->ksnr_myiface = conn_iface;
-               iface = ksocknal_index2iface(peer_ni->ksnp_ni,
-                                            conn_cb->ksnr_myiface);
-               if (iface)
-                       iface->ksni_nroutes++;
-       }
-
-       conn_cb->ksnr_connected |= (1<<type);
-       conn_cb->ksnr_conn_count++;
+       ksocknal_incr_conn_count(conn_cb, type);
 
        /* Successful connection => further attempts can
         * proceed immediately
@@ -418,7 +491,6 @@ static void
 ksocknal_add_conn_cb_locked(struct ksock_peer_ni *peer_ni,
                            struct ksock_conn_cb *conn_cb)
 {
-       struct list_head *tmp;
        struct ksock_conn *conn;
        struct ksock_net *net = peer_ni->ksnp_ni->ni_data;
 
@@ -431,20 +503,15 @@ ksocknal_add_conn_cb_locked(struct ksock_peer_ni *peer_ni,
        conn_cb->ksnr_peer = peer_ni;
        ksocknal_peer_addref(peer_ni);
 
-       /* set the conn_cb's interface to the current net's interface */
-       conn_cb->ksnr_myiface = net->ksnn_interface.ksni_index;
-       net->ksnn_interface.ksni_nroutes++;
-
        /* peer_ni's route list takes over my ref on 'route' */
        peer_ni->ksnp_conn_cb = conn_cb;
+       net->ksnn_interface.ksni_nroutes++;
 
-       list_for_each(tmp, &peer_ni->ksnp_conns) {
-               conn = list_entry(tmp, struct ksock_conn, ksnc_list);
-
+       list_for_each_entry(conn, &peer_ni->ksnp_conns, ksnc_list) {
                if (!rpc_cmp_addr((struct sockaddr *)&conn->ksnc_peeraddr,
                                  (struct sockaddr *)&conn_cb->ksnr_addr))
                        continue;
-
+               CDEBUG(D_NET, "call ksocknal_associate_cb_conn_locked\n");
                ksocknal_associate_cb_conn_locked(conn_cb, conn);
                /* keep going (typed conns) */
        }
@@ -454,9 +521,9 @@ static void
 ksocknal_del_conn_cb_locked(struct ksock_conn_cb *conn_cb)
 {
        struct ksock_peer_ni *peer_ni = conn_cb->ksnr_peer;
-       struct ksock_interface *iface;
        struct ksock_conn *conn;
        struct ksock_conn *cnxt;
+       struct ksock_net *net;
 
        LASSERT(!conn_cb->ksnr_deleted);
 
@@ -468,12 +535,9 @@ ksocknal_del_conn_cb_locked(struct ksock_conn_cb *conn_cb)
                ksocknal_close_conn_locked(conn, 0);
        }
 
-       if (conn_cb->ksnr_myiface >= 0) {
-               iface = ksocknal_index2iface(peer_ni->ksnp_ni,
-                                            conn_cb->ksnr_myiface);
-               if (iface)
-                       iface->ksni_nroutes--;
-       }
+       net = (struct ksock_net *)(peer_ni->ksnp_ni->ni_data);
+       net->ksnn_interface.ksni_nroutes--;
+       LASSERT(net->ksnn_interface.ksni_nroutes >= 0);
 
        conn_cb->ksnr_deleted = 1;
        ksocknal_conn_cb_decref(conn_cb);               /* drop peer_ni's ref */
@@ -487,16 +551,43 @@ ksocknal_del_conn_cb_locked(struct ksock_conn_cb *conn_cb)
        }
 }
 
+unsigned int
+ksocknal_get_conn_count_by_type(struct ksock_conn_cb *conn_cb,
+                               int type)
+{
+       unsigned int count = 0;
+
+       switch (type) {
+       case SOCKLND_CONN_CONTROL:
+               count = conn_cb->ksnr_ctrl_conn_count;
+               break;
+       case SOCKLND_CONN_BULK_IN:
+               count = conn_cb->ksnr_blki_conn_count;
+               break;
+       case SOCKLND_CONN_BULK_OUT:
+               count = conn_cb->ksnr_blko_conn_count;
+               break;
+       case SOCKLND_CONN_ANY:
+               count = conn_cb->ksnr_conn_count;
+               break;
+       default:
+               LBUG();
+               break;
+       }
+
+       return count;
+}
+
 int
-ksocknal_add_peer(struct lnet_ni *ni, struct lnet_process_id id,
+ksocknal_add_peer(struct lnet_ni *ni, struct lnet_processid *id,
                  struct sockaddr *addr)
 {
        struct ksock_peer_ni *peer_ni;
        struct ksock_peer_ni *peer2;
        struct ksock_conn_cb *conn_cb;
 
-       if (id.nid == LNET_NID_ANY ||
-           id.pid == LNET_PID_ANY)
+       if (LNET_NID_IS_ANY(&id->nid) ||
+           id->pid == LNET_PID_ANY)
                return (-EINVAL);
 
        /* Have a brand new peer_ni ready... */
@@ -522,10 +613,21 @@ ksocknal_add_peer(struct lnet_ni *ni, struct lnet_process_id id,
                peer_ni = peer2;
        } else {
                /* peer_ni table takes my ref on peer_ni */
-               hash_add(ksocknal_data.ksnd_peers, &peer_ni->ksnp_list, id.nid);
+               hash_add(ksocknal_data.ksnd_peers, &peer_ni->ksnp_list,
+                        nidhash(&id->nid));
        }
 
-       ksocknal_add_conn_cb_locked(peer_ni, conn_cb);
+       if (peer_ni->ksnp_conn_cb) {
+               ksocknal_conn_cb_decref(conn_cb);
+       } else {
+               /* Remember conns_per_peer setting at the time
+                * of connection initiation. It will define the
+                * max number of conns per type for this conn_cb
+                * while it's in use.
+                */
+               conn_cb->ksnr_max_conns = ksocknal_get_conns_per_peer(peer_ni);
+               ksocknal_add_conn_cb_locked(peer_ni, conn_cb);
+       }
 
        write_unlock_bh(&ksocknal_data.ksnd_global_lock);
 
@@ -533,7 +635,7 @@ ksocknal_add_peer(struct lnet_ni *ni, struct lnet_process_id id,
 }
 
 static void
-ksocknal_del_peer_locked(struct ksock_peer_ni *peer_ni, __u32 ip)
+ksocknal_del_peer_locked(struct ksock_peer_ni *peer_ni)
 {
        struct ksock_conn *conn;
        struct ksock_conn *cnxt;
@@ -556,7 +658,7 @@ ksocknal_del_peer_locked(struct ksock_peer_ni *peer_ni, __u32 ip)
 }
 
 static int
-ksocknal_del_peer(struct lnet_ni *ni, struct lnet_process_id id, __u32 ip)
+ksocknal_del_peer(struct lnet_ni *ni, struct lnet_processid *id)
 {
        LIST_HEAD(zombies);
        struct hlist_node *pnxt;
@@ -568,8 +670,9 @@ ksocknal_del_peer(struct lnet_ni *ni, struct lnet_process_id id, __u32 ip)
 
        write_lock_bh(&ksocknal_data.ksnd_global_lock);
 
-       if (id.nid != LNET_NID_ANY) {
-               lo = hash_min(id.nid, HASH_BITS(ksocknal_data.ksnd_peers));
+       if (id && !LNET_NID_IS_ANY(&id->nid)) {
+               lo = hash_min(nidhash(&id->nid),
+                             HASH_BITS(ksocknal_data.ksnd_peers));
                hi = lo;
        } else {
                lo = 0;
@@ -583,15 +686,15 @@ ksocknal_del_peer(struct lnet_ni *ni, struct lnet_process_id id, __u32 ip)
                        if (peer_ni->ksnp_ni != ni)
                                continue;
 
-                       if (!((id.nid == LNET_NID_ANY ||
-                              peer_ni->ksnp_id.nid == id.nid) &&
-                             (id.pid == LNET_PID_ANY ||
-                              peer_ni->ksnp_id.pid == id.pid)))
+                       if (!((!id || LNET_NID_IS_ANY(&id->nid) ||
+                              nid_same(&peer_ni->ksnp_id.nid, &id->nid)) &&
+                             (!id || id->pid == LNET_PID_ANY ||
+                              peer_ni->ksnp_id.pid == id->pid)))
                                continue;
 
                        ksocknal_peer_addref(peer_ni);  /* a ref for me... */
 
-                       ksocknal_del_peer_locked(peer_ni, ip);
+                       ksocknal_del_peer_locked(peer_ni);
 
                        if (peer_ni->ksnp_closing &&
                            !list_empty(&peer_ni->ksnp_tx_queue)) {
@@ -620,7 +723,6 @@ ksocknal_get_conn_by_idx(struct lnet_ni *ni, int index)
 {
        struct ksock_peer_ni *peer_ni;
        struct ksock_conn *conn;
-       struct list_head *ctmp;
        int i;
 
        read_lock(&ksocknal_data.ksnd_global_lock);
@@ -631,12 +733,11 @@ ksocknal_get_conn_by_idx(struct lnet_ni *ni, int index)
                if (peer_ni->ksnp_ni != ni)
                        continue;
 
-               list_for_each(ctmp, &peer_ni->ksnp_conns) {
+               list_for_each_entry(conn, &peer_ni->ksnp_conns,
+                                   ksnc_list) {
                        if (index-- > 0)
                                continue;
 
-                       conn = list_entry(ctmp, struct ksock_conn,
-                                         ksnc_list);
                        ksocknal_conn_addref(conn);
                        read_unlock(&ksocknal_data.ksnd_global_lock);
                        return conn;
@@ -675,12 +776,15 @@ ksocknal_accept(struct lnet_ni *ni, struct socket *sock)
        struct sockaddr_storage peer;
 
        rc = lnet_sock_getaddr(sock, true, &peer);
-       LASSERT(rc == 0);               /* we succeeded before */
+       if (rc != 0) {
+               CERROR("Can't determine new connection's address\n");
+               return rc;
+       }
 
        LIBCFS_ALLOC(cr, sizeof(*cr));
        if (cr == NULL) {
                LCONSOLE_ERROR_MSG(0x12f,
-                                  "Dropping connection request from %pIS: memory exhausted\n",
+                                  "Dropping connection request from %pISc: memory exhausted\n",
                                   &peer);
                return -ENOMEM;
        }
@@ -698,6 +802,111 @@ 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_U16
+               },
+               [LNET_NET_SOCKLND_TUNABLES_ATTR_LND_TIMEOUT]    = {
+                       .lkp_value      = "timeout",
+                       .lkp_data_type  = NLA_U32
+               },
+               [LNET_NET_SOCKLND_TUNABLES_ATTR_LND_TOS] = {
+                       .lkp_value      = "tos",
+                       .lkp_data_type  = NLA_S16,
+               },
+       },
+};
+
+static int
+ksocknal_nl_get(int cmd, struct sk_buff *msg, int type, void *data)
+{
+       struct lnet_lnd_tunables *tun;
+       struct lnet_ni *ni = data;
+
+       if (!ni || !msg)
+               return -EINVAL;
+
+        if (cmd != LNET_CMD_NETS || type != LNET_NET_LOCAL_NI_ATTR_LND_TUNABLES)
+               return -EOPNOTSUPP;
+
+       tun = &ni->ni_lnd_tunables;
+       nla_put_u16(msg, LNET_NET_SOCKLND_TUNABLES_ATTR_CONNS_PER_PEER,
+                   tun->lnd_tun_u.lnd_sock.lnd_conns_per_peer);
+       nla_put_u32(msg, LNET_NET_SOCKLND_TUNABLES_ATTR_LND_TIMEOUT,
+                   ksocknal_timeout());
+       nla_put_s16(msg, LNET_NET_SOCKLND_TUNABLES_ATTR_LND_TOS,
+                   tun->lnd_tun_u.lnd_sock.lnd_tos);
+
+       return 0;
+}
+
+static inline void
+ksocknal_nl_set_default(int cmd, int type, void *data)
+{
+       struct lnet_lnd_tunables *tunables = data;
+       struct lnet_ioctl_config_socklnd_tunables *lt;
+       struct lnet_ioctl_config_socklnd_tunables *df;
+
+       lt = &tunables->lnd_tun_u.lnd_sock;
+       df = &ksock_default_tunables;
+       switch (type) {
+       case LNET_NET_SOCKLND_TUNABLES_ATTR_CONNS_PER_PEER:
+               lt->lnd_conns_per_peer = df->lnd_conns_per_peer;
+               break;
+       case LNET_NET_SOCKLND_TUNABLES_ATTR_LND_TIMEOUT:
+               lt->lnd_timeout = df->lnd_timeout;
+               fallthrough;
+       default:
+               break;
+       }
+}
+
+static int
+ksocknal_nl_set(int cmd, struct nlattr *attr, int type, void *data)
+{
+       struct lnet_lnd_tunables *tunables = data;
+       int rc = 0;
+       s64 num;
+
+       if (cmd != LNET_CMD_NETS)
+               return -EOPNOTSUPP;
+
+       if (!attr) {
+               ksocknal_nl_set_default(cmd, type, data);
+               return 0;
+       }
+
+       if (nla_type(attr) != LN_SCALAR_ATTR_INT_VALUE)
+               return -EINVAL;
+
+       switch (type) {
+       case LNET_NET_SOCKLND_TUNABLES_ATTR_CONNS_PER_PEER:
+               /* value values are 1 to 127. Zero mean calculate the value */
+               num = nla_get_s64(attr);
+               if (num > -1 && num < 128)
+                       tunables->lnd_tun_u.lnd_sock.lnd_conns_per_peer = num;
+               else
+                       rc = -ERANGE;
+               break;
+       case LNET_NET_SOCKLND_TUNABLES_ATTR_LND_TIMEOUT:
+               num = nla_get_s64(attr);
+               tunables->lnd_tun_u.lnd_sock.lnd_timeout = num;
+               break;
+       case LNET_NET_SOCKLND_TUNABLES_ATTR_LND_TOS:
+               num = nla_get_s64(attr);
+               clamp_t(s64, num, -1, 0xff);
+               tunables->lnd_tun_u.lnd_sock.lnd_tos = num;
+               fallthrough;
+       default:
+               break;
+       }
+
+       return rc;
+}
+
 static int
 ksocknal_connecting(struct ksock_conn_cb *conn_cb, struct sockaddr *sa)
 {
@@ -713,8 +922,7 @@ ksocknal_create_conn(struct lnet_ni *ni, struct ksock_conn_cb *conn_cb,
 {
        rwlock_t *global_lock = &ksocknal_data.ksnd_global_lock;
        LIST_HEAD(zombies);
-       struct lnet_process_id peerid;
-       struct list_head *tmp;
+       struct lnet_processid peerid;
        u64 incarnation;
        struct ksock_conn *conn;
        struct ksock_conn *conn2;
@@ -728,6 +936,7 @@ ksocknal_create_conn(struct lnet_ni *ni, struct ksock_conn_cb *conn_cb,
        int rc;
        int rc2;
        int active;
+       int num_dup = 0;
        char *warn = NULL;
 
        active = (conn_cb != NULL);
@@ -776,8 +985,9 @@ ksocknal_create_conn(struct lnet_ni *ni, struct ksock_conn_cb *conn_cb,
         * Passive connections use the listener timeout since the peer_ni sends
         * eagerly
         */
-
        if (active) {
+               struct sockaddr_in *psa = (void *)&conn->ksnc_peeraddr;
+
                peer_ni = conn_cb->ksnr_peer;
                LASSERT(ni == peer_ni->ksnp_ni);
 
@@ -790,7 +1000,10 @@ ksocknal_create_conn(struct lnet_ni *ni, struct ksock_conn_cb *conn_cb,
                write_unlock_bh(global_lock);
 
                if (conn->ksnc_proto == NULL) {
-                       conn->ksnc_proto = &ksocknal_protocol_v3x;
+                       if (psa->sin_family == AF_INET6)
+                               conn->ksnc_proto = &ksocknal_protocol_v4x;
+                       else if (psa->sin_family == AF_INET)
+                               conn->ksnc_proto = &ksocknal_protocol_v3x;
 #if SOCKNAL_VERSION_DEBUG
                        if (*ksocknal_tunables.ksnd_protocol == 2)
                                conn->ksnc_proto = &ksocknal_protocol_v2x;
@@ -798,12 +1011,16 @@ ksocknal_create_conn(struct lnet_ni *ni, struct ksock_conn_cb *conn_cb,
                                conn->ksnc_proto = &ksocknal_protocol_v1x;
 #endif
                }
+               if (!conn->ksnc_proto) {
+                       rc = -EPROTO;
+                       goto failed_1;
+               }
 
-               rc = ksocknal_send_hello(ni, conn, peerid.nid, hello);
+               rc = ksocknal_send_hello(ni, conn, &peerid.nid, hello);
                if (rc != 0)
                        goto failed_1;
        } else {
-               peerid.nid = LNET_NID_ANY;
+               peerid.nid = LNET_ANY_NID;
                peerid.pid = LNET_PID_ANY;
 
                /* Passive, get protocol from peer_ni */
@@ -816,15 +1033,15 @@ ksocknal_create_conn(struct lnet_ni *ni, struct ksock_conn_cb *conn_cb,
 
        LASSERT(rc == 0 || active);
        LASSERT(conn->ksnc_proto != NULL);
-       LASSERT(peerid.nid != LNET_NID_ANY);
+       LASSERT(!LNET_NID_IS_ANY(&peerid.nid));
 
-       cpt = lnet_cpt_of_nid(peerid.nid, ni);
+       cpt = lnet_nid2cpt(&peerid.nid, ni);
 
        if (active) {
                ksocknal_peer_addref(peer_ni);
                write_lock_bh(global_lock);
        } else {
-               peer_ni = ksocknal_create_peer(ni, peerid);
+               peer_ni = ksocknal_create_peer(ni, &peerid);
                if (IS_ERR(peer_ni)) {
                        rc = PTR_ERR(peer_ni);
                        goto failed_1;
@@ -835,39 +1052,39 @@ ksocknal_create_conn(struct lnet_ni *ni, struct ksock_conn_cb *conn_cb,
                /* called with a ref on ni, so shutdown can't have started */
                LASSERT(atomic_read(&((struct ksock_net *)ni->ni_data)->ksnn_npeers) >= 0);
 
-               peer2 = ksocknal_find_peer_locked(ni, peerid);
+               peer2 = ksocknal_find_peer_locked(ni, &peerid);
                if (peer2 == NULL) {
                        /* NB this puts an "empty" peer_ni in the peer_ni
                         * table (which takes my ref) */
                        hash_add(ksocknal_data.ksnd_peers,
-                                &peer_ni->ksnp_list, peerid.nid);
+                                &peer_ni->ksnp_list, nidhash(&peerid.nid));
                } else {
                        ksocknal_peer_decref(peer_ni);
                        peer_ni = peer2;
                }
 
-                /* +1 ref for me */
-                ksocknal_peer_addref(peer_ni);
-                peer_ni->ksnp_accepting++;
+               /* +1 ref for me */
+               ksocknal_peer_addref(peer_ni);
+               peer_ni->ksnp_accepting++;
 
                /* Am I already connecting to this guy?  Resolve in
                 * favour of higher NID...
                 */
-               if (peerid.nid < ni->ni_nid &&
+               if (memcmp(&peerid.nid, &ni->ni_nid, sizeof(peerid.nid)) < 0 &&
                    ksocknal_connecting(peer_ni->ksnp_conn_cb,
                                        ((struct sockaddr *) &conn->ksnc_peeraddr))) {
                        rc = EALREADY;
                        warn = "connection race resolution";
                        goto failed_2;
                }
-        }
+       }
 
-        if (peer_ni->ksnp_closing ||
+       if (peer_ni->ksnp_closing ||
            (active && conn_cb->ksnr_deleted)) {
                /* peer_ni/conn_cb got closed under me */
-                rc = -ESTALE;
+               rc = -ESTALE;
                warn = "peer_ni/conn_cb removed";
-                goto failed_2;
+               goto failed_2;
         }
 
        if (peer_ni->ksnp_proto == NULL) {
@@ -894,26 +1111,24 @@ ksocknal_create_conn(struct lnet_ni *ni, struct ksock_conn_cb *conn_cb,
                goto failed_2;
        }
 
-        switch (rc) {
-        default:
-                LBUG();
-        case 0:
-                break;
-        case EALREADY:
-                warn = "lost conn race";
-                goto failed_2;
-        case EPROTO:
-                warn = "retry with different protocol version";
-                goto failed_2;
-        }
+       switch (rc) {
+       default:
+               LBUG();
+       case 0:
+               break;
+       case EALREADY:
+               warn = "lost conn race";
+               goto failed_2;
+       case EPROTO:
+               warn = "retry with different protocol version";
+               goto failed_2;
+       }
 
        /* Refuse to duplicate an existing connection, unless this is a
         * loopback connection */
        if (!rpc_cmp_addr((struct sockaddr *)&conn->ksnc_peeraddr,
                          (struct sockaddr *)&conn->ksnc_myaddr)) {
-               list_for_each(tmp, &peer_ni->ksnp_conns) {
-                       conn2 = list_entry(tmp, struct ksock_conn, ksnc_list);
-
+               list_for_each_entry(conn2, &peer_ni->ksnp_conns, ksnc_list) {
                        if (!rpc_cmp_addr(
                                    (struct sockaddr *)&conn2->ksnc_peeraddr,
                                    (struct sockaddr *)&conn->ksnc_peeraddr) ||
@@ -923,29 +1138,39 @@ ksocknal_create_conn(struct lnet_ni *ni, struct ksock_conn_cb *conn_cb,
                            conn2->ksnc_type != conn->ksnc_type)
                                continue;
 
-                        /* Reply on a passive connection attempt so the peer_ni
-                         * realises we're connected. */
-                        LASSERT (rc == 0);
-                        if (!active)
-                                rc = EALREADY;
+                       num_dup++;
+                       /* If max conns per type is not registered in conn_cb
+                        * as ksnr_max_conns, use ni's conns_per_peer
+                        */
+                       if ((peer_ni->ksnp_conn_cb &&
+                           num_dup < peer_ni->ksnp_conn_cb->ksnr_max_conns) ||
+                           (!peer_ni->ksnp_conn_cb &&
+                           num_dup < ksocknal_get_conns_per_peer(peer_ni)))
+                               continue;
 
-                        warn = "duplicate";
-                        goto failed_2;
-                }
-        }
+                       /* Reply on a passive connection attempt so the peer_ni
+                        * realises we're connected.
+                        */
+                       LASSERT(rc == 0);
+                       if (!active)
+                               rc = EALREADY;
 
-        /* If the connection created by this route didn't bind to the IP
-         * address the route connected to, the connection/route matching
+                       warn = "duplicate";
+                       goto failed_2;
+               }
+       }
+       /* If the connection created by this route didn't bind to the IP
+        * address the route connected to, the connection/route matching
         * code below probably isn't going to work.
         */
-        if (active &&
+       if (active &&
            !rpc_cmp_addr((struct sockaddr *)&conn_cb->ksnr_addr,
                          (struct sockaddr *)&conn->ksnc_peeraddr)) {
-               CERROR("Route %s %pIS connected to %pIS\n",
-                      libcfs_id2str(peer_ni->ksnp_id),
+               CERROR("Route %s %pISc connected to %pISc\n",
+                      libcfs_idstr(&peer_ni->ksnp_id),
                       &conn_cb->ksnr_addr,
                       &conn->ksnc_peeraddr);
-        }
+       }
 
        /* Search for a conn_cb corresponding to the new connection and
         * create an association.  This allows incoming connections created
@@ -1001,7 +1226,6 @@ ksocknal_create_conn(struct lnet_ni *ni, struct ksock_conn_cb *conn_cb,
        }
 
        write_unlock_bh(global_lock);
-
        /* We've now got a new connection.  Any errors from here on are just
         * like "normal" comms errors and we close the connection normally.
         * NB (a) we still have to send the reply HELLO for passive
@@ -1010,16 +1234,16 @@ ksocknal_create_conn(struct lnet_ni *ni, struct ksock_conn_cb *conn_cb,
         *        socket callbacks.
         */
 
-       CDEBUG(D_NET, "New conn %s p %d.x %pIS -> %pISp"
+       CDEBUG(D_NET, "New conn %s p %d.x %pISc -> %pIScp"
               " incarnation:%lld sched[%d]\n",
-              libcfs_id2str(peerid), conn->ksnc_proto->pro_version,
+              libcfs_idstr(&peerid), conn->ksnc_proto->pro_version,
               &conn->ksnc_myaddr, &conn->ksnc_peeraddr,
               incarnation, cpt);
 
        if (!active) {
                hello->kshm_nips = 0;
-                rc = ksocknal_send_hello(ni, conn, peerid.nid, hello);
-        }
+               rc = ksocknal_send_hello(ni, conn, &peerid.nid, hello);
+       }
 
        LIBCFS_FREE(hello, offsetof(struct ksock_hello_msg,
                                    kshm_ips[LNET_INTERFACES_NUM]));
@@ -1031,7 +1255,7 @@ ksocknal_create_conn(struct lnet_ni *ni, struct ksock_conn_cb *conn_cb,
         * response has been sent.
         */
        if (rc == 0)
-               rc = ksocknal_lib_setup_sock(sock);
+               rc = ksocknal_lib_setup_sock(sock, ni);
 
        write_lock_bh(global_lock);
 
@@ -1075,10 +1299,10 @@ failed_2:
        if (warn != NULL) {
                if (rc < 0)
                        CERROR("Not creating conn %s type %d: %s\n",
-                              libcfs_id2str(peerid), conn->ksnc_type, warn);
+                              libcfs_idstr(&peerid), conn->ksnc_type, warn);
                else
                        CDEBUG(D_NET, "Not creating conn %s type %d: %s\n",
-                              libcfs_id2str(peerid), conn->ksnc_type, warn);
+                              libcfs_idstr(&peerid), conn->ksnc_type, warn);
        }
 
        if (!active) {
@@ -1088,7 +1312,7 @@ failed_2:
                         */
                        conn->ksnc_type = SOCKLND_CONN_NONE;
                        hello->kshm_nips = 0;
-                       ksocknal_send_hello(ni, conn, peerid.nid, hello);
+                       ksocknal_send_hello(ni, conn, &peerid.nid, hello);
                }
 
                write_lock_bh(global_lock);
@@ -1127,7 +1351,8 @@ ksocknal_close_conn_locked(struct ksock_conn *conn, int error)
        struct ksock_peer_ni *peer_ni = conn->ksnc_peer;
        struct ksock_conn_cb *conn_cb;
        struct ksock_conn *conn2;
-       struct list_head *tmp;
+       int conn_count;
+       int duplicate_count = 0;
 
        LASSERT(peer_ni->ksnp_error == 0);
        LASSERT(!conn->ksnc_closing);
@@ -1140,20 +1365,29 @@ ksocknal_close_conn_locked(struct ksock_conn *conn, int error)
        if (conn_cb != NULL) {
                /* dissociate conn from cb... */
                LASSERT(!conn_cb->ksnr_deleted);
-               LASSERT((conn_cb->ksnr_connected & BIT(conn->ksnc_type)) != 0);
 
-               conn2 = NULL;
-               list_for_each(tmp, &peer_ni->ksnp_conns) {
-                       conn2 = list_entry(tmp, struct ksock_conn, ksnc_list);
-
-                       if (conn2->ksnc_conn_cb == conn_cb &&
-                           conn2->ksnc_type == conn->ksnc_type)
-                               break;
-
-                       conn2 = NULL;
+               conn_count = ksocknal_get_conn_count_by_type(conn_cb,
+                                                            conn->ksnc_type);
+               /* connected bit is set only if all connections
+                * of the given type got created
+                */
+               if (conn_count == conn_cb->ksnr_max_conns)
+                       LASSERT((conn_cb->ksnr_connected &
+                               BIT(conn->ksnc_type)) != 0);
+
+               if (conn_count == 1) {
+                       list_for_each_entry(conn2, &peer_ni->ksnp_conns,
+                                           ksnc_list) {
+                               if (conn2->ksnc_conn_cb == conn_cb &&
+                                   conn2->ksnc_type == conn->ksnc_type)
+                                       duplicate_count += 1;
+                       }
+                       if (duplicate_count > 0)
+                               CERROR("Found %d duplicate conns type %d\n",
+                                      duplicate_count,
+                                      conn->ksnc_type);
                }
-               if (conn2 == NULL)
-                       conn_cb->ksnr_connected &= ~BIT(conn->ksnc_type);
+               ksocknal_decr_conn_count(conn_cb, conn->ksnc_type);
 
                conn->ksnc_conn_cb = NULL;
 
@@ -1225,7 +1459,8 @@ ksocknal_peer_failed(struct ksock_peer_ni *peer_ni)
        read_unlock(&ksocknal_data.ksnd_global_lock);
 
        if (notify)
-               lnet_notify(peer_ni->ksnp_ni, peer_ni->ksnp_id.nid,
+               lnet_notify(peer_ni->ksnp_ni,
+                           &peer_ni->ksnp_id.nid,
                            false, false, last_alive);
 }
 
@@ -1243,7 +1478,8 @@ ksocknal_finalize_zcreq(struct ksock_conn *conn)
 
        spin_lock(&peer_ni->ksnp_lock);
 
-       list_for_each_entry_safe(tx, tmp, &peer_ni->ksnp_zc_req_list, tx_zc_list) {
+       list_for_each_entry_safe(tx, tmp, &peer_ni->ksnp_zc_req_list,
+                                tx_zc_list) {
                if (tx->tx_conn != conn)
                        continue;
 
@@ -1256,9 +1492,8 @@ ksocknal_finalize_zcreq(struct ksock_conn *conn)
 
        spin_unlock(&peer_ni->ksnp_lock);
 
-       while (!list_empty(&zlist)) {
-               tx = list_entry(zlist.next, struct ksock_tx, tx_zc_list);
-
+       while ((tx = list_first_entry_or_null(&zlist, struct ksock_tx,
+                                             tx_zc_list)) != NULL) {
                list_del(&tx->tx_zc_list);
                ksocknal_tx_decref(tx);
        }
@@ -1362,10 +1597,11 @@ ksocknal_destroy_conn(struct ksock_conn *conn)
         case SOCKNAL_RX_LNET_PAYLOAD:
                 last_rcv = conn->ksnc_rx_deadline -
                           ksocknal_timeout();
-               CERROR("Completing partial receive from %s[%d], ip %pISp, with error, wanted: %d, left: %d, last alive is %lld secs ago\n",
-                       libcfs_id2str(conn->ksnc_peer->ksnp_id), conn->ksnc_type,
+               CERROR("Completing partial receive from %s[%d], ip %pIScp, with error, wanted: %d, left: %d, last alive is %lld secs ago\n",
+                      libcfs_idstr(&conn->ksnc_peer->ksnp_id),
+                      conn->ksnc_type,
                       &conn->ksnc_peeraddr,
-                       conn->ksnc_rx_nob_wanted, conn->ksnc_rx_nob_left,
+                      conn->ksnc_rx_nob_wanted, conn->ksnc_rx_nob_left,
                       ktime_get_seconds() - last_rcv);
                if (conn->ksnc_lnet_msg)
                        conn->ksnc_lnet_msg->msg_health_status =
@@ -1374,32 +1610,32 @@ ksocknal_destroy_conn(struct ksock_conn *conn)
                break;
        case SOCKNAL_RX_LNET_HEADER:
                if (conn->ksnc_rx_started)
-                       CERROR("Incomplete receive of lnet header from %s, ip %pISp, with error, protocol: %d.x.\n",
-                              libcfs_id2str(conn->ksnc_peer->ksnp_id),
+                       CERROR("Incomplete receive of lnet header from %s, ip %pIScp, with error, protocol: %d.x.\n",
+                              libcfs_idstr(&conn->ksnc_peer->ksnp_id),
                               &conn->ksnc_peeraddr,
                               conn->ksnc_proto->pro_version);
                break;
-        case SOCKNAL_RX_KSM_HEADER:
-                if (conn->ksnc_rx_started)
-                       CERROR("Incomplete receive of ksock message from %s, ip %pISp, with error, protocol: %d.x.\n",
-                              libcfs_id2str(conn->ksnc_peer->ksnp_id),
+       case SOCKNAL_RX_KSM_HEADER:
+               if (conn->ksnc_rx_started)
+                       CERROR("Incomplete receive of ksock message from %s, ip %pIScp, with error, protocol: %d.x.\n",
+                              libcfs_idstr(&conn->ksnc_peer->ksnp_id),
                               &conn->ksnc_peeraddr,
                               conn->ksnc_proto->pro_version);
-                break;
-        case SOCKNAL_RX_SLOP:
-                if (conn->ksnc_rx_started)
-                       CERROR("Incomplete receive of slops from %s, ip %pISp, with error\n",
-                              libcfs_id2str(conn->ksnc_peer->ksnp_id),
+               break;
+       case SOCKNAL_RX_SLOP:
+               if (conn->ksnc_rx_started)
+                       CERROR("Incomplete receive of slops from %s, ip %pIScp, with error\n",
+                              libcfs_idstr(&conn->ksnc_peer->ksnp_id),
                               &conn->ksnc_peeraddr);
-               break;
-        default:
-                LBUG ();
-                break;
-        }
+               break;
+       default:
+               LBUG();
+               break;
+       }
 
-        ksocknal_peer_decref(conn->ksnc_peer);
+       ksocknal_peer_decref(conn->ksnc_peer);
 
-        LIBCFS_FREE (conn, sizeof (*conn));
+       LIBCFS_FREE(conn, sizeof(*conn));
 }
 
 int
@@ -1439,7 +1675,7 @@ ksocknal_close_conn_and_siblings(struct ksock_conn *conn, int why)
 }
 
 int
-ksocknal_close_matching_conns(struct lnet_process_id id, __u32 ipaddr)
+ksocknal_close_matching_conns(struct lnet_processid *id, __u32 ipaddr)
 {
        struct ksock_peer_ni *peer_ni;
        struct hlist_node *pnxt;
@@ -1451,8 +1687,9 @@ ksocknal_close_matching_conns(struct lnet_process_id id, __u32 ipaddr)
 
        write_lock_bh(&ksocknal_data.ksnd_global_lock);
 
-       if (id.nid != LNET_NID_ANY) {
-               lo = hash_min(id.nid, HASH_BITS(ksocknal_data.ksnd_peers));
+       if (!LNET_NID_IS_ANY(&id->nid)) {
+               lo = hash_min(nidhash(&id->nid),
+                             HASH_BITS(ksocknal_data.ksnd_peers));
                hi = lo;
        } else {
                lo = 0;
@@ -1465,10 +1702,10 @@ ksocknal_close_matching_conns(struct lnet_process_id id, __u32 ipaddr)
                                          &ksocknal_data.ksnd_peers[i],
                                          ksnp_list) {
 
-                       if (!((id.nid == LNET_NID_ANY ||
-                              id.nid == peer_ni->ksnp_id.nid) &&
-                             (id.pid == LNET_PID_ANY ||
-                              id.pid == peer_ni->ksnp_id.pid)))
+                       if (!((LNET_NID_IS_ANY(&id->nid) ||
+                              nid_same(&id->nid, &peer_ni->ksnp_id.nid)) &&
+                             (id->pid == LNET_PID_ANY ||
+                              id->pid == peer_ni->ksnp_id.pid)))
                                continue;
 
                        count += ksocknal_close_peer_conns_locked(
@@ -1480,31 +1717,33 @@ ksocknal_close_matching_conns(struct lnet_process_id id, __u32 ipaddr)
        write_unlock_bh(&ksocknal_data.ksnd_global_lock);
 
        /* wildcards always succeed */
-       if (id.nid == LNET_NID_ANY || id.pid == LNET_PID_ANY || ipaddr == 0)
+       if (LNET_NID_IS_ANY(&id->nid) || id->pid == LNET_PID_ANY ||
+           ipaddr == 0)
                return 0;
 
        return (count == 0 ? -ENOENT : 0);
 }
 
-void
-ksocknal_notify_gw_down(lnet_nid_t gw_nid)
+static void
+ksocknal_notify_gw_down(struct lnet_nid *gw_nid)
 {
        /* The router is telling me she's been notified of a change in
         * gateway state....
         */
-       struct lnet_process_id id = {
-               .nid    = gw_nid,
+       struct lnet_processid id = {
                .pid    = LNET_PID_ANY,
+               .nid    = *gw_nid,
        };
 
-       CDEBUG(D_NET, "gw %s down\n", libcfs_nid2str(gw_nid));
+       CDEBUG(D_NET, "gw %s down\n", libcfs_nidstr(gw_nid));
 
        /* If the gateway crashed, close all open connections... */
-       ksocknal_close_matching_conns(id, 0);
+       ksocknal_close_matching_conns(&id, 0);
        return;
 
        /* We can only establish new connections
-        * if we have autroutes, and these connect on demand. */
+        * if we have autroutes, and these connect on demand.
+        */
 }
 
 static void
@@ -1512,7 +1751,6 @@ ksocknal_push_peer(struct ksock_peer_ni *peer_ni)
 {
        int index;
        int i;
-       struct list_head *tmp;
        struct ksock_conn *conn;
 
         for (index = 0; ; index++) {
@@ -1521,10 +1759,8 @@ ksocknal_push_peer(struct ksock_peer_ni *peer_ni)
                 i = 0;
                 conn = NULL;
 
-               list_for_each(tmp, &peer_ni->ksnp_conns) {
+               list_for_each_entry(conn, &peer_ni->ksnp_conns, ksnc_list) {
                         if (i++ == index) {
-                               conn = list_entry(tmp, struct ksock_conn,
-                                                 ksnc_list);
                                 ksocknal_conn_addref(conn);
                                 break;
                         }
@@ -1532,7 +1768,7 @@ ksocknal_push_peer(struct ksock_peer_ni *peer_ni)
 
                read_unlock(&ksocknal_data.ksnd_global_lock);
 
-                if (conn == NULL)
+               if (i <= index)
                         break;
 
                 ksocknal_lib_push_conn (conn);
@@ -1541,15 +1777,16 @@ ksocknal_push_peer(struct ksock_peer_ni *peer_ni)
 }
 
 static int
-ksocknal_push(struct lnet_ni *ni, struct lnet_process_id id)
+ksocknal_push(struct lnet_ni *ni, struct lnet_processid *id)
 {
        int lo;
        int hi;
        int bkt;
        int rc = -ENOENT;
 
-       if (id.nid != LNET_NID_ANY) {
-               lo = hash_min(id.nid, HASH_BITS(ksocknal_data.ksnd_peers));
+       if (!LNET_NID_IS_ANY(&id->nid)) {
+               lo = hash_min(nidhash(&id->nid),
+                             HASH_BITS(ksocknal_data.ksnd_peers));
                hi = lo;
        } else {
                lo = 0;
@@ -1567,10 +1804,11 @@ ksocknal_push(struct lnet_ni *ni, struct lnet_process_id id)
                        hlist_for_each_entry(peer_ni,
                                             &ksocknal_data.ksnd_peers[bkt],
                                             ksnp_list) {
-                               if (!((id.nid == LNET_NID_ANY ||
-                                      id.nid == peer_ni->ksnp_id.nid) &&
-                                     (id.pid == LNET_PID_ANY ||
-                                      id.pid == peer_ni->ksnp_id.pid)))
+                               if (!((LNET_NID_IS_ANY(&id->nid) ||
+                                      nid_same(&id->nid,
+                                                &peer_ni->ksnp_id.nid)) &&
+                                     (id->pid == LNET_PID_ANY ||
+                                      id->pid == peer_ni->ksnp_id.pid)))
                                        continue;
 
                                if (i++ == peer_off) {
@@ -1594,7 +1832,7 @@ ksocknal_push(struct lnet_ni *ni, struct lnet_process_id id)
 int
 ksocknal_ctl(struct lnet_ni *ni, unsigned int cmd, void *arg)
 {
-       struct lnet_process_id id = {0};
+       struct lnet_processid id = {};
        struct libcfs_ioctl_data *data = arg;
        int rc;
 
@@ -1607,17 +1845,19 @@ ksocknal_ctl(struct lnet_ni *ni, unsigned int cmd, void *arg)
                read_lock(&ksocknal_data.ksnd_global_lock);
 
                if (data->ioc_count >= 1) {
-                        rc = -ENOENT;
-                } else {
-                        rc = 0;
+                       rc = -ENOENT;
+               } else {
+                       rc = 0;
                        iface = &net->ksnn_interface;
 
                        sa = (void *)&iface->ksni_addr;
-                       if (sa->sin_family == AF_INET)
+                       if (sa->sin_family == AF_INET) {
                                data->ioc_u32[0] = ntohl(sa->sin_addr.s_addr);
-                       else
+                               data->ioc_u32[1] = iface->ksni_netmask;
+                       } else {
                                data->ioc_u32[0] = 0xFFFFFFFF;
-                       data->ioc_u32[1] = iface->ksni_netmask;
+                               data->ioc_u32[1] = 0;
+                       }
                        data->ioc_u32[2] = iface->ksni_npeers;
                        data->ioc_u32[3] = iface->ksni_nroutes;
                }
@@ -1626,43 +1866,44 @@ ksocknal_ctl(struct lnet_ni *ni, unsigned int cmd, void *arg)
                return rc;
         }
 
-        case IOC_LIBCFS_GET_PEER: {
-                __u32            myip = 0;
-                __u32            ip = 0;
-                int              port = 0;
-                int              conn_count = 0;
-                int              share_count = 0;
-
-                rc = ksocknal_get_peer_info(ni, data->ioc_count,
-                                            &id, &myip, &ip, &port,
-                                            &conn_count,  &share_count);
-                if (rc != 0)
-                        return rc;
-
-                data->ioc_nid    = id.nid;
-                data->ioc_count  = share_count;
-                data->ioc_u32[0] = ip;
-                data->ioc_u32[1] = port;
-                data->ioc_u32[2] = myip;
-                data->ioc_u32[3] = conn_count;
-                data->ioc_u32[4] = id.pid;
-                return 0;
-        }
+       case IOC_LIBCFS_GET_PEER: {
+               __u32            myip = 0;
+               __u32            ip = 0;
+               int              port = 0;
+               int              conn_count = 0;
+               int              share_count = 0;
+
+               rc = ksocknal_get_peer_info(ni, data->ioc_count,
+                                           &id, &myip, &ip, &port,
+                                           &conn_count,  &share_count);
+               if (rc != 0)
+                       return rc;
+
+               if (!nid_is_nid4(&id.nid))
+                       return -EINVAL;
+               data->ioc_nid    = lnet_nid_to_nid4(&id.nid);
+               data->ioc_count  = share_count;
+               data->ioc_u32[0] = ip;
+               data->ioc_u32[1] = port;
+               data->ioc_u32[2] = myip;
+               data->ioc_u32[3] = conn_count;
+               data->ioc_u32[4] = id.pid;
+               return 0;
+       }
 
        case IOC_LIBCFS_ADD_PEER: {
                struct sockaddr_in sa = {.sin_family = AF_INET};
 
-               id.nid = data->ioc_nid;
                id.pid = LNET_PID_LUSTRE;
+               lnet_nid4_to_nid(data->ioc_nid, &id.nid);
                sa.sin_addr.s_addr = htonl(data->ioc_u32[0]);
                sa.sin_port = htons(data->ioc_u32[1]);
-               return ksocknal_add_peer(ni, id, (struct sockaddr *)&sa);
+               return ksocknal_add_peer(ni, &id, (struct sockaddr *)&sa);
        }
-        case IOC_LIBCFS_DEL_PEER:
-                id.nid = data->ioc_nid;
-                id.pid = LNET_PID_ANY;
-                return ksocknal_del_peer (ni, id,
-                                          data->ioc_u32[0]); /* IP */
+       case IOC_LIBCFS_DEL_PEER:
+               lnet_nid4_to_nid(data->ioc_nid, &id.nid);
+               id.pid = LNET_PID_ANY;
+               return ksocknal_del_peer(ni, &id);
 
         case IOC_LIBCFS_GET_CONN: {
                 int           txmem;
@@ -1677,9 +1918,9 @@ ksocknal_ctl(struct lnet_ni *ni, unsigned int cmd, void *arg)
 
                 ksocknal_lib_get_conn_tunables(conn, &txmem, &rxmem, &nagle);
 
-                data->ioc_count  = txmem;
-                data->ioc_nid    = conn->ksnc_peer->ksnp_id.nid;
-                data->ioc_flags  = nagle;
+               data->ioc_count = txmem;
+               data->ioc_nid = lnet_nid_to_nid4(&conn->ksnc_peer->ksnp_id.nid);
+               data->ioc_flags = nagle;
                if (psa->sin_family == AF_INET)
                        data->ioc_u32[0] = ntohl(psa->sin_addr.s_addr);
                else
@@ -1698,31 +1939,32 @@ ksocknal_ctl(struct lnet_ni *ni, unsigned int cmd, void *arg)
                 return 0;
         }
 
-        case IOC_LIBCFS_CLOSE_CONNECTION:
-                id.nid = data->ioc_nid;
-                id.pid = LNET_PID_ANY;
-                return ksocknal_close_matching_conns (id,
-                                                      data->ioc_u32[0]);
-
-        case IOC_LIBCFS_REGISTER_MYNID:
-                /* Ignore if this is a noop */
-                if (data->ioc_nid == ni->ni_nid)
-                        return 0;
-
-                CERROR("obsolete IOC_LIBCFS_REGISTER_MYNID: %s(%s)\n",
-                       libcfs_nid2str(data->ioc_nid),
-                       libcfs_nid2str(ni->ni_nid));
-                return -EINVAL;
-
-        case IOC_LIBCFS_PUSH_CONNECTION:
-                id.nid = data->ioc_nid;
-                id.pid = LNET_PID_ANY;
-                return ksocknal_push(ni, id);
-
-        default:
-                return -EINVAL;
-        }
-        /* not reached */
+       case IOC_LIBCFS_CLOSE_CONNECTION:
+               lnet_nid4_to_nid(data->ioc_nid, &id.nid);
+               id.pid = LNET_PID_ANY;
+               return ksocknal_close_matching_conns(&id,
+                                                    data->ioc_u32[0]);
+
+       case IOC_LIBCFS_REGISTER_MYNID:
+               /* Ignore if this is a noop */
+               if (nid_is_nid4(&ni->ni_nid) &&
+                   data->ioc_nid == lnet_nid_to_nid4(&ni->ni_nid))
+                       return 0;
+
+               CERROR("obsolete IOC_LIBCFS_REGISTER_MYNID: %s(%s)\n",
+                      libcfs_nid2str(data->ioc_nid),
+                      libcfs_nidstr(&ni->ni_nid));
+               return -EINVAL;
+
+       case IOC_LIBCFS_PUSH_CONNECTION:
+               lnet_nid4_to_nid(data->ioc_nid, &id.nid);
+               id.pid = LNET_PID_ANY;
+               return ksocknal_push(ni, &id);
+
+       default:
+               return -EINVAL;
+       }
+       /* not reached */
 }
 
 static void
@@ -1742,8 +1984,8 @@ ksocknal_free_buffers (void)
                list_splice_init(&ksocknal_data.ksnd_idle_noop_txs, &zlist);
                spin_unlock(&ksocknal_data.ksnd_tx_lock);
 
-               while (!list_empty(&zlist)) {
-                       tx = list_entry(zlist.next, struct ksock_tx, tx_list);
+               while ((tx = list_first_entry_or_null(&zlist, struct ksock_tx,
+                                                     tx_list)) != NULL) {
                        list_del(&tx->tx_list);
                        LIBCFS_FREE(tx, tx->tx_desc_size);
                }
@@ -1752,6 +1994,207 @@ ksocknal_free_buffers (void)
        }
 }
 
+static int
+ksocknal_handle_link_state_change(struct net_device *dev,
+                                 unsigned char operstate)
+{
+       struct lnet_ni *ni = NULL;
+       struct ksock_net *net;
+       struct ksock_net *cnxt;
+       int ifindex;
+       unsigned char link_down;
+       struct in_device *in_dev;
+       bool found_ip = false;
+       struct ksock_interface *ksi = NULL;
+       struct sockaddr_in *sa;
+       __u32 ni_state_before;
+       bool update_ping_buf = false;
+       int state;
+       DECLARE_CONST_IN_IFADDR(ifa);
+
+       link_down = !((operstate == IF_OPER_UP) || (operstate == IF_OPER_UNKNOWN));
+       ifindex = dev->ifindex;
+
+       if (!ksocknal_data.ksnd_nnets)
+               goto out;
+
+       list_for_each_entry_safe(net, cnxt, &ksocknal_data.ksnd_nets,
+                                ksnn_list) {
+
+               ksi = &net->ksnn_interface;
+               sa = (void *)&ksi->ksni_addr;
+               found_ip = false;
+
+               if (strcmp(ksi->ksni_name, dev->name))
+                       continue;
+
+               if (ksi->ksni_index == -1) {
+                       if (dev->reg_state != NETREG_REGISTERED)
+                               continue;
+                       /* A registration just happened: save the new index for
+                        * the device */
+                       ksi->ksni_index = ifindex;
+                       goto out;
+               }
+
+               if (ksi->ksni_index != ifindex)
+                       continue;
+
+               if (dev->reg_state == NETREG_UNREGISTERING) {
+                       /* Device is being unregistered, we need to clear the
+                        * index, it can change when device will be back */
+                       ksi->ksni_index = -1;
+                       goto out;
+               }
+
+               ni = net->ksnn_ni;
+
+               in_dev = __in_dev_get_rtnl(dev);
+               if (!in_dev) {
+                       CDEBUG(D_NET, "Interface %s has no IPv4 status.\n",
+                              dev->name);
+                       ni_state_before = lnet_set_link_fatal_state(ni, 1);
+                       goto ni_done;
+               }
+               in_dev_for_each_ifa_rtnl(ifa, in_dev) {
+                       if (sa->sin_addr.s_addr == ifa->ifa_local)
+                               found_ip = true;
+               }
+               endfor_ifa(in_dev);
+
+               if (!found_ip) {
+                       CDEBUG(D_NET, "Interface %s has no matching ip\n",
+                              dev->name);
+                       ni_state_before = lnet_set_link_fatal_state(ni, 1);
+                       goto ni_done;
+               }
+
+               if (link_down) {
+                       ni_state_before = lnet_set_link_fatal_state(ni, 1);
+               } else {
+                       state = (lnet_get_link_status(dev) == 0);
+                       ni_state_before = lnet_set_link_fatal_state(ni,
+                                                                   state);
+               }
+ni_done:
+               if (!update_ping_buf &&
+                   (ni->ni_state == LNET_NI_STATE_ACTIVE) &&
+                   (atomic_read(&ni->ni_fatal_error_on) != ni_state_before))
+                       update_ping_buf = true;
+       }
+
+       if (update_ping_buf)
+               lnet_mark_ping_buffer_for_update();
+out:
+       return 0;
+}
+
+
+static int
+ksocknal_handle_inetaddr_change(struct in_ifaddr *ifa, unsigned long event)
+{
+       struct lnet_ni *ni = NULL;
+       struct ksock_net *net;
+       struct ksock_net *cnxt;
+       struct net_device *event_netdev = ifa->ifa_dev->dev;
+       int ifindex;
+       struct ksock_interface *ksi = NULL;
+       struct sockaddr_in *sa;
+       __u32 ni_state_before;
+       bool update_ping_buf = false;
+       bool link_down;
+
+       if (!ksocknal_data.ksnd_nnets)
+               goto out;
+
+       ifindex = event_netdev->ifindex;
+
+       list_for_each_entry_safe(net, cnxt, &ksocknal_data.ksnd_nets,
+                                ksnn_list) {
+
+               ksi = &net->ksnn_interface;
+               sa = (void *)&ksi->ksni_addr;
+
+               if (ksi->ksni_index != ifindex ||
+                   strcmp(ksi->ksni_name, event_netdev->name))
+                       continue;
+
+               if (sa->sin_addr.s_addr == ifa->ifa_local) {
+                       ni = net->ksnn_ni;
+                       link_down = (event == NETDEV_DOWN);
+                       ni_state_before = lnet_set_link_fatal_state(ni,
+                                                                   link_down);
+
+                       if (!update_ping_buf &&
+                           (ni->ni_state == LNET_NI_STATE_ACTIVE) &&
+                           ((event == NETDEV_DOWN) != ni_state_before))
+                               update_ping_buf = true;
+               }
+       }
+
+       if (update_ping_buf)
+               lnet_mark_ping_buffer_for_update();
+out:
+       return 0;
+}
+
+/************************************
+ * Net device notifier event handler
+ ************************************/
+static int ksocknal_device_event(struct notifier_block *unused,
+                                unsigned long event, void *ptr)
+{
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+       unsigned char operstate;
+
+       operstate = dev->operstate;
+
+       CDEBUG(D_NET, "devevent: status=%ld, iface=%s ifindex %d state %u\n",
+              event, dev->name, dev->ifindex, operstate);
+
+       switch (event) {
+       case NETDEV_UP:
+       case NETDEV_DOWN:
+       case NETDEV_CHANGE:
+       case NETDEV_REGISTER:
+       case NETDEV_UNREGISTER:
+               ksocknal_handle_link_state_change(dev, operstate);
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+/************************************
+ * Inetaddr notifier event handler
+ ************************************/
+static int ksocknal_inetaddr_event(struct notifier_block *unused,
+                                  unsigned long event, void *ptr)
+{
+       struct in_ifaddr *ifa = ptr;
+
+       CDEBUG(D_NET, "addrevent: status %ld ip addr %pI4, netmask %pI4.\n",
+              event, &ifa->ifa_address, &ifa->ifa_mask);
+
+       switch (event) {
+       case NETDEV_UP:
+       case NETDEV_DOWN:
+       case NETDEV_CHANGE:
+               ksocknal_handle_inetaddr_change(ifa, event);
+               break;
+
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block ksocknal_dev_notifier_block = {
+       .notifier_call = ksocknal_device_event,
+};
+
+static struct notifier_block ksocknal_inetaddr_notifier_block = {
+       .notifier_call = ksocknal_inetaddr_event,
+};
+
 static void
 ksocknal_base_shutdown(void)
 {
@@ -1763,10 +2206,15 @@ ksocknal_base_shutdown(void)
               libcfs_kmem_read());
        LASSERT (ksocknal_data.ksnd_nnets == 0);
 
+       if (ksocknal_data.ksnd_init == SOCKNAL_INIT_ALL) {
+               unregister_netdevice_notifier(&ksocknal_dev_notifier_block);
+               unregister_inetaddr_notifier(&ksocknal_inetaddr_notifier_block);
+       }
+
        switch (ksocknal_data.ksnd_init) {
        default:
                LASSERT(0);
-               /* fallthrough */
+               fallthrough;
 
        case SOCKNAL_INIT_ALL:
        case SOCKNAL_INIT_DATA:
@@ -1905,15 +2353,13 @@ ksocknal_base_startup(void)
         }
 
         for (i = 0; i < *ksocknal_tunables.ksnd_nconnds; i++) {
-               char name[16];
                spin_lock_bh(&ksocknal_data.ksnd_connd_lock);
                ksocknal_data.ksnd_connd_starting++;
                spin_unlock_bh(&ksocknal_data.ksnd_connd_lock);
 
-
-               snprintf(name, sizeof(name), "socknal_cd%02d", i);
                rc = ksocknal_thread_start(ksocknal_connd,
-                                          (void *)((uintptr_t)i), name);
+                                          (void *)((uintptr_t)i),
+                                          "socknal_cd%02d", i);
                if (rc != 0) {
                        spin_lock_bh(&ksocknal_data.ksnd_connd_lock);
                        ksocknal_data.ksnd_connd_starting--;
@@ -1929,6 +2375,9 @@ ksocknal_base_startup(void)
                 goto failed;
         }
 
+       register_netdevice_notifier(&ksocknal_dev_notifier_block);
+       register_inetaddr_notifier(&ksocknal_inetaddr_notifier_block);
+
         /* flag everything initialised */
         ksocknal_data.ksnd_init = SOCKNAL_INIT_ALL;
 
@@ -1954,9 +2403,8 @@ ksocknal_debug_peerhash(struct lnet_ni *ni)
                if (peer_ni->ksnp_ni != ni)
                        continue;
 
-               CWARN("Active peer_ni on shutdown: %s, ref %d, "
-                     "closing %d, accepting %d, err %d, zcookie %llu, "
-                     "txq %d, zc_req %d\n", libcfs_id2str(peer_ni->ksnp_id),
+               CWARN("Active peer_ni on shutdown: %s, ref %d, closing %d, accepting %d, err %d, zcookie %llu, txq %d, zc_req %d\n",
+                     libcfs_idstr(&peer_ni->ksnp_id),
                      refcount_read(&peer_ni->ksnp_refcount),
                      peer_ni->ksnp_closing,
                      peer_ni->ksnp_accepting, peer_ni->ksnp_error,
@@ -1989,10 +2437,6 @@ void
 ksocknal_shutdown(struct lnet_ni *ni)
 {
        struct ksock_net *net = ni->ni_data;
-       struct lnet_process_id anyid = {
-               .nid = LNET_NID_ANY,
-               .pid = LNET_PID_ANY,
-       };
 
        LASSERT(ksocknal_data.ksnd_init == SOCKNAL_INIT_ALL);
        LASSERT(ksocknal_data.ksnd_nnets > 0);
@@ -2001,7 +2445,7 @@ ksocknal_shutdown(struct lnet_ni *ni)
        atomic_add(SOCKNAL_SHUTDOWN_BIAS, &net->ksnn_npeers);
 
        /* Delete all peers */
-       ksocknal_del_peer(ni, anyid, 0);
+       ksocknal_del_peer(ni, NULL);
 
        /* Wait for all peer_ni state to clean up */
        wait_var_event_warning(&net->ksnn_npeers,
@@ -2079,14 +2523,12 @@ ksocknal_start_schedulers(struct ksock_sched *sched)
 
        for (i = 0; i < nthrs; i++) {
                long id;
-               char name[20];
 
                id = KSOCK_THREAD_ID(sched->kss_cpt, sched->kss_nthreads + i);
-               snprintf(name, sizeof(name), "socknal_sd%02d_%02d",
-                        sched->kss_cpt, (int)KSOCK_THREAD_SID(id));
-
-               rc = ksocknal_thread_start(ksocknal_scheduler,
-                                          (void *)id, name);
+               rc = ksocknal_thread_start(ksocknal_scheduler, (void *)id,
+                                          "socknal_sd%02d_%02d",
+                                          sched->kss_cpt,
+                                          (int)KSOCK_THREAD_SID(id));
                if (rc == 0)
                        continue;
 
@@ -2130,101 +2572,97 @@ int
 ksocknal_startup(struct lnet_ni *ni)
 {
        struct ksock_net *net;
-       struct lnet_ioctl_config_lnd_cmn_tunables *net_tunables;
        struct ksock_interface *ksi = NULL;
        struct lnet_inetdev *ifaces = NULL;
-       struct sockaddr_in *sa;
-       int i = 0;
-       int rc;
+       int rc, if_idx;
+       int dev_status;
 
-        LASSERT (ni->ni_net->net_lnd == &the_ksocklnd);
-        if (ksocknal_data.ksnd_init == SOCKNAL_INIT_NOTHING) {
-                rc = ksocknal_base_startup();
-                if (rc != 0)
-                        return rc;
-        }
+       LASSERT (ni->ni_net->net_lnd == &the_ksocklnd);
+       if (ksocknal_data.ksnd_init == SOCKNAL_INIT_NOTHING) {
+               rc = ksocknal_base_startup();
+               if (rc != 0)
+                       return rc;
+       }
        LIBCFS_ALLOC(net, sizeof(*net));
        if (net == NULL)
-               goto fail_0;
+               goto out_base;
+
        net->ksnn_incarnation = ktime_get_real_ns();
        ni->ni_data = net;
-       net_tunables = &ni->ni_net->net_tunables;
-       if (net_tunables->lct_peer_timeout == -1)
-               net_tunables->lct_peer_timeout =
-                       *ksocknal_tunables.ksnd_peertimeout;
-
-       if (net_tunables->lct_max_tx_credits == -1)
-               net_tunables->lct_max_tx_credits =
-                       *ksocknal_tunables.ksnd_credits;
-
-       if (net_tunables->lct_peer_tx_credits == -1)
-               net_tunables->lct_peer_tx_credits =
-                       *ksocknal_tunables.ksnd_peertxcredits;
 
-       if (net_tunables->lct_peer_tx_credits >
-           net_tunables->lct_max_tx_credits)
-               net_tunables->lct_peer_tx_credits =
-                       net_tunables->lct_max_tx_credits;
+       ksocknal_tunables_setup(ni);
 
-       if (net_tunables->lct_peer_rtr_credits == -1)
-               net_tunables->lct_peer_rtr_credits =
-                       *ksocknal_tunables.ksnd_peerrtrcredits;
-
-       rc = lnet_inet_enumerate(&ifaces, ni->ni_net_ns);
+       rc = lnet_inet_enumerate(&ifaces, ni->ni_net_ns, true);
        if (rc < 0)
-               goto fail_1;
+               goto out_net;
 
        ksi = &net->ksnn_interface;
 
-       /* Use the first discovered interface or look in the list */
-       if (ni->ni_interface) {
-               for (i = 0; i < rc; i++)
-                       if (strcmp(ifaces[i].li_name, ni->ni_interface) == 0)
-                               break;
+       /* Interface and/or IP address is specified otherwise default to
+        * the first Interface
+        */
+       if_idx = lnet_inet_select(ni, ifaces, rc);
+       if (if_idx < 0)
+               goto out_net;
 
-               /* 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;
-               }
+       if (!ni->ni_interface) {
+               rc = lnet_ni_add_interface(ni, ifaces[if_idx].li_name);
+               if (rc < 0)
+                       CWARN("ksocklnd failed to allocate ni_interface\n");
        }
 
-       ni->ni_dev_cpt = ifaces[i].li_cpt;
-       sa = (void *)&ksi->ksni_addr;
-       memset(sa, 0, sizeof(*sa));
-       sa->sin_family = AF_INET;
-       sa->sin_addr.s_addr = htonl(ifaces[i].li_ipaddr);
-       ksi->ksni_index = ksocknal_ip2index((struct sockaddr *)sa, ni);
-       ksi->ksni_netmask = ifaces[i].li_netmask;
-       strlcpy(ksi->ksni_name, ifaces[i].li_name, sizeof(ksi->ksni_name));
+       ni->ni_dev_cpt = ifaces[if_idx].li_cpt;
+       ksi->ksni_index = ifaces[if_idx].li_index;
+       if (ifaces[if_idx].li_size == sizeof(struct in6_addr)) {
+               struct sockaddr_in6 *sa;
+               sa = (void *)&ksi->ksni_addr;
+               memset(sa, 0, sizeof(*sa));
+               sa->sin6_family = AF_INET6;
+               memcpy(&sa->sin6_addr, ifaces[if_idx].li_ipv6addr,
+                      sizeof(struct in6_addr));
+               ni->ni_nid.nid_size = sizeof(struct in6_addr) - 4;
+               memcpy(&ni->ni_nid.nid_addr, ifaces[if_idx].li_ipv6addr,
+                      sizeof(struct in6_addr));
+       } else {
+               struct sockaddr_in *sa;
+               sa = (void *)&ksi->ksni_addr;
+               memset(sa, 0, sizeof(*sa));
+               sa->sin_family = AF_INET;
+               sa->sin_addr.s_addr = ifaces[if_idx].li_ipaddr;
+               ksi->ksni_netmask = ifaces[if_idx].li_netmask;
+               ni->ni_nid.nid_size = 0;
+               ni->ni_nid.nid_addr[0] = sa->sin_addr.s_addr;
+       }
+       strscpy(ksi->ksni_name, ifaces[if_idx].li_name, sizeof(ksi->ksni_name));
 
        /* call it before add it to ksocknal_data.ksnd_nets */
        rc = ksocknal_net_start_threads(net, ni->ni_cpts, ni->ni_ncpts);
        if (rc != 0)
-               goto fail_1;
-
-       LASSERT(ksi);
-       LASSERT(ksi->ksni_addr.ss_family == AF_INET);
-       ni->ni_nid = LNET_MKNID(
-               LNET_NIDNET(ni->ni_nid),
-               ntohl(((struct sockaddr_in *)
-                      &ksi->ksni_addr)->sin_addr.s_addr));
+               goto out_net;
+
+       if ((ksocknal_ip2index((struct sockaddr *)&ksi->ksni_addr,
+                               ni,
+                               &dev_status) < 0) ||
+            (dev_status <= 0))
+               lnet_set_link_fatal_state(ni, 1);
+
        list_add(&net->ksnn_list, &ksocknal_data.ksnd_nets);
+       net->ksnn_ni = ni;
        ksocknal_data.ksnd_nnets++;
+       kfree(ifaces);
 
        return 0;
 
-fail_1:
+out_net:
        LIBCFS_FREE(net, sizeof(*net));
-fail_0:
+out_base:
        if (ksocknal_data.ksnd_nnets == 0)
                ksocknal_base_shutdown();
+       kfree(ifaces);
 
        return -ENETDOWN;
 }
 
-
 static void __exit ksocklnd_exit(void)
 {
        lnet_unregister_lnd(&the_ksocklnd);
@@ -2239,6 +2677,9 @@ 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_get             = ksocknal_nl_get,
+       .lnd_nl_set             = ksocknal_nl_set,
+       .lnd_keys               = &ksocknal_tunables_keys,
 };
 
 static int __init ksocklnd_init(void)
@@ -2253,6 +2694,10 @@ static int __init ksocklnd_init(void)
        if (rc != 0)
                return rc;
 
+       rc = libcfs_setup();
+       if (rc)
+               return rc;
+
        lnet_register_lnd(&the_ksocklnd);
 
        return 0;