mmc: core: Add MMC BKOPS statistics and debugfs ability to print them

The BKOPS statistics are used for BKOPS unit tests and APT tests
to determine test success or failure.
The BKOPS statstics provide the following information:
The number of times BKOPS were issued according to its severity level
The number of times manual BKOPS was started/stopped (HPI)
The number of times auto BKOPS was enabled/disabled

In order to enable and reset the statistics:
echo 1 > /sys/kernel/debug/mmc0/mmc0:0001/bkops_stats
In order to disable the statistics:
echo 0 > /sys/kernel/debug/mmc0/mmc0:0001/bkops_stats
In order to view the statistics:
cat /sys/kernel/debug/mmc0/mmc0:0001/bkops_stats

Change-Id: Ib84319aedfb49dc022bc27efbda842a5db38c7e9
Signed-off-by: Yaniv Gardi <ygardi@codeaurora.org>
Signed-off-by: Dov Levenglick <dovl@codeaurora.org>
[subhashj@codeaurora.org: fixed trivial merge conflicts]
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
This commit is contained in:
Dov Levenglick 2015-06-28 16:45:41 +03:00 committed by Subhash Jadavani
parent 650d2e1086
commit b98e6c2fa1
5 changed files with 209 additions and 2 deletions

View file

@ -294,6 +294,7 @@ struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type)
card->dev.type = type;
spin_lock_init(&card->wr_pack_stats.lock);
spin_lock_init(&card->bkops.stats.lock);
return card;
}

View file

@ -912,6 +912,77 @@ static void mmc_start_cmdq_request(struct mmc_host *host,
__func__);
}
/**
* mmc_blk_init_bkops_statistics - initialize bkops statistics
* @card: MMC card to start BKOPS
*
* Initialize and enable the bkops statistics
*/
void mmc_blk_init_bkops_statistics(struct mmc_card *card)
{
int i;
struct mmc_bkops_stats *stats;
if (!card)
return;
stats = &card->bkops.stats;
spin_lock(&stats->lock);
stats->manual_start = 0;
stats->hpi = 0;
stats->auto_start = 0;
stats->auto_stop = 0;
for (i = 0 ; i < MMC_BKOPS_NUM_SEVERITY_LEVELS ; i++)
stats->level[i] = 0;
stats->enabled = true;
spin_unlock(&stats->lock);
}
EXPORT_SYMBOL(mmc_blk_init_bkops_statistics);
static void mmc_update_bkops_hpi(struct mmc_bkops_stats *stats)
{
spin_lock_irq(&stats->lock);
if (stats->enabled)
stats->hpi++;
spin_unlock_irq(&stats->lock);
}
static void mmc_update_bkops_start(struct mmc_bkops_stats *stats)
{
spin_lock_irq(&stats->lock);
if (stats->enabled)
stats->manual_start++;
spin_unlock_irq(&stats->lock);
}
static void mmc_update_bkops_auto_on(struct mmc_bkops_stats *stats)
{
spin_lock_irq(&stats->lock);
if (stats->enabled)
stats->auto_start++;
spin_unlock_irq(&stats->lock);
}
static void mmc_update_bkops_auto_off(struct mmc_bkops_stats *stats)
{
spin_lock_irq(&stats->lock);
if (stats->enabled)
stats->auto_stop++;
spin_unlock_irq(&stats->lock);
}
static void mmc_update_bkops_level(struct mmc_bkops_stats *stats,
unsigned level)
{
BUG_ON(level >= MMC_BKOPS_NUM_SEVERITY_LEVELS);
spin_lock_irq(&stats->lock);
if (stats->enabled)
stats->level[level]++;
spin_unlock_irq(&stats->lock);
}
/**
* mmc_set_auto_bkops - set auto BKOPS for supported cards
* @card: MMC card to start BKOPS
@ -950,10 +1021,13 @@ int mmc_set_auto_bkops(struct mmc_card *card, bool enable)
pr_err("%s: %s: error in setting auto bkops to %d (%d)\n",
mmc_hostname(card->host), __func__, enable, ret);
} else {
if (enable)
if (enable) {
mmc_card_set_auto_bkops(card);
else
mmc_update_bkops_auto_on(&card->bkops.stats);
} else {
mmc_card_clr_auto_bkops(card);
mmc_update_bkops_auto_off(&card->bkops.stats);
}
card->ext_csd.man_bkops_en = bkops_en;
}
out:
@ -986,6 +1060,8 @@ void mmc_check_bkops(struct mmc_card *card)
return;
}
mmc_update_bkops_level(&card->bkops.stats,
card->ext_csd.raw_bkops_status);
if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2)
return;
@ -1021,6 +1097,7 @@ void mmc_start_manual_bkops(struct mmc_card *card)
mmc_hostname(card->host), err);
} else {
mmc_card_set_doing_bkops(card);
mmc_update_bkops_start(&card->bkops.stats);
card->bkops.needs_manual = false;
}
@ -1573,6 +1650,7 @@ int mmc_stop_bkops(struct mmc_card *card)
*/
if (!err || (err == -EINVAL)) {
mmc_card_clr_doing_bkops(card);
mmc_update_bkops_hpi(&card->bkops.stats);
mmc_retune_release(card->host);
err = 0;
}

View file

