windows-afsd-btree-20090228
authorJeffrey Altman <jaltman@secure-endpoints.com>
Sat, 28 Feb 2009 15:40:56 +0000 (15:40 +0000)
committerJeffrey Altman <jaltman@secure-endpoints.com>
Sat, 28 Feb 2009 15:40:56 +0000 (15:40 +0000)
LICENSE MIT

Change how BPlusDir enumerations behave with regards to bulk stat
operations.  If the number of entries in the enumeration is larger
than the number of cm_scache objects, then using the previous model
of cm_BPlusDirEnumBulkStat being called for the entire enumeration
list results in the early objects being recycled and the status
info discarded before the caller of cm_BPlusDirNextEnumEntry()
receives the name.

The revised model triggers bulk stat operations from within
NextEnumEntry() as objects requiring status fetching are about
to be returned to the caller.  This reduces the thrashing of the
stat cache.

We should consider adding a flag field to cm_BPlusDirEnumerate()
or cm_BPlusDirNextEnumEntry() to permit enumeration without
status fetching.

src/WINNT/afsd/cm_btree.c
src/WINNT/afsd/cm_btree.h

index ec5c853..766997e 100644 (file)
@@ -1613,7 +1613,7 @@ cm_BPlusDirLookupOriginalName(cm_dirOp_t * op, clientchar_t *centry,
          */
         slot = getSlot(op->scp->dirBplus, leafNode);
         if (slot <= BTERROR) {
-            op->scp->dirDataVersion = 0;
+            op->scp->dirDataVersion = CM_SCACHE_VERSION_BAD;
             rc = (slot == BTERROR ? EINVAL : ENOENT);
             goto done;
         }
@@ -2170,8 +2170,8 @@ cm_BPlusEnumAlloc(afs_uint32 entries)
 }
 
 long 
