Home | History | Annotate | Download | only in vold
      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 "TrimTask.h"
     18 #include "Benchmark.h"
     19 #include "Utils.h"
     20 #include "VolumeManager.h"
     21 #include "ResponseCode.h"
     22 
     23 #include <android-base/stringprintf.h>
     24 #include <android-base/logging.h>
     25 #include <cutils/properties.h>
     26 #include <fs_mgr.h>
     27 #include <private/android_filesystem_config.h>
     28 #include <hardware_legacy/power.h>
     29 
     30 #include <dirent.h>
     31 #include <sys/mount.h>
     32 #include <sys/stat.h>
     33 #include <sys/types.h>
     34 #include <sys/wait.h>
     35 #include <fcntl.h>
     36 
     37 /* From a would-be kernel header */
     38 #define FIDTRIM         _IOWR('f', 128, struct fstrim_range)    /* Deep discard trim */
     39 
     40 #define BENCHMARK_ENABLED 1
     41 
     42 using android::base::StringPrintf;
     43 
     44 namespace android {
     45 namespace vold {
     46 
     47 static const char* kWakeLock = "TrimTask";
     48 
     49 TrimTask::TrimTask(int flags) : mFlags(flags) {
     50     // Collect both fstab and vold volumes
     51     addFromFstab();
     52 
     53     VolumeManager* vm = VolumeManager::Instance();
     54     std::list<std::string> privateIds;
     55     vm->listVolumes(VolumeBase::Type::kPrivate, privateIds);
     56     for (const auto& id : privateIds) {
     57         auto vol = vm->findVolume(id);
     58         if (vol != nullptr && vol->getState() == VolumeBase::State::kMounted) {
     59             mPaths.push_back(vol->getPath());
     60         }
     61     }
     62 }
     63 
     64 TrimTask::~TrimTask() {
     65 }
     66 
     67 void TrimTask::addFromFstab() {
     68     std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
     69                                                                fs_mgr_free_fstab);
     70     struct fstab_rec *prev_rec = NULL;
     71 
     72     for (int i = 0; i < fstab->num_entries; i++) {
     73         /* Skip raw partitions */
     74         if (!strcmp(fstab->recs[i].fs_type, "emmc") ||
     75             !strcmp(fstab->recs[i].fs_type, "mtd")) {
     76             continue;
     77         }
     78         /* Skip read-only filesystems */
     79         if (fstab->recs[i].flags & MS_RDONLY) {
     80             continue;
     81         }
     82         if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {
     83             continue; /* Should we trim fat32 filesystems? */
     84         }
     85         if (fs_mgr_is_notrim(&fstab->recs[i])) {
     86             continue;
     87         }
     88 
     89         /* Skip the multi-type partitions, which are required to be following each other.
     90          * See fs_mgr.c's mount_with_alternatives().
     91          */
     92         if (prev_rec && !strcmp(prev_rec->mount_point, fstab->recs[i].mount_point)) {
     93             continue;
     94         }
     95 
     96         mPaths.push_back(fstab->recs[i].mount_point);
     97         prev_rec = &fstab->recs[i];
     98     }
     99 }
    100 
    101 void TrimTask::start() {
    102     mThread = std::thread(&TrimTask::run, this);
    103 }
    104 
    105 static void notifyResult(const std::string& path, int64_t bytes, int64_t delta) {
    106     std::string res(path
    107             + " " + std::to_string(bytes)
    108             + " " + std::to_string(delta));
    109     VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
    110             ResponseCode::TrimResult, res.c_str(), false);
    111 }
    112 
    113 void TrimTask::run() {
    114     acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLock);
    115 
    116     for (const auto& path : mPaths) {
    117         LOG(DEBUG) << "Starting trim of " << path;
    118 
    119         int fd = open(path.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
    120         if (fd < 0) {
    121             PLOG(WARNING) << "Failed to open " << path;
    122             continue;
    123         }
    124 
    125         struct fstrim_range range;
    126         memset(&range, 0, sizeof(range));
    127         range.len = ULLONG_MAX;
    128 
    129         nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
    130         if (ioctl(fd, (mFlags & Flags::kDeepTrim) ? FIDTRIM : FITRIM, &range)) {
    131             PLOG(WARNING) << "Trim failed on " << path;
    132             notifyResult(path, -1, -1);
    133         } else {
    134             nsecs_t delta = systemTime(SYSTEM_TIME_BOOTTIME) - start;
    135             LOG(INFO) << "Trimmed " << range.len << " bytes on " << path
    136                     << " in " << nanoseconds_to_milliseconds(delta) << "ms";
    137             notifyResult(path, range.len, delta);
    138         }
    139         close(fd);
    140 
    141         if (mFlags & Flags::kBenchmarkAfter) {
    142 #if BENCHMARK_ENABLED
    143             BenchmarkPrivate(path);
    144 #else
    145             LOG(DEBUG) << "Benchmark disabled";
    146 #endif
    147         }
    148     }
    149 
    150     release_wake_lock(kWakeLock);
    151 }
    152 
    153 }  // namespace vold
    154 }  // namespace android
    155