Allow d_manage() to be used in RCU-walk mode
David Howells [Fri, 14 Jan 2011 18:46:51 +0000 (18:46 +0000)]
Allow d_manage() to be called from pathwalk when it is in RCU-walk mode as well
as when it is in Ref-walk mode.  This permits __follow_mount_rcu() to call
d_manage() directly.  d_manage() needs a parameter to indicate that it is in
RCU-walk mode as it isn't allowed to sleep if in that mode (but should return
-ECHILD instead).

autofs4_d_manage() can then be set to retain RCU-walk mode if the daemon
accesses it and otherwise request dropping back to ref-walk mode.

Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

Documentation/filesystems/Locking
Documentation/filesystems/vfs.txt
fs/autofs4/root.c
fs/namei.c
include/linux/dcache.h

index cbf98b9..3970774 100644 (file)
@@ -32,7 +32,7 @@ d_release:    no              no              yes             no
 d_iput:                no              no              yes             no
 d_dname:       no              no              no              no
 d_automount:   no              no              yes             no
-d_manage:      no              no              yes             no
+d_manage:      no              no              yes (ref-walk)  maybe
 
 --------------------------- inode_operations --------------------------- 
 prototypes:
index 4682586..3c4b2f1 100644 (file)
@@ -865,7 +865,7 @@ struct dentry_operations {
        void (*d_iput)(struct dentry *, struct inode *);
        char *(*d_dname)(struct dentry *, char *, int);
        struct vfsmount *(*d_automount)(struct path *);
-       int (*d_manage)(struct dentry *, bool);
+       int (*d_manage)(struct dentry *, bool, bool);
 };
 
   d_revalidate: called when the VFS needs to revalidate a dentry. This
@@ -960,6 +960,11 @@ struct dentry_operations {
        held by the caller and the function should not initiate any mounts or
        unmounts that it will then wait for.
 
+       If the 'rcu_walk' parameter is true, then the caller is doing a
+       pathwalk in RCU-walk mode.  Sleeping is not permitted in this mode,
+       and the caller can be asked to leave it and call again by returing
+       -ECHILD.
+
        This function is only used if DCACHE_MANAGE_TRANSIT is set on the
        dentry being transited from.
 
index 9194e27..dbd9551 100644 (file)
@@ -36,7 +36,7 @@ static long autofs4_root_compat_ioctl(struct file *,unsigned int,unsigned long);
 static int autofs4_dir_open(struct inode *inode, struct file *file);
 static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *);
 static struct vfsmount *autofs4_d_automount(struct path *);
-static int autofs4_d_manage(struct dentry *, bool);
+static int autofs4_d_manage(struct dentry *, bool, bool);
 
 const struct file_operations autofs4_root_operations = {
        .open           = dcache_dir_open,
@@ -450,7 +450,7 @@ done:
        return NULL;
 }
 
-int autofs4_d_manage(struct dentry *dentry, bool mounting_here)
+int autofs4_d_manage(struct dentry *dentry, bool mounting_here, bool rcu_walk)
 {
        struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
 
@@ -464,6 +464,10 @@ int autofs4_d_manage(struct dentry *dentry, bool mounting_here)
                return 0;
        }
 
+       /* We need to sleep, so we need pathwalk to be in ref-mode */
+       if (rcu_walk)
+               return -ECHILD;
+
        /* Wait for pending expires */
        do_expire_wait(dentry);
 
index 3738520..5c89695 100644 (file)
@@ -987,7 +987,8 @@ static int follow_managed(struct path *path, unsigned flags)
                if (managed & DCACHE_MANAGE_TRANSIT) {
                        BUG_ON(!path->dentry->d_op);
                        BUG_ON(!path->dentry->d_op->d_manage);
-                       ret = path->dentry->d_op->d_manage(path->dentry, false);
+                       ret = path->dentry->d_op->d_manage(path->dentry,
+                                                          false, false);
                        if (ret < 0)
                                return ret == -EISDIR ? 0 : ret;
                }
@@ -1048,13 +1049,12 @@ int follow_down_one(struct path *path)
 static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
                               struct inode **inode, bool reverse_transit)
 {
-       unsigned abort_mask =
-               reverse_transit ? 0 : DCACHE_MANAGE_TRANSIT;
-
        while (d_mountpoint(path->dentry)) {
                struct vfsmount *mounted;
-               if (path->dentry->d_flags & abort_mask)
-                       return true;
+               if (unlikely(path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) &&
+                   !reverse_transit &&
+                   path->dentry->d_op->d_manage(path->dentry, false, true) < 0)
+                       return false;
                mounted = __lookup_mnt(path->mnt, path->dentry, 1);
                if (!mounted)
                        break;
@@ -1132,7 +1132,8 @@ int follow_down(struct path *path, bool mounting_here)
                if (managed & DCACHE_MANAGE_TRANSIT) {
                        BUG_ON(!path->dentry->d_op);
                        BUG_ON(!path->dentry->d_op->d_manage);
-                       ret = path->dentry->d_op->d_manage(path->dentry, mounting_here);
+                       ret = path->dentry->d_op->d_manage(
+                               path->dentry, mounting_here, false);
                        if (ret < 0)
                                return ret == -EISDIR ? 0 : ret;
                }
index 1a87760..f958c19 100644 (file)
@@ -168,7 +168,7 @@ struct dentry_operations {
        void (*d_iput)(struct dentry *, struct inode *);
        char *(*d_dname)(struct dentry *, char *, int);
        struct vfsmount *(*d_automount)(struct path *);
-       int (*d_manage)(struct dentry *, bool);
+       int (*d_manage)(struct dentry *, bool, bool);
 } ____cacheline_aligned;
 
 /*