diff options
author | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 01:49:57 -0500 |
---|---|---|
committer | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 01:50:29 -0500 |
commit | 34286d6662308d82aed891852d04c7c3a2649b16 (patch) | |
tree | c4b7311404d302e7cb94df7a4690298e1059910a | |
parent | 44a7d7a878c9cbb74f236ea755b25b6b2e26a9a9 (diff) |
fs: rcu-walk aware d_revalidate method
Require filesystems be aware of .d_revalidate being called in rcu-walk
mode (nd->flags & LOOKUP_RCU). For now do a simple push down, returning
-ECHILD from all implementations.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
-rw-r--r-- | Documentation/filesystems/Locking | 18 | ||||
-rw-r--r-- | Documentation/filesystems/path-lookup.txt | 5 | ||||
-rw-r--r-- | Documentation/filesystems/porting | 20 | ||||
-rw-r--r-- | Documentation/filesystems/vfs.txt | 9 | ||||
-rw-r--r-- | drivers/staging/autofs/root.c | 5 | ||||
-rw-r--r-- | drivers/staging/smbfs/dir.c | 16 | ||||
-rw-r--r-- | fs/afs/dir.c | 4 | ||||
-rw-r--r-- | fs/autofs4/root.c | 13 | ||||
-rw-r--r-- | fs/ceph/dir.c | 7 | ||||
-rw-r--r-- | fs/cifs/dir.c | 3 | ||||
-rw-r--r-- | fs/coda/dir.c | 7 | ||||
-rw-r--r-- | fs/ecryptfs/dentry.c | 9 | ||||
-rw-r--r-- | fs/fat/namei_vfat.c | 6 | ||||
-rw-r--r-- | fs/fuse/dir.c | 6 | ||||
-rw-r--r-- | fs/gfs2/dentry.c | 17 | ||||
-rw-r--r-- | fs/hfs/sysdep.c | 7 | ||||
-rw-r--r-- | fs/jfs/namei.c | 2 | ||||
-rw-r--r-- | fs/namei.c | 54 | ||||
-rw-r--r-- | fs/ncpfs/dir.c | 4 | ||||
-rw-r--r-- | fs/ncpfs/inode.c | 1 | ||||
-rw-r--r-- | fs/nfs/dir.c | 8 | ||||
-rw-r--r-- | fs/ocfs2/dcache.c | 10 | ||||
-rw-r--r-- | fs/proc/base.c | 33 | ||||
-rw-r--r-- | fs/proc/proc_sysctl.c | 3 | ||||
-rw-r--r-- | fs/reiserfs/xattr.c | 2 | ||||
-rw-r--r-- | fs/sysfs/dir.c | 6 | ||||
-rw-r--r-- | include/linux/dcache.h | 1 |
27 files changed, 215 insertions, 61 deletions
diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index bdad6414dfa0..e90ffe61eb65 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking | |||
@@ -9,7 +9,7 @@ be able to use diff(1). | |||
9 | 9 | ||
10 | --------------------------- dentry_operations -------------------------- | 10 | --------------------------- dentry_operations -------------------------- |
11 | prototypes: | 11 | prototypes: |
12 | int (*d_revalidate)(struct dentry *, int); | 12 | int (*d_revalidate)(struct dentry *, struct nameidata *); |
13 | int (*d_hash)(const struct dentry *, const struct inode *, | 13 | int (*d_hash)(const struct dentry *, const struct inode *, |
14 | struct qstr *); | 14 | struct qstr *); |
15 | int (*d_compare)(const struct dentry *, const struct inode *, | 15 | int (*d_compare)(const struct dentry *, const struct inode *, |
@@ -21,14 +21,14 @@ prototypes: | |||
21 | char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen); | 21 | char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen); |
22 | 22 | ||
23 | locking rules: | 23 | locking rules: |
24 | rename_lock ->d_lock may block | 24 | rename_lock ->d_lock may block rcu-walk |
25 | d_revalidate: no no yes | 25 | d_revalidate: no no yes (ref-walk) maybe |
26 | d_hash no no no | 26 | d_hash no no no maybe |
27 | d_compare: yes no no | 27 | d_compare: yes no no maybe |
28 | d_delete: no yes no | 28 | d_delete: no yes no no |
29 | d_release: no no yes | 29 | d_release: no no yes no |
30 | d_iput: no no yes | 30 | d_iput: no no yes no |
31 | d_dname: no no no | 31 | d_dname: no no no no |
32 | 32 | ||
33 | --------------------------- inode_operations --------------------------- | 33 | --------------------------- inode_operations --------------------------- |
34 | prototypes: | 34 | prototypes: |
diff --git a/Documentation/filesystems/path-lookup.txt b/Documentation/filesystems/path-lookup.txt index 09b2878724a1..8789d1810bed 100644 --- a/Documentation/filesystems/path-lookup.txt +++ b/Documentation/filesystems/path-lookup.txt | |||
@@ -317,11 +317,10 @@ The detailed design for rcu-walk is like this: | |||
317 | The cases where rcu-walk cannot continue are: | 317 | The cases where rcu-walk cannot continue are: |
318 | * NULL dentry (ie. any uncached path element) | 318 | * NULL dentry (ie. any uncached path element) |
319 | * parent with d_inode->i_op->permission or ACLs | 319 | * parent with d_inode->i_op->permission or ACLs |
320 | * dentries with d_revalidate | ||
321 | * Following links | 320 | * Following links |
322 | 321 | ||
323 | In future patches, permission checks and d_revalidate become rcu-walk aware. It | 322 | In future patches, permission checks become rcu-walk aware. It may be possible |
324 | may be possible eventually to make following links rcu-walk aware. | 323 | eventually to make following links rcu-walk aware. |
325 | 324 | ||
326 | Uncached path elements will always require dropping to ref-walk mode, at the | 325 | Uncached path elements will always require dropping to ref-walk mode, at the |
327 | very least because i_mutex needs to be grabbed, and objects allocated. | 326 | very least because i_mutex needs to be grabbed, and objects allocated. |
diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index ccf0ce7866b9..cd9756a2709d 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting | |||
@@ -360,3 +360,23 @@ i_dentry to be reinitialized before it is freed, so an: | |||
360 | INIT_LIST_HEAD(&inode->i_dentry); | 360 | INIT_LIST_HEAD(&inode->i_dentry); |
361 | 361 | ||
362 | must be done in the RCU callback. | 362 | must be done in the RCU callback. |
363 | |||
364 | -- | ||
365 | [recommended] | ||
366 | vfs now tries to do path walking in "rcu-walk mode", which avoids | ||
367 | atomic operations and scalability hazards on dentries and inodes (see | ||
368 | Documentation/filesystems/path-walk.txt). d_hash and d_compare changes (above) | ||
369 | are examples of the changes required to support this. For more complex | ||
370 | filesystem callbacks, the vfs drops out of rcu-walk mode before the fs call, so | ||
371 | no changes are required to the filesystem. However, this is costly and loses | ||
372 | the benefits of rcu-walk mode. We will begin to add filesystem callbacks that | ||
373 | are rcu-walk aware, shown below. Filesystems should take advantage of this | ||
374 | where possible. | ||
375 | |||
376 | -- | ||
377 | [mandatory] | ||
378 | d_revalidate is a callback that is made on every path element (if | ||
379 | the filesystem provides it), which requires dropping out of rcu-walk mode. This | ||
380 | may now be called in rcu-walk mode (nd->flags & LOOKUP_RCU). -ECHILD should be | ||
381 | returned if the filesystem cannot handle rcu-walk. See | ||
382 | Documentation/filesystems/vfs.txt for more details. | ||
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 69b10ff5ec81..c936b4912383 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt | |||
@@ -863,6 +863,15 @@ struct dentry_operations { | |||
863 | dcache. Most filesystems leave this as NULL, because all their | 863 | dcache. Most filesystems leave this as NULL, because all their |
864 | dentries in the dcache are valid | 864 | dentries in the dcache are valid |
865 | 865 | ||
866 | d_revalidate may be called in rcu-walk mode (nd->flags & LOOKUP_RCU). | ||
867 | If in rcu-walk mode, the filesystem must revalidate the dentry without | ||
868 | blocking or storing to the dentry, d_parent and d_inode should not be | ||
869 | used without care (because they can go NULL), instead nd->inode should | ||
870 | be used. | ||
871 | |||
872 | If a situation is encountered that rcu-walk cannot handle, return | ||
873 | -ECHILD and it will be called again in ref-walk mode. | ||
874 | |||
866 | d_hash: called when the VFS adds a dentry to the hash table. The first | 875 | d_hash: called when the VFS adds a dentry to the hash table. The first |
867 | dentry passed to d_hash is the parent directory that the name is | 876 | dentry passed to d_hash is the parent directory that the name is |
868 | to be hashed into. The inode is the dentry's inode. | 877 | to be hashed into. The inode is the dentry's inode. |
diff --git a/drivers/staging/autofs/root.c b/drivers/staging/autofs/root.c index b09adb57971f..bf0e9755da67 100644 --- a/drivers/staging/autofs/root.c +++ b/drivers/staging/autofs/root.c | |||
@@ -154,13 +154,16 @@ static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, str | |||
154 | * yet completely filled in, and revalidate has to delay such | 154 | * yet completely filled in, and revalidate has to delay such |
155 | * lookups.. | 155 | * lookups.. |
156 | */ | 156 | */ |
157 | static int autofs_revalidate(struct dentry * dentry, struct nameidata *nd) | 157 | static int autofs_revalidate(struct dentry *dentry, struct nameidata *nd) |
158 | { | 158 | { |
159 | struct inode * dir; | 159 | struct inode * dir; |
160 | struct autofs_sb_info *sbi; | 160 | struct autofs_sb_info *sbi; |
161 | struct autofs_dir_ent *ent; | 161 | struct autofs_dir_ent *ent; |
162 | int res; | 162 | int res; |
163 | 163 | ||
164 | if (nd->flags & LOOKUP_RCU) | ||
165 | return -ECHILD; | ||
166 | |||
164 | lock_kernel(); | 167 | lock_kernel(); |
165 | dir = dentry->d_parent->d_inode; | 168 | dir = dentry->d_parent->d_inode; |
166 | sbi = autofs_sbi(dir->i_sb); | 169 | sbi = autofs_sbi(dir->i_sb); |
diff --git a/drivers/staging/smbfs/dir.c b/drivers/staging/smbfs/dir.c index 78f09412740c..dd612f50749f 100644 --- a/drivers/staging/smbfs/dir.c +++ b/drivers/staging/smbfs/dir.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/ctype.h> | 14 | #include <linux/ctype.h> |
15 | #include <linux/net.h> | 15 | #include <linux/net.h> |
16 | #include <linux/sched.h> | 16 | #include <linux/sched.h> |
17 | #include <linux/namei.h> | ||
17 | 18 | ||
18 | #include "smb_fs.h" | 19 | #include "smb_fs.h" |
19 | #include "smb_mount.h" | 20 | #include "smb_mount.h" |
@@ -301,13 +302,20 @@ static const struct dentry_operations smbfs_dentry_operations_case = | |||
301 | * This is the callback when the dcache has a lookup hit. | 302 | * This is the callback when the dcache has a lookup hit. |
302 | */ | 303 | */ |
303 | static int | 304 | static int |
304 | smb_lookup_validate(struct dentry * dentry, struct nameidata *nd) | 305 | smb_lookup_validate(struct dentry *dentry, struct nameidata *nd) |
305 | { | 306 | { |
306 | struct smb_sb_info *server = server_from_dentry(dentry); | 307 | struct smb_sb_info *server; |
307 | struct inode * inode = dentry->d_inode; | 308 | struct inode *inode; |
308 | unsigned long age = jiffies - dentry->d_time; | 309 | unsigned long age; |
309 | int valid; | 310 | int valid; |
310 | 311 | ||
312 | if (nd->flags & LOOKUP_RCU) | ||
313 | return -ECHILD; | ||
314 | |||
315 | server = server_from_dentry(dentry); | ||
316 | inode = dentry->d_inode; | ||
317 | age = jiffies - dentry->d_time; | ||
318 | |||
311 | /* | 319 | /* |
312 | * The default validation is based on dentry age: | 320 | * The default validation is based on dentry age: |
313 | * we believe in dentries for a few seconds. (But each | 321 | * we believe in dentries for a few seconds. (But each |
diff --git a/fs/afs/dir.c b/fs/afs/dir.c index b8bb7e7148d0..34a3263d60a4 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
15 | #include <linux/fs.h> | 15 | #include <linux/fs.h> |
16 | #include <linux/namei.h> | ||
16 | #include <linux/pagemap.h> | 17 | #include <linux/pagemap.h> |
17 | #include <linux/ctype.h> | 18 | #include <linux/ctype.h> |
18 | #include <linux/sched.h> | 19 | #include <linux/sched.h> |
@@ -607,6 +608,9 @@ static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
607 | void *dir_version; | 608 | void *dir_version; |
608 | int ret; | 609 | int ret; |
609 | 610 | ||
611 | if (nd->flags & LOOKUP_RCU) | ||
612 | return -ECHILD; | ||
613 | |||
610 | vnode = AFS_FS_I(dentry->d_inode); | 614 | vnode = AFS_FS_I(dentry->d_inode); |
611 | 615 | ||
612 | if (dentry->d_inode) | 616 | if (dentry->d_inode) |
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index bfe3f2eb684d..651e4ef563b1 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c | |||
@@ -315,12 +315,19 @@ out_error: | |||
315 | */ | 315 | */ |
316 | static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) | 316 | static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) |
317 | { | 317 | { |
318 | struct inode *dir = dentry->d_parent->d_inode; | 318 | struct inode *dir; |
319 | struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); | 319 | struct autofs_sb_info *sbi; |
320 | int oz_mode = autofs4_oz_mode(sbi); | 320 | int oz_mode; |
321 | int flags = nd ? nd->flags : 0; | 321 | int flags = nd ? nd->flags : 0; |
322 | int status = 1; | 322 | int status = 1; |
323 | 323 | ||
324 | if (flags & LOOKUP_RCU) | ||
325 | return -ECHILD; | ||
326 | |||
327 | dir = dentry->d_parent->d_inode; | ||
328 | sbi = autofs4_sbi(dir->i_sb); | ||
329 | oz_mode = autofs4_oz_mode(sbi); | ||
330 | |||
324 | /* Pending dentry */ | 331 | /* Pending dentry */ |
325 | spin_lock(&sbi->fs_lock); | 332 | spin_lock(&sbi->fs_lock); |
326 | if (autofs4_ispending(dentry)) { | 333 | if (autofs4_ispending(dentry)) { |
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index cc01cf826769..fa7ca04ee816 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c | |||
@@ -990,7 +990,12 @@ static int dir_lease_is_valid(struct inode *dir, struct dentry *dentry) | |||
990 | */ | 990 | */ |
991 | static int ceph_d_revalidate(struct dentry *dentry, struct nameidata *nd) | 991 | static int ceph_d_revalidate(struct dentry *dentry, struct nameidata *nd) |
992 | { | 992 | { |
993 | struct inode *dir = dentry->d_parent->d_inode; | 993 | struct inode *dir; |
994 | |||
995 | if (nd->flags & LOOKUP_RCU) | ||
996 | return -ECHILD; | ||
997 | |||
998 | dir = dentry->d_parent->d_inode; | ||
994 | 999 | ||
995 | dout("d_revalidate %p '%.*s' inode %p offset %lld\n", dentry, | 1000 | dout("d_revalidate %p '%.*s' inode %p offset %lld\n", dentry, |
996 | dentry->d_name.len, dentry->d_name.name, dentry->d_inode, | 1001 | dentry->d_name.len, dentry->d_name.name, dentry->d_inode, |
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index e3b10ca6d453..db2a58c00f7b 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c | |||
@@ -656,6 +656,9 @@ lookup_out: | |||
656 | static int | 656 | static int |
657 | cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd) | 657 | cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd) |
658 | { | 658 | { |
659 | if (nd->flags & LOOKUP_RCU) | ||
660 | return -ECHILD; | ||
661 | |||
659 | if (direntry->d_inode) { | 662 | if (direntry->d_inode) { |
660 | if (cifs_revalidate_dentry(direntry)) | 663 | if (cifs_revalidate_dentry(direntry)) |
661 | return 0; | 664 | return 0; |
diff --git a/fs/coda/dir.c b/fs/coda/dir.c index aa40c811f8d2..619a8303766e 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/errno.h> | 18 | #include <linux/errno.h> |
19 | #include <linux/string.h> | 19 | #include <linux/string.h> |
20 | #include <linux/spinlock.h> | 20 | #include <linux/spinlock.h> |
21 | #include <linux/namei.h> | ||
21 | 22 | ||
22 | #include <asm/uaccess.h> | 23 | #include <asm/uaccess.h> |
23 | 24 | ||
@@ -541,9 +542,13 @@ out: | |||
541 | /* called when a cache lookup succeeds */ | 542 | /* called when a cache lookup succeeds */ |
542 | static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd) | 543 | static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd) |
543 | { | 544 | { |
544 | struct inode *inode = de->d_inode; | 545 | struct inode *inode; |
545 | struct coda_inode_info *cii; | 546 | struct coda_inode_info *cii; |
546 | 547 | ||
548 | if (nd->flags & LOOKUP_RCU) | ||
549 | return -ECHILD; | ||
550 | |||
551 | inode = de->d_inode; | ||
547 | if (!inode || coda_isroot(inode)) | 552 | if (!inode || coda_isroot(inode)) |
548 | goto out; | 553 | goto out; |
549 | if (is_bad_inode(inode)) | 554 | if (is_bad_inode(inode)) |
diff --git a/fs/ecryptfs/dentry.c b/fs/ecryptfs/dentry.c index 906e803f7f79..6fc4f319b550 100644 --- a/fs/ecryptfs/dentry.c +++ b/fs/ecryptfs/dentry.c | |||
@@ -44,12 +44,17 @@ | |||
44 | */ | 44 | */ |
45 | static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) | 45 | static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) |
46 | { | 46 | { |
47 | struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); | 47 | struct dentry *lower_dentry; |
48 | struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); | 48 | struct vfsmount *lower_mnt; |
49 | struct dentry *dentry_save; | 49 | struct dentry *dentry_save; |
50 | struct vfsmount *vfsmount_save; | 50 | struct vfsmount *vfsmount_save; |
51 | int rc = 1; | 51 | int rc = 1; |
52 | 52 | ||
53 | if (nd->flags & LOOKUP_RCU) | ||
54 | return -ECHILD; | ||
55 | |||
56 | lower_dentry = ecryptfs_dentry_to_lower(dentry); | ||
57 | lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); | ||
53 | if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate) | 58 | if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate) |
54 | goto out; | 59 | goto out; |
55 | dentry_save = nd->path.dentry; | 60 | dentry_save = nd->path.dentry; |
diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index 3be5ed7d859f..e3ffc5e12332 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c | |||
@@ -43,6 +43,9 @@ static int vfat_revalidate_shortname(struct dentry *dentry) | |||
43 | 43 | ||
44 | static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd) | 44 | static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd) |
45 | { | 45 | { |
46 | if (nd->flags & LOOKUP_RCU) | ||
47 | return -ECHILD; | ||
48 | |||
46 | /* This is not negative dentry. Always valid. */ | 49 | /* This is not negative dentry. Always valid. */ |
47 | if (dentry->d_inode) | 50 | if (dentry->d_inode) |
48 | return 1; | 51 | return 1; |
@@ -51,6 +54,9 @@ static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
51 | 54 | ||
52 | static int vfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd) | 55 | static int vfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd) |
53 | { | 56 | { |
57 | if (nd->flags & LOOKUP_RCU) | ||
58 | return -ECHILD; | ||
59 | |||
54 | /* | 60 | /* |
55 | * This is not negative dentry. Always valid. | 61 | * This is not negative dentry. Always valid. |
56 | * | 62 | * |
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index c9a8a426a395..07f4b5e675fc 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -156,8 +156,12 @@ u64 fuse_get_attr_version(struct fuse_conn *fc) | |||
156 | */ | 156 | */ |
157 | static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | 157 | static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) |
158 | { | 158 | { |
159 | struct inode *inode = entry->d_inode; | 159 | struct inode *inode; |
160 | |||
161 | if (nd->flags & LOOKUP_RCU) | ||
162 | return -ECHILD; | ||
160 | 163 | ||
164 | inode = entry->d_inode; | ||
161 | if (inode && is_bad_inode(inode)) | 165 | if (inode && is_bad_inode(inode)) |
162 | return 0; | 166 | return 0; |
163 | else if (fuse_dentry_time(entry) < get_jiffies_64()) { | 167 | else if (fuse_dentry_time(entry) < get_jiffies_64()) { |
diff --git a/fs/gfs2/dentry.c b/fs/gfs2/dentry.c index 50497f65763b..4a456338b873 100644 --- a/fs/gfs2/dentry.c +++ b/fs/gfs2/dentry.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/completion.h> | 11 | #include <linux/completion.h> |
12 | #include <linux/buffer_head.h> | 12 | #include <linux/buffer_head.h> |
13 | #include <linux/gfs2_ondisk.h> | 13 | #include <linux/gfs2_ondisk.h> |
14 | #include <linux/namei.h> | ||
14 | #include <linux/crc32.h> | 15 | #include <linux/crc32.h> |
15 | 16 | ||
16 | #include "gfs2.h" | 17 | #include "gfs2.h" |
@@ -34,15 +35,23 @@ | |||
34 | 35 | ||
35 | static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd) | 36 | static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd) |
36 | { | 37 | { |
37 | struct dentry *parent = dget_parent(dentry); | 38 | struct dentry *parent; |
38 | struct gfs2_sbd *sdp = GFS2_SB(parent->d_inode); | 39 | struct gfs2_sbd *sdp; |
39 | struct gfs2_inode *dip = GFS2_I(parent->d_inode); | 40 | struct gfs2_inode *dip; |
40 | struct inode *inode = dentry->d_inode; | 41 | struct inode *inode; |
41 | struct gfs2_holder d_gh; | 42 | struct gfs2_holder d_gh; |
42 | struct gfs2_inode *ip = NULL; | 43 | struct gfs2_inode *ip = NULL; |
43 | int error; | 44 | int error; |
44 | int had_lock = 0; | 45 | int had_lock = 0; |
45 | 46 | ||
47 | if (nd->flags & LOOKUP_RCU) | ||
48 | return -ECHILD; | ||
49 | |||
50 | parent = dget_parent(dentry); | ||
51 | sdp = GFS2_SB(parent->d_inode); | ||
52 | dip = GFS2_I(parent->d_inode); | ||
53 | inode = dentry->d_inode; | ||
54 | |||
46 | if (inode) { | 55 | if (inode) { |
47 | if (is_bad_inode(inode)) | 56 | if (is_bad_inode(inode)) |
48 | goto invalid; | 57 | goto invalid; |
diff --git a/fs/hfs/sysdep.c b/fs/hfs/sysdep.c index 7478f5c219aa..19cf291eb91f 100644 --- a/fs/hfs/sysdep.c +++ b/fs/hfs/sysdep.c | |||
@@ -8,15 +8,20 @@ | |||
8 | * This file contains the code to do various system dependent things. | 8 | * This file contains the code to do various system dependent things. |
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <linux/namei.h> | ||
11 | #include "hfs_fs.h" | 12 | #include "hfs_fs.h" |
12 | 13 | ||
13 | /* dentry case-handling: just lowercase everything */ | 14 | /* dentry case-handling: just lowercase everything */ |
14 | 15 | ||
15 | static int hfs_revalidate_dentry(struct dentry *dentry, struct nameidata *nd) | 16 | static int hfs_revalidate_dentry(struct dentry *dentry, struct nameidata *nd) |
16 | { | 17 | { |
17 | struct inode *inode = dentry->d_inode; | 18 | struct inode *inode; |
18 | int diff; | 19 | int diff; |
19 | 20 | ||
21 | if (nd->flags & LOOKUP_RCU) | ||
22 | return -ECHILD; | ||
23 | |||
24 | inode = dentry->d_inode; | ||
20 | if(!inode) | 25 | if(!inode) |
21 | return 1; | 26 | return 1; |
22 | 27 | ||
diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index a151cbdec626..4414e3a42264 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c | |||
@@ -1608,6 +1608,8 @@ out: | |||
1608 | 1608 | ||
1609 | static int jfs_ci_revalidate(struct dentry *dentry, struct nameidata *nd) | 1609 | static int jfs_ci_revalidate(struct dentry *dentry, struct nameidata *nd) |
1610 | { | 1610 | { |
1611 | if (nd->flags & LOOKUP_RCU) | ||
1612 | return -ECHILD; | ||
1611 | /* | 1613 | /* |
1612 | * This is not negative dentry. Always valid. | 1614 | * This is not negative dentry. Always valid. |
1613 | * | 1615 | * |
diff --git a/fs/namei.c b/fs/namei.c index 90bd2873e117..6e275363e89d 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -563,10 +563,26 @@ void release_open_intent(struct nameidata *nd) | |||
563 | fput(nd->intent.open.file); | 563 | fput(nd->intent.open.file); |
564 | } | 564 | } |
565 | 565 | ||
566 | static int d_revalidate(struct dentry *dentry, struct nameidata *nd) | ||
567 | { | ||
568 | int status; | ||
569 | |||
570 | status = dentry->d_op->d_revalidate(dentry, nd); | ||
571 | if (status == -ECHILD) { | ||
572 | if (nameidata_dentry_drop_rcu(nd, dentry)) | ||
573 | return status; | ||
574 | status = dentry->d_op->d_revalidate(dentry, nd); | ||
575 | } | ||
576 | |||
577 | return status; | ||
578 | } | ||
579 | |||
566 | static inline struct dentry * | 580 | static inline struct dentry * |
567 | do_revalidate(struct dentry *dentry, struct nameidata *nd) | 581 | do_revalidate(struct dentry *dentry, struct nameidata *nd) |
568 | { | 582 | { |
569 | int status = dentry->d_op->d_revalidate(dentry, nd); | 583 | int status; |
584 | |||
585 | status = d_revalidate(dentry, nd); | ||
570 | if (unlikely(status <= 0)) { | 586 | if (unlikely(status <= 0)) { |
571 | /* | 587 | /* |
572 | * The dentry failed validation. | 588 | * The dentry failed validation. |
@@ -574,14 +590,20 @@ do_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
574 | * the dentry otherwise d_revalidate is asking us | 590 | * the dentry otherwise d_revalidate is asking us |
575 | * to return a fail status. | 591 | * to return a fail status. |
576 | */ | 592 | */ |
577 | if (!status) { | 593 | if (status < 0) { |
594 | /* If we're in rcu-walk, we don't have a ref */ | ||
595 | if (!(nd->flags & LOOKUP_RCU)) | ||
596 | dput(dentry); | ||
597 | dentry = ERR_PTR(status); | ||
598 | |||
599 | } else { | ||
600 | /* Don't d_invalidate in rcu-walk mode */ | ||
601 | if (nameidata_dentry_drop_rcu_maybe(nd, dentry)) | ||
602 | return ERR_PTR(-ECHILD); | ||
578 | if (!d_invalidate(dentry)) { | 603 | if (!d_invalidate(dentry)) { |
579 | dput(dentry); | 604 | dput(dentry); |
580 | dentry = NULL; | 605 | dentry = NULL; |
581 | } | 606 | } |
582 | } else { | ||
583 | dput(dentry); | ||
584 | dentry = ERR_PTR(status); | ||
585 | } | 607 | } |
586 | } | 608 | } |
587 | return dentry; | 609 | return dentry; |
@@ -626,7 +648,7 @@ force_reval_path(struct path *path, struct nameidata *nd) | |||
626 | if (!need_reval_dot(dentry)) | 648 | if (!need_reval_dot(dentry)) |
627 | return 0; | 649 | return 0; |
628 | 650 | ||
629 | status = dentry->d_op->d_revalidate(dentry, nd); | 651 | status = d_revalidate(dentry, nd); |
630 | if (status > 0) | 652 | if (status > 0) |
631 | return 0; | 653 | return 0; |
632 | 654 | ||
@@ -1039,12 +1061,8 @@ static int do_lookup(struct nameidata *nd, struct qstr *name, | |||
1039 | return -ECHILD; | 1061 | return -ECHILD; |
1040 | 1062 | ||
1041 | nd->seq = seq; | 1063 | nd->seq = seq; |
1042 | if (dentry->d_flags & DCACHE_OP_REVALIDATE) { | 1064 | if (dentry->d_flags & DCACHE_OP_REVALIDATE) |
1043 | /* We commonly drop rcu-walk here */ | ||
1044 | if (nameidata_dentry_drop_rcu(nd, dentry)) | ||
1045 | return -ECHILD; | ||
1046 | goto need_revalidate; | 1065 | goto need_revalidate; |
1047 | } | ||
1048 | path->mnt = mnt; | 1066 | path->mnt = mnt; |
1049 | path->dentry = dentry; | 1067 | path->dentry = dentry; |
1050 | __follow_mount_rcu(nd, path, inode); | 1068 | __follow_mount_rcu(nd, path, inode); |
@@ -1292,12 +1310,11 @@ return_reval: | |||
1292 | * We may need to check the cached dentry for staleness. | 1310 | * We may need to check the cached dentry for staleness. |
1293 | */ | 1311 | */ |
1294 | if (need_reval_dot(nd->path.dentry)) { | 1312 | if (need_reval_dot(nd->path.dentry)) { |
1295 | if (nameidata_drop_rcu_maybe(nd)) | ||
1296 | return -ECHILD; | ||
1297 | err = -ESTALE; | ||
1298 | /* Note: we do not d_invalidate() */ | 1313 | /* Note: we do not d_invalidate() */ |
1299 | if (!nd->path.dentry->d_op->d_revalidate( | 1314 | err = d_revalidate(nd->path.dentry, nd); |
1300 | nd->path.dentry, nd)) | 1315 | if (!err) |
1316 | err = -ESTALE; | ||
1317 | if (err < 0) | ||
1301 | break; | 1318 | break; |
1302 | } | 1319 | } |
1303 | return_base: | 1320 | return_base: |
@@ -2080,10 +2097,11 @@ static struct file *do_last(struct nameidata *nd, struct path *path, | |||
2080 | dir = nd->path.dentry; | 2097 | dir = nd->path.dentry; |
2081 | case LAST_DOT: | 2098 | case LAST_DOT: |
2082 | if (need_reval_dot(dir)) { | 2099 | if (need_reval_dot(dir)) { |
2083 | if (!dir->d_op->d_revalidate(dir, nd)) { | 2100 | error = d_revalidate(nd->path.dentry, nd); |
2101 | if (!error) | ||
2084 | error = -ESTALE; | 2102 | error = -ESTALE; |
2103 | if (error < 0) | ||
2085 | goto exit; | 2104 | goto exit; |
2086 | } | ||
2087 | } | 2105 | } |
2088 | /* fallthrough */ | 2106 | /* fallthrough */ |
2089 | case LAST_ROOT: | 2107 | case LAST_ROOT: |
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 4b9cbb28d7fa..28f136d4aaec 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
18 | #include <linux/vmalloc.h> | 18 | #include <linux/vmalloc.h> |
19 | #include <linux/mm.h> | 19 | #include <linux/mm.h> |
20 | #include <linux/namei.h> | ||
20 | #include <asm/uaccess.h> | 21 | #include <asm/uaccess.h> |
21 | #include <asm/byteorder.h> | 22 | #include <asm/byteorder.h> |
22 | 23 | ||
@@ -308,6 +309,9 @@ ncp_lookup_validate(struct dentry *dentry, struct nameidata *nd) | |||
308 | int res, val = 0, len; | 309 | int res, val = 0, len; |
309 | __u8 __name[NCP_MAXPATHLEN + 1]; | 310 | __u8 __name[NCP_MAXPATHLEN + 1]; |
310 | 311 | ||
312 | if (nd->flags & LOOKUP_RCU) | ||
313 | return -ECHILD; | ||
314 | |||
311 | parent = dget_parent(dentry); | 315 | parent = dget_parent(dentry); |
312 | dir = parent->d_inode; | 316 | dir = parent->d_inode; |
313 | 317 | ||
diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 0c75a5f3cafd..9531c052d7a4 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/vfs.h> | 29 | #include <linux/vfs.h> |
30 | #include <linux/mount.h> | 30 | #include <linux/mount.h> |
31 | #include <linux/seq_file.h> | 31 | #include <linux/seq_file.h> |
32 | #include <linux/namei.h> | ||
32 | 33 | ||
33 | #include <linux/ncp_fs.h> | 34 | #include <linux/ncp_fs.h> |
34 | 35 | ||
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 37e0a8bb077e..58beace14b19 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -938,7 +938,8 @@ static int nfs_check_verifier(struct inode *dir, struct dentry *dentry) | |||
938 | * component of the path. | 938 | * component of the path. |
939 | * We check for this using LOOKUP_CONTINUE and LOOKUP_PARENT. | 939 | * We check for this using LOOKUP_CONTINUE and LOOKUP_PARENT. |
940 | */ | 940 | */ |
941 | static inline unsigned int nfs_lookup_check_intent(struct nameidata *nd, unsigned int mask) | 941 | static inline unsigned int nfs_lookup_check_intent(struct nameidata *nd, |
942 | unsigned int mask) | ||
942 | { | 943 | { |
943 | if (nd->flags & (LOOKUP_CONTINUE|LOOKUP_PARENT)) | 944 | if (nd->flags & (LOOKUP_CONTINUE|LOOKUP_PARENT)) |
944 | return 0; | 945 | return 0; |
@@ -1018,7 +1019,7 @@ int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry, | |||
1018 | * If the parent directory is seen to have changed, we throw out the | 1019 | * If the parent directory is seen to have changed, we throw out the |
1019 | * cached dentry and do a new lookup. | 1020 | * cached dentry and do a new lookup. |
1020 | */ | 1021 | */ |
1021 | static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) | 1022 | static int nfs_lookup_revalidate(struct dentry *dentry, struct nameidata *nd) |
1022 | { | 1023 | { |
1023 | struct inode *dir; | 1024 | struct inode *dir; |
1024 | struct inode *inode; | 1025 | struct inode *inode; |
@@ -1027,6 +1028,9 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) | |||
1027 | struct nfs_fattr *fattr = NULL; | 1028 | struct nfs_fattr *fattr = NULL; |
1028 | int error; | 1029 | int error; |
1029 | 1030 | ||
1031 | if (nd->flags & LOOKUP_RCU) | ||
1032 | return -ECHILD; | ||
1033 | |||
1030 | parent = dget_parent(dentry); | 1034 | parent = dget_parent(dentry); |
1031 | dir = parent->d_inode; | 1035 | dir = parent->d_inode; |
1032 | nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE); | 1036 | nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE); |
diff --git a/fs/ocfs2/dcache.c b/fs/ocfs2/dcache.c index 4d54c60ceee4..0310b16a7238 100644 --- a/fs/ocfs2/dcache.c +++ b/fs/ocfs2/dcache.c | |||
@@ -52,9 +52,15 @@ void ocfs2_dentry_attach_gen(struct dentry *dentry) | |||
52 | static int ocfs2_dentry_revalidate(struct dentry *dentry, | 52 | static int ocfs2_dentry_revalidate(struct dentry *dentry, |
53 | struct nameidata *nd) | 53 | struct nameidata *nd) |
54 | { | 54 | { |
55 | struct inode *inode = dentry->d_inode; | 55 | struct inode *inode; |
56 | int ret = 0; /* if all else fails, just return false */ | 56 | int ret = 0; /* if all else fails, just return false */ |
57 | struct ocfs2_super *osb = OCFS2_SB(dentry->d_sb); | 57 | struct ocfs2_super *osb; |
58 | |||
59 | if (nd->flags & LOOKUP_RCU) | ||
60 | return -ECHILD; | ||
61 | |||
62 | inode = dentry->d_inode; | ||
63 | osb = OCFS2_SB(dentry->d_sb); | ||
58 | 64 | ||
59 | mlog_entry("(0x%p, '%.*s')\n", dentry, | 65 | mlog_entry("(0x%p, '%.*s')\n", dentry, |
60 | dentry->d_name.len, dentry->d_name.name); | 66 | dentry->d_name.len, dentry->d_name.name); |
diff --git a/fs/proc/base.c b/fs/proc/base.c index 85f0a80912aa..dc5b2fcadc3b 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -1719,10 +1719,16 @@ static int pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat | |||
1719 | */ | 1719 | */ |
1720 | static int pid_revalidate(struct dentry *dentry, struct nameidata *nd) | 1720 | static int pid_revalidate(struct dentry *dentry, struct nameidata *nd) |
1721 | { | 1721 | { |
1722 | struct inode *inode = dentry->d_inode; | 1722 | struct inode *inode; |
1723 | struct task_struct *task = get_proc_task(inode); | 1723 | struct task_struct *task; |
1724 | const struct cred *cred; | 1724 | const struct cred *cred; |
1725 | 1725 | ||
1726 | if (nd && nd->flags & LOOKUP_RCU) | ||
1727 | return -ECHILD; | ||
1728 | |||
1729 | inode = dentry->d_inode; | ||
1730 | task = get_proc_task(inode); | ||
1731 | |||
1726 | if (task) { | 1732 | if (task) { |
1727 | if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) || | 1733 | if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) || |
1728 | task_dumpable(task)) { | 1734 | task_dumpable(task)) { |
@@ -1888,12 +1894,19 @@ static int proc_fd_link(struct inode *inode, struct path *path) | |||
1888 | 1894 | ||
1889 | static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd) | 1895 | static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd) |
1890 | { | 1896 | { |
1891 | struct inode *inode = dentry->d_inode; | 1897 | struct inode *inode; |
1892 | struct task_struct *task = get_proc_task(inode); | 1898 | struct task_struct *task; |
1893 | int fd = proc_fd(inode); | 1899 | int fd; |
1894 | struct files_struct *files; | 1900 | struct files_struct *files; |
1895 | const struct cred *cred; | 1901 | const struct cred *cred; |
1896 | 1902 | ||
1903 | if (nd && nd->flags & LOOKUP_RCU) | ||
1904 | return -ECHILD; | ||
1905 | |||
1906 | inode = dentry->d_inode; | ||
1907 | task = get_proc_task(inode); | ||
1908 | fd = proc_fd(inode); | ||
1909 | |||
1897 | if (task) { | 1910 | if (task) { |
1898 | files = get_files_struct(task); | 1911 | files = get_files_struct(task); |
1899 | if (files) { | 1912 | if (files) { |
@@ -2563,8 +2576,14 @@ static const struct pid_entry proc_base_stuff[] = { | |||
2563 | */ | 2576 | */ |
2564 | static int proc_base_revalidate(struct dentry *dentry, struct nameidata *nd) | 2577 | static int proc_base_revalidate(struct dentry *dentry, struct nameidata *nd) |
2565 | { | 2578 | { |
2566 | struct inode *inode = dentry->d_inode; | 2579 | struct inode *inode; |
2567 | struct task_struct *task = get_proc_task(inode); | 2580 | struct task_struct *task; |
2581 | |||
2582 | if (nd->flags & LOOKUP_RCU) | ||
2583 | return -ECHILD; | ||
2584 | |||
2585 | inode = dentry->d_inode; | ||
2586 | task = get_proc_task(inode); | ||
2568 | if (task) { | 2587 | if (task) { |
2569 | put_task_struct(task); | 2588 | put_task_struct(task); |
2570 | return 1; | 2589 | return 1; |
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 35efd85a4d32..c9097f43b425 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <linux/sysctl.h> | 5 | #include <linux/sysctl.h> |
6 | #include <linux/proc_fs.h> | 6 | #include <linux/proc_fs.h> |
7 | #include <linux/security.h> | 7 | #include <linux/security.h> |
8 | #include <linux/namei.h> | ||
8 | #include "internal.h" | 9 | #include "internal.h" |
9 | 10 | ||
10 | static const struct dentry_operations proc_sys_dentry_operations; | 11 | static const struct dentry_operations proc_sys_dentry_operations; |
@@ -389,6 +390,8 @@ static const struct inode_operations proc_sys_dir_operations = { | |||
389 | 390 | ||
390 | static int proc_sys_revalidate(struct dentry *dentry, struct nameidata *nd) | 391 | static int proc_sys_revalidate(struct dentry *dentry, struct nameidata *nd) |
391 | { | 392 | { |
393 | if (nd->flags & LOOKUP_RCU) | ||
394 | return -ECHILD; | ||
392 | return !PROC_I(dentry->d_inode)->sysctl->unregistering; | 395 | return !PROC_I(dentry->d_inode)->sysctl->unregistering; |
393 | } | 396 | } |
394 | 397 | ||
diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c index e0f0d7ea10a1..9ea22a56cdf1 100644 --- a/fs/reiserfs/xattr.c +++ b/fs/reiserfs/xattr.c | |||
@@ -972,6 +972,8 @@ int reiserfs_permission(struct inode *inode, int mask) | |||
972 | 972 | ||
973 | static int xattr_hide_revalidate(struct dentry *dentry, struct nameidata *nd) | 973 | static int xattr_hide_revalidate(struct dentry *dentry, struct nameidata *nd) |
974 | { | 974 | { |
975 | if (nd->flags & LOOKUP_RCU) | ||
976 | return -ECHILD; | ||
975 | return -EPERM; | 977 | return -EPERM; |
976 | } | 978 | } |
977 | 979 | ||
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 3e076caa8daf..ea9120a830d8 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
@@ -239,9 +239,13 @@ static int sysfs_dentry_delete(const struct dentry *dentry) | |||
239 | 239 | ||
240 | static int sysfs_dentry_revalidate(struct dentry *dentry, struct nameidata *nd) | 240 | static int sysfs_dentry_revalidate(struct dentry *dentry, struct nameidata *nd) |
241 | { | 241 | { |
242 | struct sysfs_dirent *sd = dentry->d_fsdata; | 242 | struct sysfs_dirent *sd; |
243 | int is_dir; | 243 | int is_dir; |
244 | 244 | ||
245 | if (nd->flags & LOOKUP_RCU) | ||
246 | return -ECHILD; | ||
247 | |||
248 | sd = dentry->d_fsdata; | ||
245 | mutex_lock(&sysfs_mutex); | 249 | mutex_lock(&sysfs_mutex); |
246 | 250 | ||
247 | /* The sysfs dirent has been deleted */ | 251 | /* The sysfs dirent has been deleted */ |
diff --git a/include/linux/dcache.h b/include/linux/dcache.h index b1aeda077258..8b2064d02928 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h | |||
@@ -190,7 +190,6 @@ struct dentry_operations { | |||
190 | #define DCACHE_OP_REVALIDATE 0x4000 | 190 | #define DCACHE_OP_REVALIDATE 0x4000 |
191 | #define DCACHE_OP_DELETE 0x8000 | 191 | #define DCACHE_OP_DELETE 0x8000 |
192 | 192 | ||
193 | |||
194 | extern spinlock_t dcache_inode_lock; | 193 | extern spinlock_t dcache_inode_lock; |
195 | extern seqlock_t rename_lock; | 194 | extern seqlock_t rename_lock; |
196 | 195 | ||