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