Home | History | Annotate | Download | only in storaged
      1 /*
      2  * Copyright (C) 2016 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 #define LOG_TAG "storaged"
     18 
     19 #include <dirent.h>
     20 #include <stdlib.h>
     21 #include <stdio.h>
     22 #include <time.h>
     23 #include <unistd.h>
     24 #include <zlib.h>
     25 
     26 #include <chrono>
     27 #include <fstream>
     28 #include <sstream>
     29 #include <string>
     30 
     31 #include <android-base/file.h>
     32 #include <android-base/logging.h>
     33 #include <android/hidl/manager/1.0/IServiceManager.h>
     34 #include <batteryservice/BatteryServiceConstants.h>
     35 #include <cutils/properties.h>
     36 #include <healthhalutils/HealthHalUtils.h>
     37 #include <hidl/HidlTransportSupport.h>
     38 #include <hwbinder/IPCThreadState.h>
     39 #include <log/log.h>
     40 
     41 #include <storaged.h>
     42 #include <storaged_utils.h>
     43 
     44 using namespace android::base;
     45 using namespace chrono;
     46 using namespace google::protobuf::io;
     47 using namespace storaged_proto;
     48 
     49 namespace {
     50 
     51 /*
     52  * The system user is the initial user that is implicitly created on first boot
     53  * and hosts most of the system services. Keep this in sync with
     54  * frameworks/base/core/java/android/os/UserManager.java
     55  */
     56 constexpr int USER_SYSTEM = 0;
     57 
     58 constexpr ssize_t benchmark_unit_size = 16 * 1024;  // 16KB
     59 
     60 constexpr ssize_t min_benchmark_size = 128 * 1024;  // 128KB
     61 
     62 }  // namespace
     63 
     64 const uint32_t storaged_t::current_version = 4;
     65 
     66 using android::hardware::interfacesEqual;
     67 using android::hardware::Return;
     68 using android::hardware::health::V1_0::BatteryStatus;
     69 using android::hardware::health::V1_0::toString;
     70 using android::hardware::health::V2_0::get_health_service;
     71 using android::hardware::health::V2_0::HealthInfo;
     72 using android::hardware::health::V2_0::IHealth;
     73 using android::hardware::health::V2_0::Result;
     74 using android::hidl::manager::V1_0::IServiceManager;
     75 
     76 
     77 inline charger_stat_t is_charger_on(BatteryStatus prop) {
     78     return (prop == BatteryStatus::CHARGING || prop == BatteryStatus::FULL) ?
     79         CHARGER_ON : CHARGER_OFF;
     80 }
     81 
     82 Return<void> storaged_t::healthInfoChanged(const HealthInfo& props) {
     83     mUidm.set_charger_state(is_charger_on(props.legacy.batteryStatus));
     84     return android::hardware::Void();
     85 }
     86 
     87 void storaged_t::init() {
     88     init_health_service();
     89     mDsm = std::make_unique<disk_stats_monitor>(health);
     90     storage_info.reset(storage_info_t::get_storage_info(health));
     91 }
     92 
     93 void storaged_t::init_health_service() {
     94     if (!mUidm.enabled())
     95         return;
     96 
     97     health = get_health_service();
     98     if (health == NULL) {
     99         LOG_TO(SYSTEM, WARNING) << "health: failed to find IHealth service";
    100         return;
    101     }
    102 
    103     BatteryStatus status = BatteryStatus::UNKNOWN;
    104     auto ret = health->getChargeStatus([&](Result r, BatteryStatus v) {
    105         if (r != Result::SUCCESS) {
    106             LOG_TO(SYSTEM, WARNING)
    107                 << "health: cannot get battery status " << toString(r);
    108             return;
    109         }
    110         if (v == BatteryStatus::UNKNOWN) {
    111             LOG_TO(SYSTEM, WARNING) << "health: invalid battery status";
    112         }
    113         status = v;
    114     });
    115     if (!ret.isOk()) {
    116         LOG_TO(SYSTEM, WARNING) << "health: get charge status transaction error "
    117             << ret.description();
    118     }
    119 
    120     mUidm.init(is_charger_on(status));
    121     // register listener after init uid_monitor
    122     health->registerCallback(this);
    123     health->linkToDeath(this, 0 /* cookie */);
    124 }
    125 
    126 void storaged_t::serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who) {
    127     if (health != NULL && interfacesEqual(health, who.promote())) {
    128         LOG_TO(SYSTEM, ERROR) << "health service died, exiting";
    129         android::hardware::IPCThreadState::self()->stopProcess();
    130         exit(1);
    131     } else {
    132         LOG_TO(SYSTEM, ERROR) << "unknown service died";
    133     }
    134 }
    135 
    136 void storaged_t::report_storage_info() {
    137     storage_info->report();
    138 }
    139 
    140 /* storaged_t */
    141 storaged_t::storaged_t(void) {
    142     mConfig.periodic_chores_interval_unit =
    143         property_get_int32("ro.storaged.event.interval",
    144                            DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT);
    145 
    146     mConfig.event_time_check_usec =
    147         property_get_int32("ro.storaged.event.perf_check", 0);
    148 
    149     mConfig.periodic_chores_interval_disk_stats_publish =
    150         property_get_int32("ro.storaged.disk_stats_pub",
    151                            DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH);
    152 
    153     mConfig.periodic_chores_interval_uid_io =
    154         property_get_int32("ro.storaged.uid_io.interval",
    155                            DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO);
    156 
    157     mConfig.periodic_chores_interval_flush_proto =
    158         property_get_int32("ro.storaged.flush_proto.interval",
    159                            DEFAULT_PERIODIC_CHORES_INTERVAL_FLUSH_PROTO);
    160 
    161     mStarttime = time(NULL);
    162     mTimer = 0;
    163 }
    164 
    165 void storaged_t::add_user_ce(userid_t user_id) {
    166     load_proto(user_id);
    167     proto_loaded[user_id] = true;
    168 }
    169 
    170 void storaged_t::remove_user_ce(userid_t user_id) {
    171     proto_loaded[user_id] = false;
    172     mUidm.clear_user_history(user_id);
    173     RemoveFileIfExists(proto_path(user_id), nullptr);
    174 }
    175 
    176 void storaged_t::load_proto(userid_t user_id) {
    177     string proto_file = proto_path(user_id);
    178     ifstream in(proto_file, ofstream::in | ofstream::binary);
    179 
    180     if (!in.good()) return;
    181 
    182     stringstream ss;
    183     ss << in.rdbuf();
    184     StoragedProto proto;
    185     proto.ParseFromString(ss.str());
    186 
    187     const UidIOUsage& uid_io_usage = proto.uid_io_usage();
    188     uint32_t computed_crc = crc32(current_version,
    189         reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
    190         uid_io_usage.ByteSize());
    191     if (proto.crc() != computed_crc) {
    192         LOG_TO(SYSTEM, WARNING) << "CRC mismatch in " << proto_file;
    193         return;
    194     }
    195 
    196     mUidm.load_uid_io_proto(proto.uid_io_usage());
    197 
    198     if (user_id == USER_SYSTEM) {
    199         storage_info->load_perf_history_proto(proto.perf_history());
    200     }
    201 }
    202 
    203 char* storaged_t:: prepare_proto(userid_t user_id, StoragedProto* proto) {
    204     proto->set_version(current_version);
    205 
    206     const UidIOUsage& uid_io_usage = proto->uid_io_usage();
    207     proto->set_crc(crc32(current_version,
    208         reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
    209         uid_io_usage.ByteSize()));
    210 
    211     uint32_t pagesize = sysconf(_SC_PAGESIZE);
    212     if (user_id == USER_SYSTEM) {
    213         proto->set_padding("", 1);
    214         vector<char> padding;
    215         ssize_t size = ROUND_UP(MAX(min_benchmark_size, proto->ByteSize()),
    216                                 pagesize);
    217         padding = vector<char>(size - proto->ByteSize(), 0xFD);
    218         proto->set_padding(padding.data(), padding.size());
    219         while (!IS_ALIGNED(proto->ByteSize(), pagesize)) {
    220             padding.push_back(0xFD);
    221             proto->set_padding(padding.data(), padding.size());
    222         }
    223     }
    224 
    225     char* data = nullptr;
    226     if (posix_memalign(reinterpret_cast<void**>(&data),
    227                        pagesize, proto->ByteSize())) {
    228         PLOG_TO(SYSTEM, ERROR) << "Faied to alloc aligned buffer (size: "
    229                                << proto->ByteSize() << ")";
    230         return data;
    231     }
    232 
    233     proto->SerializeToArray(data, proto->ByteSize());
    234     return data;
    235 }
    236 
    237 void storaged_t::flush_proto_data(userid_t user_id,
    238                                   const char* data, ssize_t size) {
    239     string proto_file = proto_path(user_id);
    240     string tmp_file = proto_file + "_tmp";
    241     unique_fd fd(TEMP_FAILURE_RETRY(open(tmp_file.c_str(),
    242                  O_SYNC | O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC |
    243                     (user_id == USER_SYSTEM ? O_DIRECT : 0),
    244                  S_IRUSR | S_IWUSR)));
    245     if (fd == -1) {
    246         PLOG_TO(SYSTEM, ERROR) << "Faied to open tmp file: " << tmp_file;
    247         return;
    248     }
    249 
    250     if (user_id == USER_SYSTEM) {
    251         time_point<steady_clock> start, end;
    252         uint32_t benchmark_size = 0;
    253         uint64_t benchmark_time_ns = 0;
    254         ssize_t ret;
    255         bool first_write = true;
    256 
    257         while (size > 0) {
    258             start = steady_clock::now();
    259             ret = write(fd, data, MIN(benchmark_unit_size, size));
    260             if (ret <= 0) {
    261                 PLOG_TO(SYSTEM, ERROR) << "Faied to write tmp file: " << tmp_file;
    262                 return;
    263             }
    264             end = steady_clock::now();
    265             /*
    266             * compute bandwidth after the first write and if write returns
    267             * exactly unit size.
    268             */
    269             if (!first_write && ret == benchmark_unit_size) {
    270                 benchmark_size += benchmark_unit_size;
    271                 benchmark_time_ns += duration_cast<nanoseconds>(end - start).count();
    272             }
    273             size -= ret;
    274             data += ret;
    275             first_write = false;
    276         }
    277 
    278         if (benchmark_size) {
    279             int perf = benchmark_size * 1000000LLU / benchmark_time_ns;
    280             storage_info->update_perf_history(perf, system_clock::now());
    281         }
    282     } else {
    283         if (!WriteFully(fd, data, size)) {
    284             PLOG_TO(SYSTEM, ERROR) << "Faied to write tmp file: " << tmp_file;
    285             return;
    286         }
    287     }
    288 
    289     fd.reset(-1);
    290     rename(tmp_file.c_str(), proto_file.c_str());
    291 }
    292 
    293 void storaged_t::flush_proto(userid_t user_id, StoragedProto* proto) {
    294     unique_ptr<char> proto_data(prepare_proto(user_id, proto));
    295     if (proto_data == nullptr) return;
    296 
    297     flush_proto_data(user_id, proto_data.get(), proto->ByteSize());
    298 }
    299 
    300 void storaged_t::flush_protos(unordered_map<int, StoragedProto>* protos) {
    301     for (auto& it : *protos) {
    302         /*
    303          * Don't flush proto if we haven't attempted to load it from file.
    304          */
    305         if (proto_loaded[it.first]) {
    306             flush_proto(it.first, &it.second);
    307         }
    308     }
    309 }
    310 
    311 void storaged_t::event(void) {
    312     unordered_map<int, StoragedProto> protos;
    313 
    314     if (mDsm->enabled()) {
    315         mDsm->update();
    316         if (!(mTimer % mConfig.periodic_chores_interval_disk_stats_publish)) {
    317             mDsm->publish();
    318         }
    319     }
    320 
    321     if (!(mTimer % mConfig.periodic_chores_interval_uid_io)) {
    322         mUidm.report(&protos);
    323     }
    324 
    325     if (storage_info) {
    326         storage_info->refresh(protos[USER_SYSTEM].mutable_perf_history());
    327     }
    328 
    329     if (!(mTimer % mConfig.periodic_chores_interval_flush_proto)) {
    330         flush_protos(&protos);
    331     }
    332 
    333     mTimer += mConfig.periodic_chores_interval_unit;
    334 }
    335 
    336 void storaged_t::event_checked(void) {
    337     struct timespec start_ts, end_ts;
    338     bool check_time = true;
    339 
    340     if (mConfig.event_time_check_usec &&
    341         clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_ts) < 0) {
    342         check_time = false;
    343         static time_t state_a;
    344         IF_ALOG_RATELIMIT_LOCAL(300, &state_a) {
    345             PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
    346         }
    347     }
    348 
    349     event();
    350 
    351     if (mConfig.event_time_check_usec && check_time) {
    352         if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_ts) < 0) {
    353             static time_t state_b;
    354             IF_ALOG_RATELIMIT_LOCAL(300, &state_b) {
    355                 PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
    356             }
    357             return;
    358         }
    359         int64_t cost = (end_ts.tv_sec - start_ts.tv_sec) * SEC_TO_USEC +
    360                        (end_ts.tv_nsec - start_ts.tv_nsec) / USEC_TO_NSEC;
    361         if (cost > mConfig.event_time_check_usec) {
    362             LOG_TO(SYSTEM, ERROR)
    363                 << "event loop spent " << cost << " usec, threshold "
    364                 << mConfig.event_time_check_usec << " usec";
    365         }
    366     }
    367 }
    368