• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     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 
refresh(IOPerfHistory * perf_history)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 
publish()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 
update_perf_history(uint32_t bw,const time_point<system_clock> & tp)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 
get_perf_history()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 
get_recent_perf()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 
report()233 void emmc_info_t::report()
234 {
235     if (!report_sysfs() && !report_debugfs())
236         return;
237 
238     publish();
239 }
240 
report_sysfs()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 
report_debugfs()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 
report()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 
report()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 
set_values_from_hal_storage_info(const StorageInfo & halInfo)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