libafs: do not allow NULL creds for afs_CreateReq
[openafs.git] / src / afs / afs_vcache.c
index 6870463..4b6ba7a 100644 (file)
@@ -172,7 +172,7 @@ afs_FlushVCache(struct vcache *avc, int *slept)
     for (wvc = *uvc; wvc; uvc = &wvc->hnext, wvc = *uvc) {
        if (avc == wvc) {
            *uvc = avc->hnext;
-           avc->hnext = (struct vcache *)NULL;
+           avc->hnext = NULL;
            break;
        }
     }
@@ -205,8 +205,6 @@ afs_FlushVCache(struct vcache *avc, int *slept)
     vn_reinit(AFSTOV(avc));
 #endif
     afs_FreeAllAxs(&(avc->Access));
-    if (!afs_shuttingdown)
-       afs_QueueVCB(avc, slept);
     ObtainWriteLock(&afs_xcbhash, 460);
     afs_DequeueCallback(avc);  /* remove it from queued callbacks list */
     avc->f.states &= ~(CStatd | CUnique);
@@ -216,6 +214,9 @@ afs_FlushVCache(struct vcache *avc, int *slept)
     else
        osi_dnlc_purgevp(avc);
 
+    if (!afs_shuttingdown)
+       afs_QueueVCB(avc, slept);
+
     /*
      * Next, keep track of which vnodes we've deleted for create's
      * optimistic synchronization algorithm
@@ -350,13 +351,13 @@ afs_FreeCBR(struct afs_cbr *asp)
 }
 
 static void
-FlushAllVCBs(struct rx_connection **rxconns, int nconns, int nservers,
-            struct afs_conn **conns, struct srvAddr **addrs)
+FlushAllVCBs(int nconns, struct rx_connection **rxconns,
+            struct afs_conn **conns)
 {
     afs_int32 *results;
     afs_int32 i;
 
-    results = afs_osi_Alloc(nservers * sizeof (afs_int32));
+    results = afs_osi_Alloc(nconns * sizeof (afs_int32));
     osi_Assert(results != NULL);
 
     AFS_GUNLOCK();
@@ -375,11 +376,11 @@ FlushAllVCBs(struct rx_connection **rxconns, int nconns, int nservers,
     for ( i = 0 ; i < nconns ; i++ ) {
        if (results[i] == 0) {
            /* Unchain all of them */
-           while (addrs[i]->server->cbrs)
-               afs_FreeCBR(addrs[i]->server->cbrs);
+           while (conns[i]->parent->srvr->server->cbrs)
+               afs_FreeCBR(conns[i]->parent->srvr->server->cbrs);
        }
     }
