summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-09-13 00:24:55 +0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-09-13 00:24:55 +0400
commit68f0d9d92e5430e250f2bd2a1e7a350e880d776a (patch)
tree2e416905bafd3e87a6b6eda8837d5fefe143b19b
parent3272c544da48f8915a0e34189182aed029bd0f2b (diff)
downloadlinux-68f0d9d92e5430e250f2bd2a1e7a350e880d776a.tar.xz
vfs: make d_path() get the root path under RCU
This avoids the spinlocks and refcounts in the d_path() sequence too (used by /proc and various other entities). See commit 8b19e34188a3 for the equivalent getcwd() system call path. And unlike getcwd(), d_path() doesn't copy the result to user space, so I don't need to fear _that_ particular bug happening again. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/dcache.c16
1 files changed, 14 insertions, 2 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 91e551b5af59..dddc67fed732 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -2869,6 +2869,16 @@ static int prepend_unreachable(char **buffer, int *buflen)
return prepend(buffer, buflen, "(unreachable)", 13);
}
+static void get_fs_root_rcu(struct fs_struct *fs, struct path *root)
+{
+ unsigned seq;
+
+ do {
+ seq = read_seqcount_begin(&fs->seq);
+ *root = fs->root;
+ } while (read_seqcount_retry(&fs->seq, seq));
+}
+
/**
* d_path - return the path of a dentry
* @path: path to report
@@ -2901,13 +2911,15 @@ char *d_path(const struct path *path, char *buf, int buflen)
if (path->dentry->d_op && path->dentry->d_op->d_dname)
return path->dentry->d_op->d_dname(path->dentry, buf, buflen);
- get_fs_root(current->fs, &root);
+ rcu_read_lock();
+ get_fs_root_rcu(current->fs, &root);
br_read_lock(&vfsmount_lock);
error = path_with_deleted(path, &root, &res, &buflen);
br_read_unlock(&vfsmount_lock);
+ rcu_read_unlock();
+
if (error < 0)
res = ERR_PTR(error);
- path_put(&root);
return res;
}
EXPORT_SYMBOL(d_path);