"ok, if you ever drop
dcache_lock you need to go to restart (i think that's pretty clear).
shrink_dcache_parent() _might_ reduce a dentry count to 0. in the
previous version, it seemed to make the assumption that this would
always happen. if shrink_dcache_parent() is unsuccessful and the
dentry is a directory, we cant restart. we would just find the
the dentry again and do the same thing over (we could always d_drop
but you shouldnt do this to active directories -- see d_invalidate).
if we find a busy dentry, we abort all processing for this inode.
going back to restart would find the same busy inode. (i suppose
we could use a d_flag to keep track of which dentry has been shrunk.
this has other trouble, like who resets the flag and when?) since we
only do this for directories and d_alias typically only grows due to
soft/hard links (as far as i can tell) this scheme seems reasonable."
(cherry picked from commit
73437ee7d469765df30285369301e3907fee0a3c)
struct list_head *cur, *head = &(AFSTOI(tvc))->i_dentry;
AFS_FAST_HOLD(tvc);
AFS_GUNLOCK();
-shrink_restart:
- DLOCK();
- cur=head;
+
+restart:
+ spin_lock(&dcache_lock);
+ cur = head;
while ((cur = cur->next) != head) {
dentry = list_entry(cur, struct dentry, d_alias);
- if (!d_unhashed(dentry) &&
- !list_empty(&dentry->d_subdirs)) {
+
+ if (d_unhashed(dentry))
+ continue;
+
+ dget_locked(dentry);
+
+ if (!list_empty(&dentry->d_subdirs)) {
DUNLOCK();
shrink_dcache_parent(dentry);
- goto shrink_restart;
+ DLOCK();
}
+
+ spin_lock(&dentry->d_lock);
+ if (atomic_read(&dentry->d_count) > 1) {
+ if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
+ spin_unlock(&dentry->d_lock);
+ spin_unlock(&dcache_lock);
+ dput(dentry);
+ goto inuse;
+ }
+ }
+
+ __d_drop(dentry);
+ spin_unlock(&dentry->d_lock);
+ spin_unlock(&dcache_lock);
+ dput(dentry);
+ goto restart;
+
}
DUNLOCK();
- d_prune_aliases(AFSTOI(tvc));
+inuse:
+
AFS_GLOCK();
AFS_FAST_RELE(tvc);
#else