windows-afsd-memdump-20081121
[openafs.git] / src / WINNT / afsd / cm_buf.c
index e5a8a3c..e191bb9 100644 (file)
@@ -213,73 +213,89 @@ void buf_Release(cm_buf_t *bp)
     }
 }
 
-/* incremental sync daemon.  Writes all dirty buffers every 5000 ms */
-void buf_IncrSyncer(long parm)
+long 
+buf_Sync(int quitOnShutdown) 
 {
     cm_buf_t **bpp, *bp, *prevbp;
-    long i;                            /* counter */
-    long wasDirty = 0;
+    afs_uint32 wasDirty = 0;
     cm_req_t req;
 
-    while (buf_ShutdownFlag == 0) {
-        if (!wasDirty) {
-            i = SleepEx(5000, 1);
-            if (i != 0) continue;
-       }
-
-       wasDirty = 0;
+    /* go through all of the dirty buffers */
+    lock_ObtainRead(&buf_globalLock);
+    for (bpp = &cm_data.buf_dirtyListp, prevbp = NULL; bp = *bpp; ) {
+        if (quitOnShutdown && buf_ShutdownFlag)
+            break;
 
-        /* go through all of the dirty buffers */
-        lock_ObtainRead(&buf_globalLock);
-        for (bpp = &cm_data.buf_dirtyListp, prevbp = NULL; bp = *bpp; ) {
-            lock_ReleaseRead(&buf_globalLock);
-           /* all dirty buffers are held when they are added to the
-            * dirty list.  No need for an additional hold.
-            */
-            lock_ObtainMutex(&bp->mx);
+        lock_ReleaseRead(&buf_globalLock);
+        /* all dirty buffers are held when they are added to the
+        * dirty list.  No need for an additional hold.
+        */
+        lock_ObtainMutex(&bp->mx);
 
-           if (bp->flags & CM_BUF_DIRTY) {
-               /* start cleaning the buffer; don't touch log pages since
-                * the log code counts on knowing exactly who is writing
-                * a log page at any given instant.
-                */
-               cm_InitReq(&req);
-               req.flags |= CM_REQ_NORETRY;
-               wasDirty |= buf_CleanAsyncLocked(bp, &req);
-           }
+        if (bp->flags & CM_BUF_DIRTY && !(bp->flags & CM_BUF_REDIR)) {
+            /* start cleaning the buffer; don't touch log pages since
+            * the log code counts on knowing exactly who is writing
+            * a log page at any given instant.
+            */
+            afs_uint32 dirty;
+
+            cm_InitReq(&req);
+            req.flags |= CM_REQ_NORETRY;
+            buf_CleanAsyncLocked(bp, &req, &dirty);
+            wasDirty |= dirty;
+        }
 
-           /* the buffer may or may not have been dirty
-            * and if dirty may or may not have been cleaned
-            * successfully.  check the dirty flag again.  
-            */
-            if (!(bp->flags & CM_BUF_DIRTY)) {
-                /* remove the buffer from the dirty list */
-                lock_ObtainWrite(&buf_globalLock);
+        /* the buffer may or may not have been dirty
+        * and if dirty may or may not have been cleaned
+        * successfully.  check the dirty flag again.  
+        */
+        if (!(bp->flags & CM_BUF_DIRTY)) {
+            /* remove the buffer from the dirty list */
+            lock_ObtainWrite(&buf_globalLock);
 #ifdef DEBUG_REFCOUNT
-                if (bp->dirtyp == NULL && bp != cm_data.buf_dirtyListEndp) {
-                    osi_Log1(afsd_logp,"buf_IncrSyncer bp 0x%p list corruption",bp);
-                    afsi_log("buf_IncrSyncer bp 0x%p list corruption", bp);
-                }
-#endif
-                *bpp = bp->dirtyp;
-                bp->dirtyp = NULL;
-                bp->flags &= ~CM_BUF_INDL;
-                if (cm_data.buf_dirtyListp == NULL)
-                    cm_data.buf_dirtyListEndp = NULL;
-                else if (cm_data.buf_dirtyListEndp == bp)
-                    cm_data.buf_dirtyListEndp = prevbp;
-                buf_ReleaseLocked(bp, TRUE);
-                lock_ConvertWToR(&buf_globalLock);
-            } else {
-                /* advance the pointer so we don't loop forever */
-                lock_ObtainRead(&buf_globalLock);
-                bpp = &bp->dirtyp;
-                prevbp = bp;
+            if (bp->dirtyp == NULL && bp != cm_data.buf_dirtyListEndp) {
+                osi_Log1(afsd_logp,"buf_IncrSyncer bp 0x%p list corruption",bp);
+                afsi_log("buf_IncrSyncer bp 0x%p list corruption", bp);
             }
-            lock_ReleaseMutex(&bp->mx);
-        }      /* for loop over a bunch of buffers */
-        lock_ReleaseRead(&buf_globalLock);
-    }          /* whole daemon's while loop */
+#endif
+            *bpp = bp->dirtyp;
+            bp->dirtyp = NULL;
+            bp->flags &= ~CM_BUF_INDL;
+            if (cm_data.buf_dirtyListp == NULL)
+                cm_data.buf_dirtyListEndp = NULL;
+            else if (cm_data.buf_dirtyListEndp == bp)
+                cm_data.buf_dirtyListEndp = prevbp;
+            buf_ReleaseLocked(bp, TRUE);
+            lock_ConvertWToR(&buf_globalLock);
+        } else {
+            /* advance the pointer so we don't loop forever */
+            lock_ObtainRead(&buf_globalLock);
+            bpp = &bp->dirtyp;
+            prevbp = bp;
+        }
+        lock_ReleaseMutex(&bp->mx);
+    }  /* for loop over a bunch of buffers */
+    lock_ReleaseRead(&buf_globalLock);
+
+    return wasDirty;
+}
+
+/* incremental sync daemon.  Writes all dirty buffers every 5000 ms */
+void buf_IncrSyncer(long parm)
+{
+    long wasDirty = 0;
+    long i;
+
+    while (buf_ShutdownFlag == 0) {
+
+        if (!wasDirty) {
+           i = SleepEx(5000, 1);
+           if (i != 0) 
+                continue;
+       }
+
+        wasDirty = buf_Sync(1);
+    } /* whole daemon's while loop */
 }
 
 long
@@ -359,8 +375,12 @@ buf_ValidateBuffers(void)
 }
 
 void buf_Shutdown(void)  
