cbr-free-what-you-alloc-20090325
[openafs.git] / src / afs / afs_vcache.c
index 6e53523..de1eb04 100644 (file)
@@ -82,6 +82,7 @@ struct afs_q afs_vhashTV[VCSIZE];
 static struct afs_cbr *afs_cbrHashT[CBRSIZE];
 afs_int32 afs_bulkStatsLost;
 int afs_norefpanic = 0;
+extern int afsd_dynamic_vcaches;
 
 
 /* Disk backed vcache definitions 
@@ -261,6 +262,7 @@ afs_FlushVCache(struct vcache *avc, int *slept)
        VN_UNLOCK(AFSTOV(avc));
 #endif
        AFS_RELE(AFSTOV(avc));
+       afs_stats_cmperf.vcacheXAllocs--;
     } else {
        if (afs_norefpanic) {
            printf("flush vc refcnt < 1");
@@ -322,6 +324,8 @@ afs_InactiveVCache(struct vcache *avc, struct AFS_UCRED *acred)
  * \return The allocated afs_cbr.
  */
 static struct afs_cbr *afs_cbrSpace = 0;
+/* if alloc limit below changes, fix me! */
+static struct afs_cbr *afs_cbrHeads[2];
 struct afs_cbr *
 afs_AllocCBR(void)
 {
@@ -343,6 +347,7 @@ afs_AllocCBR(void)
            }
            tsp[AFS_NCBRS - 1].next = 0;
            afs_cbrSpace = tsp;
+           afs_cbrHeads[afs_stats_cmperf.CallBackAlloced] = tsp;
            afs_stats_cmperf.CallBackAlloced++;
        }
     }
@@ -621,54 +626,30 @@ afs_FlushReclaimedVcaches(void)
 #endif
 }
 
