Allow GetSomeSpace_r to select an optimal host
[openafs.git] / src / viced / callback.c
index 0403221..b5006a1 100644 (file)
 #include <afs/nfs.h>           /* yuck.  This is an abomination. */
 #include <lwp.h>
 #include <rx/rx.h>
-#include <afscbint.h>
+#include <afs/afscbint.h>
 #include <afs/afsutil.h>
 #include <lock.h>
 #include <afs/ihandle.h>
@@ -518,12 +518,13 @@ int
 AddCallBack1(struct host *host, AFSFid * fid, afs_uint32 * thead, int type,
             int locked)
 {
-    int retVal;
+    int retVal = 0;
     H_LOCK;
     if (!locked) {
        h_Lock_r(host);
     }
-    retVal = AddCallBack1_r(host, fid, thead, type, 1);
+    if (!(host->hostFlags & HOSTDELETED))
+        retVal = AddCallBack1_r(host, fid, thead, type, 1);
 
     if (!locked) {
        h_Unlock_r(host);
@@ -564,6 +565,11 @@ AddCallBack1_r(struct host *host, AFSFid * fid, afs_uint32 * thead, int type,
     if (!locked) {
        h_Lock_r(host);         /* this can yield, so do it before we get any */
        /* fragile info */
+        if (host->hostFlags & HOSTDELETED) {
+            host->Console &= ~2;
+            h_Unlock_r(host);
+            return 0;
+        }
     }
 
     fe = FindFE(fid);
@@ -725,7 +731,7 @@ MultiBreakCallBack_r(struct cbstruct cba[], int ncbas,
 
                if (!hp || !idx) {
                    ViceLog(0,
-                           ("BCB: INTERNAL ERROR: hp=%x, cba=%x, thead=%u\n", 
+                           ("BCB: INTERNAL ERROR: hp=%p, cba=%p, thead=%u\n",
                             hp, cba, idx));
                } else {
                    /* 
@@ -734,7 +740,8 @@ MultiBreakCallBack_r(struct cbstruct cba[], int ncbas,
                    if (MultiBreakCallBackAlternateAddress(hp, afidp)) {
                        if (ShowProblems) {
                            ViceLog(7,
-                                   ("BCB: Failed on file %u.%u.%u, Host %x (%s:%d) is down\n",
+                                   ("BCB: Failed on file %u.%u.%u, "
+                                    "Host %p (%s:%d) is down\n",
                                     afidp->AFSCBFids_val->Volume,
                                     afidp->AFSCBFids_val->Vnode,
                                     afidp->AFSCBFids_val->Unique,
@@ -745,12 +752,14 @@ MultiBreakCallBack_r(struct cbstruct cba[], int ncbas,
 
                        H_LOCK;
                        h_Lock_r(hp); 
-                       hp->hostFlags |= VENUSDOWN;
-               /**
-                 * We always go into AddCallBack1_r with the host locked
-                 */
-                       AddCallBack1_r(hp, afidp->AFSCBFids_val, itot(idx),
-                                      CB_DELAYED, 1);
+                        if (!(hp->hostFlags & HOSTDELETED)) {
+                            hp->hostFlags |= VENUSDOWN;
+                            /**
+                             * We always go into AddCallBack1_r with the host locked
+                             */
+                            AddCallBack1_r(hp, afidp->AFSCBFids_val, itot(idx),
+                                           CB_DELAYED, 1);
+                        }
                        h_Unlock_r(hp); 
                        H_UNLOCK;
                    }
@@ -805,7 +814,7 @@ BreakCallBack(struct host *xhost, AFSFid * fid, int flag)
     char hoststr[16];
 
     ViceLog(7,
-           ("BCB: BreakCallBack(Host %x all but %s:%d, (%u,%u,%u))\n",
+           ("BCB: BreakCallBack(Host %p all but %s:%d, (%u,%u,%u))\n",
             xhost, afs_inet_ntoa_r(xhost->host, hoststr), ntohs(xhost->port),
             fid->Volume, fid->Vnode, fid->Unique));
 
@@ -835,15 +844,17 @@ BreakCallBack(struct host *xhost, AFSFid * fid, int flag)
                    ViceLog(0, ("BCB: BOGUS! cb->hhead is NULL!\n"));
                } else if (thishost->hostFlags & VENUSDOWN) {
                    ViceLog(7,
-                           ("BCB: %x (%s:%d) is down; delaying break call back\n",
+                           ("BCB: %p (%s:%d) is down; delaying break call back\n",
                             thishost, afs_inet_ntoa_r(thishost->host, hoststr),
                             ntohs(thishost->port)));
                    cb->status = CB_DELAYED;
                } else {
-                   h_Hold_r(thishost);
-                   cba[ncbas].hp = thishost;
-                   cba[ncbas].thead = cb->thead;
-                   ncbas++;
+                   if (!(thishost->hostFlags & HOSTDELETED)) {
+                       h_Hold_r(thishost);
+                       cba[ncbas].hp = thishost;
+                       cba[ncbas].thead = cb->thead;
+                       ncbas++;
+                   }
                    TDel(cb);
                    HDel(cb);
                    CDel(cb, 1);        /* Usually first; so this delete 
@@ -885,6 +896,7 @@ DeleteCallBack(struct host *host, AFSFid * fid)
     cbstuff.DeleteCallBacks++;
 
     h_Lock_r(host);
+    /* do not care if the host has been HOSTDELETED */
     fe = FindFE(fid);
     if (!fe) {
        h_Unlock_r(host);
@@ -897,7 +909,7 @@ DeleteCallBack(struct host *host, AFSFid * fid)
     pcb = FindCBPtr(fe, host);
     if (!*pcb) {
        ViceLog(8,
-               ("DCB: No call back for host %x (%s:%d), (%u, %u, %u)\n",
+               ("DCB: No call back for host %p (%s:%d), (%u, %u, %u)\n",
                 host, afs_inet_ntoa_r(host->host, hoststr), ntohs(host->port),
                 fid->Volume, fid->Vnode, fid->Unique));
        h_Unlock_r(host);
@@ -1019,15 +1031,17 @@ BreakDelayedCallBacks_r(struct host *host)
        if (code) {
            if (ShowProblems) {
                ViceLog(0,
-                       ("CB: Call back connect back failed (in break delayed) for Host %x (%s:%d)\n",
+                       ("CB: Call back connect back failed (in break delayed) "
+                        "for Host %p (%s:%d)\n",
                         host, afs_inet_ntoa_r(host->host, hoststr),
                         ntohs(host->port)));
            }
            host->hostFlags |= VENUSDOWN;
        } else {
            ViceLog(25,
-                   ("InitCallBackState success on %x (%s:%d)\n",
-                    host, afs_inet_ntoa_r(host->host, hoststr), ntohs(host->port)));
+                   ("InitCallBackState success on %p (%s:%d)\n",
+                    host, afs_inet_ntoa_r(host->host, hoststr),
+                    ntohs(host->port)));
            /* reset was done successfully */
            host->hostFlags |= RESETDONE;
            host->hostFlags &= ~VENUSDOWN;
@@ -1066,14 +1080,16 @@ BreakDelayedCallBacks_r(struct host *host)
                int i;
                if (ShowProblems) {
                    ViceLog(0,
-                           ("CB: XCallBackBulk failed, Host %x (%s:%d); callback list follows:\n",
+                           ("CB: XCallBackBulk failed, Host %p (%s:%d); "
+                            "callback list follows:\n",
                              host, afs_inet_ntoa_r(host->host, hoststr),
                             ntohs(host->port)));
                }
                for (i = 0; i < nfids; i++) {
                    if (ShowProblems) {
                        ViceLog(0,
-                               ("CB: Host %x (%s:%d), file %u.%u.%u (part of bulk callback)\n",
+                               ("CB: Host %p (%s:%d), file %u.%u.%u "
+                                "(part of bulk callback)\n",
                                 host, afs_inet_ntoa_r(host->host, hoststr),
                                 ntohs(host->port), fids[i].Volume,
                                 fids[i].Vnode, fids[i].Unique));
@@ -1116,16 +1132,10 @@ MultiBreakVolumeCallBack_r(struct host *host, int isheld,
 
     if (host->hostFlags & VENUSDOWN) {
        h_Lock_r(host);
-       if (host->hostFlags & HOSTDELETED) {
-           h_Unlock_r(host);
-           return 0;           /* Release hold */
-       }
-       ViceLog(8,
-               ("BVCB: volume call back for Host %x (%s:%d) failed\n",
-                 host, afs_inet_ntoa_r(host->host, hoststr), ntohs(host->port)));
+        /* Do not care if the host is now HOSTDELETED */
        if (ShowProblems) {
            ViceLog(0,
-                   ("CB: volume callback for Host %x (%s:%d) failed\n",
+                   ("BVCB: volume callback for Host %p (%s:%d) failed\n",
                     host, afs_inet_ntoa_r(host->host, hoststr),
                     ntohs(host->port)));
        }
@@ -1224,11 +1234,14 @@ BreakVolumeCallBacks(afs_uint32 volume)
                register struct CallBack *cbnext;
                for (cb = itocb(fe->firstcb); cb; cb = cbnext) {
                    host = h_itoh(cb->hhead);
-                   h_Hold_r(host);
-                   cbnext = itocb(cb->cnext);
-                   if (!tthead || (TNorm(tthead) < TNorm(cb->thead))) {
-                       tthead = cb->thead;
+
+                   if (!(host->hostFlags & HOSTDELETED)) {
+                       h_Hold_r(host);
+                       if (!tthead || (TNorm(tthead) < TNorm(cb->thead))) {
+                           tthead = cb->thead;
+                       }
                    }
+                   cbnext = itocb(cb->cnext);
                    TDel(cb);
                    HDel(cb);
                    FreeCB(cb);
@@ -1375,9 +1388,11 @@ BreakLaterCallBacks(void)
            cbnext = itocb(cb->cnext);
            host = h_itoh(cb->hhead);
            if (cb->status == CB_DELAYED) {
-               h_Hold_r(host);
-               if (!tthead || (TNorm(tthead) < TNorm(cb->thead))) {
-                   tthead = cb->thead;
+               if (!(host->hostFlags & HOSTDELETED)) {
+                   h_Hold_r(host);
+                   if (!tthead || (TNorm(tthead) < TNorm(cb->thead))) {
+                       tthead = cb->thead;
+                   }
                }
                TDel(cb);
                HDel(cb);
@@ -1385,7 +1400,7 @@ BreakLaterCallBacks(void)
                /* leave hold for MultiBreakVolumeCallBack to clear */
            } else {
                ViceLog(125,
-                       ("Found host %x (%s:%d) non-DELAYED cb for %u:%u:%u\n", 
+                       ("Found host %p (%s:%d) non-DELAYED cb for %u:%u:%u\n",
                         host, afs_inet_ntoa_r(host->host, hoststr),
                         ntohs(host->port), fe->vnode, fe->unique, fe->volid));
            }
@@ -1471,57 +1486,86 @@ CleanupTimedOutCallBacks_r(void)
     return (ntimedout > 0);
 }
 
-static struct host *lih_host;
-static int lih_host_held;
+/**
+ * parameters to pass to lih*_r from h_Enumerate_r when trying to find a host
+ * from which to clear callbacks.
+ */
+struct lih_params {
+    /**
+     * Points to the least interesting host found; try to clear callbacks on
+     * this host after h_Enumerate_r(lih*_r)'ing.
+     */
+    struct host *lih;
+
+    /**
+     * The last host we got from lih*_r, but we couldn't clear its callbacks
+     * for some reason. Choose the next-best host after this one (with the
+     * current lih*_r, this means to only select hosts that have an ActiveCall
+     * newer than lastlih).
+     */
+    struct host *lastlih;
+};
 
 /* Value of host->refCount that allows us to reliably infer that
  * host may be held by some other thread */
 #define OTHER_MUSTHOLD_LIH 2
 
 /* This version does not allow 'host' to be selected unless its ActiveCall 
- * is newer than 'hostp' which is the host with the oldest ActiveCall from
- * the last pass (if it is provided).  We filter out any hosts that are
- * are held by other threads.
+ * is newer than 'params->lastlih' which is the host with the oldest
+ * ActiveCall from the last pass (if it is provided).  We filter out any hosts
+ * that are are held by other threads.
+ *
+ * There is a small problem here, but it may not be easily fixable. Say we
+ * select some host A, and give it back to GetSomeSpace_r. GSS_r for some
+ * reason cannot clear the callbacks on A, and so calls us again with
+ * lastlih = A. Suppose there is another host B that has the same ActiveCall
+ * time as A. We will now skip over host B, since
+ * 'hostB->ActiveCall > hostA->ActiveCall' is not true. This could result in
+ * us prematurely going to the GSS_r 2nd or 3rd pass, and making us a little
+ * inefficient. This should be pretty rare, though, except perhaps in cases
+ * with very small numbers of hosts.
+ *
+ * Also filter out any hosts with HOSTDELETED set. h_Enumerate_r should in
+ * theory not give these to us anyway, but be paranoid.
  */
 static int
 lih0_r(register struct host *host, register int flags, void *rock)
 {
-    struct host *hostp = (struct host *) rock;
+    struct lih_params *params = (struct lih_params *)rock;
+
     if (host->cblist
-       && (hostp && host != hostp) 
+       && (!(host->hostFlags & HOSTDELETED))
        && (host->refCount < OTHER_MUSTHOLD_LIH)
-       && (!lih_host || host->ActiveCall < lih_host->ActiveCall) 
-       && (!hostp || host->ActiveCall > hostp->ActiveCall)) {
-        if (lih_host != NULL && lih_host_held) {
-            h_Release_r(lih_host); /* release prev host */
-        }
-        lih_host = host;
-        lih_host_held = !flags; /* on i==1, this === (lih_host_held = 1) */
-        flags = 1; /* now flags is 1, but at next(i), it will be 0 again */
+       && (!params->lih || host->ActiveCall < params->lih->ActiveCall)
+       && (!params->lastlih || host->ActiveCall > params->lastlih->ActiveCall)) {
+
+       if (params->lih) {
+           h_Release_r(params->lih); /* release prev host */
+       }
+
+       h_Hold_r(host);
+       params->lih = host;
     }
     return flags;
 }
 
-/* This version does not allow 'host' to be selected unless its ActiveCall 
- * is newer than 'hostp' which is the host with the oldest ActiveCall from
- * the last pass (if it is provided).  In this second varient, we do not 
- * prevent held hosts from being selected.
- */
+/* same as lih0_r, except we do not prevent held hosts from being selected. */
 static int
 lih1_r(register struct host *host, register int flags, void *rock)
 {
-    struct host *hostp = (struct host *) rock;
+    struct lih_params *params = (struct lih_params *)rock;
 
     if (host->cblist
-       && (hostp && host != hostp) 
-       && (!lih_host || host->ActiveCall < lih_host->ActiveCall) 
-       && (!hostp || host->ActiveCall > hostp->ActiveCall)) {
-           if (lih_host != NULL && lih_host_held) {
-                   h_Release_r(lih_host); /* really? */
-           }
-           lih_host = host;
-           lih_host_held = !flags; /* see note above */
-           flags = 1; /* see note above */
+       && (!(host->hostFlags & HOSTDELETED))
+       && (!params->lih || host->ActiveCall < params->lih->ActiveCall)
+       && (!params->lastlih || host->ActiveCall > params->lastlih->ActiveCall)) {
+
+       if (params->lih) {
+           h_Release_r(params->lih); /* release prev host */
+       }
+
+       h_Hold_r(host);
+       params->lih = host;
     }
     return flags;
 }
@@ -1541,7 +1585,8 @@ extern struct host *hostList;
 static int
 GetSomeSpace_r(struct host *hostp, int locked)
 {
-    register struct host *hp, *hp1, *hp2;
+    struct host *hp;
+    struct lih_params params;
     int i = 0;
 
     cbstuff.GotSomeSpaces++;
@@ -1553,36 +1598,39 @@ GetSomeSpace_r(struct host *hostp, int locked)
     }
 
     i = 0;
-    hp1 = NULL;
-    hp2 = hostList;
+    params.lastlih = NULL;
+
     do {
-       lih_host = 0;
-       h_Enumerate_r(i == 0 ? lih0_r : lih1_r, hp2, (char *)hp1);
-       hp = lih_host;
+       params.lih = NULL;
+
+       h_Enumerate_r(i == 0 ? lih0_r : lih1_r, hostList, &params);
+
+       hp = params.lih;
+       if (params.lastlih) {
+           h_Release_r(params.lastlih);
+           params.lastlih = NULL;
+       }
+
        if (hp) {
-           /* set in lih_r! private copy before giving up H_LOCK */
-           int lih_host_held2=lih_host_held;   
+           /* note that 'hp' was held by lih*_r; we will need to release it */
            cbstuff.GSS4++;
            if ((hp != hostp) && !ClearHostCallbacks_r(hp, 0 /* not locked or held */ )) {
-                if (lih_host_held2)
-                    h_Release_r(hp);
+                h_Release_r(hp);
                return 0;
            }
-           if (lih_host_held2)
-                h_Release_r(hp);
-           hp1 = hp;
-           hp2 = hostList;
+
+           params.lastlih = hp;
+           /* params.lastlih will be released on the next iteration, after
+            * h_Enumerate_r */
+
        } else {
            /*
             * Next time try getting callbacks from any host even if
-            * it's deleted (that's actually great since we can freely
-            * remove its callbacks) or it's held since the only other
-            * option is starvation for the file server (i.e. until the
-            * callback timeout arrives).
+            * it's held, since the only other option is starvation for
+            * the file server (i.e. until the callback timeout arrives).
             */
            i++;
-           hp1 = NULL;
-           hp2 = hostList;
+           params.lastlih = NULL;
            cbstuff.GSS1++;
            ViceLog(5,
                    ("GSS: Try harder for longest inactive host cnt= %d\n",
@@ -1611,9 +1659,16 @@ ClearHostCallbacks_r(struct host *hp, int locked)
     struct rx_connection *cb_conn = NULL;
 
     ViceLog(5,
-           ("GSS: Delete longest inactive host %x (%s:%d)\n",
+           ("GSS: Delete longest inactive host %p (%s:%d)\n",
              hp, afs_inet_ntoa_r(hp->host, hoststr), ntohs(hp->port)));
 
+    if ((hp->hostFlags & HOSTDELETED)) {
+       /* hp could go away after reacquiring H_LOCK in h_NBLock_r, so we can't
+        * really use it; its callbacks will get cleared anyway when
+        * h_TossStuff_r gets its hands on it */
+       return 1;
+    }
+
     h_Hold_r(hp);
 
     /** Try a non-blocking lock. If the lock is already held return
@@ -1640,7 +1695,7 @@ ClearHostCallbacks_r(struct host *hp, int locked)
     DeleteAllCallBacks_r(hp, 1);
     if (hp->hostFlags & VENUSDOWN) {
        hp->hostFlags &= ~RESETDONE;    /* remember that we must do a reset */
-    } else {
+    } else if (!(hp->hostFlags & HOSTDELETED)) {
        /* host is up, try a call */
        hp->hostFlags &= ~ALTADDR;      /* alternate addresses are invalid */
        cb_conn = hp->callback_rxcon;
@@ -1972,12 +2027,15 @@ cb_stateVerifyFE(struct fs_dump_state * state, struct FileEntry * fe)
 
     if ((fe->firstcb && !fe->ncbs) ||
        (!fe->firstcb && fe->ncbs)) {
-       ViceLog(0, ("cb_stateVerifyFE: error: fe->firstcb does not agree with fe->ncbs (fei=%d, fe->firstcb=%d, fe->ncbs=%d)\n",
-                   fetoi(fe), fe->firstcb, fe->ncbs));
+       ViceLog(0, ("cb_stateVerifyFE: error: fe->firstcb does not agree with fe->ncbs (fei=%lu, fe->firstcb=%lu, fe->ncbs=%lu)\n",
+                   afs_printable_uint32_lu(fetoi(fe)),
+                   afs_printable_uint32_lu(fe->firstcb),
+                   afs_printable_uint32_lu(fe->ncbs)));
        ret = 1;
     }
     if (cb_stateVerifyFCBList(state, fe)) {
-       ViceLog(0, ("cb_stateVerifyFE: error: FCBList failed verification (fei=%d)\n", fetoi(fe)));
+       ViceLog(0, ("cb_stateVerifyFE: error: FCBList failed verification (fei=%lu)\n",
+                   afs_printable_uint32_lu(fetoi(fe))));
        ret = 1;
     }
 
@@ -2981,7 +3039,7 @@ MultiBreakCallBackAlternateAddress_r(struct host *host,
 
     assert(j);                 /* at least one alternate address */
     ViceLog(125,
-           ("Starting multibreakcall back on all addr for host %x (%s:%d)\n",
+           ("Starting multibreakcall back on all addr for host %p (%s:%d)\n",
              host, afs_inet_ntoa_r(host->host, hoststr), ntohs(host->port)));
     H_UNLOCK;
     multi_Rx(conns, j) {
@@ -3076,7 +3134,7 @@ MultiProbeAlternateAddress_r(struct host *host)
 
     assert(j);                 /* at least one alternate address */
     ViceLog(125,
-           ("Starting multiprobe on all addr for host %x (%s:%d)\n",
+           ("Starting multiprobe on all addr for host %p (%s:%d)\n",
              host, afs_inet_ntoa_r(host->host, hoststr),
              ntohs(host->port)));
     H_UNLOCK;