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 42using android::base::StringPrintf; 43 44namespace android { 45namespace vold { 46 47static const char* kWakeLock = "TrimTask"; 48 49TrimTask::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 (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 64TrimTask::~TrimTask() { 65} 66 67void TrimTask::addFromFstab() { 68 struct fstab *fstab; 69 struct fstab_rec *prev_rec = NULL; 70 71 fstab = fs_mgr_read_fstab(android::vold::DefaultFstabPath().c_str()); 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 fs_mgr_free_fstab(fstab); 100} 101 102void TrimTask::start() { 103 mThread = std::thread(&TrimTask::run, this); 104} 105 106static void notifyResult(const std::string& path, int64_t bytes, int64_t delta) { 107 std::string res(path 108 + " " + std::to_string(bytes) 109 + " " + std::to_string(delta)); 110 VolumeManager::Instance()->getBroadcaster()->sendBroadcast( 111 ResponseCode::TrimResult, res.c_str(), false); 112} 113 114void TrimTask::run() { 115 acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLock); 116 117 for (auto path : mPaths) { 118 LOG(DEBUG) << "Starting trim of " << path; 119 120 int fd = open(path.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW); 121 if (fd < 0) { 122 PLOG(WARNING) << "Failed to open " << path; 123 continue; 124 } 125 126 struct fstrim_range range; 127 memset(&range, 0, sizeof(range)); 128 range.len = ULLONG_MAX; 129 130 nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME); 131 if (ioctl(fd, (mFlags & Flags::kDeepTrim) ? FIDTRIM : FITRIM, &range)) { 132 PLOG(WARNING) << "Trim failed on " << path; 133 notifyResult(path, -1, -1); 134 } else { 135 nsecs_t delta = systemTime(SYSTEM_TIME_BOOTTIME) - start; 136 LOG(INFO) << "Trimmed " << range.len << " bytes on " << path 137 << " in " << nanoseconds_to_milliseconds(delta) << "ms"; 138 notifyResult(path, range.len, delta); 139 } 140 close(fd); 141 142 if (mFlags & Flags::kBenchmarkAfter) { 143#if BENCHMARK_ENABLED 144 BenchmarkPrivate(path); 145#else 146 LOG(DEBUG) << "Benchmark disabled"; 147#endif 148 } 149 } 150 151 release_wake_lock(kWakeLock); 152} 153 154} // namespace vold 155} // namespace android 156