-{                        
+{  
+    /* disable the buf_IncrSyncer() threads */
     buf_ShutdownFlag = 1;
+
+    /* then force all dirty buffers to the file servers */
+    buf_Sync(0);
 }                        
 
 /* initialize the buffer package; called with no locks
@@ -389,7 +409,7 @@ long buf_Init(int newFile, cm_buf_ops_t *opsp, afs_uint64 nbuffers)
 
     if (osi_Once(&once)) {
         /* initialize global locks */
-        lock_InitializeRWLock(&buf_globalLock, "Global buffer lock");
+        lock_InitializeRWLock(&buf_globalLock, "Global buffer lock", LOCK_HIERARCHY_BUF_GLOBAL);
 
         if ( newFile ) {
             /* remember this for those who want to reset it */
@@ -424,7 +444,7 @@ long buf_Init(int newFile, cm_buf_ops_t *opsp, afs_uint64 nbuffers)
                 
                 osi_QAdd((osi_queue_t **)&cm_data.buf_freeListp, &bp->q);
                 bp->flags |= CM_BUF_INLRU;
-                lock_InitializeMutex(&bp->mx, "Buffer mutex");
+                lock_InitializeMutex(&bp->mx, "Buffer mutex", LOCK_HIERARCHY_BUFFER);
                 
                 /* grab appropriate number of bytes from aligned zone */
                 bp->datap = data;
@@ -448,7 +468,7 @@ long buf_Init(int newFile, cm_buf_ops_t *opsp, afs_uint64 nbuffers)
             data = cm_data.bufDataBaseAddress;
             
             for (i=0; i<cm_data.buf_nbuffers; i++) {
-                lock_InitializeMutex(&bp->mx, "Buffer mutex");
+                lock_InitializeMutex(&bp->mx, "Buffer mutex", LOCK_HIERARCHY_BUFFER);
                 bp->userp = NULL;
                 bp->waitCount = 0;
                 bp->waitRequests = 0;
@@ -633,10 +653,10 @@ cm_buf_t *buf_Find(struct cm_scache *scp, osi_hyper_t *offsetp)
  *
  * Returns non-zero if the buffer was dirty.
  */
-long buf_CleanAsyncLocked(cm_buf_t *bp, cm_req_t *reqp)
+afs_uint32 buf_CleanAsyncLocked(cm_buf_t *bp, cm_req_t *reqp, afs_uint32 *pisdirty)
 {
-    long code = 0;
-    long isdirty = 0;
+    afs_uint32 code = 0;
+    afs_uint32 isdirty = 0;
     cm_scache_t * scp = NULL;
     osi_hyper_t offset;
 
@@ -676,14 +696,17 @@ long buf_CleanAsyncLocked(cm_buf_t *bp, cm_req_t *reqp)
         * because we aren't going to be able to write this data to the file
         * server.
         */
-       if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BADFD || code == CM_ERROR_NOACCESS){
+       if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BADFD || code == CM_ERROR_NOACCESS || 
+            code == CM_ERROR_QUOTA || code == CM_ERROR_SPACE || code == CM_ERROR_TOOBIG || 
+            code == CM_ERROR_READONLY || code == CM_ERROR_NOSUCHPATH){
            bp->flags &= ~CM_BUF_DIRTY;
            bp->flags |= CM_BUF_ERROR;
             bp->dirty_offset = 0;
             bp->dirty_length = 0;
            bp->error = code;
-           bp->dataVersion = CM_BUF_VERSION_BAD; /* bad */
+           bp->dataVersion = CM_BUF_VERSION_BAD;
            bp->dirtyCounter++;
+            break;
        }
 
 #ifdef DISKCACHE95
@@ -700,13 +723,6 @@ long buf_CleanAsyncLocked(cm_buf_t *bp, cm_req_t *reqp)
            break;
     };
 
-    if (!(bp->flags & CM_BUF_DIRTY)) {
-       /* remove buffer from dirty buffer queue */
-
-    }
-
-    /* do logging after call to GetLastError, or else */
-        
     /* if someone was waiting for the I/O that just completed or failed,
      * wake them up.
      */
@@ -715,7 +731,11 @@ long buf_CleanAsyncLocked(cm_buf_t *bp, cm_req_t *reqp)
         osi_Log1(buf_logp, "buf_WaitIO Waking bp 0x%p", bp);
         osi_Wakeup((LONG_PTR) bp);
     }
-    return isdirty;
+
+    if (pisdirty)
+        *pisdirty = isdirty;
+
+    return code;
 }
 
 /* Called with a zero-ref count buffer and with the buf_globalLock write locked.
@@ -902,7 +922,7 @@ long buf_GetNewLocked(struct cm_scache *scp, osi_hyper_t *offsetp, cm_buf_t **bu
                  * have the WRITING flag set, so we won't get
                  * back here.
                  */
