aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBen Shelton <ben.shelton@ni.com>2017-03-27 14:24:44 -0400
committerRichard Weinberger <richard@nod.at>2017-05-08 14:49:02 -0400
commit7bccd12d27b7e358823feb5429731b8ee698b173 (patch)
treebd6304f63c5d9d935b37190ce14f1fa5a49deabc /drivers
parent798868c021016c1ee1825a3fbf10ae64ecc64c8e (diff)
ubi: Add debugfs file for tracking PEB state
Add a file under debugfs to allow easy access to the erase count for each physical erase block on an UBI device. This is useful when debugging data integrity issues with UBIFS on NAND flash devices. Signed-off-by: Ben Shelton <ben.shelton@ni.com> Signed-off-by: Zach Brown <zach.brown@ni.com> v2: * If ubi_io_is_bad eraseblk_count_seq_show just returns the err. * if ubi->lookuptbl returns null, its no longer treated as an error instead info for that block is not printeded * Removed check for UBI_MAX_ERASECOUNTER since it is impossible to hit * Removed block state from print, if a block is printed then it is good and if it is not printed, then it is bad. v3: * Remove errant ! symbol from if statement checking if erase count is valid. Signed-off-by: Richard Weinberger <richard@nod.at>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/ubi/debug.c126
1 files changed, 125 insertions, 1 deletions
diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c
index f101a4985a7c..7bc96294ae4d 100644
--- a/drivers/mtd/ubi/debug.c
+++ b/drivers/mtd/ubi/debug.c
@@ -22,6 +22,7 @@
22#include <linux/debugfs.h> 22#include <linux/debugfs.h>
23#include <linux/uaccess.h> 23#include <linux/uaccess.h>
24#include <linux/module.h> 24#include <linux/module.h>
25#include <linux/seq_file.h>
25 26
26 27
27/** 28/**
@@ -386,7 +387,9 @@ out:
386 return count; 387 return count;
387} 388}
388 389
389/* File operations for all UBI debugfs files */ 390/* File operations for all UBI debugfs files except
391 * detailed_erase_block_info
392 */
390static const struct file_operations dfs_fops = { 393static const struct file_operations dfs_fops = {
391 .read = dfs_file_read, 394 .read = dfs_file_read,
392 .write = dfs_file_write, 395 .write = dfs_file_write,
@@ -395,6 +398,121 @@ static const struct file_operations dfs_fops = {
395 .owner = THIS_MODULE, 398 .owner = THIS_MODULE,
396}; 399};
397 400
401/* As long as the position is less then that total number of erase blocks,
402 * we still have more to print.
403 */
404static void *eraseblk_count_seq_start(struct seq_file *s, loff_t *pos)
405{
406 struct ubi_device *ubi = s->private;
407
408 if (*pos == 0)
409 return SEQ_START_TOKEN;
410
411 if (*pos < ubi->peb_count)
412 return pos;
413
414 return NULL;
415}
416
417/* Since we are using the position as the iterator, we just need to check if we
418 * are done and increment the position.
419 */
420static void *eraseblk_count_seq_next(struct seq_file *s, void *v, loff_t *pos)
421{
422 struct ubi_device *ubi = s->private;
423
424 if (v == SEQ_START_TOKEN)
425 return pos;
426 (*pos)++;
427
428 if (*pos < ubi->peb_count)
429 return pos;
430
431 return NULL;
432}
433
434static void eraseblk_count_seq_stop(struct seq_file *s, void *v)
435{
436}
437
438static int eraseblk_count_seq_show(struct seq_file *s, void *iter)
439{
440 struct ubi_device *ubi = s->private;
441 struct ubi_wl_entry *wl;
442 int *block_number = iter;
443 int erase_count = -1;
444 int err;
445
446 /* If this is the start, print a header */
447 if (iter == SEQ_START_TOKEN) {
448 seq_puts(s,
449 "physical_block_number\terase_count\tblock_status\tread_status\n");
450 return 0;
451 }
452
453 err = ubi_io_is_bad(ubi, *block_number);
454 if (err)
455 return err;
456
457 spin_lock(&ubi->wl_lock);
458
459 wl = ubi->lookuptbl[*block_number];
460 if (wl)
461 erase_count = wl->ec;
462
463 spin_unlock(&ubi->wl_lock);
464
465 if (erase_count < 0)
466 return 0;
467
468 seq_printf(s, "%-22d\t%-11d\n", *block_number, erase_count);
469
470 return 0;
471}
472
473static const struct seq_operations eraseblk_count_seq_ops = {
474 .start = eraseblk_count_seq_start,
475 .next = eraseblk_count_seq_next,
476 .stop = eraseblk_count_seq_stop,
477 .show = eraseblk_count_seq_show
478};
479
480static int eraseblk_count_open(struct inode *inode, struct file *f)
481{
482 struct seq_file *s;
483 int err;
484
485 err = seq_open(f, &eraseblk_count_seq_ops);
486 if (err)
487 return err;
488
489 s = f->private_data;
490 s->private = ubi_get_device((unsigned long)inode->i_private);
491
492 if (!s->private)
493 return -ENODEV;
494 else
495 return 0;
496}
497
498static int eraseblk_count_release(struct inode *inode, struct file *f)
499{
500 struct seq_file *s = f->private_data;
501 struct ubi_device *ubi = s->private;
502
503 ubi_put_device(ubi);
504
505 return seq_release(inode, f);
506}
507
508static const struct file_operations eraseblk_count_fops = {
509 .owner = THIS_MODULE,
510 .open = eraseblk_count_open,
511 .read = seq_read,
512 .llseek = seq_lseek,
513 .release = eraseblk_count_release,
514};
515
398/** 516/**
399 * ubi_debugfs_init_dev - initialize debugfs for an UBI device. 517 * ubi_debugfs_init_dev - initialize debugfs for an UBI device.
400 * @ubi: UBI device description object 518 * @ubi: UBI device description object
@@ -491,6 +609,12 @@ int ubi_debugfs_init_dev(struct ubi_device *ubi)
491 goto out_remove; 609 goto out_remove;
492 d->dfs_power_cut_max = dent; 610 d->dfs_power_cut_max = dent;
493 611
612 fname = "detailed_erase_block_info";
613 dent = debugfs_create_file(fname, S_IRUSR, d->dfs_dir, (void *)ubi_num,
614 &eraseblk_count_fops);
615 if (IS_ERR_OR_NULL(dent))
616 goto out_remove;
617
494 return 0; 618 return 0;
495 619
496out_remove: 620out_remove: