Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/teigland/dlm
[pandora-kernel.git] / fs / ubifs / debug.c
index 4a2170d..eef109a 100644 (file)
  * various local functions of those subsystems.
  */
 
-#define UBIFS_DBG_PRESERVE_UBI
-
-#include "ubifs.h"
 #include <linux/module.h>
-#include <linux/moduleparam.h>
 #include <linux/debugfs.h>
 #include <linux/math64.h>
+#include <linux/uaccess.h>
+#include <linux/random.h>
+#include "ubifs.h"
 
 #ifdef CONFIG_UBIFS_FS_DEBUG
 
@@ -42,15 +41,6 @@ DEFINE_SPINLOCK(dbg_lock);
 static char dbg_key_buf0[128];
 static char dbg_key_buf1[128];
 
-unsigned int ubifs_chk_flags;
-unsigned int ubifs_tst_flags;
-
-module_param_named(debug_chks, ubifs_chk_flags, uint, S_IRUGO | S_IWUSR);
-module_param_named(debug_tsts, ubifs_tst_flags, uint, S_IRUGO | S_IWUSR);
-
-MODULE_PARM_DESC(debug_chks, "Debug check flags");
-MODULE_PARM_DESC(debug_tsts, "Debug special test flags");
-
 static const char *get_key_fmt(int fmt)
 {
        switch (fmt) {
@@ -330,7 +320,7 @@ void dbg_dump_node(const struct ubifs_info *c, const void *node)
        union ubifs_key key;
        const struct ubifs_ch *ch = node;
 
-       if (dbg_failure_mode)
+       if (dbg_is_tst_rcvry(c))
                return;
 
        /* If the magic is incorrect, just hexdump the first bytes */
@@ -886,7 +876,7 @@ void dbg_dump_leb(const struct ubifs_info *c, int lnum)
        struct ubifs_scan_node *snod;
        void *buf;
 
-       if (dbg_failure_mode)
+       if (dbg_is_tst_rcvry(c))
                return;
 
        printk(KERN_DEBUG "(pid %d) start dumping LEB %d\n",
@@ -1132,6 +1122,7 @@ out:
 
 /**
  * dbg_check_synced_i_size - check synchronized inode size.
+ * @c: UBIFS file-system description object
  * @inode: inode to check
  *
  * If inode is clean, synchronized inode size has to be equivalent to current
@@ -1139,12 +1130,12 @@ out:
  * has to be locked). Returns %0 if synchronized inode size if correct, and
  * %-EINVAL if not.
  */
-int dbg_check_synced_i_size(struct inode *inode)
+int dbg_check_synced_i_size(const struct ubifs_info *c, struct inode *inode)
 {
        int err = 0;
        struct ubifs_inode *ui = ubifs_inode(inode);
 
-       if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
+       if (!dbg_is_chk_gen(c))
                return 0;
        if (!S_ISREG(inode->i_mode))
                return 0;
@@ -1177,7 +1168,7 @@ int dbg_check_synced_i_size(struct inode *inode)
  * Note, it is good idea to make sure the @dir->i_mutex is locked before
  * calling this function.
  */
-int dbg_check_dir_size(struct ubifs_info *c, const struct inode *dir)
+int dbg_check_dir(struct ubifs_info *c, const struct inode *dir)
 {
        unsigned int nlink = 2;
        union ubifs_key key;
@@ -1185,7 +1176,7 @@ int dbg_check_dir_size(struct ubifs_info *c, const struct inode *dir)
        struct qstr nm = { .name = NULL };
        loff_t size = UBIFS_INO_NODE_SZ;
 
-       if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
+       if (!dbg_is_chk_gen(c))
                return 0;
 
        if (!S_ISDIR(dir->i_mode))
@@ -1543,7 +1534,7 @@ int dbg_check_tnc(struct ubifs_info *c, int extra)
        long clean_cnt = 0, dirty_cnt = 0;
        int err, last;
 
-       if (!(ubifs_chk_flags & UBIFS_CHK_TNC))
+       if (!dbg_is_chk_index(c))
                return 0;
 
        ubifs_assert(mutex_is_locked(&c->tnc_mutex));
@@ -1790,7 +1781,7 @@ int dbg_check_idx_size(struct ubifs_info *c, long long idx_size)
        int err;
        long long calc = 0;
 
-       if (!(ubifs_chk_flags & UBIFS_CHK_IDX_SZ))
+       if (!dbg_is_chk_index(c))
                return 0;
 
        err = dbg_walk_index(c, NULL, add_size, &calc);
@@ -2366,7 +2357,7 @@ int dbg_check_filesystem(struct ubifs_info *c)
        int err;
        struct fsck_data fsckd;
 
-       if (!(ubifs_chk_flags & UBIFS_CHK_FS))
+       if (!dbg_is_chk_fs(c))
                return 0;
 
        fsckd.inodes = RB_ROOT;
@@ -2401,7 +2392,7 @@ int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head)
        struct list_head *cur;
        struct ubifs_scan_node *sa, *sb;
 
-       if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
+       if (!dbg_is_chk_gen(c))
                return 0;
 
        for (cur = head->next; cur->next != head; cur = cur->next) {
@@ -2468,7 +2459,7 @@ int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head)
        struct list_head *cur;
        struct ubifs_scan_node *sa, *sb;
 
-       if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
+       if (!dbg_is_chk_gen(c))
                return 0;
 
        for (cur = head->next; cur->next != head; cur = cur->next) {
@@ -2545,214 +2536,141 @@ error_dump:
        return 0;
 }
 
-int dbg_force_in_the_gaps(void)
-{
-       if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
-               return 0;
-
-       return !(random32() & 7);
-}
-
-/* Failure mode for recovery testing */
-
-#define chance(n, d) (simple_rand() <= (n) * 32768LL / (d))
-
-struct failure_mode_info {
-       struct list_head list;
-       struct ubifs_info *c;
-};
-
-static LIST_HEAD(fmi_list);
-static DEFINE_SPINLOCK(fmi_lock);
-
-static unsigned int next;
-
-static int simple_rand(void)
-{
-       if (next == 0)
-               next = current->pid;
-       next = next * 1103515245 + 12345;
-       return (next >> 16) & 32767;
-}
-
-static void failure_mode_init(struct ubifs_info *c)
-{
-       struct failure_mode_info *fmi;
-
-       fmi = kmalloc(sizeof(struct failure_mode_info), GFP_NOFS);
-       if (!fmi) {
-               ubifs_err("Failed to register failure mode - no memory");
-               return;
-       }
-       fmi->c = c;
-       spin_lock(&fmi_lock);
-       list_add_tail(&fmi->list, &fmi_list);
-       spin_unlock(&fmi_lock);
-}
-
-static void failure_mode_exit(struct ubifs_info *c)
+static inline int chance(unsigned int n, unsigned int out_of)
 {
-       struct failure_mode_info *fmi, *tmp;
+       return !!((random32() % out_of) + 1 <= n);
 
-       spin_lock(&fmi_lock);
-       list_for_each_entry_safe(fmi, tmp, &fmi_list, list)
-               if (fmi->c == c) {
-                       list_del(&fmi->list);
-                       kfree(fmi);
-               }
-       spin_unlock(&fmi_lock);
 }
 
-static struct ubifs_info *dbg_find_info(struct ubi_volume_desc *desc)
+static int power_cut_emulated(struct ubifs_info *c, int lnum, int write)
 {
-       struct failure_mode_info *fmi;
-
-       spin_lock(&fmi_lock);
-       list_for_each_entry(fmi, &fmi_list, list)
-               if (fmi->c->ubi == desc) {
-                       struct ubifs_info *c = fmi->c;
-
-                       spin_unlock(&fmi_lock);
-                       return c;
-               }
-       spin_unlock(&fmi_lock);
-       return NULL;
-}
-
-static int in_failure_mode(struct ubi_volume_desc *desc)
-{
-       struct ubifs_info *c = dbg_find_info(desc);
-
-       if (c && dbg_failure_mode)
-               return c->dbg->failure_mode;
-       return 0;
-}
+       struct ubifs_debug_info *d = c->dbg;
 
-static int do_fail(struct ubi_volume_desc *desc, int lnum, int write)
-{
-       struct ubifs_info *c = dbg_find_info(desc);
-       struct ubifs_debug_info *d;
+       ubifs_assert(dbg_is_tst_rcvry(c));
 
-       if (!c || !dbg_failure_mode)
-               return 0;
-       d = c->dbg;
-       if (d->failure_mode)
-               return 1;
-       if (!d->fail_cnt) {
-               /* First call - decide delay to failure */
+       if (!d->pc_cnt) {
+               /* First call - decide delay to the power cut */
                if (chance(1, 2)) {
-                       unsigned int delay = 1 << (simple_rand() >> 11);
+                       unsigned long delay;
 
                        if (chance(1, 2)) {
-                               d->fail_delay = 1;
-                               d->fail_timeout = jiffies +
-                                                 msecs_to_jiffies(delay);
-                               dbg_rcvry("failing after %ums", delay);
+                               d->pc_delay = 1;
+                               /* Fail withing 1 minute */
+                               delay = random32() % 60000;
+                               d->pc_timeout = jiffies;
+                               d->pc_timeout += msecs_to_jiffies(delay);
+                               ubifs_warn("failing after %lums", delay);
                        } else {
-                               d->fail_delay = 2;
-                               d->fail_cnt_max = delay;
-                               dbg_rcvry("failing after %u calls", delay);
+                               d->pc_delay = 2;
+                               delay = random32() % 10000;
+                               /* Fail within 10000 operations */
+                               d->pc_cnt_max = delay;
+                               ubifs_warn("failing after %lu calls", delay);
                        }
                }
-               d->fail_cnt += 1;
+
+               d->pc_cnt += 1;
        }
+
        /* Determine if failure delay has expired */
-       if (d->fail_delay == 1) {
-               if (time_before(jiffies, d->fail_timeout))
+       if (d->pc_delay == 1 && time_before(jiffies, d->pc_timeout))
                        return 0;
-       } else if (d->fail_delay == 2)
-               if (d->fail_cnt++ < d->fail_cnt_max)
+       if (d->pc_delay == 2 && d->pc_cnt++ < d->pc_cnt_max)
                        return 0;
+
        if (lnum == UBIFS_SB_LNUM) {
-               if (write) {
-                       if (chance(1, 2))
-                               return 0;
-               } else if (chance(19, 20))
+               if (write && chance(1, 2))
+                       return 0;
+               if (chance(19, 20))
                        return 0;
-               dbg_rcvry("failing in super block LEB %d", lnum);
+               ubifs_warn("failing in super block LEB %d", lnum);
        } else if (lnum == UBIFS_MST_LNUM || lnum == UBIFS_MST_LNUM + 1) {
                if (chance(19, 20))
                        return 0;
-               dbg_rcvry("failing in master LEB %d", lnum);
+               ubifs_warn("failing in master LEB %d", lnum);
        } else if (lnum >= UBIFS_LOG_LNUM && lnum <= c->log_last) {
-               if (write) {
-                       if (chance(99, 100))
-                               return 0;
-               } else if (chance(399, 400))
+               if (write && chance(99, 100))
+                       return 0;
+               if (chance(399, 400))
                        return 0;
-               dbg_rcvry("failing in log LEB %d", lnum);
+               ubifs_warn("failing in log LEB %d", lnum);
        } else if (lnum >= c->lpt_first && lnum <= c->lpt_last) {
-               if (write) {
-                       if (chance(7, 8))
-                               return 0;
-               } else if (chance(19, 20))
+               if (write && chance(7, 8))
                        return 0;
-               dbg_rcvry("failing in LPT LEB %d", lnum);
+               if (chance(19, 20))
+                       return 0;
+               ubifs_warn("failing in LPT LEB %d", lnum);
        } else if (lnum >= c->orph_first && lnum <= c->orph_last) {
-               if (write) {
-                       if (chance(1, 2))
-                               return 0;
-               } else if (chance(9, 10))
+               if (write && chance(1, 2))
+                       return 0;
+               if (chance(9, 10))
                        return 0;
-               dbg_rcvry("failing in orphan LEB %d", lnum);
+               ubifs_warn("failing in orphan LEB %d", lnum);
        } else if (lnum == c->ihead_lnum) {
                if (chance(99, 100))
                        return 0;
-               dbg_rcvry("failing in index head LEB %d", lnum);
+               ubifs_warn("failing in index head LEB %d", lnum);
        } else if (c->jheads && lnum == c->jheads[GCHD].wbuf.lnum) {
                if (chance(9, 10))
                        return 0;
-               dbg_rcvry("failing in GC head LEB %d", lnum);
+               ubifs_warn("failing in GC head LEB %d", lnum);
        } else if (write && !RB_EMPTY_ROOT(&c->buds) &&
                   !ubifs_search_bud(c, lnum)) {
                if (chance(19, 20))
                        return 0;
-               dbg_rcvry("failing in non-bud LEB %d", lnum);
+               ubifs_warn("failing in non-bud LEB %d", lnum);
        } else if (c->cmt_state == COMMIT_RUNNING_BACKGROUND ||
                   c->cmt_state == COMMIT_RUNNING_REQUIRED) {
                if (chance(999, 1000))
                        return 0;
-               dbg_rcvry("failing in bud LEB %d commit running", lnum);
+               ubifs_warn("failing in bud LEB %d commit running", lnum);
        } else {
                if (chance(9999, 10000))
                        return 0;
-               dbg_rcvry("failing in bud LEB %d commit not running", lnum);
+               ubifs_warn("failing in bud LEB %d commit not running", lnum);
        }
-       ubifs_err("*** SETTING FAILURE MODE ON (LEB %d) ***", lnum);
-       d->failure_mode = 1;
+
+       d->pc_happened = 1;
+       ubifs_warn("========== Power cut emulated ==========");
        dump_stack();
        return 1;
 }
 
-static void cut_data(const void *buf, int len)
+static void cut_data(const void *buf, unsigned int len)
 {
-       int flen, i;
+       unsigned int from, to, i, ffs = chance(1, 2);
        unsigned char *p = (void *)buf;
 
-       flen = (len * (long long)simple_rand()) >> 15;
-       for (i = flen; i < len; i++)
-               p[i] = 0xff;
-}
+       from = random32() % (len + 1);
+       if (chance(1, 2))
+               to = random32() % (len - from + 1);
+       else
+               to = len;
 
-int dbg_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
-                int len, int check)
-{
-       if (in_failure_mode(desc))
-               return -EROFS;
-       return ubi_leb_read(desc, lnum, buf, offset, len, check);
+       if (from < to)
+               ubifs_warn("filled bytes %u-%u with %s", from, to - 1,
+                          ffs ? "0xFFs" : "random data");
+
+       if (ffs)
+               for (i = from; i < to; i++)
+                       p[i] = 0xFF;
+       else
+               for (i = from; i < to; i++)
+                       p[i] = random32() % 0x100;
 }
 
-int dbg_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
-                 int offset, int len, int dtype)
+int dbg_leb_write(struct ubifs_info *c, int lnum, const void *buf,
+                 int offs, int len, int dtype)
 {
        int err, failing;
 
-       if (in_failure_mode(desc))
+       if (c->dbg->pc_happened)
                return -EROFS;
-       failing = do_fail(desc, lnum, 1);
+
+       failing = power_cut_emulated(c, lnum, 1);
        if (failing)
                cut_data(buf, len);
-       err = ubi_leb_write(desc, lnum, buf, offset, len, dtype);
+       err = ubi_leb_write(c->ubi, lnum, buf, offs, len, dtype);
        if (err)
                return err;
        if (failing)
@@ -2760,162 +2678,207 @@ int dbg_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
        return 0;
 }
 
-int dbg_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
+int dbg_leb_change(struct ubifs_info *c, int lnum, const void *buf,
                   int len, int dtype)
 {
        int err;
 
-       if (do_fail(desc, lnum, 1))
+       if (c->dbg->pc_happened)
                return -EROFS;
-       err = ubi_leb_change(desc, lnum, buf, len, dtype);
+       if (power_cut_emulated(c, lnum, 1))
+               return -EROFS;
+       err = ubi_leb_change(c->ubi, lnum, buf, len, dtype);
        if (err)
                return err;
-       if (do_fail(desc, lnum, 1))
+       if (power_cut_emulated(c, lnum, 1))
                return -EROFS;
        return 0;
 }
 
-int dbg_leb_erase(struct ubi_volume_desc *desc, int lnum)
+int dbg_leb_unmap(struct ubifs_info *c, int lnum)
 {
        int err;
 
-       if (do_fail(desc, lnum, 0))
+       if (c->dbg->pc_happened)
+               return -EROFS;
+       if (power_cut_emulated(c, lnum, 0))
                return -EROFS;
-       err = ubi_leb_erase(desc, lnum);
+       err = ubi_leb_unmap(c->ubi, lnum);
        if (err)
                return err;
-       if (do_fail(desc, lnum, 0))
+       if (power_cut_emulated(c, lnum, 0))
                return -EROFS;
        return 0;
 }
 
-int dbg_leb_unmap(struct ubi_volume_desc *desc, int lnum)
+int dbg_leb_map(struct ubifs_info *c, int lnum, int dtype)
 {
        int err;
 
-       if (do_fail(desc, lnum, 0))
+       if (c->dbg->pc_happened)
+               return -EROFS;
+       if (power_cut_emulated(c, lnum, 0))
                return -EROFS;
-       err = ubi_leb_unmap(desc, lnum);
+       err = ubi_leb_map(c->ubi, lnum, dtype);
        if (err)
                return err;
-       if (do_fail(desc, lnum, 0))
+       if (power_cut_emulated(c, lnum, 0))
                return -EROFS;
        return 0;
 }
 
-int dbg_is_mapped(struct ubi_volume_desc *desc, int lnum)
-{
-       if (in_failure_mode(desc))
-               return -EROFS;
-       return ubi_is_mapped(desc, lnum);
-}
+/*
+ * Root directory for UBIFS stuff in debugfs. Contains sub-directories which
+ * contain the stuff specific to particular file-system mounts.
+ */
+static struct dentry *dfs_rootdir;
 
-int dbg_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype)
+static int dfs_file_open(struct inode *inode, struct file *file)
 {
-       int err;
-
-       if (do_fail(desc, lnum, 0))
-               return -EROFS;
-       err = ubi_leb_map(desc, lnum, dtype);
-       if (err)
-               return err;
-       if (do_fail(desc, lnum, 0))
-               return -EROFS;
-       return 0;
+       file->private_data = inode->i_private;
+       return nonseekable_open(inode, file);
 }
 
 /**
- * ubifs_debugging_init - initialize UBIFS debugging.
- * @c: UBIFS file-system description object
+ * provide_user_output - provide output to the user reading a debugfs file.
+ * @val: boolean value for the answer
+ * @u: the buffer to store the answer at
+ * @count: size of the buffer
+ * @ppos: position in the @u output buffer
  *
- * This function initializes debugging-related data for the file system.
- * Returns zero in case of success and a negative error code in case of
+ * This is a simple helper function which stores @val boolean value in the user
+ * buffer when the user reads one of UBIFS debugfs files. Returns amount of
+ * bytes written to @u in case of success and a negative error code in case of
  * failure.
  */
-int ubifs_debugging_init(struct ubifs_info *c)
+static int provide_user_output(int val, char __user *u, size_t count,
+                              loff_t *ppos)
 {
-       c->dbg = kzalloc(sizeof(struct ubifs_debug_info), GFP_KERNEL);
-       if (!c->dbg)
-               return -ENOMEM;
+       char buf[3];
 
-       failure_mode_init(c);
-       return 0;
+       if (val)
+               buf[0] = '1';
+       else
+               buf[0] = '0';
+       buf[1] = '\n';
+       buf[2] = 0x00;
+
+       return simple_read_from_buffer(u, count, ppos, buf, 2);
 }
 
-/**
- * ubifs_debugging_exit - free debugging data.
- * @c: UBIFS file-system description object
- */
-void ubifs_debugging_exit(struct ubifs_info *c)
+static ssize_t dfs_file_read(struct file *file, char __user *u, size_t count,
+                            loff_t *ppos)
 {
-       failure_mode_exit(c);
-       kfree(c->dbg);
-}
+       struct dentry *dent = file->f_path.dentry;
+       struct ubifs_info *c = file->private_data;
+       struct ubifs_debug_info *d = c->dbg;
+       int val;
+
+       if (dent == d->dfs_chk_gen)
+               val = d->chk_gen;
+       else if (dent == d->dfs_chk_index)
+               val = d->chk_index;
+       else if (dent == d->dfs_chk_orph)
+               val = d->chk_orph;
+       else if (dent == d->dfs_chk_lprops)
+               val = d->chk_lprops;
+       else if (dent == d->dfs_chk_fs)
+               val = d->chk_fs;
+       else if (dent == d->dfs_tst_rcvry)
+               val = d->tst_rcvry;
+       else
+               return -EINVAL;
 
-/*
- * Root directory for UBIFS stuff in debugfs. Contains sub-directories which
- * contain the stuff specific to particular file-system mounts.
- */
-static struct dentry *dfs_rootdir;
+       return provide_user_output(val, u, count, ppos);
+}
 
 /**
- * dbg_debugfs_init - initialize debugfs file-system.
+ * interpret_user_input - interpret user debugfs file input.
+ * @u: user-provided buffer with the input
+ * @count: buffer size
  *
- * UBIFS uses debugfs file-system to expose various debugging knobs to
- * user-space. This function creates "ubifs" directory in the debugfs
- * file-system. Returns zero in case of success and a negative error code in
- * case of failure.
+ * This is a helper function which interpret user input to a boolean UBIFS
+ * debugfs file. Returns %0 or %1 in case of success and a negative error code
+ * in case of failure.
  */
-int dbg_debugfs_init(void)
+static int interpret_user_input(const char __user *u, size_t count)
 {
-       dfs_rootdir = debugfs_create_dir("ubifs", NULL);
-       if (IS_ERR_OR_NULL(dfs_rootdir)) {
-               int err = dfs_rootdir ? PTR_ERR(dfs_rootdir) : -ENODEV;
-               ubifs_err("cannot create \"ubifs\" debugfs directory, "
-                         "error %d\n", err);
-               return err;
-       }
+       size_t buf_size;
+       char buf[8];
 
-       return 0;
-}
+       buf_size = min_t(size_t, count, (sizeof(buf) - 1));
+       if (copy_from_user(buf, u, buf_size))
+               return -EFAULT;
 
-/**
- * dbg_debugfs_exit - remove the "ubifs" directory from debugfs file-system.
- */
-void dbg_debugfs_exit(void)
-{
-       debugfs_remove(dfs_rootdir);
-}
+       if (buf[0] == '1')
+               return 1;
+       else if (buf[0] == '0')
+               return 0;
 
-static int open_debugfs_file(struct inode *inode, struct file *file)
-{
-       file->private_data = inode->i_private;
-       return nonseekable_open(inode, file);
+       return -EINVAL;
 }
 
-static ssize_t write_debugfs_file(struct file *file, const char __user *buf,
-                                 size_t count, loff_t *ppos)
+static ssize_t dfs_file_write(struct file *file, const char __user *u,
+                             size_t count, loff_t *ppos)
 {
        struct ubifs_info *c = file->private_data;
        struct ubifs_debug_info *d = c->dbg;
+       struct dentry *dent = file->f_path.dentry;
+       int val;
 
-       if (file->f_path.dentry == d->dfs_dump_lprops)
+       /*
+        * TODO: this is racy - the file-system might have already been
+        * unmounted and we'd oops in this case. The plan is to fix it with
+        * help of 'iterate_supers_type()' which we should have in v3.0: when
+        * a debugfs opened, we rember FS's UUID in file->private_data. Then
+        * whenever we access the FS via a debugfs file, we iterate all UBIFS
+        * superblocks and fine the one with the same UUID, and take the
+        * locking right.
+        *
+        * The other way to go suggested by Al Viro is to create a separate
+        * 'ubifs-debug' file-system instead.
+        */
+       if (file->f_path.dentry == d->dfs_dump_lprops) {
                dbg_dump_lprops(c);
-       else if (file->f_path.dentry == d->dfs_dump_budg)
+               return count;
+       }
+       if (file->f_path.dentry == d->dfs_dump_budg) {
                dbg_dump_budg(c, &c->bi);
-       else if (file->f_path.dentry == d->dfs_dump_tnc) {
+               return count;
+       }
+       if (file->f_path.dentry == d->dfs_dump_tnc) {
                mutex_lock(&c->tnc_mutex);
                dbg_dump_tnc(c);
                mutex_unlock(&c->tnc_mutex);
-       } else
+               return count;
+       }
+
+       val = interpret_user_input(u, count);
+       if (val < 0)
+               return val;
+
+       if (dent == d->dfs_chk_gen)
+               d->chk_gen = val;
+       else if (dent == d->dfs_chk_index)
+               d->chk_index = val;
+       else if (dent == d->dfs_chk_orph)
+               d->chk_orph = val;
+       else if (dent == d->dfs_chk_lprops)
+               d->chk_lprops = val;
+       else if (dent == d->dfs_chk_fs)
+               d->chk_fs = val;
+       else if (dent == d->dfs_tst_rcvry)
+               d->tst_rcvry = val;
+       else
                return -EINVAL;
 
        return count;
 }
 
 static const struct file_operations dfs_fops = {
-       .open = open_debugfs_file,
-       .write = write_debugfs_file,
+       .open = dfs_file_open,
+       .read = dfs_file_read,
+       .write = dfs_file_write,
        .owner = THIS_MODULE,
        .llseek = no_llseek,
 };
@@ -2972,13 +2935,55 @@ int dbg_debugfs_init_fs(struct ubifs_info *c)
                goto out_remove;
        d->dfs_dump_tnc = dent;
 
+       fname = "chk_general";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_chk_gen = dent;
+
+       fname = "chk_index";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_chk_index = dent;
+
+       fname = "chk_orphans";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_chk_orph = dent;
+
+       fname = "chk_lprops";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_chk_lprops = dent;
+
+       fname = "chk_fs";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_chk_fs = dent;
+
+       fname = "tst_recovery";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_tst_rcvry = dent;
+
        return 0;
 
 out_remove:
        debugfs_remove_recursive(d->dfs_dir);
 out:
        err = dent ? PTR_ERR(dent) : -ENODEV;
-       ubifs_err("cannot create \"%s\" debugfs filr or directory, error %d\n",
+       ubifs_err("cannot create \"%s\" debugfs file or directory, error %d\n",
                  fname, err);
        return err;
 }
@@ -2992,4 +2997,179 @@ void dbg_debugfs_exit_fs(struct ubifs_info *c)
        debugfs_remove_recursive(c->dbg->dfs_dir);
 }
 
