test_exhaustion.toml 17 KB

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