Whamcloud - gitweb
LU-13569 lnet: Recover local NI w/exponential backoff interval
[fs/lustre-release.git] / lnet / lnet / lib-move.c
index f69b21f..c9b14f5 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/lnet/lib-move.c
  *
@@ -2031,6 +2030,7 @@ lnet_initiate_peer_discovery(struct lnet_peer_ni *lpni, struct lnet_msg *msg,
                             int cpt)
 {
        struct lnet_peer *peer;
+       struct lnet_peer_ni *new_lpni;
        int rc;
 
        lnet_peer_ni_addref_locked(lpni);
@@ -2052,21 +2052,38 @@ lnet_initiate_peer_discovery(struct lnet_peer_ni *lpni, struct lnet_msg *msg,
                lnet_peer_ni_decref_locked(lpni);
                return rc;
        }
-       /* The peer may have changed. */
-       peer = lpni->lpni_peer_net->lpn_peer;
+
+       new_lpni = lnet_find_peer_ni_locked(lpni->lpni_nid);
+       if (!new_lpni) {
+               lnet_peer_ni_decref_locked(lpni);
+               return -ENOENT;
+       }
+
+       peer = new_lpni->lpni_peer_net->lpn_peer;
        spin_lock(&peer->lp_lock);
-       if (lnet_peer_is_uptodate_locked(peer)) {
+       if (lpni == new_lpni && lnet_peer_is_uptodate_locked(peer)) {
+               /* The peer NI did not change and the peer is up to date.
+                * Nothing more to do.
+                */
                spin_unlock(&peer->lp_lock);
                lnet_peer_ni_decref_locked(lpni);
+               lnet_peer_ni_decref_locked(new_lpni);
                return 0;
        }
-       /* queue message and return */
+       spin_unlock(&peer->lp_lock);
+
+       /* Either the peer NI changed during discovery, or the peer isn't up
+        * to date. In both cases we want to queue the message on the
+        * (possibly new) peer's pending queue and queue the peer for discovery
+        */
        msg->msg_sending = 0;
        msg->msg_txpeer = NULL;
-       list_add_tail(&msg->msg_list, &peer->lp_dc_pendq);
-       spin_unlock(&peer->lp_lock);
+       lnet_net_unlock(cpt);
+       lnet_peer_queue_message(peer, msg);
+       lnet_net_lock(cpt);
 
        lnet_peer_ni_decref_locked(lpni);
+       lnet_peer_ni_decref_locked(new_lpni);
 
        CDEBUG(D_NET, "msg %p delayed. %s pending discovery\n",
               msg, libcfs_nid2str(peer->lp_primary_nid));
@@ -3331,6 +3348,7 @@ lnet_recover_local_nis(void)
        lnet_nid_t nid;
        int healthv;
        int rc;
+       time64_t now;
 
        /*
         * splice the recovery queue on a local queue. We will iterate
@@ -3344,6 +3362,8 @@ lnet_recover_local_nis(void)
                         &local_queue);
        lnet_net_unlock(0);
 
+       now = ktime_get_seconds();
+
        list_for_each_entry_safe(ni, tmp, &local_queue, ni_recovery) {
                /*
                 * if an NI is being deleted or it is now healthy, there
@@ -3377,9 +3397,15 @@ lnet_recover_local_nis(void)
                        ni->ni_recovery_state &= ~LNET_NI_RECOVERY_FAILED;
                }
 
+
                lnet_ni_unlock(ni);
-               lnet_net_unlock(0);
 
+               if (now < ni->ni_next_ping) {
+                       lnet_net_unlock(0);
+                       continue;
+               }
+
+               lnet_net_unlock(0);
 
                CDEBUG(D_NET, "attempting to recover local ni: %s\n",
                       libcfs_nid2str(ni->ni_nid));
@@ -3447,30 +3473,20 @@ lnet_recover_local_nis(void)
                                LNetMDUnlink(mdh);
                                continue;
                        }
-                       /*
-                        * Same note as in lnet_recover_peer_nis(). When
-                        * we're sending the ping, the NI is free to be
-                        * deleted or manipulated. By this point it
-                        * could've been added back on the recovery queue,
-                        * and a refcount taken on it.
-                        * So we can't just add it blindly again or we'll
-                        * corrupt the queue. We must check under lock if
-                        * it's not on any list and if not then add it
-                        * to the processed list, which will eventually be
-                        * spliced back on to the recovery queue.
-                        */
+                       ni->ni_ping_count++;
+
                        ni->ni_ping_mdh = mdh;
-                       if (list_empty(&ni->ni_recovery)) {
-                               list_add_tail(&ni->ni_recovery, &processed_list);
-                               lnet_ni_addref_locked(ni, 0);
-                       }
-                       lnet_net_unlock(0);
+                       lnet_ni_add_to_recoveryq_locked(ni, &processed_list,
+                                                       now);
 
-                       lnet_ni_lock(ni);
-                       if (rc)
+                       if (rc) {
+                               lnet_ni_lock(ni);
                                ni->ni_recovery_state &= ~LNET_NI_RECOVERY_PENDING;
-               }
-               lnet_ni_unlock(ni);
+                               lnet_ni_unlock(ni);
+                       }
+                       lnet_net_unlock(0);
+               } else
+                       lnet_ni_unlock(ni);
        }
 
        /*
@@ -3592,6 +3608,7 @@ lnet_recover_peer_nis(void)
        lnet_nid_t nid;
        int healthv;
        int rc;
+       time64_t now;
 
        /*
         * Always use cpt 0 for locking across all interactions with
@@ -3602,6 +3619,8 @@ lnet_recover_peer_nis(void)
                         &local_queue);
        lnet_net_unlock(0);
 
+       now = ktime_get_seconds();
+
        list_for_each_entry_safe(lpni, tmp, &local_queue,
                                 lpni_recovery) {
                /*
@@ -3632,6 +3651,12 @@ lnet_recover_peer_nis(void)
                }
 
                spin_unlock(&lpni->lpni_lock);
+
+               if (now < lpni->lpni_next_ping) {
+                       lnet_net_unlock(0);
+                       continue;
+               }
+
                lnet_net_unlock(0);
 
                /*
@@ -3681,30 +3706,24 @@ lnet_recover_peer_nis(void)
                                continue;
                        }
 
+                       lpni->lpni_ping_count++;
+
                        lpni->lpni_recovery_ping_mdh = mdh;
-                       /*
-                        * While we're unlocked the lpni could've been
-                        * readded on the recovery queue. In this case we
-                        * don't need to add it to the local queue, since
-                        * it's already on there and the thread that added
-                        * it would've incremented the refcount on the
-                        * peer, which means we need to decref the refcount
-                        * that was implicitly grabbed by find_peer_ni_locked.
-                        * Otherwise, if the lpni is still not on
-                        * the recovery queue, then we'll add it to the
-                        * processed list.
-                        */
-                       if (list_empty(&lpni->lpni_recovery))
-                               list_add_tail(&lpni->lpni_recovery, &processed_list);
-                       else
-                               lnet_peer_ni_decref_locked(lpni);
-                       lnet_net_unlock(0);
 