@ -637,6 +637,89 @@ static const struct file_operations mmc_dbg_wr_pack_stats_fops = {
.write = mmc_wr_pack_stats_write,
};
static int mmc_bkops_stats_read(struct seq_file *file, void *data)
{
struct mmc_card *card = file->private;
struct mmc_bkops_stats *stats;
int i;
if (!card)
return -EINVAL;
stats = &card->bkops.stats;
if (!stats->enabled) {
pr_info("%s: bkops statistics are disabled\n",
mmc_hostname(card->host));
goto exit;
}
spin_lock(&stats->lock);
seq_printf(file, "%s: bkops statistics:\n",
mmc_hostname(card->host));
seq_printf(file, "%s: BKOPS: sent START_BKOPS to device: %u\n",
mmc_hostname(card->host), stats->manual_start);
seq_printf(file, "%s: BKOPS: stopped due to HPI: %u\n",
mmc_hostname(card->host), stats->hpi);
seq_printf(file, "%s: BKOPS: sent AUTO_EN set to 1: %u\n",
mmc_hostname(card->host), stats->auto_start);
seq_printf(file, "%s: BKOPS: sent AUTO_EN set to 0: %u\n",
mmc_hostname(card->host), stats->auto_stop);
for (i = 0 ; i < MMC_BKOPS_NUM_SEVERITY_LEVELS ; ++i)
seq_printf(file, "%s: BKOPS: due to level %d: %u\n",
mmc_hostname(card->host), i, stats->level[i]);
spin_unlock(&stats->lock);
exit:
return 0;
}
static ssize_t mmc_bkops_stats_write(struct file *filp,
const char __user *ubuf, size_t cnt,
loff_t *ppos)
{
struct mmc_card *card = filp->f_mapping->host->i_private;
int value;
struct mmc_bkops_stats *stats;
int err;
if (!card)
return cnt;
stats = &card->bkops.stats;
err = kstrtoint_from_user(ubuf, cnt, 0, &value);
if (err) {
pr_err("%s: %s: error parsing input from user (%d)\n",
mmc_hostname(card->host), __func__, err);
return err;
}
if (value) {
mmc_blk_init_bkops_statistics(card);
} else {
spin_lock(&stats->lock);
stats->enabled = false;
spin_unlock(&stats->lock);
}
return cnt;
}
static int mmc_bkops_stats_open(struct inode *inode, struct file *file)
{
return single_open(file, mmc_bkops_stats_read, inode->i_private);
}
static const struct file_operations mmc_dbg_bkops_stats_fops = {
.open = mmc_bkops_stats_open,
.read = seq_read,
.write = mmc_bkops_stats_write,
};
void mmc_add_card_debugfs(struct mmc_card *card)
{
struct mmc_host *host = card->host;
@ -675,6 +758,13 @@ void mmc_add_card_debugfs(struct mmc_card *card)
&mmc_dbg_wr_pack_stats_fops))
goto err;
if (mmc_card_mmc(card) && (card->ext_csd.rev >= 5) &&
(mmc_card_support_auto_bkops(card) ||
mmc_card_configured_manual_bkops(card)))
if (!debugfs_create_file("bkops_stats", S_IRUSR, root, card,
&mmc_dbg_bkops_stats_fops))
goto err;
return;
err:

View file

@ -275,12 +275,48 @@ struct mmc_part {
#define MMC_BLK_DATA_AREA_RPMB (1<<3)
};
enum {
MMC_BKOPS_NO_OP,
MMC_BKOPS_NOT_CRITICAL,
MMC_BKOPS_PERF_IMPACT,
MMC_BKOPS_CRITICAL,
MMC_BKOPS_NUM_SEVERITY_LEVELS,
};
/**
* struct mmc_bkops_stats - BKOPS statistics
* @lock: spinlock used for synchronizing the debugfs and the runtime accesses
* to this structure. No need to call with spin_lock_irq api
* @manual_start: number of times START_BKOPS was sent to the device
* @hpi: number of times HPI was sent to the device
* @auto_start: number of times AUTO_EN was set to 1
* @auto_stop: number of times AUTO_EN was set to 0
* @level: number of times the device reported the need for each level of
* bkops handling
* @enabled: control over whether statistics should be gathered
*
* This structure is used to collect statistics regarding the bkops
* configuration and use-patterns. It is collected during runtime and can be
* shown to the user via a debugfs entry.
*/
struct mmc_bkops_stats {
spinlock_t lock;
unsigned int manual_start;
unsigned int hpi;
unsigned int auto_start;
unsigned int auto_stop;
unsigned int level[MMC_BKOPS_NUM_SEVERITY_LEVELS];
bool enabled;
};
/**
* struct mmc_bkops_info - BKOPS data
* @stats: statistic information regarding bkops
* @need_manual: indication whether have to send START_BKOPS
* to the device
*/
struct mmc_bkops_info {
struct mmc_bkops_stats stats;
bool needs_manual;
};

View file

@ -208,6 +208,8 @@ extern int mmc_cache_barrier(struct mmc_card *);
extern int mmc_detect_card_removed(struct mmc_host *host);
extern void mmc_blk_init_bkops_statistics(struct mmc_card *card);
/**
* mmc_claim_host - exclusively claim a host
* @host: mmc host to claim