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