]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
vfs: make getcwd() get the root and pwd path under rcu
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 12 Sep 2013 17:35:47 +0000 (10:35 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 12 Sep 2013 17:35:47 +0000 (10:35 -0700)
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>
fs/dcache.c

index 4df68e27cbc7dc4f1fed51928137d8f0beca6e90..99d4d7226203536aaeb2956c6436d7bbc4f69f5c 100644 (file)
@@ -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;
 }