test_exhaustion.toml 15 KB

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