aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/ubi/debug.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/ubi/debug.c')
-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 @@
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/module.h>
+#include <linux/seq_file.h>
/**
@@ -386,7 +387,9 @@ out:
return count;
}
-/* File operations for all UBI debugfs files */
+/* File operations for all UBI debugfs files except
+ * detailed_erase_block_info
+ */
static const struct file_operations dfs_fops = {
.read = dfs_file_read,
.write = dfs_file_write,
@@ -395,6 +398,121 @@ static const struct file_operations dfs_fops = {
.owner = THIS_MODULE,
};
+/* As long as the position is less then that total number of erase blocks,
+ * we still have more to print.
+ */
+static void *eraseblk_count_seq_start(struct seq_file *s, loff_t *pos)
+{
+ struct ubi_device *ubi = s->private;
+
+ if (*pos == 0)
+ return SEQ_START_TOKEN;
+
+ if (*pos < ubi->peb_count)
+ return pos;
+
+ return NULL;
+}
+
+/* Since we are using the position as the iterator, we just need to check if we
+ * are done and increment the position.
+ */
+static void *eraseblk_count_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ struct ubi_device *ubi = s->private;
+
+ if (v == SEQ_START_TOKEN)
+ return pos;
+ (*pos)++;
+
+ if (*pos < ubi->peb_count)
+ return pos;
+
+ return NULL;
+}
+
+static void eraseblk_count_seq_stop(struct seq_file *s, void *v)
+{
+}
+
+static int eraseblk_count_seq_show(struct seq_file *s, void *iter)
+{
+ struct ubi_device *ubi = s->private;
+ struct ubi_wl_entry *wl;
+ int *block_number = iter;
+ int erase_count = -1;
+ int err;
+
+ /* If this is the start, print a header */
+ if (iter == SEQ_START_TOKEN) {
+ seq_puts(s,
+ "physical_block_number\terase_count\tblock_status\tread_status\n");
+ return 0;
+ }
+
+ err = ubi_io_is_bad(ubi, *block_number);
+ if (err)
+ return err;
+
+ spin_lock(&ubi->wl_lock);
+
+ wl = ubi->lookuptbl[*block_number];
+ if (wl)
+ erase_count = wl->ec;
+
+ spin_unlock(&ubi->wl_lock);
+
+ if (erase_count < 0)
+ return 0;
+
+ seq_printf(s, "%-22d\t%-11d\n", *block_number, erase_count);
+
+ return 0;
+}
+
+static const struct seq_operations eraseblk_count_seq_ops = {
+ .start = eraseblk_count_seq_start,
+ .next = eraseblk_count_seq_next,
+ .stop = eraseblk_count_seq_stop,
+ .show = eraseblk_count_seq_show
+};
+
+static int eraseblk_count_open(struct inode *inode, struct file *f)
+{
+ struct seq_file *s;
+ int err;
+
+ err = seq_open(f, &eraseblk_count_seq_ops);
+ if (err)
+ return err;
+
+ s = f->private_data;
+ s->private = ubi_get_device((unsigned long)inode->i_private);
+
+ if (!s->private)
+ return -ENODEV;
+ else
+ return 0;
+}
+
+static int eraseblk_count_release(struct inode *inode, struct file *f)
+{
+ struct seq_file *s = f->private_data;
+ struct ubi_device *ubi = s->private;
+
+ ubi_put_device(ubi);
+
+ return seq_release(inode, f);
+}
+
+static const struct file_operations eraseblk_count_fops = {
+ .owner = THIS_MODULE,
+ .open = eraseblk_count_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = eraseblk_count_release,
+};
+
/**
* ubi_debugfs_init_dev - initialize debugfs for an UBI device.
* @ubi: UBI device description object
@@ -491,6 +609,12 @@ int ubi_debugfs_init_dev(struct ubi_device *ubi)
goto out_remove;
d->dfs_power_cut_max = dent;
+ fname = "detailed_erase_block_info";
+ dent = debugfs_create_file(fname, S_IRUSR, d->dfs_dir, (void *)ubi_num,
+ &eraseblk_count_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+
return 0;
out_remove: