storaged: read emmc health data from sysfs

Sysfs data is straightforward so we don't need parsing anymore.

Also removed periodical check since data is set only once during
driver initialization. Checking at every device boot or storaged
restart should be sufficient to monitor long term status change.

Test: adb logcat -d -b events | grep storaged_emmc_info
Bug: 36228467
Merged-In: I2a181f52c9f19de1e679a3a905aaebafe4d08227
Change-Id: Ic05e353f0af9363f3bcbe793ba0c351082e446ca
This commit is contained in:
Jin Qian 2017-03-17 16:36:59 -07:00
parent e98f1a9c56
commit 8197093497
7 changed files with 89 additions and 103 deletions

View file

@ -1,6 +1,5 @@
ro.storaged.event.interval # interval storaged scans for IO stats, in seconds
ro.storaged.event.perf_check # check for time spent in event loop, in microseconds
ro.storaged.disk_stats_pub # interval storaged publish disk stats, in seconds
ro.storaged.emmc_info_pub # interval storaged publish emmc info, in seconds
ro.storaged.uid_io.interval # interval storaged checks Per UID IO usage, in seconds
ro.storaged.uid_io.threshold # Per UID IO usage limit, in bytes

View file

@ -230,7 +230,6 @@ public:
// 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_EMMC_INFO_PUBLISH ( 86400 )
#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO ( 3600 )
#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT (300)
@ -240,7 +239,6 @@ public:
struct storaged_config {
int periodic_chores_interval_unit;
int periodic_chores_interval_disk_stats_publish;
int periodic_chores_interval_emmc_info_publish;
int periodic_chores_interval_uid_io;
bool proc_uid_io_available; // whether uid_io is accessible
bool diskstats_available; // whether diskstats is accessible
@ -253,7 +251,6 @@ private:
storaged_config mConfig;
disk_stats_publisher mDiskStats;
disk_stats_monitor mDsm;
storage_info_t *info = nullptr;
uid_monitor mUidm;
time_t mStarttime;
public:
@ -264,9 +261,6 @@ public:
void pause(void) {
sleep(mConfig.periodic_chores_interval_unit);
}
void set_storage_info(storage_info_t *storage_info) {
info = storage_info;
}
time_t get_starttime(void) {
return mStarttime;

View file

@ -24,43 +24,34 @@ friend class test_case_name##_##test_name##_Test
using namespace std;
// two characters in string for each byte
struct str_hex {
char str[2];
};
class storage_info_t {
protected:
FRIEND_TEST(storaged_test, storage_info_t);
uint8_t eol; // pre-eol (end of life) information
uint8_t lifetime_a; // device life time estimation (type A)
uint8_t lifetime_b; // device life time estimation (type B)
uint16_t eol; // pre-eol (end of life) information
uint16_t lifetime_a; // device life time estimation (type A)
uint16_t lifetime_b; // device life time estimation (type B)
string version; // version string
public:
void publish();
public:
storage_info_t() : eol(0), lifetime_a(0), lifetime_b(0) {}
virtual ~storage_info_t() {}
virtual bool init() = 0;
virtual bool update() = 0;
virtual bool report() = 0;
};
class emmc_info_t : public storage_info_t {
private:
// minimum size of a ext_csd file
const int EXT_CSD_FILE_MIN_SIZE = 1024;
// List of interesting offsets
const size_t EXT_CSD_REV_IDX = 192 * sizeof(str_hex);
const size_t EXT_PRE_EOL_INFO_IDX = 267 * sizeof(str_hex);
const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * sizeof(str_hex);
const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * sizeof(str_hex);
const char* ext_csd_file = "/d/mmc0/mmc0:0001/ext_csd";
const char* emmc_ver_str[8] = {
"4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0"
const string emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
const string emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
const char* emmc_ver_str[9] = {
"4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1"
};
public:
virtual ~emmc_info_t() {}
bool init();
bool update();
bool report();
bool report_sysfs();
bool report_debugfs();
};
void report_storage_health();
#endif /* _STORAGED_INFO_H_ */

View file

@ -43,7 +43,6 @@
#include <storaged_utils.h>
storaged_t storaged;
emmc_info_t emmc_info;
// Function of storaged's main thread
void* storaged_main(void* s) {
@ -114,10 +113,7 @@ int main(int argc, char** argv) {
}
if (flag_main_service) { // start main thread
if (emmc_info.init()) {
storaged.set_storage_info(&emmc_info);
}
report_storage_health();
// Start the main thread of storaged
pthread_t storaged_main_thread;
errno = pthread_create(&storaged_main_thread, NULL, storaged_main, &storaged);

View file

@ -203,9 +203,6 @@ storaged_t::storaged_t(void) {
mConfig.periodic_chores_interval_disk_stats_publish =
property_get_int32("ro.storaged.disk_stats_pub", DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH);
mConfig.periodic_chores_interval_emmc_info_publish =
property_get_int32("ro.storaged.emmc_info_pub", DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH);
mConfig.periodic_chores_interval_uid_io =
property_get_int32("ro.storaged.uid_io.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO);
@ -221,12 +218,6 @@ void storaged_t::event(void) {
}
}
if (info && mTimer &&
(mTimer % mConfig.periodic_chores_interval_emmc_info_publish) == 0) {
info->update();
info->publish();
}
if (mConfig.proc_uid_io_available && mTimer &&
(mTimer % mConfig.periodic_chores_interval_uid_io) == 0) {
mUidm.report();

View file

@ -16,83 +16,118 @@
#define LOG_TAG "storaged"
#include <stdio.h>
#include <string.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/logging.h>
#include <log/log_event_list.h>
#include "storaged.h"
using namespace std;
using namespace android;
using namespace android::base;
void report_storage_health()
{
emmc_info_t mmc;
mmc.report();
}
void storage_info_t::publish()
{
if (eol == 0 && lifetime_a == 0 && lifetime_b == 0) {
return;
}
android_log_event_list(EVENTLOGTAG_EMMCINFO)
<< version << eol << lifetime_a << lifetime_b
<< LOG_ID_EVENTS;
}
bool emmc_info_t::init()
bool emmc_info_t::report()
{
if (!report_sysfs() && !report_debugfs())
return false;
publish();
return true;
}
bool emmc_info_t::report_sysfs()
{
string buffer;
if (!ReadFileToString(ext_csd_file, &buffer) ||
buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
uint16_t rev = 0;
if (!ReadFileToString(emmc_sysfs + "rev", &buffer)) {
return false;
}
string ver_str = buffer.substr(EXT_CSD_REV_IDX, sizeof(str_hex));
uint8_t ext_csd_rev;
if (!ParseUint(ver_str, &ext_csd_rev)) {
LOG_TO(SYSTEM, ERROR) << "Failure on parsing EXT_CSD_REV.";
if (sscanf(buffer.c_str(), "0x%hx", &rev) < 1 ||
rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
return false;
}
version = "emmc ";
version += (ext_csd_rev < ARRAY_SIZE(emmc_ver_str)) ?
emmc_ver_str[ext_csd_rev] : "Unknown";
version += emmc_ver_str[rev];
if (ext_csd_rev < 7) {
if (!ReadFileToString(emmc_sysfs + "pre_eol_info", &buffer)) {
return false;
}
return update();
}
bool emmc_info_t::update()
{
string buffer;
if (!ReadFileToString(ext_csd_file, &buffer) ||
buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
if (sscanf(buffer.c_str(), "%hx", &eol) < 1 || eol == 0) {
return false;
}
string str = buffer.substr(EXT_PRE_EOL_INFO_IDX, sizeof(str_hex));
if (!ParseUint(str, &eol)) {
LOG_TO(SYSTEM, ERROR) << "Failure on parsing EXT_PRE_EOL_INFO.";
if (!ReadFileToString(emmc_sysfs + "life_time", &buffer)) {
return false;
}
str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, sizeof(str_hex));
if (!ParseUint(str, &lifetime_a)) {
LOG_TO(SYSTEM, ERROR)
<< "Failure on parsing EXT_DEVICE_LIFE_TIME_EST_TYP_A.";
return false;
}
str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, sizeof(str_hex));
if (!ParseUint(str, &lifetime_b)) {
LOG_TO(SYSTEM, ERROR)
<< "Failure on parsing EXT_DEVICE_LIFE_TIME_EST_TYP_B.";
if (sscanf(buffer.c_str(), "0x%hx 0x%hx", &lifetime_a, &lifetime_b) < 2 ||
(lifetime_a == 0 && lifetime_b == 0)) {
return false;
}
return true;
}
const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
/* 2 characters in string for each byte */
const size_t EXT_CSD_REV_IDX = 192 * 2;
const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2;
const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
bool emmc_info_t::report_debugfs()
{
string buffer;
uint16_t rev = 0;
if (!ReadFileToString(emmc_debugfs, &buffer) ||
buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
return false;
}
string str = buffer.substr(EXT_CSD_REV_IDX, 2);
if (!ParseUint(str, &rev) ||
rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
return false;
}
version = "emmc ";
version += emmc_ver_str[rev];
str = buffer.substr(EXT_PRE_EOL_INFO_IDX, 2);
if (!ParseUint(str, &eol)) {
return false;
}
str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, 2);
if (!ParseUint(str, &lifetime_a)) {
return false;
}
str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, 2);
if (!ParseUint(str, &lifetime_b)) {
return false;
}
return true;
}

View file

@ -29,7 +29,6 @@
#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
#define EMMC_EXT_CSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
static void pause(uint32_t sec) {
const char* path = "/cache/test";
@ -58,13 +57,8 @@ static void pause(uint32_t sec) {
const char* DISK_STATS_PATH;
TEST(storaged_test, retvals) {
struct disk_stats stats;
emmc_info_t info;
memset(&stats, 0, sizeof(struct disk_stats));
if (info.init()) {
EXPECT_TRUE(info.update());
}
if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
DISK_STATS_PATH = MMC_DISK_STATS_PATH;
} else if (access(SDA_DISK_STATS_PATH, R_OK) >= 0) {
@ -127,20 +121,6 @@ TEST(storaged_test, disk_stats) {
}
}
TEST(storaged_test, storage_info_t) {
emmc_info_t info;
if (access(EMMC_EXT_CSD_PATH, R_OK) >= 0) {
int ret = info.init();
if (ret) {
EXPECT_TRUE(info.version.empty());
ASSERT_TRUE(info.update());
// update should put something in info.
EXPECT_TRUE(info.eol || info.lifetime_a || info.lifetime_b);
}
}
}
static double mean(std::deque<uint32_t> nums) {
double sum = 0.0;
for (uint32_t i : nums) {