aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEzequiel Garcia <elezegarcia@gmail.com>2012-12-03 08:31:40 -0500
committerArtem Bityutskiy <artem.bityutskiy@linux.intel.com>2012-12-10 09:42:42 -0500
commit5346c27c5fed6b30aff35e1fcb595625097e646c (patch)
treec10264204d119f2ebee24ab858009302c3b6e2f9
parente58a66d84bceba314b03e37ec0764b9b1b9227d0 (diff)
mtd: nandsim: Introduce debugfs infrastructure
It's more user friendly to report debug information and statistics through debugfs, than to use printing facilites. This patch introduces a very minimal debugfs infrastructure and moves eraseblock wear report to an entry located at: /sys/kernel/debug/nandsim/wear_report This means we can remove rptwear option and just let the user get the wear report when we needs to. Signed-off-by: Ezequiel Garcia <elezegarcia@gmail.com> Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
-rw-r--r--drivers/mtd/nand/nandsim.c186
1 files changed, 130 insertions, 56 deletions
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index a932c485eb0..2ab827428f1 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -42,6 +42,8 @@
42#include <linux/sched.h> 42#include <linux/sched.h>
43#include <linux/fs.h> 43#include <linux/fs.h>
44#include <linux/pagemap.h> 44#include <linux/pagemap.h>
45#include <linux/seq_file.h>
46#include <linux/debugfs.h>
45 47
46/* Default simulator parameters values */ 48/* Default simulator parameters values */
47#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ 49#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \
@@ -105,7 +107,6 @@ static char *weakblocks = NULL;
105static char *weakpages = NULL; 107static char *weakpages = NULL;
106static unsigned int bitflips = 0; 108static unsigned int bitflips = 0;
107static char *gravepages = NULL; 109static char *gravepages = NULL;
108static unsigned int rptwear = 0;
109static unsigned int overridesize = 0; 110static unsigned int overridesize = 0;
110static char *cache_file = NULL; 111static char *cache_file = NULL;
111static unsigned int bbt; 112static unsigned int bbt;
@@ -130,7 +131,6 @@ module_param(weakblocks, charp, 0400);
130module_param(weakpages, charp, 0400); 131module_param(weakpages, charp, 0400);
131module_param(bitflips, uint, 0400); 132module_param(bitflips, uint, 0400);
132module_param(gravepages, charp, 0400); 133module_param(gravepages, charp, 0400);
133module_param(rptwear, uint, 0400);
134module_param(overridesize, uint, 0400); 134module_param(overridesize, uint, 0400);
135module_param(cache_file, charp, 0400); 135module_param(cache_file, charp, 0400);
136module_param(bbt, uint, 0400); 136module_param(bbt, uint, 0400);
@@ -162,7 +162,6 @@ MODULE_PARM_DESC(bitflips, "Maximum number of random bit flips per page (z
162MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (defaults to 3)]" 162MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (defaults to 3)]"
163 " separated by commas e.g. 1401:2 means page 1401" 163 " separated by commas e.g. 1401:2 means page 1401"
164 " can be read only twice before failing"); 164 " can be read only twice before failing");
165MODULE_PARM_DESC(rptwear, "Number of erases between reporting wear, if not zero");
166MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. " 165MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. "
167 "The size is specified in erase blocks and as the exponent of a power of two" 166 "The size is specified in erase blocks and as the exponent of a power of two"
168 " e.g. 5 means a size of 32 erase blocks"); 167 " e.g. 5 means a size of 32 erase blocks");
@@ -286,6 +285,11 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should "
286/* Maximum page cache pages needed to read or write a NAND page to the cache_file */ 285/* Maximum page cache pages needed to read or write a NAND page to the cache_file */
287#define NS_MAX_HELD_PAGES 16 286#define NS_MAX_HELD_PAGES 16
288 287
288struct nandsim_debug_info {
289 struct dentry *dfs_root;
290 struct dentry *dfs_wear_report;
291};
292
289/* 293/*
290 * A union to represent flash memory contents and flash buffer. 294 * A union to represent flash memory contents and flash buffer.
291 */ 295 */
@@ -365,6 +369,8 @@ struct nandsim {
365 void *file_buf; 369 void *file_buf;
366 struct page *held_pages[NS_MAX_HELD_PAGES]; 370 struct page *held_pages[NS_MAX_HELD_PAGES];
367 int held_cnt; 371 int held_cnt;
372
373 struct nandsim_debug_info dbg;
368}; 374};
369 375
370/* 376/*
@@ -442,11 +448,123 @@ static LIST_HEAD(grave_pages);
442static unsigned long *erase_block_wear = NULL; 448static unsigned long *erase_block_wear = NULL;
443static unsigned int wear_eb_count = 0; 449static unsigned int wear_eb_count = 0;
444static unsigned long total_wear = 0; 450static unsigned long total_wear = 0;
445static unsigned int rptwear_cnt = 0;
446 451
447/* MTD structure for NAND controller */ 452/* MTD structure for NAND controller */
448static struct mtd_info *nsmtd; 453static struct mtd_info *nsmtd;
449 454
455static int nandsim_debugfs_show(struct seq_file *m, void *private)
456{
457 unsigned long wmin = -1, wmax = 0, avg;
458 unsigned long deciles[10], decile_max[10], tot = 0;
459 unsigned int i;
460
461 /* Calc wear stats */
462 for (i = 0; i < wear_eb_count; ++i) {
463 unsigned long wear = erase_block_wear[i];
464 if (wear < wmin)
465 wmin = wear;
466 if (wear > wmax)
467 wmax = wear;
468 tot += wear;
469 }
470
471 for (i = 0; i < 9; ++i) {
472 deciles[i] = 0;
473 decile_max[i] = (wmax * (i + 1) + 5) / 10;
474 }
475 deciles[9] = 0;
476 decile_max[9] = wmax;
477 for (i = 0; i < wear_eb_count; ++i) {
478 int d;
479 unsigned long wear = erase_block_wear[i];
480 for (d = 0; d < 10; ++d)
481 if (wear <= decile_max[d]) {
482 deciles[d] += 1;
483 break;
484 }
485 }
486 avg = tot / wear_eb_count;
487
488 /* Output wear report */
489 seq_printf(m, "Total numbers of erases: %lu\n", tot);
490 seq_printf(m, "Number of erase blocks: %u\n", wear_eb_count);
491 seq_printf(m, "Average number of erases: %lu\n", avg);
492 seq_printf(m, "Maximum number of erases: %lu\n", wmax);
493 seq_printf(m, "Minimum number of erases: %lu\n", wmin);
494 for (i = 0; i < 10; ++i) {
495 unsigned long from = (i ? decile_max[i - 1] + 1 : 0);
496 if (from > decile_max[i])
497 continue;
498 seq_printf(m, "Number of ebs with erase counts from %lu to %lu : %lu\n",
499 from,
500 decile_max[i],
501 deciles[i]);
502 }
503
504 return 0;
505}
506
507static int nandsim_debugfs_open(struct inode *inode, struct file *file)
508{
509 return single_open(file, nandsim_debugfs_show, inode->i_private);
510}
511
512static const struct file_operations dfs_fops = {
513 .open = nandsim_debugfs_open,
514 .read = seq_read,
515 .llseek = seq_lseek,
516 .release = single_release,
517};
518
519/**
520 * nandsim_debugfs_create - initialize debugfs
521 * @dev: nandsim device description object
522 *
523 * This function creates all debugfs files for UBI device @ubi. Returns zero in
524 * case of success and a negative error code in case of failure.
525 */
526static int nandsim_debugfs_create(struct nandsim *dev)
527{
528 struct nandsim_debug_info *dbg = &dev->dbg;
529 struct dentry *dent;
530 int err;
531
532 if (!IS_ENABLED(CONFIG_DEBUG_FS))
533 return 0;
534
535 dent = debugfs_create_dir("nandsim", NULL);
536 if (IS_ERR_OR_NULL(dent)) {
537 int err = dent ? -ENODEV : PTR_ERR(dent);
538
539 NS_ERR("cannot create \"nandsim\" debugfs directory, err %d\n",
540 err);
541 return err;
542 }
543 dbg->dfs_root = dent;
544
545 dent = debugfs_create_file("wear_report", S_IRUSR,
546 dbg->dfs_root, dev, &dfs_fops);
547 if (IS_ERR_OR_NULL(dent))
548 goto out_remove;
549 dbg->dfs_wear_report = dent;
550
551 return 0;
552
553out_remove:
554 debugfs_remove_recursive(dbg->dfs_root);
555 err = dent ? PTR_ERR(dent) : -ENODEV;
556 return err;
557}
558
559/**
560 * nandsim_debugfs_remove - destroy all debugfs files
561 */
562static void nandsim_debugfs_remove(struct nandsim *ns)
563{
564 if (IS_ENABLED(CONFIG_DEBUG_FS))
565 debugfs_remove_recursive(ns->dbg.dfs_root);
566}
567
450/* 568/*
451 * Allocate array of page pointers, create slab allocation for an array 569 * Allocate array of page pointers, create slab allocation for an array
452 * and initialize the array by NULL pointers. 570 * and initialize the array by NULL pointers.
@@ -911,8 +1029,6 @@ static int setup_wear_reporting(struct mtd_info *mtd)
911{ 1029{
912 size_t mem; 1030 size_t mem;
913 1031
914 if (!rptwear)
915 return 0;
916 wear_eb_count = div_u64(mtd->size, mtd->erasesize); 1032 wear_eb_count = div_u64(mtd->size, mtd->erasesize);
917 mem = wear_eb_count * sizeof(unsigned long); 1033 mem = wear_eb_count * sizeof(unsigned long);
918 if (mem / sizeof(unsigned long) != wear_eb_count) { 1034 if (mem / sizeof(unsigned long) != wear_eb_count) {
@@ -929,64 +1045,18 @@ static int setup_wear_reporting(struct mtd_info *mtd)
929 1045
930static void update_wear(unsigned int erase_block_no) 1046static void update_wear(unsigned int erase_block_no)
931{ 1047{
932 unsigned long wmin = -1, wmax = 0, avg;
933 unsigned long deciles[10], decile_max[10], tot = 0;
934 unsigned int i;
935
936 if (!erase_block_wear) 1048 if (!erase_block_wear)
937 return; 1049 return;
938 total_wear += 1; 1050 total_wear += 1;
1051 /*
1052 * TODO: Notify this through a debugfs entry,
1053 * instead of showing an error message.
1054 */
939 if (total_wear == 0) 1055 if (total_wear == 0)
940 NS_ERR("Erase counter total overflow\n"); 1056 NS_ERR("Erase counter total overflow\n");
941 erase_block_wear[erase_block_no] += 1; 1057 erase_block_wear[erase_block_no] += 1;
942 if (erase_block_wear[erase_block_no] == 0) 1058 if (erase_block_wear[erase_block_no] == 0)
943 NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no); 1059 NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no);
944 rptwear_cnt += 1;
945 if (rptwear_cnt < rptwear)
946 return;
947 rptwear_cnt = 0;
948 /* Calc wear stats */
949 for (i = 0; i < wear_eb_count; ++i) {
950 unsigned long wear = erase_block_wear[i];
951 if (wear < wmin)
952 wmin = wear;
953 if (wear > wmax)
954 wmax = wear;
955 tot += wear;
956 }
957 for (i = 0; i < 9; ++i) {
958 deciles[i] = 0;
959 decile_max[i] = (wmax * (i + 1) + 5) / 10;
960 }
961 deciles[9] = 0;
962 decile_max[9] = wmax;
963 for (i = 0; i < wear_eb_count; ++i) {
964 int d;
965 unsigned long wear = erase_block_wear[i];
966 for (d = 0; d < 10; ++d)
967 if (wear <= decile_max[d]) {
968 deciles[d] += 1;
969 break;
970 }
971 }
972 avg = tot / wear_eb_count;
973 /* Output wear report */
974 NS_INFO("*** Wear Report ***\n");
975 NS_INFO("Total numbers of erases: %lu\n", tot);
976 NS_INFO("Number of erase blocks: %u\n", wear_eb_count);
977 NS_INFO("Average number of erases: %lu\n", avg);
978 NS_INFO("Maximum number of erases: %lu\n", wmax);
979 NS_INFO("Minimum number of erases: %lu\n", wmin);
980 for (i = 0; i < 10; ++i) {
981 unsigned long from = (i ? decile_max[i - 1] + 1 : 0);
982 if (from > decile_max[i])
983 continue;
984 NS_INFO("Number of ebs with erase counts from %lu to %lu : %lu\n",
985 from,
986 decile_max[i],
987 deciles[i]);
988 }
989 NS_INFO("*** End of Wear Report ***\n");
990} 1060}
991 1061
992/* 1062/*
@@ -2330,6 +2400,9 @@ static int __init ns_init_module(void)
2330 if ((retval = setup_wear_reporting(nsmtd)) != 0) 2400 if ((retval = setup_wear_reporting(nsmtd)) != 0)
2331 goto err_exit; 2401 goto err_exit;
2332 2402
2403 if ((retval = nandsim_debugfs_create(nand)) != 0)
2404 goto err_exit;
2405
2333 if ((retval = init_nandsim(nsmtd)) != 0) 2406 if ((retval = init_nandsim(nsmtd)) != 0)
2334 goto err_exit; 2407 goto err_exit;
2335 2408
@@ -2369,6 +2442,7 @@ static void __exit ns_cleanup_module(void)
2369 struct nandsim *ns = ((struct nand_chip *)nsmtd->priv)->priv; 2442 struct nandsim *ns = ((struct nand_chip *)nsmtd->priv)->priv;
2370 int i; 2443 int i;
2371 2444
2445 nandsim_debugfs_remove(ns);
2372 free_nandsim(ns); /* Free nandsim private resources */ 2446 free_nandsim(ns); /* Free nandsim private resources */
2373 nand_release(nsmtd); /* Unregister driver */ 2447 nand_release(nsmtd); /* Unregister driver */
2374 for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i) 2448 for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i)