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/nfs/namespace.c | |
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/nfs/namespace.c')
-rw-r--r-- | fs/nfs/namespace.c | 14 |
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 | ||
56 | rename_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; |
84 | Elong_unlock: | 93 | Elong_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; | ||
86 | Elong: | 98 | Elong: |
87 | return ERR_PTR(-ENAMETOOLONG); | 99 | return ERR_PTR(-ENAMETOOLONG); |
88 | } | 100 | } |