Whamcloud - gitweb
LU-12537 lnet: Sync the start of discovery and monitor threads
[fs/lustre-release.git] / lnet / lnet / lib-move.c
index 0ef94aa..c1d4e84 100644 (file)
@@ -42,6 +42,8 @@
 #include <linux/nsproxy.h>
 #include <net/net_namespace.h>
 
+extern unsigned int lnet_current_net_count;
+
 static int local_nid_dist_zero = 1;
 module_param(local_nid_dist_zero, int, 0444);
 MODULE_PARM_DESC(local_nid_dist_zero, "Reserved");
@@ -794,12 +796,32 @@ lnet_ni_eager_recv(struct lnet_ni *ni, struct lnet_msg *msg)
        return rc;
 }
 
+static bool
+lnet_is_peer_deadline_passed(struct lnet_peer_ni *lpni, time64_t now)
+{
+       time64_t deadline;
+
+       deadline = lpni->lpni_last_alive +
+                  lpni->lpni_net->net_tunables.lct_peer_timeout;
+
+       /*
+        * assume peer_ni is alive as long as we're within the configured
+        * peer timeout
+        */
+       if (deadline > now)
+               return false;
+
+       return true;
+}
+
 /* NB: returns 1 when alive, 0 when dead, negative when error;
  *     may drop the lnet_net_lock */
 static int
 lnet_peer_alive_locked(struct lnet_ni *ni, struct lnet_peer_ni *lpni,
                       struct lnet_msg *msg)
 {
+       time64_t now = ktime_get_seconds();
+
        if (!lnet_peer_aliveness_enabled(lpni))
                return -ENODEV;
 
@@ -819,6 +841,9 @@ lnet_peer_alive_locked(struct lnet_ni *ni, struct lnet_peer_ni *lpni,
            msg->msg_type == LNET_MSG_REPLY)
                return 1;
 
+       if (!lnet_is_peer_deadline_passed(lpni, now))
+               return true;
+
        return lnet_is_peer_ni_alive(lpni);
 }
 
@@ -1294,7 +1319,6 @@ routing_off:
        }
 }
 
-#if 0
 static int
 lnet_compare_peers(struct lnet_peer_ni *p1, struct lnet_peer_ni *p2)
 {
@@ -1312,53 +1336,197 @@ lnet_compare_peers(struct lnet_peer_ni *p1, struct lnet_peer_ni *p2)
 
        return 0;
 }
