aboutsummaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorNick Piggin2011-01-14 02:35:53 +0000
committerNick Piggin2011-01-14 02:35:53 +0000
commitbb20c18db6fbb5e6ba499c76473a487d35073467 (patch)
treeca0426f86e5c2a331bd5006d7f16795711b7ade2 /fs
parenta82416da83722944d78d933301e32e7c5ad70edd (diff)
fs: force_reval_path drop rcu-walk before d_invalidate
d_revalidate can return in rcu-walk mode even when it returns 0. We can't just call any old dcache function on rcu-walk dentry (the dentry is unstable, so even through d_lock can safely be taken, the result may no longer be what we expect -- careful re-checks would be required). So just drop rcu in this case. (I missed this conversion when switching to the rcu-walk convention that Linus suggested) Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Diffstat (limited to 'fs')
-rw-r--r--fs/namei.c10
1 files changed, 10 insertions, 0 deletions
diff --git a/fs/namei.c b/fs/namei.c
index 19433cdba011..0f02359ce685 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -583,6 +583,13 @@ void release_open_intent(struct nameidata *nd)
fput(nd->intent.open.file);
}
+/*
+ * Call d_revalidate and handle filesystems that request rcu-walk
+ * to be dropped. This may be called and return in rcu-walk mode,
+ * regardless of success or error. If -ECHILD is returned, the caller
+ * must return -ECHILD back up the path walk stack so path walk may
+ * be restarted in ref-walk mode.
+ */
static int d_revalidate(struct dentry *dentry, struct nameidata *nd)
{
int status;
@@ -673,6 +680,9 @@ force_reval_path(struct path *path, struct nameidata *nd)
return 0;
if (!status) {
+ /* Don't d_invalidate in rcu-walk mode */
+ if (nameidata_drop_rcu(nd))
+ return -ECHILD;
d_invalidate(dentry);
status = -ESTALE;
}