[[case]] # test running a filesystem to exhaustion define.LFS_ERASE_CYCLES = 10 define.LFS_BLOCK_COUNT = 256 # small bd so it runs faster define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2' define.LFS_BADBLOCK_BEHAVIOR = [ 'LFS_TESTBD_BADBLOCK_NOPROG', 'LFS_TESTBD_BADBLOCK_NOERASE', 'LFS_TESTBD_BADBLOCK_NOREAD', ] define.FILES = 10 code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; lfs_mkdir(&lfs, "roadrunner") => 0; lfs_unmount(&lfs) => 0; uint32_t cycle = 0; while (true) { lfs_mount(&lfs, &cfg) => 0; for (uint32_t i = 0; i < FILES; i++) { // chose name, roughly random seed, and random 2^n size sprintf(path, "roadrunner/test%d", i); srand(cycle * i); size = 1 << ((rand() % 10)+2); lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; for (lfs_size_t j = 0; j < size; j++) { char c = 'a' + (rand() % 26); lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); assert(res == 1 || res == LFS_ERR_NOSPC); if (res == LFS_ERR_NOSPC) { goto exhausted; } } err = lfs_file_close(&lfs, &file); assert(err == 0 || err == LFS_ERR_NOSPC); if (err == LFS_ERR_NOSPC) { goto exhausted; } } for (uint32_t i = 0; i < FILES; i++) { // check for errors sprintf(path, "roadrunner/test%d", i); srand(cycle * i); size = 1 << ((rand() % 10)+2); lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; for (lfs_size_t j = 0; j < size; j++) { char c = 'a' + (rand() % 26); char r; lfs_file_read(&lfs, &file, &r, 1) => 1; assert(r == c); } lfs_file_close(&lfs, &file) => 0; } lfs_unmount(&lfs) => 0; cycle += 1; } exhausted: // should still be readable lfs_mount(&lfs, &cfg) => 0; for (uint32_t i = 0; i < FILES; i++) { // check for errors sprintf(path, "roadrunner/test%d", i); lfs_stat(&lfs, path, &info) => 0; } lfs_unmount(&lfs) => 0; LFS_WARN("completed %d cycles", cycle); ''' [[case]] # test running a filesystem to exhaustion # which also requires expanding superblocks define.LFS_ERASE_CYCLES = 10 define.LFS_BLOCK_COUNT = 256 # small bd so it runs faster define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2' define.LFS_BADBLOCK_BEHAVIOR = [ 'LFS_TESTBD_BADBLOCK_NOPROG', 'LFS_TESTBD_BADBLOCK_NOERASE', 'LFS_TESTBD_BADBLOCK_NOREAD', ] define.FILES = 10 code = ''' lfs_format(&lfs, &cfg) => 0; uint32_t cycle = 0; while (true) { lfs_mount(&lfs, &cfg) => 0; for (uint32_t i = 0; i < FILES; i++) { // chose name, roughly random seed, and random 2^n size sprintf(path, "test%d", i); srand(cycle * i); size = 1 << ((rand() % 10)+2); lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; for (lfs_size_t j = 0; j < size; j++) { char c = 'a' + (rand() % 26); lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); assert(res == 1 || res == LFS_ERR_NOSPC); if (res == LFS_ERR_NOSPC) { goto exhausted; } } err = lfs_file_close(&lfs, &file); assert(err == 0 || err == LFS_ERR_NOSPC); if (err == LFS_ERR_NOSPC) { goto exhausted; } } for (uint32_t i = 0; i < FILES; i++) { // check for errors sprintf(path, "test%d", i); srand(cycle * i); size = 1 << ((rand() % 10)+2); lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; for (lfs_size_t j = 0; j < size; j++) { char c = 'a' + (rand() % 26); char r; lfs_file_read(&lfs, &file, &r, 1) => 1; assert(r == c); } lfs_file_close(&lfs, &file) => 0; } lfs_unmount(&lfs) => 0; cycle += 1; } exhausted: // should still be readable lfs_mount(&lfs, &cfg) => 0; for (uint32_t i = 0; i < FILES; i++) { // check for errors sprintf(path, "test%d", i); lfs_stat(&lfs, path, &info) => 0; } lfs_unmount(&lfs) => 0; LFS_WARN("completed %d cycles", cycle); ''' # These are a sort of high-level litmus test for wear-leveling. One definition # of wear-leveling is that increasing a block device's space translates directly # into increasing the block devices lifetime. This is something we can actually # check for. [[case]] # wear-level test running a filesystem to exhaustion define.LFS_ERASE_CYCLES = 10 define.LFS_BLOCK_COUNT = 256 # small bd so it runs faster define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2' define.LFS_BADBLOCK_BEHAVIOR = [ 'LFS_TESTBD_BADBLOCK_NOPROG', 'LFS_TESTBD_BADBLOCK_NOERASE', 'LFS_TESTBD_BADBLOCK_NOREAD', ] define.FILES = 10 code = ''' lfs_format(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0; lfs_mkdir(&lfs, "roadrunner") => 0; lfs_unmount(&lfs) => 0; uint32_t run_cycles[2]; const uint32_t run_block_count[2] = {LFS_BLOCK_COUNT/2, LFS_BLOCK_COUNT}; for (int run = 0; run < 2; run++) { for (lfs_block_t b = 0; b < LFS_BLOCK_COUNT; b++) { lfs_testbd_setwear(&cfg, b, (b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0; } uint32_t cycle = 0; while (true) { lfs_mount(&lfs, &cfg) => 0; for (uint32_t i = 0; i < FILES; i++) { // chose name, roughly random seed, and random 2^n size sprintf(path, "roadrunner/test%d", i); srand(cycle * i); size = 1 << ((rand() % 10)+2); lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; for (lfs_size_t j = 0; j < size; j++) { char c = 'a' + (rand() % 26); lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); assert(res == 1 || res == LFS_ERR_NOSPC); if (res == LFS_ERR_NOSPC) { goto exhausted; } } err = lfs_file_close(&lfs, &file); assert(err == 0 || err == LFS_ERR_NOSPC); if (err == LFS_ERR_NOSPC) { goto exhausted; } } for (uint32_t i = 0; i < FILES; i++) { // check for errors sprintf(path, "roadrunner/test%d", i); srand(cycle * i); size = 1 << ((rand() % 10)+2); lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; for (lfs_size_t j = 0; j < size; j++) { char c = 'a' + (rand() % 26); char r; lfs_file_read(&lfs, &file, &r, 1) => 1; assert(r == c); } lfs_file_close(&lfs, &file) => 0; } lfs_unmount(&lfs) => 0; cycle += 1; } exhausted: // should still be readable lfs_mount(&lfs, &cfg) => 0; for (uint32_t i = 0; i < FILES; i++) { // check for errors sprintf(path, "roadrunner/test%d", i); lfs_stat(&lfs, path, &info) => 0; } lfs_unmount(&lfs) => 0; run_cycles[run] = cycle; LFS_WARN("completed %d blocks %d cycles", run_block_count[run], run_cycles[run]); } // check we increased the lifetime by 2x with ~5% error LFS_ASSERT(run_cycles[1] > 2*run_cycles[0]-run_cycles[0]/20); ''' [[case]] # wear-level test + expanding superblock define.LFS_ERASE_CYCLES = 10 define.LFS_BLOCK_COUNT = 256 # small bd so it runs faster define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2' define.LFS_BADBLOCK_BEHAVIOR = [ 'LFS_TESTBD_BADBLOCK_NOPROG', 'LFS_TESTBD_BADBLOCK_NOERASE', 'LFS_TESTBD_BADBLOCK_NOREAD', ] define.FILES = 10 code = ''' lfs_format(&lfs, &cfg) => 0; uint32_t run_cycles[2]; const uint32_t run_block_count[2] = {LFS_BLOCK_COUNT/2, LFS_BLOCK_COUNT}; for (int run = 0; run < 2; run++) { for (lfs_block_t b = 0; b < LFS_BLOCK_COUNT; b++) { lfs_testbd_setwear(&cfg, b, (b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0; } uint32_t cycle = 0; while (true) { lfs_mount(&lfs, &cfg) => 0; for (uint32_t i = 0; i < FILES; i++) { // chose name, roughly random seed, and random 2^n size sprintf(path, "test%d", i); srand(cycle * i); size = 1 << ((rand() % 10)+2); lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; for (lfs_size_t j = 0; j < size; j++) { char c = 'a' + (rand() % 26); lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); assert(res == 1 || res == LFS_ERR_NOSPC); if (res == LFS_ERR_NOSPC) { goto exhausted; } } err = lfs_file_close(&lfs, &file); assert(err == 0 || err == LFS_ERR_NOSPC); if (err == LFS_ERR_NOSPC) { goto exhausted; } } for (uint32_t i = 0; i < FILES; i++) { // check for errors sprintf(path, "test%d", i); srand(cycle * i); size = 1 << ((rand() % 10)+2); lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; for (lfs_size_t j = 0; j < size; j++) { char c = 'a' + (rand() % 26); char r; lfs_file_read(&lfs, &file, &r, 1) => 1; assert(r == c); } lfs_file_close(&lfs, &file) => 0; } lfs_unmount(&lfs) => 0; cycle += 1; } exhausted: // should still be readable lfs_mount(&lfs, &cfg) => 0; for (uint32_t i = 0; i < FILES; i++) { // check for errors sprintf(path, "test%d", i); lfs_stat(&lfs, path, &info) => 0; } lfs_unmount(&lfs) => 0; run_cycles[run] = cycle; LFS_WARN("completed %d blocks %d cycles", run_block_count[run], run_cycles[run]); } // check we increased the lifetime by 2x with ~5% error LFS_ASSERT(run_cycles[1] > 2*run_cycles[0]-run_cycles[0]/20); '''