diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index be7773f40a90..c3d058313e43 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -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; } diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index d317205648ff..55ad10037a6c 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -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; } diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 9f75060d3b0c..7550db69f76f 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -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: diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 80ce6cc36b88..895fc0ceaf6f 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -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; }; diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 446b56765dbb..9a3e44f993c2 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -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