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 <android-base/file.h>
     24 #include <android-base/parseint.h>
     25 #include <android-base/logging.h>
     26 #include <android-base/strings.h>
     27 #include <log/log_event_list.h>
     28 
     29 #include "storaged.h"
     30 
     31 using namespace std;
     32 using namespace android::base;
     33 
     34 const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
     35 const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
     36 const char* emmc_info_t::emmc_ver_str[9] = {
     37     "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1"
     38 };
     39 
     40 const string ufs_info_t::health_file = "/sys/devices/soc/624000.ufshc/health";
     41 
     42 static bool FileExists(const std::string& filename)
     43 {
     44   struct stat buffer;
     45   return stat(filename.c_str(), &buffer) == 0;
     46 }
     47 
     48 storage_info_t* storage_info_t::get_storage_info()
     49 {
     50     if (FileExists(emmc_info_t::emmc_sysfs) ||
     51         FileExists(emmc_info_t::emmc_debugfs)) {
     52         return new emmc_info_t;
     53     }
     54     if (FileExists(ufs_info_t::health_file)) {
     55         return new ufs_info_t;
     56     }
     57     return new storage_info_t;
     58 }
     59 
     60 void storage_info_t::refresh()
     61 {
     62     struct statvfs buf;
     63     if (statvfs(userdata_path.c_str(), &buf) != 0) {
     64         PLOG_TO(SYSTEM, WARNING) << "Failed to get userdata info";
     65         return;
     66     }
     67 
     68     userdata_total_kb = buf.f_bsize * buf.f_blocks >> 10;
     69     userdata_free_kb = buf.f_bfree * buf.f_blocks >> 10;
     70 }
     71 
     72 void storage_info_t::publish()
     73 {
     74     android_log_event_list(EVENTLOGTAG_EMMCINFO)
     75         << version << eol << lifetime_a << lifetime_b
     76         << LOG_ID_EVENTS;
     77 }
     78 
     79 void emmc_info_t::report()
     80 {
     81     if (!report_sysfs() && !report_debugfs())
     82         return;
     83 
     84     publish();
     85 }
     86 
     87 bool emmc_info_t::report_sysfs()
     88 {
     89     string buffer;
     90     uint16_t rev = 0;
     91 
     92     if (!ReadFileToString(emmc_sysfs + "rev", &buffer)) {
     93         return false;
     94     }
     95 
     96     if (sscanf(buffer.c_str(), "0x%hx", &rev) < 1 ||
     97         rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
     98         return false;
     99     }
    100 
    101     version = "emmc ";
    102     version += emmc_ver_str[rev];
    103 
    104     if (!ReadFileToString(emmc_sysfs + "pre_eol_info", &buffer)) {
    105         return false;
    106     }
    107 
    108     if (sscanf(buffer.c_str(), "%hx", &eol) < 1 || eol == 0) {
    109         return false;
    110     }
    111 
    112     if (!ReadFileToString(emmc_sysfs + "life_time", &buffer)) {
    113         return false;
    114     }
    115 
    116     if (sscanf(buffer.c_str(), "0x%hx 0x%hx", &lifetime_a, &lifetime_b) < 2 ||
    117         (lifetime_a == 0 && lifetime_b == 0)) {
    118         return false;
    119     }
    120 
    121     return true;
    122 }
    123 
    124 const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
    125 /* 2 characters in string for each byte */
    126 const size_t EXT_CSD_REV_IDX = 192 * 2;
    127 const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2;
    128 const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
    129 const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
    130 
    131 bool emmc_info_t::report_debugfs()
    132 {
    133     string buffer;
    134     uint16_t rev = 0;
    135 
    136     if (!ReadFileToString(emmc_debugfs, &buffer) ||
    137         buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
    138         return false;
    139     }
    140 
    141     string str = buffer.substr(EXT_CSD_REV_IDX, 2);
    142     if (!ParseUint(str, &rev) ||
    143         rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
    144         return false;
    145     }
    146 
    147     version = "emmc ";
    148     version += emmc_ver_str[rev];
    149 
    150     str = buffer.substr(EXT_PRE_EOL_INFO_IDX, 2);
    151     if (!ParseUint(str, &eol)) {
    152         return false;
    153     }
    154 
    155     str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, 2);
    156     if (!ParseUint(str, &lifetime_a)) {
    157         return false;
    158     }
    159 
    160     str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, 2);
    161     if (!ParseUint(str, &lifetime_b)) {
    162         return false;
    163     }
    164 
    165     return true;
    166 }
    167 
    168 void ufs_info_t::report()
    169 {
    170     string buffer;
    171     if (!ReadFileToString(health_file, &buffer)) {
    172         return;
    173     }
    174 
    175     vector<string> lines = Split(buffer, "\n");
    176     if (lines.empty()) {
    177         return;
    178     }
    179 
    180     char rev[8];
    181     if (sscanf(lines[0].c_str(), "ufs version: 0x%7s\n", rev) < 1) {
    182         return;
    183     }
    184 
    185     version = "ufs " + string(rev);
    186 
    187     for (size_t i = 1; i < lines.size(); i++) {
    188         char token[32];
    189         uint16_t val;
    190         int ret;
    191         if ((ret = sscanf(lines[i].c_str(),
    192                    "Health Descriptor[Byte offset 0x%*d]: %31s = 0x%hx",
    193                    token, &val)) < 2) {
    194             continue;
    195         }
    196 
    197         if (string(token) == "bPreEOLInfo") {
    198             eol = val;
    199         } else if (string(token) == "bDeviceLifeTimeEstA") {
    200             lifetime_a = val;
    201         } else if (string(token) == "bDeviceLifeTimeEstB") {
    202             lifetime_b = val;
    203         }
    204     }
    205 
    206     if (eol == 0 || (lifetime_a == 0 && lifetime_b == 0)) {
    207         return;
    208     }
    209 
    210     publish();
    211 }
    212 
    213