1 /*
2  * Copyright (C) 2017 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 <stdint.h>
20 #include <stdlib.h>
21 
22 #include <sstream>
23 
24 #include <android-base/file.h>
25 #include <android-base/logging.h>
26 #include <log/log_event_list.h>
27 
28 #include "storaged.h"
29 #include "storaged_diskstats.h"
30 
31 namespace {
32 
33 using aidl::android::hardware::health::DiskStats;
34 using aidl::android::hardware::health::IHealth;
35 
36 #ifdef DEBUG
log_debug_disk_perf(struct disk_perf * perf,const char * type)37 void log_debug_disk_perf(struct disk_perf* perf, const char* type) {
38     // skip if the input structure are all zeros
39     if (perf == NULL || perf->is_zero()) return;
40 
41     LOG(INFO) << "disk_perf " << type << " rd: " << perf->read_perf << " kbps, " << perf->read_ios
42               << " iops"
43               << " wr: " << perf->write_perf << " kbps, " << perf->write_ios << " iops"
44               << " q: " << perf->queue;
45 }
46 #else
log_debug_disk_perf(struct disk_perf * perf,const char * type)47 void log_debug_disk_perf(struct disk_perf* perf, const char* type) {}
48 #endif
49 
log_event_disk_stats(struct disk_stats * stats,const char * type)50 void log_event_disk_stats(struct disk_stats* stats, const char* type) {
51     // skip if the input structure are all zeros
52     if (stats == NULL || stats->is_zero()) return;
53 
54     android_log_event_list(EVENTLOGTAG_DISKSTATS)
55         << type << stats->start_time << stats->end_time
56         << stats->read_ios << stats->read_merges
57         << stats->read_sectors << stats->read_ticks
58         << stats->write_ios << stats->write_merges
59         << stats->write_sectors << stats->write_ticks
60         << (uint64_t)stats->io_avg << stats->io_ticks << stats->io_in_queue
61         << LOG_ID_EVENTS;
62 }
63 
64 } // namespace
65 
get_time(struct timespec * ts)66 bool get_time(struct timespec* ts) {
67     // Use monotonic to exclude suspend time so that we measure IO bytes/sec
68     // when system is running.
69     int ret = clock_gettime(CLOCK_MONOTONIC, ts);
70     if (ret < 0) {
71         PLOG(ERROR) << "clock_gettime() failed";
72         return false;
73     }
74     return true;
75 }
76 
init_disk_stats_other(const struct timespec & ts,struct disk_stats * stats)77 void init_disk_stats_other(const struct timespec& ts, struct disk_stats* stats) {
78     stats->start_time = 0;
79     stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC + ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC);
80     stats->counter = 1;
81     stats->io_avg = (double)stats->io_in_flight;
82 }
83 
parse_disk_stats(const char * disk_stats_path,struct disk_stats * stats)84 bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats) {
85     // Get time
86     struct timespec ts;
87     if (!get_time(&ts)) {
88         return false;
89     }
90 
91     std::string buffer;
92     if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {
93         PLOG(ERROR) << disk_stats_path << ": ReadFileToString failed.";
94         return false;
95     }
96 
97     // Regular diskstats entries
98     std::stringstream ss(buffer);
99     for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
100         ss >> *((uint64_t*)stats + i);
101     }
102     // Other entries
103     init_disk_stats_other(ts, stats);
104     return true;
105 }
106 
convert_hal_disk_stats(struct disk_stats * dst,const DiskStats & src)107 void convert_hal_disk_stats(struct disk_stats* dst, const DiskStats& src) {
108     dst->read_ios = src.reads;
109     dst->read_merges = src.readMerges;
110     dst->read_sectors = src.readSectors;
111     dst->read_ticks = src.readTicks;
112     dst->write_ios = src.writes;
113     dst->write_merges = src.writeMerges;
114     dst->write_sectors = src.writeSectors;
115     dst->write_ticks = src.writeTicks;
116     dst->io_in_flight = src.ioInFlight;
117     dst->io_ticks = src.ioTicks;
118     dst->io_in_queue = src.ioInQueue;
119 }
120 
get_disk_stats_from_health_hal(const std::shared_ptr<IHealth> & service,struct disk_stats * stats)121 bool get_disk_stats_from_health_hal(const std::shared_ptr<IHealth>& service,
122                                     struct disk_stats* stats) {
123     struct timespec ts;
124     if (!get_time(&ts)) {
125         return false;
126     }
127 
128     std::vector<DiskStats> halStats;
129     auto ret = service->getDiskStats(&halStats);
130     if (ret.isOk()) {
131         if (halStats.size() > 0) {
132             convert_hal_disk_stats(stats, halStats[0]);
133             init_disk_stats_other(ts, stats);
134             return true;
135         }
136         LOG(ERROR) << "getDiskStats succeeded but size is 0";
137         return false;
138     }
139     if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
140         LOG(DEBUG) << "getDiskStats is not supported on health HAL.";
141         return false;
142     }
143     LOG(ERROR) << "getDiskStats failed with " << ret.getDescription();
144     return false;
145 }
146 
get_disk_perf(struct disk_stats * stats)147 struct disk_perf get_disk_perf(struct disk_stats* stats)
148 {
149     struct disk_perf perf = {};
150 
151     if (stats->io_ticks) {
152         if (stats->read_ticks) {
153             unsigned long long divisor = stats->read_ticks * stats->io_ticks;
154             perf.read_perf = ((unsigned long long)SECTOR_SIZE *
155                               stats->read_sectors * stats->io_in_queue +
156                               (divisor >> 1)) / divisor;
157             perf.read_ios = ((unsigned long long)SEC_TO_MSEC *
158                              stats->read_ios * stats->io_in_queue +
159                              (divisor >> 1)) / divisor;
160         }
161         if (stats->write_ticks) {
162             unsigned long long divisor = stats->write_ticks * stats->io_ticks;
163             perf.write_perf = ((unsigned long long)SECTOR_SIZE *
164                                stats->write_sectors * stats->io_in_queue +
165                                (divisor >> 1)) / divisor;
166             perf.write_ios = ((unsigned long long)SEC_TO_MSEC *
167                               stats->write_ios * stats->io_in_queue +
168                               (divisor >> 1)) / divisor;
169         }
170         perf.queue = (stats->io_in_queue + (stats->io_ticks >> 1)) /
171                      stats->io_ticks;
172     }
173     return perf;
174 }
175 
get_inc_disk_stats(const struct disk_stats * prev,const struct disk_stats * curr,struct disk_stats * inc)176 void get_inc_disk_stats(const struct disk_stats* prev, const struct disk_stats* curr,
177                         struct disk_stats* inc)
178 {
179     *inc = *curr - *prev;
180     inc->start_time = prev->end_time;
181     inc->end_time = curr->end_time;
182     inc->io_avg = curr->io_avg;
183     inc->counter = 1;
184 }
185 
186 // Add src to dst
add_disk_stats(struct disk_stats * src,struct disk_stats * dst)187 void add_disk_stats(struct disk_stats* src, struct disk_stats* dst)
188 {
189     if (dst->end_time != 0 && dst->end_time != src->start_time) {
190         LOG(WARNING) << "Two dis-continuous periods of diskstats"
191                      << " are added. dst end with " << dst->end_time << ", src start with "
192                      << src->start_time;
193     }
194 
195     *dst += *src;
196 
197     dst->io_in_flight = src->io_in_flight;
198     if (dst->counter + src->counter) {
199         dst->io_avg =
200             ((dst->io_avg * dst->counter) + (src->io_avg * src->counter)) /
201             (dst->counter + src->counter);
202     }
203     dst->counter += src->counter;
204     dst->end_time = src->end_time;
205     if (dst->start_time == 0) {
206         dst->start_time = src->start_time;
207     }
208 }
209 
210 /* disk_stats_monitor */
update_mean()211 void disk_stats_monitor::update_mean()
212 {
213     CHECK(mValid);
214     mMean.read_perf = (uint32_t)mStats.read_perf.get_mean();
215     mMean.read_ios = (uint32_t)mStats.read_ios.get_mean();
216     mMean.write_perf = (uint32_t)mStats.write_perf.get_mean();
217     mMean.write_ios = (uint32_t)mStats.write_ios.get_mean();
218     mMean.queue = (uint32_t)mStats.queue.get_mean();
219 }
220 
update_std()221 void disk_stats_monitor::update_std()
222 {
223     CHECK(mValid);
224     mStd.read_perf = (uint32_t)mStats.read_perf.get_std();
225     mStd.read_ios = (uint32_t)mStats.read_ios.get_std();
226     mStd.write_perf = (uint32_t)mStats.write_perf.get_std();
227     mStd.write_ios = (uint32_t)mStats.write_ios.get_std();
228     mStd.queue = (uint32_t)mStats.queue.get_std();
229 }
230 
add(struct disk_perf * perf)231 void disk_stats_monitor::add(struct disk_perf* perf)
232 {
233     mStats.read_perf.add(perf->read_perf);
234     mStats.read_ios.add(perf->read_ios);
235     mStats.write_perf.add(perf->write_perf);
236     mStats.write_ios.add(perf->write_ios);
237     mStats.queue.add(perf->queue);
238 }
239 
evict(struct disk_perf * perf)240 void disk_stats_monitor::evict(struct disk_perf* perf) {
241     mStats.read_perf.evict(perf->read_perf);
242     mStats.read_ios.evict(perf->read_ios);
243     mStats.write_perf.evict(perf->write_perf);
244     mStats.write_ios.evict(perf->write_ios);
245     mStats.queue.evict(perf->queue);
246 }
247 
detect(struct disk_perf * perf)248 bool disk_stats_monitor::detect(struct disk_perf* perf)
249 {
250     return ((double)perf->queue >= (double)mMean.queue + mSigma * (double)mStd.queue) &&
251         ((double)perf->read_perf < (double)mMean.read_perf - mSigma * (double)mStd.read_perf) &&
252         ((double)perf->write_perf < (double)mMean.write_perf - mSigma * (double)mStd.write_perf);
253 }
254 
update(struct disk_stats * curr)255 void disk_stats_monitor::update(struct disk_stats* curr)
256 {
257     disk_stats inc;
258     get_inc_disk_stats(&mPrevious, curr, &inc);
259     add_disk_stats(&inc, &mAccumulate_pub);
260 
261     struct disk_perf perf = get_disk_perf(&inc);
262     log_debug_disk_perf(&perf, "regular");
263 
264     add(&perf);
265     mBuffer.push(perf);
266     if (mBuffer.size() > mWindow) {
267         evict(&mBuffer.front());
268         mBuffer.pop();
269         mValid = true;
270     }
271 
272     // Update internal data structures
273     if (LIKELY(mValid)) {
274         CHECK_EQ(mBuffer.size(), mWindow);
275         update_mean();
276         update_std();
277         if (UNLIKELY(detect(&perf))) {
278             mStall = true;
279             add_disk_stats(&inc, &mAccumulate);
280             log_debug_disk_perf(&mMean, "stalled_mean");
281             log_debug_disk_perf(&mStd, "stalled_std");
282         } else {
283             if (mStall) {
284                 struct disk_perf acc_perf = get_disk_perf(&mAccumulate);
285                 log_debug_disk_perf(&acc_perf, "stalled");
286                 log_event_disk_stats(&mAccumulate, "stalled");
287                 mStall = false;
288                 memset(&mAccumulate, 0, sizeof(mAccumulate));
289             }
290         }
291     }
292 
293     mPrevious = *curr;
294 }
295 
update()296 void disk_stats_monitor::update() {
297     disk_stats curr;
298     if (mHealth != nullptr) {
299         if (!get_disk_stats_from_health_hal(mHealth, &curr)) {
300             return;
301         }
302     } else {
303         if (!parse_disk_stats(DISK_STATS_PATH, &curr)) {
304             return;
305         }
306     }
307 
308     update(&curr);
309 }
310 
publish(void)311 void disk_stats_monitor::publish(void)
312 {
313     struct disk_perf perf = get_disk_perf(&mAccumulate_pub);
314     log_debug_disk_perf(&perf, "regular");
315     log_event_disk_stats(&mAccumulate_pub, "regular");
316     // Reset global structures
317     memset(&mAccumulate_pub, 0, sizeof(struct disk_stats));
318 }
319