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