/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "storaged" #include #include #include #include #include #include /* disk_stats_publisher */ void disk_stats_publisher::publish(void) { // Logging struct disk_perf perf = get_disk_perf(&mAccumulate); log_debug_disk_perf(&perf, "regular"); log_event_disk_stats(&mAccumulate, "regular"); // Reset global structures memset(&mAccumulate, 0, sizeof(struct disk_stats)); } void disk_stats_publisher::update(void) { struct disk_stats curr; if (parse_disk_stats(DISK_STATS_PATH, &curr)) { struct disk_stats inc = get_inc_disk_stats(&mPrevious, &curr); add_disk_stats(&inc, &mAccumulate); #ifdef DEBUG // log_kernel_disk_stats(&mPrevious, "prev stats"); // log_kernel_disk_stats(&curr, "curr stats"); // log_kernel_disk_stats(&inc, "inc stats"); // log_kernel_disk_stats(&mAccumulate, "accumulated stats"); #endif mPrevious = curr; } } /* disk_stats_monitor */ void disk_stats_monitor::update_mean() { CHECK(mValid); mMean.read_perf = (uint32_t)mStats.read_perf.get_mean(); mMean.read_ios = (uint32_t)mStats.read_ios.get_mean(); mMean.write_perf = (uint32_t)mStats.write_perf.get_mean(); mMean.write_ios = (uint32_t)mStats.write_ios.get_mean(); mMean.queue = (uint32_t)mStats.queue.get_mean(); } void disk_stats_monitor::update_std() { CHECK(mValid); mStd.read_perf = (uint32_t)mStats.read_perf.get_std(); mStd.read_ios = (uint32_t)mStats.read_ios.get_std(); mStd.write_perf = (uint32_t)mStats.write_perf.get_std(); mStd.write_ios = (uint32_t)mStats.write_ios.get_std(); mStd.queue = (uint32_t)mStats.queue.get_std(); } void disk_stats_monitor::add(struct disk_perf* perf) { mStats.read_perf.add(perf->read_perf); mStats.read_ios.add(perf->read_ios); mStats.write_perf.add(perf->write_perf); mStats.write_ios.add(perf->write_ios); mStats.queue.add(perf->queue); } void disk_stats_monitor::evict(struct disk_perf* perf) { mStats.read_perf.evict(perf->read_perf); mStats.read_ios.evict(perf->read_ios); mStats.write_perf.evict(perf->write_perf); mStats.write_ios.evict(perf->write_ios); mStats.queue.evict(perf->queue); } bool disk_stats_monitor::detect(struct disk_perf* perf) { return ((double)perf->queue >= (double)mMean.queue + mSigma * (double)mStd.queue) && ((double)perf->read_perf < (double)mMean.read_perf - mSigma * (double)mStd.read_perf) && ((double)perf->write_perf < (double)mMean.write_perf - mSigma * (double)mStd.write_perf); } void disk_stats_monitor::update(struct disk_stats* stats) { struct disk_stats inc = get_inc_disk_stats(&mPrevious, stats); struct disk_perf perf = get_disk_perf(&inc); // Update internal data structures if (LIKELY(mValid)) { CHECK_EQ(mBuffer.size(), mWindow); if (UNLIKELY(detect(&perf))) { mStall = true; add_disk_stats(&inc, &mAccumulate); log_debug_disk_perf(&mMean, "stalled_mean"); log_debug_disk_perf(&mStd, "stalled_std"); } else { if (mStall) { struct disk_perf acc_perf = get_disk_perf(&mAccumulate); log_debug_disk_perf(&acc_perf, "stalled"); log_event_disk_stats(&mAccumulate, "stalled"); mStall = false; memset(&mAccumulate, 0, sizeof(mAccumulate)); } } evict(&mBuffer.front()); mBuffer.pop(); add(&perf); mBuffer.push(perf); update_mean(); update_std(); } else { /* mValid == false */ CHECK_LT(mBuffer.size(), mWindow); add(&perf); mBuffer.push(perf); if (mBuffer.size() == mWindow) { mValid = true; update_mean(); update_std(); } } mPrevious = *stats; } void disk_stats_monitor::update(void) { struct disk_stats curr; if (LIKELY(parse_disk_stats(DISK_STATS_PATH, &curr))) { update(&curr); } } /* emmc_info_t */ void emmc_info_t::publish(void) { if (mValid) { log_event_emmc_info(&mInfo); } } void emmc_info_t::update(void) { if (mFdEmmc >= 0) { mValid = parse_emmc_ecsd(mFdEmmc, &mInfo); } } /* storaged_t */ storaged_t::storaged_t(void) { mConfig.emmc_available = (access(EMMC_ECSD_PATH, R_OK) >= 0); if (access(MMC_DISK_STATS_PATH, R_OK) < 0 && access(SDA_DISK_STATS_PATH, R_OK) < 0) { mConfig.diskstats_available = false; } else { mConfig.diskstats_available = true; } mConfig.proc_taskio_readable = true; const char* test_paths[] = {"/proc/1/io", "/proc/1/comm", "/proc/1/cmdline", "/proc/1/stat"}; for (uint i = 0; i < sizeof(test_paths) / sizeof(const char*); ++i) { if (access(test_paths[i], R_OK) < 0) { mConfig.proc_taskio_readable = false; break; } } mConfig.periodic_chores_interval_unit = DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT; mConfig.periodic_chores_interval_disk_stats_publish = DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH; mConfig.periodic_chores_interval_emmc_info_publish = DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH; mStarttime = time(NULL); } void storaged_t::event(void) { if (mConfig.diskstats_available) { mDiskStats.update(); mDsm.update(); if (mTimer && (mTimer % mConfig.periodic_chores_interval_disk_stats_publish) == 0) { mDiskStats.publish(); } } #ifdef DEBUG if (mConfig.proc_taskio_readable) { mTasks.update_running_tasks(); } #endif if (mConfig.emmc_available && mTimer && (mTimer % mConfig.periodic_chores_interval_emmc_info_publish) == 0) { mEmmcInfo.update(); mEmmcInfo.publish(); } mTimer += mConfig.periodic_chores_interval_unit; }