Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 85a62db6 authored by David Woodhouse's avatar David Woodhouse
Browse files

[JFFS2] Add paranoia debugging for superblock counts



The problem fixed in commit 014b164e
(space leak with in-band cleanmarkers) would have been caught a lot
quicker if our paranoid debugging mode had included adding up the size
counts from all the eraseblocks and comparing the totals with the counts
in the superblock. Add that.

Make jffs2_mark_erased_block() file the newly-erased block on the
free_list before calling the debug function, to make it happy.

Signed-off-by: default avatarDavid Woodhouse <dwmw2@infradead.org>
parent 014b164e
Loading
Loading
Loading
Loading
+132 −0
Original line number Diff line number Diff line
@@ -153,6 +153,135 @@ __jffs2_dbg_prewrite_paranoia_check(struct jffs2_sb_info *c,
	kfree(buf);
}

void __jffs2_dbg_superblock_counts(struct jffs2_sb_info *c)
{
	struct jffs2_eraseblock *jeb;
	uint32_t free = 0, dirty = 0, used = 0, wasted = 0,
		erasing = 0, bad = 0, unchecked = 0;
	int nr_counted = 0;
	int dump = 0;

	if (c->gcblock) {
		nr_counted++;
		free += c->gcblock->free_size;
		dirty += c->gcblock->dirty_size;
		used += c->gcblock->used_size;
		wasted += c->gcblock->wasted_size;
		unchecked += c->gcblock->unchecked_size;
	}
	if (c->nextblock) {
		nr_counted++;
		free += c->nextblock->free_size;
		dirty += c->nextblock->dirty_size;
		used += c->nextblock->used_size;
		wasted += c->nextblock->wasted_size;
		unchecked += c->nextblock->unchecked_size;
	}
	list_for_each_entry(jeb, &c->clean_list, list) {
		nr_counted++;
		free += jeb->free_size;
		dirty += jeb->dirty_size;
		used += jeb->used_size;
		wasted += jeb->wasted_size;
		unchecked += jeb->unchecked_size;
	}
	list_for_each_entry(jeb, &c->very_dirty_list, list) {
		nr_counted++;
		free += jeb->free_size;
		dirty += jeb->dirty_size;
		used += jeb->used_size;
		wasted += jeb->wasted_size;
		unchecked += jeb->unchecked_size;
	}
	list_for_each_entry(jeb, &c->dirty_list, list) {
		nr_counted++;
		free += jeb->free_size;
		dirty += jeb->dirty_size;
		used += jeb->used_size;
		wasted += jeb->wasted_size;
		unchecked += jeb->unchecked_size;
	}
	list_for_each_entry(jeb, &c->erasable_list, list) {
		nr_counted++;
		free += jeb->free_size;
		dirty += jeb->dirty_size;
		used += jeb->used_size;
		wasted += jeb->wasted_size;
		unchecked += jeb->unchecked_size;
	}
	list_for_each_entry(jeb, &c->erasable_pending_wbuf_list, list) {
		nr_counted++;
		free += jeb->free_size;
		dirty += jeb->dirty_size;
		used += jeb->used_size;
		wasted += jeb->wasted_size;
		unchecked += jeb->unchecked_size;
	}
	list_for_each_entry(jeb, &c->erase_pending_list, list) {
		nr_counted++;
		free += jeb->free_size;
		dirty += jeb->dirty_size;
		used += jeb->used_size;
		wasted += jeb->wasted_size;
		unchecked += jeb->unchecked_size;
	}
	list_for_each_entry(jeb, &c->free_list, list) {
		nr_counted++;
		free += jeb->free_size;
		dirty += jeb->dirty_size;
		used += jeb->used_size;
		wasted += jeb->wasted_size;
		unchecked += jeb->unchecked_size;
	}
	list_for_each_entry(jeb, &c->bad_used_list, list) {
		nr_counted++;
		free += jeb->free_size;
		dirty += jeb->dirty_size;
		used += jeb->used_size;
		wasted += jeb->wasted_size;
		unchecked += jeb->unchecked_size;
	}

	list_for_each_entry(jeb, &c->erasing_list, list) {
		nr_counted++;
		erasing += c->sector_size;
	}
	list_for_each_entry(jeb, &c->erase_complete_list, list) {
		nr_counted++;
		erasing += c->sector_size;
	}
	list_for_each_entry(jeb, &c->bad_list, list) {
		nr_counted++;
		bad += c->sector_size;
	}

#define check(sz) \
	if (sz != c->sz##_size) {			\
		printk(KERN_WARNING #sz "_size mismatch counted 0x%x, c->" #sz "_size 0x%x\n", \
		       sz, c->sz##_size);		\
		dump = 1;				\
	}
	check(free);
	check(dirty);
	check(used);
	check(wasted);
	check(unchecked);
	check(bad);
	check(erasing);
#undef check

	if (nr_counted != c->nr_blocks) {
		printk(KERN_WARNING "%s counted only 0x%x blocks of 0x%x. Where are the others?\n",
		       __func__, nr_counted, c->nr_blocks);
		dump = 1;
	}

	if (dump) {
		__jffs2_dbg_dump_block_lists_nolock(c);
		BUG();
	}
}

/*
 * Check the space accounting and node_ref list correctness for the JFFS2 erasable block 'jeb'.
 */
@@ -229,6 +358,9 @@ __jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c,
	}
#endif

	if (!(c->flags & (JFFS2_SB_FLAG_BUILDING|JFFS2_SB_FLAG_SCANNING)))
		__jffs2_dbg_superblock_counts(c);

	return;

error:
+4 −3
Original line number Diff line number Diff line
@@ -460,12 +460,13 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
	if (c->cleanmarker_size && !jffs2_cleanmarker_oob(c))
		jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL);

	jffs2_dbg_acct_sanity_check_nolock(c,jeb);
	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);

	list_add_tail(&jeb->list, &c->free_list);
	c->nr_erasing_blocks--;
	c->nr_free_blocks++;

	jffs2_dbg_acct_sanity_check_nolock(c, jeb);
	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);

	spin_unlock(&c->erase_completion_lock);
	mutex_unlock(&c->erase_free_sem);
	wake_up(&c->erase_wait);