• 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 aidl::android::hardware::health::IHealth;
40 using aidl::android::hardware::health::StorageInfo;
41 
42 const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
43 const char* emmc_info_t::emmc_ver_str[9] = {
44     "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1"
45 };
46 
47 const string ufs_info_t::health_file = "/sys/devices/soc/624000.ufshc/health";
48 
49 namespace {
50 
FileExists(const std::string & filename)51 bool FileExists(const std::string& filename)
52 {
53   struct stat buffer;
54   return stat(filename.c_str(), &buffer) == 0;
55 }
56 
57 } // namespace
58 
get_storage_info(const shared_ptr<IHealth> & healthService)59 storage_info_t* storage_info_t::get_storage_info(const shared_ptr<IHealth>& healthService) {
60     if (healthService != nullptr) {
61         return new health_storage_info_t(healthService);
62     }
63     if (FileExists(emmc_info_t::emmc_sysfs)) return new emmc_info_t;
64 
65     if (FileExists(ufs_info_t::health_file)) {
66         return new ufs_info_t;
67     }
68     return new storage_info_t;
69 }
70 
load_perf_history_proto(const IOPerfHistory & perf_history)71 void storage_info_t::load_perf_history_proto(const IOPerfHistory& perf_history)
72 {
73     Mutex::Autolock _l(si_mutex);
74 
75     if (!perf_history.has_day_start_sec() ||
76         perf_history.daily_perf_size() > (int)daily_perf.size() ||
77         perf_history.weekly_perf_size() > (int)weekly_perf.size()) {
78         LOG(ERROR) << "Invalid IOPerfHistory proto";
79         return;
80     }
81 
82     day_start_tp = {};
83     day_start_tp += chrono::seconds(perf_history.day_start_sec());
84 
85     nr_samples = perf_history.nr_samples();
86     if (nr_samples < recent_perf.size()) {
87         recent_perf.erase(recent_perf.begin() + nr_samples, recent_perf.end());
88     }
89     size_t i = 0;
90     for (auto bw : perf_history.recent_perf()) {
91         if (i < recent_perf.size()) {
92             recent_perf[i] = bw;
93         } else {
94             recent_perf.push_back(bw);
95         }
96         ++i;
97     }
98 
99     nr_days = perf_history.nr_days();
100     i = 0;
101     for (auto bw : perf_history.daily_perf()) {
102         daily_perf[i++] = bw;
103     }
104 
105     nr_weeks = perf_history.nr_weeks();
106     i = 0;
107     for (auto bw : perf_history.weekly_perf()) {
108         weekly_perf[i++] = bw;
109     }
110 }
111 
refresh(IOPerfHistory * perf_history)112 void storage_info_t::refresh(IOPerfHistory* perf_history)
113 {
114     struct statvfs buf;
115     if (statvfs(userdata_path.c_str(), &buf) != 0) {
116         PLOG(WARNING) << "Failed to get userdata info";
117         return;
118     }
119 
120     userdata_total_kb = buf.f_bsize * buf.f_blocks >> 10;
121     userdata_free_kb = buf.f_bfree * buf.f_blocks >> 10;
122 
123     Mutex::Autolock _l(si_mutex);
124 
125     perf_history->Clear();
126     perf_history->set_day_start_sec(
127         duration_cast<chrono::seconds>(day_start_tp.time_since_epoch()).count());
128     for (const uint32_t& bw : recent_perf) {
129         perf_history->add_recent_perf(bw);
130     }
131     perf_history->set_nr_samples(nr_samples);
132     for (const uint32_t& bw : daily_perf) {
133         perf_history->add_daily_perf(bw);
134     }
135     perf_history->set_nr_days(nr_days);
136     for (const uint32_t& bw : weekly_perf) {
137         perf_history->add_weekly_perf(bw);
138     }
139     perf_history->set_nr_weeks(nr_weeks);
140 }
141 
publish()142 void storage_info_t::publish()
143 {
144     android_log_event_list(EVENTLOGTAG_EMMCINFO)
145         << version << eol << lifetime_a << lifetime_b
146         << LOG_ID_EVENTS;
147 }
148 
update_perf_history(uint32_t bw,const time_point<system_clock> & tp)149 void storage_info_t::update_perf_history(uint32_t bw,
150                                          const time_point<system_clock>& tp)
151 {
152     Mutex::Autolock _l(si_mutex);
153 
154     if (tp > day_start_tp &&
155         duration_cast<chrono::seconds>(tp - day_start_tp).count() < DAY_TO_SEC) {
156         if (nr_samples >= recent_perf.size()) {
157             recent_perf.push_back(bw);
158         } else {
159             recent_perf[nr_samples] = bw;
160         }
161         nr_samples++;
162         return;
163     }
164 
165     if (nr_samples < recent_perf.size()) {
166         recent_perf.erase(recent_perf.begin() + nr_samples, recent_perf.end());
167     }
168 
169     uint32_t daily_avg_bw = 0;
170     if (!recent_perf.empty()) {
171         daily_avg_bw = accumulate(recent_perf.begin(), recent_perf.end(), 0) / recent_perf.size();
172     }
173 
174     day_start_tp = tp - chrono::seconds(duration_cast<chrono::seconds>(
175         tp.time_since_epoch()).count() % DAY_TO_SEC);
176 
177     nr_samples = 0;
178     if (recent_perf.empty())
179         recent_perf.resize(1);
180     recent_perf[nr_samples++] = bw;
181 
182     if (nr_days < WEEK_TO_DAYS) {
183         daily_perf[nr_days++] = daily_avg_bw;
184         return;
185     }
186 
187     DCHECK(nr_days > 0);
188     uint32_t week_avg_bw = accumulate(daily_perf.begin(),
189         daily_perf.begin() + nr_days, 0) / nr_days;
190 
191     nr_days = 0;
192     daily_perf[nr_days++] = daily_avg_bw;
193 
194     if (nr_weeks >= YEAR_TO_WEEKS) {
195         nr_weeks = 0;
196     }
197     weekly_perf[nr_weeks++] = week_avg_bw;
198 }
199 
get_perf_history()200 vector<int> storage_info_t::get_perf_history()
201 {
202     Mutex::Autolock _l(si_mutex);
203 
204     vector<int> ret(3 + recent_perf.size() + daily_perf.size() + weekly_perf.size());
205 
206     ret[0] = recent_perf.size();
207     ret[1] = daily_perf.size();
208     ret[2] = weekly_perf.size();
209 
210     int start = 3;
211     for (size_t i = 0; i < recent_perf.size(); i++) {
212         int idx = (recent_perf.size() + nr_samples - 1 - i) % recent_perf.size();
213         ret[start + i] = recent_perf[idx];
214     }
215 
216     start += recent_perf.size();
217     for (size_t i = 0; i < daily_perf.size(); i++) {
218         int idx = (daily_perf.size() + nr_days - 1 - i) % daily_perf.size();
219         ret[start + i] = daily_perf[idx];
220     }
221 
222     start += daily_perf.size();
223     for (size_t i = 0; i < weekly_perf.size(); i++) {
224         int idx = (weekly_perf.size() + nr_weeks - 1 - i) % weekly_perf.size();
225         ret[start + i] = weekly_perf[idx];
226     }
227 
228     return ret;
229 }
230 
get_recent_perf()231 uint32_t storage_info_t::get_recent_perf() {
232     Mutex::Autolock _l(si_mutex);
233     if (recent_perf.size() == 0) return 0;
234     return accumulate(recent_perf.begin(), recent_perf.end(), recent_perf.size() / 2) /
235            recent_perf.size();
236 }
237 
report()238 void emmc_info_t::report()
239 {
240     if (!report_sysfs()) return;
241 
242     publish();
243 }
244 
report_sysfs()245 bool emmc_info_t::report_sysfs()
246 {
247     string buffer;
248     uint16_t rev = 0;
249 
250     if (!ReadFileToString(emmc_sysfs + "rev", &buffer)) {
251         return false;
252     }
253 
254     if (sscanf(buffer.c_str(), "0x%hx", &rev) < 1 ||
255         rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
256         return false;
257     }
258 
259     version = "emmc ";
260     version += emmc_ver_str[rev];
261 
262     if (!ReadFileToString(emmc_sysfs + "pre_eol_info", &buffer)) {
263         return false;
264     }
265 
266     if (sscanf(buffer.c_str(), "%hx", &eol) < 1 || eol == 0) {
267         return false;
268     }
269 
270     if (!ReadFileToString(emmc_sysfs + "life_time", &buffer)) {
271         return false;
272     }
273 
274     if (sscanf(buffer.c_str(), "0x%hx 0x%hx", &lifetime_a, &lifetime_b) < 2 ||
275         (lifetime_a == 0 && lifetime_b == 0)) {
276         return false;
277     }
278 
279     return true;
280 }
281 
report()282 void ufs_info_t::report()
283 {
284     string buffer;
285     if (!ReadFileToString(health_file, &buffer)) {
286         return;
287     }
288 
289     vector<string> lines = Split(buffer, "\n");
290     if (lines.empty()) {
291         return;
292     }
293 
294     char rev[8];
295     if (sscanf(lines[0].c_str(), "ufs version: 0x%7s\n", rev) < 1) {
296         return;
297     }
298 
299     version = "ufs " + string(rev);
300 
301     for (size_t i = 1; i < lines.size(); i++) {
302         char token[32];
303         uint16_t val;
304         int ret;
305         if ((ret = sscanf(lines[i].c_str(),
306                    "Health Descriptor[Byte offset 0x%*d]: %31s = 0x%hx",
307                    token, &val)) < 2) {
308             continue;
309         }
310 
311         if (string(token) == "bPreEOLInfo") {
312             eol = val;
313         } else if (string(token) == "bDeviceLifeTimeEstA") {
314             lifetime_a = val;
315         } else if (string(token) == "bDeviceLifeTimeEstB") {
316             lifetime_b = val;
317         }
318     }
319 
320     if (eol == 0 || (lifetime_a == 0 && lifetime_b == 0)) {
321         return;
322     }
323 
324     publish();
325 }
326 
report()327 void health_storage_info_t::report() {
328     vector<StorageInfo> halInfos;
329     auto ret = mHealth->getStorageInfo(&halInfos);
330     if (ret.isOk()) {
331         if (halInfos.size() != 0) {
332             set_values_from_hal_storage_info(halInfos[0]);
333             publish();
334             return;
335         }
336         LOG(ERROR) << "getStorageInfo succeeded but size is 0";
337         return;
338     }
339     if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
340         LOG(DEBUG) << "getStorageInfo is not supported on health HAL.";
341         return;
342     }
343     LOG(ERROR) << "getStorageInfo failed with " << ret.getDescription();
344 }
345 
set_values_from_hal_storage_info(const StorageInfo & halInfo)346 void health_storage_info_t::set_values_from_hal_storage_info(const StorageInfo& halInfo) {
347     eol = halInfo.eol;
348     lifetime_a = halInfo.lifetimeA;
349     lifetime_b = halInfo.lifetimeB;
350     version = halInfo.version;
351 }
352