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