-                buf_CleanAsync(bp, &req);
+                buf_CleanAsync(bp, &req, NULL);
 
                 /* now put it back and go around again */
                 buf_Release(bp);
@@ -957,24 +977,25 @@ long buf_GetNewLocked(struct cm_scache *scp, osi_hyper_t *offsetp, cm_buf_t **bu
             osi_QRemove((osi_queue_t **) &cm_data.buf_freeListp, &bp->q);
             bp->flags &= ~CM_BUF_INLRU;
 
+            /* prepare to return it.  Give it a refcount */
+            bp->refCount = 1;
+#ifdef DEBUG_REFCOUNT
+            osi_Log2(afsd_logp,"buf_GetNewLocked bp 0x%p ref %d", bp, 1);
+            afsi_log("%s:%d buf_GetNewLocked bp 0x%p, ref %d", __FILE__, __LINE__, bp, 1);
+#endif
             /* grab the mutex so that people don't use it
              * before the caller fills it with data.  Again, no one    
              * should have been able to get to this dude to lock it.
              */
            if (!lock_TryMutex(&bp->mx)) {
                osi_Log2(afsd_logp, "buf_GetNewLocked bp 0x%p cannot be mutex locked.  refCount %d should be 0",
-                        bp, bp->refCount);
+                         bp, bp->refCount);
                osi_panic("buf_GetNewLocked: TryMutex failed",__FILE__,__LINE__);
            }
 
