blob: 5605f667cfd7807c28a810559077c5425ba8742e [file] [log] [blame]
Jin Qian4fc338e2017-03-15 19:03:06 -07001/*
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
Jin Qian726339c2017-03-17 16:36:59 -070019#include <stdio.h>
Jin Qian4fc338e2017-03-15 19:03:06 -070020#include <string.h>
Jin Qian8847c622017-07-17 15:06:11 -070021#include <sys/statvfs.h>
Jin Qian4fc338e2017-03-15 19:03:06 -070022
Jin Qiand691d6e2017-09-28 16:02:22 -070023#include <numeric>
24
Jin Qian4fc338e2017-03-15 19:03:06 -070025#include <android-base/file.h>
Jin Qian4fc338e2017-03-15 19:03:06 -070026#include <android-base/parseint.h>
Jin Qian726339c2017-03-17 16:36:59 -070027#include <android-base/logging.h>
Jin Qianb90f1ae2017-03-21 16:57:44 -070028#include <android-base/strings.h>
Jin Qian4fc338e2017-03-15 19:03:06 -070029#include <log/log_event_list.h>
30
31#include "storaged.h"
Jin Qiand691d6e2017-09-28 16:02:22 -070032#include "storaged_info.h"
Jin Qian4fc338e2017-03-15 19:03:06 -070033
34using namespace std;
Jin Qiand691d6e2017-09-28 16:02:22 -070035using namespace chrono;
Jin Qian4fc338e2017-03-15 19:03:06 -070036using namespace android::base;
Jin Qiand691d6e2017-09-28 16:02:22 -070037using namespace storaged_proto;
Jin Qian4fc338e2017-03-15 19:03:06 -070038
Yifan Hong845e35b2018-01-16 16:03:36 -080039using android::hardware::health::V2_0::IHealth;
40using android::hardware::health::V2_0::Result;
41using android::hardware::health::V2_0::StorageInfo;
42
Jin Qian8847c622017-07-17 15:06:11 -070043const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
44const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
45const 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};
Jin Qianb90f1ae2017-03-21 16:57:44 -070048
Jin Qian8847c622017-07-17 15:06:11 -070049const string ufs_info_t::health_file = "/sys/devices/soc/624000.ufshc/health";
50
Jin Qian65dea712017-08-29 16:48:20 -070051namespace {
52
53bool FileExists(const std::string& filename)
Jin Qian8847c622017-07-17 15:06:11 -070054{
55 struct stat buffer;
56 return stat(filename.c_str(), &buffer) == 0;
57}
58
Jin Qian65dea712017-08-29 16:48:20 -070059} // namespace
60
Yifan Hong845e35b2018-01-16 16:03:36 -080061storage_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 }
Jin Qian8847c622017-07-17 15:06:11 -070065 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
Jin Qiana8533322017-10-13 18:15:34 -070075void storage_info_t::load_perf_history_proto(const IOPerfHistory& perf_history)
Jin Qiand691d6e2017-09-28 16:02:22 -070076{
Jin Qian94b64ef2017-11-09 15:07:18 -080077 Mutex::Autolock _l(si_mutex);
78
Jin Qiand691d6e2017-09-28 16:02:22 -070079 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 = {};
Jin Qian6df3bc62017-10-18 17:52:14 -070087 day_start_tp += chrono::seconds(perf_history.day_start_sec());
Jin Qiand691d6e2017-09-28 16:02:22 -070088
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
107void storage_info_t::refresh(IOPerfHistory* perf_history)
Jin Qian8847c622017-07-17 15:06:11 -0700108{
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;
Jin Qiand691d6e2017-09-28 16:02:22 -0700117
Jin Qian6df3bc62017-10-18 17:52:14 -0700118 Mutex::Autolock _l(si_mutex);
Jin Qiand691d6e2017-09-28 16:02:22 -0700119
120 perf_history->Clear();
121 perf_history->set_day_start_sec(
Jin Qian6df3bc62017-10-18 17:52:14 -0700122 duration_cast<chrono::seconds>(day_start_tp.time_since_epoch()).count());
Jin Qiand691d6e2017-09-28 16:02:22 -0700123 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);
Jin Qian726339c2017-03-17 16:36:59 -0700135}
136
Jin Qian4fc338e2017-03-15 19:03:06 -0700137void storage_info_t::publish()
138{
Jin Qian4fc338e2017-03-15 19:03:06 -0700139 android_log_event_list(EVENTLOGTAG_EMMCINFO)
140 << version << eol << lifetime_a << lifetime_b
141 << LOG_ID_EVENTS;
142}
143
Jin Qiand691d6e2017-09-28 16:02:22 -0700144void storage_info_t::update_perf_history(uint32_t bw,
145 const time_point<system_clock>& tp)
146{
Jin Qian6df3bc62017-10-18 17:52:14 -0700147 Mutex::Autolock _l(si_mutex);
Jin Qiand691d6e2017-09-28 16:02:22 -0700148
149 if (tp > day_start_tp &&
Jin Qian6df3bc62017-10-18 17:52:14 -0700150 duration_cast<chrono::seconds>(tp - day_start_tp).count() < DAY_TO_SEC) {
Jin Qiand691d6e2017-09-28 16:02:22 -0700151 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
David Anderson7d74a5a2018-03-26 15:15:05 -0700160 if (nr_samples < recent_perf.size()) {
161 recent_perf.erase(recent_perf.begin() + nr_samples, recent_perf.end());
162 }
Jin Qiand691d6e2017-09-28 16:02:22 -0700163
David Anderson7d74a5a2018-03-26 15:15:05 -0700164 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 }
Jin Qiand691d6e2017-09-28 16:02:22 -0700168
Jin Qian6df3bc62017-10-18 17:52:14 -0700169 day_start_tp = tp - chrono::seconds(duration_cast<chrono::seconds>(
Jin Qiand691d6e2017-09-28 16:02:22 -0700170 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
David Anderson7d74a5a2018-03-26 15:15:05 -0700182 DCHECK(nr_days > 0);
Jin Qiand691d6e2017-09-28 16:02:22 -0700183 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
Jin Qianb049d182017-10-12 17:02:17 -0700195vector<int> storage_info_t::get_perf_history()
Jin Qiand691d6e2017-09-28 16:02:22 -0700196{
Jin Qian6df3bc62017-10-18 17:52:14 -0700197 Mutex::Autolock _l(si_mutex);
Jin Qiand691d6e2017-09-28 16:02:22 -0700198
Jin Qianb049d182017-10-12 17:02:17 -0700199 vector<int> ret(3 + recent_perf.size() + daily_perf.size() + weekly_perf.size());
Jin Qiand691d6e2017-09-28 16:02:22 -0700200
Jin Qianb049d182017-10-12 17:02:17 -0700201 ret[0] = recent_perf.size();
202 ret[1] = daily_perf.size();
203 ret[2] = weekly_perf.size();
204
205 int start = 3;
Jin Qiand691d6e2017-09-28 16:02:22 -0700206 for (size_t i = 0; i < recent_perf.size(); i++) {
207 int idx = (recent_perf.size() + nr_samples - 1 - i) % recent_perf.size();
Jin Qianb049d182017-10-12 17:02:17 -0700208 ret[start + i] = recent_perf[idx];
Jin Qiand691d6e2017-09-28 16:02:22 -0700209 }
210
Jin Qianb049d182017-10-12 17:02:17 -0700211 start += recent_perf.size();
Jin Qiand691d6e2017-09-28 16:02:22 -0700212 for (size_t i = 0; i < daily_perf.size(); i++) {
213 int idx = (daily_perf.size() + nr_days - 1 - i) % daily_perf.size();
Jin Qianb049d182017-10-12 17:02:17 -0700214 ret[start + i] = daily_perf[idx];
Jin Qiand691d6e2017-09-28 16:02:22 -0700215 }
216
Jin Qianb049d182017-10-12 17:02:17 -0700217 start += daily_perf.size();
Jin Qiand691d6e2017-09-28 16:02:22 -0700218 for (size_t i = 0; i < weekly_perf.size(); i++) {
219 int idx = (weekly_perf.size() + nr_weeks - 1 - i) % weekly_perf.size();
Jin Qianb049d182017-10-12 17:02:17 -0700220 ret[start + i] = weekly_perf[idx];
Jin Qiand691d6e2017-09-28 16:02:22 -0700221 }
222
223 return ret;
224}
225
Michael Wachenschwanz37b912b2017-12-14 18:20:26 -0800226uint32_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
Jin Qian8847c622017-07-17 15:06:11 -0700233void emmc_info_t::report()
Jin Qian726339c2017-03-17 16:36:59 -0700234{
235 if (!report_sysfs() && !report_debugfs())
Jin Qian8847c622017-07-17 15:06:11 -0700236 return;
Jin Qian726339c2017-03-17 16:36:59 -0700237
238 publish();
Jin Qian726339c2017-03-17 16:36:59 -0700239}
240
241bool emmc_info_t::report_sysfs()
Jin Qian4fc338e2017-03-15 19:03:06 -0700242{
243 string buffer;
Jin Qian726339c2017-03-17 16:36:59 -0700244 uint16_t rev = 0;
245
246 if (!ReadFileToString(emmc_sysfs + "rev", &buffer)) {
Jin Qian4fc338e2017-03-15 19:03:06 -0700247 return false;
248 }
249
Jin Qian726339c2017-03-17 16:36:59 -0700250 if (sscanf(buffer.c_str(), "0x%hx", &rev) < 1 ||
251 rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
Jin Qian4fc338e2017-03-15 19:03:06 -0700252 return false;
253 }
254
255 version = "emmc ";
Jin Qian726339c2017-03-17 16:36:59 -0700256 version += emmc_ver_str[rev];
Jin Qian4fc338e2017-03-15 19:03:06 -0700257
Jin Qian726339c2017-03-17 16:36:59 -0700258 if (!ReadFileToString(emmc_sysfs + "pre_eol_info", &buffer)) {
Jin Qian4fc338e2017-03-15 19:03:06 -0700259 return false;
260 }
261
Jin Qian726339c2017-03-17 16:36:59 -0700262 if (sscanf(buffer.c_str(), "%hx", &eol) < 1 || eol == 0) {
Jin Qian4fc338e2017-03-15 19:03:06 -0700263 return false;
264 }
265
Jin Qian726339c2017-03-17 16:36:59 -0700266 if (!ReadFileToString(emmc_sysfs + "life_time", &buffer)) {
Jin Qian4fc338e2017-03-15 19:03:06 -0700267 return false;
268 }
269
Jin Qian726339c2017-03-17 16:36:59 -0700270 if (sscanf(buffer.c_str(), "0x%hx 0x%hx", &lifetime_a, &lifetime_b) < 2 ||
271 (lifetime_a == 0 && lifetime_b == 0)) {
Jin Qian4fc338e2017-03-15 19:03:06 -0700272 return false;
273 }
274
275 return true;
276}
Jin Qian726339c2017-03-17 16:36:59 -0700277
Jin Qian65dea712017-08-29 16:48:20 -0700278namespace {
279
Jin Qian726339c2017-03-17 16:36:59 -0700280const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
281/* 2 characters in string for each byte */
282const size_t EXT_CSD_REV_IDX = 192 * 2;
283const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2;
284const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
285const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
286
Jin Qian65dea712017-08-29 16:48:20 -0700287} // namespace
288
Jin Qian726339c2017-03-17 16:36:59 -0700289bool 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;
Jin Qianb90f1ae2017-03-21 16:57:44 -0700324}
325
Jin Qian8847c622017-07-17 15:06:11 -0700326void ufs_info_t::report()
Jin Qianb90f1ae2017-03-21 16:57:44 -0700327{
328 string buffer;
329 if (!ReadFileToString(health_file, &buffer)) {
Jin Qian8847c622017-07-17 15:06:11 -0700330 return;
Jin Qianb90f1ae2017-03-21 16:57:44 -0700331 }
332
333 vector<string> lines = Split(buffer, "\n");
334 if (lines.empty()) {
Jin Qian8847c622017-07-17 15:06:11 -0700335 return;
Jin Qianb90f1ae2017-03-21 16:57:44 -0700336 }
337
338 char rev[8];
339 if (sscanf(lines[0].c_str(), "ufs version: 0x%7s\n", rev) < 1) {
Jin Qian8847c622017-07-17 15:06:11 -0700340 return;
Jin Qianb90f1ae2017-03-21 16:57:44 -0700341 }
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)) {
Jin Qian8847c622017-07-17 15:06:11 -0700365 return;
Jin Qianb90f1ae2017-03-21 16:57:44 -0700366 }
367
368 publish();
Jin Qianb90f1ae2017-03-21 16:57:44 -0700369}
370
Yifan Hong845e35b2018-01-16 16:03:36 -0800371void 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
387void 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}