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