]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
UBI: use debugfs for the extra checks knobs
authorArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
Wed, 18 May 2011 11:53:05 +0000 (14:53 +0300)
committerArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
Wed, 1 Jun 2011 08:21:47 +0000 (11:21 +0300)
This patch introduces debugfs support to UBI. All the UBI stuff is kept in the
"ubi" debugfs directory, which contains per-UBI device "ubi/ubiX"
sub-directories, containing debugging files. This file also creates
"ubi/ubiX/chk_gen" and "ubi/ubiX/chk_io" knobs for switching general and I/O
extra checks on and off. And it removes the 'debug_chks' UBI module parameters.

Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
drivers/mtd/ubi/build.c
drivers/mtd/ubi/debug.c
drivers/mtd/ubi/debug.h
drivers/mtd/ubi/io.c
drivers/mtd/ubi/scan.c
drivers/mtd/ubi/ubi.h
drivers/mtd/ubi/vmt.c
drivers/mtd/ubi/vtbl.c
drivers/mtd/ubi/wl.c

index 65626c1c446d7ccd90abadaad7ceea5438c566a9..6d0d48395d8ca492215eb02239354ad21981d983 100644 (file)
@@ -953,10 +953,14 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
        if (!ubi->peb_buf2)
                goto out_free;
 
+       err = ubi_debugging_init_dev(ubi);
+       if (err)
+               goto out_free;
+
        err = attach_by_scanning(ubi);
        if (err) {
                dbg_err("failed to attach by scanning, error %d", err);
-               goto out_free;
+               goto out_debugging;
        }
 
        if (ubi->autoresize_vol_id != -1) {
@@ -969,12 +973,16 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
        if (err)
                goto out_detach;
 
+       err = ubi_debugfs_init_dev(ubi);
+       if (err)
+               goto out_uif;
+
        ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name);
        if (IS_ERR(ubi->bgt_thread)) {
                err = PTR_ERR(ubi->bgt_thread);
                ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name,
                        err);
-               goto out_uif;
+               goto out_debugfs;
        }
 
        ubi_msg("attached mtd%d to ubi%d", mtd->index, ubi_num);
@@ -1008,12 +1016,16 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
        ubi_notify_all(ubi, UBI_VOLUME_ADDED, NULL);
        return ubi_num;
 
+out_debugfs:
+       ubi_debugfs_exit_dev(ubi);
 out_uif:
        uif_close(ubi);
 out_detach:
        ubi_wl_close(ubi);
        free_internal_volumes(ubi);
        vfree(ubi->vtbl);
+out_debugging:
+       ubi_debugging_exit_dev(ubi);
 out_free:
        vfree(ubi->peb_buf1);
        vfree(ubi->peb_buf2);
@@ -1080,11 +1092,13 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
         */
        get_device(&ubi->dev);
 
+       ubi_debugfs_exit_dev(ubi);
        uif_close(ubi);
        ubi_wl_close(ubi);
        free_internal_volumes(ubi);
        vfree(ubi->vtbl);
        put_mtd_device(ubi->mtd);
+       ubi_debugging_exit_dev(ubi);
        vfree(ubi->peb_buf1);
        vfree(ubi->peb_buf2);
        ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num);
@@ -1199,6 +1213,11 @@ static int __init ubi_init(void)
        if (!ubi_wl_entry_slab)
                goto out_dev_unreg;
 
+       err = ubi_debugfs_init();
+       if (err)
+               goto out_slab;
+
+
        /* Attach MTD devices */
        for (i = 0; i < mtd_devs; i++) {
                struct mtd_dev_param *p = &mtd_dev_param[i];
@@ -1247,6 +1266,8 @@ out_detach:
                        ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1);
                        mutex_unlock(&ubi_devices_mutex);
                }
+       ubi_debugfs_exit();
+out_slab:
        kmem_cache_destroy(ubi_wl_entry_slab);
 out_dev_unreg:
        misc_deregister(&ubi_ctrl_cdev);
@@ -1270,6 +1291,7 @@ static void __exit ubi_exit(void)
                        ubi_detach_mtd_dev(ubi_devices[i]->ubi_num, 1);
                        mutex_unlock(&ubi_devices_mutex);
                }
+       ubi_debugfs_exit();
        kmem_cache_destroy(ubi_wl_entry_slab);
        misc_deregister(&ubi_ctrl_cdev);
        class_remove_file(ubi_class, &ubi_version);
