Linux基础 -- UBI模块之 leb_read_sanity_check函数说明
它不是给你直接调用的公共 API,而是 UBI 内核层(drivers/mtd/ubi/kapi.c)里供 ubi_leb_read()/ubi_leb_read_sg() 使用的私有参数校验函数,在真正下发读请求前把各种越界与非法场景一网打尽,避免把“坏参数”送进 EBA/IO 路径。
它做什么(作用与位置)
- 位置:
drivers/mtd/ubi/kapi.c - 被谁调用:
ubi_leb_read()、ubi_leb_read_sg()在读数据前先调用它做检查;通过后才会走ubi_eba_read_leb()把 LEB 映射到 PEB 并执行实际读。
具体检查了什么(核心规则)
leb_read_sanity_check(struct ubi_volume_desc *desc, int lnum, int offset, int len) 主要做这些校验:
-
基本越界
-
卷号、LEB 号、偏移、长度都必须在合法范围内:
0 <= lnum < vol->used_ebs0 <= offset、0 <= lenoffset + len <= vol->usable_leb_size(单个 LEB 的可读上限)([Android Git Repositories][1])
-
-
静态卷(static volume)额外限制
- 若是最后一个 LEB,不允许读超过真实有效字节
vol->last_eb_bytes(静态卷最后一个 LEB 可能没写满)。([Android Git Repositories][1])
- 若是最后一个 LEB,不允许读超过真实有效字节
-
更新中断保护
- 卷带有 更新标记(
vol->upd_marker,表示上次“更新卷”被中断)时,一律拒绝读,返回-EBADF。([Android Git Repositories][1])
- 卷带有 更新标记(
通过这些检查后,
ubi_leb_read()才会去ubi_eba_read_leb()执行真正的读;长度为 0 的读直接返回成功。
与“读数据完整性”的关系
-
静态卷 CRC 检查:
ubi_leb_read(desc, lnum, buf, off, len, check)的check参数仅对静态卷生效。若置 1,会把整个 LEB 读出并核对 CRC;这会明显变慢。动态卷忽略该参数。 -
错误语义:
-EBADMSG:底层 MTD 报不可校正 ECC,或静态卷的 CRC 不匹配;UBI 还会把静态卷标记为corrupted以便上层感知。-EINVAL:各种越界/非法参数(上面 1、2 条)。-EBADF:卷处于“更新中断”状态(upd_marker)。
设计思路(为什么把检查集中在这里)
- 把 API 契约收紧在 KAPI 层:先拦截所有“坏参数”,再把干净的请求交给 EBA/IO,避免出现锁/并发/映射层里才发现的“迟到错误”。
- 静态卷与动态卷语义分离:静态卷有“最后一个 LEB 可能不满”和“可选 CRC 校验”的特性,因此在 sanity check / 读路径上都要额外处理。
在实际系统中的作用(与 UBIFS 的关系)
- UBIFS/其他内核客户端不会直接调用
leb_read_sanity_check,而是调用ubi_leb_read()或ubi_leb_read_sg();一旦传参出界,会被该检查直接打回,不会发起任何闪存访问。随后才进入ubi_eba_read_leb()完成 LEB→PEB 映射和读操作。
“正确用法”示意(内核调用方)
你要做的是 打开卷 → 调 ubi_leb_read();leb_read_sanity_check 由 UBI 内部自动执行:
struct ubi_volume_desc *d = ubi_open_volume(ubi_num, vol_id, UBI_READONLY);/* 读 LEB lnum 的一段数据 */
int err = ubi_leb_read(d, lnum, buf, offset, len, /*check=*/0);
if (err == -EINVAL) /* 参数越界/非法 */...
if (err == -EBADF) /* 卷处于更新中断(不可访问) */...
if (err == -EBADMSG) /* ECC/CRC 故障 */...
ubi_leb_read_sg()提供了 scatter-gather 版本,固定也会先做同样的 sanity check。([Android Git Repositories][1])
调试与排障要点
- 反复
-EINVAL:检查lnum是否小于vol->used_ebs,offset+len是否穿越vol->usable_leb_size,以及静态卷最后 LEB 的last_eb_bytes限制。 -EBADF:卷带有upd_marker(上次更新被断电中断);需要完成/回滚更新或重建卷。-EBADMSG:底层介质确实有不可校正错误或静态卷 CRC 错;在静态卷场景下 UBI 会将卷标记corrupted并打印告警。
小结
leb_read_sanity_check = UBI 内部的“读参数闸门”:统一检查 LEB 边界、偏移/长度、静态卷末块长度 以及 更新中断 状态;只有通过它,ubi_leb_read() 才会继续把请求交给 EBA/IO 层执行。它让 UBI 的读 API 具备可预测的错误语义,也把静态卷与动态卷的特殊性(last_eb_bytes、CRC 检查)收敛在了规范的入口。
