summaryrefslogtreecommitdiffstats
path: root/fs/dcache.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-09-12 13:35:47 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-09-12 13:35:47 -0400
commit8b19e34188a32d63a7da94ea8a3f5e39b0c66050 (patch)
treeb6affbe820569551bebfd39cc07965855080d2c1 /fs/dcache.c
parent5762482f5496cb1dd86acd2aace3ea25d1404e1f (diff)
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>
Diffstat (limited to 'fs/dcache.c')
-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:
3015 return ERR_PTR(-ENAMETOOLONG); 3015 return ERR_PTR(-ENAMETOOLONG);
3016} 3016}
3017 3017
3018static inline void get_fs_root_and_pwd(struct fs_struct *fs, struct path *root, 3018static void get_fs_root_and_pwd_rcu(struct fs_struct *fs, struct path *root,
3019 struct path *pwd) 3019 struct path *pwd)
3020{ 3020{
3021 spin_lock(&fs->lock); 3021 unsigned seq;
3022 *root = fs->root; 3022
3023 path_get(root); 3023 do {
3024 *pwd = fs->pwd; 3024 seq = read_seqcount_begin(&fs->seq);
3025 path_get(pwd); 3025 *root = fs->root;
3026 spin_unlock(&fs->lock); 3026 *pwd = fs->pwd;
3027 } while (read_seqcount_retry(&fs->seq, seq));
3027} 3028}
3028 3029
3029/* 3030/*
@@ -3053,7 +3054,8 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
3053 if (!page) 3054 if (!page)
3054 return -ENOMEM; 3055 return -ENOMEM;
3055 3056
3056 get_fs_root_and_pwd(current->fs, &root, &pwd); 3057 rcu_read_lock();
3058 get_fs_root_and_pwd_rcu(current->fs, &root, &pwd);
3057 3059
3058 error = -ENOENT; 3060 error = -ENOENT;
3059 br_read_lock(&vfsmount_lock); 3061 br_read_lock(&vfsmount_lock);
@@ -3088,8 +3090,7 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
3088 } 3090 }
3089 3091
3090out: 3092out:
3091 path_put(&pwd); 3093 rcu_read_unlock();
3092 path_put(&root);
3093 free_page((unsigned long) page); 3094 free_page((unsigned long) page);
3094 return error; 3095 return error;
3095} 3096}