index 2224cbe41ddf5cdab3b67c3dcd1c418c1a691f0b..dce1227929f1b8daa18d42cf1d66fac9270573cf 100644 (file)
 #ifdef CONFIG_MTD_UBI_DEBUG
 
 #include "ubi.h"
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 
-unsigned int ubi_chk_flags;
 unsigned int ubi_tst_flags;
 
-module_param_named(debug_chks, ubi_chk_flags, uint, S_IRUGO | S_IWUSR);
-module_param_named(debug_tsts, ubi_chk_flags, uint, S_IRUGO | S_IWUSR);
+module_param_named(debug_tsts, ubi_tst_flags, uint, S_IRUGO | S_IWUSR);
 
-MODULE_PARM_DESC(debug_chks, "Debug check flags");
 MODULE_PARM_DESC(debug_tsts, "Debug special test flags");
 
 /**
@@ -239,4 +238,228 @@ out:
        return;
 }
 
+/**
+ * ubi_debugging_init_dev - initialize debugging for an UBI device.
+ * @ubi: UBI device description object
+ *
+ * This function initializes debugging-related data for UBI device @ubi.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+int ubi_debugging_init_dev(struct ubi_device *ubi)
+{
+       ubi->dbg = kzalloc(sizeof(struct ubi_debug_info), GFP_KERNEL);
+       if (!ubi->dbg)
+               return -ENOMEM;
+
+       return 0;
+}
+
+/**
+ * ubi_debugging_exit_dev - free debugging data for an UBI device.
+ * @ubi: UBI device description object
+ */
+void ubi_debugging_exit_dev(struct ubi_device *ubi)
+{
+       kfree(ubi->dbg);
+}
+
+/*
+ * Root directory for UBI stuff in debugfs. Contains sub-directories which
+ * contain the stuff specific to particular UBI devices.
+ */
+static struct dentry *dfs_rootdir;
+
+/**
+ * ubi_debugfs_init - create UBI debugfs directory.
+ *
+ * Create UBI debugfs directory. Returns zero in case of success and a negative
+ * error code in case of failure.
+ */
+int ubi_debugfs_init(void)
+{
+       dfs_rootdir = debugfs_create_dir("ubi", NULL);
+       if (IS_ERR_OR_NULL(dfs_rootdir)) {
+               int err = dfs_rootdir ? -ENODEV : PTR_ERR(dfs_rootdir);
+
+               ubi_err("cannot create \"ubi\" debugfs directory, error %d\n",
+                       err);
+               return err;
+       }
+
+       return 0;
+}
+
+/**
+ * ubi_debugfs_exit - remove UBI debugfs directory.
+ */
+void ubi_debugfs_exit(void)
+{
+       debugfs_remove(dfs_rootdir);
+}
+
+/* Read an UBI debugfs file */
+static ssize_t dfs_file_read(struct file *file, char __user *user_buf,
+                            size_t count, loff_t *ppos)
+{
+       unsigned long ubi_num = (unsigned long)file->private_data;
+       struct dentry *dent = file->f_path.dentry;
+       struct ubi_device *ubi;
+       struct ubi_debug_info *d;
+       char buf[3];
+       int val;
+
+       ubi = ubi_get_device(ubi_num);
+       if (!ubi)
+               return -ENODEV;
+       d = ubi->dbg;
+
+       if (dent == d->dfs_chk_gen)
+               val = d->chk_gen;
+       else if (dent == d->dfs_chk_io)
+               val = d->chk_io;
+       else {
+               count = -EINVAL;
+               goto out;
+       }
+
+       if (val)
+               buf[0] = '1';
+       else
+               buf[0] = '0';
+       buf[1] = '\n';
+       buf[2] = 0x00;
+
+       count = simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+
+out:
+       ubi_put_device(ubi);
+       return count;
+}
+
+/* Write an UBI debugfs file */
+static ssize_t dfs_file_write(struct file *file, const char __user *user_buf,
+                             size_t count, loff_t *ppos)
+{
+       unsigned long ubi_num = (unsigned long)file->private_data;
+       struct dentry *dent = file->f_path.dentry;
+       struct ubi_device *ubi;
+       struct ubi_debug_info *d;
+       size_t buf_size;
+       char buf[8];
+       int val;
+
+       ubi = ubi_get_device(ubi_num);
+       if (!ubi)
+               return -ENODEV;
+       d = ubi->dbg;
+
+       buf_size = min_t(size_t, count, (sizeof(buf) - 1));
+       if (copy_from_user(buf, user_buf, buf_size)) {
+               count = -EFAULT;
+               goto out;
+       }
+
+       if (buf[0] == '1')
+               val = 1;
+       else if (buf[0] == '0')
+               val = 0;
+       else {
+               count = -EINVAL;
+               goto out;
+       }
+
+       if (dent == d->dfs_chk_gen)
+               d->chk_gen = val;
+       else if (dent == d->dfs_chk_io)
+               d->chk_io = val;
+       else
+               count = -EINVAL;
+
+out:
+       ubi_put_device(ubi);
+       return count;
+}
+
+static int default_open(struct inode *inode, struct file *file)
+{
+       if (inode->i_private)
+               file->private_data = inode->i_private;
+
+       return 0;
+}
+
+/* File operations for all UBI debugfs files */
+static const struct file_operations dfs_fops = {
+       .read   = dfs_file_read,
+       .write  = dfs_file_write,
+       .open   = default_open,
+       .llseek = no_llseek,
+       .owner  = THIS_MODULE,
+};
+
+/**
+ * ubi_debugfs_init_dev - initialize debugfs for an UBI device.
+ * @ubi: UBI device description object
+ *
+ * This function creates all debugfs files for UBI device @ubi. Returns zero in
+ * case of success and a negative error code in case of failure.
+ */
+int ubi_debugfs_init_dev(struct ubi_device *ubi)
+{
+       int err, n;
+       unsigned long ubi_num = ubi->ubi_num;
+       const char *fname;
+       struct dentry *dent;
+       struct ubi_debug_info *d = ubi->dbg;
+
+       n = snprintf(d->dfs_dir_name, UBI_DFS_DIR_LEN + 1, UBI_DFS_DIR_NAME,
+                    ubi->ubi_num);
+       if (n == UBI_DFS_DIR_LEN) {
+               /* The array size is too small */
+               fname = UBI_DFS_DIR_NAME;
+               dent = ERR_PTR(-EINVAL);
+               goto out;
+       }
+
+       fname = d->dfs_dir_name;
+       dent = debugfs_create_dir(fname, dfs_rootdir);
+       if (IS_ERR_OR_NULL(dent))
+               goto out;
+       d->dfs_dir = dent;
+
+       fname = "chk_gen";
+       dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_chk_gen = dent;
+
+       fname = "chk_io";
+       dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_chk_io = dent;
+
+       return 0;
+
+out_remove:
+       debugfs_remove_recursive(d->dfs_dir);
+out:
+       err = dent ? PTR_ERR(dent) : -ENODEV;
+       ubi_err("cannot create \"%s\" debugfs file or directory, error %d\n",
+               fname, err);
+       return err;
+}
+
+/**
+ * dbg_debug_exit_dev - free all debugfs files corresponding to device @ubi
+ * @ubi: UBI device description object
+ */
+void ubi_debugfs_exit_dev(struct ubi_device *ubi)
+{
+       debugfs_remove_recursive(ubi->dbg->dfs_dir);
+}
+
 #endif /* CONFIG_MTD_UBI_DEBUG */
