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