• 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     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