• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
17 #include "IdleMaint.h"
18 #include "FileDeviceUtils.h"
19 #include "Utils.h"
20 #include "VoldUtil.h"
21 #include "VolumeManager.h"
22 #include "model/PrivateVolume.h"
23 
24 #include <thread>
25 #include <utility>
26 
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>
36 #include <fs_mgr.h>
37 #include <private/android_filesystem_config.h>
38 #include <wakelock/wakelock.h>
39 
40 #include <dirent.h>
41 #include <fcntl.h>
42 #include <sys/mount.h>
43 #include <sys/stat.h>
44 #include <sys/types.h>
45 #include <sys/wait.h>
46 
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;
63 
64 namespace android {
65 namespace vold {
66 
67 enum class PathTypes {
68     kMountPoint = 1,
69     kBlkDevice,
70 };
71 
72 enum class IdleMaintStats {
73     kStopped = 1,
74     kRunning,
75     kAbort,
76 };
77 
78 static const char* kWakeLock = "IdleMaint";
79 static const int DIRTY_SEGMENTS_THRESHOLD = 100;
80 /*
81  * Timing policy:
82  *  1. F2FS_GC = 7 mins
83  *  2. Trim = 1 min
84  *  3. Dev GC = 2 mins
85  */
86 static const int GC_TIMEOUT_SEC = 420;
87 static const int DEVGC_TIMEOUT_SEC = 120;
88 
89 static IdleMaintStats idle_maint_stat(IdleMaintStats::kStopped);
90 static std::condition_variable cv_abort, cv_stop;
91 static std::mutex cv_m;
92 
addFromVolumeManager(std::list<std::string> * paths,PathTypes path_type)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) {
103                 std::string gc_path;
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));
108                 }
109             }
110         }
111     }
112 }
113 
addFromFstab(std::list<std::string> * paths,PathTypes path_type)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") {
119             continue;
120         }
121         // Skip read-only filesystems and bind mounts.
122         if (entry.flags & (MS_RDONLY | MS_BIND)) {
123             continue;
124         }
125         // Skip anything without an underlying block device, e.g. virtiofs.
126         if (entry.blk_device[0] != '/') {
127             continue;
128         }
129         if (entry.fs_mgr_flags.vold_managed) {
130             continue;  // Should we trim fat32 filesystems?
131         }
132         if (entry.fs_mgr_flags.no_trim) {
133             continue;
134         }
135 
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) {
139             continue;
140         }
141 
142         if (path_type == PathTypes::kMountPoint) {
143             paths->push_back(entry.mount_point);
144         } else if (path_type == PathTypes::kBlkDevice) {
145             std::string gc_path;
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));
149             }
150         }
151 
152         previous_mount_point = entry.mount_point;
153     }
154 }
155 
Trim(const android::sp<android::os::IVoldTaskListener> & listener)156 void Trim(const android::sp<android::os::IVoldTaskListener>& listener) {
157     auto wl = android::wakelock::WakeLock::tryGet(kWakeLock);
158     if (!wl.has_value()) {
159         return;
160     }
161 
162     // Collect both fstab and vold volumes
163     std::list<std::string> paths;
164     addFromFstab(&paths, PathTypes::kMountPoint);
165     addFromVolumeManager(&paths, PathTypes::kMountPoint);
166 
167     for (const auto& path : paths) {
168         LOG(DEBUG) << "Starting trim of " << path;
169 
170         android::os::PersistableBundle extras;
171         extras.putString(String16("path"), String16(path.c_str()));
172 
173         int fd = open(path.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
174         if (fd < 0) {
175             PLOG(WARNING) << "Failed to open " << path;
176             if (listener) {
177                 listener->onStatus(-1, extras);
178             }
179             continue;
180         }
181 
182         struct fstrim_range range;
183         memset(&range, 0, sizeof(range));
184         range.len = ULLONG_MAX;
185 
186         nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
187         if (ioctl(fd, FITRIM, &range)) {
188             PLOG(WARNING) << "Trim failed on " << path;
189             if (listener) {
190                 listener->onStatus(-1, extras);
191             }
192         } else {
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);
198             if (listener) {
199                 listener->onStatus(0, extras);
200             }
201         }
202         close(fd);
203     }
204 
205     if (listener) {
206         android::os::PersistableBundle extras;
207         listener->onFinished(0, extras);
208     }
209 
210 }
211 
waitForGc(const std::list<std::string> & paths)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;
215     Timer timer;
216 
217     while (!stop && !aborted) {
218         stop = true;
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;
223                 continue;
224             }
225             if (std::stoi(dirty_segments) > DIRTY_SEGMENTS_THRESHOLD) {
226                 stop = false;
227                 break;
228             }
229         }
230 
231         if (stop) break;
232 
233         if (timer.duration() >= std::chrono::seconds(GC_TIMEOUT_SEC)) {
234             LOG(WARNING) << "GC timeout";
235             break;
236         }
237 
238         lk.lock();
239         aborted =
240             cv_abort.wait_for(lk, 10s, [] { return idle_maint_stat == IdleMaintStats::kAbort; });
241         lk.unlock();
242     }
243 
244     return aborted;
245 }
246 
startGc(const std::list<std::string> & paths)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;
252         }
253     }
254     return android::OK;
255 }
256 
stopGc(const std::list<std::string> & paths)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;
262         }
263     }
264     return android::OK;
265 }
266 
runDevGcFstab(void)267 static void runDevGcFstab(void) {
268     std::string path;
269     for (const auto& entry : fstab_default) {
270         if (!entry.sysfs_path.empty()) {
271             path = entry.sysfs_path;
272             break;
273         }
274     }
275 
276     if (path.empty()) {
277         return;
278     }
279 
280     path = path + "/manual_gc";
281     Timer timer;
282 
283     LOG(DEBUG) << "Start Dev GC on " << path;
284     while (1) {
285         std::string require;
286         if (!ReadFileToString(path, &require)) {
287             PLOG(WARNING) << "Reading manual_gc failed in " << path;
288             break;
289         }
290         require = android::base::Trim(require);
291         if (require == "" || require == "off" || require == "disabled") {
292             LOG(DEBUG) << "No more to do Dev GC";
293             break;
294         }
295 
296         LOG(DEBUG) << "Trigger Dev GC on " << path;
297         if (!WriteStringToFile("1", path)) {
298             PLOG(WARNING) << "Start Dev GC failed on " << path;
299             break;
300         }
301 
302         if (timer.duration() >= std::chrono::seconds(DEVGC_TIMEOUT_SEC)) {
303             LOG(WARNING) << "Dev GC timeout";
304             break;
305         }
306         sleep(2);
307     }
308     LOG(DEBUG) << "Stop Dev GC on " << path;
309     if (!WriteStringToFile("0", path)) {
310         PLOG(WARNING) << "Stop Dev GC failed on " << path;
311     }
312     return;
313 }
314 
315 enum class IDL { HIDL, AIDL };
operator <<(std::ostream & os,IDL idl)316 std::ostream& operator<<(std::ostream& os, IDL idl) {
317     return os << (idl == IDL::HIDL ? "HIDL" : "AIDL");
318 }
319 
320 template <IDL idl, typename Result>
321 class GcCallbackImpl {
322   protected:
onFinishInternal(Result result)323     void onFinishInternal(Result result) {
324         std::unique_lock<std::mutex> lock(mMutex);
325         mFinished = true;
326         mResult = result;
327         lock.unlock();
328         mCv.notify_all();
329     }
330 
331   public:
wait(uint64_t seconds)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; });
335 
336         if (!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);
340         } else {
341             LOG(INFO) << "Dev GC on " << idl << " HAL successful";
342         }
343     }
344 
345   private:
346     std::mutex mMutex;
347     std::condition_variable mCv;
348     bool mFinished{false};
349     Result mResult{Result::UNKNOWN_ERROR};
350 };
351 
352 class AGcCallbackImpl : public ABnGarbageCollectCallback,
353                         public GcCallbackImpl<IDL::AIDL, AResult> {
onFinish(AResult result)354     ndk::ScopedAStatus onFinish(AResult result) override {
355         onFinishInternal(result);
356         return ndk::ScopedAStatus::ok();
357     }
358 };
359 
360 class HGcCallbackImpl : public HGarbageCollectCallback, public GcCallbackImpl<IDL::HIDL, HResult> {
onFinish(HResult result)361     Return<void> onFinish(HResult result) override {
362         onFinishInternal(result);
363         return Void();
364     }
365 };
366 
367 template <IDL idl, typename Service, typename GcCallbackImpl, typename GetDescription>
runDevGcOnHal(Service service,GcCallbackImpl cb,GetDescription get_description)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);
371     if (!ret.isOk()) {
372         LOG(WARNING) << "Cannot start Dev GC on " << idl
373                      << " HAL: " << std::invoke(get_description, ret);
374         return;
375     }
376     cb->wait(DEVGC_TIMEOUT_SEC);
377 }
378 
runDevGc(void)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);
388                 return;
389             }
390         }
391         LOG(WARNING) << "Device declares " << aidl_service_name
392                      << " but it is not running, skip dev GC on AIDL HAL";
393         return;
394     }
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);
399         return;
400     }
401     // fallback to legacy code path
402     runDevGcFstab();
403 }
404 
RunIdleMaint(const android::sp<android::os::IVoldTaskListener> & listener)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";
409         if (listener) {
410             android::os::PersistableBundle extras;
411             listener->onFinished(0, extras);
412         }
413         return android::OK;
414     }
415     idle_maint_stat = IdleMaintStats::kRunning;
416     lk.unlock();
417 
418     LOG(DEBUG) << "idle maintenance started";
419 
420     auto wl = android::wakelock::WakeLock::tryGet(kWakeLock);
421     if (!wl.has_value()) {
422         return android::UNEXPECTED_NULL;
423     }
424 
425     std::list<std::string> paths;
426     addFromFstab(&paths, PathTypes::kBlkDevice);
427     addFromVolumeManager(&paths, PathTypes::kBlkDevice);
428 
429     startGc(paths);
430 
431     bool gc_aborted = waitForGc(paths);
432 
433     stopGc(paths);
434 
435     lk.lock();
436     idle_maint_stat = IdleMaintStats::kStopped;
437     lk.unlock();
438 
439     cv_stop.notify_one();
440 
441     if (!gc_aborted) {
442         Trim(nullptr);
443         runDevGc();
444     }
445 
446     if (listener) {
447         android::os::PersistableBundle extras;
448         listener->onFinished(0, extras);
449     }
450 
451     LOG(DEBUG) << "idle maintenance completed";
452 
453     return android::OK;
454 }
455 
AbortIdleMaint(const android::sp<android::os::IVoldTaskListener> & listener)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;
460     }
461 
462     std::unique_lock<std::mutex> lk(cv_m);
463     if (idle_maint_stat != IdleMaintStats::kStopped) {
464         idle_maint_stat = IdleMaintStats::kAbort;
465         lk.unlock();
466         cv_abort.notify_one();
467         lk.lock();
468         LOG(DEBUG) << "aborting idle maintenance";
469         cv_stop.wait(lk, [] { return idle_maint_stat == IdleMaintStats::kStopped; });
470     }
471     lk.unlock();
472 
473     if (listener) {
474         android::os::PersistableBundle extras;
475         listener->onFinished(0, extras);
476     }
477 
478     LOG(DEBUG) << "idle maintenance stopped";
479 
480     return android::OK;
481 }
482 
483 }  // namespace vold
484 }  // namespace android
485