lfs_testbd.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. /*
  2. * Testing block device, wraps filebd and rambd while providing a bunch
  3. * of hooks for testing littlefs in various conditions.
  4. *
  5. * Copyright (c) 2022, The littlefs authors.
  6. * Copyright (c) 2017, Arm Limited. All rights reserved.
  7. * SPDX-License-Identifier: BSD-3-Clause
  8. */
  9. #include "bd/lfs_testbd.h"
  10. #include <stdlib.h>
  11. int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
  12. const struct lfs_testbd_config *bdcfg) {
  13. LFS_TESTBD_TRACE("lfs_testbd_createcfg(%p {.context=%p, "
  14. ".read=%p, .prog=%p, .erase=%p, .sync=%p, "
  15. ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
  16. ".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
  17. "\"%s\", "
  18. "%p {.erase_value=%"PRId32", .erase_cycles=%"PRIu32", "
  19. ".badblock_behavior=%"PRIu8", .power_cycles=%"PRIu32", "
  20. ".buffer=%p, .wear_buffer=%p})",
  21. (void*)cfg, cfg->context,
  22. (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
  23. (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
  24. cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
  25. path, (void*)bdcfg, bdcfg->erase_value, bdcfg->erase_cycles,
  26. bdcfg->badblock_behavior, bdcfg->power_cycles,
  27. bdcfg->buffer, bdcfg->wear_buffer);
  28. lfs_testbd_t *bd = cfg->context;
  29. bd->cfg = bdcfg;
  30. // setup testing things
  31. bd->persist = path;
  32. bd->power_cycles = bd->cfg->power_cycles;
  33. // create scratch block if we need it (for emulating erase values)
  34. if (bd->cfg->erase_value != -1) {
  35. if (bd->cfg->scratch_buffer) {
  36. bd->scratch = bd->cfg->scratch_buffer;
  37. } else {
  38. bd->scratch = lfs_malloc(cfg->block_size);
  39. if (!bd->scratch) {
  40. LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", LFS_ERR_NOMEM);
  41. return LFS_ERR_NOMEM;
  42. }
  43. }
  44. }
  45. // create map of wear
  46. if (bd->cfg->erase_cycles) {
  47. if (bd->cfg->wear_buffer) {
  48. bd->wear = bd->cfg->wear_buffer;
  49. } else {
  50. bd->wear = lfs_malloc(sizeof(lfs_testbd_wear_t)*cfg->block_count);
  51. if (!bd->wear) {
  52. LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", LFS_ERR_NOMEM);
  53. return LFS_ERR_NOMEM;
  54. }
  55. }
  56. memset(bd->wear, 0, sizeof(lfs_testbd_wear_t) * cfg->block_count);
  57. }
  58. // create underlying block device
  59. if (bd->persist) {
  60. int err = lfs_filebd_create(cfg, path);
  61. LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err);
  62. return err;
  63. } else {
  64. bd->u.ram.cfg = (struct lfs_rambd_config){
  65. .buffer = bd->cfg->buffer,
  66. };
  67. int err = lfs_rambd_createcfg(cfg, &bd->u.ram.cfg);
  68. LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err);
  69. return err;
  70. }
  71. }
  72. int lfs_testbd_create(const struct lfs_config *cfg, const char *path) {
  73. LFS_TESTBD_TRACE("lfs_testbd_create(%p {.context=%p, "
  74. ".read=%p, .prog=%p, .erase=%p, .sync=%p, "
  75. ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
  76. ".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
  77. "\"%s\")",
  78. (void*)cfg, cfg->context,
  79. (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
  80. (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
  81. cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
  82. path);
  83. static const struct lfs_testbd_config defaults = {.erase_value=-1};
  84. int err = lfs_testbd_createcfg(cfg, path, &defaults);
  85. LFS_TESTBD_TRACE("lfs_testbd_create -> %d", err);
  86. return err;
  87. }
  88. int lfs_testbd_destroy(const struct lfs_config *cfg) {
  89. LFS_TESTBD_TRACE("lfs_testbd_destroy(%p)", (void*)cfg);
  90. lfs_testbd_t *bd = cfg->context;
  91. if (bd->cfg->erase_value != -1 && !bd->cfg->scratch_buffer) {
  92. lfs_free(bd->scratch);
  93. }
  94. if (bd->cfg->erase_cycles && !bd->cfg->wear_buffer) {
  95. lfs_free(bd->wear);
  96. }
  97. if (bd->persist) {
  98. int err = lfs_filebd_destroy(cfg);
  99. LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err);
  100. return err;
  101. } else {
  102. int err = lfs_rambd_destroy(cfg);
  103. LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err);
  104. return err;
  105. }
  106. }
  107. /// Internal mapping to block devices ///
  108. static int lfs_testbd_rawread(const struct lfs_config *cfg, lfs_block_t block,
  109. lfs_off_t off, void *buffer, lfs_size_t size) {
  110. lfs_testbd_t *bd = cfg->context;
  111. if (bd->persist) {
  112. return lfs_filebd_read(cfg, block, off, buffer, size);
  113. } else {
  114. return lfs_rambd_read(cfg, block, off, buffer, size);
  115. }
  116. }
  117. static int lfs_testbd_rawprog(const struct lfs_config *cfg, lfs_block_t block,
  118. lfs_off_t off, const void *buffer, lfs_size_t size) {
  119. lfs_testbd_t *bd = cfg->context;
  120. if (bd->persist) {
  121. return lfs_filebd_prog(cfg, block, off, buffer, size);
  122. } else {
  123. return lfs_rambd_prog(cfg, block, off, buffer, size);
  124. }
  125. }
  126. static int lfs_testbd_rawerase(const struct lfs_config *cfg,
  127. lfs_block_t block) {
  128. lfs_testbd_t *bd = cfg->context;
  129. if (bd->persist) {
  130. return lfs_filebd_erase(cfg, block);
  131. } else {
  132. return lfs_rambd_erase(cfg, block);
  133. }
  134. }
  135. static int lfs_testbd_rawsync(const struct lfs_config *cfg) {
  136. lfs_testbd_t *bd = cfg->context;
  137. if (bd->persist) {
  138. return lfs_filebd_sync(cfg);
  139. } else {
  140. return lfs_rambd_sync(cfg);
  141. }
  142. }
  143. /// block device API ///
  144. int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block,
  145. lfs_off_t off, void *buffer, lfs_size_t size) {
  146. LFS_TESTBD_TRACE("lfs_testbd_read(%p, "
  147. "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
  148. (void*)cfg, block, off, buffer, size);
  149. lfs_testbd_t *bd = cfg->context;
  150. // check if read is valid
  151. LFS_ASSERT(block < cfg->block_count);
  152. LFS_ASSERT(off % cfg->read_size == 0);
  153. LFS_ASSERT(size % cfg->read_size == 0);
  154. LFS_ASSERT(off+size <= cfg->block_size);
  155. // block bad?
  156. if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles &&
  157. bd->cfg->badblock_behavior == LFS_TESTBD_BADBLOCK_READERROR) {
  158. LFS_TESTBD_TRACE("lfs_testbd_read -> %d", LFS_ERR_CORRUPT);
  159. return LFS_ERR_CORRUPT;
  160. }
  161. // read
  162. int err = lfs_testbd_rawread(cfg, block, off, buffer, size);
  163. LFS_TESTBD_TRACE("lfs_testbd_read -> %d", err);
  164. return err;
  165. }
  166. int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
  167. lfs_off_t off, const void *buffer, lfs_size_t size) {
  168. LFS_TESTBD_TRACE("lfs_testbd_prog(%p, "
  169. "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
  170. (void*)cfg, block, off, buffer, size);
  171. lfs_testbd_t *bd = cfg->context;
  172. // check if write is valid
  173. LFS_ASSERT(block < cfg->block_count);
  174. LFS_ASSERT(off % cfg->prog_size == 0);
  175. LFS_ASSERT(size % cfg->prog_size == 0);
  176. LFS_ASSERT(off+size <= cfg->block_size);
  177. // block bad?
  178. if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles) {
  179. if (bd->cfg->badblock_behavior ==
  180. LFS_TESTBD_BADBLOCK_PROGERROR) {
  181. LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", LFS_ERR_CORRUPT);
  182. return LFS_ERR_CORRUPT;
  183. } else if (bd->cfg->badblock_behavior ==
  184. LFS_TESTBD_BADBLOCK_PROGNOOP ||
  185. bd->cfg->badblock_behavior ==
  186. LFS_TESTBD_BADBLOCK_ERASENOOP) {
  187. LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0);
  188. return 0;
  189. }
  190. }
  191. // emulate an erase value?
  192. if (bd->cfg->erase_value != -1) {
  193. int err = lfs_testbd_rawread(cfg, block, 0,
  194. bd->scratch, cfg->block_size);
  195. if (err) {
  196. LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err);
  197. return err;
  198. }
  199. // assert that program block was erased
  200. for (lfs_off_t i = 0; i < size; i++) {
  201. LFS_ASSERT(bd->scratch[off+i] == bd->cfg->erase_value);
  202. }
  203. memcpy(&bd->scratch[off], buffer, size);
  204. err = lfs_testbd_rawerase(cfg, block);
  205. if (err) {
  206. LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err);
  207. return err;
  208. }
  209. err = lfs_testbd_rawprog(cfg, block, 0,
  210. bd->scratch, cfg->block_size);
  211. if (err) {
  212. LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err);
  213. return err;
  214. }
  215. } else {
  216. // prog
  217. int err = lfs_testbd_rawprog(cfg, block, off, buffer, size);
  218. if (err) {
  219. LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err);
  220. return err;
  221. }
  222. }
  223. // lose power?
  224. if (bd->power_cycles > 0) {
  225. bd->power_cycles -= 1;
  226. if (bd->power_cycles == 0) {
  227. // sync to make sure we persist the last changes
  228. LFS_ASSERT(lfs_testbd_rawsync(cfg) == 0);
  229. // simulate power loss
  230. exit(33);
  231. }
  232. }
  233. LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0);
  234. return 0;
  235. }
  236. int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
  237. LFS_TESTBD_TRACE("lfs_testbd_erase(%p, 0x%"PRIx32")", (void*)cfg, block);
  238. lfs_testbd_t *bd = cfg->context;
  239. // check if erase is valid
  240. LFS_ASSERT(block < cfg->block_count);
  241. // block bad?
  242. if (bd->cfg->erase_cycles) {
  243. if (bd->wear[block] >= bd->cfg->erase_cycles) {
  244. if (bd->cfg->badblock_behavior ==
  245. LFS_TESTBD_BADBLOCK_ERASEERROR) {
  246. LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", LFS_ERR_CORRUPT);
  247. return LFS_ERR_CORRUPT;
  248. } else if (bd->cfg->badblock_behavior ==
  249. LFS_TESTBD_BADBLOCK_ERASENOOP) {
  250. LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", 0);
  251. return 0;
  252. }
  253. } else {
  254. // mark wear
  255. bd->wear[block] += 1;
  256. }
  257. }
  258. // emulate an erase value?
  259. if (bd->cfg->erase_value != -1) {
  260. memset(bd->scratch, bd->cfg->erase_value, cfg->block_size);
  261. int err = lfs_testbd_rawerase(cfg, block);
  262. if (err) {
  263. LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", err);
  264. return err;
  265. }
  266. err = lfs_testbd_rawprog(cfg, block, 0,
  267. bd->scratch, cfg->block_size);
  268. if (err) {
  269. LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", err);
  270. return err;
  271. }
  272. } else {
  273. // erase
  274. int err = lfs_testbd_rawerase(cfg, block);
  275. if (err) {
  276. LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", err);
  277. return err;
  278. }
  279. }
  280. // lose power?
  281. if (bd->power_cycles > 0) {
  282. bd->power_cycles -= 1;
  283. if (bd->power_cycles == 0) {
  284. // sync to make sure we persist the last changes
  285. LFS_ASSERT(lfs_testbd_rawsync(cfg) == 0);
  286. // simulate power loss
  287. exit(33);
  288. }
  289. }
  290. LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0);
  291. return 0;
  292. }
  293. int lfs_testbd_sync(const struct lfs_config *cfg) {
  294. LFS_TESTBD_TRACE("lfs_testbd_sync(%p)", (void*)cfg);
  295. int err = lfs_testbd_rawsync(cfg);
  296. LFS_TESTBD_TRACE("lfs_testbd_sync -> %d", err);
  297. return err;
  298. }
  299. /// simulated wear operations ///
  300. lfs_testbd_swear_t lfs_testbd_getwear(const struct lfs_config *cfg,
  301. lfs_block_t block) {
  302. LFS_TESTBD_TRACE("lfs_testbd_getwear(%p, %"PRIu32")", (void*)cfg, block);
  303. lfs_testbd_t *bd = cfg->context;
  304. // check if block is valid
  305. LFS_ASSERT(bd->cfg->erase_cycles);
  306. LFS_ASSERT(block < cfg->block_count);
  307. LFS_TESTBD_TRACE("lfs_testbd_getwear -> %"PRIu32, bd->wear[block]);
  308. return bd->wear[block];
  309. }
  310. int lfs_testbd_setwear(const struct lfs_config *cfg,
  311. lfs_block_t block, lfs_testbd_wear_t wear) {
  312. LFS_TESTBD_TRACE("lfs_testbd_setwear(%p, %"PRIu32")", (void*)cfg, block);
  313. lfs_testbd_t *bd = cfg->context;
  314. // check if block is valid
  315. LFS_ASSERT(bd->cfg->erase_cycles);
  316. LFS_ASSERT(block < cfg->block_count);
  317. bd->wear[block] = wear;
  318. LFS_TESTBD_TRACE("lfs_testbd_setwear -> %d", 0);
  319. return 0;
  320. }