UBIFS: add debugfs support
We need to have a possibility to see various UBIFS variables and ask UBIFS to dump various information. Debugfs is what we need. Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
This commit is contained in:
parent
17c2f9f85c
commit
552ff3179d
4 changed files with 193 additions and 20 deletions
173
fs/ubifs/debug.c
173
fs/ubifs/debug.c
|
@ -32,6 +32,7 @@
|
|||
#include "ubifs.h"
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#ifdef CONFIG_UBIFS_FS_DEBUG
|
||||
|
||||
|
@ -988,22 +989,20 @@ static int dbg_check_key_order(struct ubifs_info *c, struct ubifs_zbranch *zbr1,
|
|||
err = 1;
|
||||
key_read(c, &dent1->key, &key);
|
||||
if (keys_cmp(c, &zbr1->key, &key)) {
|
||||
dbg_err("1st entry at %d:%d has key %s", zbr1->lnum,
|
||||
zbr1->offs, DBGKEY(&key));
|
||||
dbg_err("but it should have key %s according to tnc",
|
||||
DBGKEY(&zbr1->key));
|
||||
dbg_dump_node(c, dent1);
|
||||
goto out_free;
|
||||
ubifs_err("1st entry at %d:%d has key %s", zbr1->lnum,
|
||||
zbr1->offs, DBGKEY(&key));
|
||||
ubifs_err("but it should have key %s according to tnc",
|
||||
DBGKEY(&zbr1->key)); dbg_dump_node(c, dent1);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
key_read(c, &dent2->key, &key);
|
||||
if (keys_cmp(c, &zbr2->key, &key)) {
|
||||
dbg_err("2nd entry at %d:%d has key %s", zbr1->lnum,
|
||||
zbr1->offs, DBGKEY(&key));
|
||||
dbg_err("but it should have key %s according to tnc",
|
||||
DBGKEY(&zbr2->key));
|
||||
dbg_dump_node(c, dent2);
|
||||
goto out_free;
|
||||
ubifs_err("2nd entry at %d:%d has key %s", zbr1->lnum,
|
||||
zbr1->offs, DBGKEY(&key));
|
||||
ubifs_err("but it should have key %s according to tnc",
|
||||
DBGKEY(&zbr2->key)); dbg_dump_node(c, dent2);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
nlen1 = le16_to_cpu(dent1->nlen);
|
||||
|
@ -1015,14 +1014,14 @@ static int dbg_check_key_order(struct ubifs_info *c, struct ubifs_zbranch *zbr1,
|
|||
goto out_free;
|
||||
}
|
||||
if (cmp == 0 && nlen1 == nlen2)
|
||||
dbg_err("2 xent/dent nodes with the same name");
|
||||
ubifs_err("2 xent/dent nodes with the same name");
|
||||
else
|
||||
dbg_err("bad order of colliding key %s",
|
||||
ubifs_err("bad order of colliding key %s",
|
||||
DBGKEY(&key));
|
||||
|
||||
dbg_msg("first node at %d:%d\n", zbr1->lnum, zbr1->offs);
|
||||
ubifs_msg("first node at %d:%d\n", zbr1->lnum, zbr1->offs);
|
||||
dbg_dump_node(c, dent1);
|
||||
dbg_msg("second node at %d:%d\n", zbr2->lnum, zbr2->offs);
|
||||
ubifs_msg("second node at %d:%d\n", zbr2->lnum, zbr2->offs);
|
||||
dbg_dump_node(c, dent2);
|
||||
|
||||
out_free:
|
||||
|
@ -2103,7 +2102,7 @@ static void failure_mode_init(struct ubifs_info *c)
|
|||
|
||||
fmi = kmalloc(sizeof(struct failure_mode_info), GFP_NOFS);
|
||||
if (!fmi) {
|
||||
dbg_err("Failed to register failure mode - no memory");
|
||||
ubifs_err("Failed to register failure mode - no memory");
|
||||
return;
|
||||
}
|
||||
fmi->c = c;
|
||||
|
@ -2383,4 +2382,144 @@ void ubifs_debugging_exit(struct ubifs_info *c)
|
|||
kfree(c->dbg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Root directory for UBIFS stuff in debugfs. Contains sub-directories which
|
||||
* contain the stuff specific to particular file-system mounts.
|
||||
*/
|
||||
static struct dentry *debugfs_rootdir;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
debugfs_rootdir = debugfs_create_dir("ubifs", NULL);
|
||||
if (IS_ERR(debugfs_rootdir)) {
|
||||
int err = PTR_ERR(debugfs_rootdir);
|
||||
ubifs_err("cannot create \"ubifs\" debugfs directory, "
|
||||
"error %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dbg_debugfs_exit - remove the "ubifs" directory from debugfs file-system.
|
||||
*/
|
||||
void dbg_debugfs_exit(void)
|
||||
{
|
||||
debugfs_remove(debugfs_rootdir);
|
||||
}
|
||||
|
||||
static int open_debugfs_file(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = inode->i_private;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t write_debugfs_file(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ubifs_info *c = file->private_data;
|
||||
struct ubifs_debug_info *d = c->dbg;
|
||||
|
||||
if (file->f_path.dentry == d->dump_lprops)
|
||||
dbg_dump_lprops(c);
|
||||
else if (file->f_path.dentry == d->dump_budg) {
|
||||
spin_lock(&c->space_lock);
|
||||
dbg_dump_budg(c);
|
||||
spin_unlock(&c->space_lock);
|
||||
} else if (file->f_path.dentry == d->dump_budg) {
|
||||
mutex_lock(&c->tnc_mutex);
|
||||
dbg_dump_tnc(c);
|
||||
mutex_unlock(&c->tnc_mutex);
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
*ppos += count;
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations debugfs_fops = {
|
||||
.open = open_debugfs_file,
|
||||
.write = write_debugfs_file,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
/**
|
||||
* dbg_debugfs_init_fs - initialize debugfs for UBIFS instance.
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function creates all debugfs files for this instance of UBIFS. Returns
|
||||
* zero in case of success and a negative error code in case of failure.
|
||||
*
|
||||
* Note, the only reason we have not merged this function with the
|
||||
* 'ubifs_debugging_init()' function is because it is better to initialize
|
||||
* debugfs interfaces at the very end of the mount process, and remove them at
|
||||
* the very beginning of the mount process.
|
||||
*/
|
||||
int dbg_debugfs_init_fs(struct ubifs_info *c)
|
||||
{
|
||||
int err;
|
||||
const char *fname;
|
||||
struct dentry *dent;
|
||||
struct ubifs_debug_info *d = c->dbg;
|
||||
|
||||
sprintf(d->debugfs_dir_name, "ubi%d_%d", c->vi.ubi_num, c->vi.vol_id);
|
||||
d->debugfs_dir = debugfs_create_dir(d->debugfs_dir_name,
|
||||
debugfs_rootdir);
|
||||
if (IS_ERR(d->debugfs_dir)) {
|
||||
err = PTR_ERR(d->debugfs_dir);
|
||||
ubifs_err("cannot create \"%s\" debugfs directory, error %d\n",
|
||||
d->debugfs_dir_name, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
fname = "dump_lprops";
|
||||
dent = debugfs_create_file(fname, S_IWUGO, d->debugfs_dir, c,
|
||||
&debugfs_fops);
|
||||
if (IS_ERR(dent))
|
||||
goto out_remove;
|
||||
d->dump_lprops = dent;
|
||||
|
||||
fname = "dump_budg";
|
||||
dent = debugfs_create_file(fname, S_IWUGO, d->debugfs_dir, c,
|
||||
&debugfs_fops);
|
||||
if (IS_ERR(dent))
|
||||
goto out_remove;
|
||||
d->dump_budg = dent;
|
||||
|
||||
fname = "dump_tnc";
|
||||
dent = debugfs_create_file(fname, S_IWUGO, d->debugfs_dir, c,
|
||||
&debugfs_fops);
|
||||
if (IS_ERR(dent))
|
||||
goto out_remove;
|
||||
d->dump_tnc = dent;
|
||||
|
||||
return 0;
|
||||
|
||||
out_remove:
|
||||
err = PTR_ERR(dent);
|
||||
ubifs_err("cannot create \"%s\" debugfs directory, error %d\n",
|
||||
fname, err);
|
||||
debugfs_remove_recursive(d->debugfs_dir);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* dbg_debugfs_exit_fs - remove all debugfs files.
|
||||
* @c: UBIFS file-system description object
|
||||
*/
|
||||
void dbg_debugfs_exit_fs(struct ubifs_info *c)
|
||||
{
|
||||
debugfs_remove_recursive(c->dbg->debugfs_dir);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_UBIFS_FS_DEBUG */
|
||||
|
|
|
@ -43,6 +43,13 @@
|
|||
* @new_nhead_offs: used by LPT tree size checker
|
||||
* @new_ihead_lnum: used by debugging to check ihead_lnum
|
||||
* @new_ihead_offs: used by debugging to check ihead_offs
|
||||
*
|
||||
* debugfs_dir_name: name of debugfs directory containing this file-system's
|
||||
* files
|
||||
* debugfs_dir: direntry object of the file-system debugfs directory
|
||||
* dump_lprops: "dump lprops" debugfs knob
|
||||
* dump_budg: "dump budgeting information" debugfs knob
|
||||
* dump_tnc: "dump TNC" debugfs knob
|
||||
*/
|
||||
struct ubifs_debug_info {
|
||||
void *buf;
|
||||
|
@ -61,6 +68,12 @@ struct ubifs_debug_info {
|
|||
int new_nhead_offs;
|
||||
int new_ihead_lnum;
|
||||
int new_ihead_offs;
|
||||
|
||||
char debugfs_dir_name[100];
|
||||
struct dentry *debugfs_dir;
|
||||
struct dentry *dump_lprops;
|
||||
struct dentry *dump_budg;
|
||||
struct dentry *dump_tnc;
|
||||
};
|
||||
|
||||
#define ubifs_assert(expr) do { \
|
||||
|
@ -251,7 +264,6 @@ int ubifs_debugging_init(struct ubifs_info *c);
|
|||
void ubifs_debugging_exit(struct ubifs_info *c);
|
||||
|
||||
/* Dump functions */
|
||||
|
||||
const char *dbg_ntype(int type);
|
||||
const char *dbg_cstate(int cmt_state);
|
||||
const char *dbg_get_key_dump(const struct ubifs_info *c,
|
||||
|
@ -274,7 +286,6 @@ void dbg_dump_tnc(struct ubifs_info *c);
|
|||
void dbg_dump_index(struct ubifs_info *c);
|
||||
|
||||
/* Checking helper functions */
|
||||
|
||||
typedef int (*dbg_leaf_callback)(struct ubifs_info *c,
|
||||
struct ubifs_zbranch *zbr, void *priv);
|
||||
typedef int (*dbg_znode_callback)(struct ubifs_info *c,
|
||||
|
@ -354,6 +365,12 @@ static inline int dbg_change(struct ubi_volume_desc *desc, int lnum,
|
|||
return dbg_leb_change(desc, lnum, buf, len, UBI_UNKNOWN);
|
||||
}
|
||||
|
||||
/* Debugfs-related stuff */
|
||||
int dbg_debugfs_init(void);
|
||||
void dbg_debugfs_exit(void);
|
||||
int dbg_debugfs_init_fs(struct ubifs_info *c);
|
||||
void dbg_debugfs_exit_fs(struct ubifs_info *c);
|
||||
|
||||
#else /* !CONFIG_UBIFS_FS_DEBUG */
|
||||
|
||||
/* Use "if (0)" to make compiler check arguments even if debugging is off */
|
||||
|
@ -434,6 +451,10 @@ static inline int dbg_change(struct ubi_volume_desc *desc, int lnum,
|
|||
#define dbg_force_in_the_gaps() 0
|
||||
#define dbg_failure_mode 0
|
||||
|
||||
#endif /* !CONFIG_UBIFS_FS_DEBUG */
|
||||
#define dbg_debugfs_init() 0
|
||||
#define dbg_debugfs_exit()
|
||||
#define dbg_debugfs_init_fs(c) 0
|
||||
#define dbg_debugfs_exit_fs(c) 0
|
||||
|
||||
#endif /* !CONFIG_UBIFS_FS_DEBUG */
|
||||
#endif /* !__UBIFS_DEBUG_H__ */
|
||||
|
|
|
@ -1258,6 +1258,10 @@ static int mount_ubifs(struct ubifs_info *c)
|
|||
}
|
||||
}
|
||||
|
||||
err = dbg_debugfs_init_fs(c);
|
||||
if (err)
|
||||
goto out_infos;
|
||||
|
||||
err = dbg_check_filesystem(c);
|
||||
if (err)
|
||||
goto out_infos;
|
||||
|
@ -1369,6 +1373,7 @@ static void ubifs_umount(struct ubifs_info *c)
|
|||
dbg_gen("un-mounting UBI device %d, volume %d", c->vi.ubi_num,
|
||||
c->vi.vol_id);
|
||||
|
||||
dbg_debugfs_exit_fs(c);
|
||||
spin_lock(&ubifs_infos_lock);
|
||||
list_del(&c->infos_list);
|
||||
spin_unlock(&ubifs_infos_lock);
|
||||
|
@ -2078,12 +2083,18 @@ static int __init ubifs_init(void)
|
|||
register_shrinker(&ubifs_shrinker_info);
|
||||
|
||||
err = ubifs_compressors_init();
|
||||
if (err)
|
||||
goto out_shrinker;
|
||||
|
||||
err = dbg_debugfs_init();
|
||||
if (err)
|
||||
goto out_compr;
|
||||
|
||||
return 0;
|
||||
|
||||
out_compr:
|
||||
ubifs_compressors_exit();
|
||||
out_shrinker:
|
||||
unregister_shrinker(&ubifs_shrinker_info);
|
||||
kmem_cache_destroy(ubifs_inode_slab);
|
||||
out_reg:
|
||||
|
@ -2098,6 +2109,7 @@ static void __exit ubifs_exit(void)
|
|||
ubifs_assert(list_empty(&ubifs_infos));
|
||||
ubifs_assert(atomic_long_read(&ubifs_clean_zn_cnt) == 0);
|
||||
|
||||
dbg_debugfs_exit();
|
||||
ubifs_compressors_exit();
|
||||
unregister_shrinker(&ubifs_shrinker_info);
|
||||
kmem_cache_destroy(ubifs_inode_slab);
|
||||
|
|
|
@ -1158,6 +1158,7 @@ struct ubifs_debug_info;
|
|||
* @mount_opts: UBIFS-specific mount options
|
||||
*
|
||||
* @dbg: debugging-related information
|
||||
* @dfs: debugfs support-related information
|
||||
*/
|
||||
struct ubifs_info {
|
||||
struct super_block *vfs_sb;
|
||||
|
|
Loading…
Reference in a new issue