-    afs_osi_Free(results, nservers * sizeof(afs_int32));
+    afs_osi_Free(results, nconns * sizeof(afs_int32));
 }
 
 /*!
@@ -402,7 +403,7 @@ afs_FlushVCBs(afs_int32 lockit)
     int tcount;
     struct server *tsp;
     int i;
-    struct vrequest treq;
+    struct vrequest *treq = NULL;
     struct afs_conn *tc;
     int safety1, safety2, safety3;
     XSTATS_DECLS;
@@ -410,9 +411,9 @@ afs_FlushVCBs(afs_int32 lockit)
     if (AFS_IS_DISCONNECTED)
        return ENETDOWN;
 
-    if ((code = afs_InitReq(&treq, afs_osi_credp)))
+    if ((code = afs_CreateReq(&treq, afs_osi_credp)))
        return code;
-    treq.flags |= O_NONBLOCK;
+    treq->flags |= O_NONBLOCK;
     tfids = afs_osi_Alloc(sizeof(struct AFSFid) * AFS_MAXCBRSCALL);
     osi_Assert(tfids != NULL);
 
@@ -455,8 +456,8 @@ afs_FlushVCBs(afs_int32 lockit)
                    callBacks[0].CallBackType = CB_EXCLUSIVE;
                    for (safety3 = 0; safety3 < AFS_MAXHOSTS * 2; safety3++) {
                        tc = afs_ConnByHost(tsp, tsp->cell->fsport,
-                                           tsp->cell->cellNum, &treq, 0,
-                                           SHARED_LOCK, &rxconn);
+                                           tsp->cell->cellNum, treq, 0,
+                                           SHARED_LOCK, 0, &rxconn);
                        if (tc) {
                            XSTATS_START_TIME
                                (AFS_STATS_FS_RPCIDX_GIVEUPCALLBACKS);
@@ -469,7 +470,7 @@ afs_FlushVCBs(afs_int32 lockit)
                        } else
                            code = -1;
                        if (!afs_Analyze
-                           (tc, rxconn, code, 0, &treq,
+                           (tc, rxconn, code, 0, treq,
                             AFS_STATS_FS_RPCIDX_GIVEUPCALLBACKS, SHARED_LOCK,
                             tsp->cell)) {
                            break;
@@ -514,6 +515,7 @@ afs_FlushVCBs(afs_int32 lockit)
     if (lockit)
        ReleaseWriteLock(&afs_xvcb);
     afs_osi_Free(tfids, sizeof(struct AFSFid) * AFS_MAXCBRSCALL);
+    afs_DestroyReq(treq);
     return 0;
 }
 
@@ -706,18 +708,23 @@ afs_ShakeLooseVCaches(afs_int32 anumber)
     struct vcache *tvc;
     struct afs_q *tq, *uq;
     int fv_slept, defersleep = 0;
+    int limit;
     afs_int32 target = anumber;
 
-    i = 0;
     loop = 0;
+
+ retry:
+    i = 0;
+    limit = afs_vcount;
     for (tq = VLRU.prev; tq != &VLRU && anumber > 0; tq = uq) {
        tvc = QTOV(tq);
        uq = QPrev(tq);
        if (tvc->f.states & CVFlushed) {
            refpanic("CVFlushed on VLRU");
-           /* In the other path, this was 2 * afs_cacheStats */
-       } else if (!afsd_dynamic_vcaches && i++ > afs_maxvcount) {
-           refpanic("Exceeded pool of AFS vnodes(VLRU cycle?)");
+       } else if (i++ > limit) {
+           afs_warn("afs_ShakeLooseVCaches: i %d limit %d afs_vcount %d afs_maxvcount %d\n",
+                    (int)i, limit, (int)afs_vcount, (int)afs_maxvcount);
+           refpanic("Found too many AFS vnodes on VLRU (VLRU cycle?)");
        } else if (QNext(uq) != tq) {
            refpanic("VLRU inconsistent");
        } else if (tvc->f.states & CVInit) {
@@ -731,15 +738,12 @@ afs_ShakeLooseVCaches(afs_int32 anumber)
        if (fv_slept) {
            if (loop++ > 100)
                break;
-           uq = VLRU.prev;
-           i = 0;
-           continue;   /* start over - may have raced. */
+           goto retry; /* start over - may have raced. */
        }
-       if (tq == uq) {
+       if (uq == &VLRU) {
            if (anumber && !defersleep) {
                defersleep = 1;
-               tq = VLRU.prev;
-               continue;
+               goto retry;
            }
            break;
        }
@@ -831,6 +835,32 @@ afs_PrePopulateVCache(struct vcache *avc, struct VenusFid *afid,
 #endif
 }
 
+void
+afs_FlushAllVCaches(void)
+{
+    int i;
+    struct vcache *tvc, *nvc;
+
+    ObtainWriteLock(&afs_xvcache, 867);
+
+ retry:
+    for (i = 0; i < VCSIZE; i++) {
+       for (tvc = afs_vhashT[i]; tvc; tvc = nvc) {
+           int slept;
+
+           nvc = tvc->hnext;
+           if (afs_FlushVCache(tvc, &slept)) {
+               afs_warn("Failed to flush vcache 0x%lx\n", (unsigned long)(uintptrsz)tvc);
+           }
+           if (slept) {
+               goto retry;
+           }
+       }
+    }
+
+    ReleaseWriteLock(&afs_xvcache);
+}
+
 /*!
  *   This routine is responsible for allocating a new cache entry
  * from the free list.  It formats the cache entry and inserts it
@@ -965,11 +995,18 @@ afs_FlushActiveVcaches(afs_int32 doflocks)
     struct afs_conn *tc;
     afs_int32 code;
     afs_ucred_t *cred = NULL;
-    struct vrequest treq, ureq;
+    struct vrequest *treq = NULL;
     struct AFSVolSync tsync;
     int didCore;
     XSTATS_DECLS;
     AFS_STATCNT(afs_FlushActiveVcaches);
+
+    code = afs_CreateReq(&treq, afs_osi_credp);
+    if (code) {
+       afs_warn("unable to alloc treq\n");
+       return;
+    }
+
     ObtainReadLock(&afs_xvcache);
     for (i = 0; i < VCSIZE; i++) {
        for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
@@ -986,10 +1023,14 @@ afs_FlushActiveVcaches(afs_int32 doflocks)
                ReleaseReadLock(&afs_xvcache);
                ObtainWriteLock(&tvc->lock, 51);
                do {
-                   afs_InitReq(&treq, afs_osi_credp);
-                   treq.flags |= O_NONBLOCK;
+                   code = afs_InitReq(treq, afs_osi_credp);
+                   if (code) {
+                       code = -1;
+                       break; /* shutting down: do not try to extend the lock */
+                   }
+                   treq->flags |= O_NONBLOCK;
 
-                   tc = afs_Conn(&tvc->f.fid, &treq, SHARED_LOCK, &rxconn);
+                   tc = afs_Conn(&tvc->f.fid, treq, SHARED_LOCK, &rxconn);
                    if (tc) {
                        XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_EXTENDLOCK);
                        RX_AFS_GUNLOCK();
@@ -1002,7 +1043,7 @@ afs_FlushActiveVcaches(afs_int32 doflocks)
                    } else
                        code = -1;
                } while (afs_Analyze
-                        (tc, rxconn, code, &tvc->f.fid, &treq,
+                        (tc, rxconn, code, &tvc->f.fid, treq,
                          AFS_STATS_FS_RPCIDX_EXTENDLOCK, SHARED_LOCK, NULL));
 
                ReleaseWriteLock(&tvc->lock);
@@ -1039,11 +1080,13 @@ afs_FlushActiveVcaches(afs_int32 doflocks)
                    /* XXXX Find better place-holder for cred XXXX */
                    cred = (afs_ucred_t *)tvc->linkData;
                    tvc->linkData = NULL;       /* XXX */
-                   afs_InitReq(&ureq, cred);
+                   code = afs_InitReq(treq, cred);
                    afs_Trace2(afs_iclSetp, CM_TRACE_ACTCCORE,
                               ICL_TYPE_POINTER, tvc, ICL_TYPE_INT32,
                               tvc->execsOrWriters);
-                   code = afs_StoreOnLastReference(tvc, &ureq);
+                   if (!code) {  /* avoid store when shutting down */
+                       code = afs_StoreOnLastReference(tvc, treq);
+                   }
                    ReleaseWriteLock(&tvc->lock);
 #ifdef AFS_BOZONLOCK_ENV
                    afs_BozonUnlock(&tvc->pvnLock, tvc);
@@ -1101,6 +1144,7 @@ afs_FlushActiveVcaches(afs_int32 doflocks)
        }
     }
     ReleaseReadLock(&afs_xvcache);
