diff options
-rw-r--r-- | fs/dcache.c | 88 |
1 files changed, 37 insertions, 51 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 611ffe928c03..361882a14ccb 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -1012,6 +1012,34 @@ void shrink_dcache_for_umount(struct super_block *sb) | |||
1012 | } | 1012 | } |
1013 | 1013 | ||
1014 | /* | 1014 | /* |
1015 | * This tries to ascend one level of parenthood, but | ||
1016 | * we can race with renaming, so we need to re-check | ||
1017 | * the parenthood after dropping the lock and check | ||
1018 | * that the sequence number still matches. | ||
1019 | */ | ||
1020 | static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq) | ||
1021 | { | ||
1022 | struct dentry *new = old->d_parent; | ||
1023 | |||
1024 | rcu_read_lock(); | ||
1025 | spin_unlock(&old->d_lock); | ||
1026 | spin_lock(&new->d_lock); | ||
1027 | |||
1028 | /* | ||
1029 | * might go back up the wrong parent if we have had a rename | ||
1030 | * or deletion | ||
1031 | */ | ||
1032 | if (new != old->d_parent || | ||
1033 | (!locked && read_seqretry(&rename_lock, seq))) { | ||
1034 | spin_unlock(&new->d_lock); | ||
1035 | new = NULL; | ||
1036 | } | ||
1037 | rcu_read_unlock(); | ||
1038 | return new; | ||
1039 | } | ||
1040 | |||
1041 | |||
1042 | /* | ||
1015 | * Search for at least 1 mount point in the dentry's subdirs. | 1043 | * Search for at least 1 mount point in the dentry's subdirs. |
1016 | * We descend to the next level whenever the d_subdirs | 1044 | * We descend to the next level whenever the d_subdirs |
1017 | * list is non-empty and continue searching. | 1045 | * list is non-empty and continue searching. |
@@ -1066,24 +1094,10 @@ resume: | |||
1066 | * All done at this level ... ascend and resume the search. | 1094 | * All done at this level ... ascend and resume the search. |
1067 | */ | 1095 | */ |
1068 | if (this_parent != parent) { | 1096 | if (this_parent != parent) { |
1069 | struct dentry *tmp; | 1097 | struct dentry *child = this_parent; |
1070 | struct dentry *child; | 1098 | this_parent = try_to_ascend(this_parent, locked, seq); |
1071 | 1099 | 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; | 1100 | goto rename_retry; |
1085 | } | ||
1086 | rcu_read_unlock(); | ||
1087 | next = child->d_u.d_child.next; | 1101 | next = child->d_u.d_child.next; |
1088 | goto resume; | 1102 | goto resume; |
1089 | } | 1103 | } |
@@ -1181,24 +1195,10 @@ resume: | |||
1181 | * All done at this level ... ascend and resume the search. | 1195 | * All done at this level ... ascend and resume the search. |
1182 | */ | 1196 | */ |
1183 | if (this_parent != parent) { | 1197 | if (this_parent != parent) { |
1184 | struct dentry *tmp; | 1198 | struct dentry *child = this_parent; |
1185 | struct dentry *child; | 1199 | this_parent = try_to_ascend(this_parent, locked, seq); |
1186 | 1200 | 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; | 1201 | goto rename_retry; |
1200 | } | ||
1201 | rcu_read_unlock(); | ||
1202 | next = child->d_u.d_child.next; | 1202 | next = child->d_u.d_child.next; |
1203 | goto resume; | 1203 | goto resume; |
1204 | } | 1204 | } |
@@ -2942,28 +2942,14 @@ resume: | |||
2942 | spin_unlock(&dentry->d_lock); | 2942 | spin_unlock(&dentry->d_lock); |
2943 | } | 2943 | } |
2944 | if (this_parent != root) { | 2944 | if (this_parent != root) { |
2945 | struct dentry *tmp; | 2945 | struct dentry *child = this_parent; |
2946 | struct dentry *child; | ||
2947 | |||
2948 | tmp = this_parent->d_parent; | ||
2949 | if (!(this_parent->d_flags & DCACHE_GENOCIDE)) { | 2946 | if (!(this_parent->d_flags & DCACHE_GENOCIDE)) { |
2950 | this_parent->d_flags |= DCACHE_GENOCIDE; | 2947 | this_parent->d_flags |= DCACHE_GENOCIDE; |
2951 | this_parent->d_count--; | 2948 | this_parent->d_count--; |
2952 | } | 2949 | } |
2953 | rcu_read_lock(); | 2950 | this_parent = try_to_ascend(this_parent, locked, seq); |
2954 | spin_unlock(&this_parent->d_lock); | 2951 | 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; | 2952 | goto rename_retry; |
2965 | } | ||
2966 | rcu_read_unlock(); | ||
2967 | next = child->d_u.d_child.next; | 2953 | next = child->d_u.d_child.next; |
2968 | goto resume; | 2954 | goto resume; |
2969 | } | 2955 | } |