Commit 34286d6662308d82aed891852d04c7c3a2649b16
1 parent
44a7d7a878
Exists in
master
and in
7 other branches
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>
Showing 27 changed files with 215 additions and 61 deletions Side-by-side Diff
- Documentation/filesystems/Locking
- Documentation/filesystems/path-lookup.txt
- Documentation/filesystems/porting
- Documentation/filesystems/vfs.txt
- drivers/staging/autofs/root.c
- drivers/staging/smbfs/dir.c
- fs/afs/dir.c
- fs/autofs4/root.c
- fs/ceph/dir.c
- fs/cifs/dir.c
- fs/coda/dir.c
- fs/ecryptfs/dentry.c
- fs/fat/namei_vfat.c
- fs/fuse/dir.c
- fs/gfs2/dentry.c
- fs/hfs/sysdep.c
- fs/jfs/namei.c
- fs/namei.c
- fs/ncpfs/dir.c
- fs/ncpfs/inode.c
- fs/nfs/dir.c
- fs/ocfs2/dcache.c
- fs/proc/base.c
- fs/proc/proc_sysctl.c
- fs/reiserfs/xattr.c
- fs/sysfs/dir.c
- include/linux/dcache.h
Documentation/filesystems/Locking
| ... | ... | @@ -9,7 +9,7 @@ |
| 9 | 9 | |
| 10 | 10 | --------------------------- dentry_operations -------------------------- |
| 11 | 11 | prototypes: |
| 12 | - int (*d_revalidate)(struct dentry *, int); | |
| 12 | + int (*d_revalidate)(struct dentry *, struct nameidata *); | |
| 13 | 13 | int (*d_hash)(const struct dentry *, const struct inode *, |
| 14 | 14 | struct qstr *); |
| 15 | 15 | int (*d_compare)(const struct dentry *, const struct inode *, |
| ... | ... | @@ -21,14 +21,14 @@ |
| 21 | 21 | char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen); |
| 22 | 22 | |
| 23 | 23 | locking rules: |
| 24 | - rename_lock ->d_lock may block | |
| 25 | -d_revalidate: no no yes | |
| 26 | -d_hash no no no | |
| 27 | -d_compare: yes no no | |
| 28 | -d_delete: no yes no | |
| 29 | -d_release: no no yes | |
| 30 | -d_iput: no no yes | |
| 31 | -d_dname: no no no | |
| 24 | + rename_lock ->d_lock may block rcu-walk | |
| 25 | +d_revalidate: no no yes (ref-walk) maybe | |
| 26 | +d_hash no no no maybe | |
| 27 | +d_compare: yes no no maybe | |
| 28 | +d_delete: no yes no no | |
| 29 | +d_release: no no yes no | |
| 30 | +d_iput: no no yes no | |
| 31 | +d_dname: no no no no | |
| 32 | 32 | |
| 33 | 33 | --------------------------- inode_operations --------------------------- |
| 34 | 34 | prototypes: |
Documentation/filesystems/path-lookup.txt
| ... | ... | @@ -317,11 +317,10 @@ |
| 317 | 317 | The cases where rcu-walk cannot continue are: |
| 318 | 318 | * NULL dentry (ie. any uncached path element) |
| 319 | 319 | * parent with d_inode->i_op->permission or ACLs |
| 320 | -* dentries with d_revalidate | |
| 321 | 320 | * Following links |
| 322 | 321 | |
| 323 | -In future patches, permission checks and d_revalidate become rcu-walk aware. It | |
| 324 | -may be possible eventually to make following links rcu-walk aware. | |
| 322 | +In future patches, permission checks become rcu-walk aware. It may be possible | |
| 323 | +eventually to make following links rcu-walk aware. | |
| 325 | 324 | |
| 326 | 325 | Uncached path elements will always require dropping to ref-walk mode, at the |
| 327 | 326 | very least because i_mutex needs to be grabbed, and objects allocated. |
Documentation/filesystems/porting
| ... | ... | @@ -360,4 +360,24 @@ |
| 360 | 360 | INIT_LIST_HEAD(&inode->i_dentry); |
| 361 | 361 | |
| 362 | 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. |
Documentation/filesystems/vfs.txt
| ... | ... | @@ -863,6 +863,15 @@ |
| 863 | 863 | dcache. Most filesystems leave this as NULL, because all their |
| 864 | 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 | 875 | d_hash: called when the VFS adds a dentry to the hash table. The first |
| 867 | 876 | dentry passed to d_hash is the parent directory that the name is |
| 868 | 877 | to be hashed into. The inode is the dentry's inode. |
drivers/staging/autofs/root.c
| ... | ... | @@ -154,12 +154,15 @@ |
| 154 | 154 | * yet completely filled in, and revalidate has to delay such |
| 155 | 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 | 159 | struct inode * dir; |
| 160 | 160 | struct autofs_sb_info *sbi; |
| 161 | 161 | struct autofs_dir_ent *ent; |
| 162 | 162 | int res; |
| 163 | + | |
| 164 | + if (nd->flags & LOOKUP_RCU) | |
| 165 | + return -ECHILD; | |
| 163 | 166 | |
| 164 | 167 | lock_kernel(); |
| 165 | 168 | dir = dentry->d_parent->d_inode; |
drivers/staging/smbfs/dir.c
| ... | ... | @@ -14,6 +14,7 @@ |
| 14 | 14 | #include <linux/ctype.h> |
| 15 | 15 | #include <linux/net.h> |
| 16 | 16 | #include <linux/sched.h> |
| 17 | +#include <linux/namei.h> | |
| 17 | 18 | |
| 18 | 19 | #include "smb_fs.h" |
| 19 | 20 | #include "smb_mount.h" |
| 20 | 21 | |
| 21 | 22 | |
| ... | ... | @@ -301,12 +302,19 @@ |
| 301 | 302 | * This is the callback when the dcache has a lookup hit. |
| 302 | 303 | */ |
| 303 | 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 inode * inode = dentry->d_inode; | |
| 308 | - unsigned long age = jiffies - dentry->d_time; | |
| 307 | + struct smb_sb_info *server; | |
| 308 | + struct inode *inode; | |
| 309 | + unsigned long age; | |
| 309 | 310 | int valid; |
| 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; | |
| 310 | 318 | |
| 311 | 319 | /* |
| 312 | 320 | * The default validation is based on dentry age: |
fs/afs/dir.c
| ... | ... | @@ -13,6 +13,7 @@ |
| 13 | 13 | #include <linux/module.h> |
| 14 | 14 | #include <linux/init.h> |
| 15 | 15 | #include <linux/fs.h> |
| 16 | +#include <linux/namei.h> | |
| 16 | 17 | #include <linux/pagemap.h> |
| 17 | 18 | #include <linux/ctype.h> |
| 18 | 19 | #include <linux/sched.h> |
| ... | ... | @@ -606,6 +607,9 @@ |
| 606 | 607 | struct key *key; |
| 607 | 608 | void *dir_version; |
| 608 | 609 | int ret; |
| 610 | + | |
| 611 | + if (nd->flags & LOOKUP_RCU) | |
| 612 | + return -ECHILD; | |
| 609 | 613 | |
| 610 | 614 | vnode = AFS_FS_I(dentry->d_inode); |
| 611 | 615 |
fs/autofs4/root.c
| ... | ... | @@ -315,11 +315,18 @@ |
| 315 | 315 | */ |
| 316 | 316 | static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) |
| 317 | 317 | { |
| 318 | - struct inode *dir = dentry->d_parent->d_inode; | |
| 319 | - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); | |
| 320 | - int oz_mode = autofs4_oz_mode(sbi); | |
| 318 | + struct inode *dir; | |
| 319 | + struct autofs_sb_info *sbi; | |
| 320 | + int oz_mode; | |
| 321 | 321 | int flags = nd ? nd->flags : 0; |
| 322 | 322 | int status = 1; |
| 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); | |
| 323 | 330 | |
| 324 | 331 | /* Pending dentry */ |
| 325 | 332 | spin_lock(&sbi->fs_lock); |
fs/ceph/dir.c
| ... | ... | @@ -990,7 +990,12 @@ |
| 990 | 990 | */ |
| 991 | 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 | 1000 | dout("d_revalidate %p '%.*s' inode %p offset %lld\n", dentry, |
| 996 | 1001 | dentry->d_name.len, dentry->d_name.name, dentry->d_inode, |
fs/cifs/dir.c
fs/coda/dir.c
| ... | ... | @@ -18,6 +18,7 @@ |
| 18 | 18 | #include <linux/errno.h> |
| 19 | 19 | #include <linux/string.h> |
| 20 | 20 | #include <linux/spinlock.h> |
| 21 | +#include <linux/namei.h> | |
| 21 | 22 | |
| 22 | 23 | #include <asm/uaccess.h> |
| 23 | 24 | |
| 24 | 25 | |
| ... | ... | @@ -541,9 +542,13 @@ |
| 541 | 542 | /* called when a cache lookup succeeds */ |
| 542 | 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 | 546 | struct coda_inode_info *cii; |
| 546 | 547 | |
| 548 | + if (nd->flags & LOOKUP_RCU) | |
| 549 | + return -ECHILD; | |
| 550 | + | |
| 551 | + inode = de->d_inode; | |
| 547 | 552 | if (!inode || coda_isroot(inode)) |
| 548 | 553 | goto out; |
| 549 | 554 | if (is_bad_inode(inode)) |
fs/ecryptfs/dentry.c
| ... | ... | @@ -44,12 +44,17 @@ |
| 44 | 44 | */ |
| 45 | 45 | static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) |
| 46 | 46 | { |
| 47 | - struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); | |
| 48 | - struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); | |
| 47 | + struct dentry *lower_dentry; | |
| 48 | + struct vfsmount *lower_mnt; | |
| 49 | 49 | struct dentry *dentry_save; |
| 50 | 50 | struct vfsmount *vfsmount_save; |
| 51 | 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 | 58 | if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate) |
| 54 | 59 | goto out; |
| 55 | 60 | dentry_save = nd->path.dentry; |
fs/fat/namei_vfat.c
| ... | ... | @@ -43,6 +43,9 @@ |
| 43 | 43 | |
| 44 | 44 | static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd) |
| 45 | 45 | { |
| 46 | + if (nd->flags & LOOKUP_RCU) | |
| 47 | + return -ECHILD; | |
| 48 | + | |
| 46 | 49 | /* This is not negative dentry. Always valid. */ |
| 47 | 50 | if (dentry->d_inode) |
| 48 | 51 | return 1; |
| ... | ... | @@ -51,6 +54,9 @@ |
| 51 | 54 | |
| 52 | 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 | 61 | * This is not negative dentry. Always valid. |
| 56 | 62 | * |
fs/fuse/dir.c
| ... | ... | @@ -156,8 +156,12 @@ |
| 156 | 156 | */ |
| 157 | 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 | 160 | |
| 161 | + if (nd->flags & LOOKUP_RCU) | |
| 162 | + return -ECHILD; | |
| 163 | + | |
| 164 | + inode = entry->d_inode; | |
| 161 | 165 | if (inode && is_bad_inode(inode)) |
| 162 | 166 | return 0; |
| 163 | 167 | else if (fuse_dentry_time(entry) < get_jiffies_64()) { |
fs/gfs2/dentry.c
| ... | ... | @@ -11,6 +11,7 @@ |
| 11 | 11 | #include <linux/completion.h> |
| 12 | 12 | #include <linux/buffer_head.h> |
| 13 | 13 | #include <linux/gfs2_ondisk.h> |
| 14 | +#include <linux/namei.h> | |
| 14 | 15 | #include <linux/crc32.h> |
| 15 | 16 | |
| 16 | 17 | #include "gfs2.h" |
| 17 | 18 | |
| ... | ... | @@ -34,14 +35,22 @@ |
| 34 | 35 | |
| 35 | 36 | static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd) |
| 36 | 37 | { |
| 37 | - struct dentry *parent = dget_parent(dentry); | |
| 38 | - struct gfs2_sbd *sdp = GFS2_SB(parent->d_inode); | |
| 39 | - struct gfs2_inode *dip = GFS2_I(parent->d_inode); | |
| 40 | - struct inode *inode = dentry->d_inode; | |
| 38 | + struct dentry *parent; | |
| 39 | + struct gfs2_sbd *sdp; | |
| 40 | + struct gfs2_inode *dip; | |
| 41 | + struct inode *inode; | |
| 41 | 42 | struct gfs2_holder d_gh; |
| 42 | 43 | struct gfs2_inode *ip = NULL; |
| 43 | 44 | int error; |
| 44 | 45 | int had_lock = 0; |
| 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; | |
| 45 | 54 | |
| 46 | 55 | if (inode) { |
| 47 | 56 | if (is_bad_inode(inode)) |
fs/hfs/sysdep.c
| ... | ... | @@ -8,15 +8,20 @@ |
| 8 | 8 | * This file contains the code to do various system dependent things. |
| 9 | 9 | */ |
| 10 | 10 | |
| 11 | +#include <linux/namei.h> | |
| 11 | 12 | #include "hfs_fs.h" |
| 12 | 13 | |
| 13 | 14 | /* dentry case-handling: just lowercase everything */ |
| 14 | 15 | |
| 15 | 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 | 19 | int diff; |
| 19 | 20 | |
| 21 | + if (nd->flags & LOOKUP_RCU) | |
| 22 | + return -ECHILD; | |
| 23 | + | |
| 24 | + inode = dentry->d_inode; | |
| 20 | 25 | if(!inode) |
| 21 | 26 | return 1; |
| 22 | 27 |
fs/jfs/namei.c
fs/namei.c
| ... | ... | @@ -563,10 +563,26 @@ |
| 563 | 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 | 580 | static inline struct dentry * |
| 567 | 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 | 586 | if (unlikely(status <= 0)) { |
| 571 | 587 | /* |
| 572 | 588 | * The dentry failed validation. |
| 573 | 589 | |
| ... | ... | @@ -574,14 +590,20 @@ |
| 574 | 590 | * the dentry otherwise d_revalidate is asking us |
| 575 | 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 | 603 | if (!d_invalidate(dentry)) { |
| 579 | 604 | dput(dentry); |
| 580 | 605 | dentry = NULL; |
| 581 | 606 | } |
| 582 | - } else { | |
| 583 | - dput(dentry); | |
| 584 | - dentry = ERR_PTR(status); | |
| 585 | 607 | } |
| 586 | 608 | } |
| 587 | 609 | return dentry; |
| ... | ... | @@ -626,7 +648,7 @@ |
| 626 | 648 | if (!need_reval_dot(dentry)) |
| 627 | 649 | return 0; |
| 628 | 650 | |
| 629 | - status = dentry->d_op->d_revalidate(dentry, nd); | |
| 651 | + status = d_revalidate(dentry, nd); | |
| 630 | 652 | if (status > 0) |
| 631 | 653 | return 0; |
| 632 | 654 | |
| 633 | 655 | |
| ... | ... | @@ -1039,12 +1061,8 @@ |
| 1039 | 1061 | return -ECHILD; |
| 1040 | 1062 | |
| 1041 | 1063 | nd->seq = seq; |
| 1042 | - 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; | |
| 1064 | + if (dentry->d_flags & DCACHE_OP_REVALIDATE) | |
| 1046 | 1065 | goto need_revalidate; |
| 1047 | - } | |
| 1048 | 1066 | path->mnt = mnt; |
| 1049 | 1067 | path->dentry = dentry; |
| 1050 | 1068 | __follow_mount_rcu(nd, path, inode); |
| 1051 | 1069 | |
| ... | ... | @@ -1292,12 +1310,11 @@ |
| 1292 | 1310 | * We may need to check the cached dentry for staleness. |
| 1293 | 1311 | */ |
| 1294 | 1312 | if (need_reval_dot(nd->path.dentry)) { |
| 1295 | - if (nameidata_drop_rcu_maybe(nd)) | |
| 1296 | - return -ECHILD; | |
| 1297 | - err = -ESTALE; | |
| 1298 | 1313 | /* Note: we do not d_invalidate() */ |
| 1299 | - if (!nd->path.dentry->d_op->d_revalidate( | |
| 1300 | - nd->path.dentry, nd)) | |
| 1314 | + err = d_revalidate(nd->path.dentry, nd); | |
| 1315 | + if (!err) | |
| 1316 | + err = -ESTALE; | |
| 1317 | + if (err < 0) | |
| 1301 | 1318 | break; |
| 1302 | 1319 | } |
| 1303 | 1320 | return_base: |
| 1304 | 1321 | |
| 1305 | 1322 | |
| ... | ... | @@ -2080,10 +2097,11 @@ |
| 2080 | 2097 | dir = nd->path.dentry; |
| 2081 | 2098 | case LAST_DOT: |
| 2082 | 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 | 2102 | error = -ESTALE; |
| 2103 | + if (error < 0) | |
| 2085 | 2104 | goto exit; |
| 2086 | - } | |
| 2087 | 2105 | } |
| 2088 | 2106 | /* fallthrough */ |
| 2089 | 2107 | case LAST_ROOT: |
fs/ncpfs/dir.c
| ... | ... | @@ -17,6 +17,7 @@ |
| 17 | 17 | #include <linux/kernel.h> |
| 18 | 18 | #include <linux/vmalloc.h> |
| 19 | 19 | #include <linux/mm.h> |
| 20 | +#include <linux/namei.h> | |
| 20 | 21 | #include <asm/uaccess.h> |
| 21 | 22 | #include <asm/byteorder.h> |
| 22 | 23 | |
| ... | ... | @@ -307,6 +308,9 @@ |
| 307 | 308 | struct ncp_entry_info finfo; |
| 308 | 309 | int res, val = 0, len; |
| 309 | 310 | __u8 __name[NCP_MAXPATHLEN + 1]; |
| 311 | + | |
| 312 | + if (nd->flags & LOOKUP_RCU) | |
| 313 | + return -ECHILD; | |
| 310 | 314 | |
| 311 | 315 | parent = dget_parent(dentry); |
| 312 | 316 | dir = parent->d_inode; |
fs/ncpfs/inode.c
fs/nfs/dir.c
| ... | ... | @@ -938,7 +938,8 @@ |
| 938 | 938 | * component of the path. |
| 939 | 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 | 944 | if (nd->flags & (LOOKUP_CONTINUE|LOOKUP_PARENT)) |
| 944 | 945 | return 0; |
| ... | ... | @@ -1018,7 +1019,7 @@ |
| 1018 | 1019 | * If the parent directory is seen to have changed, we throw out the |
| 1019 | 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 | 1024 | struct inode *dir; |
| 1024 | 1025 | struct inode *inode; |
| ... | ... | @@ -1026,6 +1027,9 @@ |
| 1026 | 1027 | struct nfs_fh *fhandle = NULL; |
| 1027 | 1028 | struct nfs_fattr *fattr = NULL; |
| 1028 | 1029 | int error; |
| 1030 | + | |
| 1031 | + if (nd->flags & LOOKUP_RCU) | |
| 1032 | + return -ECHILD; | |
| 1029 | 1033 | |
| 1030 | 1034 | parent = dget_parent(dentry); |
| 1031 | 1035 | dir = parent->d_inode; |
fs/ocfs2/dcache.c
| ... | ... | @@ -52,9 +52,15 @@ |
| 52 | 52 | static int ocfs2_dentry_revalidate(struct dentry *dentry, |
| 53 | 53 | struct nameidata *nd) |
| 54 | 54 | { |
| 55 | - struct inode *inode = dentry->d_inode; | |
| 55 | + struct inode *inode; | |
| 56 | 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 | 65 | mlog_entry("(0x%p, '%.*s')\n", dentry, |
| 60 | 66 | dentry->d_name.len, dentry->d_name.name); |
fs/proc/base.c
| ... | ... | @@ -1719,10 +1719,16 @@ |
| 1719 | 1719 | */ |
| 1720 | 1720 | static int pid_revalidate(struct dentry *dentry, struct nameidata *nd) |
| 1721 | 1721 | { |
| 1722 | - struct inode *inode = dentry->d_inode; | |
| 1723 | - struct task_struct *task = get_proc_task(inode); | |
| 1722 | + struct inode *inode; | |
| 1723 | + struct task_struct *task; | |
| 1724 | 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 | 1732 | if (task) { |
| 1727 | 1733 | if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) || |
| 1728 | 1734 | task_dumpable(task)) { |
| 1729 | 1735 | |
| ... | ... | @@ -1888,12 +1894,19 @@ |
| 1888 | 1894 | |
| 1889 | 1895 | static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd) |
| 1890 | 1896 | { |
| 1891 | - struct inode *inode = dentry->d_inode; | |
| 1892 | - struct task_struct *task = get_proc_task(inode); | |
| 1893 | - int fd = proc_fd(inode); | |
| 1897 | + struct inode *inode; | |
| 1898 | + struct task_struct *task; | |
| 1899 | + int fd; | |
| 1894 | 1900 | struct files_struct *files; |
| 1895 | 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 | 1910 | if (task) { |
| 1898 | 1911 | files = get_files_struct(task); |
| 1899 | 1912 | if (files) { |
| ... | ... | @@ -2563,8 +2576,14 @@ |
| 2563 | 2576 | */ |
| 2564 | 2577 | static int proc_base_revalidate(struct dentry *dentry, struct nameidata *nd) |
| 2565 | 2578 | { |
| 2566 | - struct inode *inode = dentry->d_inode; | |
| 2567 | - struct task_struct *task = get_proc_task(inode); | |
| 2579 | + struct inode *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 | 2587 | if (task) { |
| 2569 | 2588 | put_task_struct(task); |
| 2570 | 2589 | return 1; |
fs/proc/proc_sysctl.c
| ... | ... | @@ -5,6 +5,7 @@ |
| 5 | 5 | #include <linux/sysctl.h> |
| 6 | 6 | #include <linux/proc_fs.h> |
| 7 | 7 | #include <linux/security.h> |
| 8 | +#include <linux/namei.h> | |
| 8 | 9 | #include "internal.h" |
| 9 | 10 | |
| 10 | 11 | static const struct dentry_operations proc_sys_dentry_operations; |
| ... | ... | @@ -389,6 +390,8 @@ |
| 389 | 390 | |
| 390 | 391 | static int proc_sys_revalidate(struct dentry *dentry, struct nameidata *nd) |
| 391 | 392 | { |
| 393 | + if (nd->flags & LOOKUP_RCU) | |
| 394 | + return -ECHILD; | |
| 392 | 395 | return !PROC_I(dentry->d_inode)->sysctl->unregistering; |
| 393 | 396 | } |
| 394 | 397 |
fs/reiserfs/xattr.c
fs/sysfs/dir.c
| ... | ... | @@ -239,9 +239,13 @@ |
| 239 | 239 | |
| 240 | 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 | 243 | int is_dir; |
| 244 | 244 | |
| 245 | + if (nd->flags & LOOKUP_RCU) | |
| 246 | + return -ECHILD; | |
| 247 | + | |
| 248 | + sd = dentry->d_fsdata; | |
| 245 | 249 | mutex_lock(&sysfs_mutex); |
| 246 | 250 | |
| 247 | 251 | /* The sysfs dirent has been deleted */ |