1[[case]] # test running a filesystem to exhaustion 2define.LFS_ERASE_CYCLES = 10 3define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster 4define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2' 5define.LFS_BADBLOCK_BEHAVIOR = [ 6 'LFS_TESTBD_BADBLOCK_PROGERROR', 7 'LFS_TESTBD_BADBLOCK_ERASEERROR', 8 'LFS_TESTBD_BADBLOCK_READERROR', 9 'LFS_TESTBD_BADBLOCK_PROGNOOP', 10 'LFS_TESTBD_BADBLOCK_ERASENOOP', 11] 12define.FILES = 10 13code = ''' 14 lfs_format(&lfs, &cfg) => 0; 15 lfs_mount(&lfs, &cfg) => 0; 16 lfs_mkdir(&lfs, "roadrunner") => 0; 17 lfs_unmount(&lfs) => 0; 18 19 uint32_t cycle = 0; 20 while (true) { 21 lfs_mount(&lfs, &cfg) => 0; 22 for (uint32_t i = 0; i < FILES; i++) { 23 // chose name, roughly random seed, and random 2^n size 24 sprintf(path, "roadrunner/test%d", i); 25 srand(cycle * i); 26 size = 1 << ((rand() % 10)+2); 27 28 lfs_file_open(&lfs, &file, path, 29 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; 30 31 for (lfs_size_t j = 0; j < size; j++) { 32 char c = 'a' + (rand() % 26); 33 lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); 34 assert(res == 1 || res == LFS_ERR_NOSPC); 35 if (res == LFS_ERR_NOSPC) { 36 err = lfs_file_close(&lfs, &file); 37 assert(err == 0 || err == LFS_ERR_NOSPC); 38 lfs_unmount(&lfs) => 0; 39 goto exhausted; 40 } 41 } 42 43 err = lfs_file_close(&lfs, &file); 44 assert(err == 0 || err == LFS_ERR_NOSPC); 45 if (err == LFS_ERR_NOSPC) { 46 lfs_unmount(&lfs) => 0; 47 goto exhausted; 48 } 49 } 50 51 for (uint32_t i = 0; i < FILES; i++) { 52 // check for errors 53 sprintf(path, "roadrunner/test%d", i); 54 srand(cycle * i); 55 size = 1 << ((rand() % 10)+2); 56 57 lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; 58 for (lfs_size_t j = 0; j < size; j++) { 59 char c = 'a' + (rand() % 26); 60 char r; 61 lfs_file_read(&lfs, &file, &r, 1) => 1; 62 assert(r == c); 63 } 64 65 lfs_file_close(&lfs, &file) => 0; 66 } 67 lfs_unmount(&lfs) => 0; 68 69 cycle += 1; 70 } 71 72exhausted: 73 // should still be readable 74 lfs_mount(&lfs, &cfg) => 0; 75 for (uint32_t i = 0; i < FILES; i++) { 76 // check for errors 77 sprintf(path, "roadrunner/test%d", i); 78 lfs_stat(&lfs, path, &info) => 0; 79 } 80 lfs_unmount(&lfs) => 0; 81 82 LFS_WARN("completed %d cycles", cycle); 83''' 84 85[[case]] # test running a filesystem to exhaustion 86 # which also requires expanding superblocks 87define.LFS_ERASE_CYCLES = 10 88define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster 89define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2' 90define.LFS_BADBLOCK_BEHAVIOR = [ 91 'LFS_TESTBD_BADBLOCK_PROGERROR', 92 'LFS_TESTBD_BADBLOCK_ERASEERROR', 93 'LFS_TESTBD_BADBLOCK_READERROR', 94 'LFS_TESTBD_BADBLOCK_PROGNOOP', 95 'LFS_TESTBD_BADBLOCK_ERASENOOP', 96] 97define.FILES = 10 98code = ''' 99 lfs_format(&lfs, &cfg) => 0; 100 101 uint32_t cycle = 0; 102 while (true) { 103 lfs_mount(&lfs, &cfg) => 0; 104 for (uint32_t i = 0; i < FILES; i++) { 105 // chose name, roughly random seed, and random 2^n size 106 sprintf(path, "test%d", i); 107 srand(cycle * i); 108 size = 1 << ((rand() % 10)+2); 109 110 lfs_file_open(&lfs, &file, path, 111 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; 112 113 for (lfs_size_t j = 0; j < size; j++) { 114 char c = 'a' + (rand() % 26); 115 lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); 116 assert(res == 1 || res == LFS_ERR_NOSPC); 117 if (res == LFS_ERR_NOSPC) { 118 err = lfs_file_close(&lfs, &file); 119 assert(err == 0 || err == LFS_ERR_NOSPC); 120 lfs_unmount(&lfs) => 0; 121 goto exhausted; 122 } 123 } 124 125 err = lfs_file_close(&lfs, &file); 126 assert(err == 0 || err == LFS_ERR_NOSPC); 127 if (err == LFS_ERR_NOSPC) { 128 lfs_unmount(&lfs) => 0; 129 goto exhausted; 130 } 131 } 132 133 for (uint32_t i = 0; i < FILES; i++) { 134 // check for errors 135 sprintf(path, "test%d", i); 136 srand(cycle * i); 137 size = 1 << ((rand() % 10)+2); 138 139 lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; 140 for (lfs_size_t j = 0; j < size; j++) { 141 char c = 'a' + (rand() % 26); 142 char r; 143 lfs_file_read(&lfs, &file, &r, 1) => 1; 144 assert(r == c); 145 } 146 147 lfs_file_close(&lfs, &file) => 0; 148 } 149 lfs_unmount(&lfs) => 0; 150 151 cycle += 1; 152 } 153 154exhausted: 155 // should still be readable 156 lfs_mount(&lfs, &cfg) => 0; 157 for (uint32_t i = 0; i < FILES; i++) { 158 // check for errors 159 sprintf(path, "test%d", i); 160 lfs_stat(&lfs, path, &info) => 0; 161 } 162 lfs_unmount(&lfs) => 0; 163 164 LFS_WARN("completed %d cycles", cycle); 165''' 166 167# These are a sort of high-level litmus test for wear-leveling. One definition 168# of wear-leveling is that increasing a block device's space translates directly 169# into increasing the block devices lifetime. This is something we can actually 170# check for. 171 172[[case]] # wear-level test running a filesystem to exhaustion 173define.LFS_ERASE_CYCLES = 20 174define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster 175define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2' 176define.FILES = 10 177code = ''' 178 uint32_t run_cycles[2]; 179 const uint32_t run_block_count[2] = {LFS_BLOCK_COUNT/2, LFS_BLOCK_COUNT}; 180 181 for (int run = 0; run < 2; run++) { 182 for (lfs_block_t b = 0; b < LFS_BLOCK_COUNT; b++) { 183 lfs_testbd_setwear(&cfg, b, 184 (b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0; 185 } 186 187 lfs_format(&lfs, &cfg) => 0; 188 lfs_mount(&lfs, &cfg) => 0; 189 lfs_mkdir(&lfs, "roadrunner") => 0; 190 lfs_unmount(&lfs) => 0; 191 192 uint32_t cycle = 0; 193 while (true) { 194 lfs_mount(&lfs, &cfg) => 0; 195 for (uint32_t i = 0; i < FILES; i++) { 196 // chose name, roughly random seed, and random 2^n size 197 sprintf(path, "roadrunner/test%d", i); 198 srand(cycle * i); 199 size = 1 << ((rand() % 10)+2); 200 201 lfs_file_open(&lfs, &file, path, 202 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; 203 204 for (lfs_size_t j = 0; j < size; j++) { 205 char c = 'a' + (rand() % 26); 206 lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); 207 assert(res == 1 || res == LFS_ERR_NOSPC); 208 if (res == LFS_ERR_NOSPC) { 209 err = lfs_file_close(&lfs, &file); 210 assert(err == 0 || err == LFS_ERR_NOSPC); 211 lfs_unmount(&lfs) => 0; 212 goto exhausted; 213 } 214 } 215 216 err = lfs_file_close(&lfs, &file); 217 assert(err == 0 || err == LFS_ERR_NOSPC); 218 if (err == LFS_ERR_NOSPC) { 219 lfs_unmount(&lfs) => 0; 220 goto exhausted; 221 } 222 } 223 224 for (uint32_t i = 0; i < FILES; i++) { 225 // check for errors 226 sprintf(path, "roadrunner/test%d", i); 227 srand(cycle * i); 228 size = 1 << ((rand() % 10)+2); 229 230 lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; 231 for (lfs_size_t j = 0; j < size; j++) { 232 char c = 'a' + (rand() % 26); 233 char r; 234 lfs_file_read(&lfs, &file, &r, 1) => 1; 235 assert(r == c); 236 } 237 238 lfs_file_close(&lfs, &file) => 0; 239 } 240 lfs_unmount(&lfs) => 0; 241 242 cycle += 1; 243 } 244 245exhausted: 246 // should still be readable 247 lfs_mount(&lfs, &cfg) => 0; 248 for (uint32_t i = 0; i < FILES; i++) { 249 // check for errors 250 sprintf(path, "roadrunner/test%d", i); 251 lfs_stat(&lfs, path, &info) => 0; 252 } 253 lfs_unmount(&lfs) => 0; 254 255 run_cycles[run] = cycle; 256 LFS_WARN("completed %d blocks %d cycles", 257 run_block_count[run], run_cycles[run]); 258 } 259 260 // check we increased the lifetime by 2x with ~10% error 261 LFS_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]); 262''' 263 264[[case]] # wear-level test + expanding superblock 265define.LFS_ERASE_CYCLES = 20 266define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster 267define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2' 268define.FILES = 10 269code = ''' 270 uint32_t run_cycles[2]; 271 const uint32_t run_block_count[2] = {LFS_BLOCK_COUNT/2, LFS_BLOCK_COUNT}; 272 273 for (int run = 0; run < 2; run++) { 274 for (lfs_block_t b = 0; b < LFS_BLOCK_COUNT; b++) { 275 lfs_testbd_setwear(&cfg, b, 276 (b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0; 277 } 278 279 lfs_format(&lfs, &cfg) => 0; 280 281 uint32_t cycle = 0; 282 while (true) { 283 lfs_mount(&lfs, &cfg) => 0; 284 for (uint32_t i = 0; i < FILES; i++) { 285 // chose name, roughly random seed, and random 2^n size 286 sprintf(path, "test%d", i); 287 srand(cycle * i); 288 size = 1 << ((rand() % 10)+2); 289 290 lfs_file_open(&lfs, &file, path, 291 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; 292 293 for (lfs_size_t j = 0; j < size; j++) { 294 char c = 'a' + (rand() % 26); 295 lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); 296 assert(res == 1 || res == LFS_ERR_NOSPC); 297 if (res == LFS_ERR_NOSPC) { 298 err = lfs_file_close(&lfs, &file); 299 assert(err == 0 || err == LFS_ERR_NOSPC); 300 lfs_unmount(&lfs) => 0; 301 goto exhausted; 302 } 303 } 304 305 err = lfs_file_close(&lfs, &file); 306 assert(err == 0 || err == LFS_ERR_NOSPC); 307 if (err == LFS_ERR_NOSPC) { 308 lfs_unmount(&lfs) => 0; 309 goto exhausted; 310 } 311 } 312 313 for (uint32_t i = 0; i < FILES; i++) { 314 // check for errors 315 sprintf(path, "test%d", i); 316 srand(cycle * i); 317 size = 1 << ((rand() % 10)+2); 318 319 lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; 320 for (lfs_size_t j = 0; j < size; j++) { 321 char c = 'a' + (rand() % 26); 322 char r; 323 lfs_file_read(&lfs, &file, &r, 1) => 1; 324 assert(r == c); 325 } 326 327 lfs_file_close(&lfs, &file) => 0; 328 } 329 lfs_unmount(&lfs) => 0; 330 331 cycle += 1; 332 } 333 334exhausted: 335 // should still be readable 336 lfs_mount(&lfs, &cfg) => 0; 337 for (uint32_t i = 0; i < FILES; i++) { 338 // check for errors 339 sprintf(path, "test%d", i); 340 lfs_stat(&lfs, path, &info) => 0; 341 } 342 lfs_unmount(&lfs) => 0; 343 344 run_cycles[run] = cycle; 345 LFS_WARN("completed %d blocks %d cycles", 346 run_block_count[run], run_cycles[run]); 347 } 348 349 // check we increased the lifetime by 2x with ~10% error 350 LFS_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]); 351''' 352 353[[case]] # test that we wear blocks roughly evenly 354define.LFS_ERASE_CYCLES = 0xffffffff 355define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster 356define.LFS_BLOCK_CYCLES = [5, 4, 3, 2, 1] 357define.CYCLES = 100 358define.FILES = 10 359if = 'LFS_BLOCK_CYCLES < CYCLES/10' 360code = ''' 361 lfs_format(&lfs, &cfg) => 0; 362 lfs_mount(&lfs, &cfg) => 0; 363 lfs_mkdir(&lfs, "roadrunner") => 0; 364 lfs_unmount(&lfs) => 0; 365 366 uint32_t cycle = 0; 367 while (cycle < CYCLES) { 368 lfs_mount(&lfs, &cfg) => 0; 369 for (uint32_t i = 0; i < FILES; i++) { 370 // chose name, roughly random seed, and random 2^n size 371 sprintf(path, "roadrunner/test%d", i); 372 srand(cycle * i); 373 size = 1 << 4; //((rand() % 10)+2); 374 375 lfs_file_open(&lfs, &file, path, 376 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; 377 378 for (lfs_size_t j = 0; j < size; j++) { 379 char c = 'a' + (rand() % 26); 380 lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); 381 assert(res == 1 || res == LFS_ERR_NOSPC); 382 if (res == LFS_ERR_NOSPC) { 383 err = lfs_file_close(&lfs, &file); 384 assert(err == 0 || err == LFS_ERR_NOSPC); 385 lfs_unmount(&lfs) => 0; 386 goto exhausted; 387 } 388 } 389 390 err = lfs_file_close(&lfs, &file); 391 assert(err == 0 || err == LFS_ERR_NOSPC); 392 if (err == LFS_ERR_NOSPC) { 393 lfs_unmount(&lfs) => 0; 394 goto exhausted; 395 } 396 } 397 398 for (uint32_t i = 0; i < FILES; i++) { 399 // check for errors 400 sprintf(path, "roadrunner/test%d", i); 401 srand(cycle * i); 402 size = 1 << 4; //((rand() % 10)+2); 403 404 lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; 405 for (lfs_size_t j = 0; j < size; j++) { 406 char c = 'a' + (rand() % 26); 407 char r; 408 lfs_file_read(&lfs, &file, &r, 1) => 1; 409 assert(r == c); 410 } 411 412 lfs_file_close(&lfs, &file) => 0; 413 } 414 lfs_unmount(&lfs) => 0; 415 416 cycle += 1; 417 } 418 419exhausted: 420 // should still be readable 421 lfs_mount(&lfs, &cfg) => 0; 422 for (uint32_t i = 0; i < FILES; i++) { 423 // check for errors 424 sprintf(path, "roadrunner/test%d", i); 425 lfs_stat(&lfs, path, &info) => 0; 426 } 427 lfs_unmount(&lfs) => 0; 428 429 LFS_WARN("completed %d cycles", cycle); 430 431 // check the wear on our block device 432 lfs_testbd_wear_t minwear = -1; 433 lfs_testbd_wear_t totalwear = 0; 434 lfs_testbd_wear_t maxwear = 0; 435 // skip 0 and 1 as superblock movement is intentionally avoided 436 for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) { 437 lfs_testbd_wear_t wear = lfs_testbd_getwear(&cfg, b); 438 printf("%08x: wear %d\n", b, wear); 439 assert(wear >= 0); 440 if (wear < minwear) { 441 minwear = wear; 442 } 443 if (wear > maxwear) { 444 maxwear = wear; 445 } 446 totalwear += wear; 447 } 448 lfs_testbd_wear_t avgwear = totalwear / LFS_BLOCK_COUNT; 449 LFS_WARN("max wear: %d cycles", maxwear); 450 LFS_WARN("avg wear: %d cycles", totalwear / LFS_BLOCK_COUNT); 451 LFS_WARN("min wear: %d cycles", minwear); 452 453 // find standard deviation^2 454 lfs_testbd_wear_t dev2 = 0; 455 for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) { 456 lfs_testbd_wear_t wear = lfs_testbd_getwear(&cfg, b); 457 assert(wear >= 0); 458 lfs_testbd_swear_t diff = wear - avgwear; 459 dev2 += diff*diff; 460 } 461 dev2 /= totalwear; 462 LFS_WARN("std dev^2: %d", dev2); 463 assert(dev2 < 8); 464''' 465 466