-                       spin_lock(&lpni->lpni_lock);
-                       if (rc)
+                       lnet_peer_ni_add_to_recoveryq_locked(lpni,
+                                                            &processed_list,
+                                                            now);
+                       if (rc) {
+                               spin_lock(&lpni->lpni_lock);
                                lpni->lpni_state &= ~LNET_PEER_NI_RECOVERY_PENDING;
-               }
-               spin_unlock(&lpni->lpni_lock);
+                               spin_unlock(&lpni->lpni_lock);
+                       }
+
+                       /* Drop the ref taken by lnet_find_peer_ni_locked() */
+                       lnet_peer_ni_decref_locked(lpni);
+                       lnet_net_unlock(0);
+               } else
+                       spin_unlock(&lpni->lpni_lock);
        }
 
        list_splice_init(&processed_list, &local_queue);
@@ -4555,61 +4574,6 @@ lnet_parse(struct lnet_ni *ni, struct lnet_hdr *hdr, lnet_nid_t from_nid,
                goto drop;
        }
 
-       if (lnet_drop_asym_route && for_me &&
-           LNET_NIDNET(src_nid) != LNET_NIDNET(from_nid)) {
-               struct lnet_net *net;
-               struct lnet_remotenet *rnet;
-               bool found = true;
-
-               /* we are dealing with a routed message,
-                * so see if route to reach src_nid goes through from_nid
-                */
-               lnet_net_lock(cpt);
-               net = lnet_get_net_locked(LNET_NIDNET(ni->ni_nid));
-               if (!net) {
-                       lnet_net_unlock(cpt);
-                       CERROR("net %s not found\n",
-                              libcfs_net2str(LNET_NIDNET(ni->ni_nid)));
-                       return -EPROTO;
-               }
-
-               rnet = lnet_find_rnet_locked(LNET_NIDNET(src_nid));
-               if (rnet) {
-                       struct lnet_peer *gw = NULL;
-                       struct lnet_peer_ni *lpni = NULL;
-                       struct lnet_route *route;
-
-                       list_for_each_entry(route, &rnet->lrn_routes, lr_list) {
-                               found = false;
-                               gw = route->lr_gateway;
-                               if (route->lr_lnet != net->net_id)
-                                       continue;
-                               /*
-                                * if the nid is one of the gateway's NIDs
-                                * then this is a valid gateway
-                                */
-                               while ((lpni = lnet_get_next_peer_ni_locked(gw,
-                                               NULL, lpni)) != NULL) {
-                                       if (lpni->lpni_nid == from_nid) {
-                                               found = true;
-                                               break;
-                                       }
-                               }
-                       }
-               }
-               lnet_net_unlock(cpt);
-               if (!found) {
-                       /* we would not use from_nid to route a message to
-                        * src_nid
-                        * => asymmetric routing detected but forbidden
-                        */
-                       CERROR("%s, src %s: Dropping asymmetrical route %s\n",
-                              libcfs_nid2str(from_nid),
-                              libcfs_nid2str(src_nid), lnet_msgtyp2str(type));
-                       goto drop;
-               }
-       }
-
        msg = lnet_msg_alloc();
        if (msg == NULL) {
                CERROR("%s, src %s: Dropping %s (out of memory)\n",
@@ -4660,8 +4624,65 @@ lnet_parse(struct lnet_ni *ni, struct lnet_hdr *hdr, lnet_nid_t from_nid,
                goto drop;
        }
 
-       if (the_lnet.ln_routing)
-               lpni->lpni_last_alive = ktime_get_seconds();
+       /* If this message was forwarded to us from a router then we may need
+        * to update router aliveness or check for an asymmetrical route
+        * (or both)
+        */
+       if (((lnet_drop_asym_route && for_me) ||
+            !lpni->lpni_peer_net->lpn_peer->lp_alive) &&
+           LNET_NIDNET(src_nid) != LNET_NIDNET(from_nid)) {
+               __u32 src_net_id = LNET_NIDNET(src_nid);
+               struct lnet_peer *gw = lpni->lpni_peer_net->lpn_peer;
+               struct lnet_route *route;
+               bool found = false;
+
+               list_for_each_entry(route, &gw->lp_routes, lr_gwlist) {
+                       if (route->lr_net == src_net_id) {
+                               found = true;
+                               /* If we're transitioning the gateway from
+                                * dead -> alive, and discovery is disabled
+                                * locally or on the gateway, then we need to
+                                * update the cached route aliveness for each
+                                * route to the src_nid's net.
+                                *
+                                * Otherwise, we're only checking for
+                                * symmetrical route, and we can break the
+                                * loop
+                                */
+                               if (!gw->lp_alive &&
+                                   lnet_is_discovery_disabled(gw))
+                                       lnet_set_route_aliveness(route, true);
+                               else
+                                       break;
+                       }
+               }
+               if (lnet_drop_asym_route && for_me && !found) {
+                       lnet_net_unlock(cpt);
+                       /* we would not use from_nid to route a message to
+                        * src_nid
+                        * => asymmetric routing detected but forbidden
+                        */
+                       CERROR("%s, src %s: Dropping asymmetrical route %s\n",
+                              libcfs_nid2str(from_nid),
+                              libcfs_nid2str(src_nid), lnet_msgtyp2str(type));
+                       lnet_msg_free(msg);
+                       goto drop;
+               }
+               if (!gw->lp_alive) {
+                       struct lnet_peer_net *lpn;
+                       struct lnet_peer_ni *lpni2;
+
+                       gw->lp_alive = true;
+                       /* Mark all remote NIs on src_nid's net UP */
+                       lpn = lnet_peer_get_net_locked(gw, src_net_id);
+                       if (lpn)
+                               list_for_each_entry(lpni2, &lpn->lpn_peer_nis,
+                                                   lpni_peer_nis)
+                                       lpni2->lpni_ns_status = LNET_NI_STATUS_UP;
+               }
+       }
+
+       lpni->lpni_last_alive = ktime_get_seconds();
 
        msg->msg_rxpeer = lpni;
        msg->msg_rxni = ni;