@@ -129,6 +129,8 @@ int lfs_emubd_create(const struct lfs_config *cfg,
129129 bd -> proged = 0 ;
130130 bd -> erased = 0 ;
131131 bd -> power_cycles = bd -> cfg -> power_cycles ;
132+ bd -> ooo_block = -1 ;
133+ bd -> ooo_data = NULL ;
132134 bd -> disk = NULL ;
133135
134136 if (bd -> cfg -> disk_path ) {
@@ -195,6 +197,7 @@ int lfs_emubd_destroy(const struct lfs_config *cfg) {
195197 free (bd -> blocks );
196198
197199 // clean up other resources
200+ lfs_emubd_decblock (bd -> ooo_data );
198201 if (bd -> disk ) {
199202 bd -> disk -> rc -= 1 ;
200203 if (bd -> disk -> rc == 0 ) {
@@ -209,6 +212,75 @@ int lfs_emubd_destroy(const struct lfs_config *cfg) {
209212}
210213
211214
215+ // powerloss hook
216+ static int lfs_emubd_powerloss (const struct lfs_config * cfg ) {
217+ lfs_emubd_t * bd = cfg -> context ;
218+
219+ // emulate out-of-order writes?
220+ lfs_emubd_block_t * ooo_data = NULL ;
221+ if (bd -> cfg -> powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO
222+ && bd -> ooo_block != -1 ) {
223+ // since writes between syncs are allowed to be out-of-order, it
224+ // shouldn't hurt to restore the first write on powerloss, right?
225+ ooo_data = bd -> blocks [bd -> ooo_block ];
226+ bd -> blocks [bd -> ooo_block ] = lfs_emubd_incblock (bd -> ooo_data );
227+
228+ // mirror to disk file?
229+ if (bd -> disk
230+ && (bd -> blocks [bd -> ooo_block ]
231+ || bd -> cfg -> erase_value != -1 )) {
232+ off_t res1 = lseek (bd -> disk -> fd ,
233+ (off_t )bd -> ooo_block * bd -> cfg -> erase_size ,
234+ SEEK_SET );
235+ if (res1 < 0 ) {
236+ return - errno ;
237+ }
238+
239+ ssize_t res2 = write (bd -> disk -> fd ,
240+ (bd -> blocks [bd -> ooo_block ])
241+ ? bd -> blocks [bd -> ooo_block ]-> data
242+ : bd -> disk -> scratch ,
243+ bd -> cfg -> erase_size );
244+ if (res2 < 0 ) {
245+ return - errno ;
246+ }
247+ }
248+ }
249+
250+ // simulate power loss
251+ bd -> cfg -> powerloss_cb (bd -> cfg -> powerloss_data );
252+
253+ // if we continue, undo out-of-order write emulation
254+ if (bd -> cfg -> powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO
255+ && bd -> ooo_block != -1 ) {
256+ lfs_emubd_decblock (bd -> blocks [bd -> ooo_block ]);
257+ bd -> blocks [bd -> ooo_block ] = ooo_data ;
258+
259+ // mirror to disk file?
260+ if (bd -> disk
261+ && (bd -> blocks [bd -> ooo_block ]
262+ || bd -> cfg -> erase_value != -1 )) {
263+ off_t res1 = lseek (bd -> disk -> fd ,
264+ (off_t )bd -> ooo_block * bd -> cfg -> erase_size ,
265+ SEEK_SET );
266+ if (res1 < 0 ) {
267+ return - errno ;
268+ }
269+
270+ ssize_t res2 = write (bd -> disk -> fd ,
271+ (bd -> blocks [bd -> ooo_block ])
272+ ? bd -> blocks [bd -> ooo_block ]-> data
273+ : bd -> disk -> scratch ,
274+ bd -> cfg -> erase_size );
275+ if (res2 < 0 ) {
276+ return - errno ;
277+ }
278+ }
279+ }
280+
281+ return 0 ;
282+ }
283+
212284
213285// block device API
214286
@@ -344,8 +416,11 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
344416 if (bd -> power_cycles > 0 ) {
345417 bd -> power_cycles -= 1 ;
346418 if (bd -> power_cycles == 0 ) {
347- // simulate power loss
348- bd -> cfg -> powerloss_cb (bd -> cfg -> powerloss_data );
419+ int err = lfs_emubd_powerloss (cfg );
420+ if (err ) {
421+ LFS_EMUBD_TRACE ("lfs_emubd_prog -> %d" , err );
422+ return err ;
423+ }
349424 }
350425 }
351426
@@ -361,10 +436,17 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
361436 // check if erase is valid
362437 LFS_ASSERT (block < bd -> cfg -> erase_count );
363438
439+ // emulate out-of-order writes? save first write
440+ if (bd -> cfg -> powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO
441+ && bd -> ooo_block == -1 ) {
442+ bd -> ooo_block = block ;
443+ bd -> ooo_data = lfs_emubd_incblock (bd -> blocks [block ]);
444+ }
445+
364446 // get the block
365447 lfs_emubd_block_t * b = lfs_emubd_mutblock (cfg , & bd -> blocks [block ]);
366448 if (!b ) {
367- LFS_EMUBD_TRACE ("lfs_emubd_prog -> %d" , LFS_ERR_NOMEM );
449+ LFS_EMUBD_TRACE ("lfs_emubd_erase -> %d" , LFS_ERR_NOMEM );
368450 return LFS_ERR_NOMEM ;
369451 }
370452
@@ -430,8 +512,11 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
430512 if (bd -> power_cycles > 0 ) {
431513 bd -> power_cycles -= 1 ;
432514 if (bd -> power_cycles == 0 ) {
433- // simulate power loss
434- bd -> cfg -> powerloss_cb (bd -> cfg -> powerloss_data );
515+ int err = lfs_emubd_powerloss (cfg );
516+ if (err ) {
517+ LFS_EMUBD_TRACE ("lfs_emubd_erase -> %d" , err );
518+ return err ;
519+ }
435520 }
436521 }
437522
@@ -441,14 +526,21 @@ int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
441526
442527int lfs_emubd_sync (const struct lfs_config * cfg ) {
443528 LFS_EMUBD_TRACE ("lfs_emubd_sync(%p)" , (void * )cfg );
529+ lfs_emubd_t * bd = cfg -> context ;
444530
445- // do nothing
446- (void )cfg ;
531+ // emulate out-of-order writes? reset first write, writes
532+ // cannot be out-of-order across sync
533+ if (bd -> cfg -> powerloss_behavior == LFS_EMUBD_POWERLOSS_OOO ) {
534+ lfs_emubd_decblock (bd -> ooo_data );
535+ bd -> ooo_block = -1 ;
536+ bd -> ooo_data = NULL ;
537+ }
447538
448539 LFS_EMUBD_TRACE ("lfs_emubd_sync -> %d" , 0 );
449540 return 0 ;
450541}
451542
543+
452544/// Additional extended API for driving test features ///
453545
454546static int lfs_emubd_crc_ (const struct lfs_config * cfg ,
@@ -633,6 +725,8 @@ int lfs_emubd_copy(const struct lfs_config *cfg, lfs_emubd_t *copy) {
633725 copy -> proged = bd -> proged ;
634726 copy -> erased = bd -> erased ;
635727 copy -> power_cycles = bd -> power_cycles ;
728+ copy -> ooo_block = bd -> ooo_block ;
729+ copy -> ooo_data = lfs_emubd_incblock (bd -> ooo_data );
636730 copy -> disk = bd -> disk ;
637731 if (copy -> disk ) {
638732 copy -> disk -> rc += 1 ;
0 commit comments