背景
由於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/