aboutsummaryrefslogtreecommitdiffstats
path: root/fs/jffs2/nodemgmt.c
diff options
context:
space:
mode:
authorFerenc Havasi <havasi@inf.u-szeged.hu>2005-09-07 04:35:26 -0400
committerThomas Gleixner <tglx@mtd.linutronix.de>2005-11-06 15:29:48 -0500
commite631ddba588783edd521c5a89f7b2902772fb691 (patch)
treee25f322ee498b344f058ce4a40060baa22a5f105 /fs/jffs2/nodemgmt.c
parent15017876751e4c2d786ba95920618359fe2b4f0a (diff)
[JFFS2] Add erase block summary support (mount time improvement)
The goal of summary is to speed up the mount time. Erase block summary (EBS) stores summary information at the end of every (closed) erase block. It is no longer necessary to scan all nodes separetly (and read all pages of them) just read this "small" summary, where every information is stored which is needed at mount time. This summary information is stored in a JFFS2_FEATURE_RWCOMPAT_DELETE. During the mount process if there is no summary info the orignal scan process will be executed. EBS works with NAND and NOR flashes, too. There is a user space tool called sumtool to generate this summary information for a JFFS2 image. Signed-off-by: Ferenc Havasi <havasi@inf.u-szeged.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'fs/jffs2/nodemgmt.c')
-rw-r--r--fs/jffs2/nodemgmt.c250
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
41static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len); 42static 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
43int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio) 45int 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
143int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len) 146int 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
162static 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
168static 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
197static 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); 262static 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) {