From 370aaaeafa43f804b0a5286d92b4ec5f1ccb62be Mon Sep 17 00:00:00 2001 From: Andrew Deason Date: Wed, 12 Dec 2012 16:14:55 -0600 Subject: [PATCH] LINUX: Avoid infinite d_invalidate loop 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 Reviewed-by: Chas Williams - CONTRACTOR Reviewed-by: Marc Dionne Reviewed-by: Derrick Brashear --- src/afs/LINUX/osi_vnodeops.c | 19 +------------------ src/afs/LINUX24/osi_vnodeops.c | 19 +------------------ 2 files changed, 2 insertions(+), 36 deletions(-) diff --git a/src/afs/LINUX/osi_vnodeops.c b/src/afs/LINUX/osi_vnodeops.c index de1a7039d1..f37a098b23 100644 --- a/src/afs/LINUX/osi_vnodeops.c +++ b/src/afs/LINUX/osi_vnodeops.c @@ -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; diff --git a/src/afs/LINUX24/osi_vnodeops.c b/src/afs/LINUX24/osi_vnodeops.c index f3079903a3..4e00559460 100644 --- a/src/afs/LINUX24/osi_vnodeops.c +++ b/src/afs/LINUX24/osi_vnodeops.c @@ -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);