+    afs_DestroyReq(treq);
 }
 
 
@@ -2066,11 +2110,9 @@ afs_GetRootVCache(struct VenusFid *afid, struct vrequest *areq,
             }
 #ifdef AFS_DARWIN80_ENV
            if (tvc->f.states & CDeadVnode) {
-               if (!(tvc->f.states & CBulkFetching)) {
-                   ReleaseSharedLock(&afs_xvcache);
-                   afs_osi_Sleep(&tvc->f.states);
-                   goto rootvc_loop;
-               }
+               ReleaseSharedLock(&afs_xvcache);
+               afs_osi_Sleep(&tvc->f.states);
+               goto rootvc_loop;
            }
            tvp = AFSTOV(tvc);
            if (vnode_get(tvp))       /* this bumps ref count */
@@ -2082,11 +2124,6 @@ afs_GetRootVCache(struct VenusFid *afid, struct vrequest *areq,
                AFS_GLOCK();
                continue;
            }
-           if (tvc->f.states & (CBulkFetching|CDeadVnode)) {
-               AFS_GUNLOCK();
-               vnode_recycle(AFSTOV(tvc));
-               AFS_GLOCK();
-           }
 #endif
            break;
        }
@@ -2303,6 +2340,49 @@ afs_UpdateStatus(struct vcache *avc, struct VenusFid *afid,
        afs_PutVolume(volp, READ_LOCK);
 }
 
