aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2013-09-05 05:44:35 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2013-09-05 16:22:44 -0400
commitdb14fc3abcd5dcc9b32ad5b9dd5b8f9e16295a39 (patch)
tree174f3dee50f4d89f893f3bca8937bd1837e265df
parent01ddc4ede5f09cdaed015d49ab66eec179085a2e (diff)
vfs: add d_walk()
This one replaces three instances open coded tree walking (have_submounts, select_parent, d_genocide) with a common helper. In addition to slightly reducing the kernel size, this simplifies the callers and makes them less bug prone. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/dcache.c309
1 files changed, 148 insertions, 161 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index f792e9f20eca..043c5b478a77 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1031,34 +1031,56 @@ static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq
1031 return new; 1031 return new;
1032} 1032}
1033 1033
1034/**
1035 * enum d_walk_ret - action to talke during tree walk
1036 * @D_WALK_CONTINUE: contrinue walk
1037 * @D_WALK_QUIT: quit walk
1038 * @D_WALK_NORETRY: quit when retry is needed
1039 * @D_WALK_SKIP: skip this dentry and its children
1040 */
1041enum d_walk_ret {
1042 D_WALK_CONTINUE,
1043 D_WALK_QUIT,
1044 D_WALK_NORETRY,
1045 D_WALK_SKIP,
1046};
1034 1047
1035/*
1036 * Search for at least 1 mount point in the dentry's subdirs.
1037 * We descend to the next level whenever the d_subdirs
1038 * list is non-empty and continue searching.
1039 */
1040
1041/** 1048/**
1042 * have_submounts - check for mounts over a dentry 1049 * d_walk - walk the dentry tree
1043 * @parent: dentry to check. 1050 * @parent: start of walk
1051 * @data: data passed to @enter() and @finish()
1052 * @enter: callback when first entering the dentry
1053 * @finish: callback when successfully finished the walk
1044 * 1054 *
1045 * Return true if the parent or its subdirectories contain 1055 * The @enter() and @finish() callbacks are called with d_lock held.
1046 * a mount point
1047 */ 1056 */
1048int have_submounts(struct dentry *parent) 1057static void d_walk(struct dentry *parent, void *data,
1058 enum d_walk_ret (*enter)(void *, struct dentry *),
1059 void (*finish)(void *))
1049{ 1060{
1050 struct dentry *this_parent; 1061 struct dentry *this_parent;
1051 struct list_head *next; 1062 struct list_head *next;
1052 unsigned seq; 1063 unsigned seq;
1053 int locked = 0; 1064 int locked = 0;
1065 enum d_walk_ret ret;
1066 bool retry = true;
1054 1067
1055 seq = read_seqbegin(&rename_lock); 1068 seq = read_seqbegin(&rename_lock);
1056again: 1069again:
1057 this_parent = parent; 1070 this_parent = parent;
1058
1059 if (d_mountpoint(parent))
1060 goto positive;
1061 spin_lock(&this_parent->d_lock); 1071 spin_lock(&this_parent->d_lock);
1072
1073 ret = enter(data, this_parent);
1074 switch (ret) {
1075 case D_WALK_CONTINUE:
1076 break;
1077 case D_WALK_QUIT:
1078 case D_WALK_SKIP:
1079 goto out_unlock;
1080 case D_WALK_NORETRY:
1081 retry = false;
1082 break;
1083 }
1062repeat: 1084repeat:
1063 next = this_parent->d_subdirs.next; 1085 next = this_parent->d_subdirs.next;
1064resume: 1086resume:
@@ -1068,12 +1090,22 @@ resume:
1068 next = tmp->next; 1090 next = tmp->next;
1069 1091
1070 spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); 1092 spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
1071 /* Have we found a mount point ? */ 1093
1072 if (d_mountpoint(dentry)) { 1094 ret = enter(data, dentry);
1095 switch (ret) {
1096 case D_WALK_CONTINUE:
1097 break;
1098 case D_WALK_QUIT:
1073 spin_unlock(&dentry->d_lock); 1099 spin_unlock(&dentry->d_lock);
1074 spin_unlock(&this_parent->d_lock); 1100 goto out_unlock;
1075 goto positive; 1101 case D_WALK_NORETRY:
1102 retry = false;
1103 break;
1104 case D_WALK_SKIP:
1105 spin_unlock(&dentry->d_lock);
1106 continue;
1076 } 1107 }
1108
1077 if (!list_empty(&dentry->d_subdirs)) { 1109 if (!list_empty(&dentry->d_subdirs)) {
1078 spin_unlock(&this_parent->d_lock); 1110 spin_unlock(&this_parent->d_lock);
1079 spin_release(&dentry->d_lock.dep_map, 1, _RET_IP_); 1111 spin_release(&dentry->d_lock.dep_map, 1, _RET_IP_);
@@ -1094,26 +1126,61 @@ resume:
1094 next = child->d_u.d_child.next; 1126 next = child->d_u.d_child.next;
1095 goto resume; 1127 goto resume;
1096 } 1128 }
1097 spin_unlock(&this_parent->d_lock); 1129 if (!locked && read_seqretry(&rename_lock, seq)) {
1098 if (!locked && read_seqretry(&rename_lock, seq)) 1130 spin_unlock(&this_parent->d_lock);
1099 goto rename_retry;
1100 if (locked)
1101 write_sequnlock(&rename_lock);
1102 return 0; /* No mount points found in tree */
1103positive:
1104 if (!locked && read_seqretry(&rename_lock, seq))
1105 goto rename_retry; 1131 goto rename_retry;
1132 }
1133 if (finish)
1134 finish(data);
1135
1136out_unlock:
1137 spin_unlock(&this_parent->d_lock);
1106 if (locked) 1138 if (locked)
1107 write_sequnlock(&rename_lock); 1139 write_sequnlock(&rename_lock);
1108 return 1; 1140 return;
1109 1141
1110rename_retry: 1142rename_retry:
1143 if (!retry)
1144 return;
1111 if (locked) 1145 if (locked)
1112 goto again; 1146 goto again;
1113 locked = 1; 1147 locked = 1;
1114 write_seqlock(&rename_lock); 1148 write_seqlock(&rename_lock);
1115 goto again; 1149 goto again;
1116} 1150}
1151
1152/*
1153 * Search for at least 1 mount point in the dentry's subdirs.
1154 * We descend to the next level whenever the d_subdirs
1155 * list is non-empty and continue searching.
1156 */
1157
1158/**
1159 * have_submounts - check for mounts over a dentry
1160 * @parent: dentry to check.
1161 *
1162 * Return true if the parent or its subdirectories contain
1163 * a mount point
1164 */
1165
1166static enum d_walk_ret check_mount(void *data, struct dentry *dentry)
1167{
1168 int *ret = data;
1169 if (d_mountpoint(dentry)) {
1170 *ret = 1;
1171 return D_WALK_QUIT;
1172 }
1173 return D_WALK_CONTINUE;
1174}
1175
1176int have_submounts(struct dentry *parent)
1177{
1178 int ret = 0;
1179
1180 d_walk(parent, &ret, check_mount, NULL);
1181
1182 return ret;
1183}
1117EXPORT_SYMBOL(have_submounts); 1184EXPORT_SYMBOL(have_submounts);
1118 1185
1119/* 1186/*
@@ -1130,93 +1197,46 @@ EXPORT_SYMBOL(have_submounts);
1130 * drop the lock and return early due to latency 1197 * drop the lock and return early due to latency
1131 * constraints. 1198 * constraints.
1132 */ 1199 */
1133static int select_parent(struct dentry *parent, struct list_head *dispose)
1134{
1135 struct dentry *this_parent;
1136 struct list_head *next;
1137 unsigned seq;
1138 int found = 0;
1139 int locked = 0;
1140 1200
1141 seq = read_seqbegin(&rename_lock); 1201struct select_data {
1142again: 1202 struct dentry *start;
1143 this_parent = parent; 1203 struct list_head dispose;
1144 spin_lock(&this_parent->d_lock); 1204 int found;
1145repeat: 1205};
1146 next = this_parent->d_subdirs.next;
1147resume:
1148 while (next != &this_parent->d_subdirs) {
1149 struct list_head *tmp = next;
1150 struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child);
1151 next = tmp->next;
1152
1153 spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
1154 1206
1155 /* 1207static enum d_walk_ret select_collect(void *_data, struct dentry *dentry)
1156 * move only zero ref count dentries to the dispose list. 1208{
1157 * 1209 struct select_data *data = _data;
1158 * Those which are presently on the shrink list, being processed 1210 enum d_walk_ret ret = D_WALK_CONTINUE;
1159 * by shrink_dentry_list(), shouldn't be moved. Otherwise the
1160 * loop in shrink_dcache_parent() might not make any progress
1161 * and loop forever.
1162 */
1163 if (dentry->d_lockref.count) {
1164 dentry_lru_del(dentry);
1165 } else if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) {
1166 dentry_lru_move_list(dentry, dispose);
1167 dentry->d_flags |= DCACHE_SHRINK_LIST;
1168 found++;
1169 }
1170 /*
1171 * We can return to the caller if we have found some (this
1172 * ensures forward progress). We'll be coming back to find
1173 * the rest.
1174 */
1175 if (found && need_resched()) {
1176 spin_unlock(&dentry->d_lock);
1177 goto out;
1178 }
1179 1211
1180 /* 1212 if (data->start == dentry)
1181 * Descend a level if the d_subdirs list is non-empty. 1213 goto out;
1182 */
1183 if (!list_empty(&dentry->d_subdirs)) {
1184 spin_unlock(&this_parent->d_lock);
1185 spin_release(&dentry->d_lock.dep_map, 1, _RET_IP_);
1186 this_parent = dentry;
1187 spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_);
1188 goto repeat;
1189 }
1190 1214
1191 spin_unlock(&dentry->d_lock);
1192 }
1193 /* 1215 /*
1194 * All done at this level ... ascend and resume the search. 1216 * move only zero ref count dentries to the dispose list.
1217 *
1218 * Those which are presently on the shrink list, being processed
1219 * by shrink_dentry_list(), shouldn't be moved. Otherwise the
1220 * loop in shrink_dcache_parent() might not make any progress
1221 * and loop forever.
1195 */ 1222 */
1196 if (this_parent != parent) { 1223 if (dentry->d_lockref.count) {
1197 struct dentry *child = this_parent; 1224 dentry_lru_del(dentry);
1198 this_parent = try_to_ascend(this_parent, locked, seq); 1225 } else if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) {
1199 if (!this_parent) 1226 dentry_lru_move_list(dentry, &data->dispose);
1200 goto rename_retry; 1227 dentry->d_flags |= DCACHE_SHRINK_LIST;
1201 next = child->d_u.d_child.next; 1228 data->found++;
1202 goto resume; 1229 ret = D_WALK_NORETRY;
1203 } 1230 }
1231 /*
1232 * We can return to the caller if we have found some (this
1233 * ensures forward progress). We'll be coming back to find
1234 * the rest.
1235 */
1236 if (data->found && need_resched())
1237 ret = D_WALK_QUIT;
1204out: 1238out:
1205 spin_unlock(&this_parent->d_lock); 1239 return ret;
1206 if (!locked && read_seqretry(&rename_lock, seq))
1207 goto rename_retry;
1208 if (locked)
1209 write_sequnlock(&rename_lock);
1210 return found;
1211
1212rename_retry:
1213 if (found)
1214 return found;
1215 if (locked)
1216 goto again;
1217 locked = 1;
1218 write_seqlock(&rename_lock);
1219 goto again;
1220} 1240}
1221 1241
1222/** 1242/**
@@ -1225,13 +1245,20 @@ rename_retry:
1225 * 1245 *
1226 * Prune the dcache to remove unused children of the parent dentry. 1246 * Prune the dcache to remove unused children of the parent dentry.
1227 */ 1247 */
1228void shrink_dcache_parent(struct dentry * parent) 1248void shrink_dcache_parent(struct dentry *parent)
1229{ 1249{
1230 LIST_HEAD(dispose); 1250 for (;;) {
1231 int found; 1251 struct select_data data;
1232 1252
1233 while ((found = select_parent(parent, &dispose)) != 0) { 1253 INIT_LIST_HEAD(&data.dispose);
1234 shrink_dentry_list(&dispose); 1254 data.start = parent;
1255 data.found = 0;
1256
1257 d_walk(parent, &data, select_collect, NULL);
1258 if (!data.found)
1259 break;
1260
1261 shrink_dentry_list(&data.dispose);
1235 cond_resched(); 1262 cond_resched();
1236 } 1263 }
1237} 1264}
@@ -2928,64 +2955,24 @@ int is_subdir(struct dentry *new_dentry, struct dentry *old_dentry)
2928 return result; 2955 return result;
2929} 2956}
2930 2957
2931void d_genocide(struct dentry *root) 2958static enum d_walk_ret d_genocide_kill(void *data, struct dentry *dentry)
2932{ 2959{
2933 struct dentry *this_parent; 2960 struct dentry *root = data;
2934 struct list_head *next; 2961 if (dentry != root) {
2935 unsigned seq; 2962 if (d_unhashed(dentry) || !dentry->d_inode)
2936 int locked = 0; 2963 return D_WALK_SKIP;
2937 2964
2938 seq = read_seqbegin(&rename_lock);
2939again:
2940 this_parent = root;
2941 spin_lock(&this_parent->d_lock);
2942repeat:
2943 next = this_parent->d_subdirs.next;
2944resume:
2945 while (next != &this_parent->d_subdirs) {
2946 struct list_head *tmp = next;
2947 struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child);
2948 next = tmp->next;
2949
2950 spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
2951 if (d_unhashed(dentry) || !dentry->d_inode) {
2952 spin_unlock(&dentry->d_lock);
2953 continue;
2954 }
2955 if (!(dentry->d_flags & DCACHE_GENOCIDE)) { 2965 if (!(dentry->d_flags & DCACHE_GENOCIDE)) {
2956 dentry->d_flags |= DCACHE_GENOCIDE; 2966 dentry->d_flags |= DCACHE_GENOCIDE;
2957 dentry->d_lockref.count--; 2967 dentry->d_lockref.count--;
2958 } 2968 }
2959 if (!list_empty(&dentry->d_subdirs)) {
2960 spin_unlock(&this_parent->d_lock);
2961 spin_release(&dentry->d_lock.dep_map, 1, _RET_IP_);
2962 this_parent = dentry;
2963 spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_);
2964 goto repeat;
2965 }
2966 spin_unlock(&dentry->d_lock);
2967 } 2969 }
2968 if (this_parent != root) { 2970 return D_WALK_CONTINUE;
2969 struct dentry *child = this_parent; 2971}
2970 this_parent = try_to_ascend(this_parent, locked, seq);
2971 if (!this_parent)
2972 goto rename_retry;
2973 next = child->d_u.d_child.next;
2974 goto resume;
2975 }
2976 spin_unlock(&this_parent->d_lock);
2977 if (!locked && read_seqretry(&rename_lock, seq))
2978 goto rename_retry;
2979 if (locked)
2980 write_sequnlock(&rename_lock);
2981 return;
2982 2972
2983rename_retry: 2973void d_genocide(struct dentry *parent)
2984 if (locked) 2974{
2985 goto again; 2975 d_walk(parent, parent, d_genocide_kill, NULL);
2986 locked = 1;
2987 write_seqlock(&rename_lock);
2988 goto again;
2989} 2976}
2990 2977
2991void d_tmpfile(struct dentry *dentry, struct inode *inode) 2978void d_tmpfile(struct dentry *dentry, struct inode *inode)