AM335x的UBIFS文件系统如何撑过十万次掉电重启

若干年后,重新整理2015年的笔记,把调试记录分享给大家。

最初发现此问题,是自己新做核心板刚上批量到一个客户的的现场,现场的主板,在运行了3-5天后,发现Linux系统无法启动的情况。返回到研发发现是ubifs文件系统崩溃,提示ecc error -74错误,无法进入根目录,文件系统挂载失败。停在Linux挂在Rootfs。

于是把同样的程序下载到一块新的核心板,不接任何设备,直接放着。神奇的是运行5天后,真的挂了。其中断过几次电,但是只要持续运行5天后,就会出错。

    出现的错误,还是ecc error,根文件系统挂载失败。

于是上网找am335x ubifs ecc error,起初怀疑是nand时序的问题,于是仔细研究am335x的gpmc nand时序设置,把时序看懂了也就比较简单了,将时序按照K9F1G08U0E的datasheet,调整到40ns一个cs的cycle,速度也快了很多,读写也很稳定,但是更新内核和boot后,重新下载测试程序,发现还是挂。排除了nand时序的问题。

接着看是否ubifs文件系统制作的问题,关于文件系统制作的时候的mkfs.ubifs的参数,

sudo ./mkfs.ubifs -F -q -r ./335x_root_release -m 2048 -e 126976 -c 1023 -o ubifs.img

sudo ./ubinize -o ubi.img -m 2048 -p 128KiB ubinize.cfg

主要是以下几点,一个是U0E不支持sub-page write,也就是它的NOP=1,一个页只能允许编程一次。所以-m 2048和一页大小匹配,因此-e参数时,需要一个块减去2个page的大小,= 128K-4K=124K。-c参数的选择,有点疑问,一开始我设置的是800,是LEBs的数量,逻辑块的数量,是按124KB大小算出来的,先按照最小的rootfs的分区大小0x6600000设置,732个lebs,结果ubifs还是挂了。后来看解释,-c只是在文件系统头上写一个标志,允许最大写的块号,比如你设置1023,如果你的flash是1Gbit的,那正好允许你的分区大小是128MB,如果你的flash是64M的,ubifs会根据-F参数,在启动时自动设置分区大小,把允许最大写的块的数量限制在511。也没问题。但是当你把生成的这个ubifs映像文件下载到2Gbit的flash上时,这个-c参数就会产生影响,文件系统最多只有128MB可用,因为-C参数限制了ubifs不允许写超过1023个块大小。所以,理论上来说,因为我会换更大的flash,比如2Gbit,我这里-c可以直接设置成2047,这样,生成的bin文件,就是兼容2Gbit和以下的容量都可以。

Ubinize.cfg内容如下,去掉了vol_size,让ubifs自己根据mtd info决定vol size。因为-F参数的引入,可以在第一次启动时,动态调整分区的大小。

[ubifs]

mode=ubi

image=ubifs.img

vol_id=0

vol_type=dynamic

vol_name=rootfs

vol_flags=autoresize

这样的参数,至少我觉得是没有问题的。

继续找ubifs的问题,发现ti的wiki上一篇说明

http://processors.wiki.ti.com/index.php/UBIFS_Support

On booting with UBIFS as rootfs, the first boot happens successfully. Before subsequent boot-ups, it is recommended to do a manual "sync" from the console. This allows UBIFS meta data properly updated on the partition. This initial sync will help later recovery.

大致的意思是,ubifs作为根文件系统时,首次运行后,最好执行一次sync,保证所有的分区结构头都flush到flash上,用来在以后出错是恢复系统用。

上面的所有措施都做好后,继续测试有问题的程序,文件系统还是崩溃了。于是继续查阅资料。

看到一份linux mtd的ubifs说明,详细阐述了ubifs的机制,原理,优势和弊端等。其中有一个说明,叫unstable bitsissue。

http://www.linux-mtd.infradead.org/doc/ubifs.html#L_unstable_bits

