blob: 0604e0ad55f7aa2f857ca40ec8aa1e348d95f989 [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
33#ifdef DEBUG
34void log_debug_disk_perf(struct disk_perf* perf, const char* type) {
35 // skip if the input structure are all zeros
36 if (perf == NULL || perf->is_zero()) return;
37
38 LOG_TO(SYSTEM, INFO) << "disk_perf " << type
39 << " rd: " << perf->read_perf << " kbps, " << perf->read_ios << " iops"
40 << " wr: " << perf->write_perf << " kbps, " << perf->write_ios << " iops"
41 << " q: " << perf->queue;
42}
43#else
44void log_debug_disk_perf(struct disk_perf* perf, const char* type) {}
45#endif
46
47void log_event_disk_stats(struct disk_stats* stats, const char* type) {
48 // skip if the input structure are all zeros
49 if (stats == NULL || stats->is_zero()) return;
50
51 android_log_event_list(EVENTLOGTAG_DISKSTATS)
52 << type << stats->start_time << stats->end_time
53 << stats->read_ios << stats->read_merges
54 << stats->read_sectors << stats->read_ticks
55 << stats->write_ios << stats->write_merges
56 << stats->write_sectors << stats->write_ticks
57 << (uint64_t)stats->io_avg << stats->io_ticks << stats->io_in_queue
58 << LOG_ID_EVENTS;
59}
60
61} // namespace
62
63bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats)
64{
65 // Get time
66 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_TO(SYSTEM, ERROR) << "clock_gettime() failed";
72 return false;
73 }
74
75 std::string buffer;
76 if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {
77 PLOG_TO(SYSTEM, ERROR) << disk_stats_path << ": ReadFileToString failed.";
78 return false;
79 }
80
81 // Regular diskstats entries
82 std::stringstream ss(buffer);
83 for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
84 ss >> *((uint64_t*)stats + i);
85 }
86 // Other entries
87 stats->start_time = 0;
88 stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC +
89 ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC);
90 stats->counter = 1;
91 stats->io_avg = (double)stats->io_in_flight;
92 return true;
93}
94
95struct disk_perf get_disk_perf(struct disk_stats* stats)
96{
97 struct disk_perf perf = {};
98
99 if (stats->io_ticks) {
100 if (stats->read_ticks) {
101 unsigned long long divisor = stats->read_ticks * stats->io_ticks;
102 perf.read_perf = ((unsigned long long)SECTOR_SIZE *
103 stats->read_sectors * stats->io_in_queue +
104 (divisor >> 1)) / divisor;
105 perf.read_ios = ((unsigned long long)SEC_TO_MSEC *
106 stats->read_ios * stats->io_in_queue +
107 (divisor >> 1)) / divisor;
108 }
109 if (stats->write_ticks) {
110 unsigned long long divisor = stats->write_ticks * stats->io_ticks;
111 perf.write_perf = ((unsigned long long)SECTOR_SIZE *
112 stats->write_sectors * stats->io_in_queue +
113 (divisor >> 1)) / divisor;
114 perf.write_ios = ((unsigned long long)SEC_TO_MSEC *
115 stats->write_ios * stats->io_in_queue +
116 (divisor >> 1)) / divisor;
117 }
118 perf.queue = (stats->io_in_queue + (stats->io_ticks >> 1)) /
119 stats->io_ticks;
120 }
121 return perf;
122}
123
124void get_inc_disk_stats(const struct disk_stats* prev, const struct disk_stats* curr,
125 struct disk_stats* inc)
126{
127 *inc = *curr - *prev;
128 inc->start_time = prev->end_time;
129 inc->end_time = curr->end_time;
130 inc->io_avg = curr->io_avg;
131 inc->counter = 1;
132}
133
134// Add src to dst
135void add_disk_stats(struct disk_stats* src, struct disk_stats* dst)
136{
137 if (dst->end_time != 0 && dst->end_time != src->start_time) {
138 LOG_TO(SYSTEM, WARNING) << "Two dis-continuous periods of diskstats"
139 << " are added. dst end with " << dst->end_time
140 << ", src start with " << src->start_time;
141 }
142
143 *dst += *src;
144
145 dst->io_in_flight = src->io_in_flight;
146 if (dst->counter + src->counter) {
147 dst->io_avg =
148 ((dst->io_avg * dst->counter) + (src->io_avg * src->counter)) /
149 (dst->counter + src->counter);
150 }
151 dst->counter += src->counter;
152 dst->end_time = src->end_time;
153 if (dst->start_time == 0) {
154 dst->start_time = src->start_time;
155 }
156}
157
158/* disk_stats_monitor */
159void disk_stats_monitor::update_mean()
160{
161 CHECK(mValid);
162 mMean.read_perf = (uint32_t)mStats.read_perf.get_mean();
163 mMean.read_ios = (uint32_t)mStats.read_ios.get_mean();
164 mMean.write_perf = (uint32_t)mStats.write_perf.get_mean();
165 mMean.write_ios = (uint32_t)mStats.write_ios.get_mean();
166 mMean.queue = (uint32_t)mStats.queue.get_mean();
167}
168
169void disk_stats_monitor::update_std()
170{
171 CHECK(mValid);
172 mStd.read_perf = (uint32_t)mStats.read_perf.get_std();
173 mStd.read_ios = (uint32_t)mStats.read_ios.get_std();
174 mStd.write_perf = (uint32_t)mStats.write_perf.get_std();
175 mStd.write_ios = (uint32_t)mStats.write_ios.get_std();
176 mStd.queue = (uint32_t)mStats.queue.get_std();
177}
178
179void disk_stats_monitor::add(struct disk_perf* perf)
180{
181 mStats.read_perf.add(perf->read_perf);
182 mStats.read_ios.add(perf->read_ios);
183 mStats.write_perf.add(perf->write_perf);
184 mStats.write_ios.add(perf->write_ios);
185 mStats.queue.add(perf->queue);
186}
187
188void disk_stats_monitor::evict(struct disk_perf* perf) {
189 mStats.read_perf.evict(perf->read_perf);
190 mStats.read_ios.evict(perf->read_ios);
191 mStats.write_perf.evict(perf->write_perf);
192 mStats.write_ios.evict(perf->write_ios);
193 mStats.queue.evict(perf->queue);
194}
195
196bool disk_stats_monitor::detect(struct disk_perf* perf)
197{
198 return ((double)perf->queue >= (double)mMean.queue + mSigma * (double)mStd.queue) &&
199 ((double)perf->read_perf < (double)mMean.read_perf - mSigma * (double)mStd.read_perf) &&
200 ((double)perf->write_perf < (double)mMean.write_perf - mSigma * (double)mStd.write_perf);
201}
202
203void disk_stats_monitor::update(struct disk_stats* curr)
204{
205 disk_stats inc;
206 get_inc_disk_stats(&mPrevious, curr, &inc);
207 add_disk_stats(&inc, &mAccumulate_pub);
208
209 struct disk_perf perf = get_disk_perf(&inc);
210 log_debug_disk_perf(&perf, "regular");
211
212 add(&perf);
213 mBuffer.push(perf);
214 if (mBuffer.size() > mWindow) {
215 evict(&mBuffer.front());
216 mBuffer.pop();
217 mValid = true;
218 }
219
220 // Update internal data structures
221 if (LIKELY(mValid)) {
222 CHECK_EQ(mBuffer.size(), mWindow);
223 update_mean();
224 update_std();
225 if (UNLIKELY(detect(&perf))) {
226 mStall = true;
227 add_disk_stats(&inc, &mAccumulate);
228 log_debug_disk_perf(&mMean, "stalled_mean");
229 log_debug_disk_perf(&mStd, "stalled_std");
230 } else {
231 if (mStall) {
232 struct disk_perf acc_perf = get_disk_perf(&mAccumulate);
233 log_debug_disk_perf(&acc_perf, "stalled");
234 log_event_disk_stats(&mAccumulate, "stalled");
235 mStall = false;
236 memset(&mAccumulate, 0, sizeof(mAccumulate));
237 }
238 }
239 }
240
241 mPrevious = *curr;
242}
243
244void disk_stats_monitor::update() {
245 disk_stats curr;
246 if (!parse_disk_stats(DISK_STATS_PATH, &curr)) {
247 return;
248 }
249
250 update(&curr);
251}
252
253void disk_stats_monitor::publish(void)
254{
255 struct disk_perf perf = get_disk_perf(&mAccumulate_pub);
256 log_debug_disk_perf(&perf, "regular");
257 log_event_disk_stats(&mAccumulate, "regular");
258 // Reset global structures
259 memset(&mAccumulate_pub, 0, sizeof(struct disk_stats));
260}