blob: 036d7e1f93f95c64ea28c15fa1dd4540c50f2707 [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
Jin Qian8847c622017-07-17 15:06:11 -070039const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
40const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
41const char* emmc_info_t::emmc_ver_str[9] = {
42 "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1"
43};
Jin Qianb90f1ae2017-03-21 16:57:44 -070044
Jin Qian8847c622017-07-17 15:06:11 -070045const string ufs_info_t::health_file = "/sys/devices/soc/624000.ufshc/health";
46
Jin Qian65dea712017-08-29 16:48:20 -070047namespace {
48
49bool FileExists(const std::string& filename)
Jin Qian8847c622017-07-17 15:06:11 -070050{
51 struct stat buffer;
52 return stat(filename.c_str(), &buffer) == 0;
53}
54
Jin Qian65dea712017-08-29 16:48:20 -070055} // namespace
56
Jin Qian8847c622017-07-17 15:06:11 -070057storage_info_t* storage_info_t::get_storage_info()
58{
59 if (FileExists(emmc_info_t::emmc_sysfs) ||
60 FileExists(emmc_info_t::emmc_debugfs)) {
61 return new emmc_info_t;
62 }
63 if (FileExists(ufs_info_t::health_file)) {
64 return new ufs_info_t;
65 }
66 return new storage_info_t;
67}
68
Jin Qiana8533322017-10-13 18:15:34 -070069void storage_info_t::load_perf_history_proto(const IOPerfHistory& perf_history)
Jin Qiand691d6e2017-09-28 16:02:22 -070070{
Jin Qian94b64ef2017-11-09 15:07:18 -080071 Mutex::Autolock _l(si_mutex);
72
Jin Qiand691d6e2017-09-28 16:02:22 -070073 if (!perf_history.has_day_start_sec() ||
74 perf_history.daily_perf_size() > (int)daily_perf.size() ||
75 perf_history.weekly_perf_size() > (int)weekly_perf.size()) {
76 LOG_TO(SYSTEM, ERROR) << "Invalid IOPerfHistory proto";
77 return;
78 }
79
80 day_start_tp = {};
Jin Qian6df3bc62017-10-18 17:52:14 -070081 day_start_tp += chrono::seconds(perf_history.day_start_sec());
Jin Qiand691d6e2017-09-28 16:02:22 -070082
83 nr_samples = perf_history.nr_samples();
84 for (auto bw : perf_history.recent_perf()) {
85 recent_perf.push_back(bw);
86 }
87
88 nr_days = perf_history.nr_days();
89 int i = 0;
90 for (auto bw : perf_history.daily_perf()) {
91 daily_perf[i++] = bw;
92 }
93
94 nr_weeks = perf_history.nr_weeks();
95 i = 0;
96 for (auto bw : perf_history.weekly_perf()) {
97 weekly_perf[i++] = bw;
98 }
99}
100
101void storage_info_t::refresh(IOPerfHistory* perf_history)
Jin Qian8847c622017-07-17 15:06:11 -0700102{
103 struct statvfs buf;
104 if (statvfs(userdata_path.c_str(), &buf) != 0) {
105 PLOG_TO(SYSTEM, WARNING) << "Failed to get userdata info";
106 return;
107 }
108
109 userdata_total_kb = buf.f_bsize * buf.f_blocks >> 10;
110 userdata_free_kb = buf.f_bfree * buf.f_blocks >> 10;
Jin Qiand691d6e2017-09-28 16:02:22 -0700111
Jin Qian6df3bc62017-10-18 17:52:14 -0700112 Mutex::Autolock _l(si_mutex);
Jin Qiand691d6e2017-09-28 16:02:22 -0700113
114 perf_history->Clear();
115 perf_history->set_day_start_sec(
Jin Qian6df3bc62017-10-18 17:52:14 -0700116 duration_cast<chrono::seconds>(day_start_tp.time_since_epoch()).count());
Jin Qiand691d6e2017-09-28 16:02:22 -0700117 for (const uint32_t& bw : recent_perf) {
118 perf_history->add_recent_perf(bw);
119 }
120 perf_history->set_nr_samples(nr_samples);
121 for (const uint32_t& bw : daily_perf) {
122 perf_history->add_daily_perf(bw);
123 }
124 perf_history->set_nr_days(nr_days);
125 for (const uint32_t& bw : weekly_perf) {
126 perf_history->add_weekly_perf(bw);
127 }
128 perf_history->set_nr_weeks(nr_weeks);
Jin Qian726339c2017-03-17 16:36:59 -0700129}
130
Jin Qian4fc338e2017-03-15 19:03:06 -0700131void storage_info_t::publish()
132{
Jin Qian4fc338e2017-03-15 19:03:06 -0700133 android_log_event_list(EVENTLOGTAG_EMMCINFO)
134 << version << eol << lifetime_a << lifetime_b
135 << LOG_ID_EVENTS;
136}
137
Jin Qiand691d6e2017-09-28 16:02:22 -0700138void storage_info_t::update_perf_history(uint32_t bw,
139 const time_point<system_clock>& tp)
140{
Jin Qian6df3bc62017-10-18 17:52:14 -0700141 Mutex::Autolock _l(si_mutex);
Jin Qiand691d6e2017-09-28 16:02:22 -0700142
143 if (tp > day_start_tp &&
Jin Qian6df3bc62017-10-18 17:52:14 -0700144 duration_cast<chrono::seconds>(tp - day_start_tp).count() < DAY_TO_SEC) {
Jin Qiand691d6e2017-09-28 16:02:22 -0700145 if (nr_samples >= recent_perf.size()) {
146 recent_perf.push_back(bw);
147 } else {
148 recent_perf[nr_samples] = bw;
149 }
150 nr_samples++;
151 return;
152 }
153
154 recent_perf.erase(recent_perf.begin() + nr_samples,
155 recent_perf.end());
156
157 uint32_t daily_avg_bw = accumulate(recent_perf.begin(),
158 recent_perf.begin() + nr_samples, 0) / nr_samples;
159
Jin Qian6df3bc62017-10-18 17:52:14 -0700160 day_start_tp = tp - chrono::seconds(duration_cast<chrono::seconds>(
Jin Qiand691d6e2017-09-28 16:02:22 -0700161 tp.time_since_epoch()).count() % DAY_TO_SEC);
162
163 nr_samples = 0;
164 if (recent_perf.empty())
165 recent_perf.resize(1);
166 recent_perf[nr_samples++] = bw;
167
168 if (nr_days < WEEK_TO_DAYS) {
169 daily_perf[nr_days++] = daily_avg_bw;
170 return;
171 }
172
173 uint32_t week_avg_bw = accumulate(daily_perf.begin(),
174 daily_perf.begin() + nr_days, 0) / nr_days;
175
176 nr_days = 0;
177 daily_perf[nr_days++] = daily_avg_bw;
178
179 if (nr_weeks >= YEAR_TO_WEEKS) {
180 nr_weeks = 0;
181 }
182 weekly_perf[nr_weeks++] = week_avg_bw;
183}
184
Jin Qianb049d182017-10-12 17:02:17 -0700185vector<int> storage_info_t::get_perf_history()
Jin Qiand691d6e2017-09-28 16:02:22 -0700186{
Jin Qian6df3bc62017-10-18 17:52:14 -0700187 Mutex::Autolock _l(si_mutex);
Jin Qiand691d6e2017-09-28 16:02:22 -0700188
Jin Qianb049d182017-10-12 17:02:17 -0700189 vector<int> ret(3 + recent_perf.size() + daily_perf.size() + weekly_perf.size());
Jin Qiand691d6e2017-09-28 16:02:22 -0700190
Jin Qianb049d182017-10-12 17:02:17 -0700191 ret[0] = recent_perf.size();
192 ret[1] = daily_perf.size();
193 ret[2] = weekly_perf.size();
194
195 int start = 3;
Jin Qiand691d6e2017-09-28 16:02:22 -0700196 for (size_t i = 0; i < recent_perf.size(); i++) {
197 int idx = (recent_perf.size() + nr_samples - 1 - i) % recent_perf.size();
Jin Qianb049d182017-10-12 17:02:17 -0700198 ret[start + i] = recent_perf[idx];
Jin Qiand691d6e2017-09-28 16:02:22 -0700199 }
200
Jin Qianb049d182017-10-12 17:02:17 -0700201 start += recent_perf.size();
Jin Qiand691d6e2017-09-28 16:02:22 -0700202 for (size_t i = 0; i < daily_perf.size(); i++) {
203 int idx = (daily_perf.size() + nr_days - 1 - i) % daily_perf.size();
Jin Qianb049d182017-10-12 17:02:17 -0700204 ret[start + i] = daily_perf[idx];
Jin Qiand691d6e2017-09-28 16:02:22 -0700205 }
206
Jin Qianb049d182017-10-12 17:02:17 -0700207 start += daily_perf.size();
Jin Qiand691d6e2017-09-28 16:02:22 -0700208 for (size_t i = 0; i < weekly_perf.size(); i++) {
209 int idx = (weekly_perf.size() + nr_weeks - 1 - i) % weekly_perf.size();
Jin Qianb049d182017-10-12 17:02:17 -0700210 ret[start + i] = weekly_perf[idx];
Jin Qiand691d6e2017-09-28 16:02:22 -0700211 }
212
213 return ret;
214}
215
Jin Qian8847c622017-07-17 15:06:11 -0700216void emmc_info_t::report()
Jin Qian726339c2017-03-17 16:36:59 -0700217{
218 if (!report_sysfs() && !report_debugfs())
Jin Qian8847c622017-07-17 15:06:11 -0700219 return;
Jin Qian726339c2017-03-17 16:36:59 -0700220
221 publish();
Jin Qian726339c2017-03-17 16:36:59 -0700222}
223
224bool emmc_info_t::report_sysfs()
Jin Qian4fc338e2017-03-15 19:03:06 -0700225{
226 string buffer;
Jin Qian726339c2017-03-17 16:36:59 -0700227 uint16_t rev = 0;
228
229 if (!ReadFileToString(emmc_sysfs + "rev", &buffer)) {
Jin Qian4fc338e2017-03-15 19:03:06 -0700230 return false;
231 }
232
Jin Qian726339c2017-03-17 16:36:59 -0700233 if (sscanf(buffer.c_str(), "0x%hx", &rev) < 1 ||
234 rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
Jin Qian4fc338e2017-03-15 19:03:06 -0700235 return false;
236 }
237
238 version = "emmc ";
Jin Qian726339c2017-03-17 16:36:59 -0700239 version += emmc_ver_str[rev];
Jin Qian4fc338e2017-03-15 19:03:06 -0700240
Jin Qian726339c2017-03-17 16:36:59 -0700241 if (!ReadFileToString(emmc_sysfs + "pre_eol_info", &buffer)) {
Jin Qian4fc338e2017-03-15 19:03:06 -0700242 return false;
243 }
244
Jin Qian726339c2017-03-17 16:36:59 -0700245 if (sscanf(buffer.c_str(), "%hx", &eol) < 1 || eol == 0) {
Jin Qian4fc338e2017-03-15 19:03:06 -0700246 return false;
247 }
248
Jin Qian726339c2017-03-17 16:36:59 -0700249 if (!ReadFileToString(emmc_sysfs + "life_time", &buffer)) {
Jin Qian4fc338e2017-03-15 19:03:06 -0700250 return false;
251 }
252
Jin Qian726339c2017-03-17 16:36:59 -0700253 if (sscanf(buffer.c_str(), "0x%hx 0x%hx", &lifetime_a, &lifetime_b) < 2 ||
254 (lifetime_a == 0 && lifetime_b == 0)) {
Jin Qian4fc338e2017-03-15 19:03:06 -0700255 return false;
256 }
257
258 return true;
259}
Jin Qian726339c2017-03-17 16:36:59 -0700260
Jin Qian65dea712017-08-29 16:48:20 -0700261namespace {
262
Jin Qian726339c2017-03-17 16:36:59 -0700263const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
264/* 2 characters in string for each byte */
265const size_t EXT_CSD_REV_IDX = 192 * 2;
266const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2;
267const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
268const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
269
Jin Qian65dea712017-08-29 16:48:20 -0700270} // namespace
271
Jin Qian726339c2017-03-17 16:36:59 -0700272bool emmc_info_t::report_debugfs()
273{
274 string buffer;
275 uint16_t rev = 0;
276
277 if (!ReadFileToString(emmc_debugfs, &buffer) ||
278 buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
279 return false;
280 }
281
282 string str = buffer.substr(EXT_CSD_REV_IDX, 2);
283 if (!ParseUint(str, &rev) ||
284 rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
285 return false;
286 }
287
288 version = "emmc ";
289 version += emmc_ver_str[rev];
290
291 str = buffer.substr(EXT_PRE_EOL_INFO_IDX, 2);
292 if (!ParseUint(str, &eol)) {
293 return false;
294 }
295
296 str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, 2);
297 if (!ParseUint(str, &lifetime_a)) {
298 return false;
299 }
300
301 str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, 2);
302 if (!ParseUint(str, &lifetime_b)) {
303 return false;
304 }
305
306 return true;
Jin Qianb90f1ae2017-03-21 16:57:44 -0700307}
308
Jin Qian8847c622017-07-17 15:06:11 -0700309void ufs_info_t::report()
Jin Qianb90f1ae2017-03-21 16:57:44 -0700310{
311 string buffer;
312 if (!ReadFileToString(health_file, &buffer)) {
Jin Qian8847c622017-07-17 15:06:11 -0700313 return;
Jin Qianb90f1ae2017-03-21 16:57:44 -0700314 }
315
316 vector<string> lines = Split(buffer, "\n");
317 if (lines.empty()) {
Jin Qian8847c622017-07-17 15:06:11 -0700318 return;
Jin Qianb90f1ae2017-03-21 16:57:44 -0700319 }
320
321 char rev[8];
322 if (sscanf(lines[0].c_str(), "ufs version: 0x%7s\n", rev) < 1) {
Jin Qian8847c622017-07-17 15:06:11 -0700323 return;
Jin Qianb90f1ae2017-03-21 16:57:44 -0700324 }
325
326 version = "ufs " + string(rev);
327
328 for (size_t i = 1; i < lines.size(); i++) {
329 char token[32];
330 uint16_t val;
331 int ret;
332 if ((ret = sscanf(lines[i].c_str(),
333 "Health Descriptor[Byte offset 0x%*d]: %31s = 0x%hx",
334 token, &val)) < 2) {
335 continue;
336 }
337
338 if (string(token) == "bPreEOLInfo") {
339 eol = val;
340 } else if (string(token) == "bDeviceLifeTimeEstA") {
341 lifetime_a = val;
342 } else if (string(token) == "bDeviceLifeTimeEstB") {
343 lifetime_b = val;
344 }
345 }
346
347 if (eol == 0 || (lifetime_a == 0 && lifetime_b == 0)) {
Jin Qian8847c622017-07-17 15:06:11 -0700348 return;
Jin Qianb90f1ae2017-03-21 16:57:44 -0700349 }
350
351 publish();
Jin Qianb90f1ae2017-03-21 16:57:44 -0700352}
353