The unstable bits are the result of a power cut during a program or erase operation. Depending on when the power cut has happened, they can corrupt the data or the free space. Consider the following 4 situations:

  1. The power cut happens just before the NAND page program operation finishes. After reboot the page may be read correctly and without a single bit-flip say, 2 times, and the 3rd time you may get an ECC error. This happens because the page contains a number of unstable bits which are sometimes read correctly and sometimes not.
  2. The power cut happens just after the NAND page program operation starts. After reboot, the page may be read correctly (return all 0xFFs) most of the time, but sometimes you may get some bits set to zero. Moreover, if you then program this page, it also may be sometimes read correctly, but sometimes return an ECC error. The reason is again the unstable bits in the NAND page.
  3. The power cut happens just before the eraseblock erase operation finishes. After reboot, the eraseblock may contain unstable bits and data in this eraseblock may suddenly become corrupted.
  4. The power cut happens just after the eraseblock erase operation starts. After reboot, the eraseblock may contain unstable bits and sometimes return zero bits on read, or corrupted data if you program it.

The number of unstable bits resulting from a power-cut may be greater than what the ECC algorithm is able to correct. This is why a previously readable page may suddenly become unreadable, or conversely a previously unreadable page may suddenly become readable.

写的 非常清楚,明白。突然恍然大悟。之前在调试ecc error的-74错误时,曾经把出错的地址,nand dump出来了,发现确实有很多位错误,已经超过BCH8的能力范围(8bit每512字节),记录如下

对比这部分数据的原始ecc,发现4块512byte数据的后三块的ECC数据是一样的,也就是都是0XFF,,,,但实际读出来的数据有很多非0xff,已经超过BCH8的纠错能力范围。

看来这就是导致问题的罪魁祸首。找到这个原因,其实花了没有多少时间,但是接下去怎么解决这个问题,着实花了我不少功夫。

NAND不稳定位的出现,是由于在写或者擦除flash时,突然的断电,造成flash还没完成每个cell的充放电过程,导致某些位的情况发生改变,比如还未从1->0,或者0->1。

很直观并彻底的方法就是保证nand的读写能在整个擦除或者写入过程中不断电,不就能彻底解决此问题吗?

于是做了个实验,把reset芯片的复位电压调高,换5V的复位芯片,让他在掉落到3.3V前,CPU就停止输出,从RESET芯片的复位门槛电压降到NAND的最低允许电压,这中间的电源保证能持续超过NAND ERASE的时间,K9F1G08U0E的这个时间是4.5ms。发现无效。还是会挂。

于是想到,在此期间,CPU复位后,NAND IO上的数据,可能处于非稳定的状态,比如WP引脚,WE引脚,断电后的最后一个电平,于是WE加下拉,如果断电发生在WE的脉冲期间,能保证CPU复位后,没有WE的上升沿,也就没有写的命令过去。发现也无效。

试过WP下拉,CPU复位后,让NAND处于写保护的状态,nand的内容也不会改变。发现还是无效。

因为4.5ms的断电时间实在太长,于是试了一颗MXIC的1Gbit的flash,这颗芯片的时间是2.5ms,1bit ecc requirement,75nm工艺,slc flash。

发现情况略微好转,但是还是会有挂的现象。

硬件上没有特别的方法后,尝试软件方法修复。

因为ubi的机制,在写之前,总会有记录,如果一旦发现写的数据读不出来了,(比如非全ff,或者全0),文件系统可以认为是这次写失败,在下次启动时,检测到这个错误,则启动recovery,也许就能恢复。当然这是我的猜测。