-           /* prepare to return it.  Give it a refcount */
-            bp->refCount = 1;
-#ifdef DEBUG_REFCOUNT
-            osi_Log2(afsd_logp,"buf_GetNewLocked bp 0x%p ref %d", bp, 1);
-            afsi_log("%s:%d buf_GetNewLocked bp 0x%p, ref %d", __FILE__, __LINE__, bp, 1);
-#endif
             lock_ReleaseWrite(&buf_globalLock);
             lock_ReleaseRead(&scp->bufCreateLock);
+
             *bufpp = bp;
 
 #ifdef TESTING
@@ -1209,13 +1230,13 @@ long buf_CountFreeList(void)
 }
 
 /* clean a buffer synchronously */
-long buf_CleanAsync(cm_buf_t *bp, cm_req_t *reqp)
+long buf_CleanAsync(cm_buf_t *bp, cm_req_t *reqp, afs_uint32 *pisdirty)
 {
     long code;
     osi_assertx(bp->magic == CM_BUF_MAGIC, "invalid cm_buf_t magic");
 
     lock_ObtainMutex(&bp->mx);
-    code = buf_CleanAsyncLocked(bp, reqp);
+    code = buf_CleanAsyncLocked(bp, reqp, pisdirty);
     lock_ReleaseMutex(&bp->mx);
 
     return code;
@@ -1346,7 +1367,7 @@ long buf_CleanAndReset(void)
                 cm_InitReq(&req);
                req.flags |= CM_REQ_NORETRY;
 
-               buf_CleanAsync(bp, &req);
+               buf_CleanAsync(bp, &req, NULL);
                buf_CleanWait(NULL, bp, FALSE);
 
                 /* relock and release buffer */
@@ -1560,7 +1581,7 @@ long buf_FlushCleanPages(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
             lock_ObtainMutex(&bp->mx);
 
             /* start cleaning the buffer, and wait for it to finish */
-            buf_CleanAsyncLocked(bp, reqp);
+            buf_CleanAsyncLocked(bp, reqp, NULL);
             buf_WaitIO(scp, bp);
             lock_ReleaseMutex(&bp->mx);
 
@@ -1678,19 +1699,44 @@ long buf_CleanVnode(struct cm_scache *scp, cm_user_t *userp, cm_req_t *reqp)
         if (cm_FidCmp(&bp->fid, &scp->fid) == 0) {
             lock_ObtainMutex(&bp->mx);
             if (bp->flags & CM_BUF_DIRTY) {
-                if (userp) {
+                if (userp && userp != bp->userp) {
                     cm_HoldUser(userp);
                     if (bp->userp) 
                         cm_ReleaseUser(bp->userp);
                     bp->userp = userp;
                 }   
-                wasDirty = buf_CleanAsyncLocked(bp, reqp);
-                buf_CleanWait(scp, bp, TRUE);
-                if (bp->flags & CM_BUF_ERROR) {
-                    code = bp->error;
-                    if (code == 0) 
-                        code = -1;
+
+                switch (code) {
+                case CM_ERROR_NOSUCHFILE:
+                case CM_ERROR_BADFD:
+                case CM_ERROR_NOACCESS:
+                case CM_ERROR_QUOTA:
+                case CM_ERROR_SPACE:
+                case CM_ERROR_TOOBIG:
+                case CM_ERROR_READONLY:
+                case CM_ERROR_NOSUCHPATH:
+                    /* 
+                     * Apply the previous fatal error to this buffer.
+                     * Do not waste the time attempting to store to
+                     * the file server when we know it will fail.
+                     */
+                    bp->flags &= ~CM_BUF_DIRTY;
+                    bp->flags |= CM_BUF_ERROR;
+                    bp->dirty_offset = 0;
+                    bp->dirty_length = 0;
+                    bp->error = code;
+                    bp->dataVersion = CM_BUF_VERSION_BAD;
+                    bp->dirtyCounter++;
+                    break;
+                default:
+                    code = buf_CleanAsyncLocked(bp, reqp, &wasDirty);
+                    if (bp->flags & CM_BUF_ERROR) {
+                        code = bp->error;
+                        if (code == 0)
+                            code = -1;
+                    }
                 }
+                buf_CleanWait(scp, bp, TRUE);
             }
             lock_ReleaseMutex(&bp->mx);
         }
@@ -1774,11 +1820,11 @@ int cm_DumpBufHashTable(FILE *outputFile, char *cookie, int lock)
            StringCbPrintfA(output, sizeof(output), 
                            "%s bp=0x%08X, hash=%d, fid (cell=%d, volume=%d, "
                            "vnode=%d, unique=%d), offset=%x:%08x, dv=%I64d, "
-                           "flags=0x%x, cmFlags=0x%x, refCount=%d\r\n",
+                           "flags=0x%x, cmFlags=0x%x, error=0x%x, refCount=%d\r\n",
                             cookie, (void *)bp, i, bp->fid.cell, bp->fid.volume, 
                             bp->fid.vnode, bp->fid.unique, bp->offset.HighPart, 
                             bp->offset.LowPart, bp->dataVersion, bp->flags, 
-                            bp->cmFlags, bp->refCount);
+                            bp->cmFlags, bp->error, bp->refCount);
            WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
         }
     }
@@ -1792,11 +1838,11 @@ int cm_DumpBufHashTable(FILE *outputFile, char *cookie, int lock)
        StringCbPrintfA(output, sizeof(output), 
                         "%s bp=0x%08X, fid (cell=%d, volume=%d, "
                         "vnode=%d, unique=%d), offset=%x:%08x, dv=%I64d, "
-                        "flags=0x%x, cmFlags=0x%x, refCount=%d\r\n",
+                        "flags=0x%x, cmFlags=0x%x, error=0x%x, refCount=%d\r\n",
                         cookie, (void *)bp, bp->fid.cell, bp->fid.volume, 
                         bp->fid.vnode, bp->fid.unique, bp->offset.HighPart, 
                         bp->offset.LowPart, bp->dataVersion, bp->flags, 
-                        bp->cmFlags, bp->refCount);
+                        bp->cmFlags, bp->error, bp->refCount);
        WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
     }
     StringCbPrintfA(output, sizeof(output), "%s - Done dumping buf_FreeListEndp.\r\n", cookie);
@@ -1808,11 +1854,11 @@ int cm_DumpBufHashTable(FILE *outputFile, char *cookie, int lock)
        StringCbPrintfA(output, sizeof(output), 
                         "%s bp=0x%08X, fid (cell=%d, volume=%d, "
                         "vnode=%d, unique=%d), offset=%x:%08x, dv=%I64d, "
-                        "flags=0x%x, cmFlags=0x%x, refCount=%d\r\n",
+                        "flags=0x%x, cmFlags=0x%x, error=0x%x, refCount=%d\r\n",
                         cookie, (void *)bp, bp->fid.cell, bp->fid.volume, 
                         bp->fid.vnode, bp->fid.unique, bp->offset.HighPart, 
                         bp->offset.LowPart, bp->dataVersion, bp->flags, 
-                        bp->cmFlags, bp->refCount);
+                        bp->cmFlags, bp->error, bp->refCount);
        WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
     }
     StringCbPrintfA(output, sizeof(output), "%s - Done dumping buf_dirtyListp.\r\n", cookie);