aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2011-01-14 13:46:51 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2011-01-15 20:07:47 -0500
commitab90911ff90cdab59b31c045c3f0ae480d14f29d (patch)
tree683450a66eb9dc6bf053e38d63f4740bb53a7b6e
parent87556ef19926e97464e0163a7840140527ae6615 (diff)
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>
-rw-r--r--Documentation/filesystems/Locking2
-rw-r--r--Documentation/filesystems/vfs.txt7
-rw-r--r--fs/autofs4/root.c8
-rw-r--r--fs/namei.c15
-rw-r--r--include/linux/dcache.h2
5 files changed, 22 insertions, 12 deletions
diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking
index cbf98b989b11..39707748ed2d 100644
--- a/Documentation/filesystems/Locking
+++ b/Documentation/filesystems/Locking
@@ -32,7 +32,7 @@ d_release: no no yes no
32d_iput: no no yes no 32d_iput: no no yes no
33d_dname: no no no no 33d_dname: no no no no
34d_automount: no no yes no 34d_automount: no no yes no
35d_manage: no no yes no 35d_manage: no no yes (ref-walk) maybe
36 36
37--------------------------- inode_operations --------------------------- 37--------------------------- inode_operations ---------------------------
38prototypes: 38prototypes:
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt
index 4682586b147a..3c4b2f1b64d0 100644
--- a/Documentation/filesystems/vfs.txt
+++ b/Documentation/filesystems/vfs.txt
@@ -865,7 +865,7 @@ struct dentry_operations {
865 void (*d_iput)(struct dentry *, struct inode *); 865 void (*d_iput)(struct dentry *, struct inode *);
866 char *(*d_dname)(struct dentry *, char *, int); 866 char *(*d_dname)(struct dentry *, char *, int);
867 struct vfsmount *(*d_automount)(struct path *); 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 d_revalidate: called when the VFS needs to revalidate a dentry. This 871 d_revalidate: called when the VFS needs to revalidate a dentry. This
@@ -960,6 +960,11 @@ struct dentry_operations {
960 held by the caller and the function should not initiate any mounts or 960 held by the caller and the function should not initiate any mounts or
961 unmounts that it will then wait for. 961 unmounts that it will then wait for.
962 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.
967
963 This function is only used if DCACHE_MANAGE_TRANSIT is set on the 968 This function is only used if DCACHE_MANAGE_TRANSIT is set on the
964 dentry being transited from. 969 dentry being transited from.
965 970
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index 9194e274f849..dbd95512808c 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -36,7 +36,7 @@ static long autofs4_root_compat_ioctl(struct file *,unsigned int,unsigned long);
36static int autofs4_dir_open(struct inode *inode, struct file *file); 36static int autofs4_dir_open(struct inode *inode, struct file *file);
37static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); 37static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *);
38static struct vfsmount *autofs4_d_automount(struct path *); 38static struct vfsmount *autofs4_d_automount(struct path *);
39static int autofs4_d_manage(struct dentry *, bool); 39static int autofs4_d_manage(struct dentry *, bool, bool);
40 40
41const struct file_operations autofs4_root_operations = { 41const struct file_operations autofs4_root_operations = {
42 .open = dcache_dir_open, 42 .open = dcache_dir_open,
@@ -450,7 +450,7 @@ done:
450 return NULL; 450 return NULL;
451} 451}
452 452
453int autofs4_d_manage(struct dentry *dentry, bool mounting_here) 453int autofs4_d_manage(struct dentry *dentry, bool mounting_here, bool rcu_walk)
454{ 454{
455 struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); 455 struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
456 456
@@ -464,6 +464,10 @@ int autofs4_d_manage(struct dentry *dentry, bool mounting_here)
464 return 0; 464 return 0;
465 } 465 }
466 466
467 /* We need to sleep, so we need pathwalk to be in ref-mode */
468 if (rcu_walk)
469 return -ECHILD;
470
467 /* Wait for pending expires */ 471 /* Wait for pending expires */
468 do_expire_wait(dentry); 472 do_expire_wait(dentry);
469 473
diff --git a/fs/namei.c b/fs/namei.c
index 373852012713..5c89695ae1e4 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -987,7 +987,8 @@ static int follow_managed(struct path *path, unsigned flags)
987 if (managed & DCACHE_MANAGE_TRANSIT) { 987 if (managed & DCACHE_MANAGE_TRANSIT) {
988 BUG_ON(!path->dentry->d_op); 988 BUG_ON(!path->dentry->d_op);
989 BUG_ON(!path->dentry->d_op->d_manage); 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 if (ret < 0) 992 if (ret < 0)
992 return ret == -EISDIR ? 0 : ret; 993 return ret == -EISDIR ? 0 : ret;
993 } 994 }
@@ -1048,13 +1049,12 @@ int follow_down_one(struct path *path)
1048static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, 1049static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
1049 struct inode **inode, bool reverse_transit) 1050 struct inode **inode, bool reverse_transit)
1050{ 1051{
1051 unsigned abort_mask =
1052 reverse_transit ? 0 : DCACHE_MANAGE_TRANSIT;
1053
1054 while (d_mountpoint(path->dentry)) { 1052 while (d_mountpoint(path->dentry)) {
1055 struct vfsmount *mounted; 1053 struct vfsmount *mounted;
1056 if (path->dentry->d_flags & abort_mask) 1054 if (unlikely(path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) &&
1057 return true; 1055 !reverse_transit &&
1056 path->dentry->d_op->d_manage(path->dentry, false, true) < 0)
1057 return false;
1058 mounted = __lookup_mnt(path->mnt, path->dentry, 1); 1058 mounted = __lookup_mnt(path->mnt, path->dentry, 1);
1059 if (!mounted) 1059 if (!mounted)
1060 break; 1060 break;
@@ -1132,7 +1132,8 @@ int follow_down(struct path *path, bool mounting_here)
1132 if (managed & DCACHE_MANAGE_TRANSIT) { 1132 if (managed & DCACHE_MANAGE_TRANSIT) {
1133 BUG_ON(!path->dentry->d_op); 1133 BUG_ON(!path->dentry->d_op);
1134 BUG_ON(!path->dentry->d_op->d_manage); 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 if (ret < 0) 1137 if (ret < 0)
1137 return ret == -EISDIR ? 0 : ret; 1138 return ret == -EISDIR ? 0 : ret;
1138 } 1139 }
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 1a87760d6532..f958c19e3ca5 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -168,7 +168,7 @@ struct dentry_operations {
168 void (*d_iput)(struct dentry *, struct inode *); 168 void (*d_iput)(struct dentry *, struct inode *);
169 char *(*d_dname)(struct dentry *, char *, int); 169 char *(*d_dname)(struct dentry *, char *, int);
170 struct vfsmount *(*d_automount)(struct path *); 170 struct vfsmount *(*d_automount)(struct path *);
171 int (*d_manage)(struct dentry *, bool); 171 int (*d_manage)(struct dentry *, bool, bool);
172} ____cacheline_aligned; 172} ____cacheline_aligned;
173 173
174/* 174/*