+struct ubifs_global_debug_info ubifs_dbg;
+
+static struct dentry *dfs_chk_gen;
+static struct dentry *dfs_chk_index;
+static struct dentry *dfs_chk_orph;
+static struct dentry *dfs_chk_lprops;
+static struct dentry *dfs_chk_fs;
+static struct dentry *dfs_tst_rcvry;
+
+static ssize_t dfs_global_file_read(struct file *file, char __user *u,
+                                   size_t count, loff_t *ppos)
+{
+       struct dentry *dent = file->f_path.dentry;
+       int val;
+
+       if (dent == dfs_chk_gen)
+               val = ubifs_dbg.chk_gen;
+       else if (dent == dfs_chk_index)
+               val = ubifs_dbg.chk_index;
+       else if (dent == dfs_chk_orph)
+               val = ubifs_dbg.chk_orph;
+       else if (dent == dfs_chk_lprops)
+               val = ubifs_dbg.chk_lprops;
+       else if (dent == dfs_chk_fs)
+               val = ubifs_dbg.chk_fs;
+       else if (dent == dfs_tst_rcvry)
+               val = ubifs_dbg.tst_rcvry;
+       else
+               return -EINVAL;
+
+       return provide_user_output(val, u, count, ppos);
+}
+
+static ssize_t dfs_global_file_write(struct file *file, const char __user *u,
+                                    size_t count, loff_t *ppos)
+{
+       struct dentry *dent = file->f_path.dentry;
+       int val;
+
+       val = interpret_user_input(u, count);
+       if (val < 0)
+               return val;
+
+       if (dent == dfs_chk_gen)
+               ubifs_dbg.chk_gen = val;
+       else if (dent == dfs_chk_index)
+               ubifs_dbg.chk_index = val;
+       else if (dent == dfs_chk_orph)
+               ubifs_dbg.chk_orph = val;
+       else if (dent == dfs_chk_lprops)
+               ubifs_dbg.chk_lprops = val;
+       else if (dent == dfs_chk_fs)
+               ubifs_dbg.chk_fs = val;
+       else if (dent == dfs_tst_rcvry)
+               ubifs_dbg.tst_rcvry = val;
+       else
+               return -EINVAL;
+
+       return count;
+}
+
+static const struct file_operations dfs_global_fops = {
+       .read = dfs_global_file_read,
+       .write = dfs_global_file_write,
+       .owner = THIS_MODULE,
+       .llseek = no_llseek,
+};
+
+/**
+ * dbg_debugfs_init - initialize debugfs file-system.
+ *
+ * UBIFS uses debugfs file-system to expose various debugging knobs to
+ * user-space. This function creates "ubifs" directory in the debugfs
+ * file-system. Returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+int dbg_debugfs_init(void)
+{
+       int err;
+       const char *fname;
+       struct dentry *dent;
+
+       fname = "ubifs";
+       dent = debugfs_create_dir(fname, NULL);
+       if (IS_ERR_OR_NULL(dent))
+               goto out;
+       dfs_rootdir = dent;
+
+       fname = "chk_general";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
+                                  &dfs_global_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       dfs_chk_gen = dent;
+
+       fname = "chk_index";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
+                                  &dfs_global_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       dfs_chk_index = dent;
+
+       fname = "chk_orphans";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
+                                  &dfs_global_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       dfs_chk_orph = dent;
+
+       fname = "chk_lprops";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
+                                  &dfs_global_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       dfs_chk_lprops = dent;
+
+       fname = "chk_fs";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
+                                  &dfs_global_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       dfs_chk_fs = dent;
+
+       fname = "tst_recovery";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
+                                  &dfs_global_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       dfs_tst_rcvry = dent;
+
+       return 0;
+
+out_remove:
+       debugfs_remove_recursive(dfs_rootdir);
+out:
+       err = dent ? PTR_ERR(dent) : -ENODEV;
+       ubifs_err("cannot create \"%s\" debugfs file or directory, error %d\n",
+                 fname, err);
+       return err;
+}
+
+/**
+ * dbg_debugfs_exit - remove the "ubifs" directory from debugfs file-system.
+ */
+void dbg_debugfs_exit(void)
+{
+       debugfs_remove_recursive(dfs_rootdir);
+}
+
+/**
+ * ubifs_debugging_init - initialize UBIFS debugging.
+ * @c: UBIFS file-system description object
+ *
+ * This function initializes debugging-related data for the file system.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+int ubifs_debugging_init(struct ubifs_info *c)
+{
+       c->dbg = kzalloc(sizeof(struct ubifs_debug_info), GFP_KERNEL);
+       if (!c->dbg)
+               return -ENOMEM;
+
+       return 0;
+}
+
+/**
+ * ubifs_debugging_exit - free debugging data.
+ * @c: UBIFS file-system description object
+ */
+void ubifs_debugging_exit(struct ubifs_info *c)
+{
+       kfree(c->dbg);
+}
+
 #endif /* CONFIG_UBIFS_FS_DEBUG */