index 3f1a09c5c438dde93c11e48529cc2a3cec5d93a7..cb6c87977be656e2f6f7e23a17cbb63df479622a 100644 (file)
@@ -71,24 +71,42 @@ void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv);
 void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type);
 void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req);
 void ubi_dbg_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len);
-
-extern unsigned int ubi_chk_flags;
+int ubi_dbg_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len);
+int ubi_dbg_check_write(struct ubi_device *ubi, const void *buf, int pnum,
+                       int offset, int len);
+int ubi_debugging_init_dev(struct ubi_device *ubi);
+void ubi_debugging_exit_dev(struct ubi_device *ubi);
+int ubi_debugfs_init(void);
+void ubi_debugfs_exit(void);
+int ubi_debugfs_init_dev(struct ubi_device *ubi);
+void ubi_debugfs_exit_dev(struct ubi_device *ubi);
 
 /*
- * Debugging check flags.
+ * The UBI debugfs directory name pattern and maximum name length (3 for "ubi"
+ * + 2 for the number plus 1 for the trailing zero byte.
+ */
+#define UBI_DFS_DIR_NAME "ubi%d"
+#define UBI_DFS_DIR_LEN  (3 + 2 + 1)
+
+/**
+ * struct ubi_debug_info - debugging information for an UBI device.
  *
- * UBI_CHK_GEN: general checks
- * UBI_CHK_IO: check writes and erases
+ * @chk_gen: if UBI general extra checks are enabled
+ * @chk_io: if UBI I/O extra checks are enabled
+ * @dfs_dir_name: name of debugfs directory containing files of this UBI device
+ * @dfs_dir: direntry object of the UBI device debugfs directory
+ * @dfs_chk_gen: debugfs knob to enable UBI general extra checks
+ * @dfs_chk_io: debugfs knob to enable UBI I/O extra checks
  */