如果是在擦除时,发生了掉电,软件上有没有办法修复这样的错误呢?我认为是有的,比如擦除命令发出后,还在等待擦除完成时,重启了。下次启动时,就会发现这个块缺失了HEAD信息,因为每个block的前2个page必定是有ec和vid的hdr,用来进行磨损均衡等算法的标识,如果一旦发现这个数据没了,则可以重新再erase一次,其实这个块是好的,重新标记一下,是可以用的。只要在擦除前,在其他地方写入一个标识,再擦除完成后,把这个标记再清零,这样如果重启时,扫描文件系统时,发现有这个标记,说明上次有未完成的擦除操作,直接再擦一遍即可。(后来查了一下,这种机制类似logfs)也就是说,我认为,从软件上来说,是有机会把数据恢复的。只有一种情况恢复不了,就是当错误发生在超级块上,但是如果你有超级块的备份机制,也就万无一失了。

于是首先想到的办法是更新ubifs的代码,直接升级到linux-3.19.3,再往上就是linux4.0了。可以认为ubi部分已经稳定,不再有重大更新。

这次测试,有了针对性,增加了不停的写RW的操作,每100ms写一次,每写一次就sync,保证会执行flash的写操作。增大了写入的强度。然后执行10-30秒的随机事件重启。

发现升级了系统,改进了测试方法后,死机的概率并没有明显降低。

继续改进nand的内核操作代码,omap_wait里,等RDY引脚和nand的status寄存器一起结合操作。貌似有点用,概率降低了一些。3000-5000次重启后,7台设备里,有1台最容易挂的U0E的死了。

同时,omap_wait的超时时间,MTD_ERASEING 和 WRITE的时间分别放长,放到400ms,保证万无一失。概率又降低了一些,最差的U0E的的重启次数可以撑到5000次了。

与此同时开始怀疑,是不是ubifs文件系统对掉电的容忍度本身就比较差,设计的不好,赶紧试一下am335x+yaffs2。

顺利打上补丁,挂载文件系统,软件启动。直接在最差的那台U0E的屏上试,结果半天左右就出现了死机的现象,但是并不是文件系统起不来,而是/lib/libpthread.so的i变成了大写的I。。导致应用程序无法启动。直接文件的内容也坏了。更悲剧。说明yaffs2也不行。

继续换现代的H27U1G8F2BTR这颗flash,做重启测试,撑的时间长一点,几千次后,也出现过死机的现象。

得出这样的结论后,再回过头来,发现和flash,cpu,ubifs,复位其实都有关系,每一项错误的改进,都会对系统的稳定性产生一定影响,当所有的措施加在一起时,就可以把概率降的足够低。

TI的E2E社区建议采用第三方的安全的商用文件系统,100%safe。但是要收费。

TI经销商的意见,是采用分区的做法,把rootfs和要保存数据的区域变成两个分区,rootfs只读。我试了一下,确实rootfs变成RO后,每次都能起来,但是RW存放的区域(我单独建了一个/disk分区),也会坏,表现为启动后RW数据丢失,但是主程序确实不会受影响。进一步,想到做数据备份,比如用户数据区,做两份,每次上电时,mount这两个分区,看那个成功哪个失败,mount失败的分区,重新格式化,把成功的分区里的数据恢复到重新格式化的分区。如果两个都成功,则把主分区的数据,备份到备份分区。这样至少可以保证出现错误时数据恢复到上次上电的时候。这个机制确实能从理论上根本接解决问题。只是,分区造成1Gbit的flash磁盘空间太小,空间有限,至少要2Gbitflash才会考虑这个方案,所以也没法实施。

综合以上所有的经过,只能从降低概率的方面出发,最终做了以下几个方面的改进:

1、更换5V的RESET复位芯片

2、继续沿用3.2.0+ubifs

3、增加omap_wait超时

4、加快nand的读写时序,采用preefetch poll模式

5、出现ecc error时,修改Linux的ubifs代码,让文件系统尝试recovery,丢失的文件直接跳过

6、应用程序中,修改执行sync跳过刷新flash,让linux管理刷新的策略,降低出现unstable bits的概率。

经过这几项修改,测试的8块核心板中,最容易挂的那台原来半天就挂的,在7天后停在Linux Rootfs mounting,撑过了5W次,剩下的7块随机断电重启已经撑过9W次,超过2周一切正常,测试就没有继续了。

  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值