什么是UBIFS文件系统
UBIFS是UBI file system的简称,用于裸的flash设备,作为jffs2的后继文件系统之一。UBIFS通过UBI子系统处理与MTD设备之间动作。UBIFS文件系统更适合MLCNAND FLASH。需要注意的是UBIFS并不是为SSD,MMC,SD,Compact Flash等之类的基于flash的存储设备,其是针对于裸flash设备。
裸flash有以下特点:
①其包含的块被称为可擦除块,而对于SSD这类的设备,并无可擦除块的概念,取而代之的是扇区的概念。
②包括读、写、擦除可擦除块三种操作。
③ 硬件并不管理坏的可擦除块,而SSD之类的设备则具有专门的控制器处理坏块。
④ 可擦除块的读写寿命从几千到几十万之间不等。
图0.1中给出的设备是MMC、SD类型的,该设备具有flash转换层的硬件控制器,该硬件控制器的损耗平衡算法属于商业秘密,华为的dorado 系列高端存储器的文档对其损耗平衡(动态和静态)原理讲解的非常透彻,感兴趣的可以自己找找。对于UBIFS使用的场景,通常只有NANDFLASH那一个模块,其和控制器的接口通常是专门的NAND flash接口。由于这类设备的速率比较慢,所以通常用在相对而言比较低端的嵌入式设备,追求加载速度快一点嵌入式设备通常会选择使用emmc存储器,其文件系统通常会选择UFS。
MTD(Memory Technology Devices)对闪存存储器提供了一个抽象,隐藏了特定flash的独特之处,提供统一的API存取各种类型的flash。
MTD在内核层的API是struct mtd_device而用户空间的API接口是/dev/mtd0,这些接口提供了设备信息,读写可擦除块,擦除一个可擦除块,标记一个可擦除块是坏块,检查可擦除块是否是坏块。MTD的API并不隐藏坏的可擦除块也不做任何损耗平衡。
UBI(Unsorted Block Images)的内核API是include/mtd/ubi-user.h,用户空间的则是/dev/ubi0,提供损耗平衡,隐藏坏块,允许运行时容量创建、删除和修改,有点类似LVM功能。UBI线性扩展,在初始化时会读取所有的可擦除块头,所以当flash容量越大,初始化所花费的时间越多,但是就可扩展性而言比JFFS2要好很多。
LEB(logic eraseblock),PEB(physical erase block);将LEB映射到PEB,任何一个LEB可能映射到任何一个PEB,可擦除块头存储的是映射信息以及擦除计数值。
ubifs的制作
1️⃣、配置内核,使其支持ubifs文件系统
1)Device Drivers --->Memory Technology Device (MTD) support --->UBI - Unsorted block images --->Enable UBI
2)File systems --->Miscellaneous filesystems --->UBIFS file system support
2️⃣、制作ubifs格式的根文件系统镜像
先说明一下,板子上既有NorFlash,又有NandFlash,其中根文件系统和应用程序放在NandFlash上,uboot和kernel放在NorFlash上,而根文件系统所在的mtd设备为mtd2,分区大小为34MiB
uboot | kernel | rootfs=34MiB | app |
3️⃣、./mkfs.ubifs -v -r ./rootfs -o rootfs.img -m 2048 -e 129024 -c 272
-r:制定文件内容的位置 -m:页面大小 -e:逻辑擦除块大小 -c:最大的逻辑擦除块数量
mkfs.ubifs -m 2048 -e 129024 -c 1984 -o rootfs.ubifs -x none -m 2048 (Minimum input/output unit size: 2048 bytes) -e 129024 (Default UBI LEB size: 129024 bytes, 126.0 KiB) -c 1984 (Amount of eraseblocks: 1984 (260046848 bytes, 248.0 MiB)) -o rootfs.ubifs (output file) -x none (no compression)
mkfs.ubifs生成的image可以用来在linux kernnel里面更新ubi volume。
其使用方法如下:
mkfs.ubifs -r root-tree -F -m 4096 -e 258048 -c 362 -o ti.ubifs
-r 后面跟文件系统的tree
-F --space-fixup, 如果要基于ti.ubifs来制作使用u-boot来烧写的ubi.img,这个flag一定要选啊
-m min i/o size
-e logical 擦除块的大小
-c 逻辑擦除块的数目
-o 目标生成文件
下面是使用ti.ubifs更新ubi vlomue的例子:
flash_eraseall /dev/mt7
ubiattach /dev/ubi_ctrl -m 7
ubimkvol /dev/ubi0 -N rootfs -s 85MiB
ubiupdatevol /dev/ubi0_0 ti.ubifs
mount /dev/ubi0_0 /mnt/ubi0
就可以看到ubifs的内容了
4️⃣、./ubinize -v -o rootfs.ubi -m 2048 -p 128KiB -s 2048 hi.cfg
-p:物理擦除块大小 ; -m:页面大小;-s: 最小的硬件输入输出页面大小,如:k9f1208为256(上下半页访问)
ubinize用来制作u-boot下烧写用的ubifs的image。
其使用方法如下:
ubinize -o ubi.img -m 4096 -p 256KiB -s 1024 ubinize.cfg
-o 后跟目标image文件名,这个ubi.img可以在u-boot里面进行烧写
-m minimum io size
-p 物理擦除块的大小
-s subpage的大小
ubinize.cfg 是生成ubi.img的配置文件,内容如下。
#cat ubinize.cfg
[ubifs]
mode=ubi
image=ti.ubifs
vol_id=0
vol_size=80MiB
vol_type=dynamic
vol_name=rootfs
vol_flags=autoresize
其中
"image=ti.ubifs"指明使用上文所述的ti.ubifs作为输入。
"vol_name=rootfs"指明volume的name.
"vol_size=80MiB"指明fs的总大小。
下面是u-boot烧写ubi.img的方法
当前是写48MB, 需要根据实际调整.
U-Boot# mw.b 0x82000000 0xFF 0x3000000
U-Boot# tftp 0x82000000 ubi.img
U-Boot# nand erase 0x1000000 0x7000000 /* 将整个mtd都擦掉了 */
U-Boot# nand write 0x82000000 0x1000000 0x3000000
在kernel里面也可以将ubi.img烧写到mtd分区里。
#ubiformat /dev/mtd7 -f ubi.img -s 4096
5️⃣、配置文件ubi.cfg如下:
[ubifs] mode=ubi image=rootfs.img vol_id=0 vol_size=34MiB vol_type=dynamic vol_alignment=1 vol_name=rootfs vol_flag=autoresize
6️⃣、然后修改uboot的环境变量:
setenv bootargs 'mem=288M console=ttyAMA0,115200 root=ubi0:rootfs rw rootflags=sync rootfstype=ubifs ubi.mtd=2 mtdparts=hi_sfc:5M(boot),1M(picture);hinand:34M(rootfs),8M(config),86M(app)';
root@localhost:/mnt# cat /proc/mtd
dev: size erasesize name
mtd0: 00040000 00040000 "NAND.dt"
mtd1: 00040000 00040000 "NAND.dt.backup"
mtd2: 00080000 00040000 "NAND.hardinfo"
mtd3: 000c0000 00040000 "NAND.u-boot"
mtd4: 00040000 00040000 "NAND.u-boot-env"
mtd5: 00800000 00040000 "NAND.userdata"
mtd6: 00600000 00040000 "NAND.kernel"
mtd7: 07000000 00040000 "NAND.file-system"
mtd8: 08000000 00040000 "NAND.hmidata"
mtd9: 10000000 00040000 "NAND.prjdata"
需要在bootargs中指出使用的内核ubiattach使用哪一个mtd
如:ubi.mtd=NAND.file-system,1024 其中“NAND.file-system”是mtd7的名字, “1024”是vid header的偏移。
也可以不用mtd的名字,而是使用mtd的编号,如:ubi.mtd=7,1024 其中7是mtd的编号
或者不指定header的偏移,如:ubi.mtd=7
需要在bootargs中指出使用的内核使用的ubi volume name,如root=ubi0:rootfs, 这个rootfs是在ubinize.cfg中指明的。ubimkvol中也会指明volume的name。也可以写成root=ubi0_0
例1:
"setenv bootargs console=ttyS0,115200n8 root=ubi0:rootfs rw ubi.mtd=NAND.file-system,1024 rootfstype=ubifs rootwait=1;tftp 0x82000000 zImage; tftp 0x88000000 snd.dtb; bootz 0x82000000 - 0x88000000"
例2:
"setenv bootargs console=ttyS0,115200n8 root=ubi0:rootfs rw ubi.mtd=7,1024 rootfstype=ubifs rootwait=1;tftp 0x82000000 zImage; tftp 0x88000000 snd.dtb; bootz 0x82000000 - 0x88000000"
例3:
"setenv bootargs console=ttyS0,115200n8 root=ubi0:rootfs rw ubi.mtd=7 rootfstype=ubifs rootwait=1;tftp 0x82000000 zImage; tftp 0x88000000 snd.dtb; bootz 0x82000000 - 0x88000000"
7️⃣、保存环境变量,执行如下命令
setenv ipaddr 192.168.253.132;
setenv serverip 192.168.253.130;
setenv ethaddr 40:61:86:67:33:47;
mw.b 82000000 ff 2200000;
tftp 82000000 rootfs.ubi;
nand erase 0 2200000;
nand write 82000000 0 $(filesize);
sf probe 0;
sf read 0x82000000 0x100000 0x400000;
bootm 0x82000000
说明:
其实从上面的烧写命令可以看出,ubifs格式的镜像中是不包含oob信息的。
参见:http://www.cnblogs.com/pengdonglin137/p/3399071.html
8️⃣、问题分析
出现如下错误信息:
UBI: attaching mtd2 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: max. sequence number: 0 UBI error: vtbl_check: volume table check failed: record 0, error 9 UBI error: ubi_init: cannot attach mtd2Fixed MDIO Bus: probed
原因:参考 http://wiki.linpert.de/index.php?title=UBIFS#record_0.2C_error_9
http://lists.infradead.org/pipermail/linux-mtd/2009-April/025127.html
原因就是:在配置文件中,volume设为34MiB,太大了,因为整个mtd2分区总共才34MiB。
解决办法:将配置文件改为:
[ubifs] mode=ubi image=rootfs.img vol_id=0 vol_size=32MiB vol_type=dynamic vol_alignment=1 vol_name=rootfs vol_flag=autoresize
说明:vol_id 表示volume的编号,一个ubi设备中可以有多个volume。(这种情况下,/dev下会出现 ubi0 和 ubi0_0)
vol_size 表示ubi0_0的大小,即volume0的大小;vol_type 表示volume0的类型,分为dynamic和static两种,其中dynamic类型的设备表示可以读写,static类型的设备表示只读;vol_name 表示volume0的名称,在挂载ubi分区是会使用到,如在bootargs中的root=ubi0:rootfs
然后重新执行: ./ubinize -v -o rootfs.ubi -m 2048 -p 128KiB -s 2048 hi.cfg
当再次重启后,又出现如下错误信息:
UBIFS: parse sync UBIFS error (pid 1): validate_sb: LEB size mismatch: 129024 in superblock, 126976 real UBIFS error (pid 1): validate_sb: bad superblock, error 1
原因:
参考:http://www.linux-mtd.infradead.org/faq/ubifs.html#L_lebsz_mismatch
原因是:逻辑块的大小与实际的大小不符
解决办法:
将-e选项的值由129024改成126976
重新执行:
./mkfs.ubifs -v -r ./rootfs -o rootfs.img -m 2048 -e 126976 -c 272
./ubinize -v -o rootfs.ubi -m 2048 -p 128KiB -s 2048 hi.cfg
重新烧写并重启。
还有一个需要注意的问题是,如果将-s选项的值搞错,如将2048写成了512,那么会有如下错误信息
UBI error: validate_ec_hdr: bad VID header offset 512, expected 2048 UBI error: validate_ec_hdr: bad EC header UBI error: ubi_io_read_ec_hdr: validation failed for PEB 0 UBI error: ubi_init: cannot attach mtd2 Fixed MDIO Bus: probed
从错误提示中就可以看到解决方法:将-s选项的值改为2048即可。
参考:http://www.cnblogs.com/pengdonglin137/p/3404685.html
也就是说,对于上面的例子,如果有subpage(可以到/sys/class/mtd/其中的一个目录下使用cat命令去查看某个mtd设备的subpagesize参数),如果是512B,这有如下参数搭配(对于块大小是128KiB,页大小是2KB的NandFlash来说):
./mkfs.ubifs -v -r ./rootfs -o rootfs.img -m 2048 -e 129024 -c 272
./ubinize -v -o rootfs.ubi -m 2048 -p 128KiB -s 512 hi.cfg
其中 -e表示的是逻辑块的大小,因为subpagesize大小是512(也就是-s选项的值),第一页的前512存放EC(实际用了前64B),接下来的512B(前64B)存放UBI headers,逻辑块的大小就是128KiB-2KiB=126KiB,转化成十进制就是129024。
假如没有subpagesize,那么有如下搭配:
./mkfs.ubifs -v -r ./rootfs -o rootfs.img -m 2048 -e 126976 -c 272
./ubinize -v -o rootfs.ubi -m 2048 -p 128KiB -s 2048 hi.cfg
其中,逻辑块的大小:128KiB-2KiB-2KiB=124KiB,转换成10进制就是126976,-s后面的值为页大小,即2048B。