diff options
Diffstat (limited to 'drivers/mtd/ubi')
| -rw-r--r-- | drivers/mtd/ubi/build.c | 28 | ||||
| -rw-r--r-- | drivers/mtd/ubi/cdev.c | 10 | ||||
| -rw-r--r-- | drivers/mtd/ubi/debug.c | 269 | ||||
| -rw-r--r-- | drivers/mtd/ubi/debug.h | 113 | ||||
| -rw-r--r-- | drivers/mtd/ubi/io.c | 20 | ||||
| -rw-r--r-- | drivers/mtd/ubi/scan.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/ubi/ubi.h | 8 | ||||
| -rw-r--r-- | drivers/mtd/ubi/vmt.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/ubi/vtbl.c | 18 | ||||
| -rw-r--r-- | drivers/mtd/ubi/wl.c | 42 | 
10 files changed, 405 insertions, 107 deletions
| diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 65626c1c446d..6c3fb5ab20f5 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -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,18 @@ 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: +	get_device(&ubi->dev); +	ubi_assert(ref);  	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 +1094,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 +1215,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 +1268,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 +1293,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); diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 191f3bb3c41a..3320a50ba4f0 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -189,12 +189,16 @@ static loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin)  	return new_offset;  } -static int vol_cdev_fsync(struct file *file, int datasync) +static int vol_cdev_fsync(struct file *file, loff_t start, loff_t end, int datasync)  {  	struct ubi_volume_desc *desc = file->private_data;  	struct ubi_device *ubi = desc->vol->ubi; - -	return ubi_sync(ubi->ubi_num); +	struct inode *inode = file->f_path.dentry->d_inode; +	int err; +	mutex_lock(&inode->i_mutex); +	err = ubi_sync(ubi->ubi_num); +	mutex_unlock(&inode->i_mutex); +	return err;  } diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c index 2224cbe41ddf..ab80c0debac8 100644 --- a/drivers/mtd/ubi/debug.c +++ b/drivers/mtd/ubi/debug.c @@ -27,17 +27,9 @@  #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_PARM_DESC(debug_chks, "Debug check flags"); -MODULE_PARM_DESC(debug_tsts, "Debug special test flags");  /**   * ubi_dbg_dump_ec_hdr - dump an erase counter header. @@ -239,4 +231,261 @@ 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 if (dent == d->dfs_disable_bgt) +		val = d->disable_bgt; +	else if (dent == d->dfs_emulate_bitflips) +		val = d->emulate_bitflips; +	else if (dent == d->dfs_emulate_io_failures) +		val = d->emulate_io_failures; +	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 if (dent == d->dfs_disable_bgt) +		d->disable_bgt = val; +	else if (dent == d->dfs_emulate_bitflips) +		d->emulate_bitflips = val; +	else if (dent == d->dfs_emulate_io_failures) +		d->emulate_io_failures = 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; + +	fname = "tst_disable_bgt"; +	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_disable_bgt = dent; + +	fname = "tst_emulate_bitflips"; +	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_emulate_bitflips = dent; + +	fname = "tst_emulate_io_failures"; +	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_emulate_io_failures = 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 */ diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h index 3f1a09c5c438..65b5b76cc379 100644 --- a/drivers/mtd/ubi/debug.h +++ b/drivers/mtd/ubi/debug.h @@ -21,14 +21,6 @@  #ifndef __UBI_DEBUG_H__  #define __UBI_DEBUG_H__ -struct ubi_ec_hdr; -struct ubi_vid_hdr; -struct ubi_volume; -struct ubi_vtbl_record; -struct ubi_scan_volume; -struct ubi_scan_leb; -struct ubi_mkvol_req; -  #ifdef CONFIG_MTD_UBI_DEBUG  #include <linux/random.h> @@ -71,86 +63,103 @@ 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; - -/* - * Debugging check flags. - * - * UBI_CHK_GEN: general checks - * UBI_CHK_IO: check writes and erases - */ -enum { -	UBI_CHK_GEN = 0x1, -	UBI_CHK_IO  = 0x2, -}; -  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; +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);  /* - * Special testing 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.   * - * UBIFS_TST_DISABLE_BGT: disable the background thread - * UBI_TST_EMULATE_BITFLIPS: emulate bit-flips - * UBI_TST_EMULATE_WRITE_FAILURES: emulate write failures - * UBI_TST_EMULATE_ERASE_FAILURES: emulate erase failures + * @chk_gen: if UBI general extra checks are enabled + * @chk_io: if UBI I/O extra checks are enabled + * @disable_bgt: disable the background task for testing purposes + * @emulate_bitflips: emulate bit-flips for testing purposes + * @emulate_io_failures: emulate write/erase failures for testing purposes + * @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 + * @dfs_disable_bgt: debugfs knob to disable the background task + * @dfs_emulate_bitflips: debugfs knob to emulate bit-flips + * @dfs_emulate_io_failures: debugfs knob to emulate write/erase failures   */ -enum { -	UBI_TST_DISABLE_BGT            = 0x1, -	UBI_TST_EMULATE_BITFLIPS       = 0x2, -	UBI_TST_EMULATE_WRITE_FAILURES = 0x4, -	UBI_TST_EMULATE_ERASE_FAILURES = 0x8, +struct ubi_debug_info { +	unsigned int chk_gen:1; +	unsigned int chk_io:1; +	unsigned int disable_bgt:1; +	unsigned int emulate_bitflips:1; +	unsigned int emulate_io_failures:1; +	char dfs_dir_name[UBI_DFS_DIR_LEN + 1]; +	struct dentry *dfs_dir; +	struct dentry *dfs_chk_gen; +	struct dentry *dfs_chk_io; +	struct dentry *dfs_disable_bgt; +	struct dentry *dfs_emulate_bitflips; +	struct dentry *dfs_emulate_io_failures;  };  /**   * ubi_dbg_is_bgt_disabled - if the background thread is disabled. + * @ubi: UBI device description object   *   * Returns non-zero if the UBI background thread is disabled for testing   * purposes.   */ -static inline int ubi_dbg_is_bgt_disabled(void) +static inline int ubi_dbg_is_bgt_disabled(const struct ubi_device *ubi)  { -	return ubi_tst_flags & UBI_TST_DISABLE_BGT; +	return ubi->dbg->disable_bgt;  }  /**   * ubi_dbg_is_bitflip - if it is time to emulate a bit-flip. + * @ubi: UBI device description object   *   * Returns non-zero if a bit-flip should be emulated, otherwise returns zero.   */ -static inline int ubi_dbg_is_bitflip(void) +static inline int ubi_dbg_is_bitflip(const struct ubi_device *ubi)  { -	if (ubi_tst_flags & UBI_TST_EMULATE_BITFLIPS) +	if (ubi->dbg->emulate_bitflips)  		return !(random32() % 200);  	return 0;  }  /**   * ubi_dbg_is_write_failure - if it is time to emulate a write failure. + * @ubi: UBI device description object   *   * Returns non-zero if a write failure should be emulated, otherwise returns   * zero.   */ -static inline int ubi_dbg_is_write_failure(void) +static inline int ubi_dbg_is_write_failure(const struct ubi_device *ubi)  { -	if (ubi_tst_flags & UBI_TST_EMULATE_WRITE_FAILURES) +	if (ubi->dbg->emulate_io_failures)  		return !(random32() % 500);  	return 0;  }  /**   * ubi_dbg_is_erase_failure - if its time to emulate an erase failure. + * @ubi: UBI device description object   *   * Returns non-zero if an erase failure should be emulated, otherwise returns   * zero.   */ -static inline int ubi_dbg_is_erase_failure(void) +static inline int ubi_dbg_is_erase_failure(const struct ubi_device *ubi)  { -	if (ubi_tst_flags & UBI_TST_EMULATE_ERASE_FAILURES) +	if (ubi->dbg->emulate_io_failures)  		return !(random32() % 400);  	return 0;  } @@ -201,11 +210,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 +217,20 @@ 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(const struct ubi_device *ubi)              { return 0; } +static inline int ubi_dbg_is_bitflip(const struct ubi_device *ubi) { return 0; } +static inline int +ubi_dbg_is_write_failure(const struct ubi_device *ubi)             { return 0; } +static inline int +ubi_dbg_is_erase_failure(const struct ubi_device *ubi)             { return 0; } +  #endif /* !CONFIG_MTD_UBI_DEBUG */  #endif /* !__UBI_DEBUG_H__ */ diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 8c1b1c7bc4a7..6ba55c235873 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -212,7 +212,7 @@ retry:  	} else {  		ubi_assert(len == read); -		if (ubi_dbg_is_bitflip()) { +		if (ubi_dbg_is_bitflip(ubi)) {  			dbg_gen("bit-flip (emulated)");  			err = UBI_IO_BITFLIPS;  		} @@ -281,7 +281,7 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,  			return err;  	} -	if (ubi_dbg_is_write_failure()) { +	if (ubi_dbg_is_write_failure(ubi)) {  		dbg_err("cannot write %d bytes to PEB %d:%d "  			"(emulated)", len, pnum, offset);  		ubi_dbg_dump_stack(); @@ -396,7 +396,7 @@ retry:  	if (err)  		return err; -	if (ubi_dbg_is_erase_failure()) { +	if (ubi_dbg_is_erase_failure(ubi)) {  		dbg_err("cannot erase PEB %d (emulated)", pnum);  		return -EIO;  	} @@ -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); diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index 2135a53732ff..a3a198f9b98d 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -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;  	/* diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index c6c22295898e..dc64c767fd21 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -44,7 +44,6 @@  #include "ubi-media.h"  #include "scan.h" -#include "debug.h"  /* Maximum number of supported UBI devices */  #define UBI_MAX_DEVICES 32 @@ -390,6 +389,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,8 +473,12 @@ struct ubi_device {  	void *peb_buf2;  	struct mutex buf_mutex;  	struct mutex ckvol_mutex; + +	struct ubi_debug_info *dbg;  }; +#include "debug.h" +  extern struct kmem_cache *ubi_wl_entry_slab;  extern const struct file_operations ubi_ctrl_cdev_operations;  extern const struct file_operations ubi_cdev_operations; @@ -662,6 +667,7 @@ static inline void ubi_ro_mode(struct ubi_device *ubi)  	if (!ubi->ro_mode) {  		ubi->ro_mode = 1;  		ubi_warn("switch to read-only mode"); +		ubi_dbg_dump_stack();  	}  } diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 366eb70219a6..97e093d19672 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -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++) { diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c index fd3bf770f518..4b50a3029b84 100644 --- a/drivers/mtd/ubi/vtbl.c +++ b/drivers/mtd/ubi/vtbl.c @@ -307,8 +307,7 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_scan_info *si,  {  	int err, tries = 0;  	static struct ubi_vid_hdr *vid_hdr; -	struct ubi_scan_volume *sv; -	struct ubi_scan_leb *new_seb, *old_seb = NULL; +	struct ubi_scan_leb *new_seb;  	ubi_msg("create volume table (copy #%d)", copy + 1); @@ -316,15 +315,6 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_scan_info *si,  	if (!vid_hdr)  		return -ENOMEM; -	/* -	 * Check if there is a logical eraseblock which would have to contain -	 * this volume table copy was found during scanning. It has to be wiped -	 * out. -	 */ -	sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOLUME_ID); -	if (sv) -		old_seb = ubi_scan_find_seb(sv, copy); -  retry:  	new_seb = ubi_scan_get_free_peb(ubi, si);  	if (IS_ERR(new_seb)) { @@ -351,8 +341,8 @@ retry:  		goto write_error;  	/* -	 * And add it to the scanning information. Don't delete the old -	 * @old_seb as it will be deleted and freed in 'ubi_scan_add_used()'. +	 * And add it to the scanning information. Don't delete the old version +	 * of this LEB as it will be deleted and freed in 'ubi_scan_add_used()'.  	 */  	err = ubi_scan_add_used(ubi, si, new_seb->pnum, new_seb->ec,  				vid_hdr, 0); @@ -876,7 +866,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)) { diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index ff2c4956eeff..42c684cf3688 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -1,4 +1,5 @@  /* + * @ubi: UBI device description object   * Copyright (c) International Business Machines Corp., 2006   *   * This program is free software; you can redistribute it and/or modify @@ -163,12 +164,14 @@ struct ubi_work {  #ifdef CONFIG_MTD_UBI_DEBUG  static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec); -static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e, +static int paranoid_check_in_wl_tree(const struct ubi_device *ubi, +				     struct ubi_wl_entry *e,  				     struct rb_root *root); -static int paranoid_check_in_pq(struct ubi_device *ubi, struct ubi_wl_entry *e); +static int paranoid_check_in_pq(const struct ubi_device *ubi, +				struct ubi_wl_entry *e);  #else  #define paranoid_check_ec(ubi, pnum, ec) 0 -#define paranoid_check_in_wl_tree(e, root) +#define paranoid_check_in_wl_tree(ubi, e, root)  #define paranoid_check_in_pq(ubi, e) 0  #endif @@ -449,7 +452,7 @@ retry:  		BUG();  	} -	paranoid_check_in_wl_tree(e, &ubi->free); +	paranoid_check_in_wl_tree(ubi, e, &ubi->free);  	/*  	 * Move the physical eraseblock to the protection queue where it will @@ -613,7 +616,7 @@ static void schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk)  	list_add_tail(&wrk->list, &ubi->works);  	ubi_assert(ubi->works_count >= 0);  	ubi->works_count += 1; -	if (ubi->thread_enabled && !ubi_dbg_is_bgt_disabled()) +	if (ubi->thread_enabled && !ubi_dbg_is_bgt_disabled(ubi))  		wake_up_process(ubi->bgt_thread);  	spin_unlock(&ubi->wl_lock);  } @@ -712,7 +715,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,  			       e1->ec, e2->ec);  			goto out_cancel;  		} -		paranoid_check_in_wl_tree(e1, &ubi->used); +		paranoid_check_in_wl_tree(ubi, e1, &ubi->used);  		rb_erase(&e1->u.rb, &ubi->used);  		dbg_wl("move PEB %d EC %d to PEB %d EC %d",  		       e1->pnum, e1->ec, e2->pnum, e2->ec); @@ -721,12 +724,12 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,  		scrubbing = 1;  		e1 = rb_entry(rb_first(&ubi->scrub), struct ubi_wl_entry, u.rb);  		e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); -		paranoid_check_in_wl_tree(e1, &ubi->scrub); +		paranoid_check_in_wl_tree(ubi, e1, &ubi->scrub);  		rb_erase(&e1->u.rb, &ubi->scrub);  		dbg_wl("scrub PEB %d to PEB %d", e1->pnum, e2->pnum);  	} -	paranoid_check_in_wl_tree(e2, &ubi->free); +	paranoid_check_in_wl_tree(ubi, e2, &ubi->free);  	rb_erase(&e2->u.rb, &ubi->free);  	ubi->move_from = e1;  	ubi->move_to = e2; @@ -1169,13 +1172,13 @@ retry:  		return 0;  	} else {  		if (in_wl_tree(e, &ubi->used)) { -			paranoid_check_in_wl_tree(e, &ubi->used); +			paranoid_check_in_wl_tree(ubi, e, &ubi->used);  			rb_erase(&e->u.rb, &ubi->used);  		} else if (in_wl_tree(e, &ubi->scrub)) { -			paranoid_check_in_wl_tree(e, &ubi->scrub); +			paranoid_check_in_wl_tree(ubi, e, &ubi->scrub);  			rb_erase(&e->u.rb, &ubi->scrub);  		} else if (in_wl_tree(e, &ubi->erroneous)) { -			paranoid_check_in_wl_tree(e, &ubi->erroneous); +			paranoid_check_in_wl_tree(ubi, e, &ubi->erroneous);  			rb_erase(&e->u.rb, &ubi->erroneous);  			ubi->erroneous_peb_count -= 1;  			ubi_assert(ubi->erroneous_peb_count >= 0); @@ -1242,7 +1245,7 @@ retry:  	}  	if (in_wl_tree(e, &ubi->used)) { -		paranoid_check_in_wl_tree(e, &ubi->used); +		paranoid_check_in_wl_tree(ubi, e, &ubi->used);  		rb_erase(&e->u.rb, &ubi->used);  	} else {  		int err; @@ -1364,7 +1367,7 @@ int ubi_thread(void *u)  		spin_lock(&ubi->wl_lock);  		if (list_empty(&ubi->works) || ubi->ro_mode || -		    !ubi->thread_enabled || ubi_dbg_is_bgt_disabled()) { +		    !ubi->thread_enabled || ubi_dbg_is_bgt_disabled(ubi)) {  			set_current_state(TASK_INTERRUPTIBLE);  			spin_unlock(&ubi->wl_lock);  			schedule(); @@ -1579,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); @@ -1609,16 +1612,18 @@ out_free:  /**   * paranoid_check_in_wl_tree - check that wear-leveling entry is in WL RB-tree. + * @ubi: UBI device description object   * @e: the wear-leveling entry to check   * @root: the root of the tree   *   * This function returns zero if @e is in the @root RB-tree and %-EINVAL if it   * is not.   */ -static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e, +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)) @@ -1638,12 +1643,13 @@ static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,   *   * This function returns zero if @e is in @ubi->pq and %-EINVAL if it is not.   */ -static int paranoid_check_in_pq(struct ubi_device *ubi, struct ubi_wl_entry *e) +static int paranoid_check_in_pq(const struct ubi_device *ubi, +				struct ubi_wl_entry *e)  {  	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) | 
