据说Yaffs很好,适合大容量Nand,mount快,比Jaffs好.不过这些都是百闻,我还没一见.对文件系统,还没完整看过一个文件系统的代码.
不过,现在我们用的jffs2也实在太慢了,整个系统启动起来 ,需要70秒.看来得试试yaffs2
Day 1:2009年2月21日 星期四
今天开始动手porting yaffs2到内核2.6.17上,
打上patch后,发现mount成功,可读写,但是umount后,文件消失.
发现在nandmtd2_ReadChunkWithTagsFromNAND中read_oob 直接读出来oob原始数据,没有加上offset
Day2:
在add_mtd_partitions函数中加上:
->. = ->;
否则在yaffs中用 mtd->oobavail ,发现等于0
在yaffs_mtdif2.c中:
_u8 rawOobBuf[64];
在函数nandmtd2_ReadChunkWithTagsFromNAND中
if (!dev->inbandTags && tags)
{
retval =
mtd->read_oob(mtd, addr, mtd->oobsize, &dummy,dev->spareBuffer);
}
修改为:
if (!dev->inbandTags && tags)
{
retval =
mtd->read_oob(mtd, addr, mtd->oobsize, &dummy,
rawOobBuf);
translateSparetoOOB(mtd,rawOobBuf,dev->spareBuffer);//translate here
}
static int translateSparetoOOB(struct mtd_info *mtd , const __u8 * spare, __u8 * oob)
{
struct nand_oobinfo *oobsel;
int i, len, ofs,j;
oobsel = &mtd->oobinfo;
ofs = 0;
for (i = 0, len = 0; len < mtd->oobavail; i++) {
int from = oobsel->oobfree[i][0];
int num = oobsel->oobfree[i][1];
for(j= 0 ; j
//memcpy (oob, spare[from], num);
len += num;
oob += num;
}
}
很奇怪,在translateSparetoOOB中,用memcpy就不行,必须要一个一个赋值 。
弄了大半天,原来,应该写成:
memcpy (oob, &spare[from], num);
一个粗心,半天都没查出来 ,很....
这样translateSparetoOOB函数改为:
static int translateSparetoOOB(struct mtd_info *mtd , const __u8 * spare, __u8 * oob)
{
struct nand_oobinfo *oobsel;
int i, len, ofs,j;
oobsel = &mtd->oobinfo;
ofs = 0;
for (i = 0, len = 0; len < mtd->oobavail; i++) {
int from = oobsel->oobfree[i][0];
int num = oobsel->oobfree[i][1];
memcpy (oob, &spare[from], num);
len += num;
oob += num;
}
}
看起来简洁了很多,不知道效率是否有提高 ?
这样后,{BANNED}中国第一天出现的问题: mount 后,添加的文件在umount后消失了的问题解决了。
不过在测试中发现:
cp /etc/welcome .
然后可以查看文件内容。
umount 再mount 后,ls 发现文件还在,但cat welcome , 文件内容不对 。
看来得好好分析一下文件系统。
Day 3: 2009年2月21日 星期六
今天闲来无事,到上随便翻翻,突然看到一篇文章
文章里写到:对于1K或者2K bytes的Nand ,其 byteCount为2 bytes ,如果这样的话,那么yaffs_PackedTags2TagsPart就可以由16bytes缩减为14bytes .但发现在函数yaffs_UpdateObjectHeader中,byteCount为文件的大小,也就是说对于chunk ,如果是page chunk的话,bytes count表示该chunk中的有效字节,但对header chunk,bytecount表示的却是文件的大小.
不过 chunkID 可以改为unsigned short,这样使得yaffs2支持的文件大小由8G变为128MB .但这已经足够了,我们系统的文件不可能大于128MB .
同时:函数yaffs_ECCCalculateOther是计算tags的ecc,而yaffs_PackedTags2TagsPart为16bytes ,
yaffs_ECCCalculateOther的参数nBytes为16,这样,lineParity和lineParityPrime也只需要16位就可以了,也就是unsigned short类型,如果改为unsigned short类型的话,并加上packed选项,可以让yaffs_ECCOther从12字节减到5字节.
如果这样的话,14+5 =19 bytes ,在我们的项目中,用HW ECC的话,只剩下20 字节,刚好够了.
另外,我很纳闷,怎么我这次移植Yaffs2费尽周折,是不是因为我用的版本不对?太新了还是太久了?
今天总是上不去.
Day 4: 2009年2月22日 星期日
今天看代码的时候,突然发现chunkID不能边为unsigned short ,因为在函数yaffs_PackTags2TagsPart中:
ptt->chunkId = EXTRA_HEADER_INFO_FLAG | t->extraParentObjectId;
也就是chunkId里面store了parent object ID ,这样chunkID必须也unsigned int 了,否则object ID 也必须限制在unsigned short ,也就是只能支持65535个文件对象(文件+目录+soft link+hard link).这样改动太大了.
不过这个文章里面
写的是tags 的ecc 为3 bytes .
在yaffs_ECCCalculateOther中nbytes为16, 这样i就小于16了(0~15),那么line_parity,line_parity_prime完全可以改为unsigned char .
对于line_parity ^=i ,除了低8位,其他的位全为0 ,所以改为unsigned char完全可以.
而line_parity_prime^=~i; 则高24位要么全为1,要么全为0,高24位和第7位一样,因为只要i小于128,那么高25位就为一样的值,那这样的话,也可以用unsigned char来代替,然后根据高7位是0还是1进行扩展.
typedef struct __attribute__((packed)) {
unsigned char colParity;
unsigned char lineParity;
unsigned char lineParityPrime;
} yaffs_ECCOther;
int yaffs_ECCCorrectOther(unsigned char *data, unsigned nBytes,
yaffs_ECCOther * read_ecc,
const yaffs_ECCOther * test_ecc)
{
..........
lDeltaPrime = read_ecc->lineParityPrime ^ test_ecc->lineParityPrime;
+++ if(lDeltaPrime&0x80)
+++ lDeltaPrime |=0xFFFFFF00
..............
再仔细分析一下,其实发现yaffs_ECCCorrectOther不用修改也可以.
只需要3bytes了.那么Yaffs的oob就是16+3=19 bytes 了,可以满足要求.
Day 5: 2009年2月25日 星期三
前几天忙于别的事情,不过,这也好,让大脑清静清静,否则总在里面绕不出来。
今天继续。用了一个老的版本,yaffs_before2.6.18 . 结果还是写大文件的时候出问题。
在网上看到别人的移植经验:一般yaffs没什么问题,问题主要是出在yaffs和MTD不 match .
难道我写的MTD 驱动有问题?
于是好好看了看nand driver . 结果在dw_nand_write_page函数中,
Block = page /this->pagemask
修改为 : Block = page /(this->pagemask+1);
发现 yaffs 好了 。 哈哈
不过,发现修改后的MTD驱动比DSPG的慢 ,还得优化一下。主要是多了一次memcpy动作 。
优化后测试发现MTD驱动读的速度和DSPG的一样,写比人家的慢(基于相同的时序条件下测试)。问题还是出在写多了一些memcpy动作。算了,就这样,以后再说。
Day 6: 2009年3月17日 星期二
Yaffs2移植好了,不过前几天有一个驱动的工程师离职了,本来交给他修改的下载yaffs2的工具也就没做了。
亲自动手吧,这些人做事太慢了。
{BANNED}中国第一步: mkyaffs2image 工具
在新的yaffs2代码里有一个目录utils ,里面有mkyaffs2image工具
修改一下Makefile :
KERNELDIR=/home/lawrencekang/xpndr-multimedia_base_1.1.1_giant/linux-dw
###add a link to yaffs in kernel as we changed the yaffs2 src
YAFFSSRC= $(KERNELDIR)/fs/yaffs2
###CFLAGS = -I/usr/include -I.. -O2 -Wall -DCONFIG_YAFFS_UTIL
CFLAGS = -I/usr/include -I$(YAFFSSRC) -O2 -Wall -DCONFIG_YAFFS_UTIL
$(COMMONLINKS) $(MKYAFFSLINKS) $(MKYAFFS2LINKS):
## ln -s ../$@ $@
ln -s $(YAFFSSRC)/$@ $@
也就是把符号链接到正确的kernel source中
注意在 mkyaffs2image.c中
static int write_chunk(__u8 *data, __u32 objId, __u32 chunkId, __u32 nBytes)
{
......
// return write(outFile,&pt,sizeof(yaffs_PackedTags2));
return write(outFile,&pt,spareSize);
}
这样,生成的文件系统结构为:
2K data + 20 个字节的 OOB + 44 无效数据
然后在uboot 中调用 MTD 的 write_ecc函数将 ecc写进去
然后 make ,就生成了mkyaffs2image
第二步:修改 u boot 代码
因为uboot 不支持 HW ECC
需要加上宏 CFG_DW_HW_ECC。
#define CFG_DW_HW_ECC
在nand_write_opts函数中
#ifdef CFG_DW_HW_ECC
if (opts->writeoob) {
memcpy(oob_buf, buffer, meminfo->oobsize); // the oob
buffer += meminfo->oobsize;
//wrtie the oob and data
result = meminfo->write_ecc(meminfo, mtdoffset, meminfo->oobblock,
&written, data_buf, (__u8 *) oob_buf, NULL);
if (result != 0) {
printf("\nMTD writeoob failure: %d\n",
result);
goto restoreoob;
}
imglen -= meminfo->oobsize;
imglen -= readlen;
}
else
{
result = meminfo->write(meminfo,
mtdoffset,
meminfo->oobblock,
&written,
(unsigned char *) &data_buf);
if (result != 0) {
printf("writing NAND page at offset 0x%lx failed\n",
mtdoffset);
goto restoreoob;
}
imglen -= readlen;
}
#else
if (opts->writeoob) {
/* read OOB data from input memory block, exit
* on failure */
memcpy(oob_buf, buffer, meminfo->oobsize);
buffer += meminfo->oobsize;
/* write OOB data first, as ecc will be placed
* in there*/
result = meminfo->write_oob(meminfo,
mtdoffset,
meminfo->oobsize,
&written,
(unsigned char *)
&oob_buf);
if (result != 0) {
printf("\nMTD writeoob failure: %d\n",
result);
goto restoreoob;
}
imglen -= meminfo->oobsize;
}
/* write out the page data */
result = meminfo->write(meminfo,
mtdoffset,
meminfo->oobblock,
&written,
(unsigned char *) &data_buf);
if (result != 0) {
printf("writing NAND page at offset 0x%lx failed\n",
mtdoffset);
goto restoreoob;
}
imglen -= readlen;
#endif
同时在 usbdfu.c中
char *rootfstype = getenv("rootfstype");