viced/callback.c: Ignore dump write errors even harder
[openafs.git] / src / viced / callback.c
index cfaaa63..9d22156 100644 (file)
@@ -627,6 +627,7 @@ AddCallBack1_r(struct host *host, AFSFid * fid, afs_uint32 * thead, int type,
        cb->cnext = 0;
        cb->fhead = fetoi(fe);
        cb->status = type;
+       cb->flags = 0;
        HAdd(cb, host);
        TAdd(cb, Thead);
     }
@@ -683,7 +684,20 @@ MultiBreakCallBack_r(struct cbstruct cba[], int ncbas,
 
     opr_Assert(ncbas <= MAX_CB_HOSTS);
 
-    /* sort cba list to avoid makecall issues */
+    /*
+     * When we issue a multi_Rx callback break, we must rx_NewCall a call for
+     * each host before we do anything. If there are no call channels
+     * available on the conn, we must wait for one of the existing calls to
+     * finish. If another thread is breaking callbacks at the same time, it is
+     * possible for us to be waiting on NewCall for one of their multi_Rx
+     * CallBack calls to finish, but they are waiting on NewCall for one of
+     * our calls to finish. So we deadlock.
+     *
+     * This can be thought of as similar to obtaining multiple locks at the
+     * same time. So if we establish an ordering, the possibility of deadlock
+     * goes away. Here we provide such an ordering, by sorting our CBAs
+     * according to CompareCBA.
+     */
     qsort(cba, ncbas, sizeof(struct cbstruct), CompareCBA);
 
     /* set up conns for multi-call */
@@ -792,8 +806,8 @@ BreakCallBack(struct host *xhost, AFSFid * fid, int flag)
 {
     struct FileEntry *fe;
     struct CallBack *cb, *nextcb;
-    struct cbstruct cbaDef[MAX_CB_HOSTS], *cba = cbaDef;
-    unsigned int ncbas, cbaAlloc = MAX_CB_HOSTS;
+    struct cbstruct cba[MAX_CB_HOSTS];
+    int ncbas;
     struct AFSCBFids tf;
     int hostindex;
     char hoststr[16];
@@ -823,12 +837,31 @@ BreakCallBack(struct host *xhost, AFSFid * fid, int flag)
     tf.AFSCBFids_len = 1;
     tf.AFSCBFids_val = fid;
 
-       for (ncbas = 0; cb ; cb = nextcb) {
+    /* Set CBFLAG_BREAKING flag on all CBs we're looking at. We do this so we
+     * can loop through all relevant CBs while dropping H_LOCK, and not lose
+     * track of which CBs we want to look at. If we look at all CBs over and
+     * over again, we can loop indefinitely as new CBs are added. */
+    for (; cb; cb = nextcb) {
+       nextcb = itocb(cb->cnext);
+
+       if ((cb->hhead != hostindex || flag)
+           && (cb->status == CB_BULK || cb->status == CB_NORMAL
+               || cb->status == CB_VOLUME)) {
+           cb->flags |= CBFLAG_BREAKING;
+       }
+    }
+
+    cb = itocb(fe->firstcb);
+    opr_Assert(cb);
+
+    /* loop through all CBs, only looking at ones with the CBFLAG_BREAKING
+     * flag set */
+    for (; cb;) {
+       for (ncbas = 0; cb && ncbas < MAX_CB_HOSTS; cb = nextcb) {
            nextcb = itocb(cb->cnext);
-           if ((cb->hhead != hostindex || flag)
-               && (cb->status == CB_BULK || cb->status == CB_NORMAL
-                   || cb->status == CB_VOLUME)) {
+           if ((cb->flags & CBFLAG_BREAKING)) {
                struct host *thishost = h_itoh(cb->hhead);
+               cb->flags &= ~CBFLAG_BREAKING;
                if (!thishost) {
                    ViceLog(0, ("BCB: BOGUS! cb->hhead is NULL!\n"));
                } else if (thishost->hostFlags & VENUSDOWN) {
@@ -840,21 +873,6 @@ BreakCallBack(struct host *xhost, AFSFid * fid, int flag)
                } else {
                    if (!(thishost->hostFlags & HOSTDELETED)) {
                        h_Hold_r(thishost);
-                       if (ncbas == cbaAlloc) {        /* Need more space */
-                           int curLen = cbaAlloc*sizeof(cba[0]);
-                           struct cbstruct *cbaOld = (cba == cbaDef) ? NULL : cba;
-
-                           /* There are logical contraints elsewhere that the number of hosts
-                              (i.e. h_HTSPERBLOCK*h_MAXHOSTTABLES) remains in the realm of a signed "int".
-                              cbaAlloc is defined unsigned int hence doubling below cannot overflow
-                           */
-                           cbaAlloc = cbaAlloc<<1;     /* double */
-                           cba = realloc(cbaOld, cbaAlloc * sizeof(cba[0]));
-
-                           if (cbaOld == NULL) {       /* realloc wouldn't have copied from cbaDef */
-                               memcpy(cba, cbaDef, curLen);
-                           }
-                       }
                        cba[ncbas].hp = thishost;
                        cba[ncbas].thead = cb->thead;
                        ncbas++;
@@ -868,16 +886,20 @@ BreakCallBack(struct host *xhost, AFSFid * fid, int flag)
        }
 
        if (ncbas) {
-           struct cbstruct *cba2;
-           int num;
+           MultiBreakCallBack_r(cba, ncbas, &tf);
 
-           for (cba2 = cba, num = ncbas; ncbas > 0; cba2 += num, ncbas -= num) {
-               num = (ncbas > MAX_CB_HOSTS) ? MAX_CB_HOSTS : ncbas;
-               MultiBreakCallBack_r(cba2, num, &tf);
+           /* we need to to all these initializations again because MultiBreakCallBack may block */
+           fe = FindFE(fid);
+           if (!fe) {
+               goto done;
+           }
+           cb = itocb(fe->firstcb);
+           if (!cb || ((fe->ncbs == 1) && (cb->hhead == hostindex) && !flag)) {
+               /* the most common case is what follows the || */
+               goto done;
            }
        }
-
-       if (cba != cbaDef) free(cba);
+    }
 
   done:
     H_UNLOCK;
@@ -2646,7 +2668,7 @@ cb_OldToNew(struct fs_dump_state * state, afs_uint32 old, afs_uint32 * new)
 }
 #endif /* AFS_DEMAND_ATTACH_FS */
 
-#define DumpBytes(fd,buf,req) if (write(fd, buf, req) < 0) ; /* don't care */
+#define DumpBytes(fd,buf,req) if (write(fd, buf, req) < 0) {} /* don't care */
 
 static int
 DumpCallBackState_r(void)