Branch data Line data Source code
1 : : /*
2 : : * JFFS2 -- Journalling Flash File System, Version 2.
3 : : *
4 : : * Copyright © 2001-2007 Red Hat, Inc.
5 : : * Copyright © 2004-2010 David Woodhouse <dwmw2@infradead.org>
6 : : *
7 : : * Created by David Woodhouse <dwmw2@infradead.org>
8 : : *
9 : : * For licensing information, see the file 'LICENCE' in this directory.
10 : : *
11 : : */
12 : :
13 : : #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14 : :
15 : : #include <linux/kernel.h>
16 : : #include <linux/types.h>
17 : : #include <linux/pagemap.h>
18 : : #include <linux/crc32.h>
19 : : #include <linux/jffs2.h>
20 : : #include <linux/mtd/mtd.h>
21 : : #include <linux/slab.h>
22 : : #include "nodelist.h"
23 : : #include "debug.h"
24 : :
25 : : #ifdef JFFS2_DBG_SANITY_CHECKS
26 : :
27 : : void
28 : 0 : __jffs2_dbg_acct_sanity_check_nolock(struct jffs2_sb_info *c,
29 : : struct jffs2_eraseblock *jeb)
30 : : {
31 [ # # ][ # # ]: 0 : if (unlikely(jeb && jeb->used_size + jeb->dirty_size +
32 : : jeb->free_size + jeb->wasted_size +
33 : : jeb->unchecked_size != c->sector_size)) {
34 : 0 : JFFS2_ERROR("eeep, space accounting for block at 0x%08x is screwed.\n", jeb->offset);
35 : 0 : JFFS2_ERROR("free %#08x + dirty %#08x + used %#08x + wasted %#08x + unchecked %#08x != total %#08x.\n",
36 : : jeb->free_size, jeb->dirty_size, jeb->used_size,
37 : : jeb->wasted_size, jeb->unchecked_size, c->sector_size);
38 : 0 : BUG();
39 : : }
40 : :
41 [ # # ]: 0 : if (unlikely(c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size
42 : : + c->wasted_size + c->unchecked_size != c->flash_size)) {
43 : 0 : JFFS2_ERROR("eeep, space accounting superblock info is screwed.\n");
44 : 0 : JFFS2_ERROR("free %#08x + dirty %#08x + used %#08x + erasing %#08x + bad %#08x + wasted %#08x + unchecked %#08x != total %#08x.\n",
45 : : c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size,
46 : : c->wasted_size, c->unchecked_size, c->flash_size);
47 : 0 : BUG();
48 : : }
49 : 0 : }
50 : :
51 : : void
52 : 0 : __jffs2_dbg_acct_sanity_check(struct jffs2_sb_info *c,
53 : : struct jffs2_eraseblock *jeb)
54 : : {
55 : : spin_lock(&c->erase_completion_lock);
56 : 0 : jffs2_dbg_acct_sanity_check_nolock(c, jeb);
57 : : spin_unlock(&c->erase_completion_lock);
58 : 0 : }
59 : :
60 : : #endif /* JFFS2_DBG_SANITY_CHECKS */
61 : :
62 : : #ifdef JFFS2_DBG_PARANOIA_CHECKS
63 : : /*
64 : : * Check the fragtree.
65 : : */
66 : : void
67 : : __jffs2_dbg_fragtree_paranoia_check(struct jffs2_inode_info *f)
68 : : {
69 : : mutex_lock(&f->sem);
70 : : __jffs2_dbg_fragtree_paranoia_check_nolock(f);
71 : : mutex_unlock(&f->sem);
72 : : }
73 : :
74 : : void
75 : : __jffs2_dbg_fragtree_paranoia_check_nolock(struct jffs2_inode_info *f)
76 : : {
77 : : struct jffs2_node_frag *frag;
78 : : int bitched = 0;
79 : :
80 : : for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) {
81 : : struct jffs2_full_dnode *fn = frag->node;
82 : :
83 : : if (!fn || !fn->raw)
84 : : continue;
85 : :
86 : : if (ref_flags(fn->raw) == REF_PRISTINE) {
87 : : if (fn->frags > 1) {
88 : : JFFS2_ERROR("REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2.\n",
89 : : ref_offset(fn->raw), fn->frags);
90 : : bitched = 1;
91 : : }
92 : :
93 : : /* A hole node which isn't multi-page should be garbage-collected
94 : : and merged anyway, so we just check for the frag size here,
95 : : rather than mucking around with actually reading the node
96 : : and checking the compression type, which is the real way
97 : : to tell a hole node. */
98 : : if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag)
99 : : && frag_prev(frag)->size < PAGE_CACHE_SIZE && frag_prev(frag)->node) {
100 : : JFFS2_ERROR("REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2.\n",
101 : : ref_offset(fn->raw));
102 : : bitched = 1;
103 : : }
104 : :
105 : : if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag)
106 : : && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) {
107 : : JFFS2_ERROR("REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2.\n",
108 : : ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size);
109 : : bitched = 1;
110 : : }
111 : : }
112 : : }
113 : :
114 : : if (bitched) {
115 : : JFFS2_ERROR("fragtree is corrupted.\n");
116 : : __jffs2_dbg_dump_fragtree_nolock(f);
117 : : BUG();
118 : : }
119 : : }
120 : :
121 : : /*
122 : : * Check if the flash contains all 0xFF before we start writing.
123 : : */
124 : : void
125 : : __jffs2_dbg_prewrite_paranoia_check(struct jffs2_sb_info *c,
126 : : uint32_t ofs, int len)
127 : : {
128 : : size_t retlen;
129 : : int ret, i;
130 : : unsigned char *buf;
131 : :
132 : : buf = kmalloc(len, GFP_KERNEL);
133 : : if (!buf)
134 : : return;
135 : :
136 : : ret = jffs2_flash_read(c, ofs, len, &retlen, buf);
137 : : if (ret || (retlen != len)) {
138 : : JFFS2_WARNING("read %d bytes failed or short. ret %d, retlen %zd.\n",
139 : : len, ret, retlen);
140 : : kfree(buf);
141 : : return;
142 : : }
143 : :
144 : : ret = 0;
145 : : for (i = 0; i < len; i++)
146 : : if (buf[i] != 0xff)
147 : : ret = 1;
148 : :
149 : : if (ret) {
150 : : JFFS2_ERROR("argh, about to write node to %#08x on flash, but there are data already there. The first corrupted byte is at %#08x offset.\n",
151 : : ofs, ofs + i);
152 : : __jffs2_dbg_dump_buffer(buf, len, ofs);
153 : : kfree(buf);
154 : : BUG();
155 : : }
156 : :
157 : : kfree(buf);
158 : : }
159 : :
160 : : void __jffs2_dbg_superblock_counts(struct jffs2_sb_info *c)
161 : : {
162 : : struct jffs2_eraseblock *jeb;
163 : : uint32_t free = 0, dirty = 0, used = 0, wasted = 0,
164 : : erasing = 0, bad = 0, unchecked = 0;
165 : : int nr_counted = 0;
166 : : int dump = 0;
167 : :
168 : : if (c->gcblock) {
169 : : nr_counted++;
170 : : free += c->gcblock->free_size;
171 : : dirty += c->gcblock->dirty_size;
172 : : used += c->gcblock->used_size;
173 : : wasted += c->gcblock->wasted_size;
174 : : unchecked += c->gcblock->unchecked_size;
175 : : }
176 : : if (c->nextblock) {
177 : : nr_counted++;
178 : : free += c->nextblock->free_size;
179 : : dirty += c->nextblock->dirty_size;
180 : : used += c->nextblock->used_size;
181 : : wasted += c->nextblock->wasted_size;
182 : : unchecked += c->nextblock->unchecked_size;
183 : : }
184 : : list_for_each_entry(jeb, &c->clean_list, list) {
185 : : nr_counted++;
186 : : free += jeb->free_size;
187 : : dirty += jeb->dirty_size;
188 : : used += jeb->used_size;
189 : : wasted += jeb->wasted_size;
190 : : unchecked += jeb->unchecked_size;
191 : : }
192 : : list_for_each_entry(jeb, &c->very_dirty_list, list) {
193 : : nr_counted++;
194 : : free += jeb->free_size;
195 : : dirty += jeb->dirty_size;
196 : : used += jeb->used_size;
197 : : wasted += jeb->wasted_size;
198 : : unchecked += jeb->unchecked_size;
199 : : }
200 : : list_for_each_entry(jeb, &c->dirty_list, list) {
201 : : nr_counted++;
202 : : free += jeb->free_size;
203 : : dirty += jeb->dirty_size;
204 : : used += jeb->used_size;
205 : : wasted += jeb->wasted_size;
206 : : unchecked += jeb->unchecked_size;
207 : : }
208 : : list_for_each_entry(jeb, &c->erasable_list, list) {
209 : : nr_counted++;
210 : : free += jeb->free_size;
211 : : dirty += jeb->dirty_size;
212 : : used += jeb->used_size;
213 : : wasted += jeb->wasted_size;
214 : : unchecked += jeb->unchecked_size;
215 : : }
216 : : list_for_each_entry(jeb, &c->erasable_pending_wbuf_list, list) {
217 : : nr_counted++;
218 : : free += jeb->free_size;
219 : : dirty += jeb->dirty_size;
220 : : used += jeb->used_size;
221 : : wasted += jeb->wasted_size;
222 : : unchecked += jeb->unchecked_size;
223 : : }
224 : : list_for_each_entry(jeb, &c->erase_pending_list, list) {
225 : : nr_counted++;
226 : : free += jeb->free_size;
227 : : dirty += jeb->dirty_size;
228 : : used += jeb->used_size;
229 : : wasted += jeb->wasted_size;
230 : : unchecked += jeb->unchecked_size;
231 : : }
232 : : list_for_each_entry(jeb, &c->free_list, list) {
233 : : nr_counted++;
234 : : free += jeb->free_size;
235 : : dirty += jeb->dirty_size;
236 : : used += jeb->used_size;
237 : : wasted += jeb->wasted_size;
238 : : unchecked += jeb->unchecked_size;
239 : : }
240 : : list_for_each_entry(jeb, &c->bad_used_list, list) {
241 : : nr_counted++;
242 : : free += jeb->free_size;
243 : : dirty += jeb->dirty_size;
244 : : used += jeb->used_size;
245 : : wasted += jeb->wasted_size;
246 : : unchecked += jeb->unchecked_size;
247 : : }
248 : :
249 : : list_for_each_entry(jeb, &c->erasing_list, list) {
250 : : nr_counted++;
251 : : erasing += c->sector_size;
252 : : }
253 : : list_for_each_entry(jeb, &c->erase_checking_list, list) {
254 : : nr_counted++;
255 : : erasing += c->sector_size;
256 : : }
257 : : list_for_each_entry(jeb, &c->erase_complete_list, list) {
258 : : nr_counted++;
259 : : erasing += c->sector_size;
260 : : }
261 : : list_for_each_entry(jeb, &c->bad_list, list) {
262 : : nr_counted++;
263 : : bad += c->sector_size;
264 : : }
265 : :
266 : : #define check(sz) \
267 : : do { \
268 : : if (sz != c->sz##_size) { \
269 : : pr_warn("%s_size mismatch counted 0x%x, c->%s_size 0x%x\n", \
270 : : #sz, sz, #sz, c->sz##_size); \
271 : : dump = 1; \
272 : : } \
273 : : } while (0)
274 : :
275 : : check(free);
276 : : check(dirty);
277 : : check(used);
278 : : check(wasted);
279 : : check(unchecked);
280 : : check(bad);
281 : : check(erasing);
282 : :
283 : : #undef check
284 : :
285 : : if (nr_counted != c->nr_blocks) {
286 : : pr_warn("%s counted only 0x%x blocks of 0x%x. Where are the others?\n",
287 : : __func__, nr_counted, c->nr_blocks);
288 : : dump = 1;
289 : : }
290 : :
291 : : if (dump) {
292 : : __jffs2_dbg_dump_block_lists_nolock(c);
293 : : BUG();
294 : : }
295 : : }
296 : :
297 : : /*
298 : : * Check the space accounting and node_ref list correctness for the JFFS2 erasable block 'jeb'.
299 : : */
300 : : void
301 : : __jffs2_dbg_acct_paranoia_check(struct jffs2_sb_info *c,
302 : : struct jffs2_eraseblock *jeb)
303 : : {
304 : : spin_lock(&c->erase_completion_lock);
305 : : __jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
306 : : spin_unlock(&c->erase_completion_lock);
307 : : }
308 : :
309 : : void
310 : : __jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c,
311 : : struct jffs2_eraseblock *jeb)
312 : : {
313 : : uint32_t my_used_size = 0;
314 : : uint32_t my_unchecked_size = 0;
315 : : uint32_t my_dirty_size = 0;
316 : : struct jffs2_raw_node_ref *ref2 = jeb->first_node;
317 : :
318 : : while (ref2) {
319 : : uint32_t totlen = ref_totlen(c, jeb, ref2);
320 : :
321 : : if (ref_offset(ref2) < jeb->offset ||
322 : : ref_offset(ref2) > jeb->offset + c->sector_size) {
323 : : JFFS2_ERROR("node_ref %#08x shouldn't be in block at %#08x.\n",
324 : : ref_offset(ref2), jeb->offset);
325 : : goto error;
326 : :
327 : : }
328 : : if (ref_flags(ref2) == REF_UNCHECKED)
329 : : my_unchecked_size += totlen;
330 : : else if (!ref_obsolete(ref2))
331 : : my_used_size += totlen;
332 : : else
333 : : my_dirty_size += totlen;
334 : :
335 : : if ((!ref_next(ref2)) != (ref2 == jeb->last_node)) {
336 : : JFFS2_ERROR("node_ref for node at %#08x (mem %p) has next at %#08x (mem %p), last_node is at %#08x (mem %p).\n",
337 : : ref_offset(ref2), ref2, ref_offset(ref_next(ref2)), ref_next(ref2),
338 : : ref_offset(jeb->last_node), jeb->last_node);
339 : : goto error;
340 : : }
341 : : ref2 = ref_next(ref2);
342 : : }
343 : :
344 : : if (my_used_size != jeb->used_size) {
345 : : JFFS2_ERROR("Calculated used size %#08x != stored used size %#08x.\n",
346 : : my_used_size, jeb->used_size);
347 : : goto error;
348 : : }
349 : :
350 : : if (my_unchecked_size != jeb->unchecked_size) {
351 : : JFFS2_ERROR("Calculated unchecked size %#08x != stored unchecked size %#08x.\n",
352 : : my_unchecked_size, jeb->unchecked_size);
353 : : goto error;
354 : : }
355 : :
356 : : #if 0
357 : : /* This should work when we implement ref->__totlen elemination */
358 : : if (my_dirty_size != jeb->dirty_size + jeb->wasted_size) {
359 : : JFFS2_ERROR("Calculated dirty+wasted size %#08x != stored dirty + wasted size %#08x\n",
360 : : my_dirty_size, jeb->dirty_size + jeb->wasted_size);
361 : : goto error;
362 : : }
363 : :
364 : : if (jeb->free_size == 0
365 : : && my_used_size + my_unchecked_size + my_dirty_size != c->sector_size) {
366 : : JFFS2_ERROR("The sum of all nodes in block (%#x) != size of block (%#x)\n",
367 : : my_used_size + my_unchecked_size + my_dirty_size,
368 : : c->sector_size);
369 : : goto error;
370 : : }
371 : : #endif
372 : :
373 : : if (!(c->flags & (JFFS2_SB_FLAG_BUILDING|JFFS2_SB_FLAG_SCANNING)))
374 : : __jffs2_dbg_superblock_counts(c);
375 : :
376 : : return;
377 : :
378 : : error:
379 : : __jffs2_dbg_dump_node_refs_nolock(c, jeb);
380 : : __jffs2_dbg_dump_jeb_nolock(jeb);
381 : : __jffs2_dbg_dump_block_lists_nolock(c);
382 : : BUG();
383 : :
384 : : }
385 : : #endif /* JFFS2_DBG_PARANOIA_CHECKS */
386 : :
387 : : #if defined(JFFS2_DBG_DUMPS) || defined(JFFS2_DBG_PARANOIA_CHECKS)
388 : : /*
389 : : * Dump the node_refs of the 'jeb' JFFS2 eraseblock.
390 : : */
391 : : void
392 : : __jffs2_dbg_dump_node_refs(struct jffs2_sb_info *c,
393 : : struct jffs2_eraseblock *jeb)
394 : : {
395 : : spin_lock(&c->erase_completion_lock);
396 : : __jffs2_dbg_dump_node_refs_nolock(c, jeb);
397 : : spin_unlock(&c->erase_completion_lock);
398 : : }
399 : :
400 : : void
401 : : __jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c,
402 : : struct jffs2_eraseblock *jeb)
403 : : {
404 : : struct jffs2_raw_node_ref *ref;
405 : : int i = 0;
406 : :
407 : : printk(JFFS2_DBG_MSG_PREFIX " Dump node_refs of the eraseblock %#08x\n", jeb->offset);
408 : : if (!jeb->first_node) {
409 : : printk(JFFS2_DBG_MSG_PREFIX " no nodes in the eraseblock %#08x\n", jeb->offset);
410 : : return;
411 : : }
412 : :
413 : : printk(JFFS2_DBG);
414 : : for (ref = jeb->first_node; ; ref = ref_next(ref)) {
415 : : printk("%#08x", ref_offset(ref));
416 : : #ifdef TEST_TOTLEN
417 : : printk("(%x)", ref->__totlen);
418 : : #endif
419 : : if (ref_next(ref))
420 : : printk("->");
421 : : else
422 : : break;
423 : : if (++i == 4) {
424 : : i = 0;
425 : : printk("\n" JFFS2_DBG);
426 : : }
427 : : }
428 : : printk("\n");
429 : : }
430 : :
431 : : /*
432 : : * Dump an eraseblock's space accounting.
433 : : */
434 : : void
435 : : __jffs2_dbg_dump_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
436 : : {
437 : : spin_lock(&c->erase_completion_lock);
438 : : __jffs2_dbg_dump_jeb_nolock(jeb);
439 : : spin_unlock(&c->erase_completion_lock);
440 : : }
441 : :
442 : : void
443 : : __jffs2_dbg_dump_jeb_nolock(struct jffs2_eraseblock *jeb)
444 : : {
445 : : if (!jeb)
446 : : return;
447 : :
448 : : printk(JFFS2_DBG_MSG_PREFIX " dump space accounting for the eraseblock at %#08x:\n",
449 : : jeb->offset);
450 : :
451 : : printk(JFFS2_DBG "used_size: %#08x\n", jeb->used_size);
452 : : printk(JFFS2_DBG "dirty_size: %#08x\n", jeb->dirty_size);
453 : : printk(JFFS2_DBG "wasted_size: %#08x\n", jeb->wasted_size);
454 : : printk(JFFS2_DBG "unchecked_size: %#08x\n", jeb->unchecked_size);
455 : : printk(JFFS2_DBG "free_size: %#08x\n", jeb->free_size);
456 : : }
457 : :
458 : : void
459 : : __jffs2_dbg_dump_block_lists(struct jffs2_sb_info *c)
460 : : {
461 : : spin_lock(&c->erase_completion_lock);
462 : : __jffs2_dbg_dump_block_lists_nolock(c);
463 : : spin_unlock(&c->erase_completion_lock);
464 : : }
465 : :
466 : : void
467 : : __jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c)
468 : : {
469 : : printk(JFFS2_DBG_MSG_PREFIX " dump JFFS2 blocks lists:\n");
470 : :
471 : : printk(JFFS2_DBG "flash_size: %#08x\n", c->flash_size);
472 : : printk(JFFS2_DBG "used_size: %#08x\n", c->used_size);
473 : : printk(JFFS2_DBG "dirty_size: %#08x\n", c->dirty_size);
474 : : printk(JFFS2_DBG "wasted_size: %#08x\n", c->wasted_size);
475 : : printk(JFFS2_DBG "unchecked_size: %#08x\n", c->unchecked_size);
476 : : printk(JFFS2_DBG "free_size: %#08x\n", c->free_size);
477 : : printk(JFFS2_DBG "erasing_size: %#08x\n", c->erasing_size);
478 : : printk(JFFS2_DBG "bad_size: %#08x\n", c->bad_size);
479 : : printk(JFFS2_DBG "sector_size: %#08x\n", c->sector_size);
480 : : printk(JFFS2_DBG "jffs2_reserved_blocks size: %#08x\n",
481 : : c->sector_size * c->resv_blocks_write);
482 : :
483 : : if (c->nextblock)
484 : : printk(JFFS2_DBG "nextblock: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n",
485 : : c->nextblock->offset, c->nextblock->used_size,
486 : : c->nextblock->dirty_size, c->nextblock->wasted_size,
487 : : c->nextblock->unchecked_size, c->nextblock->free_size);
488 : : else
489 : : printk(JFFS2_DBG "nextblock: NULL\n");
490 : :
491 : : if (c->gcblock)
492 : : printk(JFFS2_DBG "gcblock: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n",
493 : : c->gcblock->offset, c->gcblock->used_size, c->gcblock->dirty_size,
494 : : c->gcblock->wasted_size, c->gcblock->unchecked_size, c->gcblock->free_size);
495 : : else
496 : : printk(JFFS2_DBG "gcblock: NULL\n");
497 : :
498 : : if (list_empty(&c->clean_list)) {
499 : : printk(JFFS2_DBG "clean_list: empty\n");
500 : : } else {
501 : : struct list_head *this;
502 : : int numblocks = 0;
503 : : uint32_t dirty = 0;
504 : :
505 : : list_for_each(this, &c->clean_list) {
506 : : struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
507 : : numblocks ++;
508 : : dirty += jeb->wasted_size;
509 : : if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
510 : : printk(JFFS2_DBG "clean_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n",
511 : : jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size,
512 : : jeb->unchecked_size, jeb->free_size);
513 : : }
514 : : }
515 : :
516 : : printk (JFFS2_DBG "Contains %d blocks with total wasted size %u, average wasted size: %u\n",
517 : : numblocks, dirty, dirty / numblocks);
518 : : }
519 : :
520 : : if (list_empty(&c->very_dirty_list)) {
521 : : printk(JFFS2_DBG "very_dirty_list: empty\n");
522 : : } else {
523 : : struct list_head *this;
524 : : int numblocks = 0;
525 : : uint32_t dirty = 0;
526 : :
527 : : list_for_each(this, &c->very_dirty_list) {
528 : : struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
529 : :
530 : : numblocks ++;
531 : : dirty += jeb->dirty_size;
532 : : if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
533 : : printk(JFFS2_DBG "very_dirty_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n",
534 : : jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size,
535 : : jeb->unchecked_size, jeb->free_size);
536 : : }
537 : : }
538 : :
539 : : printk (JFFS2_DBG "Contains %d blocks with total dirty size %u, average dirty size: %u\n",
540 : : numblocks, dirty, dirty / numblocks);
541 : : }
542 : :
543 : : if (list_empty(&c->dirty_list)) {
544 : : printk(JFFS2_DBG "dirty_list: empty\n");
545 : : } else {
546 : : struct list_head *this;
547 : : int numblocks = 0;
548 : : uint32_t dirty = 0;
549 : :
550 : : list_for_each(this, &c->dirty_list) {
551 : : struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
552 : :
553 : : numblocks ++;
554 : : dirty += jeb->dirty_size;
555 : : if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
556 : : printk(JFFS2_DBG "dirty_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n",
557 : : jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size,
558 : : jeb->unchecked_size, jeb->free_size);
559 : : }
560 : : }
561 : :
562 : : printk (JFFS2_DBG "contains %d blocks with total dirty size %u, average dirty size: %u\n",
563 : : numblocks, dirty, dirty / numblocks);
564 : : }
565 : :
566 : : if (list_empty(&c->erasable_list)) {
567 : : printk(JFFS2_DBG "erasable_list: empty\n");
568 : : } else {
569 : : struct list_head *this;
570 : :
571 : : list_for_each(this, &c->erasable_list) {
572 : : struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
573 : :
574 : : if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
575 : : printk(JFFS2_DBG "erasable_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n",
576 : : jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size,
577 : : jeb->unchecked_size, jeb->free_size);
578 : : }
579 : : }
580 : : }
581 : :
582 : : if (list_empty(&c->erasing_list)) {
583 : : printk(JFFS2_DBG "erasing_list: empty\n");
584 : : } else {
585 : : struct list_head *this;
586 : :
587 : : list_for_each(this, &c->erasing_list) {
588 : : struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
589 : :
590 : : if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
591 : : printk(JFFS2_DBG "erasing_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n",
592 : : jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size,
593 : : jeb->unchecked_size, jeb->free_size);
594 : : }
595 : : }
596 : : }
597 : : if (list_empty(&c->erase_checking_list)) {
598 : : printk(JFFS2_DBG "erase_checking_list: empty\n");
599 : : } else {
600 : : struct list_head *this;
601 : :
602 : : list_for_each(this, &c->erase_checking_list) {
603 : : struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
604 : :
605 : : if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
606 : : printk(JFFS2_DBG "erase_checking_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n",
607 : : jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size,
608 : : jeb->unchecked_size, jeb->free_size);
609 : : }
610 : : }
611 : : }
612 : :
613 : : if (list_empty(&c->erase_pending_list)) {
614 : : printk(JFFS2_DBG "erase_pending_list: empty\n");
615 : : } else {
616 : : struct list_head *this;
617 : :
618 : : list_for_each(this, &c->erase_pending_list) {
619 : : struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
620 : :
621 : : if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
622 : : printk(JFFS2_DBG "erase_pending_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n",
623 : : jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size,
624 : : jeb->unchecked_size, jeb->free_size);
625 : : }
626 : : }
627 : : }
628 : :
629 : : if (list_empty(&c->erasable_pending_wbuf_list)) {
630 : : printk(JFFS2_DBG "erasable_pending_wbuf_list: empty\n");
631 : : } else {
632 : : struct list_head *this;
633 : :
634 : : list_for_each(this, &c->erasable_pending_wbuf_list) {
635 : : struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
636 : :
637 : : if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
638 : : printk(JFFS2_DBG "erasable_pending_wbuf_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n",
639 : : jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size,
640 : : jeb->unchecked_size, jeb->free_size);
641 : : }
642 : : }
643 : : }
644 : :
645 : : if (list_empty(&c->free_list)) {
646 : : printk(JFFS2_DBG "free_list: empty\n");
647 : : } else {
648 : : struct list_head *this;
649 : :
650 : : list_for_each(this, &c->free_list) {
651 : : struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
652 : :
653 : : if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
654 : : printk(JFFS2_DBG "free_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n",
655 : : jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size,
656 : : jeb->unchecked_size, jeb->free_size);
657 : : }
658 : : }
659 : : }
660 : :
661 : : if (list_empty(&c->bad_list)) {
662 : : printk(JFFS2_DBG "bad_list: empty\n");
663 : : } else {
664 : : struct list_head *this;
665 : :
666 : : list_for_each(this, &c->bad_list) {
667 : : struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
668 : :
669 : : if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
670 : : printk(JFFS2_DBG "bad_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n",
671 : : jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size,
672 : : jeb->unchecked_size, jeb->free_size);
673 : : }
674 : : }
675 : : }
676 : :
677 : : if (list_empty(&c->bad_used_list)) {
678 : : printk(JFFS2_DBG "bad_used_list: empty\n");
679 : : } else {
680 : : struct list_head *this;
681 : :
682 : : list_for_each(this, &c->bad_used_list) {
683 : : struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
684 : :
685 : : if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) {
686 : : printk(JFFS2_DBG "bad_used_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n",
687 : : jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size,
688 : : jeb->unchecked_size, jeb->free_size);
689 : : }
690 : : }
691 : : }
692 : : }
693 : :
694 : : void
695 : : __jffs2_dbg_dump_fragtree(struct jffs2_inode_info *f)
696 : : {
697 : : mutex_lock(&f->sem);
698 : : jffs2_dbg_dump_fragtree_nolock(f);
699 : : mutex_unlock(&f->sem);
700 : : }
701 : :
702 : : void
703 : : __jffs2_dbg_dump_fragtree_nolock(struct jffs2_inode_info *f)
704 : : {
705 : : struct jffs2_node_frag *this = frag_first(&f->fragtree);
706 : : uint32_t lastofs = 0;
707 : : int buggy = 0;
708 : :
709 : : printk(JFFS2_DBG_MSG_PREFIX " dump fragtree of ino #%u\n", f->inocache->ino);
710 : : while(this) {
711 : : if (this->node)
712 : : printk(JFFS2_DBG "frag %#04x-%#04x: %#08x(%d) on flash (*%p), left (%p), right (%p), parent (%p)\n",
713 : : this->ofs, this->ofs+this->size, ref_offset(this->node->raw),
714 : : ref_flags(this->node->raw), this, frag_left(this), frag_right(this),
715 : : frag_parent(this));
716 : : else
717 : : printk(JFFS2_DBG "frag %#04x-%#04x: hole (*%p). left (%p), right (%p), parent (%p)\n",
718 : : this->ofs, this->ofs+this->size, this, frag_left(this),
719 : : frag_right(this), frag_parent(this));
720 : : if (this->ofs != lastofs)
721 : : buggy = 1;
722 : : lastofs = this->ofs + this->size;
723 : : this = frag_next(this);
724 : : }
725 : :
726 : : if (f->metadata)
727 : : printk(JFFS2_DBG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
728 : :
729 : : if (buggy) {
730 : : JFFS2_ERROR("frag tree got a hole in it.\n");
731 : : BUG();
732 : : }
733 : : }
734 : :
735 : : #define JFFS2_BUFDUMP_BYTES_PER_LINE 32
736 : : void
737 : : __jffs2_dbg_dump_buffer(unsigned char *buf, int len, uint32_t offs)
738 : : {
739 : : int skip;
740 : : int i;
741 : :
742 : : printk(JFFS2_DBG_MSG_PREFIX " dump from offset %#08x to offset %#08x (%x bytes).\n",
743 : : offs, offs + len, len);
744 : : i = skip = offs % JFFS2_BUFDUMP_BYTES_PER_LINE;
745 : : offs = offs & ~(JFFS2_BUFDUMP_BYTES_PER_LINE - 1);
746 : :
747 : : if (skip != 0)
748 : : printk(JFFS2_DBG "%#08x: ", offs);
749 : :
750 : : while (skip--)
751 : : printk(" ");
752 : :
753 : : while (i < len) {
754 : : if ((i % JFFS2_BUFDUMP_BYTES_PER_LINE) == 0 && i != len -1) {
755 : : if (i != 0)
756 : : printk("\n");
757 : : offs += JFFS2_BUFDUMP_BYTES_PER_LINE;
758 : : printk(JFFS2_DBG "%0#8x: ", offs);
759 : : }
760 : :
761 : : printk("%02x ", buf[i]);
762 : :
763 : : i += 1;
764 : : }
765 : :
766 : : printk("\n");
767 : : }
768 : :
769 : : /*
770 : : * Dump a JFFS2 node.
771 : : */
772 : : void
773 : : __jffs2_dbg_dump_node(struct jffs2_sb_info *c, uint32_t ofs)
774 : : {
775 : : union jffs2_node_union node;
776 : : int len = sizeof(union jffs2_node_union);
777 : : size_t retlen;
778 : : uint32_t crc;
779 : : int ret;
780 : :
781 : : printk(JFFS2_DBG_MSG_PREFIX " dump node at offset %#08x.\n", ofs);
782 : :
783 : : ret = jffs2_flash_read(c, ofs, len, &retlen, (unsigned char *)&node);
784 : : if (ret || (retlen != len)) {
785 : : JFFS2_ERROR("read %d bytes failed or short. ret %d, retlen %zd.\n",
786 : : len, ret, retlen);
787 : : return;
788 : : }
789 : :
790 : : printk(JFFS2_DBG "magic:\t%#04x\n", je16_to_cpu(node.u.magic));
791 : : printk(JFFS2_DBG "nodetype:\t%#04x\n", je16_to_cpu(node.u.nodetype));
792 : : printk(JFFS2_DBG "totlen:\t%#08x\n", je32_to_cpu(node.u.totlen));
793 : : printk(JFFS2_DBG "hdr_crc:\t%#08x\n", je32_to_cpu(node.u.hdr_crc));
794 : :
795 : : crc = crc32(0, &node.u, sizeof(node.u) - 4);
796 : : if (crc != je32_to_cpu(node.u.hdr_crc)) {
797 : : JFFS2_ERROR("wrong common header CRC.\n");
798 : : return;
799 : : }
800 : :
801 : : if (je16_to_cpu(node.u.magic) != JFFS2_MAGIC_BITMASK &&
802 : : je16_to_cpu(node.u.magic) != JFFS2_OLD_MAGIC_BITMASK)
803 : : {
804 : : JFFS2_ERROR("wrong node magic: %#04x instead of %#04x.\n",
805 : : je16_to_cpu(node.u.magic), JFFS2_MAGIC_BITMASK);
806 : : return;
807 : : }
808 : :
809 : : switch(je16_to_cpu(node.u.nodetype)) {
810 : :
811 : : case JFFS2_NODETYPE_INODE:
812 : :
813 : : printk(JFFS2_DBG "the node is inode node\n");
814 : : printk(JFFS2_DBG "ino:\t%#08x\n", je32_to_cpu(node.i.ino));
815 : : printk(JFFS2_DBG "version:\t%#08x\n", je32_to_cpu(node.i.version));
816 : : printk(JFFS2_DBG "mode:\t%#08x\n", node.i.mode.m);
817 : : printk(JFFS2_DBG "uid:\t%#04x\n", je16_to_cpu(node.i.uid));
818 : : printk(JFFS2_DBG "gid:\t%#04x\n", je16_to_cpu(node.i.gid));
819 : : printk(JFFS2_DBG "isize:\t%#08x\n", je32_to_cpu(node.i.isize));
820 : : printk(JFFS2_DBG "atime:\t%#08x\n", je32_to_cpu(node.i.atime));
821 : : printk(JFFS2_DBG "mtime:\t%#08x\n", je32_to_cpu(node.i.mtime));
822 : : printk(JFFS2_DBG "ctime:\t%#08x\n", je32_to_cpu(node.i.ctime));
823 : : printk(JFFS2_DBG "offset:\t%#08x\n", je32_to_cpu(node.i.offset));
824 : : printk(JFFS2_DBG "csize:\t%#08x\n", je32_to_cpu(node.i.csize));
825 : : printk(JFFS2_DBG "dsize:\t%#08x\n", je32_to_cpu(node.i.dsize));
826 : : printk(JFFS2_DBG "compr:\t%#02x\n", node.i.compr);
827 : : printk(JFFS2_DBG "usercompr:\t%#02x\n", node.i.usercompr);
828 : : printk(JFFS2_DBG "flags:\t%#04x\n", je16_to_cpu(node.i.flags));
829 : : printk(JFFS2_DBG "data_crc:\t%#08x\n", je32_to_cpu(node.i.data_crc));
830 : : printk(JFFS2_DBG "node_crc:\t%#08x\n", je32_to_cpu(node.i.node_crc));
831 : :
832 : : crc = crc32(0, &node.i, sizeof(node.i) - 8);
833 : : if (crc != je32_to_cpu(node.i.node_crc)) {
834 : : JFFS2_ERROR("wrong node header CRC.\n");
835 : : return;
836 : : }
837 : : break;
838 : :
839 : : case JFFS2_NODETYPE_DIRENT:
840 : :
841 : : printk(JFFS2_DBG "the node is dirent node\n");
842 : : printk(JFFS2_DBG "pino:\t%#08x\n", je32_to_cpu(node.d.pino));
843 : : printk(JFFS2_DBG "version:\t%#08x\n", je32_to_cpu(node.d.version));
844 : : printk(JFFS2_DBG "ino:\t%#08x\n", je32_to_cpu(node.d.ino));
845 : : printk(JFFS2_DBG "mctime:\t%#08x\n", je32_to_cpu(node.d.mctime));
846 : : printk(JFFS2_DBG "nsize:\t%#02x\n", node.d.nsize);
847 : : printk(JFFS2_DBG "type:\t%#02x\n", node.d.type);
848 : : printk(JFFS2_DBG "node_crc:\t%#08x\n", je32_to_cpu(node.d.node_crc));
849 : : printk(JFFS2_DBG "name_crc:\t%#08x\n", je32_to_cpu(node.d.name_crc));
850 : :
851 : : node.d.name[node.d.nsize] = '\0';
852 : : printk(JFFS2_DBG "name:\t\"%s\"\n", node.d.name);
853 : :
854 : : crc = crc32(0, &node.d, sizeof(node.d) - 8);
855 : : if (crc != je32_to_cpu(node.d.node_crc)) {
856 : : JFFS2_ERROR("wrong node header CRC.\n");
857 : : return;
858 : : }
859 : : break;
860 : :
861 : : default:
862 : : printk(JFFS2_DBG "node type is unknown\n");
863 : : break;
864 : : }
865 : : }
866 : : #endif /* JFFS2_DBG_DUMPS || JFFS2_DBG_PARANOIA_CHECKS */
|