diff options
author | David Howells <dhowells@redhat.com> | 2011-01-14 13:46:51 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-01-15 20:07:47 -0500 |
commit | ab90911ff90cdab59b31c045c3f0ae480d14f29d (patch) | |
tree | 683450a66eb9dc6bf053e38d63f4740bb53a7b6e | |
parent | 87556ef19926e97464e0163a7840140527ae6615 (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/Locking | 2 | ||||
-rw-r--r-- | Documentation/filesystems/vfs.txt | 7 | ||||
-rw-r--r-- | fs/autofs4/root.c | 8 | ||||
-rw-r--r-- | fs/namei.c | 15 | ||||
-rw-r--r-- | include/linux/dcache.h | 2 |
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 | |||
32 | d_iput: no no yes no | 32 | d_iput: no no yes no |
33 | d_dname: no no no no | 33 | d_dname: no no no no |
34 | d_automount: no no yes no | 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 | --------------------------- inode_operations --------------------------- | 37 | --------------------------- inode_operations --------------------------- |
38 | prototypes: | 38 | prototypes: |
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); | |||
36 | static int autofs4_dir_open(struct inode *inode, struct file *file); | 36 | static int autofs4_dir_open(struct inode *inode, struct file *file); |
37 | static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); | 37 | static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); |
38 | static struct vfsmount *autofs4_d_automount(struct path *); | 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 | const struct file_operations autofs4_root_operations = { | 41 | const 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 | ||
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 | 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) | |||
1048 | static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, | 1049 | static 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 | /* |