diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-12 13:35:47 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-12 13:35:47 -0400 |
commit | 8b19e34188a32d63a7da94ea8a3f5e39b0c66050 (patch) | |
tree | b6affbe820569551bebfd39cc07965855080d2c1 /fs/dcache.c | |
parent | 5762482f5496cb1dd86acd2aace3ea25d1404e1f (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.c | 23 |
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 | ||
3018 | static inline void get_fs_root_and_pwd(struct fs_struct *fs, struct path *root, | 3018 | static 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 | ||
3090 | out: | 3092 | out: |
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 | } |