123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465 |
- [[case]] # test running a filesystem to exhaustion
- define.LFS_ERASE_CYCLES = 10
- define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
- define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
- define.LFS_BADBLOCK_BEHAVIOR = [
- 'LFS_TESTBD_BADBLOCK_PROGERROR',
- 'LFS_TESTBD_BADBLOCK_ERASEERROR',
- 'LFS_TESTBD_BADBLOCK_READERROR',
- 'LFS_TESTBD_BADBLOCK_PROGNOOP',
- 'LFS_TESTBD_BADBLOCK_ERASENOOP',
- ]
- 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) {
- err = lfs_file_close(&lfs, &file);
- assert(err == 0 || err == LFS_ERR_NOSPC);
- lfs_unmount(&lfs) => 0;
- goto exhausted;
- }
- }
- err = lfs_file_close(&lfs, &file);
- assert(err == 0 || err == LFS_ERR_NOSPC);
- if (err == LFS_ERR_NOSPC) {
- lfs_unmount(&lfs) => 0;
- 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 test runs faster
- define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
- define.LFS_BADBLOCK_BEHAVIOR = [
- 'LFS_TESTBD_BADBLOCK_PROGERROR',
- 'LFS_TESTBD_BADBLOCK_ERASEERROR',
- 'LFS_TESTBD_BADBLOCK_READERROR',
- 'LFS_TESTBD_BADBLOCK_PROGNOOP',
- 'LFS_TESTBD_BADBLOCK_ERASENOOP',
- ]
- 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) {
- err = lfs_file_close(&lfs, &file);
- assert(err == 0 || err == LFS_ERR_NOSPC);
- lfs_unmount(&lfs) => 0;
- goto exhausted;
- }
- }
- err = lfs_file_close(&lfs, &file);
- assert(err == 0 || err == LFS_ERR_NOSPC);
- if (err == LFS_ERR_NOSPC) {
- lfs_unmount(&lfs) => 0;
- 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 = 20
- define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
- define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
- define.FILES = 10
- code = '''
- 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;
- }
- 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) {
- err = lfs_file_close(&lfs, &file);
- assert(err == 0 || err == LFS_ERR_NOSPC);
- lfs_unmount(&lfs) => 0;
- goto exhausted;
- }
- }
- err = lfs_file_close(&lfs, &file);
- assert(err == 0 || err == LFS_ERR_NOSPC);
- if (err == LFS_ERR_NOSPC) {
- lfs_unmount(&lfs) => 0;
- 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 ~10% error
- LFS_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]);
- '''
- [[case]] # wear-level test + expanding superblock
- define.LFS_ERASE_CYCLES = 20
- define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
- define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
- define.FILES = 10
- code = '''
- 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;
- }
- 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) {
- err = lfs_file_close(&lfs, &file);
- assert(err == 0 || err == LFS_ERR_NOSPC);
- lfs_unmount(&lfs) => 0;
- goto exhausted;
- }
- }
- err = lfs_file_close(&lfs, &file);
- assert(err == 0 || err == LFS_ERR_NOSPC);
- if (err == LFS_ERR_NOSPC) {
- lfs_unmount(&lfs) => 0;
- 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 ~10% error
- LFS_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]);
- '''
- [[case]] # test that we wear blocks roughly evenly
- define.LFS_ERASE_CYCLES = 0xffffffff
- define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster
- define.LFS_BLOCK_CYCLES = [5, 4, 3, 2, 1]
- define.CYCLES = 100
- define.FILES = 10
- if = 'LFS_BLOCK_CYCLES < CYCLES/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 (cycle < CYCLES) {
- 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 << 4; //((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) {
- err = lfs_file_close(&lfs, &file);
- assert(err == 0 || err == LFS_ERR_NOSPC);
- lfs_unmount(&lfs) => 0;
- goto exhausted;
- }
- }
- err = lfs_file_close(&lfs, &file);
- assert(err == 0 || err == LFS_ERR_NOSPC);
- if (err == LFS_ERR_NOSPC) {
- lfs_unmount(&lfs) => 0;
- goto exhausted;
- }
- }
- for (uint32_t i = 0; i < FILES; i++) {
- // check for errors
- sprintf(path, "roadrunner/test%d", i);
- srand(cycle * i);
- size = 1 << 4; //((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);
- // check the wear on our block device
- lfs_testbd_wear_t minwear = -1;
- lfs_testbd_wear_t totalwear = 0;
- lfs_testbd_wear_t maxwear = 0;
- // skip 0 and 1 as superblock movement is intentionally avoided
- for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) {
- lfs_testbd_wear_t wear = lfs_testbd_getwear(&cfg, b);
- printf("%08x: wear %d\n", b, wear);
- assert(wear >= 0);
- if (wear < minwear) {
- minwear = wear;
- }
- if (wear > maxwear) {
- maxwear = wear;
- }
- totalwear += wear;
- }
- lfs_testbd_wear_t avgwear = totalwear / LFS_BLOCK_COUNT;
- LFS_WARN("max wear: %d cycles", maxwear);
- LFS_WARN("avg wear: %d cycles", totalwear / LFS_BLOCK_COUNT);
- LFS_WARN("min wear: %d cycles", minwear);
- // find standard deviation^2
- lfs_testbd_wear_t dev2 = 0;
- for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) {
- lfs_testbd_wear_t wear = lfs_testbd_getwear(&cfg, b);
- assert(wear >= 0);
- lfs_testbd_swear_t diff = wear - avgwear;
- dev2 += diff*diff;
- }
- dev2 /= totalwear;
- LFS_WARN("std dev^2: %d", dev2);
- assert(dev2 < 8);
- '''
|