windows-readonly-volume-callbacks-20071109
[openafs.git] / src / WINNT / afsd / cm_scache.c
index 0f725d4..9b6ed5e 100644 (file)
@@ -36,6 +36,8 @@ osi_rwlock_t cm_scacheLock;
 /* Dummy scache entry for use with pioctl fids */
 cm_scache_t cm_fakeSCache;
 
+osi_queue_t * cm_allFreeWaiters;        /* protected by cm_scacheLock */
+
 #ifdef AFS_FREELANCE_CLIENT
 extern osi_mutex_t cm_Freelance_Lock;
 #endif
@@ -145,8 +147,8 @@ long cm_RecycleSCache(cm_scache_t *scp, afs_int32 flags)
        buf_CleanDirtyBuffers(scp); 
     } else {
        /* look for things that shouldn't still be set */
-       osi_assert(scp->bufWritesp == NULL);
-       osi_assert(scp->bufReadsp == NULL);
+       osi_assertx(scp->bufWritesp == NULL, "non-null cm_scache_t bufWritesp");
+       osi_assertx(scp->bufReadsp == NULL, "non-null cm_scache_t bufReadsp");
     }
 #endif
 
@@ -164,6 +166,7 @@ long cm_RecycleSCache(cm_scache_t *scp, afs_int32 flags)
     scp->dataVersion = 0;
     scp->bulkStatProgress = hzero;
     scp->waitCount = 0;
+    scp->waitQueueT = NULL;
 
     if (scp->cbServerp) {
         cm_PutServer(scp->cbServerp);
@@ -201,6 +204,7 @@ long cm_RecycleSCache(cm_scache_t *scp, afs_int32 flags)
     scp->serverLock = (-1);
     scp->exclusiveLocks = 0;
     scp->sharedLocks = 0;
+    scp->lockDataVersion = -1;
 
     /* not locked, but there can be no references to this guy
      * while we hold the global refcount lock.
@@ -238,7 +242,8 @@ cm_scache_t *cm_GetNewSCache(void)
          scp;
          scp = (cm_scache_t *) osi_QPrev(&scp->q)) 
     {
-       osi_assert(scp >= cm_data.scacheBaseAddress && scp < (cm_scache_t *)cm_data.scacheHashTablep);
+       osi_assertx(scp >= cm_data.scacheBaseAddress && scp < (cm_scache_t *)cm_data.scacheHashTablep,
+                    "invalid cm_scache_t address");
 
        if (scp->refCount == 0) {
            if (scp->flags & CM_SCACHEFLAG_DELETED) {
@@ -307,7 +312,8 @@ cm_scache_t *cm_GetNewSCache(void)
      * quota or we have a leak and need to allocate a new one to avoid panicing.
      */
     scp = cm_data.scacheBaseAddress + cm_data.currentSCaches;
-    osi_assert(scp >= cm_data.scacheBaseAddress && scp < (cm_scache_t *)cm_data.scacheHashTablep);
+    osi_assertx(scp >= cm_data.scacheBaseAddress && scp < (cm_scache_t *)cm_data.scacheHashTablep,
+                "invalid cm_scache_t address");
     memset(scp, 0, sizeof(cm_scache_t));
     scp->magic = CM_SCACHE_MAGIC;
     lock_InitializeMutex(&scp->mx, "cm_scache_t mutex");
@@ -472,19 +478,32 @@ void
 cm_SuspendSCache(void)
 {
     cm_scache_t * scp;
-
-    cm_GiveUpAllCallbacksAllServers();
+    time_t now;
+
+    cm_GiveUpAllCallbacksAllServers(TRUE);
+
+    /* 
+     * After this call all servers are marked down.
+     * Do not clear the callbacks, instead change the
+     * expiration time so that the callbacks will be expired
+     * when the servers are marked back up.  However, we
+     * want the callbacks to be preserved as long as the 
+     * servers are down.  That way if the machine resumes
+     * without network, the stat cache item will still be
+     * considered valid.
+     */
+    now = osi_Time();
 
     lock_ObtainWrite(&cm_scacheLock);
-    for ( scp = cm_data.allSCachesp; scp;
-          scp = scp->allNextp ) {
-        if (scp->cbServerp && 
-            !(scp->cbServerp->flags & CM_SERVERFLAG_DOWN)) {
-            cm_PutServer(scp->cbServerp);
-            scp->cbServerp = NULL;
+    for ( scp = cm_data.allSCachesp; scp; scp = scp->allNextp ) {
+        if (scp->cbServerp) {
+            if (scp->flags & CM_SCACHEFLAG_PURERO && scp->volp) {
+                if (scp->volp->cbExpiresRO == scp->cbExpires) {
+                    scp->volp->cbExpiresRO = now+1;
+                }
+            }
+            scp->cbExpires = now+1;
         }
-        scp->cbExpires = 0;
-        scp->flags &= ~CM_SCACHEFLAG_CALLBACK;
     }
     lock_ReleaseWrite(&cm_scacheLock);
 }
@@ -523,7 +542,7 @@ cm_ShutdownSCache(void)
     }
     lock_ReleaseWrite(&cm_scacheLock);
 
-    cm_GiveUpAllCallbacksAllServers();
+    cm_GiveUpAllCallbacksAllServers(FALSE);
 
     return cm_dnlcShutdown();
 }
@@ -567,6 +586,7 @@ void cm_InitSCache(int newFile, long maxSCaches)
                 scp->dirBplus = NULL;
                 scp->dirDataVersion = -1;
 #endif
+                scp->waitQueueT = NULL;
                 scp->flags &= ~CM_SCACHEFLAG_WAITING;
             }
         }
