blob: 1eae5a10bc4c71c0570f17d1332164b128da4bad [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 Hong5291a672021-12-06 14:29:40 -080033using aidl::android::hardware::health::DiskStats;
34using aidl::android::hardware::health::IHealth;
Yifan Hongc4b46e02018-01-16 15:49:08 -080035
Jin Qian65dea712017-08-29 16:48:20 -070036#ifdef DEBUG
37void 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
Tom Cherryb8c11472020-01-10 17:08:15 -080041 LOG(INFO) << "disk_perf " << type << " rd: " << perf->read_perf << " kbps, " << perf->read_ios
42 << " iops"
Jin Qian65dea712017-08-29 16:48:20 -070043 << " wr: " << perf->write_perf << " kbps, " << perf->write_ios << " iops"
44 << " q: " << perf->queue;
45}
46#else
47void log_debug_disk_perf(struct disk_perf* perf, const char* type) {}
48#endif
49
50void 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
Yifan Hongc4b46e02018-01-16 15:49:08 -080066bool get_time(struct timespec* ts) {
Jin Qian65dea712017-08-29 16:48:20 -070067 // Use monotonic to exclude suspend time so that we measure IO bytes/sec
68 // when system is running.
Yifan Hongc4b46e02018-01-16 15:49:08 -080069 int ret = clock_gettime(CLOCK_MONOTONIC, ts);
Jin Qian65dea712017-08-29 16:48:20 -070070 if (ret < 0) {
Tom Cherryb8c11472020-01-10 17:08:15 -080071 PLOG(ERROR) << "clock_gettime() failed";
Jin Qian65dea712017-08-29 16:48:20 -070072 return false;
73 }
Yifan Hongc4b46e02018-01-16 15:49:08 -080074 return true;
75}
76
77void 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
84bool 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 }
Jin Qian65dea712017-08-29 16:48:20 -070090
91 std::string buffer;
92 if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {
Tom Cherryb8c11472020-01-10 17:08:15 -080093 PLOG(ERROR) << disk_stats_path << ": ReadFileToString failed.";
Jin Qian65dea712017-08-29 16:48:20 -070094 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
Yifan Hongc4b46e02018-01-16 15:49:08 -0800103 init_disk_stats_other(ts, stats);
104 return true;
105}
106
107void 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
Yifan Hong5291a672021-12-06 14:29:40 -0800121bool get_disk_stats_from_health_hal(const std::shared_ptr<IHealth>& service,
122 struct disk_stats* stats) {
Yifan Hongc4b46e02018-01-16 15:49:08 -0800123 struct timespec ts;
124 if (!get_time(&ts)) {
125 return false;
126 }
127
Yifan Hong5291a672021-12-06 14:29:40 -0800128 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;
Yifan Hong539ee582018-09-13 09:39:41 -0700135 }
Yifan Hong5291a672021-12-06 14:29:40 -0800136 LOG(ERROR) << "getDiskStats succeeded but size is 0";
Yifan Hongc4b46e02018-01-16 15:49:08 -0800137 return false;
138 }
Yifan Hong5291a672021-12-06 14:29:40 -0800139 if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
140 LOG(DEBUG) << "getDiskStats is not supported on health HAL.";
Yifan Hongc4b46e02018-01-16 15:49:08 -0800141 return false;
142 }
Yifan Hong5291a672021-12-06 14:29:40 -0800143 LOG(ERROR) << "getDiskStats failed with " << ret.getDescription();
144 return false;
Jin Qian65dea712017-08-29 16:48:20 -0700145}
146
147struct 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
176void 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
187void add_disk_stats(struct disk_stats* src, struct disk_stats* dst)
188{
189 if (dst->end_time != 0 && dst->end_time != src->start_time) {
Tom Cherryb8c11472020-01-10 17:08:15 -0800190 LOG(WARNING) << "Two dis-continuous periods of diskstats"
191 << " are added. dst end with " << dst->end_time << ", src start with "
192 << src->start_time;
Jin Qian65dea712017-08-29 16:48:20 -0700193 }
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 */
211void 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
221void 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
231void 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
240void 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
248bool 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
255void 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
296void disk_stats_monitor::update() {
297 disk_stats curr;
Yifan Hongc4b46e02018-01-16 15:49:08 -0800298 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 }
Jin Qian65dea712017-08-29 16:48:20 -0700306 }
307
308 update(&curr);
309}
310
311void 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, "regular");
316 // Reset global structures
317 memset(&mAccumulate_pub, 0, sizeof(struct disk_stats));
318}