Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 50269067 authored by david.oberhollenzer@sigma-star.at's avatar david.oberhollenzer@sigma-star.at Committed by Richard Weinberger
Browse files

UBI: power cut emulation for testing



Emulate random power cuts by switching device to ro after a number of
writes to allow simple power cut testing with nand-sim.

Maximum and minimum number of successful writes before power cut and
what kind of writes (EC header, VID header or none) to interrupt
configurable via debugfs.

Signed-off-by: default avatarDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
parent 1a7e985d
Loading
Loading
Loading
Loading
+87 −2
Original line number Original line Diff line number Diff line
@@ -263,7 +263,7 @@ static ssize_t dfs_file_read(struct file *file, char __user *user_buf,
	struct dentry *dent = file->f_path.dentry;
	struct dentry *dent = file->f_path.dentry;
	struct ubi_device *ubi;
	struct ubi_device *ubi;
	struct ubi_debug_info *d;
	struct ubi_debug_info *d;
	char buf[3];
	char buf[8];
	int val;
	int val;


	ubi = ubi_get_device(ubi_num);
	ubi = ubi_get_device(ubi_num);
@@ -283,6 +283,22 @@ static ssize_t dfs_file_read(struct file *file, char __user *user_buf,
		val = d->emulate_bitflips;
		val = d->emulate_bitflips;
	else if (dent == d->dfs_emulate_io_failures)
	else if (dent == d->dfs_emulate_io_failures)
		val = d->emulate_io_failures;
		val = d->emulate_io_failures;
	else if (dent == d->dfs_emulate_power_cut) {
		snprintf(buf, sizeof(buf), "%u\n", d->emulate_power_cut);
		count = simple_read_from_buffer(user_buf, count, ppos,
						buf, strlen(buf));
		goto out;
	} else if (dent == d->dfs_power_cut_min) {
		snprintf(buf, sizeof(buf), "%u\n", d->power_cut_min);
		count = simple_read_from_buffer(user_buf, count, ppos,
						buf, strlen(buf));
		goto out;
	} else if (dent == d->dfs_power_cut_max) {
		snprintf(buf, sizeof(buf), "%u\n", d->power_cut_max);
		count = simple_read_from_buffer(user_buf, count, ppos,
						buf, strlen(buf));
		goto out;
	}
	else {
	else {
		count = -EINVAL;
		count = -EINVAL;
		goto out;
		goto out;
@@ -311,7 +327,7 @@ static ssize_t dfs_file_write(struct file *file, const char __user *user_buf,
	struct ubi_device *ubi;
	struct ubi_device *ubi;
	struct ubi_debug_info *d;
	struct ubi_debug_info *d;
	size_t buf_size;
	size_t buf_size;
	char buf[8];
	char buf[8] = {0};
	int val;
	int val;


	ubi = ubi_get_device(ubi_num);
	ubi = ubi_get_device(ubi_num);
@@ -325,6 +341,21 @@ static ssize_t dfs_file_write(struct file *file, const char __user *user_buf,
		goto out;
		goto out;
	}
	}


	if (dent == d->dfs_power_cut_min) {
		if (kstrtouint(buf, 0, &d->power_cut_min) != 0)
			count = -EINVAL;
		goto out;
	} else if (dent == d->dfs_power_cut_max) {
		if (kstrtouint(buf, 0, &d->power_cut_max) != 0)
			count = -EINVAL;
		goto out;
	} else if (dent == d->dfs_emulate_power_cut) {
		if (kstrtoint(buf, 0, &val) != 0)
			count = -EINVAL;
		d->emulate_power_cut = val;
		goto out;
	}

	if (buf[0] == '1')
	if (buf[0] == '1')
		val = 1;
		val = 1;
	else if (buf[0] == '0')
	else if (buf[0] == '0')
@@ -438,6 +469,27 @@ int ubi_debugfs_init_dev(struct ubi_device *ubi)
		goto out_remove;
		goto out_remove;
	d->dfs_emulate_io_failures = dent;
	d->dfs_emulate_io_failures = dent;


	fname = "tst_emulate_power_cut";
	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_power_cut = dent;

	fname = "tst_emulate_power_cut_min";
	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_power_cut_min = dent;

	fname = "tst_emulate_power_cut_max";
	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_power_cut_max = dent;

	return 0;
	return 0;


out_remove:
out_remove:
@@ -458,3 +510,36 @@ void ubi_debugfs_exit_dev(struct ubi_device *ubi)
	if (IS_ENABLED(CONFIG_DEBUG_FS))
	if (IS_ENABLED(CONFIG_DEBUG_FS))
		debugfs_remove_recursive(ubi->dbg.dfs_dir);
		debugfs_remove_recursive(ubi->dbg.dfs_dir);
}
}

/**
 * ubi_dbg_power_cut - emulate a power cut if it is time to do so
 * @ubi: UBI device description object
 * @caller: Flags set to indicate from where the function is being called
 *
 * Returns non-zero if a power cut was emulated, zero if not.
 */
int ubi_dbg_power_cut(struct ubi_device *ubi, int caller)
{
	unsigned int range;

	if ((ubi->dbg.emulate_power_cut & caller) == 0)
		return 0;

	if (ubi->dbg.power_cut_counter == 0) {
		ubi->dbg.power_cut_counter = ubi->dbg.power_cut_min;

		if (ubi->dbg.power_cut_max > ubi->dbg.power_cut_min) {
			range = ubi->dbg.power_cut_max - ubi->dbg.power_cut_min;
			ubi->dbg.power_cut_counter += prandom_u32() % range;
		}
		return 0;
	}

	ubi->dbg.power_cut_counter--;
	if (ubi->dbg.power_cut_counter)
		return 0;

	ubi_msg(ubi, "XXXXXXXXXXXXXXX emulating a power cut XXXXXXXXXXXXXXXX");
	ubi_ro_mode(ubi);
	return 1;
}
+2 −0
Original line number Original line Diff line number Diff line
@@ -137,4 +137,6 @@ static inline void ubi_enable_dbg_chk_fastmap(struct ubi_device *ubi)
{
{
	ubi->dbg.chk_fastmap = 1;
	ubi->dbg.chk_fastmap = 1;
}
}

int ubi_dbg_power_cut(struct ubi_device *ubi, int caller);
#endif /* !__UBI_DEBUG_H__ */
#endif /* !__UBI_DEBUG_H__ */
+6 −0
Original line number Original line Diff line number Diff line
@@ -859,6 +859,9 @@ int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum,
	if (err)
	if (err)
		return err;
		return err;


	if (ubi_dbg_power_cut(ubi, POWER_CUT_EC_WRITE))
		return -EROFS;

	err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize);
	err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize);
	return err;
	return err;
}
}
@@ -1106,6 +1109,9 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
	if (err)
	if (err)
		return err;
		return err;


	if (ubi_dbg_power_cut(ubi, POWER_CUT_VID_WRITE))
		return -EROFS;

	p = (char *)vid_hdr - ubi->vid_hdr_shift;
	p = (char *)vid_hdr - ubi->vid_hdr_shift;
	err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset,
	err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset,
			   ubi->vid_hdr_alsize);
			   ubi->vid_hdr_alsize);