-cm_BPlusDirEnumerate(cm_scache_t *scp, afs_uint32 locked, 
-                     clientchar_t * maskp, cm_direnum_t **enumpp)
+cm_BPlusDirEnumerate(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, 
+                     afs_uint32 locked, clientchar_t * maskp, cm_direnum_t **enumpp)
 {
     afs_uint32 count = 0, slot, numentries;
     Nptr leafNode = NONODE, nextLeafNode;
@@ -2191,6 +2191,7 @@ cm_BPlusDirEnumerate(cm_scache_t *scp, afs_uint32 locked,
      * recycled while the enumeration is active. 
      */
     cm_HoldSCache(scp);
+    cm_HoldUser(userp);
 
     if (scp->dirBplus == NULL) {
        osi_Log0(afsd_logp, "cm_BPlusDirEnumerate No BPlus Tree");
@@ -2292,6 +2293,8 @@ cm_BPlusDirEnumerate(cm_scache_t *scp, afs_uint32 locked,
     }   
 
     enump->dscp = scp;
+    enump->userp = userp;
+    enump->reqFlags = reqp->flags;
 
   done:
     if (!locked)
@@ -2303,6 +2306,7 @@ cm_BPlusDirEnumerate(cm_scache_t *scp, afs_uint32 locked,
        
         /* release the directory because we failed to generate an enumeration object */
         cm_ReleaseSCache(scp);
+        cm_ReleaseUser(userp);
         if (enump) {
            for ( count = 0; count < enump->count && enump->entry[count].name; count++ ) {
                free(enump->entry[count].name);
@@ -2318,12 +2322,17 @@ cm_BPlusDirEnumerate(cm_scache_t *scp, afs_uint32 locked,
 }
 
 long 
-cm_BPlusDirEnumBulkStat(cm_direnum_t *enump, cm_user_t *userp, cm_req_t *reqp)
+cm_BPlusDirEnumBulkStat(cm_direnum_t *enump)
 {
     cm_scache_t *dscp = enump->dscp;
+    cm_user_t   *userp = enump->userp;
     cm_bulkStat_t *bsp;
     afs_uint32 count;
-    afs_uint32 code;
+    afs_uint32 code = 0;
+    cm_req_t req;
+
+    cm_InitReq(&req);
+    req.flags = enump->reqFlags;
 
     if ( dscp->fid.cell == AFS_FAKE_ROOT_CELL_ID )
         return 0;
@@ -2345,6 +2354,7 @@ cm_BPlusDirEnumBulkStat(cm_direnum_t *enump, cm_user_t *userp, cm_req_t *reqp)
                      */
                     lock_ReleaseWrite(&tscp->rw);
                     cm_ReleaseSCache(tscp);
+                    enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
                     continue;
                 }
                 lock_ReleaseWrite(&tscp->rw);
@@ -2356,21 +2366,82 @@ cm_BPlusDirEnumBulkStat(cm_direnum_t *enump, cm_user_t *userp, cm_req_t *reqp)
         bsp->fids[i].Volume = enump->entry[count].fid.volume;
         bsp->fids[i].Vnode = enump->entry[count].fid.vnode;
         bsp->fids[i].Unique = enump->entry[count].fid.unique;
+        enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
 
         if (bsp->counter == AFSCBMAX) {
-            code = cm_TryBulkStatRPC(dscp, bsp, userp, reqp);
+            code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
             memset(bsp, 0, sizeof(cm_bulkStat_t));
         }
     }
 
     if (bsp->counter > 0)
-        code = cm_TryBulkStatRPC(dscp, bsp, userp, reqp);
+        code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
 
     free(bsp);
-    return 0;
+    return code;
 }
 
-long 
+static long 
+cm_BPlusDirEnumBulkStatNext(cm_direnum_t *enump)
+{
+    cm_scache_t *dscp = enump->dscp;
+    cm_user_t   *userp = enump->userp;
+    cm_bulkStat_t *bsp;
+    afs_uint32 count;
+    afs_uint32 code = 0;
+    cm_req_t req;
+
+    cm_InitReq(&req);
+    req.flags = enump->reqFlags;
+
+    if ( dscp->fid.cell == AFS_FAKE_ROOT_CELL_ID )
+        return 0;
+
+    bsp = malloc(sizeof(cm_bulkStat_t));
+    memset(bsp, 0, sizeof(cm_bulkStat_t));
+
+    for ( count = enump->next; count < enump->count; count++ ) {
+        cm_scache_t   *tscp = cm_FindSCache(&enump->entry[count].fid);
+        int i;
+
+        if (tscp) {
+            if (lock_TryWrite(&tscp->rw)) {
+                /* we have an entry that we can look at */
+                if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
+                    /* we have a callback on it.  Don't bother
+                     * fetching this stat entry, since we're happy
+                     * with the info we have.
+                     */
+                    lock_ReleaseWrite(&tscp->rw);
+                    cm_ReleaseSCache(tscp);
+                    enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+                    continue;
+                }
+                lock_ReleaseWrite(&tscp->rw);
+            }  /* got lock */
+            cm_ReleaseSCache(tscp);
+        }      /* found entry */
+
+        i = bsp->counter++;
+        bsp->fids[i].Volume = enump->entry[count].fid.volume;
+        bsp->fids[i].Vnode = enump->entry[count].fid.vnode;
+        bsp->fids[i].Unique = enump->entry[count].fid.unique;
+        enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+
+        if (bsp->counter == AFSCBMAX) {
+            code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
+            break;
+        }
+    }
+
+    if (bsp->counter > 0 && bsp->counter < AFSCBMAX)
+        code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
+
+    free(bsp);
+    return code;
+}
+
+long
 cm_BPlusDirNextEnumEntry(cm_direnum_t *enump, cm_direnum_entry_t **entrypp)
 {      
     if (enump == NULL || entrypp == NULL || enump->next >= enump->count) {
@@ -2380,6 +2451,9 @@ cm_BPlusDirNextEnumEntry(cm_direnum_t *enump, cm_direnum_entry_t **entrypp)
        return CM_ERROR_INVAL;
     }
 
+    if (!(enump->entry[enump->next].flags & CM_DIRENUM_FLAG_GOT_STATUS)) 
+        cm_BPlusDirEnumBulkStatNext(enump);
+
     *entrypp = &enump->entry[enump->next++];
     if ( enump->next == enump->count ) {
        osi_Log0(afsd_logp, "cm_BPlusDirNextEnumEntry STOPNOW");
@@ -2401,6 +2475,7 @@ cm_BPlusDirFreeEnumeration(cm_direnum_t *enump)
     if (enump) {
         /* Release the directory object */
         cm_ReleaseSCache(enump->dscp);
+        cm_ReleaseUser(enump->userp);
 
        for ( count = 0; count < enump->count && enump->entry[count].name; count++ ) {
            free(enump->entry[count].name);
@@ -2411,15 +2486,16 @@ cm_BPlusDirFreeEnumeration(cm_direnum_t *enump)
 }
 
 long
-cm_BPlusDirEnumTest(cm_scache_t * dscp, afs_uint32 locked)
+cm_BPlusDirEnumTest(cm_scache_t * dscp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
 {
     cm_direnum_t *      enump = NULL;
     cm_direnum_entry_t * entryp;
     long                code;
 
+
     osi_Log0(afsd_logp, "cm_BPlusDirEnumTest start");
 
-    for (code = cm_BPlusDirEnumerate(dscp, locked, NULL, &enump); code == 0; ) {
+    for (code = cm_BPlusDirEnumerate(dscp, userp, reqp, locked, NULL, &enump); code == 0; ) {
        code = cm_BPlusDirNextEnumEntry(enump, &entryp);
        if (code == 0 || code == CM_ERROR_STOPNOW) {
            char buffer[1024];
index 5989dfd..f5c897d 100644 (file)
@@ -156,20 +156,26 @@ typedef struct cm_direnum_entry {
     clientchar_t *name;
     cm_fid_t    fid;
     normchar_t   shortName[13];
+    afs_uint32   flags;
 } cm_direnum_entry_t;
 
+#define CM_DIRENUM_FLAG_GOT_STATUS     1
+
 typedef struct cm_direnum {
     cm_scache_t        *dscp;
+    cm_user_t          *userp;
+    afs_uint32          reqFlags;
     afs_uint32         count;
     afs_uint32         next;
     cm_direnum_entry_t         entry[1];
 } cm_direnum_t;
 
-long cm_BPlusDirEnumerate(cm_scache_t *dscp, afs_uint32 locked, clientchar_t *maskp, cm_direnum_t **enumpp);
+long cm_BPlusDirEnumerate(cm_scache_t *dscp, cm_user_t *userp, cm_req_t *reqp, 
+                          afs_uint32 locked, clientchar_t *maskp, cm_direnum_t **enumpp);
 long cm_BPlusDirNextEnumEntry(cm_direnum_t *enump, cm_direnum_entry_t **entrypp);
 long cm_BPlusDirFreeEnumeration(cm_direnum_t *enump);
-long cm_BPlusDirEnumTest(cm_scache_t * dscp, afs_uint32 locked);
-long cm_BPlusDirEnumBulkStat(cm_direnum_t *enump, cm_user_t *userp, cm_req_t *reqp);
+long cm_BPlusDirEnumTest(cm_scache_t * dscp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked);
+long cm_BPlusDirEnumBulkStat(cm_direnum_t *enump);
 
 long cm_InitBPlusDir(void);