diff options
author | Adrian Hunter <ext-adrian.hunter@nokia.com> | 2007-03-19 06:40:41 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2007-04-17 13:55:29 -0400 |
commit | 57aa6b545f6f772dd317ccd29bdada999b16a13d (patch) | |
tree | efed75ea30b11cec3deaeb8025381f68a8d1ff42 | |
parent | 514087e74fb401a6621e8c836f4eaab87c269f24 (diff) |
[MTD] nandsim: Enhance nandsim optionally to report wear information
A new module parameter 'rptwear' specifies how many erases between
reporting wear information. Zero means never.
Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com>
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
-rw-r--r-- | drivers/mtd/nand/nandsim.c | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 05b42077d22f..1a44ef63c8d1 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c | |||
@@ -99,6 +99,7 @@ static char *weakblocks = NULL; | |||
99 | static char *weakpages = NULL; | 99 | static char *weakpages = NULL; |
100 | static unsigned int bitflips = 0; | 100 | static unsigned int bitflips = 0; |
101 | static char *gravepages = NULL; | 101 | static char *gravepages = NULL; |
102 | static unsigned int rptwear = 0; | ||
102 | 103 | ||
103 | module_param(first_id_byte, uint, 0400); | 104 | module_param(first_id_byte, uint, 0400); |
104 | module_param(second_id_byte, uint, 0400); | 105 | module_param(second_id_byte, uint, 0400); |
@@ -119,6 +120,7 @@ module_param(weakblocks, charp, 0400); | |||
119 | module_param(weakpages, charp, 0400); | 120 | module_param(weakpages, charp, 0400); |
120 | module_param(bitflips, uint, 0400); | 121 | module_param(bitflips, uint, 0400); |
121 | module_param(gravepages, charp, 0400); | 122 | module_param(gravepages, charp, 0400); |
123 | module_param(rptwear, uint, 0400); | ||
122 | 124 | ||
123 | MODULE_PARM_DESC(first_id_byte, "The fist byte returned by NAND Flash 'read ID' command (manufaturer ID)"); | 125 | MODULE_PARM_DESC(first_id_byte, "The fist byte returned by NAND Flash 'read ID' command (manufaturer ID)"); |
124 | MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)"); | 126 | MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)"); |
@@ -146,6 +148,7 @@ MODULE_PARM_DESC(bitflips, "Maximum number of random bit flips per page (z | |||
146 | MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (defaults to 3)]" | 148 | MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (defaults to 3)]" |
147 | " separated by commas e.g. 1401:2 means page 1401" | 149 | " separated by commas e.g. 1401:2 means page 1401" |
148 | " can be read only twice before failing"); | 150 | " can be read only twice before failing"); |
151 | MODULE_PARM_DESC(rptwear, "Number of erases inbetween reporting wear, if not zero"); | ||
149 | 152 | ||
150 | /* The largest possible page size */ | 153 | /* The largest possible page size */ |
151 | #define NS_LARGEST_PAGE_SIZE 2048 | 154 | #define NS_LARGEST_PAGE_SIZE 2048 |
@@ -162,6 +165,8 @@ MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (default | |||
162 | do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warning: " args); } while(0) | 165 | do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warning: " args); } while(0) |
163 | #define NS_ERR(args...) \ | 166 | #define NS_ERR(args...) \ |
164 | do { printk(KERN_ERR NS_OUTPUT_PREFIX " error: " args); } while(0) | 167 | do { printk(KERN_ERR NS_OUTPUT_PREFIX " error: " args); } while(0) |
168 | #define NS_INFO(args...) \ | ||
169 | do { printk(KERN_INFO NS_OUTPUT_PREFIX " " args); } while(0) | ||
165 | 170 | ||
166 | /* Busy-wait delay macros (microseconds, milliseconds) */ | 171 | /* Busy-wait delay macros (microseconds, milliseconds) */ |
167 | #define NS_UDELAY(us) \ | 172 | #define NS_UDELAY(us) \ |
@@ -394,6 +399,11 @@ struct grave_page { | |||
394 | 399 | ||
395 | static LIST_HEAD(grave_pages); | 400 | static LIST_HEAD(grave_pages); |
396 | 401 | ||
402 | static unsigned long *erase_block_wear = NULL; | ||
403 | static unsigned int wear_eb_count = 0; | ||
404 | static unsigned long total_wear = 0; | ||
405 | static unsigned int rptwear_cnt = 0; | ||
406 | |||
397 | /* MTD structure for NAND controller */ | 407 | /* MTD structure for NAND controller */ |
398 | static struct mtd_info *nsmtd; | 408 | static struct mtd_info *nsmtd; |
399 | 409 | ||
@@ -801,6 +811,89 @@ static void free_lists(void) | |||
801 | list_del(pos); | 811 | list_del(pos); |
802 | kfree(list_entry(pos, struct grave_page, list)); | 812 | kfree(list_entry(pos, struct grave_page, list)); |
803 | } | 813 | } |
814 | kfree(erase_block_wear); | ||
815 | } | ||
816 | |||
817 | static int setup_wear_reporting(struct mtd_info *mtd) | ||
818 | { | ||
819 | size_t mem; | ||
820 | |||
821 | if (!rptwear) | ||
822 | return 0; | ||
823 | wear_eb_count = mtd->size / mtd->erasesize; | ||
824 | mem = wear_eb_count * sizeof(unsigned long); | ||
825 | if (mem / sizeof(unsigned long) != wear_eb_count) { | ||
826 | NS_ERR("Too many erase blocks for wear reporting\n"); | ||
827 | return -ENOMEM; | ||
828 | } | ||
829 | erase_block_wear = kzalloc(mem, GFP_KERNEL); | ||
830 | if (!erase_block_wear) { | ||
831 | NS_ERR("Too many erase blocks for wear reporting\n"); | ||
832 | return -ENOMEM; | ||
833 | } | ||
834 | return 0; | ||
835 | } | ||
836 | |||
837 | static void update_wear(unsigned int erase_block_no) | ||
838 | { | ||
839 | unsigned long wmin = -1, wmax = 0, avg; | ||
840 | unsigned long deciles[10], decile_max[10], tot = 0; | ||
841 | unsigned int i; | ||
842 | |||
843 | if (!erase_block_wear) | ||
844 | return; | ||
845 | total_wear += 1; | ||
846 | if (total_wear == 0) | ||
847 | NS_ERR("Erase counter total overflow\n"); | ||
848 | erase_block_wear[erase_block_no] += 1; | ||
849 | if (erase_block_wear[erase_block_no] == 0) | ||
850 | NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no); | ||
851 | rptwear_cnt += 1; | ||
852 | if (rptwear_cnt < rptwear) | ||
853 | return; | ||
854 | rptwear_cnt = 0; | ||
855 | /* Calc wear stats */ | ||
856 | for (i = 0; i < wear_eb_count; ++i) { | ||
857 | unsigned long wear = erase_block_wear[i]; | ||
858 | if (wear < wmin) | ||
859 | wmin = wear; | ||
860 | if (wear > wmax) | ||
861 | wmax = wear; | ||
862 | tot += wear; | ||
863 | } | ||
864 | for (i = 0; i < 9; ++i) { | ||
865 | deciles[i] = 0; | ||
866 | decile_max[i] = (wmax * (i + 1) + 5) / 10; | ||
867 | } | ||
868 | deciles[9] = 0; | ||
869 | decile_max[9] = wmax; | ||
870 | for (i = 0; i < wear_eb_count; ++i) { | ||
871 | int d; | ||
872 | unsigned long wear = erase_block_wear[i]; | ||
873 | for (d = 0; d < 10; ++d) | ||
874 | if (wear <= decile_max[d]) { | ||
875 | deciles[d] += 1; | ||
876 | break; | ||
877 | } | ||
878 | } | ||
879 | avg = tot / wear_eb_count; | ||
880 | /* Output wear report */ | ||
881 | NS_INFO("*** Wear Report ***\n"); | ||
882 | NS_INFO("Total numbers of erases: %lu\n", tot); | ||
883 | NS_INFO("Number of erase blocks: %u\n", wear_eb_count); | ||
884 | NS_INFO("Average number of erases: %lu\n", avg); | ||
885 | NS_INFO("Maximum number of erases: %lu\n", wmax); | ||
886 | NS_INFO("Minimum number of erases: %lu\n", wmin); | ||
887 | for (i = 0; i < 10; ++i) { | ||
888 | unsigned long from = (i ? decile_max[i - 1] + 1 : 0); | ||
889 | if (from > decile_max[i]) | ||
890 | continue; | ||
891 | NS_INFO("Number of ebs with erase counts from %lu to %lu : %lu\n", | ||
892 | from, | ||
893 | decile_max[i], | ||
894 | deciles[i]); | ||
895 | } | ||
896 | NS_INFO("*** End of Wear Report ***\n"); | ||
804 | } | 897 | } |
805 | 898 | ||
806 | /* | 899 | /* |
@@ -1268,6 +1361,9 @@ static int do_state_action(struct nandsim *ns, uint32_t action) | |||
1268 | 1361 | ||
1269 | NS_MDELAY(erase_delay); | 1362 | NS_MDELAY(erase_delay); |
1270 | 1363 | ||
1364 | if (erase_block_wear) | ||
1365 | update_wear(erase_block_no); | ||
1366 | |||
1271 | if (erase_error(erase_block_no)) { | 1367 | if (erase_error(erase_block_no)) { |
1272 | NS_WARN("simulating erase failure in erase block %u\n", erase_block_no); | 1368 | NS_WARN("simulating erase failure in erase block %u\n", erase_block_no); |
1273 | return -1; | 1369 | return -1; |
@@ -1903,6 +1999,9 @@ static int __init ns_init_module(void) | |||
1903 | goto error; | 1999 | goto error; |
1904 | } | 2000 | } |
1905 | 2001 | ||
2002 | if ((retval = setup_wear_reporting(nsmtd)) != 0) | ||
2003 | goto err_exit; | ||
2004 | |||
1906 | if ((retval = init_nandsim(nsmtd)) != 0) | 2005 | if ((retval = init_nandsim(nsmtd)) != 0) |
1907 | goto err_exit; | 2006 | goto err_exit; |
1908 | 2007 | ||