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
FileExists(const std::string & filename)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
get_storage_info(const sp<IHealth> & healthService)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
load_perf_history_proto(const IOPerfHistory & perf_history)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 if (nr_samples < recent_perf.size()) {
91 recent_perf.erase(recent_perf.begin() + nr_samples, recent_perf.end());
92 }
93 size_t i = 0;
94 for (auto bw : perf_history.recent_perf()) {
95 if (i < recent_perf.size()) {
96 recent_perf[i] = bw;
97 } else {
98 recent_perf.push_back(bw);
99 }
100 ++i;
101 }
102
103 nr_days = perf_history.nr_days();
104 i = 0;
105 for (auto bw : perf_history.daily_perf()) {
106 daily_perf[i++] = bw;
107 }
108
109 nr_weeks = perf_history.nr_weeks();
110 i = 0;
111 for (auto bw : perf_history.weekly_perf()) {
112 weekly_perf[i++] = bw;
113 }
114 }
115
refresh(IOPerfHistory * perf_history)116 void storage_info_t::refresh(IOPerfHistory* perf_history)
117 {
118 struct statvfs buf;
119 if (statvfs(userdata_path.c_str(), &buf) != 0) {
120 PLOG_TO(SYSTEM, WARNING) << "Failed to get userdata info";
121 return;
122 }
123
124 userdata_total_kb = buf.f_bsize * buf.f_blocks >> 10;
125 userdata_free_kb = buf.f_bfree * buf.f_blocks >> 10;
126
127 Mutex::Autolock _l(si_mutex);
128
129 perf_history->Clear();
130 perf_history->set_day_start_sec(
131 duration_cast<chrono::seconds>(day_start_tp.time_since_epoch()).count());
132 for (const uint32_t& bw : recent_perf) {
133 perf_history->add_recent_perf(bw);
134 }
135 perf_history->set_nr_samples(nr_samples);
136 for (const uint32_t& bw : daily_perf) {
137 perf_history->add_daily_perf(bw);
138 }
139 perf_history->set_nr_days(nr_days);
140 for (const uint32_t& bw : weekly_perf) {
141 perf_history->add_weekly_perf(bw);
142 }
143 perf_history->set_nr_weeks(nr_weeks);
144 }
145
publish()146 void storage_info_t::publish()
147 {
148 android_log_event_list(EVENTLOGTAG_EMMCINFO)
149 << version << eol << lifetime_a << lifetime_b
150 << LOG_ID_EVENTS;
151 }
152
update_perf_history(uint32_t bw,const time_point<system_clock> & tp)153 void storage_info_t::update_perf_history(uint32_t bw,
154 const time_point<system_clock>& tp)
155 {
156 Mutex::Autolock _l(si_mutex);
157
158 if (tp > day_start_tp &&
159 duration_cast<chrono::seconds>(tp - day_start_tp).count() < DAY_TO_SEC) {
160 if (nr_samples >= recent_perf.size()) {
161 recent_perf.push_back(bw);
162 } else {
163 recent_perf[nr_samples] = bw;
164 }
165 nr_samples++;
166 return;
167 }
168
169 if (nr_samples < recent_perf.size()) {
170 recent_perf.erase(recent_perf.begin() + nr_samples, recent_perf.end());
171 }
172
173 uint32_t daily_avg_bw = 0;
174 if (!recent_perf.empty()) {
175 daily_avg_bw = accumulate(recent_perf.begin(), recent_perf.end(), 0) / recent_perf.size();
176 }
177
178 day_start_tp = tp - chrono::seconds(duration_cast<chrono::seconds>(
179 tp.time_since_epoch()).count() % DAY_TO_SEC);
180
181 nr_samples = 0;
182 if (recent_perf.empty())
183 recent_perf.resize(1);
184 recent_perf[nr_samples++] = bw;
185
186 if (nr_days < WEEK_TO_DAYS) {
187 daily_perf[nr_days++] = daily_avg_bw;
188 return;
189 }
190
191 DCHECK(nr_days > 0);
192 uint32_t week_avg_bw = accumulate(daily_perf.begin(),
193 daily_perf.begin() + nr_days, 0) / nr_days;
194
195 nr_days = 0;
196 daily_perf[nr_days++] = daily_avg_bw;
197
198 if (nr_weeks >= YEAR_TO_WEEKS) {
199 nr_weeks = 0;
200 }
201 weekly_perf[nr_weeks++] = week_avg_bw;
202 }
203
get_perf_history()204 vector<int> storage_info_t::get_perf_history()
205 {
206 Mutex::Autolock _l(si_mutex);
207
208 vector<int> ret(3 + recent_perf.size() + daily_perf.size() + weekly_perf.size());
209
210 ret[0] = recent_perf.size();
211 ret[1] = daily_perf.size();
212 ret[2] = weekly_perf.size();
213
214 int start = 3;
215 for (size_t i = 0; i < recent_perf.size(); i++) {
216 int idx = (recent_perf.size() + nr_samples - 1 - i) % recent_perf.size();
217 ret[start + i] = recent_perf[idx];
218 }
219
220 start += recent_perf.size();
221 for (size_t i = 0; i < daily_perf.size(); i++) {
222 int idx = (daily_perf.size() + nr_days - 1 - i) % daily_perf.size();
223 ret[start + i] = daily_perf[idx];
224 }
225
226 start += daily_perf.size();
227 for (size_t i = 0; i < weekly_perf.size(); i++) {
228 int idx = (weekly_perf.size() + nr_weeks - 1 - i) % weekly_perf.size();
229 ret[start + i] = weekly_perf[idx];
230 }
231
232 return ret;
233 }
234
get_recent_perf()235 uint32_t storage_info_t::get_recent_perf() {
236 Mutex::Autolock _l(si_mutex);
237 if (recent_perf.size() == 0) return 0;
238 return accumulate(recent_perf.begin(), recent_perf.end(), recent_perf.size() / 2) /
239 recent_perf.size();
240 }
241
report()242 void emmc_info_t::report()
243 {
244 if (!report_sysfs() && !report_debugfs())
245 return;
246
247 publish();
248 }
249
report_sysfs()250 bool emmc_info_t::report_sysfs()
251 {
252 string buffer;
253 uint16_t rev = 0;
254
255 if (!ReadFileToString(emmc_sysfs + "rev", &buffer)) {
256 return false;
257 }
258
259 if (sscanf(buffer.c_str(), "0x%hx", &rev) < 1 ||
260 rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
261 return false;
262 }
263
264 version = "emmc ";
265 version += emmc_ver_str[rev];
266
267 if (!ReadFileToString(emmc_sysfs + "pre_eol_info", &buffer)) {
268 return false;
269 }
270
271 if (sscanf(buffer.c_str(), "%hx", &eol) < 1 || eol == 0) {
272 return false;
273 }
274
275 if (!ReadFileToString(emmc_sysfs + "life_time", &buffer)) {
276 return false;
277 }
278
279 if (sscanf(buffer.c_str(), "0x%hx 0x%hx", &lifetime_a, &lifetime_b) < 2 ||
280 (lifetime_a == 0 && lifetime_b == 0)) {
281 return false;
282 }
283
284 return true;
285 }
286
287 namespace {
288
289 const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
290 /* 2 characters in string for each byte */
291 const size_t EXT_CSD_REV_IDX = 192 * 2;
292 const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2;
293 const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
294 const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
295
296 } // namespace
297
report_debugfs()298 bool emmc_info_t::report_debugfs()
299 {
300 string buffer;
301 uint16_t rev = 0;
302
303 if (!ReadFileToString(emmc_debugfs, &buffer) ||
304 buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
305 return false;
306 }
307
308 string str = buffer.substr(EXT_CSD_REV_IDX, 2);
309 if (!ParseUint(str, &rev) ||
310 rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
311 return false;
312 }
313
314 version = "emmc ";
315 version += emmc_ver_str[rev];
316
317 str = buffer.substr(EXT_PRE_EOL_INFO_IDX, 2);
318 if (!ParseUint(str, &eol)) {
319 return false;
320 }
321
322 str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, 2);
323 if (!ParseUint(str, &lifetime_a)) {
324 return false;
325 }
326
327 str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, 2);
328 if (!ParseUint(str, &lifetime_b)) {
329 return false;
330 }
331
332 return true;
333 }
334
report()335 void ufs_info_t::report()
336 {
337 string buffer;
338 if (!ReadFileToString(health_file, &buffer)) {
339 return;
340 }
341
342 vector<string> lines = Split(buffer, "\n");
343 if (lines.empty()) {
344 return;
345 }
346
347 char rev[8];
348 if (sscanf(lines[0].c_str(), "ufs version: 0x%7s\n", rev) < 1) {
349 return;
350 }
351
352 version = "ufs " + string(rev);
353
354 for (size_t i = 1; i < lines.size(); i++) {
355 char token[32];
356 uint16_t val;
357 int ret;
358 if ((ret = sscanf(lines[i].c_str(),
359 "Health Descriptor[Byte offset 0x%*d]: %31s = 0x%hx",
360 token, &val)) < 2) {
361 continue;
362 }
363
364 if (string(token) == "bPreEOLInfo") {
365 eol = val;
366 } else if (string(token) == "bDeviceLifeTimeEstA") {
367 lifetime_a = val;
368 } else if (string(token) == "bDeviceLifeTimeEstB") {
369 lifetime_b = val;
370 }
371 }
372
373 if (eol == 0 || (lifetime_a == 0 && lifetime_b == 0)) {
374 return;
375 }
376
377 publish();
378 }
379
report()380 void health_storage_info_t::report() {
381 auto ret = mHealth->getStorageInfo([this](auto result, const auto& halInfos) {
382 if (result == Result::NOT_SUPPORTED) {
383 LOG_TO(SYSTEM, DEBUG) << "getStorageInfo is not supported on health HAL.";
384 return;
385 }
386 if (result != Result::SUCCESS || halInfos.size() == 0) {
387 LOG_TO(SYSTEM, ERROR) << "getStorageInfo failed with result " << toString(result)
388 << " and size " << halInfos.size();
389 return;
390 }
391 set_values_from_hal_storage_info(halInfos[0]);
392 publish();
393 });
394
395 if (!ret.isOk()) {
396 LOG_TO(SYSTEM, ERROR) << "getStorageInfo failed with " << ret.description();
397 }
398 }
399
set_values_from_hal_storage_info(const StorageInfo & halInfo)400 void health_storage_info_t::set_values_from_hal_storage_info(const StorageInfo& halInfo) {
401 eol = halInfo.eol;
402 lifetime_a = halInfo.lifetimeA;
403 lifetime_b = halInfo.lifetimeB;
404 version = halInfo.version;
405 }
406