这几天在调研yaffs在SEP0611上的可行性。由于当前开发板上使用的NAND芯片是K9HCG08U1M,是4K页,128字节OOB区的M芯片。因为yaffs2中要写入至少16字节的tags,而0611的BCH16模式会产生28bytes/K的ECC码,所以在这种芯片上不能实现yaffs。(OOB区的前2个字节要留下来,给坏块判断用)
TCC8900上是采用的yaffs文件系统(依稀记得也是采用的K9HCG08U1M,恨哪,没看清楚),而且spare data为32字节。ECC为160字节。所以一直不知道它是怎么处理这些数据的。而且ECC的代码写的极其繁琐复杂,研究了一天半才发现TCC采用的NAND芯片为K9LBG08U0D,是4K页,218字节00B区的MLC芯片。oh ,my GOD。疯了,都怪开始没看仔细。
2011.04.20:
昨天发现yaffs上存在的一个bug。是关于inband-tags的使用。
那么什么是yaffs的inband-tags呢?我们知道yaffs是在OOB区中存放tags信息的。但是会不会出现OOB区中空间不够,无法完整的存放tags信息的情况呢?(SEP0611上就出现了这种情况,因为ECC控制模块的最小为BCH16)。
出现这种情况怎么办呢?是不是就不能使用yaffs呢?事实上yaffs的开发者已经考虑到了这种情况,如果OOB区中空间不够的话,那么yaffs会将这些tags信息保存在数据的末尾(其实就是缩短了数据区的长度),这种情况在yaffs称为inband-tags。
在static int yaffs_CreateInitialDirectories(yaffs_Device *dev)函数中:
- /* Sort out space for inband tags, if required */
-
if (dev->inbandTags)
-
dev->nDataBytesPerChunk=dev->totalBytesPerChunk-sizeof(yaffs_PackedTags2TagsPart);
-
else
-
dev->nDataBytesPerChunk = dev->totalBytesPerChunk;
来设定nDataBytesPerChunk的数值,显然如果是inbandtags的话,就需要从数据区中预留出tags的空间。
在函数yaffs_internal_read_super中有:
dev->inbandTags = options.inband_tags;
可知inbandTags 是由options.inband_tags的值觉得的。那么下一步就是去找options.inband_tags了。
事实上我为了偷工减料。直接在初始化函数中设置dev->inbandTags = 1了。然后挂载yaffs文件系统使用iozone来进行性能的测试。挂载的时候会出现下面的打印信息(打印信息是我添加的。)
yaffs_GutsInitialise:ndatabytesperchunk:4080。
说明此事每个chunk的数据长度为4080个字节(page大小为4096字节)。
但是在测试的过程中发现了文件系统老是报Erasure failed的错误,通过跟踪代码发现是在下面的一段代码发生错误的。
- erasedOk = yaffs_EraseBlockInNAND(dev, blockNo);
-
if (!erasedOk) {
-
dev->nErasureFailures++;
-
T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
-
(TSTR("**>> Erasure failed %d" TENDSTR), blockNo));
-
}
反正是由擦除失败造成的错误的错误。
- int nandmtd_EraseBlockInNAND(yaffs_Device * dev, int blockNumber)
-
{
-
struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
-
__u32 addr =
-
((loff_t) blockNumber) * dev->nDataBytesPerChunk
-
* dev->nChunksPerBlock;
-
struct erase_info ei;
-
int retval = 0;
-
-
ei.mtd = mtd;
-
ei.addr = addr;
-
ei.len = dev->nDataBytesPerChunk * dev->nChunksPerBlock;
-
ei.time = 1000;
-
ei.retries = 2;
-
ei.callback = NULL;
-
ei.priv = (u_long) dev;
-
sema_init(&dev->sem, 0);
-
retval = mtd->erase(mtd, &ei);
我看到这段代码的时候就感觉上面红色字体的部分不太对,因为在inband-tags的时候,dev->nDataBytesPerChunk不等于pagesize,所以这样计算需要擦除的地址就会算错。
mtd->erase函数指针最终指向nand_erase函数。
nand_erase ->nand_erase_nand;
- int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
-
int allowbbt)
-
{
-
int page, status, pages_per_block, ret, chipnr;
-
struct nand_chip *chip = mtd->priv;
-
loff_t rewrite_bbt[NAND_MAX_CHIPS]={0};
-
unsigned int bbt_masked_page = 0xffffffff;
-
loff_t len;
-
-
DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%012llx, len = %llu\n",
-
(unsigned long long)instr->addr, (unsigned long long)instr->len);
-
-
/* Start address must align on block boundary */
-
if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) {
-
DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
-
return -EINVAL;
-
}
-
-
/* Length must align on block boundary */
-
if (instr->len & ((1 << chip->phys_erase_shift) - 1)) {
-
DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
-
"Length not block aligned\n");
-
return -EINVAL;
-
}
注意上述代码中红色字体部分,这段代码检查addr是不是blocksize对齐的,同时也检查要擦除的长度len是不是blocksize对齐的。如果按照nandmtd_EraseBlockInNAND中那么写的话,addr以及len都不可能对齐的。
今天下载了最新的yaffs的代码,发现这个bug已经被修正了。
阅读(1645) | 评论(0) | 转发(0) |