-#endif
+
+static struct lnet_peer_ni *
+lnet_select_peer_ni(struct lnet_ni *best_ni, lnet_nid_t dst_nid,
+                   struct lnet_peer *peer,
+                   struct lnet_peer_net *peer_net)
+{
+       /*
+        * Look at the peer NIs for the destination peer that connect
+        * to the chosen net. If a peer_ni is preferred when using the
+        * best_ni to communicate, we use that one. If there is no
+        * preferred peer_ni, or there are multiple preferred peer_ni,
+        * the available transmit credits are used. If the transmit
+        * credits are equal, we round-robin over the peer_ni.
+        */
+       struct lnet_peer_ni *lpni = NULL;
+       struct lnet_peer_ni *best_lpni = NULL;
+       int best_lpni_credits = INT_MIN;
+       bool preferred = false;
+       bool ni_is_pref;
+       int best_lpni_healthv = 0;
+       int lpni_healthv;
+
+       while ((lpni = lnet_get_next_peer_ni_locked(peer, peer_net, lpni))) {
+               /*
+                * if the best_ni we've chosen aleady has this lpni
+                * preferred, then let's use it
+                */
+               if (best_ni) {
+                       ni_is_pref = lnet_peer_is_pref_nid_locked(lpni,
+                                                               best_ni->ni_nid);
+                       CDEBUG(D_NET, "%s ni_is_pref = %d\n",
+                              libcfs_nid2str(best_ni->ni_nid), ni_is_pref);
+               } else {
+                       ni_is_pref = false;
+               }
+
+               lpni_healthv = atomic_read(&lpni->lpni_healthv);
+
+               if (best_lpni)
+                       CDEBUG(D_NET, "%s c:[%d, %d], s:[%d, %d]\n",
+                               libcfs_nid2str(lpni->lpni_nid),
+                               lpni->lpni_txcredits, best_lpni_credits,
+                               lpni->lpni_seq, best_lpni->lpni_seq);
+
+               /* pick the healthiest peer ni */
+               if (lpni_healthv < best_lpni_healthv) {
+                       continue;
+               } else if (lpni_healthv > best_lpni_healthv) {
+                       best_lpni_healthv = lpni_healthv;
+               /* if this is a preferred peer use it */
+               } else if (!preferred && ni_is_pref) {
+                       preferred = true;
+               } else if (preferred && !ni_is_pref) {
+                       /*
+                        * this is not the preferred peer so let's ignore
+                        * it.
+                        */
+                       continue;
+               } else if (lpni->lpni_txcredits < best_lpni_credits) {
+                       /*
+                        * We already have a peer that has more credits
+                        * available than this one. No need to consider
+                        * this peer further.
+                        */
+                       continue;
+               } else if (lpni->lpni_txcredits == best_lpni_credits) {
+                       /*
+                        * The best peer found so far and the current peer
+                        * have the same number of available credits let's
+                        * make sure to select between them using Round
+                        * Robin
+                        */
+                       if (best_lpni) {
+                               if (best_lpni->lpni_seq <= lpni->lpni_seq)
+                                       continue;
+                       }
+               }
+
+               best_lpni = lpni;
+               best_lpni_credits = lpni->lpni_txcredits;
+       }
+
+       /* if we still can't find a peer ni then we can't reach it */
+       if (!best_lpni) {
+               __u32 net_id = (peer_net) ? peer_net->lpn_net_id :
+                       LNET_NIDNET(dst_nid);
+               CDEBUG(D_NET, "no peer_ni found on peer net %s\n",
+                               libcfs_net2str(net_id));
+               return NULL;
+       }
+
+       CDEBUG(D_NET, "sd_best_lpni = %s\n",
+              libcfs_nid2str(best_lpni->lpni_nid));
+
+       return best_lpni;
+}
+
+/*
+ * Prerequisite: the best_ni should already be set in the sd
+ */
+static inline struct lnet_peer_ni *
+lnet_find_best_lpni_on_net(struct lnet_send_data *sd, struct lnet_peer *peer,
+                          __u32 net_id)
+{
+       struct lnet_peer_net *peer_net;
+
+       /*
+        * The gateway is Multi-Rail capable so now we must select the
+        * proper peer_ni
+        */
+       peer_net = lnet_peer_get_net_locked(peer, net_id);
+
+       if (!peer_net) {
+               CERROR("gateway peer %s has no NI on net %s\n",
+                      libcfs_nid2str(peer->lp_primary_nid),
+                      libcfs_net2str(net_id));
+               return NULL;
+       }
+
+       return lnet_select_peer_ni(sd->sd_best_ni, sd->sd_dst_nid,
+                                  peer, peer_net);
+}
 
 static int
-lnet_compare_routes(struct lnet_route *r1, struct lnet_route *r2)
+lnet_compare_routes(struct lnet_route *r1, struct lnet_route *r2,
+                   struct lnet_peer_ni **best_lpni)
 {
-       /* TODO re-implement gateway comparison
-       struct lnet_peer_ni *p1 = r1->lr_gateway;
-       struct lnet_peer_ni *p2 = r2->lr_gateway;
-       */
        int r1_hops = (r1->lr_hops == LNET_UNDEFINED_HOPS) ? 1 : r1->lr_hops;
        int r2_hops = (r2->lr_hops == LNET_UNDEFINED_HOPS) ? 1 : r2->lr_hops;
-       /*int rc;*/
+       struct lnet_peer *lp1 = r1->lr_gateway;
+       struct lnet_peer *lp2 = r2->lr_gateway;
+       struct lnet_peer_ni *lpni1;
+       struct lnet_peer_ni *lpni2;
+       struct lnet_send_data sd;
+       int rc;
 
-       if (r1->lr_priority < r2->lr_priority)
+       sd.sd_best_ni = NULL;
+       sd.sd_dst_nid = LNET_NID_ANY;
+       lpni1 = lnet_find_best_lpni_on_net(&sd, lp1, r1->lr_lnet);
+       lpni2 = lnet_find_best_lpni_on_net(&sd, lp2, r2->lr_lnet);
+       LASSERT(lpni1 && lpni2);
+
+       if (r1->lr_priority < r2->lr_priority) {
+               *best_lpni = lpni1;
                return 1;
+       }
 
-       if (r1->lr_priority > r2->lr_priority)
+       if (r1->lr_priority > r2->lr_priority) {
+               *best_lpni = lpni2;
                return -1;
+       }
 
-       if (r1_hops < r2_hops)
+       if (r1_hops < r2_hops) {
+               *best_lpni = lpni1;
                return 1;
+       }
 
-       if (r1_hops > r2_hops)
+       if (r1_hops > r2_hops) {
+               *best_lpni = lpni2;
                return -1;
+       }
 
-       /*
-       rc = lnet_compare_peers(p1, p2);
-       if (rc)
+       rc = lnet_compare_peers(lpni1, lpni2);
+       if (rc == 1) {
+               *best_lpni = lpni1;
                return rc;
-       */
+       } else if (rc == -1) {
+               *best_lpni = lpni2;
+               return rc;
+       }
 
