Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Adrian Hunter and committed by
David Woodhouse
57aa6b54 514087e7

+99
+99
drivers/mtd/nand/nandsim.c
··· 99 99 static char *weakpages = NULL; 100 100 static unsigned int bitflips = 0; 101 101 static char *gravepages = NULL; 102 + static unsigned int rptwear = 0; 102 103 103 104 module_param(first_id_byte, uint, 0400); 104 105 module_param(second_id_byte, uint, 0400); ··· 120 119 module_param(weakpages, charp, 0400); 121 120 module_param(bitflips, uint, 0400); 122 121 module_param(gravepages, charp, 0400); 122 + module_param(rptwear, uint, 0400); 123 123 124 124 MODULE_PARM_DESC(first_id_byte, "The fist byte returned by NAND Flash 'read ID' command (manufaturer ID)"); 125 125 MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)"); ··· 148 146 MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (defaults to 3)]" 149 147 " separated by commas e.g. 1401:2 means page 1401" 150 148 " can be read only twice before failing"); 149 + MODULE_PARM_DESC(rptwear, "Number of erases inbetween reporting wear, if not zero"); 151 150 152 151 /* The largest possible page size */ 153 152 #define NS_LARGEST_PAGE_SIZE 2048 ··· 165 162 do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warning: " args); } while(0) 166 163 #define NS_ERR(args...) \ 167 164 do { printk(KERN_ERR NS_OUTPUT_PREFIX " error: " args); } while(0) 165 + #define NS_INFO(args...) \ 166 + do { printk(KERN_INFO NS_OUTPUT_PREFIX " " args); } while(0) 168 167 169 168 /* Busy-wait delay macros (microseconds, milliseconds) */ 170 169 #define NS_UDELAY(us) \ ··· 398 393 }; 399 394 400 395 static LIST_HEAD(grave_pages); 396 + 397 + static unsigned long *erase_block_wear = NULL; 398 + static unsigned int wear_eb_count = 0; 399 + static unsigned long total_wear = 0; 400 + static unsigned int rptwear_cnt = 0; 401 401 402 402 /* MTD structure for NAND controller */ 403 403 static struct mtd_info *nsmtd; ··· 811 801 list_del(pos); 812 802 kfree(list_entry(pos, struct grave_page, list)); 813 803 } 804 + kfree(erase_block_wear); 805 + } 806 + 807 + static int setup_wear_reporting(struct mtd_info *mtd) 808 + { 809 + size_t mem; 810 + 811 + if (!rptwear) 812 + return 0; 813 + wear_eb_count = mtd->size / mtd->erasesize; 814 + mem = wear_eb_count * sizeof(unsigned long); 815 + if (mem / sizeof(unsigned long) != wear_eb_count) { 816 + NS_ERR("Too many erase blocks for wear reporting\n"); 817 + return -ENOMEM; 818 + } 819 + erase_block_wear = kzalloc(mem, GFP_KERNEL); 820 + if (!erase_block_wear) { 821 + NS_ERR("Too many erase blocks for wear reporting\n"); 822 + return -ENOMEM; 823 + } 824 + return 0; 825 + } 826 + 827 + static void update_wear(unsigned int erase_block_no) 828 + { 829 + unsigned long wmin = -1, wmax = 0, avg; 830 + unsigned long deciles[10], decile_max[10], tot = 0; 831 + unsigned int i; 832 + 833 + if (!erase_block_wear) 834 + return; 835 + total_wear += 1; 836 + if (total_wear == 0) 837 + NS_ERR("Erase counter total overflow\n"); 838 + erase_block_wear[erase_block_no] += 1; 839 + if (erase_block_wear[erase_block_no] == 0) 840 + NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no); 841 + rptwear_cnt += 1; 842 + if (rptwear_cnt < rptwear) 843 + return; 844 + rptwear_cnt = 0; 845 + /* Calc wear stats */ 846 + for (i = 0; i < wear_eb_count; ++i) { 847 + unsigned long wear = erase_block_wear[i]; 848 + if (wear < wmin) 849 + wmin = wear; 850 + if (wear > wmax) 851 + wmax = wear; 852 + tot += wear; 853 + } 854 + for (i = 0; i < 9; ++i) { 855 + deciles[i] = 0; 856 + decile_max[i] = (wmax * (i + 1) + 5) / 10; 857 + } 858 + deciles[9] = 0; 859 + decile_max[9] = wmax; 860 + for (i = 0; i < wear_eb_count; ++i) { 861 + int d; 862 + unsigned long wear = erase_block_wear[i]; 863 + for (d = 0; d < 10; ++d) 864 + if (wear <= decile_max[d]) { 865 + deciles[d] += 1; 866 + break; 867 + } 868 + } 869 + avg = tot / wear_eb_count; 870 + /* Output wear report */ 871 + NS_INFO("*** Wear Report ***\n"); 872 + NS_INFO("Total numbers of erases: %lu\n", tot); 873 + NS_INFO("Number of erase blocks: %u\n", wear_eb_count); 874 + NS_INFO("Average number of erases: %lu\n", avg); 875 + NS_INFO("Maximum number of erases: %lu\n", wmax); 876 + NS_INFO("Minimum number of erases: %lu\n", wmin); 877 + for (i = 0; i < 10; ++i) { 878 + unsigned long from = (i ? decile_max[i - 1] + 1 : 0); 879 + if (from > decile_max[i]) 880 + continue; 881 + NS_INFO("Number of ebs with erase counts from %lu to %lu : %lu\n", 882 + from, 883 + decile_max[i], 884 + deciles[i]); 885 + } 886 + NS_INFO("*** End of Wear Report ***\n"); 814 887 } 815 888 816 889 /* ··· 1360 1267 erase_sector(ns); 1361 1268 1362 1269 NS_MDELAY(erase_delay); 1270 + 1271 + if (erase_block_wear) 1272 + update_wear(erase_block_no); 1363 1273 1364 1274 if (erase_error(erase_block_no)) { 1365 1275 NS_WARN("simulating erase failure in erase block %u\n", erase_block_no); ··· 1998 1902 retval = -ENXIO; 1999 1903 goto error; 2000 1904 } 1905 + 1906 + if ((retval = setup_wear_reporting(nsmtd)) != 0) 1907 + goto err_exit; 2001 1908 2002 1909 if ((retval = init_nandsim(nsmtd)) != 0) 2003 1910 goto err_exit;