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 /fs/namei.c | |
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>
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 54 |
1 files changed, 36 insertions, 18 deletions
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: |