Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

david.oberhollenzer@sigma-star.at and committed by
Richard Weinberger
50269067 1a7e985d

+120 -2
+87 -2
drivers/mtd/ubi/debug.c
··· 263 263 struct dentry *dent = file->f_path.dentry; 264 264 struct ubi_device *ubi; 265 265 struct ubi_debug_info *d; 266 - char buf[3]; 266 + char buf[8]; 267 267 int val; 268 268 269 269 ubi = ubi_get_device(ubi_num); ··· 283 283 val = d->emulate_bitflips; 284 284 else if (dent == d->dfs_emulate_io_failures) 285 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 302 else { 287 303 count = -EINVAL; 288 304 goto out; ··· 327 311 struct ubi_device *ubi; 328 312 struct ubi_debug_info *d; 329 313 size_t buf_size; 330 - char buf[8]; 314 + char buf[8] = {0}; 331 315 int val; 332 316 333 317 ubi = ubi_get_device(ubi_num); ··· 338 322 buf_size = min_t(size_t, count, (sizeof(buf) - 1)); 339 323 if (copy_from_user(buf, user_buf, buf_size)) { 340 324 count = -EFAULT; 325 + goto out; 326 + } 327 + 328 + if (dent == d->dfs_power_cut_min) { 329 + if (kstrtouint(buf, 0, &d->power_cut_min) != 0) 330 + count = -EINVAL; 331 + goto out; 332 + } else if (dent == d->dfs_power_cut_max) { 333 + if (kstrtouint(buf, 0, &d->power_cut_max) != 0) 334 + count = -EINVAL; 335 + goto out; 336 + } else if (dent == d->dfs_emulate_power_cut) { 337 + if (kstrtoint(buf, 0, &val) != 0) 338 + count = -EINVAL; 339 + d->emulate_power_cut = val; 341 340 goto out; 342 341 } 343 342 ··· 469 438 goto out_remove; 470 439 d->dfs_emulate_io_failures = dent; 471 440 441 + fname = "tst_emulate_power_cut"; 442 + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, 443 + &dfs_fops); 444 + if (IS_ERR_OR_NULL(dent)) 445 + goto out_remove; 446 + d->dfs_emulate_power_cut = dent; 447 + 448 + fname = "tst_emulate_power_cut_min"; 449 + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, 450 + &dfs_fops); 451 + if (IS_ERR_OR_NULL(dent)) 452 + goto out_remove; 453 + d->dfs_power_cut_min = dent; 454 + 455 + fname = "tst_emulate_power_cut_max"; 456 + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, 457 + &dfs_fops); 458 + if (IS_ERR_OR_NULL(dent)) 459 + goto out_remove; 460 + d->dfs_power_cut_max = dent; 461 + 472 462 return 0; 473 463 474 464 out_remove: ··· 509 457 { 510 458 if (IS_ENABLED(CONFIG_DEBUG_FS)) 511 459 debugfs_remove_recursive(ubi->dbg.dfs_dir); 460 + } 461 + 462 + /** 463 + * ubi_dbg_power_cut - emulate a power cut if it is time to do so 464 + * @ubi: UBI device description object 465 + * @caller: Flags set to indicate from where the function is being called 466 + * 467 + * Returns non-zero if a power cut was emulated, zero if not. 468 + */ 469 + int ubi_dbg_power_cut(struct ubi_device *ubi, int caller) 470 + { 471 + unsigned int range; 472 + 473 + if ((ubi->dbg.emulate_power_cut & caller) == 0) 474 + return 0; 475 + 476 + if (ubi->dbg.power_cut_counter == 0) { 477 + ubi->dbg.power_cut_counter = ubi->dbg.power_cut_min; 478 + 479 + if (ubi->dbg.power_cut_max > ubi->dbg.power_cut_min) { 480 + range = ubi->dbg.power_cut_max - ubi->dbg.power_cut_min; 481 + ubi->dbg.power_cut_counter += prandom_u32() % range; 482 + } 483 + return 0; 484 + } 485 + 486 + ubi->dbg.power_cut_counter--; 487 + if (ubi->dbg.power_cut_counter) 488 + return 0; 489 + 490 + ubi_msg(ubi, "XXXXXXXXXXXXXXX emulating a power cut XXXXXXXXXXXXXXXX"); 491 + ubi_ro_mode(ubi); 492 + return 1; 512 493 }
+2
drivers/mtd/ubi/debug.h
··· 137 137 { 138 138 ubi->dbg.chk_fastmap = 1; 139 139 } 140 + 141 + int ubi_dbg_power_cut(struct ubi_device *ubi, int caller); 140 142 #endif /* !__UBI_DEBUG_H__ */
+6
drivers/mtd/ubi/io.c
··· 859 859 if (err) 860 860 return err; 861 861 862 + if (ubi_dbg_power_cut(ubi, POWER_CUT_EC_WRITE)) 863 + return -EROFS; 864 + 862 865 err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize); 863 866 return err; 864 867 } ··· 1108 1105 err = self_check_vid_hdr(ubi, pnum, vid_hdr); 1109 1106 if (err) 1110 1107 return err; 1108 + 1109 + if (ubi_dbg_power_cut(ubi, POWER_CUT_VID_WRITE)) 1110 + return -EROFS; 1111 1111 1112 1112 p = (char *)vid_hdr - ubi->vid_hdr_shift; 1113 1113 err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset,
+25
drivers/mtd/ubi/ubi.h
··· 151 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 166 * struct ubi_wl_entry - wear-leveling entry. 156 167 * @u.rb: link in the corresponding (free/used) RB-tree ··· 371 360 * @disable_bgt: disable the background task for testing purposes 372 361 * @emulate_bitflips: emulate bit-flips for testing purposes 373 362 * @emulate_io_failures: emulate write/erase failures for testing purposes 363 + * @emulate_power_cut: emulate power cut for testing purposes 364 + * @power_cut_counter: count down for writes left until emulated power cut 365 + * @power_cut_min: minimum number of writes before emulating a power cut 366 + * @power_cut_max: maximum number of writes until emulating a power cut 374 367 * @dfs_dir_name: name of debugfs directory containing files of this UBI device 375 368 * @dfs_dir: direntry object of the UBI device debugfs directory 376 369 * @dfs_chk_gen: debugfs knob to enable UBI general extra checks ··· 383 368 * @dfs_disable_bgt: debugfs knob to disable the background task 384 369 * @dfs_emulate_bitflips: debugfs knob to emulate bit-flips 385 370 * @dfs_emulate_io_failures: debugfs knob to emulate write/erase failures 371 + * @dfs_emulate_power_cut: debugfs knob to emulate power cuts 372 + * @dfs_power_cut_min: debugfs knob for minimum writes before power cut 373 + * @dfs_power_cut_max: debugfs knob for maximum writes until power cut 386 374 */ 387 375 struct ubi_debug_info { 388 376 unsigned int chk_gen:1; ··· 394 376 unsigned int disable_bgt:1; 395 377 unsigned int emulate_bitflips:1; 396 378 unsigned int emulate_io_failures:1; 379 + unsigned int emulate_power_cut:2; 380 + unsigned int power_cut_counter; 381 + unsigned int power_cut_min; 382 + unsigned int power_cut_max; 397 383 char dfs_dir_name[UBI_DFS_DIR_LEN + 1]; 398 384 struct dentry *dfs_dir; 399 385 struct dentry *dfs_chk_gen; ··· 406 384 struct dentry *dfs_disable_bgt; 407 385 struct dentry *dfs_emulate_bitflips; 408 386 struct dentry *dfs_emulate_io_failures; 387 + struct dentry *dfs_emulate_power_cut; 388 + struct dentry *dfs_power_cut_min; 389 + struct dentry *dfs_power_cut_max; 409 390 }; 410 391 411 392 /**