Merge "mmc: core: Add MMC BKOPS statistics and debugfs ability to print them" into msm-4.9
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 32469b2..b7a607a 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -294,6 +294,7 @@
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 38ea7fa..41165c7 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -959,6 +959,77 @@
}
/**
+ * 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
* @enable: enable/disable flag
@@ -996,10 +1067,13 @@
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:
@@ -1032,6 +1106,8 @@
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;
@@ -1067,6 +1143,7 @@
mmc_hostname(card->host), err);
} else {
mmc_card_set_doing_bkops(card);
+ mmc_update_bkops_start(&card->bkops.stats);
card->bkops.needs_manual = false;
}
@@ -1673,6 +1750,7 @@
*/
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 b2591d6..8beed5c 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -638,6 +638,89 @@
.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;
@@ -676,6 +759,13 @@
&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 b63f872..61399c0 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -278,12 +278,48 @@
#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 6d93843..4a1e8cc 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -222,6 +222,8 @@
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