diff --git a/Checkpoint.cpp b/Checkpoint.cpp index f96ca39..1a89039 100644 --- a/Checkpoint.cpp +++ b/Checkpoint.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -37,7 +38,11 @@ #include #include #include +#include +#include +using android::base::GetBoolProperty; +using android::base::GetUintProperty; using android::base::SetProperty; using android::binder::Status; using android::fs_mgr::Fstab; @@ -126,7 +131,7 @@ Status cp_startCheckpoint(int retry) { namespace { -bool isCheckpointing = false; +volatile bool isCheckpointing = false; } Status cp_commitChanges() { @@ -180,9 +185,37 @@ Status cp_commitChanges() { return Status::ok(); } -Status cp_abortChanges() { - android_reboot(ANDROID_RB_RESTART2, 0, nullptr); - return Status::ok(); +namespace { +void abort_metadata_file() { + std::string oldContent, newContent; + int retry = 0; + struct stat st; + int result = stat(kMetadataCPFile.c_str(), &st); + + // If the file doesn't exist, we aren't managing a checkpoint retry counter + if (result != 0) return; + if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent)) { + PLOG(ERROR) << "Failed to read checkpoint file"; + return; + } + std::string retryContent = oldContent.substr(0, oldContent.find_first_of(" ")); + + if (!android::base::ParseInt(retryContent, &retry)) { + PLOG(ERROR) << "Could not parse retry count"; + return; + } + if (retry > 0) { + newContent = "0"; + if (!android::base::WriteStringToFile(newContent, kMetadataCPFile)) + PLOG(ERROR) << "Could not write checkpoint file"; + } +} +} // namespace + +void cp_abortChanges(const std::string& message, bool retry) { + if (!cp_needsCheckpoint()) return; + if (!retry) abort_metadata_file(); + android_reboot(ANDROID_RB_RESTART2, 0, message.c_str()); } bool cp_needsRollback() { @@ -212,6 +245,8 @@ bool cp_needsCheckpoint() { std::string content; sp module = IBootControl::getService(); + if (isCheckpointing) return isCheckpointing; + if (module && module->isSlotMarkedSuccessful(module->getCurrentSlot()) == BoolResult::FALSE) { isCheckpointing = true; return true; @@ -225,6 +260,53 @@ bool cp_needsCheckpoint() { return false; } +namespace { +const std::string kSleepTimeProp = "ro.sys.cp_usleeptime"; +const uint32_t usleeptime_default = 1000; // 1 s + +const std::string kMinFreeBytesProp = "ro.sys.cp_min_free_bytes"; +const uint64_t min_free_bytes_default = 100 * (1 << 20); // 100 MiB + +const std::string kCommitOnFullProp = "ro.sys.cp_commit_on_full"; +const bool commit_on_full_default = true; + +static void cp_healthDaemon(std::string mnt_pnt, std::string blk_device, bool is_fs_cp) { + struct statvfs data; + uint64_t free_bytes = 0; + uint32_t usleeptime = GetUintProperty(kSleepTimeProp, usleeptime_default, (uint32_t)-1); + uint64_t min_free_bytes = GetUintProperty(kSleepTimeProp, min_free_bytes_default, (uint64_t)-1); + bool commit_on_full = GetBoolProperty(kCommitOnFullProp, commit_on_full_default); + + while (isCheckpointing) { + if (is_fs_cp) { + statvfs(mnt_pnt.c_str(), &data); + free_bytes = data.f_bavail * data.f_frsize; + } else { + int ret; + std::string size_filename = std::string("/sys/") + blk_device.substr(5) + "/bow/free"; + std::string content; + ret = android::base::ReadFileToString(kMetadataCPFile, &content); + if (ret) { + free_bytes = std::strtoul(content.c_str(), NULL, 10); + } + } + if (free_bytes < min_free_bytes) { + if (commit_on_full) { + LOG(INFO) << "Low space for checkpointing. Commiting changes"; + cp_commitChanges(); + break; + } else { + LOG(INFO) << "Low space for checkpointing. Rebooting"; + cp_abortChanges("checkpoint,low_space", false); + break; + } + } + usleep(usleeptime); + } +} + +} // namespace + Status cp_prepareCheckpoint() { if (!isCheckpointing) { return Status::ok(); @@ -256,6 +338,12 @@ Status cp_prepareCheckpoint() { setBowState(mount_rec.blk_device, "1"); } + if (fstab_rec->fs_mgr_flags.checkpoint_blk || fstab_rec->fs_mgr_flags.checkpoint_fs) { + std::thread(cp_healthDaemon, std::string(mount_rec.mount_point), + std::string(mount_rec.mount_point), + fstab_rec->fs_mgr_flags.checkpoint_fs == 1) + .detach(); + } } return Status::ok(); } diff --git a/Checkpoint.h b/Checkpoint.h index d077d19..63ead83 100644 --- a/Checkpoint.h +++ b/Checkpoint.h @@ -33,7 +33,7 @@ android::binder::Status cp_startCheckpoint(int retry); android::binder::Status cp_commitChanges(); -android::binder::Status cp_abortChanges(); +void cp_abortChanges(const std::string& message, bool retry); bool cp_needsRollback(); diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp index 6868c83..623cced 100644 --- a/VoldNativeService.cpp +++ b/VoldNativeService.cpp @@ -865,11 +865,12 @@ binder::Status VoldNativeService::markBootAttempt() { return cp_markBootAttempt(); } -binder::Status VoldNativeService::abortChanges() { +binder::Status VoldNativeService::abortChanges(const std::string& message, bool retry) { ENFORCE_UID(AID_SYSTEM); ACQUIRE_LOCK; - return cp_abortChanges(); + cp_abortChanges(message, retry); + return ok(); } binder::Status VoldNativeService::supportsCheckpoint(bool* _aidl_return) { diff --git a/VoldNativeService.h b/VoldNativeService.h index 8fbc32d..b90c5b8 100644 --- a/VoldNativeService.h +++ b/VoldNativeService.h @@ -129,7 +129,7 @@ class VoldNativeService : public BinderService, public os::Bn binder::Status restoreCheckpoint(const std::string& mountPoint); binder::Status restoreCheckpointPart(const std::string& mountPoint, int count); binder::Status markBootAttempt(); - binder::Status abortChanges(); + binder::Status abortChanges(const std::string& message, bool retry); binder::Status supportsCheckpoint(bool* _aidl_return); binder::Status supportsBlockCheckpoint(bool* _aidl_return); binder::Status supportsFileCheckpoint(bool* _aidl_return); diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl index 3c5ef78..72f2643 100644 --- a/binder/android/os/IVold.aidl +++ b/binder/android/os/IVold.aidl @@ -99,7 +99,7 @@ interface IVold { void startCheckpoint(int retry); boolean needsCheckpoint(); boolean needsRollback(); - void abortChanges(); + void abortChanges(in @utf8InCpp String device, boolean retry); void commitChanges(); void prepareCheckpoint(); void restoreCheckpoint(@utf8InCpp String device); diff --git a/vdc.cpp b/vdc.cpp index 1ec46c8..76eca3e 100644 --- a/vdc.cpp +++ b/vdc.cpp @@ -141,8 +141,10 @@ int main(int argc, char** argv) { checkStatus(vold->restoreCheckpointPart(args[2], count)); } else if (args[0] == "checkpoint" && args[1] == "markBootAttempt" && args.size() == 2) { checkStatus(vold->markBootAttempt()); - } else if (args[0] == "checkpoint" && args[1] == "abortChanges" && args.size() == 2) { - checkStatus(vold->abortChanges()); + } else if (args[0] == "checkpoint" && args[1] == "abortChanges" && args.size() == 4) { + int retry; + if (!android::base::ParseInt(args[2], &retry)) exit(EINVAL); + checkStatus(vold->abortChanges(args[2], retry != 0)); } else { LOG(ERROR) << "Raw commands are no longer supported"; exit(EINVAL);