summaryrefslogtreecommitdiffstats
path: root/fs/ubifs/debug.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ubifs/debug.c')
-rw-r--r--fs/ubifs/debug.c909
1 files changed, 600 insertions, 309 deletions
diff --git a/fs/ubifs/debug.c b/fs/ubifs/debug.c
index 004d3745dc4..eef109a1a92 100644
--- a/fs/ubifs/debug.c
+++ b/fs/ubifs/debug.c
@@ -27,14 +27,12 @@
* 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/slab.h>
+#include <linux/uaccess.h>
+#include <linux/random.h>
+#include "ubifs.h"
#ifdef CONFIG_UBIFS_FS_DEBUG
@@ -43,18 +41,6 @@ DEFINE_SPINLOCK(dbg_lock);
static char dbg_key_buf0[128];
static char dbg_key_buf1[128];
-unsigned int ubifs_msg_flags;
-unsigned int ubifs_chk_flags;
-unsigned int ubifs_tst_flags;
-
-module_param_named(debug_msgs, ubifs_msg_flags, uint, S_IRUGO | S_IWUSR);
-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_msgs, "Debug message type flags");
-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) {
@@ -95,6 +81,28 @@ static const char *get_key_type(int type)
}
}
+static const char *get_dent_type(int type)
+{
+ switch (type) {
+ case UBIFS_ITYPE_REG:
+ return "file";
+ case UBIFS_ITYPE_DIR:
+ return "dir";
+ case UBIFS_ITYPE_LNK:
+ return "symlink";
+ case UBIFS_ITYPE_BLK:
+ return "blkdev";
+ case UBIFS_ITYPE_CHR:
+ return "char dev";
+ case UBIFS_ITYPE_FIFO:
+ return "fifo";
+ case UBIFS_ITYPE_SOCK:
+ return "socket";
+ default:
+ return "unknown/invalid type";
+ }
+}
+
static void sprintf_key(const struct ubifs_info *c, const union ubifs_key *key,
char *buffer)
{
@@ -238,9 +246,13 @@ static void dump_ch(const struct ubifs_ch *ch)
printk(KERN_DEBUG "\tlen %u\n", le32_to_cpu(ch->len));
}
-void dbg_dump_inode(const struct ubifs_info *c, const struct inode *inode)
+void dbg_dump_inode(struct ubifs_info *c, const struct inode *inode)
{
const struct ubifs_inode *ui = ubifs_inode(inode);
+ struct qstr nm = { .name = NULL };
+ union ubifs_key key;
+ struct ubifs_dent_node *dent, *pdent = NULL;
+ int count = 2;
printk(KERN_DEBUG "Dump in-memory inode:");
printk(KERN_DEBUG "\tinode %lu\n", inode->i_ino);
@@ -274,6 +286,32 @@ void dbg_dump_inode(const struct ubifs_info *c, const struct inode *inode)
printk(KERN_DEBUG "\tlast_page_read %lu\n", ui->last_page_read);
printk(KERN_DEBUG "\tread_in_a_row %lu\n", ui->read_in_a_row);
printk(KERN_DEBUG "\tdata_len %d\n", ui->data_len);
+
+ if (!S_ISDIR(inode->i_mode))
+ return;
+
+ printk(KERN_DEBUG "List of directory entries:\n");
+ ubifs_assert(!mutex_is_locked(&c->tnc_mutex));
+
+ lowest_dent_key(c, &key, inode->i_ino);
+ while (1) {
+ dent = ubifs_tnc_next_ent(c, &key, &nm);
+ if (IS_ERR(dent)) {
+ if (PTR_ERR(dent) != -ENOENT)
+ printk(KERN_DEBUG "error %ld\n", PTR_ERR(dent));
+ break;
+ }
+
+ printk(KERN_DEBUG "\t%d: %s (%s)\n",
+ count++, dent->name, get_dent_type(dent->type));
+
+ nm.name = dent->name;
+ nm.len = le16_to_cpu(dent->nlen);
+ kfree(pdent);
+ pdent = dent;
+ key_read(c, &dent->key, &key);
+ }
+ kfree(pdent);
}
void dbg_dump_node(const struct ubifs_info *c, const void *node)
@@ -282,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 */
@@ -317,6 +355,8 @@ void dbg_dump_node(const struct ubifs_info *c, const void *node)
printk(KERN_DEBUG "\tflags %#x\n", sup_flags);
printk(KERN_DEBUG "\t big_lpt %u\n",
!!(sup_flags & UBIFS_FLG_BIGLPT));
+ printk(KERN_DEBUG "\t space_fixup %u\n",
+ !!(sup_flags & UBIFS_FLG_SPACE_FIXUP));
printk(KERN_DEBUG "\tmin_io_size %u\n",
le32_to_cpu(sup->min_io_size));
printk(KERN_DEBUG "\tleb_size %u\n",
@@ -602,7 +642,7 @@ void dbg_dump_lstats(const struct ubifs_lp_stats *lst)
spin_unlock(&dbg_lock);
}
-void dbg_dump_budg(struct ubifs_info *c)
+void dbg_dump_budg(struct ubifs_info *c, const struct ubifs_budg_info *bi)
{
int i;
struct rb_node *rb;
@@ -610,26 +650,42 @@ void dbg_dump_budg(struct ubifs_info *c)
struct ubifs_gced_idx_leb *idx_gc;
long long available, outstanding, free;
- ubifs_assert(spin_is_locked(&c->space_lock));
+ spin_lock(&c->space_lock);
spin_lock(&dbg_lock);
- printk(KERN_DEBUG "(pid %d) Budgeting info: budg_data_growth %lld, "
- "budg_dd_growth %lld, budg_idx_growth %lld\n", current->pid,
- c->budg_data_growth, c->budg_dd_growth, c->budg_idx_growth);
- printk(KERN_DEBUG "\tdata budget sum %lld, total budget sum %lld, "
- "freeable_cnt %d\n", c->budg_data_growth + c->budg_dd_growth,
- c->budg_data_growth + c->budg_dd_growth + c->budg_idx_growth,
- c->freeable_cnt);
- printk(KERN_DEBUG "\tmin_idx_lebs %d, old_idx_sz %lld, "
- "calc_idx_sz %lld, idx_gc_cnt %d\n", c->min_idx_lebs,
- c->old_idx_sz, c->calc_idx_sz, c->idx_gc_cnt);
+ printk(KERN_DEBUG "(pid %d) Budgeting info: data budget sum %lld, "
+ "total budget sum %lld\n", current->pid,
+ bi->data_growth + bi->dd_growth,
+ bi->data_growth + bi->dd_growth + bi->idx_growth);
+ printk(KERN_DEBUG "\tbudg_data_growth %lld, budg_dd_growth %lld, "
+ "budg_idx_growth %lld\n", bi->data_growth, bi->dd_growth,
+ bi->idx_growth);
+ printk(KERN_DEBUG "\tmin_idx_lebs %d, old_idx_sz %llu, "
+ "uncommitted_idx %lld\n", bi->min_idx_lebs, bi->old_idx_sz,
+ bi->uncommitted_idx);
+ printk(KERN_DEBUG "\tpage_budget %d, inode_budget %d, dent_budget %d\n",
+ bi->page_budget, bi->inode_budget, bi->dent_budget);
+ printk(KERN_DEBUG "\tnospace %u, nospace_rp %u\n",
+ bi->nospace, bi->nospace_rp);
+ printk(KERN_DEBUG "\tdark_wm %d, dead_wm %d, max_idx_node_sz %d\n",
+ c->dark_wm, c->dead_wm, c->max_idx_node_sz);
+
+ if (bi != &c->bi)
+ /*
+ * If we are dumping saved budgeting data, do not print
+ * additional information which is about the current state, not
+ * the old one which corresponded to the saved budgeting data.
+ */
+ goto out_unlock;
+
+ printk(KERN_DEBUG "\tfreeable_cnt %d, calc_idx_sz %lld, idx_gc_cnt %d\n",
+ c->freeable_cnt, c->calc_idx_sz, c->idx_gc_cnt);
printk(KERN_DEBUG "\tdirty_pg_cnt %ld, dirty_zn_cnt %ld, "
"clean_zn_cnt %ld\n", atomic_long_read(&c->dirty_pg_cnt),
atomic_long_read(&c->dirty_zn_cnt),
atomic_long_read(&c->clean_zn_cnt));
- printk(KERN_DEBUG "\tdark_wm %d, dead_wm %d, max_idx_node_sz %d\n",
- c->dark_wm, c->dead_wm, c->max_idx_node_sz);
printk(KERN_DEBUG "\tgc_lnum %d, ihead_lnum %d\n",
c->gc_lnum, c->ihead_lnum);
+
/* If we are in R/O mode, journal heads do not exist */
if (c->jheads)
for (i = 0; i < c->jhead_cnt; i++)
@@ -648,13 +704,15 @@ void dbg_dump_budg(struct ubifs_info *c)
printk(KERN_DEBUG "\tcommit state %d\n", c->cmt_state);
/* Print budgeting predictions */
- available = ubifs_calc_available(c, c->min_idx_lebs);
- outstanding = c->budg_data_growth + c->budg_dd_growth;
+ available = ubifs_calc_available(c, c->bi.min_idx_lebs);
+ outstanding = c->bi.data_growth + c->bi.dd_growth;
free = ubifs_get_free_space_nolock(c);
printk(KERN_DEBUG "Budgeting predictions:\n");
printk(KERN_DEBUG "\tavailable: %lld, outstanding %lld, free %lld\n",
available, outstanding, free);
+out_unlock:
spin_unlock(&dbg_lock);
+ spin_unlock(&c->space_lock);
}
void dbg_dump_lprop(const struct ubifs_info *c, const struct ubifs_lprops *lp)
@@ -729,7 +787,13 @@ void dbg_dump_lprop(const struct ubifs_info *c, const struct ubifs_lprops *lp)
if (bud->lnum == lp->lnum) {
int head = 0;
for (i = 0; i < c->jhead_cnt; i++) {
- if (lp->lnum == c->jheads[i].wbuf.lnum) {
+ /*
+ * Note, if we are in R/O mode or in the middle
+ * of mounting/re-mounting, the write-buffers do
+ * not exist.
+ */
+ if (c->jheads &&
+ lp->lnum == c->jheads[i].wbuf.lnum) {
printk(KERN_CONT ", jhead %s",
dbg_jhead(i));
head = 1;
@@ -812,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",
@@ -976,6 +1040,8 @@ void dbg_save_space_info(struct ubifs_info *c)
spin_lock(&c->space_lock);
memcpy(&d->saved_lst, &c->lst, sizeof(struct ubifs_lp_stats));
+ memcpy(&d->saved_bi, &c->bi, sizeof(struct ubifs_budg_info));
+ d->saved_idx_gc_cnt = c->idx_gc_cnt;
/*
* We use a dirty hack here and zero out @c->freeable_cnt, because it
@@ -1042,20 +1108,21 @@ int dbg_check_space_info(struct ubifs_info *c)
out:
ubifs_msg("saved lprops statistics dump");
dbg_dump_lstats(&d->saved_lst);
- ubifs_get_lp_stats(c, &lst);
-
+ ubifs_msg("saved budgeting info dump");
+ dbg_dump_budg(c, &d->saved_bi);
+ ubifs_msg("saved idx_gc_cnt %d", d->saved_idx_gc_cnt);
ubifs_msg("current lprops statistics dump");
+ ubifs_get_lp_stats(c, &lst);
dbg_dump_lstats(&lst);
-
- spin_lock(&c->space_lock);
- dbg_dump_budg(c);
- spin_unlock(&c->space_lock);
+ ubifs_msg("current budgeting info dump");
+ dbg_dump_budg(c, &c->bi);
dump_stack();
return -EINVAL;
}
/**
* 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
@@ -1063,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;
@@ -1101,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;
@@ -1109,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))
@@ -1143,12 +1210,14 @@ int dbg_check_dir_size(struct ubifs_info *c, const struct inode *dir)
"but calculated size is %llu", dir->i_ino,
(unsigned long long)i_size_read(dir),
(unsigned long long)size);
+ dbg_dump_inode(c, dir);
dump_stack();
return -EINVAL;
}
if (dir->i_nlink != nlink) {
ubifs_err("directory inode %lu has nlink %u, but calculated "
"nlink is %u", dir->i_ino, dir->i_nlink, nlink);
+ dbg_dump_inode(c, dir);
dump_stack();
return -EINVAL;
}
@@ -1465,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));
@@ -1712,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);
@@ -1793,6 +1862,8 @@ static struct fsck_inode *add_inode(struct ubifs_info *c,
struct rb_node **p, *parent = NULL;
struct fsck_inode *fscki;
ino_t inum = key_inum_flash(c, &ino->key);
+ struct inode *inode;
+ struct ubifs_inode *ui;
p = &fsckd->inodes.rb_node;
while (*p) {
@@ -1816,19 +1887,46 @@ static struct fsck_inode *add_inode(struct ubifs_info *c,
if (!fscki)
return ERR_PTR(-ENOMEM);
+ inode = ilookup(c->vfs_sb, inum);
+
fscki->inum = inum;
- fscki->nlink = le32_to_cpu(ino->nlink);
- fscki->size = le64_to_cpu(ino->size);
- fscki->xattr_cnt = le32_to_cpu(ino->xattr_cnt);
- fscki->xattr_sz = le32_to_cpu(ino->xattr_size);
- fscki->xattr_nms = le32_to_cpu(ino->xattr_names);
- fscki->mode = le32_to_cpu(ino->mode);
+ /*
+ * If the inode is present in the VFS inode cache, use it instead of
+ * the on-flash inode which might be out-of-date. E.g., the size might
+ * be out-of-date. If we do not do this, the following may happen, for
+ * example:
+ * 1. A power cut happens
+ * 2. We mount the file-system R/O, the replay process fixes up the
+ * inode size in the VFS cache, but on on-flash.
+ * 3. 'check_leaf()' fails because it hits a data node beyond inode
+ * size.
+ */
+ if (!inode) {
+ fscki->nlink = le32_to_cpu(ino->nlink);
+ fscki->size = le64_to_cpu(ino->size);
+ fscki->xattr_cnt = le32_to_cpu(ino->xattr_cnt);
+ fscki->xattr_sz = le32_to_cpu(ino->xattr_size);
+ fscki->xattr_nms = le32_to_cpu(ino->xattr_names);
+ fscki->mode = le32_to_cpu(ino->mode);
+ } else {
+ ui = ubifs_inode(inode);
+ fscki->nlink = inode->i_nlink;
+ fscki->size = inode->i_size;
+ fscki->xattr_cnt = ui->xattr_cnt;
+ fscki->xattr_sz = ui->xattr_size;
+ fscki->xattr_nms = ui->xattr_names;
+ fscki->mode = inode->i_mode;
+ iput(inode);
+ }
+
if (S_ISDIR(fscki->mode)) {
fscki->calc_sz = UBIFS_INO_NODE_SZ;
fscki->calc_cnt = 2;
}
+
rb_link_node(&fscki->rb, parent, p);
rb_insert_color(&fscki->rb, &fsckd->inodes);
+
return fscki;
}
@@ -2259,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;
@@ -2294,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) {
@@ -2361,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) {
@@ -2421,7 +2519,8 @@ int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head)
hashb = key_block(c, &sb->key);
if (hasha > hashb) {
- ubifs_err("larger hash %u goes before %u", hasha, hashb);
+ ubifs_err("larger hash %u goes before %u",
+ hasha, hashb);
goto error_dump;
}
}
@@ -2437,384 +2536,351 @@ error_dump:
return 0;
}
-static int invocation_cnt;
-
-int dbg_force_in_the_gaps(void)
-{
- if (!dbg_force_in_the_gaps_enabled)
- return 0;
- /* Force in-the-gaps every 8th commit */
- return !((invocation_cnt++) & 0x7);
-}
-
-/* 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;
- dbg_rcvry("failing in orphan LEB %d", lnum);
+ if (chance(9, 10))
+ return 0;
+ 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 -EIO;
- 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))
- return -EIO;
- failing = do_fail(desc, lnum, 1);
+ if (c->dbg->pc_happened)
+ return -EROFS;
+
+ 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)
- return -EIO;
+ return -EROFS;
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))
- return -EIO;
- err = ubi_leb_change(desc, lnum, buf, len, dtype);
+ if (c->dbg->pc_happened)
+ return -EROFS;
+ 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))
- return -EIO;
+ 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))
- return -EIO;
- err = ubi_leb_erase(desc, lnum);
+ if (c->dbg->pc_happened)
+ return -EROFS;
+ if (power_cut_emulated(c, lnum, 0))
+ return -EROFS;
+ err = ubi_leb_unmap(c->ubi, lnum);
if (err)
return err;
- if (do_fail(desc, lnum, 0))
- return -EIO;
+ 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))
- return -EIO;
- err = ubi_leb_unmap(desc, lnum);
+ if (c->dbg->pc_happened)
+ return -EROFS;
+ if (power_cut_emulated(c, lnum, 0))
+ return -EROFS;
+ err = ubi_leb_map(c->ubi, lnum, dtype);
if (err)
return err;
- if (do_fail(desc, lnum, 0))
- return -EIO;
+ 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 -EIO;
- 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 -EIO;
- err = ubi_leb_map(desc, lnum, dtype);
- if (err)
- return err;
- if (do_fail(desc, lnum, 0))
- return -EIO;
- 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(dfs_rootdir)) {
- int err = PTR_ERR(dfs_rootdir);
- 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 0;
+ 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) {
- spin_lock(&c->space_lock);
- dbg_dump_budg(c);
- spin_unlock(&c->space_lock);
- } else if (file->f_path.dentry == d->dfs_dump_tnc) {
+ return count;
+ }
+ if (file->f_path.dentry == d->dfs_dump_budg) {
+ dbg_dump_budg(c, &c->bi);
+ 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;
- *ppos += count;
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 = default_llseek,
+ .llseek = no_llseek,
};
/**
@@ -2831,12 +2897,20 @@ static const struct file_operations dfs_fops = {
*/
int dbg_debugfs_init_fs(struct ubifs_info *c)
{
- int err;
+ int err, n;
const char *fname;
struct dentry *dent;
struct ubifs_debug_info *d = c->dbg;
- sprintf(d->dfs_dir_name, "ubi%d_%d", c->vi.ubi_num, c->vi.vol_id);
+ n = snprintf(d->dfs_dir_name, UBIFS_DFS_DIR_LEN + 1, UBIFS_DFS_DIR_NAME,
+ c->vi.ubi_num, c->vi.vol_id);
+ if (n == UBIFS_DFS_DIR_LEN) {
+ /* The array size is too small */
+ fname = UBIFS_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))
@@ -2861,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 directory, error %d\n",
+ ubifs_err("cannot create \"%s\" debugfs file or directory, error %d\n",
fname, err);
return err;
}
@@ -2881,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 */