Jin Qian | 65dea71 | 2017-08-29 16:48:20 -0700 | [diff] [blame] | 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 | #ifdef DEBUG |
| 34 | void 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 |
| 44 | void log_debug_disk_perf(struct disk_perf* perf, const char* type) {} |
| 45 | #endif |
| 46 | |
| 47 | void 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 | |
| 63 | bool 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 | |
| 95 | struct 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 | |
| 124 | void 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 |
| 135 | void 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 */ |
| 159 | void 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 | |
| 169 | void 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 | |
| 179 | void 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 | |
| 188 | void 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 | |
| 196 | bool 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 | |
| 203 | void 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 | |
| 244 | void 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 | |
| 253 | void 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 | } |