+25 −0
Original line number Original line Diff line number Diff line
@@ -151,6 +151,17 @@ enum {
	UBI_BAD_FASTMAP,
	UBI_BAD_FASTMAP,
};
};


/*
 * Flags for emulate_power_cut in ubi_debug_info
 *
 * POWER_CUT_EC_WRITE: Emulate a power cut when writing an EC header
 * POWER_CUT_VID_WRITE: Emulate a power cut when writing a VID header
 */
enum {
	POWER_CUT_EC_WRITE = 0x01,
	POWER_CUT_VID_WRITE = 0x02,
};

/**
/**
 * struct ubi_wl_entry - wear-leveling entry.
 * struct ubi_wl_entry - wear-leveling entry.
 * @u.rb: link in the corresponding (free/used) RB-tree
 * @u.rb: link in the corresponding (free/used) RB-tree
@@ -360,6 +371,10 @@ struct ubi_wl_entry;
 * @disable_bgt: disable the background task for testing purposes
 * @disable_bgt: disable the background task for testing purposes
 * @emulate_bitflips: emulate bit-flips for testing purposes
 * @emulate_bitflips: emulate bit-flips for testing purposes
 * @emulate_io_failures: emulate write/erase failures for testing purposes
 * @emulate_io_failures: emulate write/erase failures for testing purposes
 * @emulate_power_cut: emulate power cut for testing purposes
 * @power_cut_counter: count down for writes left until emulated power cut
 * @power_cut_min: minimum number of writes before emulating a power cut
 * @power_cut_max: maximum number of writes until emulating a power cut
 * @dfs_dir_name: name of debugfs directory containing files of this UBI device
 * @dfs_dir_name: name of debugfs directory containing files of this UBI device
 * @dfs_dir: direntry object of the UBI device debugfs directory
 * @dfs_dir: direntry object of the UBI device debugfs directory
 * @dfs_chk_gen: debugfs knob to enable UBI general extra checks
 * @dfs_chk_gen: debugfs knob to enable UBI general extra checks
@@ -368,6 +383,9 @@ struct ubi_wl_entry;
 * @dfs_disable_bgt: debugfs knob to disable the background task
 * @dfs_disable_bgt: debugfs knob to disable the background task
 * @dfs_emulate_bitflips: debugfs knob to emulate bit-flips
 * @dfs_emulate_bitflips: debugfs knob to emulate bit-flips
 * @dfs_emulate_io_failures: debugfs knob to emulate write/erase failures
 * @dfs_emulate_io_failures: debugfs knob to emulate write/erase failures
 * @dfs_emulate_power_cut: debugfs knob to emulate power cuts
 * @dfs_power_cut_min: debugfs knob for minimum writes before power cut
 * @dfs_power_cut_max: debugfs knob for maximum writes until power cut
 */
 */
struct ubi_debug_info {
struct ubi_debug_info {
	unsigned int chk_gen:1;
	unsigned int chk_gen:1;
@@ -376,6 +394,10 @@ struct ubi_debug_info {
	unsigned int disable_bgt:1;
	unsigned int disable_bgt:1;
	unsigned int emulate_bitflips:1;
	unsigned int emulate_bitflips:1;
	unsigned int emulate_io_failures:1;
	unsigned int emulate_io_failures:1;
	unsigned int emulate_power_cut:2;
	unsigned int power_cut_counter;
	unsigned int power_cut_min;
	unsigned int power_cut_max;
	char dfs_dir_name[UBI_DFS_DIR_LEN + 1];
	char dfs_dir_name[UBI_DFS_DIR_LEN + 1];
	struct dentry *dfs_dir;
	struct dentry *dfs_dir;
	struct dentry *dfs_chk_gen;
	struct dentry *dfs_chk_gen;
@@ -384,6 +406,9 @@ struct ubi_debug_info {
	struct dentry *dfs_disable_bgt;
	struct dentry *dfs_disable_bgt;
	struct dentry *dfs_emulate_bitflips;
	struct dentry *dfs_emulate_bitflips;
	struct dentry *dfs_emulate_io_failures;
	struct dentry *dfs_emulate_io_failures;
	struct dentry *dfs_emulate_power_cut;
	struct dentry *dfs_power_cut_min;
	struct dentry *dfs_power_cut_max;
};
};


/**
/**