分类: LINUX
2013-07-13 15:28:24
我在用TI的dm368开发板,kernel是2.6.32.17,默认的flash文件系统是jffs2,但是jffs2在大分区下,mount速度很慢,而且占用ram较多,因此,我想使用ubifs看看性能是否会更好些。
ubifs的原理和配置过程,很多网页都有介绍的,我给一个链接,大家可以看看,我就不转载了,我重点说我移植过程中遇到并解决的问题。
kerne的配置很简单,2.6.32里面都有,选上并重新编译就好了。
ubiattach、ubimkvol等工具,TI的dvsdk里面自带了,能用,我就偷了个懒,没有重新编译。
很轻松的照着网页说明操作了下,mount 分区也成功了,复制文件也成功了,很高兴:)
ubiattach /dev/ubi_ctrl -m 3
ubimkvol /dev/ubi0 -N rootfs -s 480MiB
mount -t ubifs ubi0_0 /mnt/nand/ -o sync
但很快就遇到麻烦了,开发板关机重新启动,我再mout ubi文件系统就出错了,提示了一堆错误,而且分区是空的,之前复制的文件不见了。
问题如下:
ubiattach /dev/ubi_ctrl -m 3
UBI error: ubi_io_read: error -74while reading 64 bytes from PEB 1662:0, read 64 bytes
UBI error: ubi_io_read: error -74while reading 64 bytes from PEB 1663:0, read 64 bytes
为了分析问题,我把mtd和ubifs的debug log都打开了,看到了一些信息;
#define EBADMSG 74 /* Not a data message */
/*Nand returns -EBADMSG on ecc errors, but it returns
* the data. For our userspace tools it isimportant
* to dump areas with ecc errors !
* For kernel internal usage it also mightreturn -EUCLEAN
* to signal the caller that a bitflip hasoccured and has
* been corrected by the ECC algorithm.
* Userspace software which accesses NAND thisway
* must be aware of the fact that it deals withNAND
*/
nand_do_read_ops
stats= mtd->ecc_stats;
if(mtd->ecc_stats.failed - stats.failed)
return-EBADMSG;
nand_read_page_hwecc
stat= chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
if(stat < 0)
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected+= stat;
看样子,应该是ECC出错了,可是我用的是硬件ECC校验,怎么可能几乎全部的page都出现ecc校验错误了呢?
root@dm368-evm:~#flash_eraseall /dev/mtd3
root@dm368-evm:~# ubiattach/dev/ubi_ctrl -m 3
UBI: attaching mtd3 to ubi0
UBI: physical eraseblock size: 131072 bytes (128 KiB)
UBI: logical eraseblock size: 129024 bytes
UBI: smallest flash I/O unit: 2048
UBI: sub-page size: 512
UBI: VID header offset: 512 (aligned 512)
UBI: data offset: 2048
分析下UBI写数据的过程,
UBI DBG (pid 1484): ubi_io_write:write 512 bytes to PEB 57:0
int ubi_io_write(struct ubi_device*ubi, const void *buf, int pnum, int offset,
int len)
addr= (loff_t)pnum * ubi->peb_size + offset;
err= ubi->mtd->write(ubi->mtd, addr, len, &written, buf);
在io_init函数中可以看到几个变量的赋值;
ubi->peb_size = ubi->mtd->erasesize;
ubi->peb_count = mtd_div_by_eb(ubi->mtd->size,ubi->mtd);
ubi->flash_size= ubi->mtd->size;
从debug log来看,这里一次写了512字节,从某个block的起始page开始,offset是0;
那么到了nand mtd底层驱动,行为就是;
nand_write->nand_do_write_ops:
subpage= column || (writelen & (mtd->writesize - 1));
这里,subpage就是writelen,512;
也可以得知一点,ubifs没有使用oob,这跟jffs2和yaffs2是不同的;
/*Partial page write ? */
if(unlikely(column || writelen < (mtd->writesize - 1))) {
cached= 0;
bytes= min_t(int, bytes - column, (int) writelen);
chip->pagebuf= -1;
memset(chip->buffers->databuf,0xff, mtd->writesize);
memcpy(&chip->buffers->databuf[column],buf, bytes);
wbuf= chip->buffers->databuf;
}
ret= chip->write_page(mtd, chip, wbuf, page, cached,
(ops->mode == MTD_OOB_RAW));
下面是write_page函数的代码;
static int nand_write_page(structmtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int page, int cached,int raw)
{
intstatus;
chip->cmdfunc(mtd,NAND_CMD_SEQIN, 0x00, page);
if(unlikely(raw))
chip->ecc.write_page_raw(mtd,chip, buf);
else
chip->ecc.write_page(mtd,chip, buf);
/*
* Cached progamming disabled for now, Not sureif its worth the
* trouble. The speed gain is not veryimpressive. (2.3->2.6Mib/s)
*/
cached= 0;
这里需要注意的就是raw,如果是MTD_OOB_RAW,那么不会做ECC校验,也不会把ECC码写入OOB;
如果是这样,在read的时候也必须指定是MTD_OOB_RAW,不需要ECC校验;否则,就会出现我们最开始看到的错误;
if(mtd->ecc_stats.failed - stats.failed)
return-EBADMSG;
那么,从这里的情况来看,我们可能已经找到出错的原因了;ubi使用了subpage write,而底层nand flash驱动实际上是不支持subpage write的,尽管ubi一次只写了512字节,但这个page的其他部分已经不能再次写入新的数据了。
从Nand_base.c(drivers\mtd\nand)来看,large page的nand flash,对subpage write的支持是不完善的,限制条件比较多,比如,不能是MLC的nand flash,不能用硬件ECC;
更严重的问题是代码存在缺陷,在写入部分data的时候,将其他部分的数据填充为0xff了,然后write整个page,并写入全部ecc码到oob,也许这就是前面ecc校验出错的原因吧。
nand_do_write_ops()
/*Partial page write ? */
memset(chip->buffers->databuf, 0xff, mtd->writesize);
memcpy(&chip->buffers->databuf[column],buf, bytes);
因此,我想到的解决办法就是在nand驱动中禁止subpage write。
第一步,在chip options中增加NAND_NO_SUBPAGE_WRITE;
static struct davinci_nand_pdatadavinci_nand_data = {
.options = NAND_USE_FLASH_BBT|NAND_NO_SUBPAGE_WRITE,
然后重新编译下载kernel,但问题依旧;
root@dm368-evm:~# ubiattach/dev/ubi_ctrl -m 3
UBI: attaching mtd3 to ubi0
UBI: physical eraseblock size: 131072 bytes (128 KiB)
UBI: logical eraseblock size: 129024 bytes
UBI: smallest flash I/O unit: 2048
UBI: sub-page size: 512
UBI: VID header offset: 512 (aligned 512)
心想很奇怪,为什么 sub-page size还是512?
回头查看代码,想看看sub page size是怎样计算出来的,
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
!(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
switch(chip->ecc.steps) {
case 2:
mtd->subpage_sft = 1;
break;
case 4:
case 8:
case 16:
mtd->subpage_sft = 2;
break;
}
}
chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
可是我已经给options增加了NAND_NO_SUBPAGE_WRITE啊?有些怀疑,我就在此处加了打印log,果然是这里出了问题,
chip->options = 0x10101.
mtd->subpage_sft = 0.
chip->subpagesize = 512.
#define NAND_NO_SUBPAGE_WRITE 0x00000200
可是,这里的options 明明是不对的!那我设置的NAND_NO_SUBPAGE_WRITE在哪里丢掉了?
在下面的函数中有对chip->options赋值改变;
nand_get_flash_type()
printk(KERN_INFO"nand_get_flash_type 1, chip->options = 0x%x.\n",chip->options);
/*Get chip options, preserve non chip based options */
chip->options &= ~NAND_CHIPOPTIONS_MSK;
printk(KERN_INFO"nand_get_flash_type 2, chip->options = 0x%x.\n",chip->options);
chip->options|= type->options & NAND_CHIPOPTIONS_MSK;
printk(KERN_INFO"nand_get_flash_type 3, chip->options = 0x%x.\n",chip->options);
/*
* Set chip as a default. Board drivers canoverride it, if necessary
*/
chip->options|= NAND_NO_AUTOINCR;
/*Check if chip is a not a samsung device. Do not clear the
* options for chips which are not having anextended id.
*/
if(*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
chip->options&= ~NAND_SAMSUNG_LP_OPTIONS;
printk(KERN_INFO"nand_get_flash_type 4, chip->options = 0x%x.\n",chip->options);
nand_get_flash_type 1,chip->options = 0x10200.
nand_get_flash_type 2,chip->options = 0x10000.
nand_get_flash_type 3,chip->options = 0x1011c.
nand_get_flash_type 4,chip->options = 0x10101.
问题出在这里,红色的代码!
/* Mask to zero out thechip options, which come from the id table */
#defineNAND_CHIPOPTIONS_MSK (0x0000ffff &~NAND_NO_AUTOINCR)
/* Chip can not autoincrement pages */
#define NAND_NO_AUTOINCR 0x00000001
找到问题了,解决办法就有了,注释掉红色的那行代码,就是他把NAND_NO_SUBPAGE_WRITE给丢掉了。
//chip->options &=~NAND_CHIPOPTIONS_MSK;
再重新编译下载kernel,问题搞定了!
再次启动开发板,加载ubi,一切正常了。
root@dm368-evm:/# ubiattach/dev/ubi_ctrl -m 3
UBI: attaching mtd3 to ubi0
UBI: physical eraseblock size: 131072 bytes (128 KiB)
UBI: logical eraseblock size: 126976 bytes
UBI: smallest flash I/O unit: 2048
UBI: VID header offset: 2048 (aligned 2048)
UBI: data offset: 4096
UBI: attached mtd3 to ubi0
UBI: MTD device name: "filesystem1"
UBI: MTD device size: 512 MiB
UBI: number of good PEBs: 4096
UBI: number of bad PEBs: 0
UBI: max. allowed volumes: 128
UBI: wear-leveling threshold: 4096
UBI: number of internal volumes: 1
UBI: number of user volumes: 1
UBI: available PEBs: 3639
UBI: total number of reserved PEBs:457
UBI: number of PEBs reserved for badPEB handling: 40
UBI: max/mean erase counter: 2/1
UBI: image sequence number: 0
UBI: background thread"ubi_bgt0d" started, PID 1483
UBI device number 0, total 4096 LEBs(520093696 bytes, 496.0 MiB), available 3639 LEBs (462065664 bytes, 440.7 MiB),LEB size 126976 bytes (124.0 KiB)
root@dm368-evm:/# mount -tubifs ubi0_0 /mnt/nand/ -o sync
UBIFS: mounted UBI device 0, volume0, name "rootfs"
UBIFS: file system size: 51171328 bytes (49972 KiB, 48 MiB, 403 LEBs)
UBIFS: journal size: 2539520 bytes (2480 KiB, 2 MiB, 20 LEBs)
UBIFS: media format: w4/r0 (latest is w4/r0)
UBIFS: default compressor: lzo
UBIFS: reserved for root: 2416947 bytes (2360 KiB)
root@dm368-evm:/# df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/root 39544232 16239820 21295632 43% /
none 1024 24 1000 2% /dev
tmpfs 16384 20 16364 0% /var/volatile
tmpfs 21760 0 21760 0% /dev/shm
tmpfs 16384 0 16384 0% /media/ram
ubi0_0 45528 1528 41640 4% /mnt/nand
最后,总结下,就是要禁止nand flash驱动里面的subpage write,让 chip->subpagesize == mtd->writesize 就好了。
这是我的解决办法。如果有人要使用subpage write,那么一定要保证你的读写逻辑是正确的,要么不用ecc,要么各个subpage的ecc都应是正确的。