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 <stdio.h>
     20 #include <string.h>
     21 #include <sys/statvfs.h>
     22 
     23 #include <numeric>
     24 
     25 #include <android-base/file.h>
     26 #include <android-base/parseint.h>
     27 #include <android-base/logging.h>
     28 #include <android-base/strings.h>
     29 #include <log/log_event_list.h>
     30 
     31 #include "storaged.h"
     32 #include "storaged_info.h"
     33 
     34 using namespace std;
     35 using namespace chrono;
     36 using namespace android::base;
     37 using namespace storaged_proto;
     38 
     39 using android::hardware::health::V2_0::IHealth;
     40 using android::hardware::health::V2_0::Result;
     41 using android::hardware::health::V2_0::StorageInfo;
     42 
     43 const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
     44 const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
     45 const char* emmc_info_t::emmc_ver_str[9] = {
     46     "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1"
     47 };
     48 
     49 const string ufs_info_t::health_file = "/sys/devices/soc/624000.ufshc/health";
     50 
     51 namespace {
     52 
     53 bool FileExists(const std::string& filename)
     54 {
     55   struct stat buffer;
     56   return stat(filename.c_str(), &buffer) == 0;
     57 }
     58 
     59 } // namespace
     60 
     61 storage_info_t* storage_info_t::get_storage_info(const sp<IHealth>& healthService) {
     62     if (healthService != nullptr) {
     63         return new health_storage_info_t(healthService);
     64     }
     65     if (FileExists(emmc_info_t::emmc_sysfs) ||
     66         FileExists(emmc_info_t::emmc_debugfs)) {
     67         return new emmc_info_t;
     68     }
     69     if (FileExists(ufs_info_t::health_file)) {
     70         return new ufs_info_t;
     71     }
     72     return new storage_info_t;
     73 }
     74 
     75 void storage_info_t::load_perf_history_proto(const IOPerfHistory& perf_history)
     76 {
     77     Mutex::Autolock _l(si_mutex);
     78 
     79     if (!perf_history.has_day_start_sec() ||
     80         perf_history.daily_perf_size() > (int)daily_perf.size() ||
     81         perf_history.weekly_perf_size() > (int)weekly_perf.size()) {
     82         LOG_TO(SYSTEM, ERROR) << "Invalid IOPerfHistory proto";
     83         return;
     84     }
     85 
     86     day_start_tp = {};
     87     day_start_tp += chrono::seconds(perf_history.day_start_sec());
     88 
     89     nr_samples = perf_history.nr_samples();
     90     for (auto bw : perf_history.recent_perf()) {
     91         recent_perf.push_back(bw);
     92     }
     93 
     94     nr_days = perf_history.nr_days();
     95     int i = 0;
     96     for (auto bw : perf_history.daily_perf()) {
     97         daily_perf[i++] = bw;
     98     }
     99 
    100     nr_weeks = perf_history.nr_weeks();
    101     i = 0;
    102     for (auto bw : perf_history.weekly_perf()) {
    103         weekly_perf[i++] = bw;
    104     }
    105 }
    106 
    107 void storage_info_t::refresh(IOPerfHistory* perf_history)
    108 {
    109     struct statvfs buf;
    110     if (statvfs(userdata_path.c_str(), &buf) != 0) {
    111         PLOG_TO(SYSTEM, WARNING) << "Failed to get userdata info";
    112         return;
    113     }
    114 
    115     userdata_total_kb = buf.f_bsize * buf.f_blocks >> 10;
    116     userdata_free_kb = buf.f_bfree * buf.f_blocks >> 10;
    117 
    118     Mutex::Autolock _l(si_mutex);
    119 
    120     perf_history->Clear();
    121     perf_history->set_day_start_sec(
    122         duration_cast<chrono::seconds>(day_start_tp.time_since_epoch()).count());
    123     for (const uint32_t& bw : recent_perf) {
    124         perf_history->add_recent_perf(bw);
    125     }
    126     perf_history->set_nr_samples(nr_samples);
    127     for (const uint32_t& bw : daily_perf) {
    128         perf_history->add_daily_perf(bw);
    129     }
    130     perf_history->set_nr_days(nr_days);
    131     for (const uint32_t& bw : weekly_perf) {
    132         perf_history->add_weekly_perf(bw);
    133     }
    134     perf_history->set_nr_weeks(nr_weeks);
    135 }
    136 
    137 void storage_info_t::publish()
    138 {
    139     android_log_event_list(EVENTLOGTAG_EMMCINFO)
    140         << version << eol << lifetime_a << lifetime_b
    141         << LOG_ID_EVENTS;
    142 }
    143 
    144 void storage_info_t::update_perf_history(uint32_t bw,
    145                                          const time_point<system_clock>& tp)
    146 {
    147     Mutex::Autolock _l(si_mutex);
    148 
    149     if (tp > day_start_tp &&
    150         duration_cast<chrono::seconds>(tp - day_start_tp).count() < DAY_TO_SEC) {
    151         if (nr_samples >= recent_perf.size()) {
    152             recent_perf.push_back(bw);
    153         } else {
    154             recent_perf[nr_samples] = bw;
    155         }
    156         nr_samples++;
    157         return;
    158     }
    159 
    160     if (nr_samples < recent_perf.size()) {
    161         recent_perf.erase(recent_perf.begin() + nr_samples, recent_perf.end());
    162     }
    163 
    164     uint32_t daily_avg_bw = 0;
    165     if (!recent_perf.empty()) {
    166         daily_avg_bw = accumulate(recent_perf.begin(), recent_perf.end(), 0) / recent_perf.size();
    167     }
    168 
    169     day_start_tp = tp - chrono::seconds(duration_cast<chrono::seconds>(
    170         tp.time_since_epoch()).count() % DAY_TO_SEC);
    171 
    172     nr_samples = 0;
    173     if (recent_perf.empty())
    174         recent_perf.resize(1);
    175     recent_perf[nr_samples++] = bw;
    176 
    177     if (nr_days < WEEK_TO_DAYS) {
    178         daily_perf[nr_days++] = daily_avg_bw;
    179         return;
    180     }
    181 
    182     DCHECK(nr_days > 0);
    183     uint32_t week_avg_bw = accumulate(daily_perf.begin(),
    184         daily_perf.begin() + nr_days, 0) / nr_days;
    185 
    186     nr_days = 0;
    187     daily_perf[nr_days++] = daily_avg_bw;
    188 
    189     if (nr_weeks >= YEAR_TO_WEEKS) {
    190         nr_weeks = 0;
    191     }
    192     weekly_perf[nr_weeks++] = week_avg_bw;
    193 }
    194 
    195 vector<int> storage_info_t::get_perf_history()
    196 {
    197     Mutex::Autolock _l(si_mutex);
    198 
    199     vector<int> ret(3 + recent_perf.size() + daily_perf.size() + weekly_perf.size());
    200 
    201     ret[0] = recent_perf.size();
    202     ret[1] = daily_perf.size();
    203     ret[2] = weekly_perf.size();
    204 
    205     int start = 3;
    206     for (size_t i = 0; i < recent_perf.size(); i++) {
    207         int idx = (recent_perf.size() + nr_samples - 1 - i) % recent_perf.size();
    208         ret[start + i] = recent_perf[idx];
    209     }
    210 
    211     start += recent_perf.size();
    212     for (size_t i = 0; i < daily_perf.size(); i++) {
    213         int idx = (daily_perf.size() + nr_days - 1 - i) % daily_perf.size();
    214         ret[start + i] = daily_perf[idx];
    215     }
    216 
    217     start += daily_perf.size();
    218     for (size_t i = 0; i < weekly_perf.size(); i++) {
    219         int idx = (weekly_perf.size() + nr_weeks - 1 - i) % weekly_perf.size();
    220         ret[start + i] = weekly_perf[idx];
    221     }
    222 
    223     return ret;
    224 }
    225 
    226 uint32_t storage_info_t::get_recent_perf() {
    227     Mutex::Autolock _l(si_mutex);
    228     if (recent_perf.size() == 0) return 0;
    229     return accumulate(recent_perf.begin(), recent_perf.end(), recent_perf.size() / 2) /
    230            recent_perf.size();
    231 }
    232 
    233 void emmc_info_t::report()
    234 {
    235     if (!report_sysfs() && !report_debugfs())
    236         return;
    237 
    238     publish();
    239 }
    240 
    241 bool emmc_info_t::report_sysfs()
    242 {
    243     string buffer;
    244     uint16_t rev = 0;
    245 
    246     if (!ReadFileToString(emmc_sysfs + "rev", &buffer)) {
    247         return false;
    248     }
    249 
    250     if (sscanf(buffer.c_str(), "0x%hx", &rev) < 1 ||
    251         rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
    252         return false;
    253     }
    254 
    255     version = "emmc ";
    256     version += emmc_ver_str[rev];
    257 
    258     if (!ReadFileToString(emmc_sysfs + "pre_eol_info", &buffer)) {
    259         return false;
    260     }
    261 
    262     if (sscanf(buffer.c_str(), "%hx", &eol) < 1 || eol == 0) {
    263         return false;
    264     }
    265 
    266     if (!ReadFileToString(emmc_sysfs + "life_time", &buffer)) {
    267         return false;
    268     }
    269 
    270     if (sscanf(buffer.c_str(), "0x%hx 0x%hx", &lifetime_a, &lifetime_b) < 2 ||
    271         (lifetime_a == 0 && lifetime_b == 0)) {
    272         return false;
    273     }
    274 
    275     return true;
    276 }
    277 
    278 namespace {
    279 
    280 const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
    281 /* 2 characters in string for each byte */
    282 const size_t EXT_CSD_REV_IDX = 192 * 2;
    283 const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2;
    284 const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
    285 const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
    286 
    287 } // namespace
    288 
    289 bool emmc_info_t::report_debugfs()
    290 {
    291     string buffer;
    292     uint16_t rev = 0;
    293 
    294     if (!ReadFileToString(emmc_debugfs, &buffer) ||
    295         buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
    296         return false;
    297     }
    298 
    299     string str = buffer.substr(EXT_CSD_REV_IDX, 2);
    300     if (!ParseUint(str, &rev) ||
    301         rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
    302         return false;
    303     }
    304 
    305     version = "emmc ";
    306     version += emmc_ver_str[rev];
    307 
    308     str = buffer.substr(EXT_PRE_EOL_INFO_IDX, 2);
    309     if (!ParseUint(str, &eol)) {
    310         return false;
    311     }
    312 
    313     str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, 2);
    314     if (!ParseUint(str, &lifetime_a)) {
    315         return false;
    316     }
    317 
    318     str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, 2);
    319     if (!ParseUint(str, &lifetime_b)) {
    320         return false;
    321     }
    322 
    323     return true;
    324 }
    325 
    326 void ufs_info_t::report()
    327 {
    328     string buffer;
    329     if (!ReadFileToString(health_file, &buffer)) {
    330         return;
    331     }
    332 
    333     vector<string> lines = Split(buffer, "\n");
    334     if (lines.empty()) {
    335         return;
    336     }
    337 
    338     char rev[8];
    339     if (sscanf(lines[0].c_str(), "ufs version: 0x%7s\n", rev) < 1) {
    340         return;
    341     }
    342 
    343     version = "ufs " + string(rev);
    344 
    345     for (size_t i = 1; i < lines.size(); i++) {
    346         char token[32];
    347         uint16_t val;
    348         int ret;
    349         if ((ret = sscanf(lines[i].c_str(),
    350                    "Health Descriptor[Byte offset 0x%*d]: %31s = 0x%hx",
    351                    token, &val)) < 2) {
    352             continue;
    353         }
    354 
    355         if (string(token) == "bPreEOLInfo") {
    356             eol = val;
    357         } else if (string(token) == "bDeviceLifeTimeEstA") {
    358             lifetime_a = val;
    359         } else if (string(token) == "bDeviceLifeTimeEstB") {
    360             lifetime_b = val;
    361         }
    362     }
    363 
    364     if (eol == 0 || (lifetime_a == 0 && lifetime_b == 0)) {
    365         return;
    366     }
    367 
    368     publish();
    369 }
    370 
    371 void health_storage_info_t::report() {
    372     auto ret = mHealth->getStorageInfo([this](auto result, const auto& halInfos) {
    373         if (result != Result::SUCCESS || halInfos.size() == 0) {
    374             LOG_TO(SYSTEM, DEBUG) << "getStorageInfo failed with result " << toString(result)
    375                                   << " and size " << halInfos.size();
    376             return;
    377         }
    378         set_values_from_hal_storage_info(halInfos[0]);
    379         publish();
    380     });
    381 
    382     if (!ret.isOk()) {
    383         LOG_TO(SYSTEM, DEBUG) << "getStorageInfo failed with " << ret.description();
    384     }
    385 }
    386 
    387 void health_storage_info_t::set_values_from_hal_storage_info(const StorageInfo& halInfo) {
    388     eol = halInfo.eol;
    389     lifetime_a = halInfo.lifetimeA;
    390     lifetime_b = halInfo.lifetimeB;
    391     version = halInfo.version;
    392 }
    393