Commit ab90911ff90cdab59b31c045c3f0ae480d14f29d
Committed by
Al Viro
1 parent
87556ef199
Exists in
master
and in
39 other branches
Allow d_manage() to be used in RCU-walk mode
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>
Showing 5 changed files with 22 additions and 12 deletions Side-by-side Diff
Documentation/filesystems/Locking
Documentation/filesystems/vfs.txt
... | ... | @@ -865,7 +865,7 @@ |
865 | 865 | void (*d_iput)(struct dentry *, struct inode *); |
866 | 866 | char *(*d_dname)(struct dentry *, char *, int); |
867 | 867 | struct vfsmount *(*d_automount)(struct path *); |
868 | - int (*d_manage)(struct dentry *, bool); | |
868 | + int (*d_manage)(struct dentry *, bool, bool); | |
869 | 869 | }; |
870 | 870 | |
871 | 871 | d_revalidate: called when the VFS needs to revalidate a dentry. This |
... | ... | @@ -959,6 +959,11 @@ |
959 | 959 | If the 'mounting_here' parameter is true, then namespace_sem is being |
960 | 960 | held by the caller and the function should not initiate any mounts or |
961 | 961 | unmounts that it will then wait for. |
962 | + | |
963 | + If the 'rcu_walk' parameter is true, then the caller is doing a | |
964 | + pathwalk in RCU-walk mode. Sleeping is not permitted in this mode, | |
965 | + and the caller can be asked to leave it and call again by returing | |
966 | + -ECHILD. | |
962 | 967 | |
963 | 968 | This function is only used if DCACHE_MANAGE_TRANSIT is set on the |
964 | 969 | dentry being transited from. |
fs/autofs4/root.c
... | ... | @@ -36,7 +36,7 @@ |
36 | 36 | static int autofs4_dir_open(struct inode *inode, struct file *file); |
37 | 37 | static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); |
38 | 38 | static struct vfsmount *autofs4_d_automount(struct path *); |
39 | -static int autofs4_d_manage(struct dentry *, bool); | |
39 | +static int autofs4_d_manage(struct dentry *, bool, bool); | |
40 | 40 | |
41 | 41 | const struct file_operations autofs4_root_operations = { |
42 | 42 | .open = dcache_dir_open, |
... | ... | @@ -450,7 +450,7 @@ |
450 | 450 | return NULL; |
451 | 451 | } |
452 | 452 | |
453 | -int autofs4_d_manage(struct dentry *dentry, bool mounting_here) | |
453 | +int autofs4_d_manage(struct dentry *dentry, bool mounting_here, bool rcu_walk) | |
454 | 454 | { |
455 | 455 | struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); |
456 | 456 | |
... | ... | @@ -463,6 +463,10 @@ |
463 | 463 | return -EISDIR; |
464 | 464 | return 0; |
465 | 465 | } |
466 | + | |
467 | + /* We need to sleep, so we need pathwalk to be in ref-mode */ | |
468 | + if (rcu_walk) | |
469 | + return -ECHILD; | |
466 | 470 | |
467 | 471 | /* Wait for pending expires */ |
468 | 472 | do_expire_wait(dentry); |
fs/namei.c
... | ... | @@ -987,7 +987,8 @@ |
987 | 987 | if (managed & DCACHE_MANAGE_TRANSIT) { |
988 | 988 | BUG_ON(!path->dentry->d_op); |
989 | 989 | BUG_ON(!path->dentry->d_op->d_manage); |
990 | - ret = path->dentry->d_op->d_manage(path->dentry, false); | |
990 | + ret = path->dentry->d_op->d_manage(path->dentry, | |
991 | + false, false); | |
991 | 992 | if (ret < 0) |
992 | 993 | return ret == -EISDIR ? 0 : ret; |
993 | 994 | } |
994 | 995 | |
... | ... | @@ -1048,13 +1049,12 @@ |
1048 | 1049 | static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, |
1049 | 1050 | struct inode **inode, bool reverse_transit) |
1050 | 1051 | { |
1051 | - unsigned abort_mask = | |
1052 | - reverse_transit ? 0 : DCACHE_MANAGE_TRANSIT; | |
1053 | - | |
1054 | 1052 | while (d_mountpoint(path->dentry)) { |
1055 | 1053 | struct vfsmount *mounted; |
1056 | - if (path->dentry->d_flags & abort_mask) | |
1057 | - return true; | |
1054 | + if (unlikely(path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) && | |
1055 | + !reverse_transit && | |
1056 | + path->dentry->d_op->d_manage(path->dentry, false, true) < 0) | |
1057 | + return false; | |
1058 | 1058 | mounted = __lookup_mnt(path->mnt, path->dentry, 1); |
1059 | 1059 | if (!mounted) |
1060 | 1060 | break; |
... | ... | @@ -1132,7 +1132,8 @@ |
1132 | 1132 | if (managed & DCACHE_MANAGE_TRANSIT) { |
1133 | 1133 | BUG_ON(!path->dentry->d_op); |
1134 | 1134 | BUG_ON(!path->dentry->d_op->d_manage); |
1135 | - ret = path->dentry->d_op->d_manage(path->dentry, mounting_here); | |
1135 | + ret = path->dentry->d_op->d_manage( | |
1136 | + path->dentry, mounting_here, false); | |
1136 | 1137 | if (ret < 0) |
1137 | 1138 | return ret == -EISDIR ? 0 : ret; |
1138 | 1139 | } |
include/linux/dcache.h
... | ... | @@ -168,7 +168,7 @@ |
168 | 168 | void (*d_iput)(struct dentry *, struct inode *); |
169 | 169 | char *(*d_dname)(struct dentry *, char *, int); |
170 | 170 | struct vfsmount *(*d_automount)(struct path *); |
171 | - int (*d_manage)(struct dentry *, bool); | |
171 | + int (*d_manage)(struct dentry *, bool, bool); | |
172 | 172 | } ____cacheline_aligned; |
173 | 173 | |
174 | 174 | /* |