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 android::sp; 34 using android::hardware::health::V2_0::DiskStats; 35 using android::hardware::health::V2_0::IHealth; 36 using android::hardware::health::V2_0::Result; 37 using android::hardware::health::V2_0::toString; 38 39 #ifdef DEBUG 40 void 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 50 void log_debug_disk_perf(struct disk_perf* perf, const char* type) {} 51 #endif 52 53 void 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 69 bool get_time(struct timespec* ts) { 70 // Use monotonic to exclude suspend time so that we measure IO bytes/sec 71 // when system is running. 72 int ret = clock_gettime(CLOCK_MONOTONIC, ts); 73 if (ret < 0) { 74 PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed"; 75 return false; 76 } 77 return true; 78 } 79 80 void 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 87 bool 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 } 93 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 106 init_disk_stats_other(ts, stats); 107 return true; 108 } 109 110 void 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 124 bool 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); 152 return true; 153 } 154 155 struct 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 184 void 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 195 void 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 */ 219 void 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 229 void 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 239 void 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 248 void 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 256 bool 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 263 void 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 304 void disk_stats_monitor::update() { 305 disk_stats curr; 306 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 } 314 } 315 316 update(&curr); 317 } 318 319 void 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 } 327