Add health check to checkpointing
Take action if we are running out of checkpoint space.
Configurable via ro.sys properties.
ro.sys.cp_usleeptime = Time to sleep between checks
ro.sys.cp_min_free_bytes = Min free space to act on
ro.sys.cp_commit_on_full = action to take. Either commits or reboots to
continue attempt without checkpoint, or retry
and eventually abort OTA
Test: Trigger a checkpoint and fill the disk.
Bug: 119769392
Change-Id: I977cc03b7aef9320d661c8a0d716f8a1ef0be347
diff --git a/Checkpoint.cpp b/Checkpoint.cpp
index 1762521..1a89039 100644
--- a/Checkpoint.cpp
+++ b/Checkpoint.cpp
@@ -22,6 +22,7 @@
#include <list>
#include <memory>
#include <string>
+#include <thread>
#include <vector>
#include <android-base/file.h>
@@ -37,7 +38,11 @@
#include <mntent.h>
#include <sys/mount.h>
#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <unistd.h>
+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 @@
namespace {
-bool isCheckpointing = false;
+volatile bool isCheckpointing = false;
}
Status cp_commitChanges() {
@@ -255,6 +260,53 @@
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();
@@ -286,6 +338,12 @@
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();
}