From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751866AbcISSsr (ORCPT ); Mon, 19 Sep 2016 14:48:47 -0400 Received: from skprod2.natinst.com ([130.164.80.23]:33725 "EHLO ni.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1750708AbcISSsp (ORCPT ); Mon, 19 Sep 2016 14:48:45 -0400 From: Zach Brown To: dwmw2@infradead.org Cc: computersforpeace@gmail.com, dedekind1@gmail.com, richard@nod.at, linux-mtd@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH] UBI: add debugfs file for tracking PEB state Date: Mon, 19 Sep 2016 13:47:44 -0500 Message-Id: <1474310864-1743-1-git-send-email-zach.brown@ni.com> X-Mailer: git-send-email 2.7.4 X-MIMETrack: Itemize by SMTP Server on US-AUS-MGWOut1/AUS/H/NIC(Release 8.5.3FP6 HF1218|December 12, 2014) at 09/19/2016 01:47:44 PM, Serialize by Router on US-AUS-MGWOut1/AUS/H/NIC(Release 8.5.3FP6 HF1218|December 12, 2014) at 09/19/2016 01:47:44 PM, Serialize complete at 09/19/2016 01:47:44 PM X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:,, definitions=2016-09-19_10:,, signatures=0 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Ben Shelton 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 Signed-off-by: Zach Brown --- drivers/mtd/ubi/debug.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 150 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c index f101a49..09eaab6 100644 --- a/drivers/mtd/ubi/debug.c +++ b/drivers/mtd/ubi/debug.c @@ -22,6 +22,7 @@ #include #include #include +#include /** @@ -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,146 @@ 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) +{ +} + +enum block_status { + BLOCK_STATUS_OK, + BLOCK_STATUS_BAD_BLOCK, + BLOCK_STATUS_ERASE_COUNT_BEYOND_MAX +}; + +static char const *block_status_names[] = {"OK", "marked_bad", + "erase_count_beyond_max"}; + +enum read_status { + READ_STATUS_OK, + READ_STATUS_ERR_READING_BLOCK, + READ_STATUS_ERR_READING_ERASE_COUNT +}; + +static char const *read_status_names[] = {"OK", "err_reading_block", + "err_reading_erase_count"}; + +static int eraseblk_count_seq_show(struct seq_file *s, void *iter) +{ + struct ubi_device *ubi = s->private; + int *block_number = iter; + int erase_count = -1; + enum block_status b_sts = BLOCK_STATUS_OK; + enum read_status r_sts = READ_STATUS_OK; + 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) { + if (err < 0) + r_sts = READ_STATUS_ERR_READING_BLOCK; + else + b_sts = BLOCK_STATUS_BAD_BLOCK; + } else { + struct ubi_ec_hdr erase_count_header; + + err = ubi_io_read_ec_hdr(ubi, *block_number, + &erase_count_header, 0); + if (err && err != UBI_IO_BITFLIPS) + r_sts = READ_STATUS_ERR_READING_ERASE_COUNT; + else { + erase_count = be64_to_cpu(erase_count_header.ec); + if (erase_count > UBI_MAX_ERASECOUNTER) + b_sts = BLOCK_STATUS_ERASE_COUNT_BEYOND_MAX; + } + } + + seq_printf(s, "%-22d\t%-11d\t%-12s\t%-12s\n", *block_number, + erase_count, block_status_names[b_sts], + read_status_names[r_sts]); + 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((int)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 +634,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: -- 2.7.4