mmc_test: collect data and show it via sysfs by demand
Make it possible to get test results via sysfs. It helps to do tests non-interactively. We have the file created under sysfs already and can use it to show test results. Prior to this patch, the "test" file under each card's sysfs node was write-only, and results were obtained by looking at dmesg. This patch improves programmatic access to the test results, making them available by reading back from the same "test" file: [root@host mmc0:e624]# echo 6 > test [root@host mmc0:e624]# cat test Test 6: 2 [cjb@laptop.org: changelog improvements] Signed-off-by: Andy Shevchenko <ext-andriy.shevchenko@nokia.com> Cc: Chris Ball <cjb@laptop.org> Cc: <linux-mmc@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Chris Ball <cjb@laptop.org>
This commit is contained in:
parent
5c25aee536
commit
3183aa1534
1 changed files with 169 additions and 2 deletions
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/swap.h> /* For nr_free_buffer_pages() */
|
||||
#include <linux/list.h>
|
||||
|
||||
#define RESULT_OK 0
|
||||
#define RESULT_FAIL 1
|
||||
|
@ -76,6 +77,38 @@ struct mmc_test_area {
|
|||
struct scatterlist *sg;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mmc_test_transfer_result - transfer results for performance tests.
|
||||
* @link: double-linked list
|
||||
* @count: amount of group of sectors to check
|
||||
* @sectors: amount of sectors to check in one group
|
||||
* @ts: time values of transfer
|
||||
* @rate: calculated transfer rate
|
||||
*/
|
||||
struct mmc_test_transfer_result {
|
||||
struct list_head link;
|
||||
unsigned int count;
|
||||
unsigned int sectors;
|
||||
struct timespec ts;
|
||||
unsigned int rate;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mmc_test_general_result - results for tests.
|
||||
* @link: double-linked list
|
||||
* @card: card under test
|
||||
* @testcase: number of test case
|
||||
* @result: result of test run
|
||||
* @tr_lst: transfer measurements if any as mmc_test_transfer_result
|
||||
*/
|
||||
struct mmc_test_general_result {
|
||||
struct list_head link;
|
||||
struct mmc_card *card;
|
||||
int testcase;
|
||||
int result;
|
||||
struct list_head tr_lst;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mmc_test_card - test information.
|
||||
* @card: card under test
|
||||
|
@ -83,6 +116,7 @@ struct mmc_test_area {
|
|||
* @buffer: transfer buffer
|
||||
* @highmem: buffer for highmem tests
|
||||
* @area: information for performance tests
|
||||
* @gr: pointer to results of current testcase
|
||||
*/
|
||||
struct mmc_test_card {
|
||||
struct mmc_card *card;
|
||||
|
@ -92,7 +126,8 @@ struct mmc_test_card {
|
|||
#ifdef CONFIG_HIGHMEM
|
||||
struct page *highmem;
|
||||
#endif
|
||||
struct mmc_test_area area;
|
||||
struct mmc_test_area area;
|
||||
struct mmc_test_general_result *gr;
|
||||
};
|
||||
|
||||
/*******************************************************************/
|
||||
|
@ -448,6 +483,30 @@ static unsigned int mmc_test_rate(uint64_t bytes, struct timespec *ts)
|
|||
return bytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save transfer results for future usage
|
||||
*/
|
||||
static void mmc_test_save_transfer_result(struct mmc_test_card *test,
|
||||
unsigned int count, unsigned int sectors, struct timespec ts,
|
||||
unsigned int rate)
|
||||
{
|
||||
struct mmc_test_transfer_result *tr;
|
||||
|
||||
if (!test->gr)
|
||||
return;
|
||||
|
||||
tr = kmalloc(sizeof(struct mmc_test_transfer_result), GFP_KERNEL);
|
||||
if (!tr)
|
||||
return;
|
||||
|
||||
tr->count = count;
|
||||
tr->sectors = sectors;
|
||||
tr->ts = ts;
|
||||
tr->rate = rate;
|
||||
|
||||
list_add_tail(&tr->link, &test->gr->tr_lst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the transfer rate.
|
||||
*/
|
||||
|
@ -466,6 +525,8 @@ static void mmc_test_print_rate(struct mmc_test_card *test, uint64_t bytes,
|
|||
mmc_hostname(test->card->host), sectors, sectors >> 1,
|
||||
(sectors == 1 ? ".5" : ""), (unsigned long)ts.tv_sec,
|
||||
(unsigned long)ts.tv_nsec, rate / 1000, rate / 1024);
|
||||
|
||||
mmc_test_save_transfer_result(test, 1, sectors, ts, rate);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -489,6 +550,8 @@ static void mmc_test_print_avg_rate(struct mmc_test_card *test, uint64_t bytes,
|
|||
sectors >> 1, (sectors == 1 ? ".5" : ""),
|
||||
(unsigned long)ts.tv_sec, (unsigned long)ts.tv_nsec,
|
||||
rate / 1000, rate / 1024);
|
||||
|
||||
mmc_test_save_transfer_result(test, count, sectors, ts, rate);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1940,6 +2003,8 @@ static const struct mmc_test_case mmc_test_cases[] = {
|
|||
|
||||
static DEFINE_MUTEX(mmc_test_lock);
|
||||
|
||||
static LIST_HEAD(mmc_test_result);
|
||||
|
||||
static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
||||
{
|
||||
int i, ret;
|
||||
|
@ -1950,6 +2015,8 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
|||
mmc_claim_host(test->card->host);
|
||||
|
||||
for (i = 0;i < ARRAY_SIZE(mmc_test_cases);i++) {
|
||||
struct mmc_test_general_result *gr;
|
||||
|
||||
if (testcase && ((i + 1) != testcase))
|
||||
continue;
|
||||
|
||||
|
@ -1968,6 +2035,25 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
|||
}
|
||||
}
|
||||
|
||||
gr = kzalloc(sizeof(struct mmc_test_general_result),
|
||||
GFP_KERNEL);
|
||||
if (gr) {
|
||||
INIT_LIST_HEAD(&gr->tr_lst);
|
||||
|
||||
/* Assign data what we know already */
|
||||
gr->card = test->card;
|
||||
gr->testcase = i;
|
||||
|
||||
/* Append container to global one */
|
||||
list_add_tail(&gr->link, &mmc_test_result);
|
||||
|
||||
/*
|
||||
* Save the pointer to created container in our private
|
||||
* structure.
|
||||
*/
|
||||
test->gr = gr;
|
||||
}
|
||||
|
||||
ret = mmc_test_cases[i].run(test);
|
||||
switch (ret) {
|
||||
case RESULT_OK:
|
||||
|
@ -1993,6 +2079,10 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
|||
mmc_hostname(test->card->host), ret);
|
||||
}
|
||||
|
||||
/* Save the result */
|
||||
if (gr)
|
||||
gr->result = ret;
|
||||
|
||||
if (mmc_test_cases[i].cleanup) {
|
||||
ret = mmc_test_cases[i].cleanup(test);
|
||||
if (ret) {
|
||||
|
@ -2010,13 +2100,80 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
|||
mmc_hostname(test->card->host));
|
||||
}
|
||||
|
||||
static void mmc_test_free_result(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_test_general_result *gr, *grs;
|
||||
|
||||
mutex_lock(&mmc_test_lock);
|
||||
|
||||
list_for_each_entry_safe(gr, grs, &mmc_test_result, link) {
|
||||
struct mmc_test_transfer_result *tr, *trs;
|
||||
|
||||
if (card && gr->card != card)
|
||||
continue;
|
||||
|
||||
list_for_each_entry_safe(tr, trs, &gr->tr_lst, link) {
|
||||
list_del(&tr->link);
|
||||
kfree(tr);
|
||||
}
|
||||
|
||||
list_del(&gr->link);
|
||||
kfree(gr);
|
||||
}
|
||||
|
||||
mutex_unlock(&mmc_test_lock);
|
||||
}
|
||||
|
||||
static ssize_t mmc_test_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
struct mmc_test_general_result *gr;
|
||||
char *p = buf;
|
||||
size_t len = PAGE_SIZE;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&mmc_test_lock);
|
||||
|
||||
list_for_each_entry(gr, &mmc_test_result, link) {
|
||||
struct mmc_test_transfer_result *tr;
|
||||
|
||||
if (gr->card != card)
|
||||
continue;
|
||||
|
||||
ret = snprintf(p, len, "Test %d: %d\n", gr->testcase + 1,
|
||||
gr->result);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
if (ret >= len) {
|
||||
ret = -ENOBUFS;
|
||||
goto err;
|
||||
}
|
||||
p += ret;
|
||||
len -= ret;
|
||||
|
||||
list_for_each_entry(tr, &gr->tr_lst, link) {
|
||||
ret = snprintf(p, len, "%u %d %lu.%09lu %u\n",
|
||||
tr->count, tr->sectors,
|
||||
(unsigned long)tr->ts.tv_sec,
|
||||
(unsigned long)tr->ts.tv_nsec,
|
||||
tr->rate);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
if (ret >= len) {
|
||||
ret = -ENOBUFS;
|
||||
goto err;
|
||||
}
|
||||
p += ret;
|
||||
len -= ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = PAGE_SIZE - len;
|
||||
err:
|
||||
mutex_unlock(&mmc_test_lock);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t mmc_test_store(struct device *dev,
|
||||
|
@ -2033,6 +2190,12 @@ static ssize_t mmc_test_store(struct device *dev,
|
|||
if (!test)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Remove all test cases associated with given card. Thus we have only
|
||||
* actual data of the last run.
|
||||
*/
|
||||
mmc_test_free_result(card);
|
||||
|
||||
test->card = card;
|
||||
|
||||
test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL);
|
||||
|
@ -2079,6 +2242,7 @@ static int mmc_test_probe(struct mmc_card *card)
|
|||
|
||||
static void mmc_test_remove(struct mmc_card *card)
|
||||
{
|
||||
mmc_test_free_result(card);
|
||||
device_remove_file(&card->dev, &dev_attr_test);
|
||||
}
|
||||
|
||||
|
@ -2097,6 +2261,9 @@ static int __init mmc_test_init(void)
|
|||
|
||||
static void __exit mmc_test_exit(void)
|
||||
{
|
||||
/* Clear stalled data if card is still plugged */
|
||||
mmc_test_free_result(NULL);
|
||||
|
||||
mmc_unregister_driver(&mmc_driver);
|
||||
}
|
||||
|
||||
|
|
Reference in a new issue