mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-25 10:59:05 +00:00
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: David Oberhollenzer <david.oberhollenzer@sigma-star.at> Signed-off-by: Richard Weinberger <richard@nod.at>
This commit is contained in:
parent
1a7e985dd1
commit
5026906742
@ -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;
|
||||||
|
}
|
||||||
|
@ -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__ */
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user