-/*!
- *   This routine is responsible for allocating a new cache entry
- * from the free list.  It formats the cache entry and inserts it
- * into the appropriate hash tables.  It must be called with
- * afs_xvcache write-locked so as to prevent several processes from
- * trying to create a new cache entry simultaneously.
- *
- * LOCK: afs_NewVCache  afs_xvcache W
- *
- * \param afid The file id of the file whose cache entry is being created.
- *
- * \return The new vcache struct.
- */
-struct vcache *
-afs_NewVCache(struct VenusFid *afid, struct server *serverp)
+int
+afs_ShakeLooseVCaches(afs_int32 anumber)
 {
-    struct vcache *tvc;
+#if defined(AFS_OSF_ENV) || defined(AFS_LINUX22_ENV)
     afs_int32 i, j;
-    afs_int32 anumber = VCACHE_FREE;
-#ifdef AFS_AIX_ENV
-    struct gnode *gnodepnt;
-#endif
-#ifdef AFS_OSF_ENV
-    struct vcache *nvc;
-#endif /* AFS_OSF_ENV */
+    struct vcache *tvc;
     struct afs_q *tq, *uq;
     int code, fv_slept;
+    afs_int32 target = anumber;
+    int haveGlock = 1;
 
-    AFS_STATCNT(afs_NewVCache);
-
-    afs_FlushReclaimedVcaches();
+    /* Should probably deal better */
+    if (!ISAFS_GLOCK()) {
+       haveGlock = 0;
+       AFS_GLOCK();
+    }
 
-#if defined(AFS_OSF_ENV) || defined(AFS_LINUX22_ENV)
-# if defined(AFS_OSF30_ENV) || defined(AFS_LINUX22_ENV)
-    if (afs_vcount >= afs_maxvcount)
-# else
-    /*
-     * If we are using > 33 % of the total system vnodes for AFS vcache
-     * entries or we are using the maximum number of vcache entries,
-     * then free some.  (if our usage is > 33% we should free some, if
-     * our usage is > afs_maxvcount, set elsewhere to 0.5*nvnode,
-     * we _must_ free some -- no choice).
-     */
-    if (((3 * afs_vcount) > nvnode) || (afs_vcount >= afs_maxvcount))
-# endif
-    {
+    if (
+#ifdef AFS_MAXVCOUNT_ENV
+       afsd_dynamic_vcaches || /* Always run if dynamic vcaches are enabled. */
+#endif
+       afs_vcount >= afs_maxvcount
+       ) {
        int i;
-       char *panicstr;
 
        i = 0;
        for (tq = VLRU.prev; tq != &VLRU && anumber > 0; tq = uq) {
@@ -676,7 +657,11 @@ afs_NewVCache(struct VenusFid *afid, struct server *serverp)
            uq = QPrev(tq);
            if (tvc->f.states & CVFlushed) {
                refpanic("CVFlushed on VLRU");
-           } else if (i++ > afs_maxvcount) {
+           } else if (
+#ifdef AFS_MAXVCOUNT_ENV
+           ! afsd_dynamic_vcaches && 
+#endif
+           i++ > afs_maxvcount) {
                refpanic("Exceeded pool of AFS vnodes(VLRU cycle?)");
            } else if (QNext(uq) != tq) {
                refpanic("VLRU inconsistent");
@@ -684,14 +669,14 @@ afs_NewVCache(struct VenusFid *afid, struct server *serverp)
                refpanic("refcnt 0 on VLRU");
            }
 
-# if defined(AFS_LINUX22_ENV)
+#if defined(AFS_LINUX22_ENV)
            if (tvc != afs_globalVp && VREFCOUNT(tvc) > 1 && tvc->opens == 0) {
                 struct dentry *dentry;
                 struct list_head *cur, *head;
                 AFS_GUNLOCK();
-#  if defined(AFS_LINUX24_ENV)
+#if defined(AFS_LINUX24_ENV)
                 spin_lock(&dcache_lock);
-#  endif /* AFS_LINUX24_ENV */
+#endif /* AFS_LINUX24_ENV */
                head = &(AFSTOV(tvc))->i_dentry;
 
 restart:
@@ -704,27 +689,27 @@ restart:
 
                    dget_locked(dentry);
 
-#  if defined(AFS_LINUX24_ENV)
+#if defined(AFS_LINUX24_ENV)
                    spin_unlock(&dcache_lock);
-#  endif /* AFS_LINUX24_ENV */
+#endif /* AFS_LINUX24_ENV */
                    if (d_invalidate(dentry) == -EBUSY) {
                        dput(dentry);
                        /* perhaps lock and try to continue? (use cur as head?) */
                        goto inuse;
                    }
                    dput(dentry);
-#  if defined(AFS_LINUX24_ENV)
+#if defined(AFS_LINUX24_ENV)
                    spin_lock(&dcache_lock);
-#  endif /* AFS_LINUX24_ENV */
+#endif /* AFS_LINUX24_ENV */
                    goto restart;
                }                   
-#  if defined(AFS_LINUX24_ENV)
+#if defined(AFS_LINUX24_ENV)
                spin_unlock(&dcache_lock);
-#  endif /* AFS_LINUX24_ENV */
+#endif /* AFS_LINUX24_ENV */
            inuse:
                AFS_GLOCK();
            }
-# endif /* AFS_LINUX22_ENV */
+#endif /* AFS_LINUX22_ENV */
 
            if (VREFCOUNT_GT(tvc,0) && !VREFCOUNT_GT(tvc,1) &&
                tvc->opens == 0
@@ -742,47 +727,97 @@ restart:
            if (tq == uq)
                break;
        }
-       if (anumber == VCACHE_FREE) {
-           printf("afs_NewVCache: warning none freed, using %d of %d\n",
+       if (
+#ifdef AFS_MAXVCOUNT_ENV
+        !afsd_dynamic_vcaches &&
+#endif
+        anumber == target) {
+           printf("afs_ShakeLooseVCaches: warning none freed, using %d of %d\n",
                   afs_vcount, afs_maxvcount);
-           if (afs_vcount >= afs_maxvcount) {
-               printf("afs_NewVCache - none freed\n");
-               return NULL;
-           }
        }
     } /* finished freeing up space */
+/*
+    printf("recycled %d entries\n", target-anumber);
+*/
+    if (!haveGlock)
+       AFS_GUNLOCK();
+#endif
+    return 0;
+}
 
 /* Alloc new vnode. */
-#if defined(AFS_LINUX22_ENV)
+
+static struct vcache *
+afs_AllocVCache(void) 
 {
+    struct vcache *tvc;
+#if defined(AFS_OSF30_ENV)
+    struct vcache *nvc;
+    AFS_GUNLOCK();
+    if (getnewvnode(MOUNT_AFS, &Afs_vnodeops, &nvc)) {
+       /* What should we do ???? */
+       osi_Panic("afs_AllocVCache: no more vnodes");
+    }
+    AFS_GLOCK();
+
+    tvc = nvc;
+    tvc->nextfree = NULL;
+    afs_vcount++;
+#elif defined(AFS_LINUX22_ENV)
     struct inode *ip;
 
     AFS_GUNLOCK();
     ip = new_inode(afs_globalVFS);
     if (!ip)
-       osi_Panic("afs_NewVCache: no more inodes");
+       osi_Panic("afs_AllocVCache: no more inodes");
     AFS_GLOCK();
-#  if defined(STRUCT_SUPER_HAS_ALLOC_INODE)
+#if defined(STRUCT_SUPER_HAS_ALLOC_INODE)
     tvc = VTOAFS(ip);
-#  else
+#else
     tvc = afs_osi_Alloc(sizeof(struct vcache));
     ip->u.generic_ip = tvc;
     tvc->v = ip;
-#  endif
-}
-# else
-    AFS_GUNLOCK();
-    if (getnewvnode(MOUNT_AFS, &Afs_vnodeops, &nvc)) {
-       /* What should we do ???? */
-       osi_Panic("afs_NewVCache: no more vnodes");
-    }
-    AFS_GLOCK();
+#endif
 
-    tvc = nvc;
-    tvc->nextfree = NULL;
-# endif
     afs_vcount++;
-
+#ifdef AFS_MAXVCOUNT_ENV
+    /* track the peak */
+    if (afsd_dynamic_vcaches && afs_maxvcount < afs_vcount) {
+       afs_maxvcount = afs_vcount;
+       printf("peak vnodes: %d\n", afs_maxvcount);
+    }
+#endif
+    afs_stats_cmperf.vcacheXAllocs++;  /* count in case we have a leak */
+#else
+    /* none free, making one is better than a panic */
+    afs_stats_cmperf.vcacheXAllocs++;  /* count in case we have a leak */
+    tvc = (struct vcache *)afs_osi_Alloc(sizeof(struct vcache));
+#if defined(AFS_DARWIN_ENV) && !defined(UKERNEL)
+    tvc->v = NULL; /* important to clean this, or use memset 0 */
+#endif
+#ifdef KERNEL_HAVE_PIN
+    pin((char *)tvc, sizeof(struct vcache));   /* XXX */
+#endif
+#if defined(AFS_SGI_ENV)
+    {
+       char name[METER_NAMSZ];
+       memset(tvc, 0, sizeof(struct vcache));
+       tvc->v.v_number = ++afsvnumbers;
+       tvc->vc_rwlockid = OSI_NO_LOCKID;
+       initnsema(&tvc->vc_rwlock, 1,
+                 makesname(name, "vrw", tvc->v.v_number));
+#ifndef        AFS_SGI53_ENV
+       initnsema(&tvc->v.v_sync, 0,
+                 makesname(name, "vsy", tvc->v.v_number));
+#endif
+#ifndef AFS_SGI62_ENV
+       initnlock(&tvc->v.v_lock,
+                 makesname(name, "vlk", tvc->v.v_number));
+#endif
+    }
+#endif /* AFS_SGI_ENV */
+#endif
+#ifdef AFS_DISCON_ENV
     /* If we create a new inode, we either give it a new slot number,
      * or if one's available, use a slot number from the slot free list
      */
@@ -796,8 +831,54 @@ restart:
     }  else {
        tvc->diskSlot = afs_nextVcacheSlot++;
     }
+#endif
+
+    return tvc;
+}
+
+/*!
+ *   This routine is responsible for allocating a new cache entry
+ * from the free list.  It formats the cache entry and inserts it
+ * into the appropriate hash tables.  It must be called with
+ * afs_xvcache write-locked so as to prevent several processes from
+ * trying to create a new cache entry simultaneously.
+ *
+ * LOCK: afs_NewVCache  afs_xvcache W
+ *
+ * \param afid The file id of the file whose cache entry is being created.
+ *
+ * \return The new vcache struct.
+ */
+struct vcache *
+afs_NewVCache(struct VenusFid *afid, struct server *serverp)
+{
+    struct vcache *tvc;
+    afs_int32 i, j;
+    afs_int32 anumber = VCACHE_FREE;
+#ifdef AFS_AIX_ENV
+    struct gnode *gnodepnt;
+#endif
+    struct afs_q *tq, *uq;
+    int code, fv_slept;
 
-#else /* AFS_OSF_ENV || AFS_LINUX22_ENV */
+    AFS_STATCNT(afs_NewVCache);
+
+    afs_FlushReclaimedVcaches();
+
+#if defined(AFS_OSF_ENV) || defined(AFS_LINUX22_ENV)
+#ifdef AFS_MAXVCOUNT_ENV
+    if(!afsd_dynamic_vcaches) {
+#endif
+       afs_ShakeLooseVCaches(anumber);
+       if (afs_vcount >= afs_maxvcount) {
+           printf("afs_NewVCache - none freed\n");
+           return NULL;
+       }
+#ifdef AFS_MAXVCOUNT_ENV
+    }
+#endif
+    tvc = afs_AllocVCache();
+#else /* AFS_OSF_ENV */
     /* pull out a free cache entry */
     if (!freeVCList) {
         int loop = 0;
@@ -817,13 +898,13 @@ restart:
             }
 
            if (!VREFCOUNT_GT(tvc,0)
-# if defined(AFS_DARWIN_ENV) && !defined(UKERNEL) && !defined(AFS_DARWIN80_ENV)
+#if defined(AFS_DARWIN_ENV) && !defined(UKERNEL) && !defined(AFS_DARWIN80_ENV)
                || ((VREFCOUNT(tvc) == 1) && 
                    (UBCINFOEXISTS(AFSTOV(tvc))))
-# endif
+#endif
                && tvc->opens == 0 && (tvc->f.states & CUnlinkedDel) == 0) {
-# if defined (AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
-#  ifdef AFS_DARWIN80_ENV
+#if defined (AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
+#ifdef AFS_DARWIN80_ENV
                vnode_t tvp = AFSTOV(tvc);
                /* VREFCOUNT_GT only sees usecounts, not iocounts */
                /* so this may fail to actually recycle the vnode now */
@@ -851,7 +932,7 @@ restart:
                    code = EBUSY;
                } else
                    code = 0;
-#  else /* AFS_DARWIN80_ENV */
+#else /* AFS_DARWIN80_ENV */
                 /*
                  * vgone() reclaims the vnode, which calls afs_FlushVCache(),
                  * then it puts the vnode on the free list.
@@ -864,10 +945,10 @@ restart:
                 fv_slept = 0;
                 code = 0;
                 AFS_GLOCK();
-#  endif
-# else /* AFS_DARWIN80_ENV || AFS_XBSD_ENV */
+#endif
+#else /* AFS_DARWIN80_ENV || AFS_XBSD_ENV */
                 code = afs_FlushVCache(tvc, &fv_slept);
-# endif /* AFS_DARWIN80_ENV || AFS_XBSD_ENV */
+#endif /* AFS_DARWIN80_ENV || AFS_XBSD_ENV */
                if (code == 0) {
                    anumber--;
                }
@@ -885,49 +966,7 @@ restart:
     } /* end of if (!freeVCList) */
 
     if (!freeVCList) {
-       /* none free, making one is better than a panic */
-       afs_stats_cmperf.vcacheXAllocs++;       /* count in case we have a leak */
-       tvc = (struct vcache *)afs_osi_Alloc(sizeof(struct vcache));
-# if defined(AFS_DARWIN_ENV) && !defined(UKERNEL)
-       tvc->v = NULL; /* important to clean this, or use memset 0 */
-# endif
-# ifdef        KERNEL_HAVE_PIN
-       pin((char *)tvc, sizeof(struct vcache));        /* XXX */
-# endif
-
-#ifdef AFS_DISCON_ENV
-       /* If we create a new inode, we either give it a new slot number,
-         * or if one's available, use a slot number from the slot free list
-         */
-        if (afs_freeSlotList != NULL) {
-           struct afs_slotlist *tmp;
-       
-            tvc->diskSlot = afs_freeSlotList->slot;
-            tmp = afs_freeSlotList;
-           afs_freeSlotList = tmp->next;
-           afs_osi_Free(tmp, sizeof(struct afs_slotlist));
-        } else {
-           tvc->diskSlot = afs_nextVcacheSlot++;
-        }
-#endif
-# if defined(AFS_SGI_ENV)
-       {
-           char name[METER_NAMSZ];
-           memset(tvc, 0, sizeof(struct vcache));
-           tvc->v.v_number = ++afsvnumbers;
-           tvc->vc_rwlockid = OSI_NO_LOCKID;
-           initnsema(&tvc->vc_rwlock, 1,
-                     makesname(name, "vrw", tvc->v.v_number));
-#ifndef        AFS_SGI53_ENV
-           initnsema(&tvc->v.v_sync, 0,
-                     makesname(name, "vsy", tvc->v.v_number));
-#endif
-#ifndef AFS_SGI62_ENV
-           initnlock(&tvc->v.v_lock,
-                     makesname(name, "vlk", tvc->v.v_number));
-#endif
-       }
-#endif /* AFS_SGI_ENV */
+       tvc = afs_AllocVCache();
     } else {
        tvc = freeVCList;       /* take from free list */
        freeVCList = tvc->nextfree;
@@ -943,7 +982,7 @@ restart:
 
 #if !defined(AFS_SGI_ENV) && !defined(AFS_OSF_ENV) && !defined(AFS_LINUX22_ENV)
 
-# if AFS_DISCON_ENV
+#if defined(AFS_DISCON_ENV)
     /* We need to preserve the slot that we're being stored into on
      * disk */
     { 
@@ -952,9 +991,9 @@ restart:
        memset((char *)tvc, 0, sizeof(struct vcache));
        tvc->diskSlot = slot;
     }
-# else
+#else
     memset((char *)tvc, 0, sizeof(struct vcache));
-# endif
+#endif
 
 #else
     tvc->uncred = 0;
@@ -1908,7 +1947,7 @@ afs_GetVCache(register struct VenusFid *afid, struct vrequest *areq,
        newvcache = 1;
 
        ConvertWToSLock(&afs_xvcache);
-       if (!tvc)
+       if (tvc == NULL)
        {
                ReleaseSharedLock(&afs_xvcache);
                return NULL;
@@ -3173,7 +3212,9 @@ afs_NFSFindVCache(struct vcache **avcp, struct VenusFid *afid)
 void
 afs_vcacheInit(int astatSize)
 {
+#if (!defined(AFS_OSF_ENV) && !defined(AFS_LINUX22_ENV)) || defined(AFS_SGI_ENV)
     register struct vcache *tvp;
+#endif
     int i;
 #if defined(AFS_OSF_ENV) || defined(AFS_LINUX22_ENV)
     if (!afs_maxvcount) {
@@ -3248,7 +3289,7 @@ shutdown_vcache(void)
      */
 
     {
-       register struct afs_q *tq, *uq;
+       register struct afs_q *tq, *uq = NULL;
        register struct vcache *tvc;
        for (tq = VLRU.prev; tq != &VLRU; tq = uq) {
            tvc = QTOV(tq);
@@ -3303,7 +3344,8 @@ shutdown_vcache(void)
                    tvc->linkData = 0;
                }
 
-               afs_FreeAllAxs(&(tvc->Access));
+               if (tvc->Access)
+                   afs_FreeAllAxs(&(tvc->Access));
            }
            afs_vhashT[i] = 0;
        }
@@ -3311,8 +3353,9 @@ shutdown_vcache(void)
     /*
      * Free any leftover callback queue
      */
-    for (tsp = afs_cbrSpace; tsp; tsp = nsp) {
-       nsp = tsp->next;
+    for (i = 0; i < afs_stats_cmperf.CallBackAlloced; i++) {
+       tsp = afs_cbrHeads[i];
+       afs_cbrHeads[i] = 0;
        afs_osi_Free((char *)tsp, AFS_NCBRS * sizeof(struct afs_cbr));
     }
     afs_cbrSpace = 0;