@@ -596,42 +596,48 @@ static int lfs_rawunmount(lfs_t *lfs);
596596#ifndef  LFS_READONLY 
597597static  int  lfs_alloc_lookahead (void  * p , lfs_block_t  block ) {
598598    lfs_t  * lfs  =  (lfs_t * )p ;
599-     lfs_block_t  off  =  ((block  -  lfs -> free . off )
599+     lfs_block_t  off  =  ((block  -  lfs -> lookahead . start )
600600            +  lfs -> block_count ) % lfs -> block_count ;
601601
602-     if  (off  <  lfs -> free .size ) {
603-         lfs -> free .buffer [off  / 32 ] |= 1U  << (off  % 32 );
602+     if  (off  <  lfs -> lookahead .size ) {
603+         lfs -> lookahead .buffer [off  / 8 ] |= 1U  << (off  % 8 );
604604    }
605605
606606    return  0 ;
607607}
608608#endif 
609609
610- // indicate allocated blocks have been committed into the filesystem, this 
611- // is to prevent blocks from being garbage collected in the middle of a 
612- // commit operation 
613- static  void  lfs_alloc_ack (lfs_t  * lfs ) {
614-     lfs -> free .ack  =  lfs -> block_count ;
610+ // allocations should call this when all allocated blocks are committed to 
611+ // the filesystem 
612+ // 
613+ // after a checkpoint, the block allocator may realloc any untracked blocks 
614+ static  void  lfs_alloc_ckpoint (lfs_t  * lfs ) {
615+     lfs -> lookahead .ckpoint  =  lfs -> block_count ;
615616}
616617
617618// drop the lookahead buffer, this is done during mounting and failed 
618619// traversals in order to avoid invalid lookahead state 
619620static  void  lfs_alloc_drop (lfs_t  * lfs ) {
620-     lfs -> free .size  =  0 ;
621-     lfs -> free . i  =  0 ;
622-     lfs_alloc_ack (lfs );
621+     lfs -> lookahead .size  =  0 ;
622+     lfs -> lookahead . next  =  0 ;
623+     lfs_alloc_ckpoint (lfs );
623624}
624625
625626#ifndef  LFS_READONLY 
626627static  int  lfs_fs_rawgc (lfs_t  * lfs ) {
627-     // Move free offset at the first unused block (lfs->free.i) 
628-     // lfs->free.i is equal lfs->free.size when all blocks are used 
629-     lfs -> free .off  =  (lfs -> free .off  +  lfs -> free .i ) % lfs -> block_count ;
630-     lfs -> free .size  =  lfs_min (8 * lfs -> cfg -> lookahead_size , lfs -> free .ack );
631-     lfs -> free .i  =  0 ;
628+     // move lookahead buffer to the first unused block 
629+     // 
630+     // note we limit the lookahead buffer to at most the amount of blocks 
631+     // checkpointed, this prevents the math in lfs_alloc from underflowing 
632+     lfs -> lookahead .start  =  (lfs -> lookahead .start  +  lfs -> lookahead .next ) 
633+             % lfs -> block_count ;
634+     lfs -> lookahead .next  =  0 ;
635+     lfs -> lookahead .size  =  lfs_min (
636+             8 * lfs -> cfg -> lookahead_size ,
637+             lfs -> lookahead .ckpoint );
632638
633639    // find mask of free blocks from tree 
634-     memset (lfs -> free .buffer , 0 , lfs -> cfg -> lookahead_size );
640+     memset (lfs -> lookahead .buffer , 0 , lfs -> cfg -> lookahead_size );
635641    int  err  =  lfs_fs_rawtraverse (lfs , lfs_alloc_lookahead , lfs , true);
636642    if  (err ) {
637643        lfs_alloc_drop (lfs );
@@ -645,35 +651,48 @@ static int lfs_fs_rawgc(lfs_t *lfs) {
645651#ifndef  LFS_READONLY 
646652static  int  lfs_alloc (lfs_t  * lfs , lfs_block_t  * block ) {
647653    while  (true) {
648-         while  (lfs -> free .i  !=  lfs -> free .size ) {
649-             lfs_block_t  off  =  lfs -> free .i ;
650-             lfs -> free .i  +=  1 ;
651-             lfs -> free .ack  -=  1 ;
652- 
653-             if  (!(lfs -> free .buffer [off  / 32 ] &  (1U  << (off  % 32 )))) {
654+         // scan our lookahead buffer for free blocks 
655+         while  (lfs -> lookahead .next  <  lfs -> lookahead .size ) {
656+             if  (!(lfs -> lookahead .buffer [lfs -> lookahead .next  / 8 ]
657+                     &  (1U  << (lfs -> lookahead .next  % 8 )))) {
654658                // found a free block 
655-                 * block  =  (lfs -> free .off  +  off ) % lfs -> block_count ;
656- 
657-                 // eagerly find next off so an alloc ack can 
658-                 // discredit old lookahead blocks 
659-                 while  (lfs -> free .i  !=  lfs -> free .size  && 
660-                         (lfs -> free .buffer [lfs -> free .i  / 32 ]
661-                             &  (1U  << (lfs -> free .i  % 32 )))) {
662-                     lfs -> free .i  +=  1 ;
663-                     lfs -> free .ack  -=  1 ;
659+                 * block  =  (lfs -> lookahead .start  +  lfs -> lookahead .next )
660+                         % lfs -> block_count ;
661+ 
662+                 // eagerly find next free block to maximize how many blocks 
663+                 // lfs_alloc_ckpoint makes available for scanning 
664+                 while  (true) {
665+                     lfs -> lookahead .next  +=  1 ;
666+                     lfs -> lookahead .ckpoint  -=  1 ;
667+ 
668+                     if  (lfs -> lookahead .next  >= lfs -> lookahead .size 
669+                             ||  !(lfs -> lookahead .buffer [lfs -> lookahead .next  / 8 ]
670+                                 &  (1U  << (lfs -> lookahead .next  % 8 )))) {
671+                         return  0 ;
672+                     }
664673                }
665- 
666-                 return  0 ;
667674            }
675+ 
676+             lfs -> lookahead .next  +=  1 ;
677+             lfs -> lookahead .ckpoint  -=  1 ;
668678        }
669679
670-         // check if we have looked at all blocks since last ack 
671-         if  (lfs -> free .ack  ==  0 ) {
672-             LFS_ERROR ("No more free space %" PRIu32 ,
673-                     lfs -> free .i  +  lfs -> free .off );
680+         // In order to keep our block allocator from spinning forever when our 
681+         // filesystem is full, we mark points where there are no in-flight 
682+         // allocations with a checkpoint before starting a set of allocations. 
683+         // 
684+         // If we've looked at all blocks since the last checkpoint, we report 
685+         // the filesystem as out of storage. 
686+         // 
687+         if  (lfs -> lookahead .ckpoint  <= 0 ) {
688+             LFS_ERROR ("No more free space 0x%" PRIx32 ,
689+                     (lfs -> lookahead .start  +  lfs -> lookahead .next )
690+                         % lfs -> cfg -> block_count );
674691            return  LFS_ERR_NOSPC ;
675692        }
676693
694+         // No blocks in our lookahead buffer, we need to scan the filesystem for 
695+         // unused blocks in the next lookahead window. 
677696        int  err  =  lfs_fs_rawgc (lfs );
678697        if (err ) {
679698            return  err ;
@@ -2588,7 +2607,7 @@ static int lfs_rawmkdir(lfs_t *lfs, const char *path) {
25882607    }
25892608
25902609    // build up new directory 
2591-     lfs_alloc_ack (lfs );
2610+     lfs_alloc_ckpoint (lfs );
25922611    lfs_mdir_t  dir ;
25932612    err  =  lfs_dir_alloc (lfs , & dir );
25942613    if  (err ) {
@@ -3274,7 +3293,7 @@ static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) {
32743293#ifndef  LFS_READONLY 
32753294static  int  lfs_file_outline (lfs_t  * lfs , lfs_file_t  * file ) {
32763295    file -> off  =  file -> pos ;
3277-     lfs_alloc_ack (lfs );
3296+     lfs_alloc_ckpoint (lfs );
32783297    int  err  =  lfs_file_relocate (lfs , file );
32793298    if  (err ) {
32803299        return  err ;
@@ -3537,7 +3556,7 @@ static lfs_ssize_t lfs_file_flushedwrite(lfs_t *lfs, lfs_file_t *file,
35373556                }
35383557
35393558                // extend file with new blocks 
3540-                 lfs_alloc_ack (lfs );
3559+                 lfs_alloc_ckpoint (lfs );
35413560                int  err  =  lfs_ctz_extend (lfs , & file -> cache , & lfs -> rcache ,
35423561                        file -> block , file -> pos ,
35433562                        & file -> block , & file -> off );
@@ -3580,7 +3599,7 @@ static lfs_ssize_t lfs_file_flushedwrite(lfs_t *lfs, lfs_file_t *file,
35803599        data  +=  diff ;
35813600        nsize  -=  diff ;
35823601
3583-         lfs_alloc_ack (lfs );
3602+         lfs_alloc_ckpoint (lfs );
35843603    }
35853604
35863605    return  size ;
@@ -4197,15 +4216,14 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
41974216    lfs_cache_zero (lfs , & lfs -> rcache );
41984217    lfs_cache_zero (lfs , & lfs -> pcache );
41994218
4200-     // setup lookahead, must be multiple of 64-bits, 32-bit aligned 
4219+     // setup lookahead buffer, note mount finishes initializing this after 
4220+     // we establish a decent pseudo-random seed 
42014221    LFS_ASSERT (lfs -> cfg -> lookahead_size  >  0 );
4202-     LFS_ASSERT (lfs -> cfg -> lookahead_size  % 8  ==  0  && 
4203-             (uintptr_t )lfs -> cfg -> lookahead_buffer  % 4  ==  0 );
42044222    if  (lfs -> cfg -> lookahead_buffer ) {
4205-         lfs -> free .buffer  =  lfs -> cfg -> lookahead_buffer ;
4223+         lfs -> lookahead .buffer  =  lfs -> cfg -> lookahead_buffer ;
42064224    } else  {
4207-         lfs -> free .buffer  =  lfs_malloc (lfs -> cfg -> lookahead_size );
4208-         if  (!lfs -> free .buffer ) {
4225+         lfs -> lookahead .buffer  =  lfs_malloc (lfs -> cfg -> lookahead_size );
4226+         if  (!lfs -> lookahead .buffer ) {
42094227            err  =  LFS_ERR_NOMEM ;
42104228            goto cleanup ;
42114229        }
@@ -4262,7 +4280,7 @@ static int lfs_deinit(lfs_t *lfs) {
42624280    }
42634281
42644282    if  (!lfs -> cfg -> lookahead_buffer ) {
4265-         lfs_free (lfs -> free .buffer );
4283+         lfs_free (lfs -> lookahead .buffer );
42664284    }
42674285
42684286    return  0 ;
@@ -4282,12 +4300,12 @@ static int lfs_rawformat(lfs_t *lfs, const struct lfs_config *cfg) {
42824300        LFS_ASSERT (cfg -> block_count  !=  0 );
42834301
42844302        // create free lookahead 
4285-         memset (lfs -> free .buffer , 0 , lfs -> cfg -> lookahead_size );
4286-         lfs -> free . off  =  0 ;
4287-         lfs -> free .size  =  lfs_min (8 * lfs -> cfg -> lookahead_size ,
4303+         memset (lfs -> lookahead .buffer , 0 , lfs -> cfg -> lookahead_size );
4304+         lfs -> lookahead . start  =  0 ;
4305+         lfs -> lookahead .size  =  lfs_min (8 * lfs -> cfg -> lookahead_size ,
42884306                lfs -> block_count );
4289-         lfs -> free . i  =  0 ;
4290-         lfs_alloc_ack (lfs );
4307+         lfs -> lookahead . next  =  0 ;
4308+         lfs_alloc_ckpoint (lfs );
42914309
42924310        // create root dir 
42934311        lfs_mdir_t  root ;
@@ -4495,7 +4513,7 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) {
44954513
44964514    // setup free lookahead, to distribute allocations uniformly across 
44974515    // boots, we start the allocator at a random location 
4498-     lfs -> free . off  =  lfs -> seed  % lfs -> block_count ;
4516+     lfs -> lookahead . start  =  lfs -> seed  % lfs -> block_count ;
44994517    lfs_alloc_drop (lfs );
45004518
45014519    return  0 ;
@@ -5468,10 +5486,10 @@ static int lfs1_mount(lfs_t *lfs, struct lfs1 *lfs1,
54685486        lfs -> lfs1 -> root [1 ] =  LFS_BLOCK_NULL ;
54695487
54705488        // setup free lookahead 
5471-         lfs -> free . off  =  0 ;
5472-         lfs -> free .size  =  0 ;
5473-         lfs -> free . i  =  0 ;
5474-         lfs_alloc_ack (lfs );
5489+         lfs -> lookahead . start  =  0 ;
5490+         lfs -> lookahead .size  =  0 ;
5491+         lfs -> lookahead . next  =  0 ;
5492+         lfs_alloc_ckpoint (lfs );
54755493
54765494        // load superblock 
54775495        lfs1_dir_t  dir ;
0 commit comments