Whamcloud - gitweb
LU-14668 lnet: Lock primary NID logic 06/50106/4
authorAmir Shehata <ashehata@whamcloud.com>
Wed, 5 May 2021 18:35:06 +0000 (11:35 -0700)
committerOleg Drokin <green@whamcloud.com>
Wed, 8 Mar 2023 03:26:55 +0000 (03:26 +0000)
If a peer is created by Lustre make sure to lock that peer's
primary NID. This peer can be discovered in the background.
There is no need to block until discovery is complete, as Lustre
can continue on with the primary NID it provided.

Discovery will populate the peer with other interfaces the peer has
but will not change the peer's primary NID. It can also delete
peer's NIDs which Lustre told it about (not the Primary NID).

If a peer has been manually discovered via
   lnetctl discover <nid>
command, then make sure to delete the manually discovered
peer and recreate it with the Lustre NID information
provided for us.

Signed-off-by: Amir Shehata <ashehata@whamcloud.com>
Change-Id: I8fc8a69caccca047e3085bb33d026a3f09fb359b
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/50106
Tested-by: Maloo <maloo@whamcloud.com>
Tested-by: jenkins <devops@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
Reviewed-by: Cyril Bordage <cbordage@whamcloud.com>
Reviewed-by: Frank Sehr <fsehr@whamcloud.com>
lnet/lnet/peer.c

index 37ffb23..e2ae275 100644 (file)
@@ -544,6 +544,15 @@ lnet_peer_del_nid(struct lnet_peer *lp, struct lnet_nid *nid,
                }
        }
 
