Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e813b4b3 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

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

parents 2243dac2 5475671b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -299,6 +299,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;
}
+80 −2
Original line number Diff line number Diff line
@@ -857,6 +857,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
@@ -896,10 +967,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.bkops_en = bkops_en;
	}
out:
@@ -932,6 +1006,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;

@@ -965,6 +1041,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;
	}
	mmc_release_host(card->host);
@@ -1496,6 +1573,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);
		err = 0;
	}
out:
+90 −0
Original line number Diff line number Diff line
@@ -619,6 +619,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;
@@ -657,6 +740,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:
+36 −0
Original line number Diff line number Diff line
@@ -272,12 +272,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;
};

+2 −0
Original line number Diff line number Diff line
@@ -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