aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/namespace.c
diff options
context:
space:
mode:
authorNick Piggin <npiggin@kernel.dk>2011-01-07 01:49:37 -0500
committerNick Piggin <npiggin@kernel.dk>2011-01-07 01:50:22 -0500
commit949854d02455080d20cd3e1db28a3a18daf7599d (patch)
tree9b13a6f86c1d0b91e462a471e53b0e717036b18e /fs/nfs/namespace.c
parent9abca36087288fe28de4749c71ca003d4b9e3ed0 (diff)
fs: Use rename lock and RCU for multi-step operations
The remaining usages for dcache_lock is to allow atomic, multi-step read-side operations over the directory tree by excluding modifications to the tree. Also, to walk in the leaf->root direction in the tree where we don't have a natural d_lock ordering. This could be accomplished by taking every d_lock, but this would mean a huge number of locks and actually gets very tricky. Solve this instead by using the rename seqlock for multi-step read-side operations, retry in case of a rename so we don't walk up the wrong parent. Concurrent dentry insertions are not serialised against. Concurrent deletes are tricky when walking up the directory: our parent might have been deleted when dropping locks so also need to check and retry for that. We can also use the rename lock in cases where livelock is a worry (and it is introduced in subsequent patch). Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Diffstat (limited to 'fs/nfs/namespace.c')
-rw-r--r--fs/nfs/namespace.c14
1 files changed, 13 insertions, 1 deletions
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index db6aa3673cf3..78c0ebb0b07c 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -49,11 +49,17 @@ char *nfs_path(const char *base,
49 const struct dentry *dentry, 49 const struct dentry *dentry,
50 char *buffer, ssize_t buflen) 50 char *buffer, ssize_t buflen)
51{ 51{
52 char *end = buffer+buflen; 52 char *end;
53 int namelen; 53 int namelen;
54 unsigned seq;
54 55
56rename_retry:
57 end = buffer+buflen;
55 *--end = '\0'; 58 *--end = '\0';
56 buflen--; 59 buflen--;
60
61 seq = read_seqbegin(&rename_lock);
62 rcu_read_lock();
57 spin_lock(&dcache_lock); 63 spin_lock(&dcache_lock);
58 while (!IS_ROOT(dentry) && dentry != droot) { 64 while (!IS_ROOT(dentry) && dentry != droot) {
59 namelen = dentry->d_name.len; 65 namelen = dentry->d_name.len;
@@ -66,6 +72,9 @@ char *nfs_path(const char *base,
66 dentry = dentry->d_parent; 72 dentry = dentry->d_parent;
67 } 73 }
68 spin_unlock(&dcache_lock); 74 spin_unlock(&dcache_lock);
75 rcu_read_unlock();
76 if (read_seqretry(&rename_lock, seq))
77 goto rename_retry;
69 if (*end != '/') { 78 if (*end != '/') {
70 if (--buflen < 0) 79 if (--buflen < 0)
71 goto Elong; 80 goto Elong;
@@ -83,6 +92,9 @@ char *nfs_path(const char *base,
83 return end; 92 return end;
84Elong_unlock: 93Elong_unlock:
85 spin_unlock(&dcache_lock); 94 spin_unlock(&dcache_lock);
95 rcu_read_unlock();
96 if (read_seqretry(&rename_lock, seq))
97 goto rename_retry;
86Elong: 98Elong:
87 return ERR_PTR(-ENAMETOOLONG); 99 return ERR_PTR(-ENAMETOOLONG);
88} 100}