summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-09-12 10:35:47 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2013-09-12 10:35:47 -0700
commit8b19e34188a32d63a7da94ea8a3f5e39b0c66050 (patch)
treeb6affbe820569551bebfd39cc07965855080d2c1
parent5762482f5496cb1dd86acd2aace3ea25d1404e1f (diff)
downloadlinux-stable-8b19e34188a32d63a7da94ea8a3f5e39b0c66050.tar.gz
linux-stable-8b19e34188a32d63a7da94ea8a3f5e39b0c66050.tar.bz2
linux-stable-8b19e34188a32d63a7da94ea8a3f5e39b0c66050.zip
vfs: make getcwd() get the root and pwd path under rcu
This allows us to skip all the crazy spinlocks and reference count updates, and instead use the fs sequence read-lock to get an atomic snapshot of the root and cwd information. We might want to make the rule that "prepend_path()" is always called with the RCU lock held, but the RCU lock nests fine and this is the minimal fix. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/dcache.c23
1 files changed, 12 insertions, 11 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 4df68e27cbc7..99d4d7226203 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -3015,15 +3015,16 @@ Elong:
return ERR_PTR(-ENAMETOOLONG);
}
-static inline void get_fs_root_and_pwd(struct fs_struct *fs, struct path *root,
- struct path *pwd)
+static void get_fs_root_and_pwd_rcu(struct fs_struct *fs, struct path *root,
+ struct path *pwd)
{
- spin_lock(&fs->lock);
- *root = fs->root;
- path_get(root);
- *pwd = fs->pwd;
- path_get(pwd);
- spin_unlock(&fs->lock);
+ unsigned seq;
+
+ do {
+ seq = read_seqcount_begin(&fs->seq);
+ *root = fs->root;
+ *pwd = fs->pwd;
+ } while (read_seqcount_retry(&fs->seq, seq));
}
/*
@@ -3053,7 +3054,8 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
if (!page)
return -ENOMEM;
- get_fs_root_and_pwd(current->fs, &root, &pwd);
+ rcu_read_lock();
+ get_fs_root_and_pwd_rcu(current->fs, &root, &pwd);
error = -ENOENT;
br_read_lock(&vfsmount_lock);
@@ -3088,8 +3090,7 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
}
out:
- path_put(&pwd);
- path_put(&root);
+ rcu_read_unlock();
free_page((unsigned long) page);
return error;
}