From 999fceb07c67a686085124259f6c82ba1fb8e8c0 Mon Sep 17 00:00:00 2001 From: Daeho Jeong Date: Tue, 4 Jan 2022 11:37:39 -0800 Subject: [PATCH] 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 Change-Id: I012cfb9b01e5d21ec71700c3c52ac9c096cd1a90 --- IdleMaint.cpp | 195 ++++++++++++++++++++++++++++++++--- IdleMaint.h | 7 +- VoldNativeService.cpp | 38 ++++++- VoldNativeService.h | 8 +- binder/android/os/IVold.aidl | 7 +- 5 files changed, 234 insertions(+), 21 deletions(-) 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 DIRTY_SEGMENTS_THRESHOLD = 100; */ 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 addFromVolumeManager(std::list* paths, PathTypes path_t } } -static void addFromFstab(std::list* paths, PathTypes path_type) { +static void addFromFstab(std::list* 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 @@ static void addFromFstab(std::list* paths, PathTypes path_type) { 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 @@ static void addFromFstab(std::list* paths, PathTypes path_type) { 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 @@ void Trim(const android::sp& listener) { // Collect both fstab and vold volumes std::list paths; - addFromFstab(&paths, PathTypes::kMountPoint); + addFromFstab(&paths, PathTypes::kMountPoint, false); addFromVolumeManager(&paths, PathTypes::kMountPoint); for (const auto& path : paths) { @@ -264,15 +275,18 @@ static int stopGc(const std::list& paths) { 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 @@ static void runDevGc(void) { runDevGcFstab(); } -int RunIdleMaint(const android::sp& listener) { +int RunIdleMaint(bool needGC, const android::sp& listener) { std::unique_lock 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 @@ int RunIdleMaint(const android::sp& listener) { return android::UNEXPECTED_NULL; } - std::list paths; - addFromFstab(&paths, PathTypes::kBlkDevice); - addFromVolumeManager(&paths, PathTypes::kBlkDevice); + if (needGC) { + std::list 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 @@ int AbortIdleMaint(const android::sp& listener) 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 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 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 android { namespace vold { void Trim(const android::sp& listener); -int RunIdleMaint(const android::sp& listener); +int RunIdleMaint(bool needGC, const android::sp& listener); int AbortIdleMaint(const android::sp& 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::fstrim( } binder::Status VoldNativeService::runIdleMaint( - const android::sp& listener) { + bool needGC, const android::sp& 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 @@ binder::Status VoldNativeService::abortIdleMaint( 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 @@ class VoldNativeService : public BinderService, public os::Bn binder::Status fstrim(int32_t fstrimFlags, const android::sp& listener); - binder::Status runIdleMaint(const android::sp& listener); + binder::Status runIdleMaint(bool needGC, + const android::sp& listener); binder::Status abortIdleMaint(const android::sp& 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 @@ interface IVold { 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);