diff options
Diffstat (limited to 'fs/jffs2/nodemgmt.c')
-rw-r--r-- | fs/jffs2/nodemgmt.c | 250 |
1 files changed, 166 insertions, 84 deletions
diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index fe7e70a4055b..208b2bdf01e5 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * | 7 | * |
8 | * For licensing information, see the file 'LICENCE' in this directory. | 8 | * For licensing information, see the file 'LICENCE' in this directory. |
9 | * | 9 | * |
10 | * $Id: nodemgmt.c,v 1.124 2005/07/20 15:32:28 dedekind Exp $ | 10 | * $Id: nodemgmt.c,v 1.125 2005/09/07 08:34:54 havasi Exp $ |
11 | * | 11 | * |
12 | */ | 12 | */ |
13 | 13 | ||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/compiler.h> | 17 | #include <linux/compiler.h> |
18 | #include <linux/sched.h> /* For cond_resched() */ | 18 | #include <linux/sched.h> /* For cond_resched() */ |
19 | #include "nodelist.h" | 19 | #include "nodelist.h" |
20 | #include "debug.h" | ||
20 | 21 | ||
21 | /** | 22 | /** |
22 | * jffs2_reserve_space - request physical space to write nodes to flash | 23 | * jffs2_reserve_space - request physical space to write nodes to flash |
@@ -38,9 +39,11 @@ | |||
38 | * for the requested allocation. | 39 | * for the requested allocation. |
39 | */ | 40 | */ |
40 | 41 | ||
41 | static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len); | 42 | static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, |
43 | uint32_t *ofs, uint32_t *len, uint32_t sumsize); | ||
42 | 44 | ||
43 | int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio) | 45 | int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, |
46 | uint32_t *len, int prio, uint32_t sumsize) | ||
44 | { | 47 | { |
45 | int ret = -EAGAIN; | 48 | int ret = -EAGAIN; |
46 | int blocksneeded = c->resv_blocks_write; | 49 | int blocksneeded = c->resv_blocks_write; |
@@ -129,7 +132,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs | |||
129 | spin_lock(&c->erase_completion_lock); | 132 | spin_lock(&c->erase_completion_lock); |
130 | } | 133 | } |
131 | 134 | ||
132 | ret = jffs2_do_reserve_space(c, minsize, ofs, len); | 135 | ret = jffs2_do_reserve_space(c, minsize, ofs, len, sumsize); |
133 | if (ret) { | 136 | if (ret) { |
134 | D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret)); | 137 | D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret)); |
135 | } | 138 | } |
@@ -140,7 +143,8 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs | |||
140 | return ret; | 143 | return ret; |
141 | } | 144 | } |
142 | 145 | ||
143 | int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len) | 146 | int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, |
147 | uint32_t *len, uint32_t sumsize) | ||
144 | { | 148 | { |
145 | int ret = -EAGAIN; | 149 | int ret = -EAGAIN; |
146 | minsize = PAD(minsize); | 150 | minsize = PAD(minsize); |
@@ -149,7 +153,7 @@ int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t * | |||
149 | 153 | ||
150 | spin_lock(&c->erase_completion_lock); | 154 | spin_lock(&c->erase_completion_lock); |
151 | while(ret == -EAGAIN) { | 155 | while(ret == -EAGAIN) { |
152 | ret = jffs2_do_reserve_space(c, minsize, ofs, len); | 156 | ret = jffs2_do_reserve_space(c, minsize, ofs, len, sumsize); |
153 | if (ret) { | 157 | if (ret) { |
154 | D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret)); | 158 | D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret)); |
155 | } | 159 | } |
@@ -158,105 +162,183 @@ int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t * | |||
158 | return ret; | 162 | return ret; |
159 | } | 163 | } |
160 | 164 | ||
161 | /* Called with alloc sem _and_ erase_completion_lock */ | 165 | |
162 | static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len) | 166 | /* Classify nextblock (clean, dirty of verydirty) and force to select an other one */ |
167 | |||
168 | static void jffs2_close_nextblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) | ||
163 | { | 169 | { |
164 | struct jffs2_eraseblock *jeb = c->nextblock; | 170 | |
171 | /* Check, if we have a dirty block now, or if it was dirty already */ | ||
172 | if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) { | ||
173 | c->dirty_size += jeb->wasted_size; | ||
174 | c->wasted_size -= jeb->wasted_size; | ||
175 | jeb->dirty_size += jeb->wasted_size; | ||
176 | jeb->wasted_size = 0; | ||
177 | if (VERYDIRTY(c, jeb->dirty_size)) { | ||
178 | D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", | ||
179 | jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); | ||
180 | list_add_tail(&jeb->list, &c->very_dirty_list); | ||
181 | } else { | ||
182 | D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", | ||
183 | jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); | ||
184 | list_add_tail(&jeb->list, &c->dirty_list); | ||
185 | } | ||
186 | } else { | ||
187 | D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", | ||
188 | jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); | ||
189 | list_add_tail(&jeb->list, &c->clean_list); | ||
190 | } | ||
191 | c->nextblock = NULL; | ||
192 | |||
193 | } | ||
194 | |||
195 | /* Select a new jeb for nextblock */ | ||
196 | |||
197 | static int jffs2_find_nextblock(struct jffs2_sb_info *c) | ||
198 | { | ||
199 | struct list_head *next; | ||
165 | 200 | ||
166 | restart: | 201 | /* Take the next block off the 'free' list */ |
167 | if (jeb && minsize > jeb->free_size) { | 202 | |
168 | /* Skip the end of this block and file it as having some dirty space */ | 203 | if (list_empty(&c->free_list)) { |
169 | /* If there's a pending write to it, flush now */ | 204 | |
170 | if (jffs2_wbuf_dirty(c)) { | 205 | if (!c->nr_erasing_blocks && |
206 | !list_empty(&c->erasable_list)) { | ||
207 | struct jffs2_eraseblock *ejeb; | ||
208 | |||
209 | ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list); | ||
210 | list_del(&ejeb->list); | ||
211 | list_add_tail(&ejeb->list, &c->erase_pending_list); | ||
212 | c->nr_erasing_blocks++; | ||
213 | jffs2_erase_pending_trigger(c); | ||
214 | D1(printk(KERN_DEBUG "jffs2_find_nextblock: Triggering erase of erasable block at 0x%08x\n", | ||
215 | ejeb->offset)); | ||
216 | } | ||
217 | |||
218 | if (!c->nr_erasing_blocks && | ||
219 | !list_empty(&c->erasable_pending_wbuf_list)) { | ||
220 | D1(printk(KERN_DEBUG "jffs2_find_nextblock: Flushing write buffer\n")); | ||
221 | /* c->nextblock is NULL, no update to c->nextblock allowed */ | ||
171 | spin_unlock(&c->erase_completion_lock); | 222 | spin_unlock(&c->erase_completion_lock); |
172 | D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); | ||
173 | jffs2_flush_wbuf_pad(c); | 223 | jffs2_flush_wbuf_pad(c); |
174 | spin_lock(&c->erase_completion_lock); | 224 | spin_lock(&c->erase_completion_lock); |
175 | jeb = c->nextblock; | 225 | /* Have another go. It'll be on the erasable_list now */ |
176 | goto restart; | 226 | return -EAGAIN; |
177 | } | 227 | } |
178 | c->wasted_size += jeb->free_size; | 228 | |
179 | c->free_size -= jeb->free_size; | 229 | if (!c->nr_erasing_blocks) { |
180 | jeb->wasted_size += jeb->free_size; | 230 | /* Ouch. We're in GC, or we wouldn't have got here. |
181 | jeb->free_size = 0; | 231 | And there's no space left. At all. */ |
182 | 232 | printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n", | |
183 | /* Check, if we have a dirty block now, or if it was dirty already */ | 233 | c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasable_list)?"yes":"no", |
184 | if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) { | 234 | list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"); |
185 | c->dirty_size += jeb->wasted_size; | 235 | return -ENOSPC; |
186 | c->wasted_size -= jeb->wasted_size; | ||
187 | jeb->dirty_size += jeb->wasted_size; | ||
188 | jeb->wasted_size = 0; | ||
189 | if (VERYDIRTY(c, jeb->dirty_size)) { | ||
190 | D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", | ||
191 | jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); | ||
192 | list_add_tail(&jeb->list, &c->very_dirty_list); | ||
193 | } else { | ||
194 | D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", | ||
195 | jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); | ||
196 | list_add_tail(&jeb->list, &c->dirty_list); | ||
197 | } | ||
198 | } else { | ||
199 | D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", | ||
200 | jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); | ||
201 | list_add_tail(&jeb->list, &c->clean_list); | ||
202 | } | 236 | } |
203 | c->nextblock = jeb = NULL; | 237 | |
238 | spin_unlock(&c->erase_completion_lock); | ||
239 | /* Don't wait for it; just erase one right now */ | ||
240 | jffs2_erase_pending_blocks(c, 1); | ||
241 | spin_lock(&c->erase_completion_lock); | ||
242 | |||
243 | /* An erase may have failed, decreasing the | ||
244 | amount of free space available. So we must | ||
245 | restart from the beginning */ | ||
246 | return -EAGAIN; | ||
204 | } | 247 | } |
248 | |||
249 | next = c->free_list.next; | ||
250 | list_del(next); | ||
251 | c->nextblock = list_entry(next, struct jffs2_eraseblock, list); | ||
252 | c->nr_free_blocks--; | ||
205 | 253 | ||
206 | if (!jeb) { | 254 | jffs2_sum_reset_collected(c->summary); /* reset collected summary */ |
207 | struct list_head *next; | ||
208 | /* Take the next block off the 'free' list */ | ||
209 | 255 | ||
210 | if (list_empty(&c->free_list)) { | 256 | D1(printk(KERN_DEBUG "jffs2_find_nextblock(): new nextblock = 0x%08x\n", c->nextblock->offset)); |
211 | 257 | ||
212 | if (!c->nr_erasing_blocks && | 258 | return 0; |
213 | !list_empty(&c->erasable_list)) { | 259 | } |
214 | struct jffs2_eraseblock *ejeb; | ||
215 | 260 | ||
216 | ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list); | 261 | /* Called with alloc sem _and_ erase_completion_lock */ |
217 | list_del(&ejeb->list); | 262 | static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, uint32_t sumsize) |
218 | list_add_tail(&ejeb->list, &c->erase_pending_list); | 263 | { |
219 | c->nr_erasing_blocks++; | 264 | struct jffs2_eraseblock *jeb = c->nextblock; |
220 | jffs2_erase_pending_trigger(c); | 265 | uint32_t reserved_size; /* for summary information at the end of the jeb */ |
221 | D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Triggering erase of erasable block at 0x%08x\n", | 266 | int ret; |
222 | ejeb->offset)); | 267 | |
268 | restart: | ||
269 | reserved_size = 0; | ||
270 | |||
271 | if (jffs2_sum_active() && (sumsize != JFFS2_SUMMARY_NOSUM_SIZE)) { | ||
272 | /* NOSUM_SIZE means not to generate summary */ | ||
273 | |||
274 | if (jeb) { | ||
275 | reserved_size = PAD(sumsize + c->summary->sum_size + JFFS2_SUMMARY_FRAME_SIZE); | ||
276 | JFFS2_DBG_SUMMARY("minsize=%d , jeb->free=%d ," | ||
277 | "summary->size=%d , sumsize=%d\n", | ||
278 | minsize, jeb->free_size, | ||
279 | c->summary->sum_size, sumsize); | ||
280 | } | ||
281 | |||
282 | /* Is there enough space for writing out the current node, or we have to | ||
283 | write out summary information now, close this jeb and select new nextblock? */ | ||
284 | if (jeb && (PAD(minsize) + PAD(c->summary->sum_size + sumsize + | ||
285 | JFFS2_SUMMARY_FRAME_SIZE) > jeb->free_size)) { | ||
286 | |||
287 | /* Has summary been disabled for this jeb? */ | ||
288 | if (jffs2_sum_is_disabled(c->summary)) { | ||
289 | sumsize = JFFS2_SUMMARY_NOSUM_SIZE; | ||
290 | goto restart; | ||
223 | } | 291 | } |
224 | 292 | ||
225 | if (!c->nr_erasing_blocks && | 293 | /* Writing out the collected summary information */ |
226 | !list_empty(&c->erasable_pending_wbuf_list)) { | 294 | JFFS2_DBG_SUMMARY("generating summary for 0x%08x.\n", jeb->offset); |
227 | D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); | 295 | ret = jffs2_sum_write_sumnode(c); |
228 | /* c->nextblock is NULL, no update to c->nextblock allowed */ | 296 | |
297 | if (ret) | ||
298 | return ret; | ||
299 | |||
300 | if (jffs2_sum_is_disabled(c->summary)) { | ||
301 | /* jffs2_write_sumnode() couldn't write out the summary information | ||
302 | diabling summary for this jeb and free the collected information | ||
303 | */ | ||
304 | sumsize = JFFS2_SUMMARY_NOSUM_SIZE; | ||
305 | goto restart; | ||
306 | } | ||
307 | |||
308 | jffs2_close_nextblock(c, jeb); | ||
309 | jeb = NULL; | ||
310 | } | ||
311 | } else { | ||
312 | if (jeb && minsize > jeb->free_size) { | ||
313 | /* Skip the end of this block and file it as having some dirty space */ | ||
314 | /* If there's a pending write to it, flush now */ | ||
315 | |||
316 | if (jffs2_wbuf_dirty(c)) { | ||
229 | spin_unlock(&c->erase_completion_lock); | 317 | spin_unlock(&c->erase_completion_lock); |
318 | D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); | ||
230 | jffs2_flush_wbuf_pad(c); | 319 | jffs2_flush_wbuf_pad(c); |
231 | spin_lock(&c->erase_completion_lock); | 320 | spin_lock(&c->erase_completion_lock); |
232 | /* Have another go. It'll be on the erasable_list now */ | 321 | jeb = c->nextblock; |
233 | return -EAGAIN; | 322 | goto restart; |
234 | } | ||
235 | |||
236 | if (!c->nr_erasing_blocks) { | ||
237 | /* Ouch. We're in GC, or we wouldn't have got here. | ||
238 | And there's no space left. At all. */ | ||
239 | printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n", | ||
240 | c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasable_list)?"yes":"no", | ||
241 | list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"); | ||
242 | return -ENOSPC; | ||
243 | } | 323 | } |
244 | 324 | ||
245 | spin_unlock(&c->erase_completion_lock); | 325 | c->wasted_size += jeb->free_size; |
246 | /* Don't wait for it; just erase one right now */ | 326 | c->free_size -= jeb->free_size; |
247 | jffs2_erase_pending_blocks(c, 1); | 327 | jeb->wasted_size += jeb->free_size; |
248 | spin_lock(&c->erase_completion_lock); | 328 | jeb->free_size = 0; |
249 | 329 | ||
250 | /* An erase may have failed, decreasing the | 330 | jffs2_close_nextblock(c, jeb); |
251 | amount of free space available. So we must | 331 | jeb = NULL; |
252 | restart from the beginning */ | ||
253 | return -EAGAIN; | ||
254 | } | 332 | } |
333 | } | ||
334 | |||
335 | if (!jeb) { | ||
336 | |||
337 | ret = jffs2_find_nextblock(c); | ||
338 | if (ret) | ||
339 | return ret; | ||
255 | 340 | ||
256 | next = c->free_list.next; | 341 | jeb = c->nextblock; |
257 | list_del(next); | ||
258 | c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list); | ||
259 | c->nr_free_blocks--; | ||
260 | 342 | ||
261 | if (jeb->free_size != c->sector_size - c->cleanmarker_size) { | 343 | if (jeb->free_size != c->sector_size - c->cleanmarker_size) { |
262 | printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size); | 344 | printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size); |
@@ -266,7 +348,7 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui | |||
266 | /* OK, jeb (==c->nextblock) is now pointing at a block which definitely has | 348 | /* OK, jeb (==c->nextblock) is now pointing at a block which definitely has |
267 | enough space */ | 349 | enough space */ |
268 | *ofs = jeb->offset + (c->sector_size - jeb->free_size); | 350 | *ofs = jeb->offset + (c->sector_size - jeb->free_size); |
269 | *len = jeb->free_size; | 351 | *len = jeb->free_size - reserved_size; |
270 | 352 | ||
271 | if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size && | 353 | if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size && |
272 | !jeb->first_node->next_in_ino) { | 354 | !jeb->first_node->next_in_ino) { |