-       if (r1->lr_seq - r2->lr_seq <= 0)
+       if (r1->lr_seq - r2->lr_seq <= 0) {
+               *best_lpni = lpni1;
                return 1;
+       }
 
+       *best_lpni = lpni2;
        return -1;
 }
 
-/* TODO: lnet_find_route_locked() needs to be reimplemented */
 static struct lnet_route *
 lnet_find_route_locked(struct lnet_net *net, __u32 remote_net,
-                      lnet_nid_t rtr_nid, struct lnet_route **prev_route)
+                      lnet_nid_t rtr_nid, struct lnet_route **prev_route,
+                      struct lnet_peer_ni **gwni)
 {
-       struct lnet_remotenet *rnet;
-       struct lnet_route *route;
+       struct lnet_peer_ni *best_gw_ni = NULL;
        struct lnet_route *best_route;
        struct lnet_route *last_route;
+       struct lnet_remotenet *rnet;
        struct lnet_peer *lp_best;
+       struct lnet_route *route;
        struct lnet_peer *lp;
        int rc;
 
@@ -1380,14 +1548,13 @@ lnet_find_route_locked(struct lnet_net *net, __u32 remote_net,
                if (lp_best == NULL) {
                        best_route = last_route = route;
                        lp_best = lp;
-                       continue;
                }
 
                /* no protection on below fields, but it's harmless */
                if (last_route->lr_seq - route->lr_seq < 0)
                        last_route = route;
 
-               rc = lnet_compare_routes(route, best_route);
+               rc = lnet_compare_routes(route, best_route, &best_gw_ni);
                if (rc < 0)
                        continue;
 
@@ -1396,6 +1563,7 @@ lnet_find_route_locked(struct lnet_net *net, __u32 remote_net,
        }
 
        *prev_route = last_route;
+       *gwni = best_gw_ni;
 
        return best_route;
 }
@@ -1693,125 +1861,6 @@ lnet_handle_send(struct lnet_send_data *sd)
        return rc;
 }
 
