1 /* 2 * Emulating 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 #ifndef LFS_EMUBD_H 10 #define LFS_EMUBD_H 11 12 #include "lfs.h" 13 #include "lfs_util.h" 14 #include "bd/lfs_rambd.h" 15 #include "bd/lfs_filebd.h" 16 17 #ifdef __cplusplus 18 extern "C" 19 { 20 #endif 21 22 23 // Block device specific tracing 24 #ifndef LFS_EMUBD_TRACE 25 #ifdef LFS_EMUBD_YES_TRACE 26 #define LFS_EMUBD_TRACE(...) LFS_TRACE(__VA_ARGS__) 27 #else 28 #define LFS_EMUBD_TRACE(...) 29 #endif 30 #endif 31 32 // Mode determining how "bad-blocks" behave during testing. This simulates 33 // some real-world circumstances such as progs not sticking (prog-noop), 34 // a readonly disk (erase-noop), and ECC failures (read-error). 35 // 36 // Not that read-noop is not allowed. Read _must_ return a consistent (but 37 // may be arbitrary) value on every read. 38 typedef enum lfs_emubd_badblock_behavior { 39 LFS_EMUBD_BADBLOCK_PROGERROR, 40 LFS_EMUBD_BADBLOCK_ERASEERROR, 41 LFS_EMUBD_BADBLOCK_READERROR, 42 LFS_EMUBD_BADBLOCK_PROGNOOP, 43 LFS_EMUBD_BADBLOCK_ERASENOOP, 44 } lfs_emubd_badblock_behavior_t; 45 46 // Mode determining how power-loss behaves during testing. For now this 47 // only supports a noop behavior, leaving the data on-disk untouched. 48 typedef enum lfs_emubd_powerloss_behavior { 49 LFS_EMUBD_POWERLOSS_NOOP, 50 } lfs_emubd_powerloss_behavior_t; 51 52 // Type for measuring read/program/erase operations 53 typedef uint64_t lfs_emubd_io_t; 54 typedef int64_t lfs_emubd_sio_t; 55 56 // Type for measuring wear 57 typedef uint32_t lfs_emubd_wear_t; 58 typedef int32_t lfs_emubd_swear_t; 59 60 // Type for tracking power-cycles 61 typedef uint32_t lfs_emubd_powercycles_t; 62 typedef int32_t lfs_emubd_spowercycles_t; 63 64 // Type for delays in nanoseconds 65 typedef uint64_t lfs_emubd_sleep_t; 66 typedef int64_t lfs_emubd_ssleep_t; 67 68 // emubd config, this is required for testing 69 struct lfs_emubd_config { 70 // Minimum size of a read operation in bytes. 71 lfs_size_t read_size; 72 73 // Minimum size of a program operation in bytes. 74 lfs_size_t prog_size; 75 76 // Size of an erase operation in bytes. 77 lfs_size_t erase_size; 78 79 // Number of erase blocks on the device. 80 lfs_size_t erase_count; 81 82 // 8-bit erase value to use for simulating erases. -1 does not simulate 83 // erases, which can speed up testing by avoiding the extra block-device 84 // operations to store the erase value. 85 int32_t erase_value; 86 87 // Number of erase cycles before a block becomes "bad". The exact behavior 88 // of bad blocks is controlled by badblock_behavior. 89 uint32_t erase_cycles; 90 91 // The mode determining how bad-blocks fail 92 lfs_emubd_badblock_behavior_t badblock_behavior; 93 94 // Number of write operations (erase/prog) before triggering a power-loss. 95 // power_cycles=0 disables this. The exact behavior of power-loss is 96 // controlled by a combination of powerloss_behavior and powerloss_cb. 97 lfs_emubd_powercycles_t power_cycles; 98 99 // The mode determining how power-loss affects disk 100 lfs_emubd_powerloss_behavior_t powerloss_behavior; 101 102 // Function to call to emulate power-loss. The exact behavior of power-loss 103 // is up to the runner to provide. 104 void (*powerloss_cb)(void*); 105 106 // Data for power-loss callback 107 void *powerloss_data; 108 109 // True to track when power-loss could have occured. Note this involves 110 // heavy memory usage! 111 bool track_branches; 112 113 // Path to file to use as a mirror of the disk. This provides a way to view 114 // the current state of the block device. 115 const char *disk_path; 116 117 // Artificial delay in nanoseconds, there is no purpose for this other 118 // than slowing down the simulation. 119 lfs_emubd_sleep_t read_sleep; 120 121 // Artificial delay in nanoseconds, there is no purpose for this other 122 // than slowing down the simulation. 123 lfs_emubd_sleep_t prog_sleep; 124 125 // Artificial delay in nanoseconds, there is no purpose for this other 126 // than slowing down the simulation. 127 lfs_emubd_sleep_t erase_sleep; 128 }; 129 130 // A reference counted block 131 typedef struct lfs_emubd_block { 132 uint32_t rc; 133 lfs_emubd_wear_t wear; 134 135 uint8_t data[]; 136 } lfs_emubd_block_t; 137 138 // Disk mirror 139 typedef struct lfs_emubd_disk { 140 uint32_t rc; 141 int fd; 142 uint8_t *scratch; 143 } lfs_emubd_disk_t; 144 145 // emubd state 146 typedef struct lfs_emubd { 147 // array of copy-on-write blocks 148 lfs_emubd_block_t **blocks; 149 150 // some other test state 151 lfs_emubd_io_t readed; 152 lfs_emubd_io_t proged; 153 lfs_emubd_io_t erased; 154 lfs_emubd_powercycles_t power_cycles; 155 lfs_emubd_disk_t *disk; 156 157 const struct lfs_emubd_config *cfg; 158 } lfs_emubd_t; 159 160 161 /// Block device API /// 162 163 // Create an emulating block device using the geometry in lfs_config 164 int lfs_emubd_create(const struct lfs_config *cfg, 165 const struct lfs_emubd_config *bdcfg); 166 167 // Clean up memory associated with block device 168 int lfs_emubd_destroy(const struct lfs_config *cfg); 169 170 // Read a block 171 int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block, 172 lfs_off_t off, void *buffer, lfs_size_t size); 173 174 // Program a block 175 // 176 // The block must have previously been erased. 177 int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, 178 lfs_off_t off, const void *buffer, lfs_size_t size); 179 180 // Erase a block 181 // 182 // A block must be erased before being programmed. The 183 // state of an erased block is undefined. 184 int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block); 185 186 // Sync the block device 187 int lfs_emubd_sync(const struct lfs_config *cfg); 188 189 190 /// Additional extended API for driving test features /// 191 192 // A CRC of a block for debugging purposes 193 int lfs_emubd_crc(const struct lfs_config *cfg, 194 lfs_block_t block, uint32_t *crc); 195 196 // A CRC of the entire block device for debugging purposes 197 int lfs_emubd_bdcrc(const struct lfs_config *cfg, uint32_t *crc); 198 199 // Get total amount of bytes read 200 lfs_emubd_sio_t lfs_emubd_readed(const struct lfs_config *cfg); 201 202 // Get total amount of bytes programmed 203 lfs_emubd_sio_t lfs_emubd_proged(const struct lfs_config *cfg); 204 205 // Get total amount of bytes erased 206 lfs_emubd_sio_t lfs_emubd_erased(const struct lfs_config *cfg); 207 208 // Manually set amount of bytes read 209 int lfs_emubd_setreaded(const struct lfs_config *cfg, lfs_emubd_io_t readed); 210 211 // Manually set amount of bytes programmed 212 int lfs_emubd_setproged(const struct lfs_config *cfg, lfs_emubd_io_t proged); 213 214 // Manually set amount of bytes erased 215 int lfs_emubd_seterased(const struct lfs_config *cfg, lfs_emubd_io_t erased); 216 217 // Get simulated wear on a given block 218 lfs_emubd_swear_t lfs_emubd_wear(const struct lfs_config *cfg, 219 lfs_block_t block); 220 221 // Manually set simulated wear on a given block 222 int lfs_emubd_setwear(const struct lfs_config *cfg, 223 lfs_block_t block, lfs_emubd_wear_t wear); 224 225 // Get the remaining power-cycles 226 lfs_emubd_spowercycles_t lfs_emubd_powercycles( 227 const struct lfs_config *cfg); 228 229 // Manually set the remaining power-cycles 230 int lfs_emubd_setpowercycles(const struct lfs_config *cfg, 231 lfs_emubd_powercycles_t power_cycles); 232 233 // Create a copy-on-write copy of the state of this block device 234 int lfs_emubd_copy(const struct lfs_config *cfg, lfs_emubd_t *copy); 235 236 237 #ifdef __cplusplus 238 } /* extern "C" */ 239 #endif 240 241 #endif 242