Home | History | Annotate | Download | only in storaged
      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