+       /* If we're asked to lock down the primary NID we shouldn't be
+        * deleting it
+        */
+       if (lp->lp_state & LNET_PEER_LOCK_PRIMARY &&
+           nid_same(&primary_nid, nid)) {
+               rc = -EPERM;
+               goto out;
+       }
+
        lpni = lnet_peer_ni_find_locked(nid);
        if (!lpni) {
                rc = -ENOENT;
@@ -1367,6 +1376,19 @@ LNetAddPeer(lnet_nid_t *nids, __u32 num_nids)
                if (LNET_NID_IS_ANY(&pnid)) {
                        lnet_nid4_to_nid(nids[i], &pnid);
                        rc = lnet_add_peer_ni(&pnid, &LNET_ANY_NID, mr, true);
+                       if (rc == -EALREADY) {
+                               struct lnet_peer *lp;
+
+                               CDEBUG(D_NET, "A peer exists for NID %s\n",
+                                      libcfs_nidstr(&pnid));
+                               rc = 0;
+                               /* Adds a refcount */
+                               lp = lnet_find_peer(&pnid);
+                               LASSERT(lp);
+                               pnid = lp->lp_primary_nid;
+                               /* Drop refcount from lookup */
+                               lnet_peer_decref_locked(lp);
+                       }
                } else if (lnet_peer_discovery_disabled) {
                        lnet_nid4_to_nid(nids[i], &nid);
                        rc = lnet_add_peer_ni(&nid, &LNET_ANY_NID, mr, true);
@@ -1414,13 +1436,20 @@ void LNetPrimaryNID(struct lnet_nid *nid)
         * down then this discovery can introduce long delays into the mount
         * process, so skip it if it isn't necessary.
         */
-       while (!lnet_peer_discovery_disabled && !lnet_peer_is_uptodate(lp)) {
-               spin_lock(&lp->lp_lock);
+       spin_lock(&lp->lp_lock);
+       if (!lnet_peer_discovery_disabled &&
+           (!(lp->lp_state & LNET_PEER_LOCK_PRIMARY) ||
+            !lnet_peer_is_uptodate_locked(lp))) {
                /* force a full discovery cycle */
-               lp->lp_state |= LNET_PEER_FORCE_PING | LNET_PEER_FORCE_PUSH;
+               lp->lp_state |= LNET_PEER_FORCE_PING | LNET_PEER_FORCE_PUSH |
+                               LNET_PEER_LOCK_PRIMARY;
                spin_unlock(&lp->lp_lock);
 
-               rc = lnet_discover_peer_locked(lpni, cpt, true);
+               /* start discovery in the background. Messages to that
+                * peer will not go through until the discovery is
+                * complete
+                */
+               rc = lnet_discover_peer_locked(lpni, cpt, false);
                if (rc)
                        goto out_decref;
                /* The lpni (or lp) for this NID may have changed and our ref is
@@ -1434,14 +1463,8 @@ void LNetPrimaryNID(struct lnet_nid *nid)
                        goto out_unlock;
                }
                lp = lpni->lpni_peer_net->lpn_peer;
-
-               /* If we find that the peer has discovery disabled then we will
-                * not modify whatever primary NID is currently set for this
-                * peer. Thus, we can break out of this loop even if the peer
-                * is not fully up to date.
-                */
-               if (lnet_is_discovery_disabled(lp))
-                       break;
+       } else {
+               spin_unlock(&lp->lp_lock);
        }
        *nid = lp->lp_primary_nid;
 out_decref:
@@ -1477,9 +1500,9 @@ lnet_peer_get_net_locked(struct lnet_peer *peer, __u32 net_id)
  */
 static int
 lnet_peer_attach_peer_ni(struct lnet_peer *lp,
-                               struct lnet_peer_net *lpn,
-                               struct lnet_peer_ni *lpni,
-                               unsigned flags)
+                        struct lnet_peer_net *lpn,
+                        struct lnet_peer_ni *lpni,
+                        unsigned flags)
 {
        struct lnet_peer_table *ptable;
        bool new_lpn = false;
@@ -1546,6 +1569,8 @@ lnet_peer_attach_peer_ni(struct lnet_peer *lp,
                        lnet_peer_clr_non_mr_pref_nids(lp);
                }
        }
+       if (flags & LNET_PEER_LOCK_PRIMARY)
+               lp->lp_state |= LNET_PEER_LOCK_PRIMARY;
        spin_unlock(&lp->lp_lock);
 
        lp->lp_nnis++;
@@ -1607,13 +1632,28 @@ lnet_peer_add(struct lnet_nid *nid, unsigned int flags)
                        else if ((lp->lp_state ^ flags) & LNET_PEER_MULTI_RAIL)
                                rc = -EPERM;
                        goto out;
-               } else if (!(flags & LNET_PEER_CONFIGURED)) {
+               } else if (lp->lp_state & LNET_PEER_LOCK_PRIMARY) {
                        if (nid_same(&lp->lp_primary_nid, nid)) {
                                rc = -EEXIST;
                                goto out;
                        }
+                       /* we're trying to recreate an existing peer which
+                        * has already been created and its primary
+                        * locked. This is likely due to two servers
+                        * existing on the same node. So we'll just refer
+                        * to that node with the primary NID which was
+                        * first added by Lustre
+                        */
+                       rc = -EALREADY;
+                       goto out;
                }
-               /* Delete and recreate as a configured peer. */
+               /* Delete and recreate the peer.
+                * We can get here:
+                * 1. If the peer is being recreated as a configured NID
+                * 2. if there already exists a peer which
+                *    was discovered manually, but is recreated via Lustre
+                *    with PRIMARY_lock
+                */
                rc = lnet_peer_del(lp);
                if (rc)
                        goto out;
@@ -1703,9 +1743,27 @@ lnet_peer_add_nid(struct lnet_peer *lp, struct lnet_nid *nid,
                }
                /* If this is the primary NID, destroy the peer. */
                if (lnet_peer_ni_is_primary(lpni)) {
-                       struct lnet_peer *rtr_lp =
+                       struct lnet_peer *lp2 =
                                lpni->lpni_peer_net->lpn_peer;
-                       int rtr_refcount = rtr_lp->lp_rtr_refcount;
+                       int rtr_refcount = lp2->lp_rtr_refcount;
+
+                       /* If the new peer that this NID belongs to is
+                        * a primary NID for another peer which we're
+                        * suppose to preserve the Primary for then we
+                        * don't want to mess with it. But the
+                        * configuration is wrong at this point, so we
+                        * should flag both of these peers as in a bad
+                        * state
+                        */
+                       if (lp2->lp_state & LNET_PEER_LOCK_PRIMARY) {
+                               spin_lock(&lp->lp_lock);
+                               lp->lp_state |= LNET_PEER_BAD_CONFIG;
+                               spin_unlock(&lp->lp_lock);
+                               spin_lock(&lp2->lp_lock);
+                               lp2->lp_state |= LNET_PEER_BAD_CONFIG;
+                               spin_unlock(&lp2->lp_lock);
+                               goto out_free_lpni;
+                       }
                        /*
                         * if we're trying to delete a router it means
                         * we're moving this peer NI to a new peer so must
@@ -1713,9 +1771,9 @@ lnet_peer_add_nid(struct lnet_peer *lp, struct lnet_nid *nid,
                         */
                        if (rtr_refcount > 0) {
                                flags |= LNET_PEER_RTR_NI_FORCE_DEL;
-                               lnet_rtr_transfer_to_peer(rtr_lp, lp);
+                               lnet_rtr_transfer_to_peer(lp2, lp);
                        }
-                       lnet_peer_del(lpni->lpni_peer_net->lpn_peer);
+                       lnet_peer_del(lp2);
                        lnet_peer_ni_decref_locked(lpni);
                        lpni = lnet_peer_ni_alloc(nid);
                        if (!lpni) {
@@ -1773,7 +1831,8 @@ lnet_peer_set_primary_nid(struct lnet_peer *lp, struct lnet_nid *nid,
        if (nid_same(&lp->lp_primary_nid, nid))
                goto out;
 
-       lp->lp_primary_nid = *nid;
+       if (!(lp->lp_state & LNET_PEER_LOCK_PRIMARY))
+               lp->lp_primary_nid = *nid;
 
        rc = lnet_peer_add_nid(lp, nid, flags);
        if (rc) {
@@ -1781,6 +1840,14 @@ lnet_peer_set_primary_nid(struct lnet_peer *lp, struct lnet_nid *nid,
                goto out;
        }
 out:
+       /* if this is a configured peer or the primary for that peer has
+        * been locked, then we don't want to flag this scenario as
+        * a failure
+        */
+       if (lp->lp_state & LNET_PEER_CONFIGURED ||
+           lp->lp_state & LNET_PEER_LOCK_PRIMARY)
+               return 0;
+
        CDEBUG(D_NET, "peer %s NID %s: %d\n",
               libcfs_nidstr(&old), libcfs_nidstr(nid), rc);