diff options
Diffstat (limited to 'fs/dcache.c')
-rw-r--r-- | fs/dcache.c | 95 |
1 files changed, 43 insertions, 52 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 611ffe928c03..a39fe47c466f 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -296,8 +296,12 @@ static struct dentry *d_kill(struct dentry *dentry, struct dentry *parent) | |||
296 | __releases(parent->d_lock) | 296 | __releases(parent->d_lock) |
297 | __releases(dentry->d_inode->i_lock) | 297 | __releases(dentry->d_inode->i_lock) |
298 | { | 298 | { |
299 | dentry->d_parent = NULL; | ||
300 | list_del(&dentry->d_u.d_child); | 299 | list_del(&dentry->d_u.d_child); |
300 | /* | ||
301 | * Inform try_to_ascend() that we are no longer attached to the | ||
302 | * dentry tree | ||
303 | */ | ||
304 | dentry->d_flags |= DCACHE_DISCONNECTED; | ||
301 | if (parent) | 305 | if (parent) |
302 | spin_unlock(&parent->d_lock); | 306 | spin_unlock(&parent->d_lock); |
303 | dentry_iput(dentry); | 307 | dentry_iput(dentry); |
@@ -1012,6 +1016,35 @@ void shrink_dcache_for_umount(struct super_block *sb) | |||
1012 | } | 1016 | } |
1013 | 1017 | ||
1014 | /* | 1018 | /* |
1019 | * This tries to ascend one level of parenthood, but | ||
1020 | * we can race with renaming, so we need to re-check | ||
1021 | * the parenthood after dropping the lock and check | ||
1022 | * that the sequence number still matches. | ||
1023 | */ | ||
1024 | static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq) | ||
1025 | { | ||
1026 | struct dentry *new = old->d_parent; | ||
1027 | |||
1028 | rcu_read_lock(); | ||
1029 | spin_unlock(&old->d_lock); | ||
1030 | spin_lock(&new->d_lock); | ||
1031 | |||
1032 | /* | ||
1033 | * might go back up the wrong parent if we have had a rename | ||
1034 | * or deletion | ||
1035 | */ | ||
1036 | if (new != old->d_parent || | ||
1037 | (old->d_flags & DCACHE_DISCONNECTED) || | ||
1038 | (!locked && read_seqretry(&rename_lock, seq))) { | ||
1039 | spin_unlock(&new->d_lock); | ||
1040 | new = NULL; | ||
1041 | } | ||
1042 | rcu_read_unlock(); | ||
1043 | return new; | ||
1044 | } | ||
1045 | |||
1046 | |||
1047 | /* | ||
1015 | * Search for at least 1 mount point in the dentry's subdirs. | 1048 | * Search for at least 1 mount point in the dentry's subdirs. |
1016 | * We descend to the next level whenever the d_subdirs | 1049 | * We descend to the next level whenever the d_subdirs |
1017 | * list is non-empty and continue searching. | 1050 | * list is non-empty and continue searching. |
@@ -1066,24 +1099,10 @@ resume: | |||
1066 | * All done at this level ... ascend and resume the search. | 1099 | * All done at this level ... ascend and resume the search. |
1067 | */ | 1100 | */ |
1068 | if (this_parent != parent) { | 1101 | if (this_parent != parent) { |
1069 | struct dentry *tmp; | 1102 | struct dentry *child = this_parent; |
1070 | struct dentry *child; | 1103 | this_parent = try_to_ascend(this_parent, locked, seq); |
1071 | 1104 | if (!this_parent) | |
1072 | tmp = this_parent->d_parent; | ||
1073 | rcu_read_lock(); | ||
1074 | spin_unlock(&this_parent->d_lock); | ||
1075 | child = this_parent; | ||
1076 | this_parent = tmp; | ||
1077 | spin_lock(&this_parent->d_lock); | ||
1078 | /* might go back up the wrong parent if we have had a rename | ||
1079 | * or deletion */ | ||
1080 | if (this_parent != child->d_parent || | ||
1081 | (!locked && read_seqretry(&rename_lock, seq))) { | ||
1082 | spin_unlock(&this_parent->d_lock); | ||
1083 | rcu_read_unlock(); | ||
1084 | goto rename_retry; | 1105 | goto rename_retry; |
1085 | } | ||
1086 | rcu_read_unlock(); | ||
1087 | next = child->d_u.d_child.next; | 1106 | next = child->d_u.d_child.next; |
1088 | goto resume; | 1107 | goto resume; |
1089 | } | 1108 | } |
@@ -1181,24 +1200,10 @@ resume: | |||
1181 | * All done at this level ... ascend and resume the search. | 1200 | * All done at this level ... ascend and resume the search. |
1182 | */ | 1201 | */ |
1183 | if (this_parent != parent) { | 1202 | if (this_parent != parent) { |
1184 | struct dentry *tmp; | 1203 | struct dentry *child = this_parent; |
1185 | struct dentry *child; | 1204 | this_parent = try_to_ascend(this_parent, locked, seq); |
1186 | 1205 | if (!this_parent) | |
1187 | tmp = this_parent->d_parent; | ||
1188 | rcu_read_lock(); | ||
1189 | spin_unlock(&this_parent->d_lock); | ||
1190 | child = this_parent; | ||
1191 | this_parent = tmp; | ||
1192 | spin_lock(&this_parent->d_lock); | ||
1193 | /* might go back up the wrong parent if we have had a rename | ||
1194 | * or deletion */ | ||
1195 | if (this_parent != child->d_parent || | ||
1196 | (!locked && read_seqretry(&rename_lock, seq))) { | ||
1197 | spin_unlock(&this_parent->d_lock); | ||
1198 | rcu_read_unlock(); | ||
1199 | goto rename_retry; | 1206 | goto rename_retry; |
1200 | } | ||
1201 | rcu_read_unlock(); | ||
1202 | next = child->d_u.d_child.next; | 1207 | next = child->d_u.d_child.next; |
1203 | goto resume; | 1208 | goto resume; |
1204 | } | 1209 | } |
@@ -2942,28 +2947,14 @@ resume: | |||
2942 | spin_unlock(&dentry->d_lock); | 2947 | spin_unlock(&dentry->d_lock); |
2943 | } | 2948 | } |
2944 | if (this_parent != root) { | 2949 | if (this_parent != root) { |
2945 | struct dentry *tmp; | 2950 | struct dentry *child = this_parent; |
2946 | struct dentry *child; | ||
2947 | |||
2948 | tmp = this_parent->d_parent; | ||
2949 | if (!(this_parent->d_flags & DCACHE_GENOCIDE)) { | 2951 | if (!(this_parent->d_flags & DCACHE_GENOCIDE)) { |
2950 | this_parent->d_flags |= DCACHE_GENOCIDE; | 2952 | this_parent->d_flags |= DCACHE_GENOCIDE; |
2951 | this_parent->d_count--; | 2953 | this_parent->d_count--; |
2952 | } | 2954 | } |
2953 | rcu_read_lock(); | 2955 | this_parent = try_to_ascend(this_parent, locked, seq); |
2954 | spin_unlock(&this_parent->d_lock); | 2956 | if (!this_parent) |
2955 | child = this_parent; | ||
2956 | this_parent = tmp; | ||
2957 | spin_lock(&this_parent->d_lock); | ||
2958 | /* might go back up the wrong parent if we have had a rename | ||
2959 | * or deletion */ | ||
2960 | if (this_parent != child->d_parent || | ||
2961 | (!locked && read_seqretry(&rename_lock, seq))) { | ||
2962 | spin_unlock(&this_parent->d_lock); | ||
2963 | rcu_read_unlock(); | ||
2964 | goto rename_retry; | 2957 | goto rename_retry; |
2965 | } | ||
2966 | rcu_read_unlock(); | ||
2967 | next = child->d_u.d_child.next; | 2958 | next = child->d_u.d_child.next; |
2968 | goto resume; | 2959 | goto resume; |
2969 | } | 2960 | } |