test_exhaustion.toml 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. [[case]] # test running a filesystem to exhaustion
  2. define.LFS_ERASE_CYCLES = 10
  3. define.LFS_BLOCK_COUNT = 256 # small bd so it runs faster
  4. define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
  5. define.LFS_BADBLOCK_BEHAVIOR = [
  6. 'LFS_TESTBD_BADBLOCK_NOPROG',
  7. 'LFS_TESTBD_BADBLOCK_NOERASE',
  8. 'LFS_TESTBD_BADBLOCK_NOREAD',
  9. ]
  10. define.FILES = 10
  11. code = '''
  12. lfs_format(&lfs, &cfg) => 0;
  13. lfs_mount(&lfs, &cfg) => 0;
  14. lfs_mkdir(&lfs, "roadrunner") => 0;
  15. lfs_unmount(&lfs) => 0;
  16. uint32_t cycle = 0;
  17. while (true) {
  18. lfs_mount(&lfs, &cfg) => 0;
  19. for (uint32_t i = 0; i < FILES; i++) {
  20. // chose name, roughly random seed, and random 2^n size
  21. sprintf(path, "roadrunner/test%d", i);
  22. srand(cycle * i);
  23. size = 1 << ((rand() % 10)+2);
  24. lfs_file_open(&lfs, &file, path,
  25. LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
  26. for (lfs_size_t j = 0; j < size; j++) {
  27. char c = 'a' + (rand() % 26);
  28. lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
  29. assert(res == 1 || res == LFS_ERR_NOSPC);
  30. if (res == LFS_ERR_NOSPC) {
  31. goto exhausted;
  32. }
  33. }
  34. err = lfs_file_close(&lfs, &file);
  35. assert(err == 0 || err == LFS_ERR_NOSPC);
  36. if (err == LFS_ERR_NOSPC) {
  37. goto exhausted;
  38. }
  39. }
  40. for (uint32_t i = 0; i < FILES; i++) {
  41. // check for errors
  42. sprintf(path, "roadrunner/test%d", i);
  43. srand(cycle * i);
  44. size = 1 << ((rand() % 10)+2);
  45. lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
  46. for (lfs_size_t j = 0; j < size; j++) {
  47. char c = 'a' + (rand() % 26);
  48. char r;
  49. lfs_file_read(&lfs, &file, &r, 1) => 1;
  50. assert(r == c);
  51. }
  52. lfs_file_close(&lfs, &file) => 0;
  53. }
  54. lfs_unmount(&lfs) => 0;
  55. cycle += 1;
  56. }
  57. exhausted:
  58. // should still be readable
  59. lfs_mount(&lfs, &cfg) => 0;
  60. for (uint32_t i = 0; i < FILES; i++) {
  61. // check for errors
  62. sprintf(path, "roadrunner/test%d", i);
  63. lfs_stat(&lfs, path, &info) => 0;
  64. }
  65. lfs_unmount(&lfs) => 0;
  66. LFS_WARN("completed %d cycles", cycle);
  67. '''
  68. [[case]] # test running a filesystem to exhaustion
  69. # which also requires expanding superblocks
  70. define.LFS_ERASE_CYCLES = 10
  71. define.LFS_BLOCK_COUNT = 256 # small bd so it runs faster
  72. define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
  73. define.LFS_BADBLOCK_BEHAVIOR = [
  74. 'LFS_TESTBD_BADBLOCK_NOPROG',
  75. 'LFS_TESTBD_BADBLOCK_NOERASE',
  76. 'LFS_TESTBD_BADBLOCK_NOREAD',
  77. ]
  78. define.FILES = 10
  79. code = '''
  80. lfs_format(&lfs, &cfg) => 0;
  81. uint32_t cycle = 0;
  82. while (true) {
  83. lfs_mount(&lfs, &cfg) => 0;
  84. for (uint32_t i = 0; i < FILES; i++) {
  85. // chose name, roughly random seed, and random 2^n size
  86. sprintf(path, "test%d", i);
  87. srand(cycle * i);
  88. size = 1 << ((rand() % 10)+2);
  89. lfs_file_open(&lfs, &file, path,
  90. LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
  91. for (lfs_size_t j = 0; j < size; j++) {
  92. char c = 'a' + (rand() % 26);
  93. lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
  94. assert(res == 1 || res == LFS_ERR_NOSPC);
  95. if (res == LFS_ERR_NOSPC) {
  96. goto exhausted;
  97. }
  98. }
  99. err = lfs_file_close(&lfs, &file);
  100. assert(err == 0 || err == LFS_ERR_NOSPC);
  101. if (err == LFS_ERR_NOSPC) {
  102. goto exhausted;
  103. }
  104. }
  105. for (uint32_t i = 0; i < FILES; i++) {
  106. // check for errors
  107. sprintf(path, "test%d", i);
  108. srand(cycle * i);
  109. size = 1 << ((rand() % 10)+2);
  110. lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
  111. for (lfs_size_t j = 0; j < size; j++) {
  112. char c = 'a' + (rand() % 26);
  113. char r;
  114. lfs_file_read(&lfs, &file, &r, 1) => 1;
  115. assert(r == c);
  116. }
  117. lfs_file_close(&lfs, &file) => 0;
  118. }
  119. lfs_unmount(&lfs) => 0;
  120. cycle += 1;
  121. }
  122. exhausted:
  123. // should still be readable
  124. lfs_mount(&lfs, &cfg) => 0;
  125. for (uint32_t i = 0; i < FILES; i++) {
  126. // check for errors
  127. sprintf(path, "test%d", i);
  128. lfs_stat(&lfs, path, &info) => 0;
  129. }
  130. lfs_unmount(&lfs) => 0;
  131. LFS_WARN("completed %d cycles", cycle);
  132. '''
  133. # These are a sort of high-level litmus test for wear-leveling. One definition
  134. # of wear-leveling is that increasing a block device's space translates directly
  135. # into increasing the block devices lifetime. This is something we can actually
  136. # check for.
  137. [[case]] # wear-level test running a filesystem to exhaustion
  138. define.LFS_ERASE_CYCLES = 10
  139. define.LFS_BLOCK_COUNT = 256 # small bd so it runs faster
  140. define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
  141. define.LFS_BADBLOCK_BEHAVIOR = [
  142. 'LFS_TESTBD_BADBLOCK_NOPROG',
  143. 'LFS_TESTBD_BADBLOCK_NOERASE',
  144. 'LFS_TESTBD_BADBLOCK_NOREAD',
  145. ]
  146. define.FILES = 10
  147. code = '''
  148. lfs_format(&lfs, &cfg) => 0;
  149. lfs_mount(&lfs, &cfg) => 0;
  150. lfs_mkdir(&lfs, "roadrunner") => 0;
  151. lfs_unmount(&lfs) => 0;
  152. uint32_t run_cycles[2];
  153. const uint32_t run_block_count[2] = {LFS_BLOCK_COUNT/2, LFS_BLOCK_COUNT};
  154. for (int run = 0; run < 2; run++) {
  155. for (lfs_block_t b = 0; b < LFS_BLOCK_COUNT; b++) {
  156. lfs_testbd_setwear(&cfg, b,
  157. (b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0;
  158. }
  159. uint32_t cycle = 0;
  160. while (true) {
  161. lfs_mount(&lfs, &cfg) => 0;
  162. for (uint32_t i = 0; i < FILES; i++) {
  163. // chose name, roughly random seed, and random 2^n size
  164. sprintf(path, "roadrunner/test%d", i);
  165. srand(cycle * i);
  166. size = 1 << ((rand() % 10)+2);
  167. lfs_file_open(&lfs, &file, path,
  168. LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
  169. for (lfs_size_t j = 0; j < size; j++) {
  170. char c = 'a' + (rand() % 26);
  171. lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
  172. assert(res == 1 || res == LFS_ERR_NOSPC);
  173. if (res == LFS_ERR_NOSPC) {
  174. goto exhausted;
  175. }
  176. }
  177. err = lfs_file_close(&lfs, &file);
  178. assert(err == 0 || err == LFS_ERR_NOSPC);
  179. if (err == LFS_ERR_NOSPC) {
  180. goto exhausted;
  181. }
  182. }
  183. for (uint32_t i = 0; i < FILES; i++) {
  184. // check for errors
  185. sprintf(path, "roadrunner/test%d", i);
  186. srand(cycle * i);
  187. size = 1 << ((rand() % 10)+2);
  188. lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
  189. for (lfs_size_t j = 0; j < size; j++) {
  190. char c = 'a' + (rand() % 26);
  191. char r;
  192. lfs_file_read(&lfs, &file, &r, 1) => 1;
  193. assert(r == c);
  194. }
  195. lfs_file_close(&lfs, &file) => 0;
  196. }
  197. lfs_unmount(&lfs) => 0;
  198. cycle += 1;
  199. }
  200. exhausted:
  201. // should still be readable
  202. lfs_mount(&lfs, &cfg) => 0;
  203. for (uint32_t i = 0; i < FILES; i++) {
  204. // check for errors
  205. sprintf(path, "roadrunner/test%d", i);
  206. lfs_stat(&lfs, path, &info) => 0;
  207. }
  208. lfs_unmount(&lfs) => 0;
  209. run_cycles[run] = cycle;
  210. LFS_WARN("completed %d blocks %d cycles",
  211. run_block_count[run], run_cycles[run]);
  212. }
  213. // check we increased the lifetime by 2x with ~10% error
  214. LFS_ASSERT(run_cycles[1] > 2*run_cycles[0]-run_cycles[0]/10);
  215. '''
  216. [[case]] # wear-level test + expanding superblock
  217. define.LFS_ERASE_CYCLES = 10
  218. define.LFS_BLOCK_COUNT = 256 # small bd so it runs faster
  219. define.LFS_BLOCK_CYCLES = 'LFS_ERASE_CYCLES / 2'
  220. define.LFS_BADBLOCK_BEHAVIOR = [
  221. 'LFS_TESTBD_BADBLOCK_NOPROG',
  222. 'LFS_TESTBD_BADBLOCK_NOERASE',
  223. 'LFS_TESTBD_BADBLOCK_NOREAD',
  224. ]
  225. define.FILES = 10
  226. code = '''
  227. lfs_format(&lfs, &cfg) => 0;
  228. uint32_t run_cycles[2];
  229. const uint32_t run_block_count[2] = {LFS_BLOCK_COUNT/2, LFS_BLOCK_COUNT};
  230. for (int run = 0; run < 2; run++) {
  231. for (lfs_block_t b = 0; b < LFS_BLOCK_COUNT; b++) {
  232. lfs_testbd_setwear(&cfg, b,
  233. (b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0;
  234. }
  235. uint32_t cycle = 0;
  236. while (true) {
  237. lfs_mount(&lfs, &cfg) => 0;
  238. for (uint32_t i = 0; i < FILES; i++) {
  239. // chose name, roughly random seed, and random 2^n size
  240. sprintf(path, "test%d", i);
  241. srand(cycle * i);
  242. size = 1 << ((rand() % 10)+2);
  243. lfs_file_open(&lfs, &file, path,
  244. LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
  245. for (lfs_size_t j = 0; j < size; j++) {
  246. char c = 'a' + (rand() % 26);
  247. lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1);
  248. assert(res == 1 || res == LFS_ERR_NOSPC);
  249. if (res == LFS_ERR_NOSPC) {
  250. goto exhausted;
  251. }
  252. }
  253. err = lfs_file_close(&lfs, &file);
  254. assert(err == 0 || err == LFS_ERR_NOSPC);
  255. if (err == LFS_ERR_NOSPC) {
  256. goto exhausted;
  257. }
  258. }
  259. for (uint32_t i = 0; i < FILES; i++) {
  260. // check for errors
  261. sprintf(path, "test%d", i);
  262. srand(cycle * i);
  263. size = 1 << ((rand() % 10)+2);
  264. lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
  265. for (lfs_size_t j = 0; j < size; j++) {
  266. char c = 'a' + (rand() % 26);
  267. char r;
  268. lfs_file_read(&lfs, &file, &r, 1) => 1;
  269. assert(r == c);
  270. }
  271. lfs_file_close(&lfs, &file) => 0;
  272. }
  273. lfs_unmount(&lfs) => 0;
  274. cycle += 1;
  275. }
  276. exhausted:
  277. // should still be readable
  278. lfs_mount(&lfs, &cfg) => 0;
  279. for (uint32_t i = 0; i < FILES; i++) {
  280. // check for errors
  281. sprintf(path, "test%d", i);
  282. lfs_stat(&lfs, path, &info) => 0;
  283. }
  284. lfs_unmount(&lfs) => 0;
  285. run_cycles[run] = cycle;
  286. LFS_WARN("completed %d blocks %d cycles",
  287. run_block_count[run], run_cycles[run]);
  288. }
  289. // check we increased the lifetime by 2x with ~10% error
  290. LFS_ASSERT(run_cycles[1] > 2*run_cycles[0]-run_cycles[0]/10);
  291. '''