diff options
author | david.oberhollenzer@sigma-star.at <david.oberhollenzer@sigma-star.at> | 2015-03-26 18:59:50 -0400 |
---|---|---|
committer | Richard Weinberger <richard@nod.at> | 2015-04-13 15:05:17 -0400 |
commit | 502690674281a047abd45f81e64c498bc23a8bb3 (patch) | |
tree | 5aa08ca80d7de94bf0b3050a7f458ad0ed25d004 | |
parent | 1a7e985dd1bf5939af0f56bbd6d13d2caf48dd63 (diff) |
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>
-rw-r--r-- | drivers/mtd/ubi/debug.c | 89 | ||||
-rw-r--r-- | drivers/mtd/ubi/debug.h | 2 | ||||
-rw-r--r-- | drivers/mtd/ubi/io.c | 6 | ||||
-rw-r--r-- | drivers/mtd/ubi/ubi.h | 25 |
4 files changed, 120 insertions, 2 deletions
diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c index 224fed085102..b077e43b5ba9 100644 --- a/drivers/mtd/ubi/debug.c +++ b/drivers/mtd/ubi/debug.c | |||
@@ -263,7 +263,7 @@ static ssize_t dfs_file_read(struct file *file, char __user *user_buf, | |||
263 | struct dentry *dent = file->f_path.dentry; | 263 | struct dentry *dent = file->f_path.dentry; |
264 | struct ubi_device *ubi; | 264 | struct ubi_device *ubi; |
265 | struct ubi_debug_info *d; | 265 | struct ubi_debug_info *d; |
266 | char buf[3]; | 266 | char buf[8]; |
267 | int val; | 267 | int val; |
268 | 268 | ||
269 | ubi = ubi_get_device(ubi_num); | 269 | ubi = ubi_get_device(ubi_num); |
@@ -283,6 +283,22 @@ static ssize_t dfs_file_read(struct file *file, char __user *user_buf, | |||
283 | val = d->emulate_bitflips; | 283 | val = d->emulate_bitflips; |
284 | else if (dent == d->dfs_emulate_io_failures) | 284 | else if (dent == d->dfs_emulate_io_failures) |
285 | val = d->emulate_io_failures; | 285 | val = d->emulate_io_failures; |
286 | else if (dent == d->dfs_emulate_power_cut) { | ||
287 | snprintf(buf, sizeof(buf), "%u\n", d->emulate_power_cut); | ||
288 | count = simple_read_from_buffer(user_buf, count, ppos, | ||
289 | buf, strlen(buf)); | ||
290 | goto out; | ||
291 | } else if (dent == d->dfs_power_cut_min) { | ||
292 | snprintf(buf, sizeof(buf), "%u\n", d->power_cut_min); | ||
293 | count = simple_read_from_buffer(user_buf, count, ppos, | ||
294 | buf, strlen(buf)); | ||
295 | goto out; | ||
296 | } else if (dent == d->dfs_power_cut_max) { | ||
297 | snprintf(buf, sizeof(buf), "%u\n", d->power_cut_max); | ||
298 | count = simple_read_from_buffer(user_buf, count, ppos, | ||
299 | buf, strlen(buf)); | ||
300 | goto out; | ||
301 | } | ||
286 | else { | 302 | else { |
287 | count = -EINVAL; | 303 | count = -EINVAL; |
288 | goto out; | 304 | goto out; |
@@ -311,7 +327,7 @@ static ssize_t dfs_file_write(struct file *file, const char __user *user_buf, | |||
311 | struct ubi_device *ubi; | 327 | struct ubi_device *ubi; |
312 | struct ubi_debug_info *d; | 328 | struct ubi_debug_info *d; |
313 | size_t buf_size; | 329 | size_t buf_size; |
314 | char buf[8]; | 330 | char buf[8] = {0}; |
315 | int val; | 331 | int val; |
316 | 332 | ||
317 | ubi = ubi_get_device(ubi_num); | 333 | ubi = ubi_get_device(ubi_num); |
@@ -325,6 +341,21 @@ static ssize_t dfs_file_write(struct file *file, const char __user *user_buf, | |||
325 | goto out; | 341 | goto out; |
326 | } | 342 | } |
327 | 343 | ||
344 | if (dent == d->dfs_power_cut_min) { | ||
345 | if (kstrtouint(buf, 0, &d->power_cut_min) != 0) | ||
346 | count = -EINVAL; | ||
347 | goto out; | ||
348 | } else if (dent == d->dfs_power_cut_max) { | ||
349 | if (kstrtouint(buf, 0, &d->power_cut_max) != 0) | ||
350 | count = -EINVAL; | ||
351 | goto out; | ||
352 | } else if (dent == d->dfs_emulate_power_cut) { | ||
353 | if (kstrtoint(buf, 0, &val) != 0) | ||
354 | count = -EINVAL; | ||
355 | d->emulate_power_cut = val; | ||
356 | goto out; | ||
357 | } | ||
358 | |||
328 | if (buf[0] == '1') | 359 | if (buf[0] == '1') |
329 | val = 1; | 360 | val = 1; |
330 | else if (buf[0] == '0') | 361 | else if (buf[0] == '0') |
@@ -438,6 +469,27 @@ int ubi_debugfs_init_dev(struct ubi_device *ubi) | |||
438 | goto out_remove; | 469 | goto out_remove; |
439 | d->dfs_emulate_io_failures = dent; | 470 | d->dfs_emulate_io_failures = dent; |
440 | 471 | ||
472 | fname = "tst_emulate_power_cut"; | ||
473 | dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, | ||
474 | &dfs_fops); | ||
475 | if (IS_ERR_OR_NULL(dent)) | ||
476 | goto out_remove; | ||
477 | d->dfs_emulate_power_cut = dent; | ||
478 | |||
479 | fname = "tst_emulate_power_cut_min"; | ||
480 | dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, | ||
481 | &dfs_fops); | ||
482 | if (IS_ERR_OR_NULL(dent)) | ||
483 | goto out_remove; | ||
484 | d->dfs_power_cut_min = dent; | ||
485 | |||
486 | fname = "tst_emulate_power_cut_max"; | ||
487 | dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, | ||
488 | &dfs_fops); | ||
489 | if (IS_ERR_OR_NULL(dent)) | ||
490 | goto out_remove; | ||
491 | d->dfs_power_cut_max = dent; | ||
492 | |||
441 | return 0; | 493 | return 0; |
442 | 494 | ||
443 | out_remove: | 495 | out_remove: |
@@ -458,3 +510,36 @@ void ubi_debugfs_exit_dev(struct ubi_device *ubi) | |||
458 | if (IS_ENABLED(CONFIG_DEBUG_FS)) | 510 | if (IS_ENABLED(CONFIG_DEBUG_FS)) |
459 | debugfs_remove_recursive(ubi->dbg.dfs_dir); | 511 | debugfs_remove_recursive(ubi->dbg.dfs_dir); |
460 | } | 512 | } |
513 | |||
514 | /** | ||
515 | * ubi_dbg_power_cut - emulate a power cut if it is time to do so | ||
516 | * @ubi: UBI device description object | ||
517 | * @caller: Flags set to indicate from where the function is being called | ||
518 | * | ||
519 | * Returns non-zero if a power cut was emulated, zero if not. | ||
520 | */ | ||
521 | int ubi_dbg_power_cut(struct ubi_device *ubi, int caller) | ||
522 | { | ||
523 | unsigned int range; | ||
524 | |||
525 | if ((ubi->dbg.emulate_power_cut & caller) == 0) | ||
526 | return 0; | ||
527 | |||
528 | if (ubi->dbg.power_cut_counter == 0) { | ||
529 | ubi->dbg.power_cut_counter = ubi->dbg.power_cut_min; | ||
530 | |||
531 | if (ubi->dbg.power_cut_max > ubi->dbg.power_cut_min) { | ||
532 | range = ubi->dbg.power_cut_max - ubi->dbg.power_cut_min; | ||
533 | ubi->dbg.power_cut_counter += prandom_u32() % range; | ||
534 | } | ||
535 | return 0; | ||
536 | } | ||
537 | |||
538 | ubi->dbg.power_cut_counter--; | ||
539 | if (ubi->dbg.power_cut_counter) | ||
540 | return 0; | ||
541 | |||
542 | ubi_msg(ubi, "XXXXXXXXXXXXXXX emulating a power cut XXXXXXXXXXXXXXXX"); | ||
543 | ubi_ro_mode(ubi); | ||
544 | return 1; | ||
545 | } | ||
diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h index c6ea4431acf3..eb8985e5c178 100644 --- a/drivers/mtd/ubi/debug.h +++ b/drivers/mtd/ubi/debug.h | |||
@@ -137,4 +137,6 @@ static inline void ubi_enable_dbg_chk_fastmap(struct ubi_device *ubi) | |||
137 | { | 137 | { |
138 | ubi->dbg.chk_fastmap = 1; | 138 | ubi->dbg.chk_fastmap = 1; |
139 | } | 139 | } |
140 | |||
141 | int ubi_dbg_power_cut(struct ubi_device *ubi, int caller); | ||
140 | #endif /* !__UBI_DEBUG_H__ */ | 142 | #endif /* !__UBI_DEBUG_H__ */ |
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index ed0bcb35472f..5bbd1f094f4e 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c | |||
@@ -859,6 +859,9 @@ int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum, | |||
859 | if (err) | 859 | if (err) |
860 | return err; | 860 | return err; |
861 | 861 | ||
862 | if (ubi_dbg_power_cut(ubi, POWER_CUT_EC_WRITE)) | ||
863 | return -EROFS; | ||
864 | |||
862 | err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize); | 865 | err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize); |
863 | return err; | 866 | return err; |
864 | } | 867 | } |
@@ -1106,6 +1109,9 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, | |||
1106 | if (err) | 1109 | if (err) |
1107 | return err; | 1110 | return err; |
1108 | 1111 | ||
1112 | if (ubi_dbg_power_cut(ubi, POWER_CUT_VID_WRITE)) | ||
1113 | return -EROFS; | ||
1114 | |||
1109 | p = (char *)vid_hdr - ubi->vid_hdr_shift; | 1115 | p = (char *)vid_hdr - ubi->vid_hdr_shift; |
1110 | err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset, | 1116 | err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset, |
1111 | ubi->vid_hdr_alsize); | 1117 | ubi->vid_hdr_alsize); |
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 3d58c749f9f1..c998212fc680 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h | |||
@@ -151,6 +151,17 @@ enum { | |||
151 | UBI_BAD_FASTMAP, | 151 | UBI_BAD_FASTMAP, |
152 | }; | 152 | }; |
153 | 153 | ||
154 | /* | ||
155 | * Flags for emulate_power_cut in ubi_debug_info | ||
156 | * | ||
157 | * POWER_CUT_EC_WRITE: Emulate a power cut when writing an EC header | ||
158 | * POWER_CUT_VID_WRITE: Emulate a power cut when writing a VID header | ||
159 | */ | ||
160 | enum { | ||
161 | POWER_CUT_EC_WRITE = 0x01, | ||
162 | POWER_CUT_VID_WRITE = 0x02, | ||
163 | }; | ||
164 | |||
154 | /** | 165 | /** |
155 | * struct ubi_wl_entry - wear-leveling entry. | 166 | * struct ubi_wl_entry - wear-leveling entry. |
156 | * @u.rb: link in the corresponding (free/used) RB-tree | 167 | * @u.rb: link in the corresponding (free/used) RB-tree |
@@ -360,6 +371,10 @@ struct ubi_wl_entry; | |||
360 | * @disable_bgt: disable the background task for testing purposes | 371 | * @disable_bgt: disable the background task for testing purposes |
361 | * @emulate_bitflips: emulate bit-flips for testing purposes | 372 | * @emulate_bitflips: emulate bit-flips for testing purposes |
362 | * @emulate_io_failures: emulate write/erase failures for testing purposes | 373 | * @emulate_io_failures: emulate write/erase failures for testing purposes |
374 | * @emulate_power_cut: emulate power cut for testing purposes | ||
375 | * @power_cut_counter: count down for writes left until emulated power cut | ||
376 | * @power_cut_min: minimum number of writes before emulating a power cut | ||
377 | * @power_cut_max: maximum number of writes until emulating a power cut | ||
363 | * @dfs_dir_name: name of debugfs directory containing files of this UBI device | 378 | * @dfs_dir_name: name of debugfs directory containing files of this UBI device |
364 | * @dfs_dir: direntry object of the UBI device debugfs directory | 379 | * @dfs_dir: direntry object of the UBI device debugfs directory |
365 | * @dfs_chk_gen: debugfs knob to enable UBI general extra checks | 380 | * @dfs_chk_gen: debugfs knob to enable UBI general extra checks |
@@ -368,6 +383,9 @@ struct ubi_wl_entry; | |||
368 | * @dfs_disable_bgt: debugfs knob to disable the background task | 383 | * @dfs_disable_bgt: debugfs knob to disable the background task |
369 | * @dfs_emulate_bitflips: debugfs knob to emulate bit-flips | 384 | * @dfs_emulate_bitflips: debugfs knob to emulate bit-flips |
370 | * @dfs_emulate_io_failures: debugfs knob to emulate write/erase failures | 385 | * @dfs_emulate_io_failures: debugfs knob to emulate write/erase failures |
386 | * @dfs_emulate_power_cut: debugfs knob to emulate power cuts | ||
387 | * @dfs_power_cut_min: debugfs knob for minimum writes before power cut | ||
388 | * @dfs_power_cut_max: debugfs knob for maximum writes until power cut | ||
371 | */ | 389 | */ |
372 | struct ubi_debug_info { | 390 | struct ubi_debug_info { |
373 | unsigned int chk_gen:1; | 391 | unsigned int chk_gen:1; |
@@ -376,6 +394,10 @@ struct ubi_debug_info { | |||
376 | unsigned int disable_bgt:1; | 394 | unsigned int disable_bgt:1; |
377 | unsigned int emulate_bitflips:1; | 395 | unsigned int emulate_bitflips:1; |
378 | unsigned int emulate_io_failures:1; | 396 | unsigned int emulate_io_failures:1; |
397 | unsigned int emulate_power_cut:2; | ||
398 | unsigned int power_cut_counter; | ||
399 | unsigned int power_cut_min; | ||
400 | unsigned int power_cut_max; | ||
379 | char dfs_dir_name[UBI_DFS_DIR_LEN + 1]; | 401 | char dfs_dir_name[UBI_DFS_DIR_LEN + 1]; |
380 | struct dentry *dfs_dir; | 402 | struct dentry *dfs_dir; |
381 | struct dentry *dfs_chk_gen; | 403 | struct dentry *dfs_chk_gen; |
@@ -384,6 +406,9 @@ struct ubi_debug_info { | |||
384 | struct dentry *dfs_disable_bgt; | 406 | struct dentry *dfs_disable_bgt; |
385 | struct dentry *dfs_emulate_bitflips; | 407 | struct dentry *dfs_emulate_bitflips; |
386 | struct dentry *dfs_emulate_io_failures; | 408 | struct dentry *dfs_emulate_io_failures; |
409 | struct dentry *dfs_emulate_power_cut; | ||
410 | struct dentry *dfs_power_cut_min; | ||
411 | struct dentry *dfs_power_cut_max; | ||
387 | }; | 412 | }; |
388 | 413 | ||
389 | /** | 414 | /** |