@@ -574,6 +594,7 @@ void cm_InitSCache(int newFile, long maxSCaches)
         cm_freeFileLocks = NULL;
         cm_lockRefreshCycle = 0;
         cm_fakeSCacheInit(newFile);
+        cm_allFreeWaiters = NULL;
         cm_dnlcInit(newFile);
         osi_EndOnce(&once);
     }
@@ -624,7 +645,7 @@ long cm_GetSCache(cm_fid_t *fidp, cm_scache_t **outScpp, cm_user_t *userp,
         
     hash = CM_SCACHE_HASH(fidp);
         
-    osi_assert(fidp->cell != 0);
+    osi_assertx(fidp->cell != 0, "unassigned cell value");
 
     if (fidp->cell== cm_data.rootFid.cell && 
          fidp->volume==cm_data.rootFid.volume &&
@@ -770,7 +791,7 @@ long cm_GetSCache(cm_fid_t *fidp, cm_scache_t **outScpp, cm_user_t *userp,
            osi_Log1(afsd_logp,"cm_GetSCache (3) outScpp 0x%p", scp);
 #endif
             cm_HoldSCacheNoLock(scp);
-            osi_assert(scp->volp == volp);
+            osi_assertx(scp->volp == volp, "cm_scache_t volume has unexpected value");
             cm_AdjustScacheLRU(scp);
             lock_ReleaseWrite(&cm_scacheLock);
             if (volp)
@@ -791,7 +812,7 @@ long cm_GetSCache(cm_fid_t *fidp, cm_scache_t **outScpp, cm_user_t *userp,
     }
     osi_Log2(afsd_logp,"cm_GetNewSCache returns scp 0x%x flags 0x%x", scp, scp->flags);
 
-    osi_assert(!(scp->flags & CM_SCACHEFLAG_INHASH));
+    osi_assertx(!(scp->flags & CM_SCACHEFLAG_INHASH), "CM_SCACHEFLAG_INHASH set");
 
 #if not_too_dangerous
     /* dropping the cm_scacheLock allows more than one thread
@@ -875,6 +896,69 @@ cm_scache_t * cm_FindSCacheParent(cm_scache_t * scp)
     return pscp;
 }
 
+void cm_SyncOpAddToWaitQueue(cm_scache_t * scp, afs_int32 flags, cm_buf_t * bufp)
+{
+    cm_scache_waiter_t * w;
+
+    lock_ObtainWrite(&cm_scacheLock);
+    if (cm_allFreeWaiters == NULL) {
+        w = malloc(sizeof(*w));
+        memset(w, 0, sizeof(*w));
+    } else {
+        w = (cm_scache_waiter_t *) cm_allFreeWaiters;
+        osi_QRemove(&cm_allFreeWaiters, (osi_queue_t *) w);
+    }
+
+    w->threadId = thrd_Current();
+    w->scp = scp;
+    cm_HoldSCacheNoLock(scp);
+    w->flags = flags;
+    w->bufp = bufp;
+
+    osi_QAddT(&scp->waitQueueH, &scp->waitQueueT, (osi_queue_t *) w);
+    lock_ReleaseWrite(&cm_scacheLock);
+
+    osi_Log2(afsd_logp, "cm_SyncOpAddToWaitQueue : Adding thread to wait queue scp 0x%p w 0x%p", scp, w);
+}
+
+int cm_SyncOpCheckContinue(cm_scache_t * scp, afs_int32 flags, cm_buf_t * bufp)
+{
+    cm_scache_waiter_t * w;
+    int this_is_me;
+
+    osi_Log0(afsd_logp, "cm_SyncOpCheckContinue checking for continuation");
+
+    lock_ObtainRead(&cm_scacheLock);
+    for (w = (cm_scache_waiter_t *)scp->waitQueueH;
+         w;
+         w = (cm_scache_waiter_t *)osi_QNext((osi_queue_t *) w)) {
+        if (w->flags == flags && w->bufp == bufp) {
+            break;
+        }
+    }
+
+    osi_assertx(w != NULL, "null cm_scache_waiter_t");
+    this_is_me = (w->threadId == thrd_Current());
+    lock_ReleaseRead(&cm_scacheLock);
+
+    if (!this_is_me) {
+        osi_Log1(afsd_logp, "cm_SyncOpCheckContinue MISS: Waiter 0x%p", w);
+        return 0;
+    }
+
+    osi_Log1(afsd_logp, "cm_SyncOpCheckContinue HIT: Waiter 0x%p", w);
+
+    lock_ObtainWrite(&cm_scacheLock);
+    osi_QRemoveHT(&scp->waitQueueH, &scp->waitQueueT, (osi_queue_t *) w);
+    cm_ReleaseSCacheNoLock(scp);
+    memset(w, 0, sizeof(*w));
+    osi_QAdd(&cm_allFreeWaiters, (osi_queue_t *) w);
+    lock_ReleaseWrite(&cm_scacheLock);
+
+    return 1;
+}
+
+
 /* synchronize a fetch, store, read, write, fetch status or store status.
  * Called with scache mutex held, and returns with it held, but temporarily
  * drops it during the fetch.
@@ -941,12 +1025,13 @@ long cm_SyncOp(cm_scache_t *scp, cm_buf_t *bufp, cm_user_t *userp, cm_req_t *req
     afs_uint32 sleep_scp_flags = 0;
     afs_uint32 sleep_buf_cmflags = 0;
     afs_uint32 sleep_scp_bufs = 0;
+    int wakeupCycle;
 
     /* lookup this first */
     bufLocked = flags & CM_SCACHESYNC_BUFLOCKED;
 
-       if (bufp)
-               osi_assert(bufp->refCount > 0);
+    if (bufp)
+        osi_assertx(bufp->refCount > 0, "cm_buf_t refCount 0");
 
 
     /* Do the access check.  Now we don't really do the access check
@@ -1138,7 +1223,7 @@ long cm_SyncOp(cm_scache_t *scp, cm_buf_t *bufp, cm_user_t *userp, cm_req_t *req
 
         if (rights) {
             /* can't check access rights without a callback */
-            osi_assert(flags & CM_SCACHESYNC_NEEDCALLBACK);
+            osi_assertx(flags & CM_SCACHESYNC_NEEDCALLBACK, "!CM_SCACHESYNC_NEEDCALLBACK");
 
             if ((rights & PRSFS_WRITE) && (scp->flags & CM_SCACHEFLAG_RO))
                 return CM_ERROR_READONLY;
@@ -1172,6 +1257,7 @@ long cm_SyncOp(cm_scache_t *scp, cm_buf_t *bufp, cm_user_t *userp, cm_req_t *req
         if (flags & CM_SCACHESYNC_NOWAIT) 
             return CM_ERROR_WOULDBLOCK;
 
+        /* These are used for minidump debugging */
        sleep_scp_flags = scp->flags;           /* so we know why we slept */
        sleep_buf_cmflags = bufp ? bufp->cmFlags : 0;
        sleep_scp_bufs = (scp->bufReadsp ? 1 : 0) | (scp->bufWritesp ? 2 : 0);
@@ -1188,9 +1274,17 @@ long cm_SyncOp(cm_scache_t *scp, cm_buf_t *bufp, cm_user_t *userp, cm_req_t *req
             scp->flags |= CM_SCACHEFLAG_WAITING;
             scp->waitCount = scp->waitRequests = 1;
         }
+
         if (bufLocked) 
             lock_ReleaseMutex(&bufp->mx);
-        osi_SleepM((LONG_PTR) &scp->flags, &scp->mx);
+
+        cm_SyncOpAddToWaitQueue(scp, flags, bufp);
+        wakeupCycle = 0;
+        do {
+            if (wakeupCycle++ != 0)
+                lock_ObtainMutex(&scp->mx);
+            osi_SleepM((LONG_PTR) &scp->flags, &scp->mx);
+        } while (!cm_SyncOpCheckContinue(scp, flags, bufp));
 
        smb_UpdateServerPriority();
 
@@ -1229,7 +1323,7 @@ long cm_SyncOp(cm_scache_t *scp, cm_buf_t *bufp, cm_user_t *userp, cm_req_t *req
         if (bufp) {
             for(qdp = scp->bufReadsp; qdp; qdp = (osi_queueData_t *) osi_QNext(&qdp->q)) {
                 tbufp = osi_GetQData(qdp);
-                osi_assert(tbufp != bufp);
+                osi_assertx(tbufp != bufp, "unexpected cm_buf_t value");
             }
         }
 
@@ -1248,7 +1342,7 @@ long cm_SyncOp(cm_scache_t *scp, cm_buf_t *bufp, cm_user_t *userp, cm_req_t *req
         if (bufp) {
             for(qdp = scp->bufWritesp; qdp; qdp = (osi_queueData_t *) osi_QNext(&qdp->q)) {
                 tbufp = osi_GetQData(qdp);
-                osi_assert(tbufp != bufp);
+                osi_assertx(tbufp != bufp, "unexpected cm_buf_t value");
             }
         }
 
@@ -1351,7 +1445,7 @@ void cm_SyncOpDone(cm_scache_t *scp, cm_buf_t *bufp, afs_uint32 flags)
 
     if (flags & CM_SCACHESYNC_WRITE) {
         if (bufp) {
-            osi_assert(bufp->cmFlags & CM_BUF_CMWRITING);
+            osi_assertx(bufp->cmFlags & CM_BUF_CMWRITING, "!CM_BUF_CMWRITING");
 
             bufp->cmFlags &= ~CM_BUF_CMWRITING;
         }
@@ -1588,7 +1682,7 @@ void cm_HoldSCacheNoLockDbg(cm_scache_t *scp, char * file, long line)
 void cm_HoldSCacheNoLock(cm_scache_t *scp)
 #endif
 {
-    osi_assert(scp != 0);
+    osi_assertx(scp != NULL, "null cm_scache_t");
     scp->refCount++;
 #ifdef DEBUG_REFCOUNT
     osi_Log2(afsd_logp,"cm_HoldSCacheNoLock scp 0x%p ref %d",scp, scp->refCount);
@@ -1602,7 +1696,7 @@ void cm_HoldSCacheDbg(cm_scache_t *scp, char * file, long line)
 void cm_HoldSCache(cm_scache_t *scp)
 #endif
 {
-    osi_assert(scp != 0);
+    osi_assertx(scp != NULL, "null cm_scache_t");
     lock_ObtainWrite(&cm_scacheLock);
     scp->refCount++;
 #ifdef DEBUG_REFCOUNT
@@ -1618,10 +1712,10 @@ void cm_ReleaseSCacheNoLockDbg(cm_scache_t *scp, char * file, long line)
 void cm_ReleaseSCacheNoLock(cm_scache_t *scp)
 #endif
 {
-    osi_assert(scp != NULL);
+    osi_assertx(scp != NULL, "null cm_scache_t");
     if (scp->refCount == 0)
        osi_Log1(afsd_logp,"cm_ReleaseSCacheNoLock about to panic scp 0x%x",scp);
-    osi_assert(scp->refCount-- >= 0);
+    osi_assertx(scp->refCount-- >= 0, "cm_scache_t refCount 0");
 #ifdef DEBUG_REFCOUNT
     osi_Log2(afsd_logp,"cm_ReleaseSCacheNoLock scp 0x%p ref %d",scp,scp->refCount);
     afsi_log("%s:%d cm_ReleaseSCacheNoLock scp 0x%p ref %d", file, line, scp, scp->refCount);
@@ -1634,11 +1728,11 @@ void cm_ReleaseSCacheDbg(cm_scache_t *scp, char * file, long line)
 void cm_ReleaseSCache(cm_scache_t *scp)
 #endif
 {
-    osi_assert(scp != NULL);
+    osi_assertx(scp != NULL, "null cm_scache_t");
     lock_ObtainWrite(&cm_scacheLock);
     if (scp->refCount == 0)
        osi_Log1(afsd_logp,"cm_ReleaseSCache about to panic scp 0x%x",scp);
-    osi_assert(scp->refCount != 0);
+    osi_assertx(scp->refCount != 0, "cm_scache_t refCount 0");
     scp->refCount--;
 #ifdef DEBUG_REFCOUNT
     osi_Log2(afsd_logp,"cm_ReleaseSCache scp 0x%p ref %d",scp,scp->refCount);
@@ -1656,7 +1750,7 @@ int cm_FindFileType(cm_fid_t *fidp)
         
     hash = CM_SCACHE_HASH(fidp);
         
-    osi_assert(fidp->cell != 0);
+    osi_assertx(fidp->cell != 0, "unassigned cell value");
 
     lock_ObtainWrite(&cm_scacheLock);
     for (scp=cm_data.scacheHashTablep[hash]; scp; scp=scp->nextp) {