Add interfaces required by smart idle maintenance service
Added interfaces required by smart idle maintenance service in
StorageManagerService, whose goal is to determine when to trigger
filesystem defragmentation while keeping the best user experience
as long as possible, and avoiding hurting UFS lifetime.
Test: check smart idle maintenance log every hour
Bug: 202283480
Bug: 181079477
Signed-off-by: Daeho Jeong <daehojeong@google.com>
Change-Id: I012cfb9b01e5d21ec71700c3c52ac9c096cd1a90
diff --git a/IdleMaint.cpp b/IdleMaint.cpp
index 8005cf4..769d7a5 100644
--- a/IdleMaint.cpp
+++ b/IdleMaint.cpp
@@ -85,6 +85,13 @@
*/
static const int GC_TIMEOUT_SEC = 420;
static const int DEVGC_TIMEOUT_SEC = 120;
+static const int KBYTES_IN_SEGMENT = 2048;
+static const int MIN_GC_URGENT_SLEEP_TIME = 500;
+static const int ONE_HOUR_IN_MS = 3600000;
+static const int GC_NORMAL_MODE = 0;
+static const int GC_URGENT_HIGH_MODE = 1;
+
+static int32_t previousSegmentWrite = 0;
static IdleMaintStats idle_maint_stat(IdleMaintStats::kStopped);
static std::condition_variable cv_abort, cv_stop;
@@ -111,7 +118,7 @@
}
}
-static void addFromFstab(std::list<std::string>* paths, PathTypes path_type) {
+static void addFromFstab(std::list<std::string>* paths, PathTypes path_type, bool only_data_part) {
std::string previous_mount_point;
for (const auto& entry : fstab_default) {
// Skip raw partitions and swap space.
@@ -133,6 +140,10 @@
continue;
}
+ if (only_data_part && entry.mount_point != "/data") {
+ continue;
+ }
+
// Skip the multi-type partitions, which are required to be following each other.
// See fs_mgr.c's mount_with_alternatives().
if (entry.mount_point == previous_mount_point) {
@@ -142,10 +153,10 @@
if (path_type == PathTypes::kMountPoint) {
paths->push_back(entry.mount_point);
} else if (path_type == PathTypes::kBlkDevice) {
- std::string gc_path;
+ std::string path;
if (entry.fs_type == "f2fs" &&
- Realpath(android::vold::BlockDeviceForPath(entry.mount_point + "/"), &gc_path)) {
- paths->push_back("/sys/fs/" + entry.fs_type + "/" + Basename(gc_path));
+ Realpath(android::vold::BlockDeviceForPath(entry.mount_point + "/"), &path)) {
+ paths->push_back("/sys/fs/" + entry.fs_type + "/" + Basename(path));
}
}
@@ -161,7 +172,7 @@
// Collect both fstab and vold volumes
std::list<std::string> paths;
- addFromFstab(&paths, PathTypes::kMountPoint);
+ addFromFstab(&paths, PathTypes::kMountPoint, false);
addFromVolumeManager(&paths, PathTypes::kMountPoint);
for (const auto& path : paths) {
@@ -264,15 +275,18 @@
return android::OK;
}
-static void runDevGcFstab(void) {
- std::string path;
+static std::string getDevSysfsPath() {
for (const auto& entry : fstab_default) {
if (!entry.sysfs_path.empty()) {
- path = entry.sysfs_path;
- break;
+ return entry.sysfs_path;
}
}
+ LOG(WARNING) << "Cannot find dev sysfs path";
+ return "";
+}
+static void runDevGcFstab(void) {
+ std::string path = getDevSysfsPath();
if (path.empty()) {
return;
}
@@ -402,8 +416,10 @@
runDevGcFstab();
}
-int RunIdleMaint(const android::sp<android::os::IVoldTaskListener>& listener) {
+int RunIdleMaint(bool needGC, const android::sp<android::os::IVoldTaskListener>& listener) {
std::unique_lock<std::mutex> lk(cv_m);
+ bool gc_aborted = false;
+
if (idle_maint_stat != IdleMaintStats::kStopped) {
LOG(DEBUG) << "idle maintenance is already running";
if (listener) {
@@ -422,15 +438,17 @@
return android::UNEXPECTED_NULL;
}
- std::list<std::string> paths;
- addFromFstab(&paths, PathTypes::kBlkDevice);
- addFromVolumeManager(&paths, PathTypes::kBlkDevice);
+ if (needGC) {
+ std::list<std::string> paths;
+ addFromFstab(&paths, PathTypes::kBlkDevice, false);
+ addFromVolumeManager(&paths, PathTypes::kBlkDevice);
- startGc(paths);
+ startGc(paths);
- bool gc_aborted = waitForGc(paths);
+ gc_aborted = waitForGc(paths);
- stopGc(paths);
+ stopGc(paths);
+ }
lk.lock();
idle_maint_stat = IdleMaintStats::kStopped;
@@ -480,5 +498,150 @@
return android::OK;
}
+int getLifeTime(const std::string& path) {
+ std::string result;
+
+ if (!ReadFileToString(path, &result)) {
+ PLOG(WARNING) << "Reading lifetime estimation failed for " << path;
+ return -1;
+ }
+ return std::stoi(result, 0, 16);
+}
+
+int32_t GetStorageLifeTime() {
+ std::string path = getDevSysfsPath();
+ if (path.empty()) {
+ return -1;
+ }
+
+ std::string lifeTimeBasePath = path + "/health_descriptor/life_time_estimation_";
+
+ int32_t lifeTime = getLifeTime(lifeTimeBasePath + "c");
+ if (lifeTime != -1) {
+ return lifeTime;
+ }
+
+ int32_t lifeTimeA = getLifeTime(lifeTimeBasePath + "a");
+ int32_t lifeTimeB = getLifeTime(lifeTimeBasePath + "b");
+ lifeTime = std::max(lifeTimeA, lifeTimeB);
+ if (lifeTime != -1) {
+ return lifeTime == 0 ? -1 : lifeTime * 10;
+ }
+ return -1;
+}
+
+void SetGCUrgentPace(int32_t neededSegments, int32_t minSegmentThreshold, float dirtyReclaimRate,
+ float reclaimWeight) {
+ std::list<std::string> paths;
+ bool needGC = true;
+
+ addFromFstab(&paths, PathTypes::kBlkDevice, true);
+ if (paths.empty()) {
+ LOG(WARNING) << "There is no valid blk device path for data partition";
+ return;
+ }
+
+ std::string f2fsSysfsPath = paths.front();
+ std::string freeSegmentsPath = f2fsSysfsPath + "/free_segments";
+ std::string dirtySegmentsPath = f2fsSysfsPath + "/dirty_segments";
+ std::string gcSleepTimePath = f2fsSysfsPath + "/gc_urgent_sleep_time";
+ std::string gcUrgentModePath = f2fsSysfsPath + "/gc_urgent";
+ std::string freeSegmentsStr, dirtySegmentsStr;
+
+ if (!ReadFileToString(freeSegmentsPath, &freeSegmentsStr)) {
+ PLOG(WARNING) << "Reading failed in " << freeSegmentsPath;
+ return;
+ }
+
+ if (!ReadFileToString(dirtySegmentsPath, &dirtySegmentsStr)) {
+ PLOG(WARNING) << "Reading failed in " << dirtySegmentsPath;
+ return;
+ }
+
+ int32_t freeSegments = std::stoi(freeSegmentsStr);
+ int32_t dirtySegments = std::stoi(dirtySegmentsStr);
+
+ neededSegments *= reclaimWeight;
+ if (freeSegments >= neededSegments) {
+ LOG(INFO) << "Enough free segments: " << freeSegments
+ << ", needed segments: " << neededSegments;
+ needGC = false;
+ } else if (freeSegments + dirtySegments < minSegmentThreshold) {
+ LOG(INFO) << "The sum of free segments: " << freeSegments
+ << ", dirty segments: " << dirtySegments << " is under " << minSegmentThreshold;
+ needGC = false;
+ }
+
+ if (!needGC) {
+ if (!WriteStringToFile(std::to_string(GC_NORMAL_MODE), gcUrgentModePath)) {
+ PLOG(WARNING) << "Writing failed in " << gcUrgentModePath;
+ }
+ return;
+ }
+
+ int32_t sleepTime;
+
+ neededSegments -= freeSegments;
+ neededSegments = std::min(neededSegments, (int32_t)(dirtySegments * dirtyReclaimRate));
+ if (neededSegments == 0) {
+ sleepTime = MIN_GC_URGENT_SLEEP_TIME;
+ } else {
+ sleepTime = ONE_HOUR_IN_MS / neededSegments;
+ if (sleepTime < MIN_GC_URGENT_SLEEP_TIME) {
+ sleepTime = MIN_GC_URGENT_SLEEP_TIME;
+ }
+ }
+ if (!WriteStringToFile(std::to_string(sleepTime), gcSleepTimePath)) {
+ PLOG(WARNING) << "Writing failed in " << gcSleepTimePath;
+ return;
+ }
+
+ if (!WriteStringToFile(std::to_string(GC_URGENT_HIGH_MODE), gcUrgentModePath)) {
+ PLOG(WARNING) << "Writing failed in " << gcUrgentModePath;
+ return;
+ }
+
+ LOG(INFO) << "Successfully set gc urgent mode: "
+ << "free segments: " << freeSegments << ", reclaim target: " << neededSegments
+ << ", sleep time: " << sleepTime;
+}
+
+static int32_t getLifeTimeWrite() {
+ std::list<std::string> paths;
+ addFromFstab(&paths, PathTypes::kBlkDevice, true);
+ if (paths.empty()) {
+ LOG(WARNING) << "There is no valid blk device path for data partition";
+ return -1;
+ }
+
+ std::string writeKbytesPath = paths.front() + "/lifetime_write_kbytes";
+ std::string writeKbytesStr;
+ if (!ReadFileToString(writeKbytesPath, &writeKbytesStr)) {
+ PLOG(WARNING) << "Reading failed in " << writeKbytesPath;
+ return -1;
+ }
+
+ long long writeBytes = std::stoll(writeKbytesStr);
+ return writeBytes / KBYTES_IN_SEGMENT;
+}
+
+void RefreshLatestWrite() {
+ int32_t segmentWrite = getLifeTimeWrite();
+ if (segmentWrite != -1) {
+ previousSegmentWrite = segmentWrite;
+ }
+}
+
+int32_t GetWriteAmount() {
+ int32_t currentSegmentWrite = getLifeTimeWrite();
+ if (currentSegmentWrite == -1) {
+ return -1;
+ }
+
+ int32_t writeAmount = currentSegmentWrite - previousSegmentWrite;
+ previousSegmentWrite = currentSegmentWrite;
+ return writeAmount;
+}
+
} // namespace vold
} // namespace android
diff --git a/IdleMaint.h b/IdleMaint.h
index e043db4..ae70b63 100644
--- a/IdleMaint.h
+++ b/IdleMaint.h
@@ -23,8 +23,13 @@
namespace vold {
void Trim(const android::sp<android::os::IVoldTaskListener>& listener);
-int RunIdleMaint(const android::sp<android::os::IVoldTaskListener>& listener);
+int RunIdleMaint(bool needGC, const android::sp<android::os::IVoldTaskListener>& listener);
int AbortIdleMaint(const android::sp<android::os::IVoldTaskListener>& listener);
+int32_t GetStorageLifeTime();
+void SetGCUrgentPace(int32_t neededSegments, int32_t minSegmentThreshold, float dirtyReclaimRate,
+ float reclaimWeight);
+void RefreshLatestWrite();
+int32_t GetWriteAmount();
} // namespace vold
} // namespace android
diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp
index 8c9cc16..1c94220 100644
--- a/VoldNativeService.cpp
+++ b/VoldNativeService.cpp
@@ -470,11 +470,11 @@
}
binder::Status VoldNativeService::runIdleMaint(
- const android::sp<android::os::IVoldTaskListener>& listener) {
+ bool needGC, const android::sp<android::os::IVoldTaskListener>& listener) {
ENFORCE_SYSTEM_OR_ROOT;
ACQUIRE_LOCK;
- std::thread([=]() { android::vold::RunIdleMaint(listener); }).detach();
+ std::thread([=]() { android::vold::RunIdleMaint(needGC, listener); }).detach();
return Ok();
}
@@ -487,6 +487,40 @@
return Ok();
}
+binder::Status VoldNativeService::getStorageLifeTime(int32_t* _aidl_return) {
+ ENFORCE_SYSTEM_OR_ROOT;
+ ACQUIRE_LOCK;
+
+ *_aidl_return = GetStorageLifeTime();
+ return Ok();
+}
+
+binder::Status VoldNativeService::setGCUrgentPace(int32_t neededSegments,
+ int32_t minSegmentThreshold,
+ float dirtyReclaimRate, float reclaimWeight) {
+ ENFORCE_SYSTEM_OR_ROOT;
+ ACQUIRE_LOCK;
+
+ SetGCUrgentPace(neededSegments, minSegmentThreshold, dirtyReclaimRate, reclaimWeight);
+ return Ok();
+}
+
+binder::Status VoldNativeService::refreshLatestWrite() {
+ ENFORCE_SYSTEM_OR_ROOT;
+ ACQUIRE_LOCK;
+
+ RefreshLatestWrite();
+ return Ok();
+}
+
+binder::Status VoldNativeService::getWriteAmount(int32_t* _aidl_return) {
+ ENFORCE_SYSTEM_OR_ROOT;
+ ACQUIRE_LOCK;
+
+ *_aidl_return = GetWriteAmount();
+ return Ok();
+}
+
binder::Status VoldNativeService::mountAppFuse(int32_t uid, int32_t mountId,
android::base::unique_fd* _aidl_return) {
ENFORCE_SYSTEM_OR_ROOT;
diff --git a/VoldNativeService.h b/VoldNativeService.h
index 5fa04f5..49bcbaa 100644
--- a/VoldNativeService.h
+++ b/VoldNativeService.h
@@ -85,8 +85,14 @@
binder::Status fstrim(int32_t fstrimFlags,
const android::sp<android::os::IVoldTaskListener>& listener);
- binder::Status runIdleMaint(const android::sp<android::os::IVoldTaskListener>& listener);
+ binder::Status runIdleMaint(bool needGC,
+ const android::sp<android::os::IVoldTaskListener>& listener);
binder::Status abortIdleMaint(const android::sp<android::os::IVoldTaskListener>& listener);
+ binder::Status getStorageLifeTime(int32_t* _aidl_return);
+ binder::Status setGCUrgentPace(int32_t neededSegments, int32_t minSegmentThreshold,
+ float dirtyReclaimRate, float reclaimWeight);
+ binder::Status refreshLatestWrite();
+ binder::Status getWriteAmount(int32_t* _aidl_return);
binder::Status mountAppFuse(int32_t uid, int32_t mountId,
android::base::unique_fd* _aidl_return);
diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl
index 8dd7860..c72ceea 100644
--- a/binder/android/os/IVold.aidl
+++ b/binder/android/os/IVold.aidl
@@ -65,8 +65,13 @@
void destroyObb(@utf8InCpp String volId);
void fstrim(int fstrimFlags, IVoldTaskListener listener);
- void runIdleMaint(IVoldTaskListener listener);
+ void runIdleMaint(boolean needGC, IVoldTaskListener listener);
void abortIdleMaint(IVoldTaskListener listener);
+ int getStorageLifeTime();
+ void setGCUrgentPace(int neededSegments, int minSegmentThreshold,
+ float dirtyReclaimRate, float reclaimWeight);
+ void refreshLatestWrite();
+ int getWriteAmount();
FileDescriptor mountAppFuse(int uid, int mountId);
void unmountAppFuse(int uid, int mountId);