2 * Copyright (C) 2015 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "IdleMaint.h"
18 #include "FileDeviceUtils.h"
21 #include "VolumeManager.h"
22 #include "model/PrivateVolume.h"
27 #include <aidl/android/hardware/health/storage/BnGarbageCollectCallback.h>
28 #include <aidl/android/hardware/health/storage/IStorage.h>
29 #include <android-base/chrono_utils.h>
30 #include <android-base/file.h>
31 #include <android-base/logging.h>
32 #include <android-base/stringprintf.h>
33 #include <android-base/strings.h>
34 #include <android/binder_manager.h>
35 #include <android/hardware/health/storage/1.0/IStorage.h>
37 #include <private/android_filesystem_config.h>
38 #include <wakelock/wakelock.h>
42 #include <sys/mount.h>
44 #include <sys/types.h>
47 using android::base::Basename;
48 using android::base::ReadFileToString;
49 using android::base::Realpath;
50 using android::base::StringPrintf;
51 using android::base::Timer;
52 using android::base::WriteStringToFile;
53 using android::hardware::Return;
54 using android::hardware::Void;
55 using AStorage = aidl::android::hardware::health::storage::IStorage;
56 using ABnGarbageCollectCallback =
57 aidl::android::hardware::health::storage::BnGarbageCollectCallback;
58 using AResult = aidl::android::hardware::health::storage::Result;
59 using HStorage = android::hardware::health::storage::V1_0::IStorage;
60 using HGarbageCollectCallback = android::hardware::health::storage::V1_0::IGarbageCollectCallback;
61 using HResult = android::hardware::health::storage::V1_0::Result;
62 using std::string_literals::operator""s;
67 enum class PathTypes {
72 enum class IdleMaintStats {
78 static const char* kWakeLock = "IdleMaint";
79 static const int DIRTY_SEGMENTS_THRESHOLD = 100;
86 static const int GC_TIMEOUT_SEC = 420;
87 static const int DEVGC_TIMEOUT_SEC = 120;
89 static IdleMaintStats idle_maint_stat(IdleMaintStats::kStopped);
90 static std::condition_variable cv_abort, cv_stop;
91 static std::mutex cv_m;
93 static void addFromVolumeManager(std::list<std::string>* paths, PathTypes path_type) {
94 VolumeManager* vm = VolumeManager::Instance();
95 std::list<std::string> privateIds;
96 vm->listVolumes(VolumeBase::Type::kPrivate, privateIds);
97 for (const auto& id : privateIds) {
98 PrivateVolume* vol = static_cast<PrivateVolume*>(vm->findVolume(id).get());
99 if (vol != nullptr && vol->getState() == VolumeBase::State::kMounted) {
100 if (path_type == PathTypes::kMountPoint) {
101 paths->push_back(vol->getPath());
102 } else if (path_type == PathTypes::kBlkDevice) {
104 const std::string& fs_type = vol->getFsType();
105 if (fs_type == "f2fs" && (Realpath(vol->getRawDmDevPath(), &gc_path) ||
106 Realpath(vol->getRawDevPath(), &gc_path))) {
107 paths->push_back(std::string("/sys/fs/") + fs_type + "/" + Basename(gc_path));
114 static void addFromFstab(std::list<std::string>* paths, PathTypes path_type) {
115 std::string previous_mount_point;
116 for (const auto& entry : fstab_default) {
117 // Skip raw partitions and swap space.
118 if (entry.fs_type == "emmc" || entry.fs_type == "mtd" || entry.fs_type == "swap") {
121 // Skip read-only filesystems and bind mounts.
122 if (entry.flags & (MS_RDONLY | MS_BIND)) {
125 // Skip anything without an underlying block device, e.g. virtiofs.
126 if (entry.blk_device[0] != '/') {
129 if (entry.fs_mgr_flags.vold_managed) {
130 continue; // Should we trim fat32 filesystems?
132 if (entry.fs_mgr_flags.no_trim) {
136 // Skip the multi-type partitions, which are required to be following each other.
137 // See fs_mgr.c's mount_with_alternatives().
138 if (entry.mount_point == previous_mount_point) {
142 if (path_type == PathTypes::kMountPoint) {
143 paths->push_back(entry.mount_point);
144 } else if (path_type == PathTypes::kBlkDevice) {
146 if (entry.fs_type == "f2fs" &&
147 Realpath(android::vold::BlockDeviceForPath(entry.mount_point + "/"), &gc_path)) {
148 paths->push_back("/sys/fs/" + entry.fs_type + "/" + Basename(gc_path));
152 previous_mount_point = entry.mount_point;
156 void Trim(const android::sp<android::os::IVoldTaskListener>& listener) {
157 auto wl = android::wakelock::WakeLock::tryGet(kWakeLock);
158 if (!wl.has_value()) {
162 // Collect both fstab and vold volumes
163 std::list<std::string> paths;
164 addFromFstab(&paths, PathTypes::kMountPoint);
165 addFromVolumeManager(&paths, PathTypes::kMountPoint);
167 for (const auto& path : paths) {
168 LOG(DEBUG) << "Starting trim of " << path;
170 android::os::PersistableBundle extras;
171 extras.putString(String16("path"), String16(path.c_str()));
173 int fd = open(path.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
175 PLOG(WARNING) << "Failed to open " << path;
177 listener->onStatus(-1, extras);
182 struct fstrim_range range;
183 memset(&range, 0, sizeof(range));
184 range.len = ULLONG_MAX;
186 nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
187 if (ioctl(fd, FITRIM, &range)) {
188 PLOG(WARNING) << "Trim failed on " << path;
190 listener->onStatus(-1, extras);
193 nsecs_t time = systemTime(SYSTEM_TIME_BOOTTIME) - start;
194 LOG(INFO) << "Trimmed " << range.len << " bytes on " << path << " in "
195 << nanoseconds_to_milliseconds(time) << "ms";
196 extras.putLong(String16("bytes"), range.len);
197 extras.putLong(String16("time"), time);
199 listener->onStatus(0, extras);
206 android::os::PersistableBundle extras;
207 listener->onFinished(0, extras);
212 static bool waitForGc(const std::list<std::string>& paths) {
213 std::unique_lock<std::mutex> lk(cv_m, std::defer_lock);
214 bool stop = false, aborted = false;
217 while (!stop && !aborted) {
219 for (const auto& path : paths) {
220 std::string dirty_segments;
221 if (!ReadFileToString(path + "/dirty_segments", &dirty_segments)) {
222 PLOG(WARNING) << "Reading dirty_segments failed in " << path;
225 if (std::stoi(dirty_segments) > DIRTY_SEGMENTS_THRESHOLD) {
233 if (timer.duration() >= std::chrono::seconds(GC_TIMEOUT_SEC)) {
234 LOG(WARNING) << "GC timeout";
240 cv_abort.wait_for(lk, 10s, [] { return idle_maint_stat == IdleMaintStats::kAbort; });
247 static int startGc(const std::list<std::string>& paths) {
248 for (const auto& path : paths) {
249 LOG(DEBUG) << "Start GC on " << path;
250 if (!WriteStringToFile("1", path + "/gc_urgent")) {
251 PLOG(WARNING) << "Start GC failed on " << path;
257 static int stopGc(const std::list<std::string>& paths) {
258 for (const auto& path : paths) {
259 LOG(DEBUG) << "Stop GC on " << path;
260 if (!WriteStringToFile("0", path + "/gc_urgent")) {
261 PLOG(WARNING) << "Stop GC failed on " << path;
267 static void runDevGcFstab(void) {
269 for (const auto& entry : fstab_default) {
270 if (!entry.sysfs_path.empty()) {
271 path = entry.sysfs_path;
280 path = path + "/manual_gc";
283 LOG(DEBUG) << "Start Dev GC on " << path;
286 if (!ReadFileToString(path, &require)) {
287 PLOG(WARNING) << "Reading manual_gc failed in " << path;
290 require = android::base::Trim(require);
291 if (require == "" || require == "off" || require == "disabled") {
292 LOG(DEBUG) << "No more to do Dev GC";
296 LOG(DEBUG) << "Trigger Dev GC on " << path;
297 if (!WriteStringToFile("1", path)) {
298 PLOG(WARNING) << "Start Dev GC failed on " << path;
302 if (timer.duration() >= std::chrono::seconds(DEVGC_TIMEOUT_SEC)) {
303 LOG(WARNING) << "Dev GC timeout";
308 LOG(DEBUG) << "Stop Dev GC on " << path;
309 if (!WriteStringToFile("0", path)) {
310 PLOG(WARNING) << "Stop Dev GC failed on " << path;
315 enum class IDL { HIDL, AIDL };
316 std::ostream& operator<<(std::ostream& os, IDL idl) {
317 return os << (idl == IDL::HIDL ? "HIDL" : "AIDL");
320 template <IDL idl, typename Result>
321 class GcCallbackImpl {
323 void onFinishInternal(Result result) {
324 std::unique_lock<std::mutex> lock(mMutex);
332 void wait(uint64_t seconds) {
333 std::unique_lock<std::mutex> lock(mMutex);
334 mCv.wait_for(lock, std::chrono::seconds(seconds), [this] { return mFinished; });
337 LOG(WARNING) << "Dev GC on " << idl << " HAL timeout";
338 } else if (mResult != Result::SUCCESS) {
339 LOG(WARNING) << "Dev GC on " << idl << " HAL failed with " << toString(mResult);
341 LOG(INFO) << "Dev GC on " << idl << " HAL successful";
347 std::condition_variable mCv;
348 bool mFinished{false};
349 Result mResult{Result::UNKNOWN_ERROR};
352 class AGcCallbackImpl : public ABnGarbageCollectCallback,
353 public GcCallbackImpl<IDL::AIDL, AResult> {
354 ndk::ScopedAStatus onFinish(AResult result) override {
355 onFinishInternal(result);
356 return ndk::ScopedAStatus::ok();
360 class HGcCallbackImpl : public HGarbageCollectCallback, public GcCallbackImpl<IDL::HIDL, HResult> {
361 Return<void> onFinish(HResult result) override {
362 onFinishInternal(result);
367 template <IDL idl, typename Service, typename GcCallbackImpl, typename GetDescription>
368 static void runDevGcOnHal(Service service, GcCallbackImpl cb, GetDescription get_description) {
369 LOG(DEBUG) << "Start Dev GC on " << idl << " HAL";
370 auto ret = service->garbageCollect(DEVGC_TIMEOUT_SEC, cb);
372 LOG(WARNING) << "Cannot start Dev GC on " << idl
373 << " HAL: " << std::invoke(get_description, ret);
376 cb->wait(DEVGC_TIMEOUT_SEC);
379 static void runDevGc(void) {
380 auto aidl_service_name = AStorage::descriptor + "/default"s;
381 if (AServiceManager_isDeclared(aidl_service_name.c_str())) {
382 ndk::SpAIBinder binder(AServiceManager_waitForService(aidl_service_name.c_str()));
383 if (binder.get() != nullptr) {
384 std::shared_ptr<AStorage> aidl_service = AStorage::fromBinder(binder);
385 if (aidl_service != nullptr) {
386 runDevGcOnHal<IDL::AIDL>(aidl_service, ndk::SharedRefBase::make<AGcCallbackImpl>(),
387 &ndk::ScopedAStatus::getDescription);
391 LOG(WARNING) << "Device declares " << aidl_service_name
392 << " but it is not running, skip dev GC on AIDL HAL";
395 auto hidl_service = HStorage::getService();
396 if (hidl_service != nullptr) {
397 runDevGcOnHal<IDL::HIDL>(hidl_service, sp<HGcCallbackImpl>(new HGcCallbackImpl()),
398 &Return<void>::description);
401 // fallback to legacy code path
405 int RunIdleMaint(const android::sp<android::os::IVoldTaskListener>& listener) {
406 std::unique_lock<std::mutex> lk(cv_m);
407 if (idle_maint_stat != IdleMaintStats::kStopped) {
408 LOG(DEBUG) << "idle maintenance is already running";
410 android::os::PersistableBundle extras;
411 listener->onFinished(0, extras);
415 idle_maint_stat = IdleMaintStats::kRunning;
418 LOG(DEBUG) << "idle maintenance started";
420 auto wl = android::wakelock::WakeLock::tryGet(kWakeLock);
421 if (!wl.has_value()) {
422 return android::UNEXPECTED_NULL;
425 std::list<std::string> paths;
426 addFromFstab(&paths, PathTypes::kBlkDevice);
427 addFromVolumeManager(&paths, PathTypes::kBlkDevice);
431 bool gc_aborted = waitForGc(paths);
436 idle_maint_stat = IdleMaintStats::kStopped;
439 cv_stop.notify_one();
447 android::os::PersistableBundle extras;
448 listener->onFinished(0, extras);
451 LOG(DEBUG) << "idle maintenance completed";
456 int AbortIdleMaint(const android::sp<android::os::IVoldTaskListener>& listener) {
457 auto wl = android::wakelock::WakeLock::tryGet(kWakeLock);
458 if (!wl.has_value()) {
459 return android::UNEXPECTED_NULL;
462 std::unique_lock<std::mutex> lk(cv_m);
463 if (idle_maint_stat != IdleMaintStats::kStopped) {
464 idle_maint_stat = IdleMaintStats::kAbort;
466 cv_abort.notify_one();
468 LOG(DEBUG) << "aborting idle maintenance";
469 cv_stop.wait(lk, [] { return idle_maint_stat == IdleMaintStats::kStopped; });
474 android::os::PersistableBundle extras;
475 listener->onFinished(0, extras);
478 LOG(DEBUG) << "idle maintenance stopped";
484 } // namespace android