+void
+afs_BadFetchStatus(struct afs_conn *tc)
+{
+    int addr = ntohl(tc->parent->srvr->sa_ip);
+    afs_warn("afs: Invalid AFSFetchStatus from server %u.%u.%u.%u\n",
+             (addr >> 24) & 0xff, (addr >> 16) & 0xff, (addr >> 8) & 0xff,
+             (addr) & 0xff);
+    afs_warn("afs: This suggests the server may be sending bad data that "
+             "can lead to availability issues or data corruption. The "
+             "issue has been avoided for now, but it may not always be "
+             "detectable. Please upgrade the server if possible.\n");
+}
+
+/**
+ * Check if a given AFSFetchStatus structure is sane.
+ *
+ * @param[in] tc The server from which we received the status
+ * @param[in] status The status we received
+ *
+ * @return whether the given structure is valid or not
+ *  @retval 0 the structure is fine
+ *  @retval nonzero the structure looks like garbage; act as if we received
+ *                  the returned error code from the server
+ */
+int
+afs_CheckFetchStatus(struct afs_conn *tc, struct AFSFetchStatus *status)
+{
+    if (status->errorCode ||
+        status->InterfaceVersion != 1 ||
+        !(status->FileType > Invalid && status->FileType <= SymbolicLink) ||
+        status->ParentVnode == 0 || status->ParentUnique == 0) {
+
+       afs_warn("afs: FetchStatus ec %u iv %u ft %u pv %u pu %u\n",
+                (unsigned)status->errorCode, (unsigned)status->InterfaceVersion,
+                (unsigned)status->FileType, (unsigned)status->ParentVnode,
+                (unsigned)status->ParentUnique);
+       afs_BadFetchStatus(tc);
+
+       return VBUSY;
+    }
+    return 0;
+}
+
 /*!
  * Must be called with avc write-locked
  * don't absolutely have to invalidate the hint unless the dv has
@@ -2334,6 +2414,10 @@ afs_FetchStatus(struct vcache * avc, struct VenusFid * afid,
 
            XSTATS_END_TIME;
 
+           if (code == 0) {
+               code = afs_CheckFetchStatus(tc, Outsp);
+           }
+
        } else
            code = -1;
     } while (afs_Analyze
@@ -2535,11 +2619,14 @@ afs_PutVCache(struct vcache *avc)
  *
  * \param avc Pointer to the cache entry to reset
  * \param acred
+ * \param skipdnlc  skip the dnlc purge for this vnode
  *
  * \note avc must be write locked on entry
+ *
+ * \note The caller should purge the dnlc when skipdnlc is set.
  */
 void
