Merge "Add interfaces required by smart idle maintenance service" am: 6845e06ab9

Original change: https://android-review.googlesource.com/c/platform/system/vold/+/1936286

Change-Id: Ib432dfc545cc95fc0c0063b84343b3899df259b6
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);