文章目录
前言
概述
本文档主要介绍Linux UBI子系统常见FAQ等。
修订记录
日期 | 作者 | 版本 | 修改说明 |
---|---|---|---|
2023.10.10 | 枫潇潇 | V1.0.0 | 初始版本 |
2024.01.22 | 枫潇潇 | V1.0.1 | 增加ubi使用只读块设备 |
2024.01.23 | 枫潇潇 | V1.0.2 | 增加ubifs做rootfs |
2024.01.24 | 枫潇潇 | V1.0.3 | 增加fastmap使用 |
ubi attach流程及作用
ubi如何attach mtd设备?
启动参数法
参数格式:
Parameter format: mtd=<name|num|path>[,<vid_hdr_offs>[,max_beb_per1024[,ubi_num]]]
在cmdline中加入ubi参数,示例如下:
ubi.mtd=rootfs ubi.mtd=firmware,0,20,1 ubi.mtd=/dev/mtdX,0,20,2 ubi.block=0,rootfs root=/dev/ubiblock0_0 rootfstype=squashfs
cmdline中mtd分区参数,示例如下:
mtdparts=spi0.1:512k(boot)ro,512k(misc),512k(pstore),512k(factory),1536k(rp),4608k(system)ro,4608k(recovery)ro,5632k(rootfs),40m(firmware),-(data)
参数含义解析:
ubi.mtd=firmware,0,20,1
firmware:mtd分区名,也可以是8,或者是/dev/mtd8
0:vid_hdr_offs
20:每1024块中保留块的数量,当前ubi保留块的数量为:20*nand_size_in_blocks/1024
1:ubi设备号
模块参数法
modprobe ubi mtd=rootfs mtd=firmware,0,20,1
或者:
modprobe ubi mtd=7 mtd=8,0,20,1
mtd-utils工具法
ubiattach -h
ubiattach version 2.0.1 - a tool to attach MTD device to UBI.
Usage: ubiattach [<UBI control device node file name>]
[-m <MTD device number>] [-d <UBI device number>] [-p <path to device>]
[--mtdn=<MTD device number>] [--devn=<UBI device number>]
[--dev-path=<path to device>]
[--max-beb-per1024=<maximum bad block number per 1024 blocks>]
UBI control device defaults to /dev/ubi_ctrl if not supplied.
Example 1: ubiattach -p /dev/mtd0 - attach /dev/mtd0 to UBI
Example 2: ubiattach -m 0 - attach MTD device 0 (mtd0) to UBI
Example 3: ubiattach -m 0 -d 3 - attach MTD device 0 (mtd0) to UBI
and create UBI device number 3 (ubi3)
Example 4: ubiattach -m 1 -b 25 - attach /dev/mtd1 to UBI and reserve
25*C/1024 eraseblocks for bad block handling, where C is the flash
is total flash chip eraseblocks count, that is flash chip size in
eraseblocks (including bad eraseblocks). E.g., if the flash chip
has 4096 PEBs, 100 will be reserved.
-d, --devn=<number> the number to assign to the newly created UBI device
(assigned automatically if this is not specified)
-p, --dev-path=<path> path to MTD device node to attach
-m, --mtdn=<number> MTD device number to attach (alternative method, e.g
if the character device node does not exist)
-O, --vid-hdr-offset VID header offset (do not specify this unless you really
know what you are doing, the default should be optimal)
-b, --max-beb-per1024 maximum expected bad block number per 1024 eraseblock.
The default value is correct for most NAND devices.
Allowed range is 0-768, 0 means the default kernel value.
-h, --help print help message
-V, --version print program version
ubi attach mtd设备的作用
根据mtd_dev_param
参数信息,创建ubi设备与对应的mtd设备进行一一对应绑定。整个过程将会根据mtd设备的erasesize、size、writesize等信息,结合mtd设备的扫描信息,初始化好ubi_device、ubi_volume等实例。
ubi attach mtd设备的流程
1)通过 ubi_mtd_param_parse
解析ubi参数配置好mtd_dev_param参数信息;
2)通过 open_mtd_device
找到对应的mtd设备实例——mtd_info;
3)通过 io_init
函数,根据mtd_info的IO属性对ubi中的IO属性进行初始化;
4)scan_all
扫描ubi设备中所有的PEB,判断其是否为bad,同时获取每个PEB的EC、VID等信息;
5)ubi_read_volume_table
创建vtbl,并初始化好逻辑卷volume;
6)ubi_wl_init
磨损平衡初始化;
7)ubi_eba_init
LEB 与 PEB映射关系初始化,实例为eba_tbl;
8)autoresize
重新计算卷大小;
ubi_init()
|--->open_mtd_device()
|--->ubi_attach_mtd_dev()
| |--->io_init()
| |--->ubi_attach()
| | |--->scan_all()
| | | |--->scan_peb()
| | |--->ubi_read_volume_table()
| | | |--->create_empty_lvol()
| | | |--->process_lvol()
| | | |--->init_volumes()
| | |--->ubi_wl_init()
| | |--->ubi_eba_init()
| |--->autoresize()
| |--->uif_init()
| |--->ubi_debugfs_init_dev()
|--->ubiblock_init()
ubi保留PEB数量如何配置?
默认情况下,大约 2% 的整个芯片大小 (20/1024 PEB) 被保留用于坏块处理。如果坏块的数量超过了分配的数量,就会打印一条错误消息,并且 UBI 将切换到只读模式。
NandFlash设备在出厂时可能包含一些无效块,并且规定了总可用块中的最小有效块数(Minimum number of Valid Blocks ,NVB)约总块数的98%。无效块是指其中至少包含一个页面的错误位数超过最小所需的 ECC 能够纠正的错误位数。在使用过程中可能会出现额外的坏块。然而,在产品的耐久寿命内,总可用块数不会低于 NVB。
- 1)kernel默认配置
Device Drivers --->
<*> Memory Technology Device (MTD) support --->
<*> Enable UBI - Unsorted block images --->
--- Enable UBI - Unsorted block images
(20) Maximum expected bad eraseblock count per 1024 eraseblocks
CONFIG_MTD_UBI_BEB_LIMIT:指定UBI在MTD设备上期望的最大坏物理擦除块数(每1024擦除块),默认值:20,即:2%
-
2)引导参数配置
详见:《启动参数法》
-
3)ubiattach工具配置
详见:《mtd-utils工具法》
ubi内LEB & PEB如何建立映射?
/* linux-4.19.132/drivers/mtd/ubi/ubi.h */
struct ubi_volume {
......
struct ubi_device *ubi; // reference to the UBI device description object
int vol_id; // volume ID
......
int reserved_pebs; // how many physical eraseblocks are reserved for this volume
int vol_type; // volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
......
int name_len; // volume name length
char name[UBI_VOL_NAME_MAX + 1]; // volume name
......
struct ubi_eba_table *eba_tbl; // EBA table of this volume (LEB->PEB mapping)
......
};
ubi_volume结构体中的eba_tbl
用于记录volume使用了哪些逻辑擦写块,以及逻辑擦写块到物理擦写块的映射; eba_tbl 的数据类型为struct ubi_eba_table,结构体定义如下所示:
/*
* struct ubi_eba_entry - structure encoding a single LEB -> PEB association
*/
struct ubi_eba_entry {
int pnum; // the physical eraseblock number attached to the LEB
};
/*
* struct ubi_eba_table - LEB -> PEB association information
*/
struct ubi_eba_table {
struct ubi_eba_entry *entries; // the LEB to PEB mapping (one entry per LEB)
};
ubi_eba_table结构体中的entries是个数组,数组长度为ubi_volume结构体中的reserved_pebs,即为这个volume分配的物理擦写块个数;数组元素类型为 struct ubi_eba_entry,这个结构体只包含一个成员pnum;
问题:逻辑擦写块如何映射到物理擦写块?struct ubi_eba_entry结构体中的pnum是什么?
以nand flash来举例说明,假设nand flash的erase block size=128KB,这个nand flash上有一个96MB的mtd分区,假设这个分区名称为mtd1,ubi attach到mtd1;则mtd1总共包含有 96MB/128KB=768个erase block;
如果要读取pnum=12的物理擦写块的内容,则pnum=12在mtd1分区的offset=12*(erase block size)=12*128KB=1536KB,有了offset后就可以通过mtd提供的接口把这个物理擦写块的内容读出来;
如果给定逻辑擦写块lnum=0,通过ubi_eba_table结构体中的entries[lnum].pnum 获取到对应的pnum(因为mtd1有768个erase block,这个值的取值范围:0~767),假设entries[lnum].pnum=12,则lnum=0被映射到pnum=12,即通过逻辑擦写块lnum=0可以读写物理擦写块pnum=12(mtd1分区offset=1536KB)的数据;
ubi上如何使用只读块设备?
UBI允许在卷之上创建块设备,但是有以下限制:
Ø 只读操作
Ø 连续的IO操作,请记住NAND驱动核心已经将所有的IO连续了。
尽管有这么多的限制,挂载一个只读的块设备仍然非常有作用。比如被压缩的文件系统(squashfs),它就可以作为一个轻量级的只读根文件系统放在NAND设备上。UBI层将负责管理像bit-flips和损耗均衡这一类的事情。
使用方法
生成和销毁UBI卷之上的块设备和将MTD设备和UBI关联起来有些相似。既可以使用UBI模块参数block,也可以使用用户空间工具“ubiblock”.
为了在启动的时候就创建一个块设备,可以指定block参数为内核启动参数:
ubi.mtd=5 ubi.block=0,0 root=/dev/ubiblock0_0
- 方式一:引导参数
Ø 使用UBI卷路径
ubi.block=/dev/ubi0_0
Ø 使用UBI设备和卷名:
ubi.block=0,rootfs
Ø 使用UBI设备号和UBI卷号:
ubi.block=0,0
- 方式二:模块参数
如果已经将UBI创建为一个模块,在模块加载时可以使用以下参数:
$ modprobe ubi mtd=/dev/mtd5 block=/dev/ubi0_0
- 方式三:ubiblock工具
使用用户空间工具ubiblock,块设备也可以在运行时被动态的创建和移除
$ ubiblock --create /dev/ubi0_0
$ ubiblock --remove /dev/ubi0_0
示例——squashfs做rootfs
ubi.mtd=rootfs ubi.mtd=firmware ubi.mtd=data ubi.block=0,rootfs root=/dev/ubiblock0_0 rootfstype=squashfs mtdparts=spi0.1:1m(boot)ro,1m(misc),1m(pstore),1m(factory),1m(rp1),1m(rp2),6m(system1)ro,6m(system2)ro,6m(rootfs)ro,32m(firmware),-(data)
参数含义:
-
ubi.mtd=XXX:ubi attach mtd设备
-
ubi.block=0,rootfs:为指定的ubi设备的卷创建块设备,即为ubi0的rootfs卷创建一个ubi块设备
-
root=/dev/ubiblock0_0:指定根文件系统
-
rootfstype=squashfs:根文件系统类型
ubi上如何使用ubifs做rootfs?
用法同上,差异点如下:
ubi.mtd=rootfs ubi.mtd=firmware ubi.mtd=data root=ubi0:rootfs rw rootwait rootfstype=ubifs
ubi上如何使用fastmap?
fastmap工作原理
快速映射是一个试验的可选的UBI特征,可以将CONFIG_MTD_UBI_FASTMAP设置为y来使能该功能。一旦被使能,UBI将评估模块参数”fm_autoconvert”。如果它被设置为1(默认为0),UBI将为每一个被关联镜像(attachedimage)自动使能快速映射。这意味着UBI用快速映射的数据创建了一个新的内部卷,以便下次快速关联模式可以使用这些数据。
在默认配置下,UBI将会使用存储在快速映射卷里面的信息,以此来加速关联(attach)过程。如果想测试快速映射,设置fm_autoconver为1并将其关联(attach)到一个卷。
以下是可进行的配置:
CONFIG_MTD_UBI_FASTMAP | Fm_autoconvert | resultn |
---|---|---|
n | 0 | 快速映射完全被屏蔽 |
y | 0 | 如果镜像上存在一个快速映射,它将附在(attach)UBI上,但是如果没有快速映射,也就不会有快速映射会被安装在镜像上。 |
y | 1 | 如果镜像上存在一个快速映射,它将附在(attach)UBI上,快速映射会自动的被安装在所有依附(attach)的镜像上。 |
技术实现
一个磁盘存储的快速映射包含所有的需要附加到整个镜像的信息,也就是说所有的擦除计数值,所有的PEB列表和他们的状态,所有卷的列表和他们当前的EBA……为了避免对快速映射的太多次的写入,快速映射有一个PEB列表,这些PEB是已经更改过的或者需要在附加时进行全扫描的。这个表叫作快速映射池,并且有一个固定的大小,PEB总数的5%。仅当UBI对快速映射进行写入,并且快速映射池里没有空闲的PEB时,才会使用到这个技术。否则,每当一个卷的EBA改变时,UBI都会写入到快速映射。
快速映射有一个超级块(也叫作PEB锚),并且可以负载任何PEB上的数据。PEB锚必须在MTD设备的前64个PEB中,它有一个指向其它剩余PEB的指针,而这些PEB才是真正的装载实际的快速映射数据。在现在的NAND flash芯片中,整个快速映射是放在一个单独的PEB中的,因此,PEB锚指向它自己。在加载快速映射数据以后,UBI将据此来生成信息结构。
attach 流程如下
Ø UBI试图找到快速映射PEB锚,如果没有找到PEB锚,UBI将进行一个传统的全扫描
Ø 根据存储在PEB锚中的指针读取快速映射的负载数据
Ø 仅把池中的PEB执行一个传统的扫描,而不是所有的PEB。
如果UBI检测到使用的快速映射是无效的,它将自动退回扫描模式并执行一个全扫描。使用CRC32的checksum和一致性来校验内部的UBI结构体,以此来判断快速映射是否有效。
每当快速映射池满,快速映射将会被写入设备,卷层将改变或者镜像被detach。也许会疑问,为什么在detach的时候需要被写入,如果UBI在detach的时候不写一个新的快速映射,所有的擦除计数将发生修改当上一个快速映射写入已丢失。
开销
如果使能了快速映射,UBI将存储足够的PEB来装载两个完整的快速映射。在实际当中,NAND flash芯片有两个PEB是为快速映射预留的。
也有一些运行时的开销,为了确保新的快速映射是有效的,UBI将管理所有那些将导致EBA更改的IO,这将消耗一秒。所以,快速映射在大flash芯片,也就是进行一次全扫描需要较长时间的芯片上使用才会有意义。比如一个4G的NAND flash芯片进行一次全扫描需要好几秒,而一个快速的attach仅需要不到一秒的时间。
备注
启用Fastmap并不保证每个attach过程都会在最短时间内完成。在某些情况下,仍然需要进行完整扫描。这可能发生在两种情况下:
(i) 如果在写入Fastmap到闪存时发生意外重启;
(ii) 在写入Fastmap时UBI用尽了PEBs。
后一种情况可能发生在写入时发生大量I/O错误,并且UBI找不到足够可用的PEBs。
使用方法
-
使用CONFIG_UBI_FASTMAP配置编译内核
-
使用ubi.fm_autoconvert = 1内核参数至少引导一次系统。
-
以干净的方式重启系统
-
保证正常启动一次系统后可以删除ubi.fm_autoconvert = 1
fastmap优化效果
128MB spinand与32MB spinor测试对比
阶段 | 耗时(uint:S) | Spinor启动时间 | ||
---|---|---|---|---|
过程 | 启动fastmap | 关闭fastmap | ||
Boot阶段 | Brom | 0.4 | —— | |
Spl | 1.2 | 0.4 | ||
Uboot | 1.3 | 1.4 | ||
总计 | 2.900 | 1.744 | ||
Kernel阶段 | 自解压 | 2.888 | 2.878 | 3.091 |
Nand init | 1.021 | 1.103 | 0 | |
Ubi attach | 0.707 | 2.125 | 0 | |
Ubifs mount | 1.330 | 1.235 | 1.175 | |
总计 | 7.574 | 8.830 | 6.762 | |
欢迎使用Markdown编辑器
你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。
新的改变
我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:
- 全新的界面设计 ,将会带来全新的写作体验;
- 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
- 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
- 全新的 KaTeX数学公式 语法;
- 增加了支持甘特图的mermaid语法1 功能;
- 增加了 多屏幕编辑 Markdown文章功能;
- 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
- 增加了 检查列表 功能。
功能快捷键
撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G
合理的创建标题,有助于目录的生成
直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC
语法后生成一个完美的目录。
如何改变文本的样式
强调文本 强调文本
加粗文本 加粗文本
标记文本
删除文本
引用文本
H2O is是液体。
210 运算结果是 1024.
插入链接与图片
链接: link.
图片:
带尺寸的图片:
居中的图片:
居中并且带尺寸的图片:
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。
如何插入一段漂亮的代码片
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片
.
// An highlighted block
var foo = 'bar';
生成一个适合你的列表
- 项目
- 项目
- 项目
- 项目
- 项目1
- 项目2
- 项目3
- 计划任务
- 完成任务
创建一个表格
一个简单的表格是这么创建的:
项目 | Value |
---|---|
电脑 | $1600 |
手机 | $12 |
导管 | $1 |
设定内容居中、居左、居右
使用:---------:
居中
使用:----------
居左
使用----------:
居右
第一列 | 第二列 | 第三列 |
---|---|---|
第一列文本居中 | 第二列文本居右 | 第三列文本居左 |
SmartyPants
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
TYPE | ASCII | HTML |
---|---|---|
Single backticks | 'Isn't this fun?' | ‘Isn’t this fun?’ |
Quotes | "Isn't this fun?" | “Isn’t this fun?” |
Dashes | -- is en-dash, --- is em-dash | – is en-dash, — is em-dash |
创建一个自定义列表
-
Markdown
- Text-to- HTML conversion tool Authors
- John
- Luke
如何创建一个注脚
一个具有注脚的文本。2
注释也是必不可少的
Markdown将文本转换为 HTML。
KaTeX数学公式
您可以使用渲染LaTeX数学表达式 KaTeX:
Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n−1)!∀n∈N 是通过欧拉积分
Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=∫0∞tz−1e−tdt.
你可以找到更多关于的信息 LaTeX 数学表达式here.
新的甘特图功能,丰富你的文章
- 关于 甘特图 语法,参考 这儿,
UML 图表
可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图:
这将产生一个流程图。:
- 关于 Mermaid 语法,参考 这儿,
FLowchart流程图
我们依旧会支持flowchart的流程图:
- 关于 Flowchart流程图 语法,参考 这儿.
导出与导入
导出
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。
导入
如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
注脚的解释 ↩︎