-afs_ResetVCache(struct vcache *avc, afs_ucred_t *acred)
+afs_ResetVCache(struct vcache *avc, afs_ucred_t *acred, afs_int32 skipdnlc)
 {
     ObtainWriteLock(&afs_xcbhash, 456);
     afs_DequeueCallback(avc);
@@ -2547,7 +2634,9 @@ afs_ResetVCache(struct vcache *avc, afs_ucred_t *acred)
     ReleaseWriteLock(&afs_xcbhash);
     /* now find the disk cache entries */
     afs_TryToSmush(avc, acred, 1);
-    osi_dnlc_purgedp(avc);
+    if (!skipdnlc) {
+       osi_dnlc_purgedp(avc);
+    }
     if (avc->linkData && !(avc->f.states & CCore)) {
        afs_osi_Free(avc->linkData, strlen(avc->linkData) + 1);
        avc->linkData = NULL;
@@ -2566,7 +2655,6 @@ afs_ResetVCache(struct vcache *avc, afs_ucred_t *acred)
 static void
 findvc_sleep(struct vcache *avc, int flag)
 {
-    int fstates = avc->f.states;
     if (flag & IS_SLOCK) {
            ReleaseSharedLock(&afs_xvcache);
     } else {
@@ -2576,16 +2664,7 @@ findvc_sleep(struct vcache *avc, int flag)
            ReleaseReadLock(&afs_xvcache);
        }
     }
-    if (flag & FIND_CDEAD) {
-       ObtainWriteLock(&afs_xvcache, 342);
-       afs_FlushReclaimedVcaches();
-       if (fstates == avc->f.states) {
-           ReleaseWriteLock(&afs_xvcache);
-           afs_osi_Sleep(&avc->f.states);
-       } else
-           ReleaseWriteLock(&afs_xvcache);
-    } else
-       afs_osi_Sleep(&avc->f.states);
+    afs_osi_Sleep(&avc->f.states);
     if (flag & IS_SLOCK) {
            ObtainSharedLock(&afs_xvcache, 341);
     } else {
@@ -2666,19 +2745,6 @@ afs_FindVCache(struct VenusFid *afid, afs_int32 * retry, afs_int32 flag)
     i = VCHash(afid);
     for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
        if (FidMatches(afid, tvc)) {
-#ifdef  AFS_DARWIN80_ENV
-           if (flag & FIND_CDEAD) {
-               if (tvc->f.states & (CDeadVnode|CBulkFetching)) {
-                   deadvc = tvc;
-                   continue;
-               }
-           } else {
-               if (tvc->f.states & CDeadVnode)
-                   if ((tvc->f.states & CBulkFetching) &&
-                       !(flag & FIND_BULKDEAD))
-                       continue;
-           }
-#endif
             if (tvc->f.states & CVInit) {
                findvc_sleep(tvc, flag);
                goto findloop;
@@ -2688,30 +2754,10 @@ afs_FindVCache(struct VenusFid *afid, afs_int32 * retry, afs_int32 flag)
                findvc_sleep(tvc, flag);
                goto findloop;
             }
-           if (flag & FIND_CDEAD) {
-               livevc = tvc;
-               continue;
-           }
 #endif
            break;
        }
     }
-#ifdef  AFS_DARWIN80_ENV
-       if (flag & FIND_CDEAD) {
-           if (livevc && deadvc) {
-               /* discard deadvc */
-               AFS_GUNLOCK();
-               vnode_recycle(AFSTOV(deadvc));
-               vnode_put(AFSTOV(deadvc));
-               vnode_rele(AFSTOV(deadvc));
-               AFS_GLOCK();
-               deadvc = NULL;
-           }
-
-           /* return what's left */
-           tvc = livevc ? livevc : deadvc;
-       }
-#endif
 
     /* should I have a read lock on the vnode here? */
     if (tvc) {
@@ -2728,11 +2774,6 @@ afs_FindVCache(struct VenusFid *afid, afs_int32 * retry, afs_int32 flag)
            AFS_GLOCK();
            tvp = NULL;
        }
-       if (tvp && (tvc->f.states & (CBulkFetching|CDeadVnode))) {
-           AFS_GUNLOCK();
-           vnode_recycle(AFSTOV(tvc));
-           AFS_GLOCK();
-       }
        if (!tvp) {
            tvc = NULL;
            return tvc;
@@ -2845,11 +2886,9 @@ afs_NFSFindVCache(struct vcache **avcp, struct VenusFid *afid)
             }
 #ifdef  AFS_DARWIN80_ENV
            if (tvc->f.states & CDeadVnode) {
-               if (!(tvc->f.states & CBulkFetching)) {
-                   ReleaseSharedLock(&afs_xvcache);
-                   afs_osi_Sleep(&tvc->f.states);
-                   goto loop;
-               }
+               ReleaseSharedLock(&afs_xvcache);
+               afs_osi_Sleep(&tvc->f.states);
+               goto loop;
            }
            tvp = AFSTOV(tvc);
            if (vnode_get(tvp)) {
@@ -2864,11 +2903,6 @@ afs_NFSFindVCache(struct vcache **avcp, struct VenusFid *afid)
                AFS_GLOCK();
                continue;
            }
-           if (tvc->f.states & (CBulkFetching|CDeadVnode)) {
-               AFS_GUNLOCK();
-               vnode_recycle(AFSTOV(tvc));
-               AFS_GLOCK();
-           }
 #endif /* AFS_DARWIN80_ENV */
            count++;
            if (found_tvc) {