-static struct lnet_peer_ni *
-lnet_select_peer_ni(struct lnet_send_data *sd, struct lnet_peer *peer,
-                   struct lnet_peer_net *peer_net)
-{
-       /*
-        * Look at the peer NIs for the destination peer that connect
-        * to the chosen net. If a peer_ni is preferred when using the
-        * best_ni to communicate, we use that one. If there is no
-        * preferred peer_ni, or there are multiple preferred peer_ni,
-        * the available transmit credits are used. If the transmit
-        * credits are equal, we round-robin over the peer_ni.
-        */
-       struct lnet_peer_ni *lpni = NULL;
-       struct lnet_peer_ni *best_lpni = NULL;
-       struct lnet_ni *best_ni = sd->sd_best_ni;
-       lnet_nid_t dst_nid = sd->sd_dst_nid;
-       int best_lpni_credits = INT_MIN;
-       bool preferred = false;
-       bool ni_is_pref;
-       int best_lpni_healthv = 0;
-       int lpni_healthv;
-
-       while ((lpni = lnet_get_next_peer_ni_locked(peer, peer_net, lpni))) {
-               /*
-                * if the best_ni we've chosen aleady has this lpni
-                * preferred, then let's use it
-                */
-               ni_is_pref = lnet_peer_is_pref_nid_locked(lpni,
-                                                         best_ni->ni_nid);
-
-               lpni_healthv = atomic_read(&lpni->lpni_healthv);
-
-               CDEBUG(D_NET, "%s ni_is_pref = %d\n",
-                      libcfs_nid2str(best_ni->ni_nid), ni_is_pref);
-
-               if (best_lpni)
-                       CDEBUG(D_NET, "%s c:[%d, %d], s:[%d, %d]\n",
-                               libcfs_nid2str(lpni->lpni_nid),
-                               lpni->lpni_txcredits, best_lpni_credits,
-                               lpni->lpni_seq, best_lpni->lpni_seq);
-
-               /* pick the healthiest peer ni */
-               if (lpni_healthv < best_lpni_healthv) {
-                       continue;
-               } else if (lpni_healthv > best_lpni_healthv) {
-                       best_lpni_healthv = lpni_healthv;
-               /* if this is a preferred peer use it */
-               } else if (!preferred && ni_is_pref) {
-                       preferred = true;
-               } else if (preferred && !ni_is_pref) {
-                       /*
-                        * this is not the preferred peer so let's ignore
-                        * it.
-                        */
-                       continue;
-               } else if (lpni->lpni_txcredits < best_lpni_credits) {
-                       /*
-                        * We already have a peer that has more credits
-                        * available than this one. No need to consider
-                        * this peer further.
-                        */
-                       continue;
-               } else if (lpni->lpni_txcredits == best_lpni_credits) {
-                       /*
-                        * The best peer found so far and the current peer
-                        * have the same number of available credits let's
-                        * make sure to select between them using Round
-                        * Robin
-                        */
-                       if (best_lpni) {
-                               if (best_lpni->lpni_seq <= lpni->lpni_seq)
-                                       continue;
-                       }
-               }
-
-               best_lpni = lpni;
-               best_lpni_credits = lpni->lpni_txcredits;
-       }
-
-       /* if we still can't find a peer ni then we can't reach it */
-       if (!best_lpni) {
-               __u32 net_id = (peer_net) ? peer_net->lpn_net_id :
-                       LNET_NIDNET(dst_nid);
-               CDEBUG(D_NET, "no peer_ni found on peer net %s\n",
-                               libcfs_net2str(net_id));
-               return NULL;
-       }
-
-       CDEBUG(D_NET, "sd_best_lpni = %s\n",
-              libcfs_nid2str(best_lpni->lpni_nid));
-
-       return best_lpni;
-}
-
-/*
- * Prerequisite: the best_ni should already be set in the sd
- */
-static inline struct lnet_peer_ni *
-lnet_find_best_lpni_on_net(struct lnet_send_data *sd, struct lnet_peer *peer,
-                          __u32 net_id)
-{
-       struct lnet_peer_net *peer_net;
-
-       /*
-        * The gateway is Multi-Rail capable so now we must select the
-        * proper peer_ni
-        */
-       peer_net = lnet_peer_get_net_locked(peer, net_id);
-
-       if (!peer_net) {
-               CERROR("gateway peer %s has no NI on net %s\n",
-                      libcfs_nid2str(peer->lp_primary_nid),
-                      libcfs_net2str(net_id));
-               return NULL;
-       }
-
-       return lnet_select_peer_ni(sd, peer, peer_net);
-}
-
 static inline void
 lnet_set_non_mr_pref_nid(struct lnet_send_data *sd)
 {
@@ -1941,6 +1990,18 @@ lnet_initiate_peer_discovery(struct lnet_peer_ni *lpni,
 
        lnet_peer_ni_addref_locked(lpni);
 
+       peer = lpni->lpni_peer_net->lpn_peer;
+
+       if (lnet_peer_gw_discovery(peer)) {
+               lnet_peer_ni_decref_locked(lpni);
+               return 0;
+       }
+
+       if (!lnet_msg_discovery(msg) || lnet_peer_is_uptodate(peer)) {
+               lnet_peer_ni_decref_locked(lpni);
+               return 0;
+       }
+
        rc = lnet_discover_peer_locked(lpni, cpt, false);
        if (rc) {
                lnet_peer_ni_decref_locked(lpni);
@@ -1970,37 +2031,80 @@ lnet_handle_find_routed_path(struct lnet_send_data *sd,
                             struct lnet_peer_ni **gw_lpni,
                             struct lnet_peer **gw_peer)
 {
+       int rc;
        struct lnet_peer *gw;
+       struct lnet_peer *lp;
+       struct lnet_peer_net *lpn;
+       struct lnet_peer_net *best_lpn = NULL;
+       struct lnet_remotenet *rnet;
        struct lnet_route *best_route;
        struct lnet_route *last_route;
        struct lnet_peer_ni *lpni = NULL;
+       struct lnet_peer_ni *gwni = NULL;
        lnet_nid_t src_nid = sd->sd_src_nid;
 
-       best_route = lnet_find_route_locked(NULL, LNET_NIDNET(dst_nid),
-                                           sd->sd_rtr_nid, &last_route);
+       /* we've already looked up the initial lpni using dst_nid */
+       lpni = sd->sd_best_lpni;
+       /* the peer tree must be in existence */
+       LASSERT(lpni && lpni->lpni_peer_net && lpni->lpni_peer_net->lpn_peer);
+       lp = lpni->lpni_peer_net->lpn_peer;
+
+       list_for_each_entry(lpn, &lp->lp_peer_nets, lpn_peer_nets) {
+               /* is this remote network reachable?  */
+               rnet = lnet_find_rnet_locked(lpn->lpn_net_id);
+               if (!rnet)
+                       continue;
+
+               if (!best_lpn)
+                       best_lpn = lpn;
+
+               if (best_lpn->lpn_seq <= lpn->lpn_seq)
+                       continue;
+
+               best_lpn = lpn;
+       }
+
+       if (!best_lpn) {
+               CERROR("peer %s has no available nets \n",
+                      libcfs_nid2str(sd->sd_dst_nid));
+               return -EHOSTUNREACH;
+       }
+
+       sd->sd_best_lpni = lnet_find_best_lpni_on_net(sd, lp, best_lpn->lpn_net_id);
+       if (!sd->sd_best_lpni) {
+               CERROR("peer %s down\n", libcfs_nid2str(sd->sd_dst_nid));
+               return -EHOSTUNREACH;
+       }
+
+       best_route = lnet_find_route_locked(NULL, best_lpn->lpn_net_id,
+                                           sd->sd_rtr_nid, &last_route,
+                                           &gwni);
        if (!best_route) {
                CERROR("no route to %s from %s\n",
                       libcfs_nid2str(dst_nid), libcfs_nid2str(src_nid));
                return -EHOSTUNREACH;
        }
 
+       if (!gwni) {
+               CERROR("Internal Error. Route expected to %s from %s\n",
+                       libcfs_nid2str(dst_nid),
+                       libcfs_nid2str(src_nid));
+               return -EFAULT;
+       }
+
        gw = best_route->lr_gateway;
-       *gw_peer = gw;
+       LASSERT(gw == gwni->lpni_peer_net->lpn_peer);
 
        /*
         * Discover this gateway if it hasn't already been discovered.
         * This means we might delay the message until discovery has
         * completed
         */
-#if 0
-       /* TODO: disable discovey for now */
-       if (lnet_msg_discovery(sd->sd_msg) &&
-           !lnet_peer_is_uptodate(*gw_peer)) {
-               sd->sd_msg->msg_src_nid_param = sd->sd_src_nid;
-               return lnet_initiate_peer_discovery(gw, sd->sd_msg,
-                                                   sd->sd_rtr_nid, sd->sd_cpt);
-       }
-#endif
+       sd->sd_msg->msg_src_nid_param = sd->sd_src_nid;
+       rc = lnet_initiate_peer_discovery(gwni, sd->sd_msg, sd->sd_rtr_nid,
+                                         sd->sd_cpt);
+       if (rc)
+               return rc;
 
        if (!sd->sd_best_ni)
                sd->sd_best_ni = lnet_find_best_ni_on_spec_net(NULL, gw,
@@ -2017,50 +2121,16 @@ lnet_handle_find_routed_path(struct lnet_send_data *sd,
                return -EFAULT;
        }
 
-       /*
-        * if gw is MR let's find its best peer_ni
-        */
-       if (lnet_peer_is_multi_rail(gw)) {
-               lpni = lnet_find_best_lpni_on_net(sd, gw,
-                               sd->sd_best_ni->ni_net->net_id);
-               /*
-                * We've already verified that the gw has an NI on that
-                * desired net, but we're not finding it. Something is
-                * wrong.
-                */
-               if (!lpni) {
-                       CERROR("Internal Error. Route expected to %s from %s\n",
-                               libcfs_nid2str(dst_nid),
-                               libcfs_nid2str(src_nid));
-                       return -EFAULT;
-               }
-       } else {
-               struct lnet_peer_net *lpn;
-               lpn = lnet_peer_get_net_locked(gw, best_route->lr_lnet);
-               if (!lpn) {
-                       CERROR("Internal Error. Route expected to %s from %s\n",
-                               libcfs_nid2str(dst_nid),
-                               libcfs_nid2str(src_nid));
-                       return -EFAULT;
-               }
-               lpni = list_entry(lpn->lpn_peer_nis.next, struct lnet_peer_ni,
-                                 lpni_peer_nis);
-               if (!lpni) {
-                       CERROR("Internal Error. Route expected to %s from %s\n",
-                               libcfs_nid2str(dst_nid),
-                               libcfs_nid2str(src_nid));
-                       return -EFAULT;
-               }
-       }
-
-       *gw_lpni = lpni;
+       *gw_lpni = gwni;
+       *gw_peer = gw;
 
        /*
-        * increment the route sequence number since now we're sure we're
-        * going to use it
+        * increment the sequence numbers since now we're sure we're
+        * going to use this path
         */
        LASSERT(best_route && last_route);
        best_route->lr_seq = last_route->lr_seq + 1;
+       best_lpn->lpn_seq++;
 
        return 0;
 }
@@ -2120,7 +2190,8 @@ lnet_handle_spec_router_dst(struct lnet_send_data *sd)
 }
 
 struct lnet_ni *
-lnet_find_best_ni_on_local_net(struct lnet_peer *peer, int md_cpt)
+lnet_find_best_ni_on_local_net(struct lnet_peer *peer, int md_cpt,
+                              bool discovery)
 {
        struct lnet_peer_net *peer_net = NULL;
        struct lnet_ni *best_ni = NULL;
@@ -2142,6 +2213,14 @@ lnet_find_best_ni_on_local_net(struct lnet_peer *peer, int md_cpt)
                        continue;
                best_ni = lnet_find_best_ni_on_spec_net(best_ni, peer,
                                                   peer_net, md_cpt, false);
+
+               /*
+                * if this is a discovery message and lp_disc_net_id is
+                * specified then use that net to send the discovery on.
+                */
+               if (peer->lp_disc_net_id == peer_net->lpn_net_id &&
+                   discovery)
+                       break;
        }
 
        if (best_ni)
@@ -2311,7 +2390,8 @@ lnet_handle_any_mr_dsta(struct lnet_send_data *sd)
         * networks.
         */
        sd->sd_best_ni = lnet_find_best_ni_on_local_net(sd->sd_peer,
-                                                       sd->sd_md_cpt);
+                                       sd->sd_md_cpt,
+                                       lnet_msg_discovery(sd->sd_msg));
        if (sd->sd_best_ni) {
                sd->sd_best_lpni =
                  lnet_find_best_lpni_on_net(sd, sd->sd_peer,
@@ -2421,11 +2501,11 @@ lnet_handle_any_mr_dst(struct lnet_send_data *sd)
                return rc;
 
        /*
-        * TODO; One possible enhancement is to run the selection
-        * algorithm on the peer. However for remote peers the credits are
-        * not decremented, so we'll be basically going over the peer NIs
-        * in round robin. An MR router will run the selection algorithm
-        * on the next-hop interfaces.
+        * Now that we must route to the destination, we must consider the
+        * MR case, where the destination has multiple interfaces, some of
+        * which we can route to and others we do not. For this reason we
+        * need to select the destination which we can route to and if
+        * there are multiple, we need to round robin.
         */
        rc = lnet_handle_find_routed_path(sd, sd->sd_dst_nid, &gw_lpni,
                                          &gw_peer);
@@ -2596,8 +2676,8 @@ again:
         * trigger discovery.
         */
        peer = lpni->lpni_peer_net->lpn_peer;
-       if (lnet_msg_discovery(msg) && !lnet_peer_is_uptodate(peer)) {
-               rc = lnet_initiate_peer_discovery(lpni, msg, rtr_nid, cpt);
+       rc = lnet_initiate_peer_discovery(lpni, msg, rtr_nid, cpt);
+       if (rc) {
                lnet_peer_ni_decref_locked(lpni);
                lnet_net_unlock(cpt);
                return rc;
@@ -2684,8 +2764,13 @@ lnet_send(lnet_nid_t src_nid, struct lnet_msg *msg, lnet_nid_t rtr_nid)
        LASSERT(!msg->msg_tx_committed);
 
        rc = lnet_select_pathway(src_nid, dst_nid, msg, rtr_nid);
-       if (rc < 0)
+       if (rc < 0) {
+               if (rc == -EHOSTUNREACH)
+                       msg->msg_health_status = LNET_MSG_STATUS_REMOTE_ERROR;
+               else
+                       msg->msg_health_status = LNET_MSG_STATUS_LOCAL_ERROR;
                return rc;
+       }
 
        if (rc == LNET_CREDIT_OK)
                lnet_ni_send(msg->msg_txni, msg);
@@ -2799,8 +2884,9 @@ lnet_finalize_expired_responses(bool force)
 
                                nid = rspt->rspt_next_hop_nid;
 
-                               CNETERR("Response timed out: md = %p: nid = %s\n",
-                                       md, libcfs_nid2str(nid));
+                               CDEBUG(D_NET,
+                                      "Response timeout: md = %p: nid = %s\n",
+                                      md, libcfs_nid2str(nid));
                                LNetMDUnlink(rspt->rspt_mdh);
                                lnet_rspt_free(rspt, i);
 
@@ -3360,6 +3446,7 @@ lnet_monitor_thread(void *arg)
        int interval;
        time64_t now;
 
+       wait_for_completion(&the_lnet.ln_started);
        /*
         * The monitor thread takes care of the following:
         *  1. Checks the aliveness of routers
@@ -3399,17 +3486,20 @@ lnet_monitor_thread(void *arg)
                 * if we wake up every 1 second? Although, we've seen
                 * cases where we get a complaint that an idle thread
                 * is waking up unnecessarily.
+                *
+                * Take into account the current net_count when you wake
+                * up for alive router checking, since we need to check
+                * possibly as many networks as we have configured.
                 */
                interval = min(lnet_recovery_interval,
-                              lnet_transaction_timeout / 2);
+                              min((unsigned int) alive_router_check_interval /
+                                       lnet_current_net_count,
+                                  lnet_transaction_timeout / 2));
                wait_event_interruptible_timeout(the_lnet.ln_mt_waitq,
                                                false,
                                                cfs_time_seconds(interval));
        }
 
-       /* clean up the router checker */
-       lnet_prune_rc_data(1);
-
        /* Shutting down */
        lnet_net_lock(LNET_LOCK_EX);
        the_lnet.ln_mt_state = LNET_MT_STATE_SHUTDOWN;
@@ -3622,11 +3712,6 @@ int lnet_monitor_thr_start(void)
        if (rc)
                goto clean_queues;
 
-       /* Pre monitor thread start processing */
-       rc = lnet_router_pre_mt_start();
-       if (rc)
-               goto free_mem;
-
        sema_init(&the_lnet.ln_mt_signal, 0);
 
        lnet_net_lock(LNET_LOCK_EX);
@@ -3639,9 +3724,6 @@ int lnet_monitor_thr_start(void)
                goto clean_thread;
        }
 
-       /* post monitor thread start processing */
-       lnet_router_post_mt_start();
-
        return 0;
 
 clean_thread:
@@ -3651,8 +3733,6 @@ clean_thread:
        /* block until event callback signals exit */
        down(&the_lnet.ln_mt_signal);
        /* clean up */
-       lnet_router_cleanup();
-free_mem:
        lnet_net_lock(LNET_LOCK_EX);
        the_lnet.ln_mt_state = LNET_MT_STATE_SHUTDOWN;
        lnet_net_unlock(LNET_LOCK_EX);
@@ -3688,7 +3768,6 @@ void lnet_monitor_thr_stop(void)
        LASSERT(the_lnet.ln_mt_state == LNET_MT_STATE_SHUTDOWN);
 
        /* perform cleanup tasks */
-       lnet_router_cleanup();
        lnet_rsp_tracker_clean();
        lnet_clean_local_ni_recoveryq();
        lnet_clean_peer_ni_recoveryq();
@@ -4116,16 +4195,17 @@ int
 lnet_parse(struct lnet_ni *ni, struct lnet_hdr *hdr, lnet_nid_t from_nid,
           void *private, int rdma_req)
 {
-       int             rc = 0;
-       int             cpt;
-       int             for_me;
-       struct lnet_msg *msg;
-       lnet_pid_t     dest_pid;
-       lnet_nid_t     dest_nid;
-       lnet_nid_t     src_nid;
        struct lnet_peer_ni *lpni;
-       __u32          payload_length;
-       __u32          type;
+       struct lnet_msg *msg;
+       __u32 payload_length;
+       lnet_pid_t dest_pid;
+       lnet_nid_t dest_nid;
+       lnet_nid_t src_nid;
+       bool push = false;
+       int for_me;
+       __u32 type;
+       int rc = 0;
+       int cpt;
 
        LASSERT (!in_interrupt ());
 
@@ -4180,16 +4260,22 @@ lnet_parse(struct lnet_ni *ni, struct lnet_hdr *hdr, lnet_nid_t from_nid,
        }
 
        if (the_lnet.ln_routing &&
-           ni->ni_last_alive != ktime_get_real_seconds()) {
-               /* NB: so far here is the only place to set NI status to "up */
+           ni->ni_net->net_last_alive != ktime_get_real_seconds()) {
                lnet_ni_lock(ni);
-               ni->ni_last_alive = ktime_get_real_seconds();
+               spin_lock(&ni->ni_net->net_lock);
+               ni->ni_net->net_last_alive = ktime_get_real_seconds();
+               spin_unlock(&ni->ni_net->net_lock);
                if (ni->ni_status != NULL &&
-                   ni->ni_status->ns_status == LNET_NI_STATUS_DOWN)
+                   ni->ni_status->ns_status == LNET_NI_STATUS_DOWN) {
                        ni->ni_status->ns_status = LNET_NI_STATUS_UP;
+                       push = true;
+               }
                lnet_ni_unlock(ni);
        }
 
+       if (push)
+               lnet_push_update_to_peers(1);
+
        /* Regard a bad destination NID as a protocol error.  Senders should
         * know what they're doing; if they don't they're misconfigured, buggy
         * or malicious so we chop them off at the knees :) */
@@ -4247,7 +4333,7 @@ lnet_parse(struct lnet_ni *ni, struct lnet_hdr *hdr, lnet_nid_t from_nid,
        }
 
        if (!list_empty(&the_lnet.ln_drop_rules) &&
-           lnet_drop_rule_match(hdr, NULL)) {
+           lnet_drop_rule_match(hdr, ni->ni_nid, NULL)) {
                CDEBUG(D_NET, "%s, src %s, dst %s: Dropping %s to simulate"
                              "silent message loss\n",
                       libcfs_nid2str(from_nid), libcfs_nid2str(src_nid),
@@ -4359,6 +4445,10 @@ lnet_parse(struct lnet_ni *ni, struct lnet_hdr *hdr, lnet_nid_t from_nid,
                        return 0;
                goto drop;
        }
+
+       if (the_lnet.ln_routing)
+               lpni->lpni_last_alive = ktime_get_seconds();
+
        msg->msg_rxpeer = lpni;
        msg->msg_rxni = ni;
        lnet_ni_addref_locked(ni, cpt);