-enum {
-       UBI_CHK_GEN = 0x1,
-       UBI_CHK_IO  = 0x2,
+struct ubi_debug_info {
+       unsigned int chk_gen:1;
+       unsigned int chk_io:1;
+       char dfs_dir_name[UBI_DFS_DIR_LEN + 1];
+       struct dentry *dfs_dir;
+       struct dentry *dfs_chk_gen;
+       struct dentry *dfs_chk_io;
 };
 
-int ubi_dbg_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len);
-int ubi_dbg_check_write(struct ubi_device *ubi, const void *buf, int pnum,
-                       int offset, int len);
-
 extern unsigned int ubi_tst_flags;
 
 /*
@@ -201,11 +219,6 @@ static inline void ubi_dbg_dump_flash(struct ubi_device *ubi,
 static inline void
 ubi_dbg_print_hex_dump(const char *l, const char *ps, int pt, int r,
                       int g, const void *b, size_t len, bool a)     { return; }
-
-static inline int ubi_dbg_is_bgt_disabled(void)                    { return 0; }
-static inline int ubi_dbg_is_bitflip(void)                         { return 0; }
-static inline int ubi_dbg_is_write_failure(void)                   { return 0; }
-static inline int ubi_dbg_is_erase_failure(void)                   { return 0; }
 static inline int ubi_dbg_check_all_ff(struct ubi_device *ubi,
                                       int pnum, int offset,
                                       int len)                    { return 0; }
@@ -213,5 +226,17 @@ static inline int ubi_dbg_check_write(struct ubi_device *ubi,
                                      const void *buf, int pnum,
                                      int offset, int len)         { return 0; }
 
+static inline int ubi_debugging_init_dev(struct ubi_device *ubi)   { return 0; }
+static inline void ubi_debugging_exit_dev(struct ubi_device *ubi)  { return; }
+static inline int ubi_debugfs_init(void)                           { return 0; }
+static inline void ubi_debugfs_exit(void)                          { return; }
+static inline int ubi_debugfs_init_dev(struct ubi_device *ubi)     { return 0; }
+static inline void ubi_debugfs_exit_dev(struct ubi_device *ubi)    { return; }
+
+static inline int ubi_dbg_is_bgt_disabled(void)                    { return 0; }
+static inline int ubi_dbg_is_bitflip(void)                         { return 0; }
+static inline int ubi_dbg_is_write_failure(void)                   { return 0; }
+static inline int ubi_dbg_is_erase_failure(void)                   { return 0; }
+
 #endif /* !CONFIG_MTD_UBI_DEBUG */
 #endif /* !__UBI_DEBUG_H__ */
index 8c1b1c7bc4a7dca93d75ccd2067f1094169b7783..e23d6118224f4c550a61145bcdf03192bb6b57fa 100644 (file)
@@ -1146,7 +1146,7 @@ static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum)
 {
        int err;
 
-       if (!(ubi_chk_flags & UBI_CHK_IO))
+       if (!ubi->dbg->chk_io)
                return 0;
 
        err = ubi_io_is_bad(ubi, pnum);
@@ -1173,7 +1173,7 @@ static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum,
        int err;
        uint32_t magic;
 
-       if (!(ubi_chk_flags & UBI_CHK_IO))
+       if (!ubi->dbg->chk_io)
                return 0;
 
        magic = be32_to_cpu(ec_hdr->magic);
@@ -1211,7 +1211,7 @@ static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum)
        uint32_t crc, hdr_crc;
        struct ubi_ec_hdr *ec_hdr;
 
-       if (!(ubi_chk_flags & UBI_CHK_IO))
+       if (!ubi->dbg->chk_io)
                return 0;
 
        ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS);
@@ -1255,7 +1255,7 @@ static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum,
        int err;
        uint32_t magic;
 
-       if (!(ubi_chk_flags & UBI_CHK_IO))
+       if (!ubi->dbg->chk_io)
                return 0;
 
        magic = be32_to_cpu(vid_hdr->magic);
@@ -1296,7 +1296,7 @@ static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum)
        struct ubi_vid_hdr *vid_hdr;
        void *p;
 
