最近发现的 __nand_calculate_ecc(buf,eccsize,code)中buf地址非4字节对齐导致的异常问题, 经查发现是由于fs/jffs2/gc.c中
jffs2_garbage_collect_live()函数中获取的start值非对齐引起,这个start值传给给jffs2_garbage_collect_dnode(),
在jffs2_garbage_collect_dnode()中,会根据start值算出对应的frag(frag=jffs2_lookup_node_frag(&f->fragtree,start)),然
后找这个frag的前一个frag, 如下代码所示:
while((frag = frag_prev(frag)) && frag->ofs >= min) {
/* If the previous frag doesn't even reach the beginning, there's
excessive fragmentation. Just merge. */
if (frag->ofs > min) {
D1(printk(KERN_DEBUG "Expanding down to cover partial frag (0x%x-0x%x)\n",
frag->ofs, frag->ofs+frag->size));
start = frag->ofs;
continue;
}
/* OK. This frag holds the first byte of the page. */
if (!frag->node || !frag->node->raw) {
D1(printk(KERN_DEBUG "First frag in page is hole (0x%x-0x%x). Not expanding down.\n",
frag->ofs, frag->ofs+frag->size));
start = frag->ofs;
break;
} else {
/* OK, it's a frag which extends to the beginning of the page. Does it live
in a block which is still considered clean? If so, don't obsolete it.
If not, cover it anyway. */
struct jffs2_raw_node_ref *raw = frag->node->raw;
struct jffs2_eraseblock *jeb;
jeb = &c->blocks[raw->flash_offset / c->sector_size];
if (jeb == c->gcblock) {
D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in gcblock at %08x\n",
frag->ofs, frag->ofs+frag->size, ref_offset(raw)));
start = frag->ofs;
break;
}
if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
D1(printk(KERN_DEBUG "Not expanding down to cover frag (0x%x-0x%x) in clean block %08x\n",
frag->ofs, frag->ofs+frag->size, jeb->offset));
//add for fix bug--start
if(start%4){
start = frag->ofs;
printk("expanding down to cover frag (0x%x-0x%x) in clean block %08x, anyway, other wise unalligned exception
will be raised if start address unalligned\n",frag->ofs, frag->ofs+frag->size, jeb->offset);
}
//add for fix bug--start
break;
}
D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in dirty block %08x\n",
frag->ofs, frag->ofs+frag->size, jeb->offset));
start = frag->ofs;
break;
}
}
正常情况下,找到前一个frag的frag->ofs是大于min的,而且是4字节对齐的,但现在的情况是frag->ofs刚好等于min, 而且又满足
if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) 条件,导致start没有更新,start仍然未对齐,进而导致异常。
这个问题是更换toolchain(从uclibc到glibc)引起的,用uclibc时,while中的if (frag->ofs > min) 条件满足,start被更新成对齐的地址。
更深层的原因暂时没能找到,暂时作如此修改。
另外,发现linux内核跑起来后,过了大概10几秒钟,会跑到jffs2_garbage_collect_pass()中的以下代码,不断调用jffs2_erase_pending_blocks(),
/* If there are any blocks which need erasing, erase them now */
if (!list_empty(&c->erase_complete_list) ||
!list_empty(&c->erase_pending_list)) {
spin_unlock(&c->erase_completion_lock);
D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() erasing pending blocks\n"));
if (jffs2_erase_pending_blocks(c, 1)) {
mutex_unlock(&c->alloc_sem);
return 0;
}
D1(printk(KERN_DEBUG "No progress from erasing blocks; doing GC anyway\n"));
spin_lock(&c->erase_completion_lock);
}
在jffs2_erase_pending_blocks()中,也只是调用到jffs2_mark_erased_block()。这个过程会持续大概半分钟,在这半分钟内,几乎不能运行其它APP,
不知道是否正常,以前的版本也没有注意,使用uclibc toolchain也一样。