diff options
author | Andy Shevchenko <andy.shevchenko@gmail.com> | 2010-09-01 02:26:47 -0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2010-10-23 09:11:13 -0400 |
commit | 3183aa1534de4e98ffb0527d4f2be7ac9f019a4e (patch) | |
tree | b38ce66b22f1b6385ad2387278a259a46a0dccd4 /drivers/mmc/card | |
parent | 5c25aee5364550d7fa6314886370e76cda18d7e2 (diff) |
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>
Diffstat (limited to 'drivers/mmc/card')
-rw-r--r-- | drivers/mmc/card/mmc_test.c | 171 |
1 files changed, 169 insertions, 2 deletions
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index 2888fdccd7ad..0c8b5685d4b7 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c | |||
@@ -17,6 +17,7 @@ | |||
17 | 17 | ||
18 | #include <linux/scatterlist.h> | 18 | #include <linux/scatterlist.h> |
19 | #include <linux/swap.h> /* For nr_free_buffer_pages() */ | 19 | #include <linux/swap.h> /* For nr_free_buffer_pages() */ |
20 | #include <linux/list.h> | ||
20 | 21 | ||
21 | #define RESULT_OK 0 | 22 | #define RESULT_OK 0 |
22 | #define RESULT_FAIL 1 | 23 | #define RESULT_FAIL 1 |
@@ -77,12 +78,45 @@ struct mmc_test_area { | |||
77 | }; | 78 | }; |
78 | 79 | ||
79 | /** | 80 | /** |
81 | * struct mmc_test_transfer_result - transfer results for performance tests. | ||
82 | * @link: double-linked list | ||
83 | * @count: amount of group of sectors to check | ||
84 | * @sectors: amount of sectors to check in one group | ||
85 | * @ts: time values of transfer | ||
86 | * @rate: calculated transfer rate | ||
87 | */ | ||
88 | struct mmc_test_transfer_result { | ||
89 | struct list_head link; | ||
90 | unsigned int count; | ||
91 | unsigned int sectors; | ||
92 | struct timespec ts; | ||
93 | unsigned int rate; | ||
94 | }; | ||
95 | |||
96 | /** | ||
97 | * struct mmc_test_general_result - results for tests. | ||
98 | * @link: double-linked list | ||
99 | * @card: card under test | ||
100 | * @testcase: number of test case | ||
101 | * @result: result of test run | ||
102 | * @tr_lst: transfer measurements if any as mmc_test_transfer_result | ||
103 | */ | ||
104 | struct mmc_test_general_result { | ||
105 | struct list_head link; | ||
106 | struct mmc_card *card; | ||
107 | int testcase; | ||
108 | int result; | ||
109 | struct list_head tr_lst; | ||
110 | }; | ||
111 | |||
112 | /** | ||
80 | * struct mmc_test_card - test information. | 113 | * struct mmc_test_card - test information. |
81 | * @card: card under test | 114 | * @card: card under test |
82 | * @scratch: transfer buffer | 115 | * @scratch: transfer buffer |
83 | * @buffer: transfer buffer | 116 | * @buffer: transfer buffer |
84 | * @highmem: buffer for highmem tests | 117 | * @highmem: buffer for highmem tests |
85 | * @area: information for performance tests | 118 | * @area: information for performance tests |
119 | * @gr: pointer to results of current testcase | ||
86 | */ | 120 | */ |
87 | struct mmc_test_card { | 121 | struct mmc_test_card { |
88 | struct mmc_card *card; | 122 | struct mmc_card *card; |
@@ -92,7 +126,8 @@ struct mmc_test_card { | |||
92 | #ifdef CONFIG_HIGHMEM | 126 | #ifdef CONFIG_HIGHMEM |
93 | struct page *highmem; | 127 | struct page *highmem; |
94 | #endif | 128 | #endif |
95 | struct mmc_test_area area; | 129 | struct mmc_test_area area; |
130 | struct mmc_test_general_result *gr; | ||
96 | }; | 131 | }; |
97 | 132 | ||
98 | /*******************************************************************/ | 133 | /*******************************************************************/ |
@@ -449,6 +484,30 @@ static unsigned int mmc_test_rate(uint64_t bytes, struct timespec *ts) | |||
449 | } | 484 | } |
450 | 485 | ||
451 | /* | 486 | /* |
487 | * Save transfer results for future usage | ||
488 | */ | ||
489 | static void mmc_test_save_transfer_result(struct mmc_test_card *test, | ||
490 | unsigned int count, unsigned int sectors, struct timespec ts, | ||
491 | unsigned int rate) | ||
492 | { | ||
493 | struct mmc_test_transfer_result *tr; | ||
494 | |||
495 | if (!test->gr) | ||
496 | return; | ||
497 | |||
498 | tr = kmalloc(sizeof(struct mmc_test_transfer_result), GFP_KERNEL); | ||
499 | if (!tr) | ||
500 | return; | ||
501 | |||
502 | tr->count = count; | ||
503 | tr->sectors = sectors; | ||
504 | tr->ts = ts; | ||
505 | tr->rate = rate; | ||
506 | |||
507 | list_add_tail(&tr->link, &test->gr->tr_lst); | ||
508 | } | ||
509 | |||
510 | /* | ||
452 | * Print the transfer rate. | 511 | * Print the transfer rate. |
453 | */ | 512 | */ |
454 | static void mmc_test_print_rate(struct mmc_test_card *test, uint64_t bytes, | 513 | static void mmc_test_print_rate(struct mmc_test_card *test, uint64_t bytes, |
@@ -466,6 +525,8 @@ static void mmc_test_print_rate(struct mmc_test_card *test, uint64_t bytes, | |||
466 | mmc_hostname(test->card->host), sectors, sectors >> 1, | 525 | mmc_hostname(test->card->host), sectors, sectors >> 1, |
467 | (sectors == 1 ? ".5" : ""), (unsigned long)ts.tv_sec, | 526 | (sectors == 1 ? ".5" : ""), (unsigned long)ts.tv_sec, |
468 | (unsigned long)ts.tv_nsec, rate / 1000, rate / 1024); | 527 | (unsigned long)ts.tv_nsec, rate / 1000, rate / 1024); |
528 | |||
529 | mmc_test_save_transfer_result(test, 1, sectors, ts, rate); | ||
469 | } | 530 | } |
470 | 531 | ||
471 | /* | 532 | /* |
@@ -489,6 +550,8 @@ static void mmc_test_print_avg_rate(struct mmc_test_card *test, uint64_t bytes, | |||
489 | sectors >> 1, (sectors == 1 ? ".5" : ""), | 550 | sectors >> 1, (sectors == 1 ? ".5" : ""), |
490 | (unsigned long)ts.tv_sec, (unsigned long)ts.tv_nsec, | 551 | (unsigned long)ts.tv_sec, (unsigned long)ts.tv_nsec, |
491 | rate / 1000, rate / 1024); | 552 | rate / 1000, rate / 1024); |
553 | |||
554 | mmc_test_save_transfer_result(test, count, sectors, ts, rate); | ||
492 | } | 555 | } |
493 | 556 | ||
494 | /* | 557 | /* |
@@ -1940,6 +2003,8 @@ static const struct mmc_test_case mmc_test_cases[] = { | |||
1940 | 2003 | ||
1941 | static DEFINE_MUTEX(mmc_test_lock); | 2004 | static DEFINE_MUTEX(mmc_test_lock); |
1942 | 2005 | ||
2006 | static LIST_HEAD(mmc_test_result); | ||
2007 | |||
1943 | static void mmc_test_run(struct mmc_test_card *test, int testcase) | 2008 | static void mmc_test_run(struct mmc_test_card *test, int testcase) |
1944 | { | 2009 | { |
1945 | int i, ret; | 2010 | int i, ret; |
@@ -1950,6 +2015,8 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase) | |||
1950 | mmc_claim_host(test->card->host); | 2015 | mmc_claim_host(test->card->host); |
1951 | 2016 | ||
1952 | for (i = 0;i < ARRAY_SIZE(mmc_test_cases);i++) { | 2017 | for (i = 0;i < ARRAY_SIZE(mmc_test_cases);i++) { |
2018 | struct mmc_test_general_result *gr; | ||
2019 | |||
1953 | if (testcase && ((i + 1) != testcase)) | 2020 | if (testcase && ((i + 1) != testcase)) |
1954 | continue; | 2021 | continue; |
1955 | 2022 | ||
@@ -1968,6 +2035,25 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase) | |||
1968 | } | 2035 | } |
1969 | } | 2036 | } |
1970 | 2037 | ||
2038 | gr = kzalloc(sizeof(struct mmc_test_general_result), | ||
2039 | GFP_KERNEL); | ||
2040 | if (gr) { | ||
2041 | INIT_LIST_HEAD(&gr->tr_lst); | ||
2042 | |||
2043 | /* Assign data what we know already */ | ||
2044 | gr->card = test->card; | ||
2045 | gr->testcase = i; | ||
2046 | |||
2047 | /* Append container to global one */ | ||
2048 | list_add_tail(&gr->link, &mmc_test_result); | ||
2049 | |||
2050 | /* | ||
2051 | * Save the pointer to created container in our private | ||
2052 | * structure. | ||
2053 | */ | ||
2054 | test->gr = gr; | ||
2055 | } | ||
2056 | |||
1971 | ret = mmc_test_cases[i].run(test); | 2057 | ret = mmc_test_cases[i].run(test); |
1972 | switch (ret) { | 2058 | switch (ret) { |
1973 | case RESULT_OK: | 2059 | case RESULT_OK: |
@@ -1993,6 +2079,10 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase) | |||
1993 | mmc_hostname(test->card->host), ret); | 2079 | mmc_hostname(test->card->host), ret); |
1994 | } | 2080 | } |
1995 | 2081 | ||
2082 | /* Save the result */ | ||
2083 | if (gr) | ||
2084 | gr->result = ret; | ||
2085 | |||
1996 | if (mmc_test_cases[i].cleanup) { | 2086 | if (mmc_test_cases[i].cleanup) { |
1997 | ret = mmc_test_cases[i].cleanup(test); | 2087 | ret = mmc_test_cases[i].cleanup(test); |
1998 | if (ret) { | 2088 | if (ret) { |
@@ -2010,13 +2100,80 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase) | |||
2010 | mmc_hostname(test->card->host)); | 2100 | mmc_hostname(test->card->host)); |
2011 | } | 2101 | } |
2012 | 2102 | ||
2103 | static void mmc_test_free_result(struct mmc_card *card) | ||
2104 | { | ||
2105 | struct mmc_test_general_result *gr, *grs; | ||
2106 | |||
2107 | mutex_lock(&mmc_test_lock); | ||
2108 | |||
2109 | list_for_each_entry_safe(gr, grs, &mmc_test_result, link) { | ||
2110 | struct mmc_test_transfer_result *tr, *trs; | ||
2111 | |||
2112 | if (card && gr->card != card) | ||
2113 | continue; | ||
2114 | |||
2115 | list_for_each_entry_safe(tr, trs, &gr->tr_lst, link) { | ||
2116 | list_del(&tr->link); | ||
2117 | kfree(tr); | ||
2118 | } | ||
2119 | |||
2120 | list_del(&gr->link); | ||
2121 | kfree(gr); | ||
2122 | } | ||
2123 | |||
2124 | mutex_unlock(&mmc_test_lock); | ||
2125 | } | ||
2126 | |||
2013 | static ssize_t mmc_test_show(struct device *dev, | 2127 | static ssize_t mmc_test_show(struct device *dev, |
2014 | struct device_attribute *attr, char *buf) | 2128 | struct device_attribute *attr, char *buf) |
2015 | { | 2129 | { |
2130 | struct mmc_card *card = mmc_dev_to_card(dev); | ||
2131 | struct mmc_test_general_result *gr; | ||
2132 | char *p = buf; | ||
2133 | size_t len = PAGE_SIZE; | ||
2134 | int ret; | ||
2135 | |||
2016 | mutex_lock(&mmc_test_lock); | 2136 | mutex_lock(&mmc_test_lock); |
2137 | |||
2138 | list_for_each_entry(gr, &mmc_test_result, link) { | ||
2139 | struct mmc_test_transfer_result *tr; | ||
2140 | |||
2141 | if (gr->card != card) | ||
2142 | continue; | ||
2143 | |||
2144 | ret = snprintf(p, len, "Test %d: %d\n", gr->testcase + 1, | ||
2145 | gr->result); | ||
2146 | if (ret < 0) | ||
2147 | goto err; | ||
2148 | if (ret >= len) { | ||
2149 | ret = -ENOBUFS; | ||
2150 | goto err; | ||
2151 | } | ||
2152 | p += ret; | ||
2153 | len -= ret; | ||
2154 | |||
2155 | list_for_each_entry(tr, &gr->tr_lst, link) { | ||
2156 | ret = snprintf(p, len, "%u %d %lu.%09lu %u\n", | ||
2157 | tr->count, tr->sectors, | ||
2158 | (unsigned long)tr->ts.tv_sec, | ||
2159 | (unsigned long)tr->ts.tv_nsec, | ||
2160 | tr->rate); | ||
2161 | if (ret < 0) | ||
2162 | goto err; | ||
2163 | if (ret >= len) { | ||
2164 | ret = -ENOBUFS; | ||
2165 | goto err; | ||
2166 | } | ||
2167 | p += ret; | ||
2168 | len -= ret; | ||
2169 | } | ||
2170 | } | ||
2171 | |||
2172 | ret = PAGE_SIZE - len; | ||
2173 | err: | ||
2017 | mutex_unlock(&mmc_test_lock); | 2174 | mutex_unlock(&mmc_test_lock); |
2018 | 2175 | ||
2019 | return 0; | 2176 | return ret; |
2020 | } | 2177 | } |
2021 | 2178 | ||
2022 | static ssize_t mmc_test_store(struct device *dev, | 2179 | static ssize_t mmc_test_store(struct device *dev, |
@@ -2033,6 +2190,12 @@ static ssize_t mmc_test_store(struct device *dev, | |||
2033 | if (!test) | 2190 | if (!test) |
2034 | return -ENOMEM; | 2191 | return -ENOMEM; |
2035 | 2192 | ||
2193 | /* | ||
2194 | * Remove all test cases associated with given card. Thus we have only | ||
2195 | * actual data of the last run. | ||
2196 | */ | ||
2197 | mmc_test_free_result(card); | ||
2198 | |||
2036 | test->card = card; | 2199 | test->card = card; |
2037 | 2200 | ||
2038 | test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL); | 2201 | test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL); |
@@ -2079,6 +2242,7 @@ static int mmc_test_probe(struct mmc_card *card) | |||
2079 | 2242 | ||
2080 | static void mmc_test_remove(struct mmc_card *card) | 2243 | static void mmc_test_remove(struct mmc_card *card) |
2081 | { | 2244 | { |
2245 | mmc_test_free_result(card); | ||
2082 | device_remove_file(&card->dev, &dev_attr_test); | 2246 | device_remove_file(&card->dev, &dev_attr_test); |
2083 | } | 2247 | } |
2084 | 2248 | ||
@@ -2097,6 +2261,9 @@ static int __init mmc_test_init(void) | |||
2097 | 2261 | ||
2098 | static void __exit mmc_test_exit(void) | 2262 | static void __exit mmc_test_exit(void) |
2099 | { | 2263 | { |
2264 | /* Clear stalled data if card is still plugged */ | ||
2265 | mmc_test_free_result(NULL); | ||
2266 | |||
2100 | mmc_unregister_driver(&mmc_driver); | 2267 | mmc_unregister_driver(&mmc_driver); |
2101 | } | 2268 | } |
2102 | 2269 | ||