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