mmc: block: Add MMC write packing statistics
The write packing statistics are used for the packed commands unit tests in order to determine test success or failure Change-Id: I5eea513991c794543fbb3d009d8b7db0b0912fc5 Signed-off-by: Maya Erez <merez@codeaurora.org> Signed-off-by: Tatyana Brokhman <tlinder@codeaurora.org> [subhashj@codeaurora.org: fixed trivial merge conflicts] Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
This commit is contained in:
parent
86dc987a4f
commit
a37b447025
5 changed files with 285 additions and 6 deletions
|
@ -70,6 +70,12 @@ MODULE_ALIAS("mmc:block");
|
|||
#define PACKED_CMD_VER 0x01
|
||||
#define PACKED_CMD_WR 0x02
|
||||
|
||||
#define MMC_BLK_UPDATE_STOP_REASON(stats, reason) \
|
||||
do { \
|
||||
if (stats->enabled) \
|
||||
stats->pack_stop_reason[reason]++; \
|
||||
} while (0)
|
||||
|
||||
static DEFINE_MUTEX(block_mutex);
|
||||
|
||||
/*
|
||||
|
@ -1706,6 +1712,35 @@ static void mmc_blk_write_packing_control(struct mmc_queue *mq,
|
|||
mq->wr_packing_enabled = true;
|
||||
}
|
||||
|
||||
struct mmc_wr_pack_stats *mmc_blk_get_packed_statistics(struct mmc_card *card)
|
||||
{
|
||||
if (!card)
|
||||
return NULL;
|
||||
|
||||
return &card->wr_pack_stats;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_blk_get_packed_statistics);
|
||||
|
||||
void mmc_blk_init_packed_statistics(struct mmc_card *card)
|
||||
{
|
||||
int max_num_of_packed_reqs = 0;
|
||||
|
||||
if (!card || !card->wr_pack_stats.packing_events)
|
||||
return;
|
||||
|
||||
max_num_of_packed_reqs = card->ext_csd.max_packed_writes;
|
||||
|
||||
spin_lock(&card->wr_pack_stats.lock);
|
||||
memset(card->wr_pack_stats.packing_events, 0,
|
||||
(max_num_of_packed_reqs + 1) *
|
||||
sizeof(*card->wr_pack_stats.packing_events));
|
||||
memset(&card->wr_pack_stats.pack_stop_reason, 0,
|
||||
sizeof(card->wr_pack_stats.pack_stop_reason));
|
||||
card->wr_pack_stats.enabled = true;
|
||||
spin_unlock(&card->wr_pack_stats.lock);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_blk_init_packed_statistics);
|
||||
|
||||
static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
|
||||
{
|
||||
struct request_queue *q = mq->queue;
|
||||
|
@ -1719,6 +1754,7 @@ static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
|
|||
bool put_back = true;
|
||||
u8 max_packed_rw = 0;
|
||||
u8 reqs = 0;
|
||||
struct mmc_wr_pack_stats *stats = &card->wr_pack_stats;
|
||||
|
||||
if (!(md->flags & MMC_BLK_PACKED_CMD))
|
||||
goto no_packed;
|
||||
|
@ -1757,6 +1793,7 @@ static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
|
|||
phys_segments += mmc_calc_packed_hdr_segs(q, card);
|
||||
}
|
||||
|
||||
spin_lock(&stats->lock);
|
||||
do {
|
||||
if (reqs >= max_packed_rw - 1) {
|
||||
put_back = false;
|
||||
|
@ -1767,32 +1804,46 @@ static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
|
|||
next = blk_fetch_request(q);
|
||||
spin_unlock_irq(q->queue_lock);
|
||||
if (!next) {
|
||||
MMC_BLK_UPDATE_STOP_REASON(stats, EMPTY_QUEUE);
|
||||
put_back = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (mmc_large_sector(card) &&
|
||||
!IS_ALIGNED(blk_rq_sectors(next), 8))
|
||||
!IS_ALIGNED(blk_rq_sectors(next), 8)) {
|
||||
MMC_BLK_UPDATE_STOP_REASON(stats, LARGE_SEC_ALIGN);
|
||||
break;
|
||||
}
|
||||
|
||||
if (next->cmd_flags & REQ_DISCARD ||
|
||||
next->cmd_flags & REQ_FLUSH)
|
||||
next->cmd_flags & REQ_FLUSH) {
|
||||
MMC_BLK_UPDATE_STOP_REASON(stats, FLUSH_OR_DISCARD);
|
||||
break;
|
||||
}
|
||||
|
||||
if (rq_data_dir(cur) != rq_data_dir(next))
|
||||
if (rq_data_dir(cur) != rq_data_dir(next)) {
|
||||
MMC_BLK_UPDATE_STOP_REASON(stats, WRONG_DATA_DIR);
|
||||
break;
|
||||
}
|
||||
|
||||
if (mmc_req_rel_wr(next) &&
|
||||
(md->flags & MMC_BLK_REL_WR) && !en_rel_wr)
|
||||
(md->flags & MMC_BLK_REL_WR) && !en_rel_wr) {
|
||||
MMC_BLK_UPDATE_STOP_REASON(stats, REL_WRITE);
|
||||
break;
|
||||
}
|
||||
|
||||
req_sectors += blk_rq_sectors(next);
|
||||
if (req_sectors > max_blk_count)
|
||||
if (req_sectors > max_blk_count) {
|
||||
if (stats->enabled)
|
||||
stats->pack_stop_reason[EXCEEDS_SECTORS]++;
|
||||
break;
|
||||
}
|
||||
|
||||
phys_segments += next->nr_phys_segments;
|
||||
if (phys_segments > max_phys_segs)
|
||||
if (phys_segments > max_phys_segs) {
|
||||
MMC_BLK_UPDATE_STOP_REASON(stats, EXCEEDS_SEGMENTS);
|
||||
break;
|
||||
}
|
||||
|
||||
if (rq_data_dir(next) == WRITE)
|
||||
mq->num_of_potential_packed_wr_reqs++;
|
||||
|
@ -1807,6 +1858,15 @@ static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
|
|||
spin_unlock_irq(q->queue_lock);
|
||||
}
|
||||
|
||||
if (stats->enabled) {
|
||||
if (reqs + 1 <= card->ext_csd.max_packed_writes)
|
||||
stats->packing_events[reqs + 1]++;
|
||||
if (reqs + 1 == max_packed_rw)
|
||||
MMC_BLK_UPDATE_STOP_REASON(stats, THRESHOLD);
|
||||
}
|
||||
|
||||
spin_unlock(&stats->lock);
|
||||
|
||||
if (reqs > 0) {
|
||||
list_add(&req->queuelist, &mqrq->packed->list);
|
||||
mqrq->packed->nr_entries = ++reqs;
|
||||
|
|
|
@ -273,6 +273,8 @@ struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type)
|
|||
card->dev.release = mmc_release_card;
|
||||
card->dev.type = type;
|
||||
|
||||
spin_lock_init(&card->wr_pack_stats.lock);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
|
@ -380,6 +382,8 @@ void mmc_remove_card(struct mmc_card *card)
|
|||
of_node_put(card->dev.of_node);
|
||||
}
|
||||
|
||||
kfree(card->wr_pack_stats.packing_events);
|
||||
|
||||
put_device(&card->dev);
|
||||
}
|
||||
|
||||
|
|
|
@ -402,6 +402,172 @@ static const struct file_operations mmc_dbg_ext_csd_fops = {
|
|||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static int mmc_wr_pack_stats_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct mmc_card *card = inode->i_private;
|
||||
|
||||
filp->private_data = card;
|
||||
card->wr_pack_stats.print_in_read = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TEMP_BUF_SIZE 256
|
||||
static ssize_t mmc_wr_pack_stats_read(struct file *filp, char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct mmc_card *card = filp->private_data;
|
||||
struct mmc_wr_pack_stats *pack_stats;
|
||||
int i;
|
||||
int max_num_of_packed_reqs = 0;
|
||||
char *temp_buf;
|
||||
|
||||
if (!card)
|
||||
return cnt;
|
||||
|
||||
if (!card->wr_pack_stats.print_in_read)
|
||||
return 0;
|
||||
|
||||
if (!card->wr_pack_stats.enabled) {
|
||||
pr_info("%s: write packing statistics are disabled\n",
|
||||
mmc_hostname(card->host));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
pack_stats = &card->wr_pack_stats;
|
||||
|
||||
if (!pack_stats->packing_events) {
|
||||
pr_info("%s: NULL packing_events\n", mmc_hostname(card->host));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
max_num_of_packed_reqs = card->ext_csd.max_packed_writes;
|
||||
|
||||
temp_buf = kmalloc(TEMP_BUF_SIZE, GFP_KERNEL);
|
||||
if (!temp_buf)
|
||||
goto exit;
|
||||
|
||||
spin_lock(&pack_stats->lock);
|
||||
|
||||
snprintf(temp_buf, TEMP_BUF_SIZE, "%s: write packing statistics:\n",
|
||||
mmc_hostname(card->host));
|
||||
strlcat(ubuf, temp_buf, cnt);
|
||||
|
||||
for (i = 1 ; i <= max_num_of_packed_reqs ; ++i) {
|
||||
if (pack_stats->packing_events[i]) {
|
||||
snprintf(temp_buf, TEMP_BUF_SIZE,
|
||||
"%s: Packed %d reqs - %d times\n",
|
||||
mmc_hostname(card->host), i,
|
||||
pack_stats->packing_events[i]);
|
||||
strlcat(ubuf, temp_buf, cnt);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(temp_buf, TEMP_BUF_SIZE,
|
||||
"%s: stopped packing due to the following reasons:\n",
|
||||
mmc_hostname(card->host));
|
||||
strlcat(ubuf, temp_buf, cnt);
|
||||
|
||||
if (pack_stats->pack_stop_reason[EXCEEDS_SEGMENTS]) {
|
||||
snprintf(temp_buf, TEMP_BUF_SIZE,
|
||||
"%s: %d times: exceed max num of segments\n",
|
||||
mmc_hostname(card->host),
|
||||
pack_stats->pack_stop_reason[EXCEEDS_SEGMENTS]);
|
||||
strlcat(ubuf, temp_buf, cnt);
|
||||
}
|
||||
if (pack_stats->pack_stop_reason[EXCEEDS_SECTORS]) {
|
||||
snprintf(temp_buf, TEMP_BUF_SIZE,
|
||||
"%s: %d times: exceed max num of sectors\n",
|
||||
mmc_hostname(card->host),
|
||||
pack_stats->pack_stop_reason[EXCEEDS_SECTORS]);
|
||||
strlcat(ubuf, temp_buf, cnt);
|
||||
}
|
||||
if (pack_stats->pack_stop_reason[WRONG_DATA_DIR]) {
|
||||
snprintf(temp_buf, TEMP_BUF_SIZE,
|
||||
"%s: %d times: wrong data direction\n",
|
||||
mmc_hostname(card->host),
|
||||
pack_stats->pack_stop_reason[WRONG_DATA_DIR]);
|
||||
strlcat(ubuf, temp_buf, cnt);
|
||||
}
|
||||
if (pack_stats->pack_stop_reason[FLUSH_OR_DISCARD]) {
|
||||
snprintf(temp_buf, TEMP_BUF_SIZE,
|
||||
"%s: %d times: flush or discard\n",
|
||||
mmc_hostname(card->host),
|
||||
pack_stats->pack_stop_reason[FLUSH_OR_DISCARD]);
|
||||
strlcat(ubuf, temp_buf, cnt);
|
||||
}
|
||||
if (pack_stats->pack_stop_reason[EMPTY_QUEUE]) {
|
||||
snprintf(temp_buf, TEMP_BUF_SIZE,
|
||||
"%s: %d times: empty queue\n",
|
||||
mmc_hostname(card->host),
|
||||
pack_stats->pack_stop_reason[EMPTY_QUEUE]);
|
||||
strlcat(ubuf, temp_buf, cnt);
|
||||
}
|
||||
if (pack_stats->pack_stop_reason[REL_WRITE]) {
|
||||
snprintf(temp_buf, TEMP_BUF_SIZE,
|
||||
"%s: %d times: rel write\n",
|
||||
mmc_hostname(card->host),
|
||||
pack_stats->pack_stop_reason[REL_WRITE]);
|
||||
strlcat(ubuf, temp_buf, cnt);
|
||||
}
|
||||
if (pack_stats->pack_stop_reason[THRESHOLD]) {
|
||||
snprintf(temp_buf, TEMP_BUF_SIZE,
|
||||
"%s: %d times: Threshold\n",
|
||||
mmc_hostname(card->host),
|
||||
pack_stats->pack_stop_reason[THRESHOLD]);
|
||||
strlcat(ubuf, temp_buf, cnt);
|
||||
}
|
||||
|
||||
if (pack_stats->pack_stop_reason[LARGE_SEC_ALIGN]) {
|
||||
snprintf(temp_buf, TEMP_BUF_SIZE,
|
||||
"%s: %d times: Large sector alignment\n",
|
||||
mmc_hostname(card->host),
|
||||
pack_stats->pack_stop_reason[LARGE_SEC_ALIGN]);
|
||||
strlcat(ubuf, temp_buf, cnt);
|
||||
}
|
||||
|
||||
spin_unlock(&pack_stats->lock);
|
||||
|
||||
kfree(temp_buf);
|
||||
|
||||
pr_info("%s", ubuf);
|
||||
|
||||
exit:
|
||||
if (card->wr_pack_stats.print_in_read == 1) {
|
||||
card->wr_pack_stats.print_in_read = 0;
|
||||
return strnlen(ubuf, cnt);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t mmc_wr_pack_stats_write(struct file *filp,
|
||||
const char __user *ubuf, size_t cnt,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct mmc_card *card = filp->private_data;
|
||||
int value;
|
||||
|
||||
if (!card)
|
||||
return cnt;
|
||||
|
||||
sscanf(ubuf, "%d", &value);
|
||||
if (value) {
|
||||
mmc_blk_init_packed_statistics(card);
|
||||
} else {
|
||||
spin_lock(&card->wr_pack_stats.lock);
|
||||
card->wr_pack_stats.enabled = false;
|
||||
spin_unlock(&card->wr_pack_stats.lock);
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static const struct file_operations mmc_dbg_wr_pack_stats_fops = {
|
||||
.open = mmc_wr_pack_stats_open,
|
||||
.read = mmc_wr_pack_stats_read,
|
||||
.write = mmc_wr_pack_stats_write,
|
||||
};
|
||||
|
||||
void mmc_add_card_debugfs(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
|
@ -434,6 +600,12 @@ void mmc_add_card_debugfs(struct mmc_card *card)
|
|||
&mmc_dbg_ext_csd_fops))
|
||||
goto err;
|
||||
|
||||
if (mmc_card_mmc(card) && (card->ext_csd.rev >= 6) &&
|
||||
(card->host->caps2 & MMC_CAP2_PACKED_WR))
|
||||
if (!debugfs_create_file("wr_pack_stats", S_IRUSR, root, card,
|
||||
&mmc_dbg_wr_pack_stats_fops))
|
||||
goto err;
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
|
|
|
@ -1723,6 +1723,24 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
} else {
|
||||
card->ext_csd.packed_event_en = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!oldcard) {
|
||||
if ((host->caps2 & MMC_CAP2_PACKED_CMD) &&
|
||||
(card->ext_csd.max_packed_writes > 0)) {
|
||||
/*
|
||||
* We would like to keep the statistics in an index
|
||||
* that equals the num of packed requests
|
||||
* (1 to max_packed_writes)
|
||||
*/
|
||||
card->wr_pack_stats.packing_events = kzalloc(
|
||||
(card->ext_csd.max_packed_writes + 1) *
|
||||
sizeof(*card->wr_pack_stats.packing_events),
|
||||
GFP_KERNEL);
|
||||
if (!card->wr_pack_stats.packing_events)
|
||||
goto free_card;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -218,6 +218,26 @@ enum mmc_blk_status {
|
|||
MMC_BLK_NEW_REQUEST,
|
||||
};
|
||||
|
||||
enum mmc_packed_stop_reasons {
|
||||
EXCEEDS_SEGMENTS = 0,
|
||||
EXCEEDS_SECTORS,
|
||||
WRONG_DATA_DIR,
|
||||
FLUSH_OR_DISCARD,
|
||||
EMPTY_QUEUE,
|
||||
REL_WRITE,
|
||||
THRESHOLD,
|
||||
LARGE_SEC_ALIGN,
|
||||
MAX_REASONS,
|
||||
};
|
||||
|
||||
struct mmc_wr_pack_stats {
|
||||
u32 *packing_events;
|
||||
u32 pack_stop_reason[MAX_REASONS];
|
||||
spinlock_t lock;
|
||||
bool enabled;
|
||||
bool print_in_read;
|
||||
};
|
||||
|
||||
/* The number of MMC physical partitions. These consist of:
|
||||
* boot partitions (2), general purpose partitions (4) and
|
||||
* RPMB partition (1) in MMC v4.4.
|
||||
|
@ -316,6 +336,8 @@ struct mmc_card {
|
|||
struct dentry *debugfs_root;
|
||||
struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
|
||||
unsigned int nr_parts;
|
||||
|
||||
struct mmc_wr_pack_stats wr_pack_stats; /* packed commands stats*/
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -534,5 +556,8 @@ extern void mmc_unregister_driver(struct mmc_driver *);
|
|||
|
||||
extern void mmc_fixup_device(struct mmc_card *card,
|
||||
const struct mmc_fixup *table);
|
||||
extern struct mmc_wr_pack_stats *mmc_blk_get_packed_statistics(
|
||||
struct mmc_card *card);
|
||||
extern void mmc_blk_init_packed_statistics(struct mmc_card *card);
|
||||
|
||||
#endif /* LINUX_MMC_CARD_H */
|
||||
|
|
Loading…
Add table
Reference in a new issue