From a370c14f75e97e131b06662f6bdd4691c4bce0e9 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Tue, 17 Oct 2017 15:41:45 -0700 Subject: [PATCH] Add functions to handle idle maintenance runIdleMaint is equivalent with: 1. echo 1 > /sys/fs/f2fs/sdX/gc_urgent 2. wait until /sys/fs/f2fs/sdX/dirty_segments <= threshold or timeout 3. echo 0 > /sys/fs/f2fs/sdX/gc_urgent 4. fstrim abortIdleMaint forces the wait loop above to exit and skips fstrim. However, if fstrim is already running, abortIdleMaint will just leave it run to completion. Test: adb shell sm idle-maint [run|abort] Bug: 67776637 Change-Id: I4adff8d9b6bbd63bce41368cea55dc9e9b117eb6 --- IdleMaint.cpp | 196 +++++++++++++++++++++++++++++++++-- IdleMaint.h | 2 + VoldNativeService.cpp | 22 ++++ VoldNativeService.h | 4 + binder/android/os/IVold.aidl | 2 + model/PrivateVolume.h | 2 + 6 files changed, 220 insertions(+), 8 deletions(-) diff --git a/IdleMaint.cpp b/IdleMaint.cpp index ed6374f..62086cd 100644 --- a/IdleMaint.cpp +++ b/IdleMaint.cpp @@ -17,7 +17,12 @@ #include "IdleMaint.h" #include "Utils.h" #include "VolumeManager.h" +#include "model/PrivateVolume.h" +#include + +#include +#include #include #include #include @@ -31,26 +36,60 @@ #include #include +using android::base::Basename; +using android::base::ReadFileToString; +using android::base::Realpath; using android::base::StringPrintf; +using android::base::Timer; +using android::base::WriteStringToFile; namespace android { namespace vold { -static const char* kWakeLock = "IdleMaint"; +enum class PathTypes { + kMountPoint = 1, + kBlkDevice, +}; -static void addFromVolumeManager(std::list* paths) { +enum class IdleMaintStats { + kStopped = 1, + kRunning, + kAbort, +}; + +static const char* kWakeLock = "IdleMaint"; +static const int DIRTY_SEGMENTS_THRESHOLD = 100; +static const int GC_TIMEOUT_SEC = 480; + +static IdleMaintStats idle_maint_stat(IdleMaintStats::kStopped); +static std::condition_variable cv_abort, cv_stop; +static std::mutex cv_m; + +static void addFromVolumeManager(std::list* paths, + PathTypes path_type) { VolumeManager* vm = VolumeManager::Instance(); std::list privateIds; vm->listVolumes(VolumeBase::Type::kPrivate, privateIds); for (const auto& id : privateIds) { - auto vol = vm->findVolume(id); + PrivateVolume* vol = static_cast(vm->findVolume(id).get()); if (vol != nullptr && vol->getState() == VolumeBase::State::kMounted) { - paths->push_back(vol->getPath()); + if (path_type == PathTypes::kMountPoint) { + paths->push_back(vol->getPath()); + } else if (path_type == PathTypes::kBlkDevice) { + std::string gc_path; + const std::string& fs_type = vol->getFsType(); + if (fs_type == "f2fs" && + Realpath(vol->getRawDevPath(), &gc_path)) { + paths->push_back(std::string("/sys/fs/") + fs_type + + "/" + Basename(gc_path)); + } + } + } } } -static void addFromFstab(std::list* paths) { +static void addFromFstab(std::list* paths, PathTypes path_type) { std::unique_ptr fstab(fs_mgr_read_fstab_default(), fs_mgr_free_fstab); struct fstab_rec *prev_rec = NULL; @@ -79,7 +118,17 @@ static void addFromFstab(std::list* paths) { continue; } - paths->push_back(fstab->recs[i].mount_point); + if (path_type == PathTypes::kMountPoint) { + paths->push_back(fstab->recs[i].mount_point); + } else if (path_type == PathTypes::kBlkDevice) { + std::string gc_path; + if (std::string(fstab->recs[i].fs_type) == "f2fs" && + Realpath(fstab->recs[i].blk_device, &gc_path)) { + paths->push_back(std::string("/sys/fs/") + fstab->recs[i].fs_type + + "/" + Basename(gc_path)); + } + } + prev_rec = &fstab->recs[i]; } } @@ -89,8 +138,8 @@ void Trim(const android::sp& listener) { // Collect both fstab and vold volumes std::list paths; - addFromFstab(&paths); - addFromVolumeManager(&paths); + addFromFstab(&paths, PathTypes::kMountPoint); + addFromVolumeManager(&paths, PathTypes::kMountPoint); for (const auto& path : paths) { LOG(DEBUG) << "Starting trim of " << path; @@ -138,5 +187,136 @@ void Trim(const android::sp& listener) { release_wake_lock(kWakeLock); } +static bool waitForGc(const std::list& paths) { + std::unique_lock lk(cv_m, std::defer_lock); + bool stop = false, aborted = false; + Timer timer; + + while (!stop && !aborted) { + stop = true; + for (const auto& path : paths) { + std::string dirty_segments; + if (!ReadFileToString(path + "/dirty_segments", &dirty_segments)) { + PLOG(WARNING) << "Reading dirty_segments failed in " << path; + continue; + } + if (std::stoi(dirty_segments) > DIRTY_SEGMENTS_THRESHOLD) { + stop = false; + break; + } + } + + if (stop) break; + + if (timer.duration() >= std::chrono::seconds(GC_TIMEOUT_SEC)) { + LOG(WARNING) << "GC timeout"; + break; + } + + lk.lock(); + aborted = cv_abort.wait_for(lk, 10s, []{ + return idle_maint_stat == IdleMaintStats::kAbort;}); + lk.unlock(); + } + + return aborted; +} + +static int startGc(const std::list& paths) { + for (const auto& path : paths) { + LOG(DEBUG) << "Start GC on " << path; + if (!WriteStringToFile("1", path + "/gc_urgent")) { + PLOG(WARNING) << "Start GC failed on " << path; + } + } + return android::OK; +} + +static int stopGc(const std::list& paths) { + for (const auto& path : paths) { + LOG(DEBUG) << "Stop GC on " << path; + if (!WriteStringToFile("0", path + "/gc_urgent")) { + PLOG(WARNING) << "Stop GC failed on " << path; + } + } + return android::OK; +} + +int RunIdleMaint(const android::sp& listener) { + std::unique_lock lk(cv_m); + if (idle_maint_stat != IdleMaintStats::kStopped) { + LOG(DEBUG) << "idle maintenance is already running"; + if (listener) { + android::os::PersistableBundle extras; + listener->onFinished(0, extras); + } + return android::OK; + } + idle_maint_stat = IdleMaintStats::kRunning; + lk.unlock(); + + LOG(DEBUG) << "idle maintenance started"; + + acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLock); + + std::list paths; + addFromFstab(&paths, PathTypes::kBlkDevice); + addFromVolumeManager(&paths, PathTypes::kBlkDevice); + + startGc(paths); + + bool gc_aborted = waitForGc(paths); + + stopGc(paths); + + lk.lock(); + idle_maint_stat = IdleMaintStats::kStopped; + lk.unlock(); + + cv_stop.notify_one(); + + if (!gc_aborted) { + Trim(nullptr); + } + + if (listener) { + android::os::PersistableBundle extras; + listener->onFinished(0, extras); + } + + LOG(DEBUG) << "idle maintenance completed"; + + release_wake_lock(kWakeLock); + + return android::OK; +} + +int AbortIdleMaint(const android::sp& listener) { + acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLock); + + std::unique_lock lk(cv_m); + if (idle_maint_stat != IdleMaintStats::kStopped) { + idle_maint_stat = IdleMaintStats::kAbort; + lk.unlock(); + cv_abort.notify_one(); + lk.lock(); + LOG(DEBUG) << "aborting idle maintenance"; + cv_stop.wait(lk, []{ + return idle_maint_stat == IdleMaintStats::kStopped;}); + } + lk.unlock(); + + if (listener) { + android::os::PersistableBundle extras; + listener->onFinished(0, extras); + } + + release_wake_lock(kWakeLock); + + LOG(DEBUG) << "idle maintenance stopped"; + + return android::OK; +} + } // namespace vold } // namespace android diff --git a/IdleMaint.h b/IdleMaint.h index 38dadfb..e043db4 100644 --- a/IdleMaint.h +++ b/IdleMaint.h @@ -23,6 +23,8 @@ namespace android { namespace vold { void Trim(const android::sp& listener); +int RunIdleMaint(const android::sp& listener); +int AbortIdleMaint(const android::sp& listener); } // namespace vold } // namespace android diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp index d7a6576..e8e151f 100644 --- a/VoldNativeService.cpp +++ b/VoldNativeService.cpp @@ -481,6 +481,28 @@ binder::Status VoldNativeService::fstrim(int32_t fstrimFlags, return ok(); } +binder::Status VoldNativeService::runIdleMaint( + const android::sp& listener) { + ENFORCE_UID(AID_SYSTEM); + ACQUIRE_LOCK; + + std::thread([=]() { + android::vold::RunIdleMaint(listener); + }).detach(); + return ok(); +} + +binder::Status VoldNativeService::abortIdleMaint( + const android::sp& listener) { + ENFORCE_UID(AID_SYSTEM); + ACQUIRE_LOCK; + + std::thread([=]() { + android::vold::AbortIdleMaint(listener); + }).detach(); + return ok(); +} + binder::Status VoldNativeService::mountAppFuse(int32_t uid, int32_t pid, int32_t mountId, android::base::unique_fd* _aidl_return) { ENFORCE_UID(AID_SYSTEM); diff --git a/VoldNativeService.h b/VoldNativeService.h index 7ca72e5..d107138 100644 --- a/VoldNativeService.h +++ b/VoldNativeService.h @@ -66,6 +66,10 @@ public: binder::Status fstrim(int32_t fstrimFlags, const android::sp& listener); + binder::Status runIdleMaint( + const android::sp& listener); + binder::Status abortIdleMaint( + const android::sp& listener); binder::Status mountAppFuse(int32_t uid, int32_t pid, int32_t mountId, android::base::unique_fd* _aidl_return); diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl index 5d182c9..c3f5029 100644 --- a/binder/android/os/IVold.aidl +++ b/binder/android/os/IVold.aidl @@ -54,6 +54,8 @@ interface IVold { void destroyObb(@utf8InCpp String volId); void fstrim(int fstrimFlags, IVoldTaskListener listener); + void runIdleMaint(IVoldTaskListener listener); + void abortIdleMaint(IVoldTaskListener listener); FileDescriptor mountAppFuse(int uid, int pid, int mountId); void unmountAppFuse(int uid, int pid, int mountId); diff --git a/model/PrivateVolume.h b/model/PrivateVolume.h index 95b718d..9508671 100644 --- a/model/PrivateVolume.h +++ b/model/PrivateVolume.h @@ -39,6 +39,8 @@ class PrivateVolume : public VolumeBase { public: PrivateVolume(dev_t device, const std::string& keyRaw); virtual ~PrivateVolume(); + const std::string& getFsType() { return mFsType; }; + const std::string& getRawDevPath() { return mRawDevPath; }; protected: status_t doCreate() override;