LINUX: Avoid infinite d_invalidate loop
authorAndrew Deason <adeason@sinenomine.net>
Wed, 12 Dec 2012 22:14:55 +0000 (16:14 -0600)
committerDerrick Brashear <shadow@your-file-system.com>
Fri, 28 Dec 2012 20:05:17 +0000 (12:05 -0800)
In afs_linux_lookup, we try to invalidate as many dentry aliases as we
can. However, a successful d_invalidate() against a dentry does not
necessarily mean that the dentry will not show up in a later
d_find_alias() call. This is because d_invalidate() does not remove
the dentry from the d_alias list, but just removes it from the hash
chain. dput() is what removes it from the d_alias list when all of the
references go away. If a reference is grabbed between our
d_invalidate() and dput() calls, the dentry will stay on the d_alias
list.

We will then retry the loop, and we will get the same dentry back from
d_find_alias(). Running d_invalidate() on an unhashed dentry is a
no-op, so we don't change anything in the loop. We will retry again
and again, looping forever and spinning the CPU.

To avoid this, just call d_prune_aliases instead, instead of
repeatedly looping through the alias list ourselves. Note that this
does remove our check for DCACHE_DISCONNECTED in each alias' d_flags.
This should not be a problem, since we will still use any remaining
DCACHE_DISCONNECTED dentry via d_splice_alias if one still exists.

Change-Id: I8a09a922d07f2c4971269f3c681c748c33bf8e3d
Reviewed-on: http://gerrit.openafs.org/8751
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Chas Williams - CONTRACTOR <chas@cmf.nrl.navy.mil>
Reviewed-by: Marc Dionne <marc.c.dionne@gmail.com>
Reviewed-by: Derrick Brashear <shadow@your-file-system.com>

src/afs/LINUX/osi_vnodeops.c
src/afs/LINUX24/osi_vnodeops.c

index de1a703..f37a098 100644 (file)
@@ -1409,24 +1409,7 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
     AFS_GUNLOCK();
 
     if (ip && S_ISDIR(ip->i_mode)) {
-       int retry = 1;
-       struct dentry *alias;
-
-       while (retry) {
-           retry = 0;
-
-           /* Try to invalidate an existing alias in favor of our new one */
-           alias = d_find_alias(ip);
-           /* But not if it's disconnected; then we want d_splice_alias below */
-           if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) {
-               if (d_invalidate(alias) == 0) {
-                   /* there may be more aliases; try again until we run out */
-                   retry = 1;
-               }
-           }
-
-           dput(alias);
-       }
+       d_prune_aliases(ip);
 
 #ifdef STRUCT_DENTRY_OPERATIONS_HAS_D_AUTOMOUNT
        ip->i_flags |= S_AUTOMOUNT;
index f307990..4e00559 100644 (file)
@@ -1304,24 +1304,7 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
 
 #if defined(AFS_LINUX24_ENV)
     if (ip && S_ISDIR(ip->i_mode)) {
-       int retry = 1;
-       struct dentry *alias;
-
-       while (retry) {
-           retry = 0;
-
-           /* Try to invalidate an existing alias in favor of our new one */
-           alias = d_find_alias(ip);
-           /* But not if it's disconnected; then we want d_splice_alias below */
-           if (alias) {
-               if (d_invalidate(alias) == 0) {
-                   /* there may be more aliases; try again until we run out */
-                   retry = 1;
-               }
-           }
-
-           dput(alias);
-       }
+       d_prune_aliases(ip);
     }
 #endif
     d_add(dp, ip);