diff options
author | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 01:49:37 -0500 |
---|---|---|
committer | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 01:50:22 -0500 |
commit | 949854d02455080d20cd3e1db28a3a18daf7599d (patch) | |
tree | 9b13a6f86c1d0b91e462a471e53b0e717036b18e /fs/autofs4 | |
parent | 9abca36087288fe28de4749c71ca003d4b9e3ed0 (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/autofs4')
-rw-r--r-- | fs/autofs4/waitq.c | 18 |
1 files changed, 15 insertions, 3 deletions
diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index 2341375386f8..4be8f778a418 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c | |||
@@ -186,16 +186,25 @@ static int autofs4_getpath(struct autofs_sb_info *sbi, | |||
186 | { | 186 | { |
187 | struct dentry *root = sbi->sb->s_root; | 187 | struct dentry *root = sbi->sb->s_root; |
188 | struct dentry *tmp; | 188 | struct dentry *tmp; |
189 | char *buf = *name; | 189 | char *buf; |
190 | char *p; | 190 | char *p; |
191 | int len = 0; | 191 | int len; |
192 | 192 | unsigned seq; | |
193 | |||
194 | rename_retry: | ||
195 | buf = *name; | ||
196 | len = 0; | ||
197 | seq = read_seqbegin(&rename_lock); | ||
198 | rcu_read_lock(); | ||
193 | spin_lock(&dcache_lock); | 199 | spin_lock(&dcache_lock); |
194 | for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) | 200 | for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) |
195 | len += tmp->d_name.len + 1; | 201 | len += tmp->d_name.len + 1; |
196 | 202 | ||
197 | if (!len || --len > NAME_MAX) { | 203 | if (!len || --len > NAME_MAX) { |
198 | spin_unlock(&dcache_lock); | 204 | spin_unlock(&dcache_lock); |
205 | rcu_read_unlock(); | ||
206 | if (read_seqretry(&rename_lock, seq)) | ||
207 | goto rename_retry; | ||
199 | return 0; | 208 | return 0; |
200 | } | 209 | } |
201 | 210 | ||
@@ -209,6 +218,9 @@ static int autofs4_getpath(struct autofs_sb_info *sbi, | |||
209 | strncpy(p, tmp->d_name.name, tmp->d_name.len); | 218 | strncpy(p, tmp->d_name.name, tmp->d_name.len); |
210 | } | 219 | } |
211 | spin_unlock(&dcache_lock); | 220 | spin_unlock(&dcache_lock); |
221 | rcu_read_unlock(); | ||
222 | if (read_seqretry(&rename_lock, seq)) | ||
223 | goto rename_retry; | ||
212 | 224 | ||
213 | return len; | 225 | return len; |
214 | } | 226 | } |