blob: fa6840638e165fac37081642323852045a338af3 [file] [log] [blame]
/*
* 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.
*/
#ifndef _STORAGED_H_
#define _STORAGED_H_
#include <semaphore.h>
#include <stdint.h>
#include <time.h>
#include <queue>
#include <string>
#include <unordered_map>
#include <vector>
#include <batteryservice/IBatteryPropertiesListener.h>
#include <batteryservice/IBatteryPropertiesRegistrar.h>
#include "storaged_info.h"
#include "storaged_uid_monitor.h"
using namespace android;
#define FRIEND_TEST(test_case_name, test_name) \
friend class test_case_name##_##test_name##_Test
/* For debug */
#ifdef DEBUG
#define debuginfo(fmt, ...) \
do {printf("%s():\t" fmt "\t[%s:%d]\n", __FUNCTION__, ##__VA_ARGS__, __FILE__, __LINE__);} \
while(0)
#else
#define debuginfo(...)
#endif
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define SECTOR_SIZE ( 512 )
#define SEC_TO_MSEC ( 1000 )
#define MSEC_TO_USEC ( 1000 )
#define USEC_TO_NSEC ( 1000 )
#define SEC_TO_USEC ( 1000000 )
#define HOUR_TO_SEC ( 3600 )
#define DAY_TO_SEC ( 3600 * 24 )
// number of attributes diskstats has
#define DISK_STATS_SIZE ( 11 )
// maximum size limit of a stats file
#define DISK_STATS_FILE_MAX_SIZE ( 256 )
#define DISK_STATS_IO_IN_FLIGHT_IDX ( 8 )
struct disk_stats {
/* It will be extremely unlikely for any of the following entries to overflow.
* For read_bytes(which will be greater than any of the following entries), it
* will take 27 years to overflow uint64_t at the reading rate of 20GB/s, which
* is the peak memory transfer rate for current memory.
* The diskstats entries (first 11) need to be at top in this structure _after_
* compiler's optimization.
*/
uint64_t read_ios; // number of read I/Os processed
uint64_t read_merges; // number of read I/Os merged with in-queue I/Os
uint64_t read_sectors; // number of sectors read
uint64_t read_ticks; // total wait time for read requests
uint64_t write_ios; // number of write I/Os processed
uint64_t write_merges; // number of write I/Os merged with in-queue I/Os
uint64_t write_sectors; // number of sectors written
uint64_t write_ticks; // total wait time for write requests
uint64_t io_in_flight; // number of I/Os currently in flight
uint64_t io_ticks; // total time this block device has been active
uint64_t io_in_queue; // total wait time for all requests
uint64_t start_time; // monotonic time accounting starts
uint64_t end_time; // monotonic time accounting ends
uint32_t counter; // private counter for accumulate calculations
double io_avg; // average io_in_flight for accumulate calculations
};
struct disk_perf {
uint32_t read_perf; // read speed (kbytes/s)
uint32_t read_ios; // read I/Os per second
uint32_t write_perf; // write speed (kbytes/s)
uint32_t write_ios; // write I/Os per second
uint32_t queue; // I/Os in queue
};
#define CMD_MAX_LEN ( 64 )
struct task_info {
uint32_t pid; // task id
uint64_t rchar; // characters read
uint64_t wchar; // characters written
uint64_t syscr; // read syscalls
uint64_t syscw; // write syscalls
uint64_t read_bytes; // bytes read (from storage layer)
uint64_t write_bytes; // bytes written (to storage layer)
uint64_t cancelled_write_bytes; // cancelled write byte by truncate
uint64_t starttime; // start time of task
char cmd[CMD_MAX_LEN]; // filename of the executable
};
class lock_t {
sem_t* mSem;
public:
lock_t(sem_t* sem) {
mSem = sem;
sem_wait(mSem);
}
~lock_t() {
sem_post(mSem);
}
};
class stream_stats {
private:
double mSum;
double mSquareSum;
uint32_t mCnt;
public:
stream_stats() : mSum(0), mSquareSum(0), mCnt(0) {};
~stream_stats() {};
double get_mean() {
return mSum / mCnt;
}
double get_std() {
return sqrt(mSquareSum / mCnt - mSum * mSum / (mCnt * mCnt));
}
void add(uint32_t num) {
mSum += (double)num;
mSquareSum += (double)num * (double)num;
mCnt++;
}
void evict(uint32_t num) {
if (mSum < num || mSquareSum < (double)num * (double)num) return;
mSum -= (double)num;
mSquareSum -= (double)num * (double)num;
mCnt--;
}
};
#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
#define EMMC_ECSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
#define UID_IO_STATS_PATH "/proc/uid_io/stats"
class disk_stats_monitor {
private:
FRIEND_TEST(storaged_test, disk_stats_monitor);
const char* DISK_STATS_PATH;
struct disk_stats mPrevious;
struct disk_stats mAccumulate;
bool mStall;
std::queue<struct disk_perf> mBuffer;
struct {
stream_stats read_perf; // read speed (bytes/s)
stream_stats read_ios; // read I/Os per second
stream_stats write_perf; // write speed (bytes/s)
stream_stats write_ios; // write I/O per second
stream_stats queue; // I/Os in queue
} mStats;
bool mValid;
const uint32_t mWindow;
const double mSigma;
struct disk_perf mMean;
struct disk_perf mStd;
void update_mean();
void update_std();
void add(struct disk_perf* perf);
void evict(struct disk_perf* perf);
bool detect(struct disk_perf* perf);
void update(struct disk_stats* stats);
public:
disk_stats_monitor(uint32_t window_size = 5, double sigma = 1.0) :
mStall(false),
mValid(false),
mWindow(window_size),
mSigma(sigma) {
memset(&mPrevious, 0, sizeof(mPrevious));
memset(&mMean, 0, sizeof(mMean));
memset(&mStd, 0, sizeof(mStd));
if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
DISK_STATS_PATH = MMC_DISK_STATS_PATH;
} else {
DISK_STATS_PATH = SDA_DISK_STATS_PATH;
}
}
void update(void);
};
class disk_stats_publisher {
private:
FRIEND_TEST(storaged_test, disk_stats_publisher);
const char* DISK_STATS_PATH;
struct disk_stats mAccumulate;
struct disk_stats mPrevious;
public:
disk_stats_publisher(void) {
memset(&mAccumulate, 0, sizeof(struct disk_stats));
memset(&mPrevious, 0, sizeof(struct disk_stats));
if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
DISK_STATS_PATH = MMC_DISK_STATS_PATH;
} else {
DISK_STATS_PATH = SDA_DISK_STATS_PATH;
}
}
~disk_stats_publisher(void) {}
void publish(void);
void update(void);
};
// Periodic chores intervals in seconds
#define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 )
#define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 )
#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO ( 3600 )
#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT (300)
// UID IO threshold in bytes
#define DEFAULT_PERIODIC_CHORES_UID_IO_THRESHOLD ( 1024 * 1024 * 1024ULL )
struct storaged_config {
int periodic_chores_interval_unit;
int periodic_chores_interval_disk_stats_publish;
int periodic_chores_interval_uid_io;
bool proc_uid_io_available; // whether uid_io is accessible
bool diskstats_available; // whether diskstats is accessible
int event_time_check_usec; // check how much cputime spent in event loop
};
class storaged_t : public BnBatteryPropertiesListener,
public IBinder::DeathRecipient {
private:
time_t mTimer;
storaged_config mConfig;
disk_stats_publisher mDiskStats;
disk_stats_monitor mDsm;
uid_monitor mUidm;
time_t mStarttime;
sp<IBatteryPropertiesRegistrar> battery_properties;
std::unique_ptr<storage_info_t> storage_info;
public:
storaged_t(void);
~storaged_t() {}
void event(void);
void event_checked(void);
void pause(void) {
sleep(mConfig.periodic_chores_interval_unit);
}
time_t get_starttime(void) {
return mStarttime;
}
std::unordered_map<uint32_t, struct uid_info> get_uids(void) {
return mUidm.get_uid_io_stats();
}
std::map<uint64_t, struct uid_records> get_uid_records(
double hours, uint64_t threshold, bool force_report) {
return mUidm.dump(hours, threshold, force_report);
}
void update_uid_io_interval(int interval) {
if (interval >= DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT) {
mConfig.periodic_chores_interval_uid_io = interval;
}
}
void init_battery_service();
virtual void batteryPropertiesChanged(struct BatteryProperties props);
void binderDied(const wp<IBinder>& who);
void report_storage_info();
};
// Eventlog tag
// The content must match the definition in EventLogTags.logtags
#define EVENTLOGTAG_DISKSTATS ( 2732 )
#define EVENTLOGTAG_EMMCINFO ( 2733 )
#define EVENTLOGTAG_UID_IO_ALERT ( 2734 )
#endif /* _STORAGED_H_ */