]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
Allow d_manage() to be used in RCU-walk mode
authorDavid Howells <dhowells@redhat.com>
Fri, 14 Jan 2011 18:46:51 +0000 (18:46 +0000)
committerAl Viro <viro@zeniv.linux.org.uk>
Sun, 16 Jan 2011 01:07:47 +0000 (20:07 -0500)
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 cbf98b989b11fa8358a9ac1965c58987012f5adf..39707748ed2d01e44f529d23fecc4774f7a5041c 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_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:
 
 --------------------------- inode_operations --------------------------- 
 prototypes:
index 4682586b147a8495f7add164662c0d2dec928d7b..3c4b2f1b64d0dc787f14fbfe31c6bc5ad61e6a06 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 *);
        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
 };
 
   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.
 
        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.
 
        This function is only used if DCACHE_MANAGE_TRANSIT is set on the
        dentry being transited from.
 
index 9194e274f849b65b570be6efd621e5974750fdac..dbd95512808c65805f2404032bf53e1b4ede5456 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_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,
 
 const struct file_operations autofs4_root_operations = {
        .open           = dcache_dir_open,
@@ -450,7 +450,7 @@ done:
        return NULL;
 }
 
        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);
 
 {
        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;
        }
 
                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);
 
        /* Wait for pending expires */
        do_expire_wait(dentry);
 
index 373852012713e11983223006a166634ebaa7e74e..5c89695ae1e47754fc4e716854b3b8f6902f8f7c 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);
                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;
                }
                        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)
 {
 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;
        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;
                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);
                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;
                }
                        if (ret < 0)
                                return ret == -EISDIR ? 0 : ret;
                }
index 1a87760d65322b2d0528886b3232d2707f725532..f958c19e3ca54f13ca47480397658e1f13b5f8b1 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 *);
        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;
 
 /*
 } ____cacheline_aligned;
 
 /*