Linux: When revalidating, don't drop in-use dentries
authorMarc Dionne <marc.dionne@your-file-system.com>
Thu, 30 Jan 2014 18:50:37 +0000 (13:50 -0500)
committerDerrick Brashear <shadow@your-file-system.com>
Wed, 5 Feb 2014 14:38:22 +0000 (06:38 -0800)
The Linux client can get into a state where the current working
directory is seen as "deleted" by some tools, while it is still
there and accessible to "ls" and other tools.  This has been
reported by several users and sites.

One scenario that has been observed while debugging:
- A process does a chdir() into a directory
- This stores a pointer to the dir's dentry in the task structure
- The server hosting the volume goes offline temporarily
- The dentry for the directory is passed to afs_linux_dentry_revalidate
- afs_linux_dentry_revalidate calls afs_lookup which returns an
error (110 - ETIMEDOUT)
- It then considers the dentry not valid, and calls d_drop()
- d_drop unhashes the dentry unconditionally
- Server comes back up, but dentry is still unhashed
- getcwd() fetches the task structure pointer to the current dir
dentry.  If unhashed, it returns ENOENT, and the vfs layer is
not involved at all.

At that point, many things won't work and there is no obvious way
for the user to get the directory rehashed.

Instead of calling d_drop directly, call d_invalidate instead, as
it will only drop (unhash) the dentry if we're the only one holding
a reference.  Since d_invalidate will also call shrink_dcache_parent,
also remove that call from our code so it doesn't get called twice.

Change-Id: I03e9872f6f9aebd28cdf6b833e14955edaa2527c
Reviewed-on: http://gerrit.openafs.org/10774
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Tested-by: Anders Kaseorg <andersk@mit.edu>
Reviewed-by: Derrick Brashear <shadow@your-file-system.com>

src/afs/LINUX/osi_vnodeops.c

index 28841c8..b7cae5a 100644 (file)
@@ -1262,10 +1262,9 @@ afs_linux_dentry_revalidate(struct dentry *dp, int flags)
     if (credp)
        crfree(credp);
 
-    if (!valid) {
-       shrink_dcache_parent(dp);
-       d_drop(dp);
-    }
+    if (!valid)
+       d_invalidate(dp);
+
     return valid;
 
   bad_dentry: