From d581ab206ed03e93d60e61caa48fc0142211e0fa Mon Sep 17 00:00:00 2001 From: Ted Anderson Date: Wed, 16 Jan 2002 02:03:49 +0000 Subject: [PATCH] linux-newvcache-clean-up-dentries-20020115 With work and feedback from Omkar Sathe and Srikanth Vishwanathan --- src/afs/afs.h | 8 +++ src/afs/afs_vcache.c | 167 +++++++++++++++++++++++++++++---------------------- 2 files changed, 102 insertions(+), 73 deletions(-) diff --git a/src/afs/afs.h b/src/afs/afs.h index 50d7d1f..1660ea2 100644 --- a/src/afs/afs.h +++ b/src/afs/afs.h @@ -533,11 +533,19 @@ struct SimpleLocks { #define VREFCOUNT_SET(v, c) atomic_set(&((vnode_t *) v)->v_count, c) #define VREFCOUNT_DEC(v) atomic_dec(&((vnode_t *) v)->v_count) #define VREFCOUNT_INC(v) atomic_inc(&((vnode_t *) v)->v_count) +#define DLOCK() spin_lock(&dcache_lock) +#define DUNLOCK() spin_unlock(&dcache_lock) +#define DGET(d) dget_locked(d) +#define DCOUNT(d) atomic_read(&(d)->d_count) #else #define VREFCOUNT(v) ((v)->vrefCount) #define VREFCOUNT_SET(v, c) (v)->vrefCount = c; #define VREFCOUNT_DEC(v) (v)->vrefCount--; #define VREFCOUNT_INC(v) (v)->vrefCount++; +#define DLOCK() +#define DUNLOCK() +#define DGET(d) dget(d) +#define DCOUNT(d) ((d)->d_count) #endif #define AFS_MAXDV 0x7fffffff /* largest dataversion number */ diff --git a/src/afs/afs_vcache.c b/src/afs/afs_vcache.c index 67d3461..75c198b 100644 --- a/src/afs/afs_vcache.c +++ b/src/afs/afs_vcache.c @@ -525,7 +525,97 @@ afs_RemoveVCB(afid) } /*afs_RemoveVCB*/ +#if defined(AFS_LINUX22_ENV) +/* afs_TryFlushDcacheChildren -- Shakes loose vcache references held by + * children of the dentry + * LOCKS -- Called with afs_xvcache write locked. Drops and reaquires + * AFS_GLOCK, so it can call dput, which may call iput, but + * keeps afs_xvcache exclusively. + * + * Tree traversal algorithm from fs/dcache.c: select_parent() + */ +static void afs_TryFlushDcacheChildren(struct dentry *parent) +{ + struct dentry *this_parent = parent; + struct list_head *next; + + repeat: + next = this_parent->d_subdirs.next; + resume: + while (next != &this_parent->d_subdirs) { + struct list_head *tmp = next; + struct dentry *dentry = list_entry(tmp, struct dentry, d_child); + + next = tmp->next; + if (!DCOUNT(dentry) && !dentry->d_inode) { + DGET(dentry); + DUNLOCK(); + AFS_GUNLOCK(); + d_drop(dentry); + dput(dentry); + AFS_GLOCK(); + DLOCK(); + goto repeat; + } + + /* + * Descend a level if the d_subdirs list is non-empty. + */ + if (!list_empty(&dentry->d_subdirs)) { + this_parent = dentry; + goto repeat; + } + } + + /* + * All done at this level ... ascend and resume the search. + */ + if (this_parent != parent) { + next = this_parent->d_child.next; + this_parent = this_parent->d_parent; + goto resume; + } +} +/* afs_TryFlushDcache -- Shakes loose vcache references held by the Linux + * dcache. + * + * LOCKS -- Called with afs_xvcache write locked. Drops and reaquires + * AFS_GLOCK, so it can call dput, which may call iput, but + * keeps afs_xvcache exclusively. + */ +static void afs_TryFlushDcache(struct vcache *vcp) +{ + struct inode *ip = (struct inode *) vcp; + + DLOCK(); + retry: + if (!list_empty(&ip->i_dentry)) { + struct list_head *cur, *head = &ip->i_dentry; + cur = head; + while ((cur = cur->next) != head) { + struct dentry *dentry = list_entry(cur, struct dentry, d_alias); + + if (DCOUNT(dentry)) { + afs_TryFlushDcacheChildren(dentry); + } + + if (!DCOUNT(dentry)) { + DGET(dentry); + DUNLOCK(); + AFS_GUNLOCK(); + d_drop(dentry); + dput(dentry); + AFS_GLOCK(); + DLOCK(); + goto retry; + } + } + DUNLOCK(); + } +} +#endif + /* * afs_NewVCache * @@ -560,79 +650,6 @@ struct vcache *afs_NewVCache(struct VenusFid *afid, struct server *serverp, int code, fv_slept; AFS_STATCNT(afs_NewVCache); -#ifdef AFS_LINUX22_ENV - if (!freeVCList) { - /* Free some if possible. */ - struct afs_q *tq, *uq; - int i; char *panicstr; - int vmax = 2 * afs_cacheStats; - int vn = VCACHE_FREE; - - AFS_GUNLOCK(); - shrink_dcache_sb(afs_globalVFS); - AFS_GLOCK(); - - i = 0; - for(tq = VLRU.prev; tq != &VLRU && vn > 0; tq = uq) { - tvc = QTOV(tq); - uq = QPrev(tq); - if (tvc->states & CVFlushed) - refpanic ("CVFlushed on VLRU"); - else if (i++ > vmax) - refpanic ("Exceeded pool of AFS vnodes(VLRU cycle?)"); - else if (QNext(uq) != tq) - refpanic ("VLRU inconsistent"); - - if (tvc == afs_globalVp) - continue; - - if ( VREFCOUNT(tvc) && tvc->opens == 0 ) { - struct inode *ip = (struct inode*)tvc; - if (list_empty(&ip->i_dentry)) { - vn --; - } - else { - struct list_head *cur; - struct list_head *head = &ip->i_dentry; - int all = 1; - restart: -#if defined(AFS_LINUX24_ENV) - spin_lock(&dcache_lock); -#endif - cur = head; - while ((cur = cur->next) != head) { - struct dentry *dentry = list_entry(cur, struct dentry, d_alias); -#if defined(AFS_LINUX24_ENV) - if (!atomic_read(&dentry->d_count)) { -#else - if (!dentry->d_count) { -#endif - AFS_GUNLOCK(); -#if defined(AFS_LINUX24_ENV) - dget_locked(dentry); - spin_unlock(&dcache_lock); -#else - dget(dentry); -#endif - d_drop(dentry); - dput(dentry); - AFS_GLOCK(); - goto restart; - } - else { - all = 0; - } - } -#if defined(AFS_LINUX24_ENV) - spin_unlock(&dcache_lock); -#endif - if (all) vn --; - } - } - if (tq == uq) break; - } - } -#endif /* AFS_LINUX22_ENV */ #ifdef AFS_OSF_ENV #ifdef AFS_OSF30_ENV if (afs_vcount >= afs_maxvcount) @@ -724,6 +741,10 @@ struct vcache *afs_NewVCache(struct VenusFid *afid, struct server *serverp, } } #endif +#if defined(AFS_LINUX22_ENV) + if (tvc != afs_globalVp && VREFCOUNT(tvc) && tvc->opens == 0) + afs_TryFlushDcache(tvc); +#endif if (VREFCOUNT(tvc) == 0 && tvc->opens == 0 && (tvc->states & CUnlinkedDel) == 0) { code = afs_FlushVCache(tvc, &fv_slept); -- 1.9.4