-       if (!(ubi_chk_flags & UBI_CHK_IO))
+       if (!ubi->dbg->chk_io)
                return 0;
 
        vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
@@ -1348,7 +1348,7 @@ int ubi_dbg_check_write(struct ubi_device *ubi, const void *buf, int pnum,
        void *buf1;
        loff_t addr = (loff_t)pnum * ubi->peb_size + offset;
 
-       if (!(ubi_chk_flags & UBI_CHK_IO))
+       if (!ubi->dbg->chk_io)
                return 0;
 
        buf1 = __vmalloc(len, GFP_NOFS, PAGE_KERNEL);
@@ -1412,7 +1412,7 @@ int ubi_dbg_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len)
        void *buf;
        loff_t addr = (loff_t)pnum * ubi->peb_size + offset;
 
-       if (!(ubi_chk_flags & UBI_CHK_IO))
+       if (!ubi->dbg->chk_io)
                return 0;
 
        buf = __vmalloc(len, GFP_NOFS, PAGE_KERNEL);
index 2135a53732ffce0dc0b4b7806637449f7ddec1f6..a3a198f9b98dd05ea7690f4239c6874a9a3f0526 100644 (file)
@@ -1347,7 +1347,7 @@ static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si)
        struct ubi_scan_leb *seb, *last_seb;
        uint8_t *buf;
 
-       if (!(ubi_chk_flags & UBI_CHK_GEN))
+       if (!ubi->dbg->chk_gen)
                return 0;
 
        /*
index c6c22295898e97ef3fdbfa16fd4171ec650fb55a..d0e321885fd6e0c922d750913240fd4c075657e3 100644 (file)
@@ -390,6 +390,8 @@ struct ubi_wl_entry;
  * @peb_buf2: another buffer of PEB size used for different purposes
  * @buf_mutex: protects @peb_buf1 and @peb_buf2
  * @ckvol_mutex: serializes static volume checking when opening
+ *
+ * @dbg: debugging information for this UBI device
  */
 struct ubi_device {
        struct cdev cdev;
@@ -472,6 +474,8 @@ struct ubi_device {
        void *peb_buf2;
        struct mutex buf_mutex;
        struct mutex ckvol_mutex;
+
+       struct ubi_debug_info *dbg;
 };
 
 extern struct kmem_cache *ubi_wl_entry_slab;
index 366eb70219a65bb0a5aca0ccaed329b470ab1b83..97e093d196725ca2b8529221d5b3a5cbc8e47d72 100644 (file)
@@ -871,7 +871,7 @@ static int paranoid_check_volumes(struct ubi_device *ubi)
 {
        int i, err = 0;
 
-       if (!(ubi_chk_flags & UBI_CHK_GEN))
+       if (!ubi->dbg->chk_gen)
                return 0;
 
        for (i = 0; i < ubi->vtbl_slots; i++) {
index fd3bf770f518552265a22e254c08b47a34aa2dcf..d704ad275932b2b147b47bc4814e02754d7f519b 100644 (file)
@@ -876,7 +876,7 @@ out_free:
  */
 static void paranoid_vtbl_check(const struct ubi_device *ubi)
 {
-       if (!(ubi_chk_flags & UBI_CHK_GEN))
+       if (!ubi->dbg->chk_gen)
                return;
 
        if (vtbl_check(ubi, ubi->vtbl)) {
index d5076d2cce39491e4f0724319778954f1d60e958..bf13ca275f85ca94a62595f0b9ab97b3833569fe 100644 (file)
@@ -1582,7 +1582,7 @@ static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec)
        long long read_ec;
        struct ubi_ec_hdr *ec_hdr;
 
-       if (!(ubi_chk_flags & UBI_CHK_GEN))
+       if (!ubi->dbg->chk_gen)
                return 0;
 
        ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS);
@@ -1623,7 +1623,7 @@ static int paranoid_check_in_wl_tree(const struct ubi_device *ubi,
                                     struct ubi_wl_entry *e,
                                     struct rb_root *root)
 {
-       if (!(ubi_chk_flags & UBI_CHK_GEN))
+       if (!ubi->dbg->chk_gen)
                return 0;
 
        if (in_wl_tree(e, root))
@@ -1649,7 +1649,7 @@ static int paranoid_check_in_pq(const struct ubi_device *ubi,
        struct ubi_wl_entry *p;
        int i;
 
-       if (!(ubi_chk_flags & UBI_CHK_GEN))
+       if (!ubi->dbg->chk_gen)
                return 0;
 
        for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i)