diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-12 10:35:47 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-12 10:35:47 -0700 |
commit | 8b19e34188a32d63a7da94ea8a3f5e39b0c66050 (patch) | |
tree | b6affbe820569551bebfd39cc07965855080d2c1 /fs | |
parent | 5762482f5496cb1dd86acd2aace3ea25d1404e1f (diff) | |
download | op-kernel-dev-8b19e34188a32d63a7da94ea8a3f5e39b0c66050.zip op-kernel-dev-8b19e34188a32d63a7da94ea8a3f5e39b0c66050.tar.gz |
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')
-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 4df68e2..99d4d72 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; } |