背景
由于AB升级的回滚机制只支持到early_boot阶段,如果OTA升级的过程中,data分区被修改了,并且OTA升级失败了,则data分区是无法回滚到之前的状态的。UDC功能是为了解决OTA升级失败后,当data分区被修改后,不支持回滚data分区的问题。UDC同时支持绑定key版本以及防止key回滚的功能。
实现机制
对于f2fs,UDC添加checkpoint功能到4.20版本的内核中,其他版本的内核也可以移植该功能过去。所以,f2fs系统是默认支持checkpoint功能的。对于在 /data
装载的设备,请将 checkpoint=fs
标记添加到 fstab 的 <fs_mgr_flags>
部分。
对于其他文件系统,UDC会使用dm-bow
,必须在内核配置中启用 dm-bow
。对于在 /data
装载的设备,请将 checkpoint=block
标记添加到 fstab 的 <fs_mgr_flags>
部分。
源码解析
1. Checkpoint模块
init: Calling: /system/bin/vdc checkpoint needsCheckpoint -> vold->needsCheckpoint(&enabled) -> cp_needsCheckpoint
1.1 cp_needsCheckpoint
bool cp_needsCheckpoint() {
std::lock_guard<std::mutex> lock(isCheckpointingLock);
// Make sure we only return true during boot. See b/138952436 for discussion
// 已经checkpoint过了,就直接返回了
if (needsCheckpointWasCalled) return isCheckpointing;
needsCheckpointWasCalled = true;
bool ret;
std::string content;
sp<IBootControl> module = IBootControl::getService();
if (isCheckpointing) return isCheckpointing;
// 正常情况isSlotMarkedSuccessful返回为true,不走进去
if (module && module->isSlotMarkedSuccessful(module->getCurrentSlot()) == BoolResult::FALSE) {
// OTA阶段为false,走这里;返回true
isCheckpointing = true;
return true;
}// const std::string kMetadataCPFile = "/metadata/vold/checkpoint";
// 找不到文件,返回false
ret = android::base::ReadFileToString(kMetadataCPFile, &content);
if (ret) {
ret = content != "0";
isCheckpointing = ret;
return ret;
}
return false; // 正常情况返回false
}
checkpointing的流程
-
mount_all
阶段挂载data分区时- 调用
cp_needsCheckpoint
函数:如果是OTA升级阶段或者是第一次刷固件的时候(调用boot_ctrl判断当前slot是否已经Marked Successful了,如果没有,表明此时处于OTA升级阶段或者是第一次刷固件的时候,需要进行checkpoint),则返回true;正常情况就是false - 如果上面返回true,则调用
UpdateCheckpointPartition
函数:mount data分区的时候加上checkpoint=disable
选项。然后如果OTA升级失败了,就可以恢复到这个点上了。
- 调用
-
ActivityManager finishBoot
阶段- 调用
cp_commitChanges
函数:调用boot_ctrl
的markBootSuccessful
函数,以及用checkpoint=enable
选项,重新挂载data分区(f2fs文件系统就会自动将对data
分区的改动写入data分区中,表明OTA升级成功)。
ActivityManager: About to commit checkpoint
- 调用
如果在mount_all
到finishboot
期间,OTA升级失败并重启了,则写入到data分区的数据重启之后全部无效。这就达到了data分区回滚的目的了。
管理 keymaster 密钥
Keymaster 密钥用于设备加密或其他目的。为管理这些密钥,Android 特地推迟到提交检查点之后才调用密钥删除命令。
监控运行状况
该线程在开机的post-fs-data:vdc checkpoint prepareCheckpoint
阶段运行,要在start checkpoint之后才会运行该线程。运行状况守护进程会检查是否有足够的磁盘空间来创建检查点。运行状况守护进程位于 Checkpoint.cpp
中的 cp_healthDaemon
内。
默认表现:当data分区的可用空间小于100M的时候,它就会提交检查点(cp_commitChanges
提交对data分区的修改)或者重启设备(放弃对data分区的修改)。
运行状况守护进程具有以下可配置的行为:
ro.sys.cp_msleeptime
:控制设备检查磁盘使用情况的频率。(隔多久去检查磁盘的可用空间,默认为1秒钟)ro.sys.cp_min_free_bytes
:控制运行状况守护进程查找的最小值。(可用空间的最小值,默认为100M)ro.sys.cp_commit_on_full
:控制运行状况守护进程在磁盘已满时,是重新启动设备还是提交检查点并继续运行。
checkpoint配置
- 在
device/softwinner/ceres-common/init.sun50iw10p1.rc
文件的on fs
中添加
mount_all /vendor/etc/fstab.sun50iw10p1 --early
- 在
device/softwinner/ceres-common/init.sun50iw10p1.rc
文件的on late-fs
中添加
mount_all /vendor/etc/fstab.sun50iw10p1 --late
- 在
device/softwinner/ceres-b3/fstab.sun50iw10p1
文件的data
分区中添加latemount
和checkpoint=fs
/dev/block/by-name/UDISK /data f2fs noatime,nosuid,nodev,discard wait,check,formattable,quota,reservedsize=33554432,fileencryption=aes-256-xts:aes-256-cts,latemount,checkpoint=fs
- 在
device/softwinner/ceres-b3/fstab.sun50iw10p1
文件的metadata
分区中添加first_stage_mount
。
/dev/block/by-name/metadata /metadata ext4 nodev,noatime,nosuid,errors=panic wait,first_stage_mount,formattable,check
- 在
device/softwinner/ceres-b3/BoardConfig.mk
文件中添加:
BOARD_USES_METADATA_PARTITION := true
验证
要测试您的 UDC 实现,请运行 VTS 测试的 VtsKernelCheckpointTest
测试集。
参考
1. f2fs: checkpoint disabling
https://lwn.net/Articles/763072/