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