转:http://blog.csdn.net/kickxxx/article/details/6707589
人们经常迷惑于UBI到底是什么,这就是我们创建这一节的理由。请记住:
1. UBI不是一个Flash Translation Layer,它和FTL没有任何关系。
2. 工作在bare flashes,它不能工作在MMC, eMMC, SD, mini-SD, micro-SD, USB flash设备上;UBI工作与raw flash devices上,大部分出现在嵌入式设备上,比如mobile phones等等
Overview
UBI全称"Unsorted Block Images"。它是工作于raw flash devices之上的volume管理系统,它管理一个单一physical flash设备上的多个logical volume,能够把I/O负载均匀的分发到flash chip上。
在一定意义上,UBI可以和Logical Volume Manager相比较。LVM映射logical sector到物理sectors,UBI映射logical eraseblcok到physical eraseblocks。但是除了映射,UBI还实现了wear-leveling和透明的I/O错误的管理。
一个UBI volume是一组连续的logical eraseblocks(LEBs)。每一个逻辑eraseblcok可以被映射为任意的physical eraseblock。这个映射是由UBI管理,并且对上层隐藏了global wear-leveling机制(记录per-physical eraseblock erase counters 以及透明的移动more worn-out数据到less worn-out上)。
UBI volume在创建时确定尺寸大小,也可以在日后改变(volume是动态re-sizable)。UBI有user-space工具可以用来管理UBI volumes。
有两种UBI volume,动态的和静态的。静态volumes是只读的,内容由CRC-32 checksums保护;而动态volume是read-write,上层负责确保数据的完整性。
UBI处理坏块,上层不需要关心坏块管理。UBI有一个保留的physical eraseblocks pool,当一个physical eraseblock变成坏块,UBI透明的用一个好physical block替换这个坏块。UBI把新出现的physical eraseblock的数据移动到好physical eraseblock。UBI volume对发生的事毫无察觉。
NAND flashes在读写操作时可能会发生bit-flips。Bit-flips通过ECC checksums纠正,但是积累到一定数据可能会发生数据丢失。UBI会移动发生bit-flips的数据到另外一个physical eraseblocks。这个过程叫scrubbing。scrubbing过程是后台的,并且对上层透明。
下面是UBI主要功能的一个短列表:
1. UBI提供动态生成,删除或re-sized的volume;
2. UBI实现全flash设备的wear-leveling(比如,你写的一个UBI volume的逻辑块,可能写入flash chip的任何physical eraseblocks);
3. UBI透明的处理所有physical eraseblocks;
4. UBI通过bit-flips使得数据丢失的可能性最小化。
下面是MTD分区和UBI volumes的比较,他们有一些相似之处:
1. 都是由eraseblocks组成的 - UBI volumes是逻辑eraseblocks,而MTD分区是物理删除块。
2. 都支持下面三个基本操作 - read, write, erase
但是UBI volumes和MTD分区相比有如下优点:
1. UBI volumes没有eraseblock wear-leveling 限制,所以用户不需要考虑这个,上层软件实现更简单。
2. UBI volumes没有bad eraseblocks,这也使上层软件不需要做坏块管理,因此上层软件也更简单。
3. UBI volumes是动态的,可以创建,删除和动态resize,而MTD分区是静态的。
4. UBI 处理bit-flips,同样使得上层软件更简单。
5. UBI提供volume update操作which makes it easy to detect interrupted software updates and recover。
6. UBI 提供了原子逻辑块修改操作,这个操作保证在修改logical eraseblock的内容过程中,发生unclean reboots不会造成数据丢失; 这对于上层软件可能是非常有用的
7. UBI有一个un-map操作,un-maps一个逻辑eraseblock到physical eraseblock的映射。schedules the physical eraseblock for erasure and returns;这是非常快的并且使得上层软件避免实现他们自己的机制
有一个驱动叫做glubi是用来在MTD设备上模拟UBI volumes。这看起来很奇怪,因为UBI工作在MTD设备之上,而gluebi又在UBI上模拟另外一个MTD设备,
UBI在每一个非坏physical eraseblcok的起始位置,储存两个小的64-byte头:
这就是为什么eraseblocks要小于physical eraseblock - 头占据了一部分flash空间
所有的UBI headers 使用CRC-32保护,参考drivers/mtd/ubi/ubi-media.h文件查看header的内容
当UBI attach到一个MTD device,首先扫描它,读所有的headers,检查CRC-32 checksums,存储erase counters和logical-to-physical eraseblock映射信息到RAM。
当UBI删除一个PEB,它写EC header,包含增加的erase count value。这意味着PEBs一直有EC header,除了从删除结束到EC header被写入的这个空档,如果在这时发生了unclean reboot,造成EC header丢失。在这种情况下,UBI写一个新的EC header,erase counter为MTD device扫描后的平均erase counter。
当UBI关联一个PEB和LEB时,VID头被写入PEB中。让我们考虑下面操作发生时,头部发生的变化。
UBI维护着两个per-PEB头因为它需要在不同的时间写不同信息到flash上:
当EC header写入PEB,UBI并不知道这个PEB要关联的volume ID和LEB number。这就是为什么UBI需要两个写操作,来写两个分离的headers
volume table是on-flash数据结构,保存着这个 UBI设备上每一个volume的信息。volume table是一个volume table records的数组,每一个record包含以下信息:
每一个record描述一个UBI volume,记录在volume table array的index对应着这个记录的volume ID,比如UBI volume0被volume table的record0描述。volume table内的records数目受限于LEB尺寸,但是不能大于128。这就意味着最大的volume数目不能大于128
每当UBI volume被创建,删除,re-sized,re-named或者updated时,相应的volume table record被修改。UBI维护着两个volume table以维护掉电发生后的可用性。
UBI在updating volume table内容时,使用下列算法。
当attaching MTD设备时,UBI确保两个volume table拷贝是相同的。unclean reboot可能会导致他们不相同,这是UBI选择LEB0的内容copy到LEB1中(因为LEB0是新的)。如果有一个volume table被损坏了,UBI会从另外一个volume table恢复过来
flash read和write是有最小I/O unit尺寸的,依赖于flash类型。
像之前所说的,所有的UBI I/O必须是min. I/O unit,也就是NAND flash的page size。然而一些SLC NAND flashes允许更小的I/O units,这在MTD术语称为sub-pages。不是所有的NAND都有sub-pages。
如果NAND flash支持sub-pages,那么ECC codes的计算可以以sub-page为单位,而不是per-NAND。在这种情况下可以read和write sub-pages单独的。
很明显,尽管NAND chip支持sub-pages,NAND controller可能disallow。事实上,如果管理flash的controller计算ECC是per-NAND,那么就不可能进行sub-page级的I/O操作。
使用sub-pages可以减少flash 空间开销,比如对于128KiB eraseblock,page size是2048-bytes。如果没有sub-pages,EC占据第一个2048,VID herder占据第二个2048,因此LEB size变成124KiB。反之支持sub-pages,UBI把EC 和VID分别放在第一和第二个512字节,所以LEB变成了126KiB。
Sub-pages仅仅在UBI内部使用,用来存放EC VID headers,UBI API不允许用户使用sub-page I/O unit。这是因为sub-page writes可能很慢,写一个sub-page,驱动可能会写整个NAND page,但是会把和本次操作无关的sub-pages都写入0xff。这也就是说写四个sub-pages可能比写整个NAND pages慢四倍。因此,UBI仅对headers使用sub-pages,但是在UBI API中不存在这个概念。
UBI header position
EC header存放在PEB的偏移0处,占据64bytes;VID header位于下一个min. I/O unit或者sub-page处,占据64 bytes。例如:
UBI使用一定数量的flash space来保存管理信息,因此减少了flash device可供UBI用户使用的空间,这些开销包括:
在实践中,UBI image通常都小于UBI volume,因此flasher要正确的烧写PEBs,正确的处理删除的PEBs
注意在写UBI image时,UBI image写到哪个eraseblocks是无所谓的,比如,image的第一个eraseblock可以写入第一个PEB,或者第二个,或者对后一个。
注意,如果你实现一个产线UBI images烧写工具。那么flasher不需要改变EC headers,因为这是新的flash,所以每一个PEB的erase counter应该为0。这意味着产线flasher更简单。
如果你的UBI image 包含UBIFS file system,并且你的flash是NAND, you may have to drop 0xff bytes the end of input PEB data. this is very importtant, although not required for all NAND flashes. Sometimes a failure to do this may result in very unpleasant problems which might be diffcult to debug later. So we recommend to always do this.
原因是UBIFS对待仅包含oxff byte的NAND pages为free。例如,假定PEB的第一个NAND page有一些数据,第二个是empty,第三个有一些数据,第四个和其他的都是空的,在这种情况下,UBIFS认为从第四个 page开始的NANDpages是空闲的,并且会向这些page写数据。然而,如果flasher程序已经向这些pages写了0xff,结果导致他们被写了两次!然而,一些NAND flashes要求他们的NAND pages仅被写一次,即便这些数据包含的都是0xff bytes。
flasher不得不做的是drop掉所有到PEB结尾的empty NAND pages。没必要drop掉所有的空NAND pages,仅需要最后一个。这就意味着flasher不需要扫描整个buffer,查找0xff。只需从buffer末尾开始查找知道第一个non-0xff byte,这是非常快的。
另外一个替代方法,是在生成UBIFS文件系统时使用mkfs.ubifs增加free space fixup选项。这将允许你的flasher不需要担心PEBs末尾的0xff。这在你使用一个产线烧写程序写一个UBI image时非常有用。
这一节是针对那些允许出现坏块的NAND flashes和其他的flashes。UBI在两种强况下mark physical eraseblocks为坏块:
1. eraseblock写操作失败,在这种情况下,UBI把这个PEB的数据移到其他的PEB(data recovery),然后调用对这个PEB的诊断
2. 删除操作出现EIO error,在这种情况下,直接把PEB标记为坏块。
诊断是在后台处理的,目的是检测这个physical eraseblock是否真的坏了。写失败可能有多种原因,包括驱动的bugs或者上层file system 的bug(比如,文件系统多次写了同一个NAND page)。整个诊断过程包括如下步骤:
如果eraseblock通过了torture test,那么不会被标记为坏块。注意,在诊断测试过程中,bit-flip发生是把eraseblock mark为坏块的很好理由,请参考torture_prb函数。
下面是一些测试结果:
一般来说,UBI需要三个表:
volume table维护在flash上。它仅仅在UBI volumes被创建,删除和re-sized时改变。这种操作很少且对速度没有要求,UBI可以负担一个慢的简单的方法来管理volume table。
EBA和EC表在每次LEB被映射或者PEB被删除是都会改变,因此需要这两个表的管理方法要快而且高效。
UBI可以在flash上,维护EBA和EC tables。这无法避免的引入了journaling, journal replay, journal commit等等。换句话说,这将引入许多复杂性。但是这可以使得EBA和EC在算法上是可扩展的。
UBI的一个需求是on-flash上格式的简化,因为UBI作者不得不在boot-loader中读取UBI volumes,而在boot-loader的代码中有很严格的限制,基本上很难在boot-loader代码中加入复杂的journal scanning和replay code。
所以UBI没有在flash media上维护EBA和EC tables。而是在每次attach MTD设备时构造它。这意味着UBI不得不扫描整个flash,从每个PEB读取EC和VID header以便构造in-RAM EC和EBA tables。
这个设计的缺点是很差的可扩展性以及NAND flashes空间的负载(大约1.5~3%的flash空间,2KiB NAND page,128KiB eraseblock),优点是简单以及可靠性。
当然,可以创建UBI2,维护这些表在分离的flash area。UBI2将不和UBI兼容,因为他们有完全不同的on-flash格式,但是用户接口是完全一样的,这将保证UBI上层软件的兼容性。
当需要创建一个UBI image用来在产线上刷如终端用户设备,你应该定义所有volume的确切尺寸。但是又有total flash size是依赖于初始坏块总数的,所以很难这么做。
解决这个问题的一个明显方法是假定最坏的情况,当所有的chips都有一个bad PEBs的最大值。但是实际中,大部分chips仅仅有几个bad PEBs,远小于这个最大值。一般来说,这没什么问题,这将增加可靠性,因为UBI一直使用设备上的所有PEBs。另一方面UBI无论如何要保留一定数目的physical eraseblocks用做坏块处理,缺省情况下是1%。所以上面提到的OneNAND chip将有%1 PEB保留给UBI,0~2%PEB不可以被使用。
但是有另外一个替代办法 - 一个volume可以有auto-resize mark,这意味着当UBI第一次运行时它的尺寸可以enlarded。在volume size调整后,UBI移除auto-resize标志后这个volume不能再re-sized。auto-resize flag存储在volume table仅仅一个volume可以被mark为auto-resize。
LEB un-map操作的实现是ubi_leb_unmap() kernel API。从2.6.29开始un-map操作通过UBI_IOCEBUNMAP ioctl命令暴露给user-space程序。这个ioctl应该被UBI volume character devices调用
LEB un-map操作:
当读取一个un-mapped LEB时,UBI返回全0xff bytes,所以un-map操作可以被认为是一个非常快的erase operation。但是有一点需要注意。
假定你un-map LEB numL从PEB numP。因为P不是同步删除的,而是仅仅调度了删除操作,这可能在unclean reboots时给个”惊喜“;如果在P物理删除前恰好发生了reboot。在UBI再次attaches到MTD设备上时,会发现L仍然mapped到UBI。事实上,UBI将扫描整个MTD设备发现哪个P对应L,然后把这个映射加到EBA table。
但是一旦你写数据到numL,或者使用LEB map操作,numL映射一个新的PEB,老数据就变成了历史,因为即便此时发生了unclean reboot,UBI仍然会选择numL的最新映射。
这一节描述在发生unclean reboot后,UBI如何区分一个LEB的older和newer的PEB,假定我们un-map LEB L到PEB P1的映射,此时UBI调度了P1的erasure。然后我们向L写数据,意味着UBI找到另外一个PEB P2,映射L到P2,然后写数据到P2,如果在P1被物理删除前,发生unclean reboot,我们得到了2个PEBs(P1, P2)对应同一个LEB L
为了处理这种情况,UBI维护一个全局64-bit sequence number variable。这个可变序列号每次映射一个LEB时都会增加并且存储到PEB的VID header中,所以每一个VID header有一个唯一的序列号,序列号越大,VID越年轻。当UBI attach到MTD设备时,初始化全局sequence number为VID headers中的最大值加1
在上面的情况,很显然UBI会选择higher sequence number (P2) 然后抛弃lower sequence number(P1)
注意,如果unclean reboot发生在,磨损平衡而执行的移动一个PEB数据到另外一个PEB的过程中,或者atomic LEB change操作时发生。在这种情况下不能简单的选择新的PEB,必须确保新的数据是否已经写到new PEB。
LEB map操作映射一个从前un-mapped 逻辑eraseblock到一个物理eraseblock。比如,如果要为LEB A映射,UBI首先发现相应的PEB,然后把VID header写入PEB,然后修正in-memory EBA table。VID header将指向LEB A,这个操作完成后,所有到LEB A的操作最终都会到mapped PEB
LEB映射操作是通过ubi_leb_map() UBI kernel API函数,或者通过UBI_IOCEBMAP ioctl
LEB map操作接受dtype类型参数,这个参数建议LEB将保存的数据类型,dtype有如下类型:
记住dtype仅仅是一个hint。请使用UBI_UNKNOWN如果无法确定。此外UBI作者从来没有测试过UBI_SHORTERM和UBI_LONGTERM,所以不保证他们有没有效果。