diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ubifs/recovery.c | 77 |
1 files changed, 50 insertions, 27 deletions
diff --git a/fs/ubifs/recovery.c b/fs/ubifs/recovery.c index 3d2598deaa58..11776ae8caf6 100644 --- a/fs/ubifs/recovery.c +++ b/fs/ubifs/recovery.c | |||
@@ -1070,6 +1070,53 @@ int ubifs_clean_lebs(const struct ubifs_info *c, void *sbuf) | |||
1070 | } | 1070 | } |
1071 | 1071 | ||
1072 | /** | 1072 | /** |
1073 | * grab_empty_leb - grab an empty LEB to use as GC LEB and run commit. | ||
1074 | * @c: UBIFS file-system description object | ||
1075 | * | ||
1076 | * This is a helper function for 'ubifs_rcvry_gc_commit()' which grabs an empty | ||
1077 | * LEB to be used as GC LEB (@c->gc_lnum), and then runs the commit. Returns | ||
1078 | * zero in case of success and a negative error code in case of failure. | ||
1079 | */ | ||
1080 | static int grab_empty_leb(struct ubifs_info *c) | ||
1081 | { | ||
1082 | int lnum, err; | ||
1083 | |||
1084 | /* | ||
1085 | * Note, it is very important to first search for an empty LEB and then | ||
1086 | * run the commit, not vice-versa. The reason is that there might be | ||
1087 | * only one empty LEB at the moment, the one which has been the | ||
1088 | * @c->gc_lnum just before the power cut happened. During the regular | ||
1089 | * UBIFS operation (not now) @c->gc_lnum is marked as "taken", so no | ||
1090 | * one but GC can grab it. But at this moment this single empty LEB is | ||
1091 | * not marked as taken, so if we run commit - what happens? Right, the | ||
1092 | * commit will grab it and write the index there. Remember that the | ||
1093 | * index always expands as long as there is free space, and it only | ||
1094 | * starts consolidating when we run out of space. | ||
1095 | * | ||
1096 | * IOW, if we run commit now, we might not be able to find a free LEB | ||
1097 | * after this. | ||
1098 | */ | ||
1099 | lnum = ubifs_find_free_leb_for_idx(c); | ||
1100 | if (lnum < 0) { | ||
1101 | dbg_err("could not find an empty LEB"); | ||
1102 | dbg_dump_lprops(c); | ||
1103 | dbg_dump_budg(c, &c->bi); | ||
1104 | return lnum; | ||
1105 | } | ||
1106 | |||
1107 | /* Reset the index flag */ | ||
1108 | err = ubifs_change_one_lp(c, lnum, LPROPS_NC, LPROPS_NC, 0, | ||
1109 | LPROPS_INDEX, 0); | ||
1110 | if (err) | ||
1111 | return err; | ||
1112 | |||
1113 | c->gc_lnum = lnum; | ||
1114 | dbg_rcvry("found empty LEB %d, run commit", lnum); | ||
1115 | |||
1116 | return ubifs_run_commit(c); | ||
1117 | } | ||
1118 | |||
1119 | /** | ||
1073 | * ubifs_rcvry_gc_commit - recover the GC LEB number and run the commit. | 1120 | * ubifs_rcvry_gc_commit - recover the GC LEB number and run the commit. |
1074 | * @c: UBIFS file-system description object | 1121 | * @c: UBIFS file-system description object |
1075 | * | 1122 | * |
@@ -1096,7 +1143,7 @@ int ubifs_rcvry_gc_commit(struct ubifs_info *c) | |||
1096 | c->gc_lnum = -1; | 1143 | c->gc_lnum = -1; |
1097 | if (wbuf->lnum == -1) { | 1144 | if (wbuf->lnum == -1) { |
1098 | dbg_rcvry("no GC head LEB"); | 1145 | dbg_rcvry("no GC head LEB"); |
1099 | goto find_free; | 1146 | return grab_empty_leb(c); |
1100 | } | 1147 | } |
1101 | /* | 1148 | /* |
1102 | * See whether the used space in the dirtiest LEB fits in the GC head | 1149 | * See whether the used space in the dirtiest LEB fits in the GC head |
@@ -1104,7 +1151,7 @@ int ubifs_rcvry_gc_commit(struct ubifs_info *c) | |||
1104 | */ | 1151 | */ |
1105 | if (wbuf->offs == c->leb_size) { | 1152 | if (wbuf->offs == c->leb_size) { |
1106 | dbg_rcvry("no room in GC head LEB"); | 1153 | dbg_rcvry("no room in GC head LEB"); |
1107 | goto find_free; | 1154 | return grab_empty_leb(c); |
1108 | } | 1155 | } |
1109 | err = ubifs_find_dirty_leb(c, &lp, wbuf->offs, 2); | 1156 | err = ubifs_find_dirty_leb(c, &lp, wbuf->offs, 2); |
1110 | if (err) { | 1157 | if (err) { |
@@ -1121,7 +1168,7 @@ int ubifs_rcvry_gc_commit(struct ubifs_info *c) | |||
1121 | */ | 1168 | */ |
1122 | if (err == -ENOSPC) { | 1169 | if (err == -ENOSPC) { |
1123 | dbg_rcvry("could not find a dirty LEB"); | 1170 | dbg_rcvry("could not find a dirty LEB"); |
1124 | goto find_free; | 1171 | return grab_empty_leb(c); |
1125 | } | 1172 | } |
1126 | return err; | 1173 | return err; |
1127 | } | 1174 | } |
@@ -1167,30 +1214,6 @@ int ubifs_rcvry_gc_commit(struct ubifs_info *c) | |||
1167 | return err; | 1214 | return err; |
1168 | dbg_rcvry("allocated LEB %d for GC", lnum); | 1215 | dbg_rcvry("allocated LEB %d for GC", lnum); |
1169 | return 0; | 1216 | return 0; |
1170 | |||
1171 | find_free: | ||
1172 | /* | ||
1173 | * There is no GC head LEB or the free space in the GC head LEB is too | ||
1174 | * small, or there are not dirty LEBs. Allocate gc_lnum by calling | ||
1175 | * 'ubifs_find_free_leb_for_idx()' so GC is not run. | ||
1176 | */ | ||
1177 | lnum = ubifs_find_free_leb_for_idx(c); | ||
1178 | if (lnum < 0) { | ||
1179 | dbg_err("could not find an empty LEB"); | ||
1180 | dbg_dump_lprops(c); | ||
1181 | dbg_dump_budg(c, &c->bi); | ||
1182 | return lnum; | ||
1183 | } | ||
1184 | /* And reset the index flag */ | ||
1185 | err = ubifs_change_one_lp(c, lnum, LPROPS_NC, LPROPS_NC, 0, | ||
1186 | LPROPS_INDEX, 0); | ||
1187 | if (err) | ||
1188 | return err; | ||
1189 | c->gc_lnum = lnum; | ||
1190 | dbg_rcvry("allocated LEB %d for GC", lnum); | ||
1191 | /* Run the commit */ | ||
1192 | dbg_rcvry("committing"); | ||
1193 | return ubifs_run_commit(c); | ||
1194 | } | 1217 | } |
1195 | 1218 | ||
1196 | /** | 1219 | /** |