Commit ab90911ff90cdab59b31c045c3f0ae480d14f29d

Authored by David Howells
Committed by Al Viro
1 parent 87556ef199

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
... ... @@ -32,7 +32,7 @@
32 32 d_iput: no no yes no
33 33 d_dname: no no no no
34 34 d_automount: no no yes no
35   -d_manage: no no yes no
  35 +d_manage: no no yes (ref-walk) maybe
36 36  
37 37 --------------------------- inode_operations ---------------------------
38 38 prototypes:
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.
... ... @@ -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);
... ... @@ -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 /*