LCOV - code coverage report
Current view: top level - groove - fd_groove_data.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 479 577 83.0 %
Date: 2025-03-20 12:08:36 Functions: 13 13 100.0 %

          Line data    Source code
       1             : #include "fd_groove_data.h"
       2             : 
       3             : /* fd_groove_data_private_active_displace lockfree atomically sets the
       4             :    active superblock for (szc,cgroup) to the superblock at offset
       5             :    superblock_off and returns the offset of the previously active
       6             :    superblock.  Offsets are relative to volume0.
       7             : 
       8             :    Assumes active_slot points in the caller's address space to the
       9             :    active superblock offset for (szc,cgroup).  If superblock_off is
      10             :    non-zero, further assumes the input superblock is for sizeclass szc,
      11             :    has at least one free block and is not in circulation (i.e. neither
      12             :    in any active slot nor on any inactive stack such that nobody can
      13             :    concurrently allocate from it).
      14             : 
      15             :    If this returns zero, there was no active superblock for (szc,cgroup)
      16             :    just before it was set to the input superblock.
      17             : 
      18             :    If this returns non-zero, the output superblock was the previously
      19             :    active superblock.  The output superblock will have at least one free
      20             :    block (and possibly growing over time to all blocks free due to
      21             :    concurrent frees) and will not be in circulation.
      22             : 
      23             :    If superblock_off is non-zero, the input superblock will be in
      24             :    circulation as the active superblock for (szc,cgroup) on return.
      25             : 
      26             :    If superblock_off is zero, there will be no active superblock for
      27             :    (szc,cgroup) on return.
      28             : 
      29             :    This is a compiler fence. */
      30             : 
      31             : static inline ulong
      32             : fd_groove_data_private_active_displace( ulong volatile *     _active_slot,
      33             :                                         fd_groove_volume_t * volume0,
      34     3242331 :                                         ulong                superblock_off ) {
      35     3242331 :   (void)volume0;
      36     3242331 :   FD_COMPILER_MFENCE();
      37     3242331 : # if FD_HAS_ATOMIC
      38     3242331 :   superblock_off = FD_ATOMIC_XCHG( _active_slot, superblock_off );
      39             : # else
      40             :   ulong old      = *_active_slot;
      41             :   *_active_slot  = superblock_off;
      42             :   superblock_off = old;
      43             : # endif
      44     3242331 :   FD_COMPILER_MFENCE();
      45     3242331 :   return superblock_off;
      46     3242331 : }
      47             : 
      48             : /* fd_groove_data_private_inactive_push does a lockfree atomic push of
      49             :    the superblock at superblock_off relative to volume0 onto the given
      50             :    inactive stack.  Assumes all inputs are valid, the inactive stack and
      51             :    superblock have the same sizeclass, the superblock is not in
      52             :    circulation, and superblock contains at least one free block.  On
      53             :    return, superblock will be the top of the inactive stack.  This is a
      54             :    compiler fence. */
      55             : 
      56             : static inline void
      57             : fd_groove_data_private_inactive_push( ulong volatile *     _inactive_stack,
      58             :                                       fd_groove_volume_t * volume0,
      59     1020489 :                                       ulong                superblock_off ) {
      60     1020489 :   FD_COMPILER_MFENCE();
      61             : 
      62     1020489 :   fd_groove_data_hdr_t * superblock = (fd_groove_data_hdr_t *)(((ulong)volume0) + superblock_off);
      63             : 
      64     1020489 :   for(;;) {
      65     1020489 :     ulong ver_off = *_inactive_stack;
      66             : 
      67     1020489 :     ulong      ver = ver_off &  (FD_GROOVE_BLOCK_FOOTPRINT-1UL);
      68     1020489 :     ulong next_off = ver_off & ~(FD_GROOVE_BLOCK_FOOTPRINT-1UL);
      69             : 
      70     1020489 :     superblock->info = next_off;
      71             : 
      72     1020489 :     ulong next_ver = (ver+1UL) &  (FD_GROOVE_BLOCK_FOOTPRINT-1UL);
      73             : 
      74     1020489 : #   if FD_HAS_ATOMIC
      75     1020489 :     ulong old = FD_ATOMIC_CAS( _inactive_stack, ver_off, next_ver | superblock_off );
      76             : #   else
      77             :     ulong old = *_inactive_stack;
      78             :     *_inactive_stack = fd_ulong_if( old==ver_off, next_ver | superblock_off, old );
      79             : #   endif
      80             : 
      81     1020489 :     if( FD_LIKELY( old==ver_off ) ) break;
      82             : 
      83           0 :     FD_SPIN_PAUSE();
      84           0 :   }
      85             : 
      86     1020489 :   FD_COMPILER_MFENCE();
      87     1020489 : }
      88             : 
      89             : /* fd_groove_data_private_inactive_pop does a lockfree atomic pop of the
      90             :    given inactive stack.  Assumes all inputs are valid.  Returns the
      91             :    offset relative to volume0 of the superblock.  The superblock will be
      92             :    for the same sizeclass as the inactive stack, will not be in
      93             :    circulation and will have at least 1 block free.  If the stack was
      94             :    empty when observed, returns 0.  This is a compiler fence. */
      95             : 
      96             : static inline ulong
      97             : fd_groove_data_private_inactive_pop( ulong volatile *     _inactive_stack,
      98     1028541 :                                      fd_groove_volume_t * volume0 ) {
      99             : 
     100     1028541 :   ulong off;
     101             : 
     102     1028541 :   FD_COMPILER_MFENCE();
     103             : 
     104     1028541 :   for(;;) {
     105     1028541 :     ulong ver_off = *_inactive_stack;
     106             : 
     107     1028541 :     ulong ver = ver_off &  (FD_GROOVE_BLOCK_FOOTPRINT-1UL);
     108     1028541 :     /**/  off = ver_off & ~(FD_GROOVE_BLOCK_FOOTPRINT-1UL);
     109             : 
     110     1028541 :     if( FD_UNLIKELY( !off ) ) break;
     111             : 
     112     1013592 :     fd_groove_data_hdr_t * superblock = (fd_groove_data_hdr_t *)(((ulong)volume0) + off);
     113             : 
     114     1013592 :     ulong next_ver = (ver+1UL) & (FD_GROOVE_BLOCK_FOOTPRINT-1UL);
     115     1013592 :     ulong next_off = superblock->info;
     116             : 
     117     1013592 : #   if FD_HAS_ATOMIC
     118     1013592 :     ulong old = FD_ATOMIC_CAS( _inactive_stack, ver_off, next_ver | next_off );
     119             : #   else
     120             :     ulong old = *_inactive_stack;
     121             :     *_inactive_stack = fd_ulong_if( old==ver_off, next_ver | next_off, old );
     122             : #   endif
     123             : 
     124     1013592 :     if( FD_LIKELY( old==ver_off ) ) break;
     125             : 
     126           0 :     FD_SPIN_PAUSE();
     127           0 :   }
     128             : 
     129     1028541 :   FD_COMPILER_MFENCE();
     130             : 
     131     1028541 :   return off;
     132     1028541 : }
     133             : 
     134             : void *
     135           9 : fd_groove_data_new( void * shmem ) {
     136           9 :   fd_groove_data_shmem_t * shdata = (fd_groove_data_shmem_t *)shmem;
     137             : 
     138           9 :   if( FD_UNLIKELY( !shdata ) ) {
     139           3 :     FD_LOG_WARNING(( "NULL shmem" ));
     140           3 :     return NULL;
     141           3 :   }
     142             : 
     143           6 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shdata, fd_groove_data_align() ) ) ) {
     144           3 :     FD_LOG_WARNING(( "misaligned shmem" ));
     145           3 :     return NULL;
     146           3 :   }
     147             : 
     148           3 :   ulong footprint = fd_groove_data_footprint();
     149             : 
     150           3 :   if( FD_UNLIKELY( !footprint ) ) { /* currently not possible */
     151           0 :     FD_LOG_WARNING(( "bad configuration" ));
     152           0 :     return NULL;
     153           0 :   }
     154             : 
     155           3 :   memset( shdata, 0, footprint );
     156             : 
     157           3 :   if( FD_UNLIKELY( !fd_groove_volume_pool_new( shdata->volume_pool ) ) ) return NULL; /* logs details (currently not possible) */
     158             : 
     159           3 :   FD_COMPILER_MFENCE();
     160           3 :   shdata->magic = FD_GROOVE_DATA_MAGIC;
     161           3 :   FD_COMPILER_MFENCE();
     162             : 
     163           3 :   return shmem;
     164           3 : }
     165             : 
     166             : fd_groove_data_t *
     167             : fd_groove_data_join( void * ljoin,
     168             :                      void * shdata,
     169             :                      void * volume0,
     170             :                      ulong  volume_max,
     171          30 :                      ulong  cgroup_hint ) {
     172          30 :   volume_max = fd_ulong_if( !!volume_max, volume_max, fd_groove_volume_pool_ele_max_max() );
     173             : 
     174          30 :   fd_groove_data_t *       join = (fd_groove_data_t       *)ljoin;
     175          30 :   fd_groove_data_shmem_t * data = (fd_groove_data_shmem_t *)shdata;
     176             : 
     177          30 :   if( FD_UNLIKELY( !join ) ) {
     178           3 :     FD_LOG_WARNING(( "NULL ljoin" ));
     179           3 :     return NULL;
     180           3 :   }
     181             : 
     182          27 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)join, alignof(fd_groove_data_t) ) ) ) {
     183           3 :     FD_LOG_WARNING(( "misaligned ljoin" ));
     184           3 :     return NULL;
     185           3 :   }
     186             : 
     187          24 :   if( FD_UNLIKELY( !data ) ) {
     188           3 :     FD_LOG_WARNING(( "NULL shdata" ));
     189           3 :     return NULL;
     190           3 :   }
     191             : 
     192          21 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)data, fd_groove_data_align() ) ) ) {
     193           3 :     FD_LOG_WARNING(( "misaligned shdata" ));
     194           3 :     return NULL;
     195           3 :   }
     196             : 
     197          18 :   if( FD_UNLIKELY( data->magic!=FD_GROOVE_DATA_MAGIC ) ) {
     198           3 :     FD_LOG_WARNING(( "bad magic" ));
     199           3 :     return NULL;
     200           3 :   }
     201             : 
     202          15 :   if( FD_UNLIKELY( !volume0 ) ) {
     203           3 :     FD_LOG_WARNING(( "NULL volume0" ));
     204           3 :     return NULL;
     205           3 :   }
     206             : 
     207          12 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)volume0, alignof(fd_groove_volume_t) ) ) ) {
     208           3 :     FD_LOG_WARNING(( "misaligned volume0" ));
     209           3 :     return NULL;
     210           3 :   }
     211             : 
     212           9 :   if( FD_UNLIKELY( !fd_groove_volume_pool_join( join->volume_pool, data->volume_pool, volume0, volume_max ) ) ) /* logs details */
     213           3 :     return NULL;
     214             : 
     215           6 :   join->active_slot    = data->active_slot;
     216           6 :   join->inactive_stack = data->inactive_stack;
     217           6 :   join->cgroup_hint    = cgroup_hint;
     218             : 
     219           6 :   return join;
     220           9 : }
     221             : 
     222             : void *
     223           9 : fd_groove_data_leave( fd_groove_data_t * join ) {
     224           9 :   if( FD_UNLIKELY( !join ) ) {
     225           3 :     FD_LOG_WARNING(( "NULL join" ));
     226           3 :     return NULL;
     227           3 :   }
     228             : 
     229           6 :   if( FD_UNLIKELY( !fd_groove_volume_pool_leave( join->volume_pool ) ) ) { /* currently not possible */
     230           0 :     FD_LOG_WARNING(( "fd_groove_volume_pool_leave failed" ));
     231           0 :     return NULL;
     232           0 :   }
     233             : 
     234           6 :   return join;
     235           6 : }
     236             : 
     237             : void *
     238          12 : fd_groove_data_delete( void * shdata ) {
     239          12 :   fd_groove_data_shmem_t * data = (fd_groove_data_shmem_t *)shdata;
     240             : 
     241          12 :   if( FD_UNLIKELY( !data ) ) {
     242           3 :     FD_LOG_WARNING(( "NULL shdata" ));
     243           3 :     return NULL;
     244           3 :   }
     245             : 
     246           9 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)data, fd_groove_data_align() ) ) ) {
     247           3 :     FD_LOG_WARNING(( "misaligned shdata" ));
     248           3 :     return NULL;
     249           3 :   }
     250             : 
     251           6 :   if( FD_UNLIKELY( data->magic!=FD_GROOVE_DATA_MAGIC ) ) {
     252           3 :     FD_LOG_WARNING(( "bad magic" ));
     253           3 :     return NULL;
     254           3 :   }
     255             : 
     256           3 :   FD_COMPILER_MFENCE();
     257           3 :   data->magic = 0UL;
     258           3 :   FD_COMPILER_MFENCE();
     259             : 
     260           3 :   return shdata;
     261           6 : }
     262             : 
     263             : /* FIXME: ideally would update the free_objs bit field after writing
     264             :    an alloc / superblock data hdr to give non-invasive concurrent
     265             :    real-time inspection / diagnostics a strong guarantee all objects
     266             :    marked as allocated have valid data hdrs. */
     267             : 
     268             : static int
     269             : fd_groove_data_private_alloc_obj( fd_groove_data_t * data,
     270             :                                   ulong              obj_szc,
     271             :                                   ulong *            _obj_off,
     272     2121675 :                                   ulong *            _obj_idx ) {
     273             : 
     274     2121675 :   fd_groove_volume_t * _volume0 = (fd_groove_volume_t *)fd_groove_data_volume0( data );
     275             : 
     276     2121675 : # if FD_GROOVE_PARANOID
     277     2121675 :   fd_groove_volume_t * _volume1 = (fd_groove_volume_t *)fd_groove_data_volume1( data );
     278     2121675 : # endif
     279             : 
     280     2121675 :   ulong obj_cnt       = (ulong)fd_groove_data_szc_cfg[ obj_szc ].obj_cnt;
     281     2121675 :   ulong obj_footprint = (ulong)fd_groove_data_szc_cfg[ obj_szc ].obj_footprint;
     282     2121675 :   ulong cgroup_mask   = (ulong)fd_groove_data_szc_cfg[ obj_szc ].cgroup_mask;
     283     2121675 :   ulong parent_szc    = (ulong)fd_groove_data_szc_cfg[ obj_szc ].parent_szc;
     284             : 
     285             :   /* At this point, we are allocating an object from a sizeclass obj_szc
     286             :      superblock.  Get the locations of the active slot and inactive
     287             :      stack for this sizeclass and our concurrency group. */
     288             : 
     289     2121675 :   ulong cgroup = data->cgroup_hint & cgroup_mask;
     290             : 
     291     2121675 :   ulong volatile * _active_slot    = data->active_slot    + obj_szc + FD_GROOVE_DATA_SZC_CNT*cgroup;
     292     2121675 :   ulong volatile * _inactive_stack = data->inactive_stack + obj_szc;
     293             : 
     294     2121675 :   ulong superblock_off;
     295             : 
     296             :   /* Try to get exclusive access to the active superblock.  Note that
     297             :      active superblocks have at least one free obj.  We do this
     298             :      test-and-test-and-set style to avoid atomic operations if there is
     299             :      no current active_superblock for this cgroup. */
     300             : 
     301     2121675 :   FD_COMPILER_MFENCE();
     302     2121675 :   superblock_off = *_active_slot;
     303     2121675 :   FD_COMPILER_MFENCE();
     304             : 
     305     2121675 :   if( FD_LIKELY( superblock_off ) ) superblock_off = fd_groove_data_private_active_displace( _active_slot, _volume0, 0UL );
     306             : 
     307     2121675 :   if( FD_UNLIKELY( !superblock_off ) ) {
     308             : 
     309             :     /* At this point, there was no active superblock for our cgroup when
     310             :        we observed it.  Try to pop the inactive superblock stack for
     311             :        this sizeclass instead.  Note that inactive superblocks also have
     312             :        at least one free obj. */
     313             : 
     314      996180 :     superblock_off = fd_groove_data_private_inactive_pop( _inactive_stack, _volume0 );
     315             : 
     316      996180 :     if( FD_UNLIKELY( !superblock_off ) ) {
     317             : 
     318             :       /* At this point, there were no inactive superblocks for this
     319             :          sizeclass when we observed the inactive stack.  Try to create a
     320             :          new superblock for this sizeclass */
     321             : 
     322       14376 :       ulong parent_idx = 0UL; /* reduce risk of uninitialized variable false positives from code analysis tools */
     323             : 
     324       14376 :       if( FD_UNLIKELY( parent_szc==FD_GROOVE_DATA_SZC_CNT ) ) { /* Acquire a volume to use for the new superblock */
     325             : 
     326           6 :         int err;
     327           6 :         fd_groove_volume_t * _volume = fd_groove_volume_pool_acquire( data->volume_pool, NULL, 1 /* blocking */, &err );
     328             : 
     329           6 :         if( FD_UNLIKELY( !_volume ) ) {
     330           3 :           if( FD_UNLIKELY( err!=FD_POOL_ERR_EMPTY ) ) {
     331           0 :             FD_LOG_WARNING(( "fd_groove_volume_pool_acquire failed (%i-%s)", err, fd_groove_volume_pool_strerror( err ) ));
     332           0 :             return FD_GROOVE_ERR_CORRUPT;
     333           0 :           }
     334           3 :           return FD_GROOVE_ERR_FULL;
     335           3 :         }
     336             : 
     337           3 : #       if FD_GROOVE_PARANOID
     338           3 :         ulong volume_off = (ulong)_volume - (ulong)_volume0;
     339             : 
     340           3 :         if( FD_UNLIKELY( !( (_volume0<=_volume) & (_volume<_volume1) &
     341           3 :                             fd_ulong_is_aligned( volume_off, FD_GROOVE_VOLUME_FOOTPRINT ) ) ) ) {
     342           0 :           FD_LOG_WARNING(( "volume not at a valid groove data local address" ));
     343           0 :           return FD_GROOVE_ERR_CORRUPT;
     344           0 :         }
     345             : 
     346           3 :         if( FD_UNLIKELY( !( (_volume->magic                         ==~FD_GROOVE_VOLUME_MAGIC  ) &
     347           3 :                             (_volume->idx*FD_GROOVE_VOLUME_FOOTPRINT==volume_off               ) &
     348           3 :                             (_volume->info_sz                       <=FD_GROOVE_VOLUME_INFO_MAX) ) ) ) {
     349           0 :           FD_LOG_WARNING(( "unexpected volume header" ));
     350           0 :           return FD_GROOVE_ERR_CORRUPT;
     351           0 :         }
     352           3 : #       endif
     353             : 
     354           3 :         FD_COMPILER_MFENCE();
     355           3 :         _volume->magic = FD_GROOVE_VOLUME_MAGIC; /* mark volume as potentially containing groove data allocations */
     356           3 :         FD_COMPILER_MFENCE();
     357             : 
     358             :       //parent_idx     = 0UL; /* See note above about initialization */
     359           3 :         superblock_off = (ulong)_volume->data - (ulong)_volume0;
     360             : 
     361       14370 :       } else { /* Acquire a parent_szc object to use for the new superblock */
     362             : 
     363       14370 :         int err = fd_groove_data_private_alloc_obj( data, parent_szc, &superblock_off, &parent_idx ); /* logs details */
     364       14370 :         if( FD_UNLIKELY( err ) ) return err;
     365             : 
     366       14370 :       }
     367             : 
     368       14340 :       ulong superblock_align = FD_GROOVE_DATA_HDR_ALIGN;
     369       14340 :       ulong superblock_sz    = FD_GROOVE_BLOCK_FOOTPRINT - FD_GROOVE_DATA_HDR_FOOTPRINT + obj_footprint*obj_cnt;
     370             : 
     371       14340 :       fd_groove_data_hdr_t * _superblock_hdr = (fd_groove_data_hdr_t *)(((ulong)_volume0) + superblock_off);
     372             : 
     373       14340 : #     if FD_GROOVE_PARANOID
     374       14340 :       if( FD_UNLIKELY( !( ((ulong)_volume0<(ulong)_superblock_hdr) & ((ulong)_superblock_hdr<(ulong)_volume1) &
     375       14340 :                           fd_ulong_is_aligned( (ulong)_superblock_hdr, FD_GROOVE_BLOCK_ALIGN ) ) ) ) {
     376           0 :         FD_LOG_WARNING(( "superblock not at a valid groove data local address" ));
     377           0 :         return FD_GROOVE_ERR_CORRUPT;
     378           0 :       }
     379       14340 : #     endif
     380             : 
     381       14340 :       *_superblock_hdr = fd_groove_data_hdr( FD_GROOVE_DATA_HDR_TYPE_SUPERBLOCK, parent_idx, obj_szc,
     382       14340 :                                              superblock_align, fd_ulong_min( superblock_sz, (1UL<<25)-1UL ), 0UL /* no next */ );
     383       14340 :       *(ulong *)(_superblock_hdr+1) = (1UL<<obj_cnt)-1UL; /* mark all objects in superblock as free */
     384       14340 :     }
     385      996180 :   }
     386             : 
     387             :   /* At this point, we have exclusive access to the superblock, there is
     388             :      at least one free block in it and only we can allocate blocks from
     389             :      it.  (Other threads could free blocks to it concurrently though.)
     390             :      Allocate a free block.  If there were more free blocks, put the
     391             :      superblock back into circulation as the active superblock for our
     392             :      cgroup.  Otherwise, free will put in back into circulation when the
     393             :      application frees a block in it.  See fd_alloc.c for details. */
     394             : 
     395     2121639 :   fd_groove_data_hdr_t * _superblock_hdr = (fd_groove_data_hdr_t *)(((ulong)_volume0) + superblock_off);
     396             : 
     397     2121639 :   ulong volatile * _free_objs = (ulong volatile *)(_superblock_hdr+1);
     398             : 
     399     2121639 : # if FD_GROOVE_PARANOID
     400     2121639 :   if( FD_UNLIKELY( !( ((ulong)_volume0<(ulong)_superblock_hdr) & ((ulong)_superblock_hdr<(ulong)_volume1) &
     401     2121639 :                       fd_ulong_is_aligned( (ulong)_superblock_hdr, FD_GROOVE_BLOCK_ALIGN ) ) ) ) {
     402           0 :     FD_LOG_WARNING(( "superblock not at a valid groove data local address" ));
     403           0 :     return FD_GROOVE_ERR_CORRUPT;
     404           0 :   }
     405             : 
     406     2121639 :   fd_groove_data_hdr_t superblock_hdr = *_superblock_hdr;
     407             : 
     408     2121639 :   ulong superblock_type = fd_groove_data_hdr_type( superblock_hdr );
     409     2121639 :   ulong superblock_szc  = fd_groove_data_hdr_szc ( superblock_hdr );
     410             : 
     411     2121639 :   if( FD_UNLIKELY( !((superblock_type==FD_GROOVE_DATA_HDR_TYPE_SUPERBLOCK) & (superblock_szc==obj_szc)) ) ) {
     412           0 :     FD_LOG_WARNING(( "unexpected superblock header" ));
     413           0 :     return FD_GROOVE_ERR_CORRUPT;
     414           0 :   }
     415     2121639 : # endif
     416             : 
     417     2121639 :   FD_COMPILER_MFENCE();
     418     2121639 :   ulong free_objs = *_free_objs;
     419     2121639 :   FD_COMPILER_MFENCE();
     420             : 
     421     2121639 : # if FD_GROOVE_PARANOID
     422     2121639 :   if( FD_UNLIKELY( (!free_objs) | (!!fd_ulong_shift_right( free_objs, (int)obj_cnt )) ) ) {
     423           0 :     FD_LOG_WARNING(( "%s", (!free_objs) ? "full superblock in circulation" : "invalid free_objs bit field" ));
     424           0 :     return FD_GROOVE_ERR_CORRUPT;
     425           0 :   }
     426     2121639 : # endif
     427             : 
     428     2121639 :   ulong obj = fd_ulong_lsb( free_objs );
     429             : 
     430     2121639 :   FD_COMPILER_MFENCE();
     431     2121639 : # if FD_HAS_ATOMIC
     432     2121639 :   free_objs = FD_ATOMIC_FETCH_AND_SUB( _free_objs, obj ); /* Marginally better asm than FETCH_AND_AND */
     433             : # else
     434             :   free_objs   = *_free_objs;
     435             :   *_free_objs = free_objs & ~obj;
     436             : # endif
     437     2121639 :   FD_COMPILER_MFENCE();
     438             : 
     439     2121639 : # if FD_GROOVE_PARANOID
     440     2121639 :   if( FD_UNLIKELY( (!free_objs) | (!!fd_ulong_shift_right( free_objs, (int)obj_cnt )) ) ) {
     441           0 :     FD_LOG_WARNING(( "%s", (!free_objs) ? "full superblock in circulation" : "invalid free_objs bit field" ));
     442           0 :     return FD_GROOVE_ERR_CORRUPT;
     443           0 :   }
     444     2121639 : # endif
     445             : 
     446     2121639 :   if( FD_LIKELY( free_objs!=obj ) ) {
     447      218265 :     ulong displaced_superblock_off = fd_groove_data_private_active_displace( _active_slot, _volume0, superblock_off );
     448      218265 :     if( FD_UNLIKELY( displaced_superblock_off ) )
     449           0 :       fd_groove_data_private_inactive_push( _inactive_stack, _volume0, displaced_superblock_off );
     450      218265 :   }
     451             : 
     452             :   /* At this point, we've allocated the object */
     453             : 
     454     2121639 :   ulong obj_idx = (ulong)fd_ulong_find_lsb( obj );
     455             : 
     456     2121639 :   *_obj_off = superblock_off + FD_GROOVE_BLOCK_FOOTPRINT + obj_idx*obj_footprint;
     457     2121639 :   *_obj_idx = obj_idx;
     458     2121639 :   return FD_GROOVE_SUCCESS;
     459     2121639 : }
     460             : 
     461             : void *
     462             : fd_groove_data_alloc( fd_groove_data_t * data,
     463             :                       ulong              align,
     464             :                       ulong              sz,
     465             :                       ulong              tag,
     466     2107329 :                       int *              _err ) {
     467             : 
     468     2107329 :   int stack_err[1];
     469     2107329 :   if( !_err ) _err = stack_err;
     470             : 
     471             :   /* Check input args */
     472             : 
     473     2107329 :   if( FD_UNLIKELY( !data ) ) {
     474           6 :     FD_LOG_WARNING(( "NULL data" ));
     475           6 :     *_err = FD_GROOVE_ERR_INVAL;
     476           6 :     return NULL;
     477           6 :   }
     478             : 
     479     2107323 :   align = fd_ulong_if( !!align, align, FD_GROOVE_DATA_ALLOC_ALIGN_DEFAULT );
     480     2107323 :   if( FD_UNLIKELY( !(fd_ulong_is_pow2( align ) & (align<=FD_GROOVE_DATA_ALLOC_ALIGN_MAX)) ) ) {
     481          12 :     FD_LOG_WARNING(( "bad align" ));
     482          12 :     *_err = FD_GROOVE_ERR_INVAL;
     483          12 :     return NULL;
     484          12 :   }
     485             : 
     486     2107311 :   ulong off_obj   = fd_ulong_align_up( FD_GROOVE_DATA_HDR_FOOTPRINT, align );
     487     2107311 :   ulong footprint = fd_ulong_align_up( off_obj+sz, FD_GROOVE_BLOCK_ALIGN );
     488             : 
     489     2107311 :   if( FD_UNLIKELY( !((sz<footprint) & (footprint<=FD_GROOVE_DATA_ALLOC_FOOTPRINT_MAX)) ) ) {
     490           6 :     FD_LOG_WARNING(( "bad sz/align" ));
     491           6 :     *_err = FD_GROOVE_ERR_INVAL;
     492           6 :     return NULL;
     493           6 :   }
     494             : 
     495             :   /* Acquire an object from the tightest suitable sizeclass */
     496             : 
     497     2107305 :   ulong obj_szc = fd_groove_data_szc( footprint );
     498             : 
     499     2107305 :   ulong obj_off;
     500     2107305 :   ulong obj_idx;
     501     2107305 :   int   err = fd_groove_data_private_alloc_obj( data, obj_szc, &obj_off, &obj_idx ); /* logs details */
     502     2107305 :   if( FD_UNLIKELY( err ) ) {
     503           3 :     *_err = err;
     504           3 :     return NULL;
     505           3 :   }
     506             : 
     507             :   /* Carve an allocation into it. */
     508             : 
     509     2107302 :   fd_groove_data_hdr_t * obj_hdr = (fd_groove_data_hdr_t *)((ulong)fd_groove_volume_pool_shele( data->volume_pool ) + obj_off);
     510             : 
     511     2107302 :   *obj_hdr = fd_groove_data_hdr( FD_GROOVE_DATA_HDR_TYPE_ALLOC, obj_idx, obj_szc, align, sz, tag );
     512             : 
     513     2107302 :   *_err = FD_GROOVE_SUCCESS;
     514     2107302 :   return (void *)((ulong)obj_hdr + off_obj);
     515     2107305 : }
     516             : 
     517             : int
     518             : fd_groove_data_private_free( fd_groove_data_t * data,
     519             :                              void *             _obj,
     520     2109834 :                              ulong              exp_type ) {
     521             : 
     522             : # if !FD_GROOVE_PARANOID
     523             :   (void)exp_type; /* Suppress unused warning if running without paranoia */
     524             : # endif
     525             : 
     526     2109834 :   if( FD_UNLIKELY( !data ) ) {
     527           3 :     FD_LOG_WARNING(( "NULL data" ));
     528           3 :     return FD_GROOVE_ERR_INVAL;
     529           3 :   }
     530             : 
     531     2109831 :   if( FD_UNLIKELY( !_obj ) ) return FD_GROOVE_ERR_INVAL;
     532             : 
     533     2109828 :   fd_groove_data_hdr_t * _obj_hdr = fd_groove_data_object_hdr( _obj );
     534             : 
     535     2109828 :   fd_groove_volume_t * _volume0 = (fd_groove_volume_t *)fd_groove_data_volume0( data );
     536             : 
     537     2109828 : # if FD_GROOVE_PARANOID
     538     2109828 :   fd_groove_volume_t * _volume1 = (fd_groove_volume_t *)fd_groove_data_volume1( data );
     539     2109828 :   if( FD_UNLIKELY( !( ((ulong)_volume0<=(ulong)_obj_hdr                             ) &
     540     2109828 :                       ((ulong)_obj_hdr< (ulong)_volume1                             ) &
     541     2109828 :                       (fd_ulong_is_aligned( (ulong)_obj_hdr, FD_GROOVE_BLOCK_ALIGN )) ) ) ) {
     542           0 :     FD_LOG_WARNING(( "object not at a valid groove data local address" ));
     543           0 :     return FD_GROOVE_ERR_INVAL;
     544           0 :   }
     545     2109828 : # endif
     546             : 
     547     2109828 :   fd_groove_data_hdr_t obj_hdr = *_obj_hdr;
     548             : 
     549     2109828 :   ulong obj_type = fd_groove_data_hdr_type( obj_hdr );
     550     2109828 :   ulong obj_idx  = fd_groove_data_hdr_idx ( obj_hdr );
     551     2109828 :   ulong obj_szc  = fd_groove_data_hdr_szc ( obj_hdr );
     552             : 
     553     2109828 : #if FD_GROOVE_PARANOID
     554     2109828 :   if( FD_UNLIKELY( !((obj_type==exp_type) & (obj_szc<FD_GROOVE_DATA_SZC_CNT)) ) ) {
     555           0 :     FD_LOG_WARNING(( "object does not appear to be a groove data %s",
     556           0 :                      exp_type==FD_GROOVE_DATA_HDR_TYPE_ALLOC ? "alloc" : "superblock" ));
     557           0 :     return FD_GROOVE_ERR_INVAL;
     558           0 :   }
     559     2109828 : # endif
     560             : 
     561     2109828 :   obj_szc = fd_ulong_if( obj_type==FD_GROOVE_DATA_HDR_TYPE_ALLOC, obj_szc,
     562     2109828 :                         (ulong)fd_groove_data_szc_cfg[ obj_szc ].parent_szc );
     563     2109828 :   ulong obj_cnt       = (ulong)fd_groove_data_szc_cfg[ obj_szc ].obj_cnt;
     564     2109828 : # if FD_GROOVE_PARANOID
     565     2109828 :   ulong obj_footprint = (ulong)fd_groove_data_szc_cfg[ obj_szc ].obj_footprint;
     566             : 
     567     2109828 :   ulong req_align    = fd_groove_data_hdr_align( obj_hdr );
     568     2109828 :   ulong req_sz       = fd_groove_data_hdr_sz   ( obj_hdr );
     569     2109828 :   ulong req_footprint =
     570     2109828 :     fd_ulong_align_up( fd_ulong_align_up( FD_GROOVE_DATA_HDR_FOOTPRINT, req_align ) + req_sz, FD_GROOVE_BLOCK_ALIGN );
     571             : 
     572     2109828 :   if( FD_UNLIKELY( !( (obj_idx<obj_cnt                 ) &
     573     2109828 :                       (fd_ulong_is_pow2( req_align )   ) &
     574     2109828 :                       (req_align<=FD_GROOVE_BLOCK_ALIGN) &
     575     2109828 :                       (req_footprint<=obj_footprint    ) ) ) ) {
     576           0 :     FD_LOG_WARNING(( "object does not appear to be a groove data %s",
     577           0 :                      exp_type==FD_GROOVE_DATA_HDR_TYPE_ALLOC ? "alloc" : "superblock" ));
     578           0 :     return FD_GROOVE_ERR_INVAL;
     579           0 :   }
     580     2109828 : # endif
     581             : 
     582             :   /* At this point, we appear to have a valid allocated object.  Mark
     583             :      the object as not valid and then free it.
     584             : 
     585             :      Note: marking the object as dead is optional.  It is mostly a hint
     586             :      for diagnostics and for handholding as groove data users shouldn't
     587             :      be calling free on it again and alloc doesn't care about the state
     588             :      unallocated object memory.  Most useful, marking the object as dead
     589             :      can detect double free scenarios.
     590             : 
     591             :      The below implementation is not robust against _concurrent_ double
     592             :      frees.  Would probably have to use something like ATOMIC_CAS
     593             :      semantics on the object header bits to insure nobody freed the
     594             :      object behind our back.  And since the free bit field update isn't
     595             :      atomic with marking the object as dead, would probably need further
     596             :      to do something like mark the object as freeing then update bit
     597             :      field and then mark object as dead, etc. */
     598             : 
     599     2109828 :   fd_groove_data_hdr_t * _superblock_hdr = fd_groove_data_superblock_hdr( _obj, obj_szc, obj_idx );
     600             : 
     601     2109828 :   ulong volatile * _free_objs = (ulong volatile *)(_superblock_hdr+1);
     602             : 
     603     2109828 :   ulong free_objs;
     604             : 
     605     2109828 :   ulong obj = 1UL << obj_idx;
     606             : 
     607     2109828 : # if FD_GROOVE_PARANOID
     608     2109828 :   if( FD_UNLIKELY( !( ((ulong)_volume0<(ulong)_superblock_hdr) & ((ulong)_superblock_hdr<(ulong)_obj_hdr) &
     609     2109828 :                       fd_ulong_is_aligned( (ulong)_superblock_hdr, FD_GROOVE_BLOCK_ALIGN ) ) ) ) {
     610           0 :     FD_LOG_WARNING(( "superblock not at a valid groove data local address" ));
     611           0 :     return FD_GROOVE_ERR_INVAL;
     612           0 :   }
     613             : 
     614     2109828 :   fd_groove_data_hdr_t superblock_hdr = *_superblock_hdr;
     615             : 
     616     2109828 :   ulong superblock_type = fd_groove_data_hdr_type( superblock_hdr );
     617     2109828 :   ulong superblock_szc  = fd_groove_data_hdr_szc ( superblock_hdr );
     618             : 
     619     2109828 :   if( FD_UNLIKELY( !((superblock_type==FD_GROOVE_DATA_HDR_TYPE_SUPERBLOCK) & (superblock_szc==obj_szc)) ) ) {
     620           0 :     FD_LOG_WARNING(( "unexpected superblock header" ));
     621           0 :     return FD_GROOVE_ERR_INVAL;
     622           0 :   }
     623             : 
     624     2109828 :   FD_COMPILER_MFENCE();
     625     2109828 :   free_objs = *_free_objs;
     626     2109828 :   FD_COMPILER_MFENCE();
     627             : 
     628     2109828 :   if( FD_UNLIKELY( (free_objs & obj) | fd_ulong_shift_right( free_objs, (int)obj_cnt ) ) ) {
     629           0 :     FD_LOG_WARNING(( "%s", (free_objs & obj) ? "possible concurrent double free" : "invalid free_objs bit field" ));
     630           0 :     return FD_GROOVE_ERR_INVAL;
     631           0 :   }
     632             : 
     633     2109828 :   FD_COMPILER_MFENCE();
     634     2109828 :   _obj_hdr->bits = 0UL; /* sets object type to an invalid value */
     635     2109828 :   FD_COMPILER_MFENCE();
     636     2109828 : # endif
     637             : 
     638     2109828 :   FD_COMPILER_MFENCE();
     639     2109828 : # if FD_HAS_ATOMIC
     640     2109828 :   free_objs = FD_ATOMIC_FETCH_AND_ADD( _free_objs, obj ); /* Marginally better asm than FETCH_AND_OR */
     641             : # else
     642             :   free_objs   = *_free_objs;
     643             :   *_free_objs = free_objs | obj;
     644             : # endif
     645     2109828 :   FD_COMPILER_MFENCE();
     646             : 
     647     2109828 : # if FD_GROOVE_PARANOID
     648     2109828 :   if( FD_UNLIKELY( (free_objs & obj) | fd_ulong_shift_right( free_objs, (int)obj_cnt ) ) ) {
     649           0 :     FD_LOG_WARNING(( "%s", (free_objs & obj) ? "possible concurrent double free" : "invalid free_objs bit field" ));
     650           0 :     return FD_GROOVE_ERR_CORRUPT;
     651           0 :   }
     652     2109828 : # endif
     653             : 
     654             :   /* At this point, we've freed the object.  We might need to get the
     655             :      object's superblock back into circulation and/or free up excess
     656             :      empty superblocks for this sizeclass. */
     657             : 
     658     2109828 :   ulong free_cnt = (ulong)fd_ulong_popcnt( free_objs );
     659             : 
     660     2109828 :   if( FD_UNLIKELY( !free_cnt ) ) {
     661             : 
     662             :     /* At this point, the superblock was full before we freed it and
     663             :        thus not in circulation for use by fd_alloc.  We need to get the
     664             :        superblock back into circulation.  There are options for this
     665             :        with various subtle tradeoffs.  See fd_alloc.c for details.
     666             :        (FIXME: amongst then, consider using allocation cgroup instead of
     667             :        the groove data instance's cgroup?) */
     668             : 
     669     1898571 :     ulong cgroup = fd_groove_data_cgroup_hint( data ) & (ulong)fd_groove_data_szc_cfg[ obj_szc ].cgroup_mask;
     670             : 
     671     1898571 :     ulong displaced_superblock_off =
     672     1898571 :       fd_groove_data_private_active_displace( data->active_slot + obj_szc + FD_GROOVE_DATA_SZC_CNT*cgroup,
     673     1898571 :                                               _volume0, (ulong)_superblock_hdr - (ulong)_volume0 );
     674             : 
     675     1898571 :     if( FD_UNLIKELY( displaced_superblock_off ) )
     676      991227 :       fd_groove_data_private_inactive_push( data->inactive_stack + obj_szc, _volume0, displaced_superblock_off );
     677             : 
     678     1898571 :   } else if( FD_UNLIKELY( (free_cnt+1UL)==obj_cnt ) ) {
     679             : 
     680             :     /* At this point, the superblock was completely empty after we freed
     681             :        from it, hence it is still in circulation.  If there is also a
     682             :        completely empty superblock on top of the inactive stack, we free
     683             :        that one for general reuse.  This is more subtle than it looks,
     684             :        see fd_alloc.c for details. */
     685             : 
     686       32361 :     ulong volatile * _inactive_stack = data->inactive_stack + obj_szc;
     687             : 
     688       32361 :     ulong superblock_off = fd_groove_data_private_inactive_pop( _inactive_stack, _volume0 );
     689             : 
     690       32361 :     if( FD_LIKELY( superblock_off ) ) {
     691             : 
     692       31788 :       _superblock_hdr = (fd_groove_data_hdr_t *)((ulong)_volume0 + superblock_off);
     693             : 
     694       31788 :       _free_objs = (ulong volatile *)(_superblock_hdr+1);
     695             : 
     696       31788 : #     if FD_GROOVE_PARANOID
     697       31788 :       if( FD_UNLIKELY( !( ((ulong)_volume0<(ulong)_superblock_hdr) & ((ulong)_superblock_hdr<(ulong)_volume1) &
     698       31788 :                           fd_ulong_is_aligned( (ulong)_superblock_hdr, FD_GROOVE_BLOCK_ALIGN ) ) ) ) {
     699           0 :         FD_LOG_WARNING(( "superblock not at a valid groove data local address" ));
     700           0 :         return FD_GROOVE_ERR_CORRUPT;
     701           0 :       }
     702             : 
     703       31788 :       superblock_type = fd_groove_data_hdr_type( superblock_hdr );
     704       31788 :       superblock_szc  = fd_groove_data_hdr_szc ( superblock_hdr );
     705             : 
     706       31788 :       if( FD_UNLIKELY( !((superblock_type==FD_GROOVE_DATA_HDR_TYPE_SUPERBLOCK) & (superblock_szc==obj_szc)) ) ) {
     707           0 :         FD_LOG_WARNING(( "unexpected superblock header" ));
     708           0 :         return FD_GROOVE_ERR_CORRUPT;
     709           0 :       }
     710       31788 : #     endif
     711             : 
     712       31788 :       FD_COMPILER_MFENCE();
     713       31788 :       free_objs = *_free_objs;
     714       31788 :       FD_COMPILER_MFENCE();
     715             : 
     716       31788 : #     if FD_GROOVE_PARANOID
     717       31788 :       if( FD_UNLIKELY( fd_ulong_shift_right( free_objs, (int)obj_cnt ) ) ) {
     718           0 :         FD_LOG_WARNING(( "invalid free_objs bit field" ));
     719           0 :         return FD_GROOVE_ERR_CORRUPT;
     720           0 :       }
     721       31788 : #     endif
     722             : 
     723       31788 :       free_cnt = (ulong)fd_ulong_popcnt( free_objs );
     724             : 
     725       31788 :       if( FD_LIKELY( free_cnt<obj_cnt ) ) { /* inactive top was not completely empty, return to circulation */
     726             : 
     727       29262 :         fd_groove_data_private_inactive_push( _inactive_stack, _volume0, superblock_off );
     728             : 
     729       29262 :       } else if( FD_LIKELY( obj_szc<(FD_GROOVE_DATA_SZC_CNT-1UL) ) ) { /* completely empty and should free from parent sb */
     730             : 
     731        2526 :         int err = fd_groove_data_private_free( data, _superblock_hdr+1, FD_GROOVE_DATA_HDR_TYPE_SUPERBLOCK ); /* offset the hdr */
     732        2526 :         if( FD_UNLIKELY( err ) ) {
     733           0 :           FD_LOG_WARNING(( "superblock free failed (%i-%s)", err, fd_groove_strerror( err ) ));
     734           0 :           return FD_GROOVE_ERR_CORRUPT;
     735           0 :         }
     736             : 
     737        2526 :       } else { /* completely empty and should free parent volume */
     738             : 
     739           0 :         ulong volume_off = superblock_off - FD_GROOVE_BLOCK_FOOTPRINT;
     740             : 
     741           0 :         fd_groove_volume_t * _volume = (fd_groove_volume_t *)((ulong)_volume0 + volume_off);
     742             : 
     743           0 : #       if FD_GROOVE_PARANOID
     744           0 :         if( FD_UNLIKELY( !( (_volume0<=_volume) & (_volume<_volume1) &
     745           0 :                             fd_ulong_is_aligned( volume_off, FD_GROOVE_VOLUME_FOOTPRINT ) ) ) ) {
     746           0 :           FD_LOG_WARNING(( "volume not at a valid groove data local address" ));
     747           0 :           return FD_GROOVE_ERR_CORRUPT;
     748           0 :         }
     749             : 
     750           0 :         if( FD_UNLIKELY( !( (_volume->magic                         ==FD_GROOVE_VOLUME_MAGIC   ) &
     751           0 :                             (_volume->idx*FD_GROOVE_VOLUME_FOOTPRINT==volume_off               ) &
     752           0 :                             (_volume->info_sz                       <=FD_GROOVE_VOLUME_INFO_MAX) ) ) ) {
     753           0 :           FD_LOG_WARNING(( "unexpected volume header" ));
     754           0 :           return FD_GROOVE_ERR_CORRUPT;
     755           0 :         }
     756           0 : #       endif
     757             : 
     758           0 :         FD_COMPILER_MFENCE();
     759           0 :         _volume->magic = ~FD_GROOVE_VOLUME_MAGIC; /* mark volume as containing no groove data allocations */
     760           0 :         FD_COMPILER_MFENCE();
     761             : 
     762           0 :         int err = fd_groove_volume_pool_release( data->volume_pool, _volume, 1 /* blocking */ );
     763           0 :         if( FD_UNLIKELY( err ) ) {
     764           0 :           FD_LOG_WARNING(( "fd_groove_volume_pool_release failed (%i-%s)", err, fd_groove_volume_pool_strerror( err ) ));
     765           0 :           return FD_GROOVE_ERR_CORRUPT;
     766           0 :         }
     767             : 
     768           0 :       }
     769       31788 :     }
     770       32361 :   }
     771             : 
     772     2109828 :   return FD_GROOVE_SUCCESS;
     773     2109828 : }
     774             : 
     775      619491 : #define TEST(c) do {                                                                                \
     776      619491 :     if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return FD_GROOVE_ERR_CORRUPT; } \
     777      619491 :   } while(0)
     778             : 
     779             : /* fd_groove_data_private_verify_superblock verifies the location
     780             :    superblock_off (relative to _volume0) seems to contain a valid
     781             :    superblock.  groove data is located in the caller's address space at
     782             :    [_volume0,_volume1).  exp_szc gives the expected sizeclass for the
     783             :    superblock.  If in_circulation is non-zero, the superblock is known
     784             :    to be in circulation (i.e. contains at least one free object / is
     785             :    either any active or an inactive superblock / is available to alloc
     786             :    for allocation).  Assumes _volume0, _volume1, exp_szc and the
     787             :    sizeclass configuration are valid. */
     788             : 
     789             : static int
     790             : fd_groove_data_private_verify_superblock( ulong                      superblock_off,
     791             :                                           ulong                      exp_szc,
     792             :                                           int                        in_circulation,
     793             :                                           int                        verify_descendents,
     794             :                                           fd_groove_volume_t const * _volume0,
     795       32169 :                                           fd_groove_volume_t const * _volume1 ) {
     796             : 
     797             :   /* Verify superblock_off */
     798             : 
     799       32169 :   fd_groove_data_hdr_t const * _superblock_hdr = (fd_groove_data_hdr_t const *)(((ulong)_volume0) + superblock_off);
     800             : 
     801       32169 :   TEST( ((ulong)_volume0<(ulong)_superblock_hdr) & ((ulong)_superblock_hdr<(ulong)_volume1) );
     802       32169 :   TEST( fd_ulong_is_aligned( (ulong)_superblock_hdr, FD_GROOVE_BLOCK_ALIGN ) );
     803             : 
     804             :   /* Verify superblock header */
     805             : 
     806       32169 :   fd_groove_data_hdr_t hdr = *_superblock_hdr;
     807             : 
     808       32169 :   TEST( fd_groove_data_hdr_type( hdr )==FD_GROOVE_DATA_HDR_TYPE_SUPERBLOCK );
     809             : 
     810       32169 :   ulong szc = fd_groove_data_hdr_szc( hdr );
     811             : 
     812       32169 :   TEST( szc==exp_szc );
     813             : 
     814       32169 :   ulong obj_cnt       = (ulong)fd_groove_data_szc_cfg[ szc ].obj_cnt;
     815       32169 :   ulong obj_footprint = (ulong)fd_groove_data_szc_cfg[ szc ].obj_footprint;
     816       32169 :   ulong parent_szc    = (ulong)fd_groove_data_szc_cfg[ szc ].parent_szc;
     817             : 
     818       32169 :   ulong parent_obj_idx       = fd_groove_data_hdr_idx( hdr );
     819       32169 :   ulong parent_obj_cnt       = (parent_szc<FD_GROOVE_DATA_SZC_CNT) ? (ulong)fd_groove_data_szc_cfg[ parent_szc ].obj_cnt : 1UL;
     820             : //ulong parent_obj_footprint = (parent_szc<FD_GROOVE_DATA_SZC_CNT) ? (ulong)fd_groove_data_szc_cfg[ parent_szc ].obj_footprint :
     821             : //                             (FD_GROOVE_VOLUME_DATA_MAX - FD_GROOVE_BLOCK_FOOTPRINT);
     822             : 
     823       32169 :   TEST( parent_obj_idx < parent_obj_cnt );
     824             : 
     825       32169 :   TEST( fd_groove_data_hdr_align( hdr )==FD_GROOVE_DATA_HDR_ALIGN );
     826       32169 :   TEST( fd_groove_data_hdr_sz   ( hdr )==
     827       32169 :         fd_ulong_min( FD_GROOVE_BLOCK_FOOTPRINT - FD_GROOVE_DATA_HDR_FOOTPRINT + obj_cnt*obj_footprint, (1UL<<25)-1UL ) );
     828             : 
     829       32169 :   ulong free_objs = *(ulong const *)(_superblock_hdr+1);
     830             : 
     831       32169 :   TEST( !fd_ulong_shift_right( free_objs, (int)obj_cnt ) ); /* valid free obj bit field */
     832       32169 :   if( in_circulation ) TEST( !!free_objs ); /* at least 1 free obj for superblocks in circulation */
     833             : 
     834             :   /* Verify superblock object headers */
     835             : 
     836       32169 :   ulong rem_objs = free_objs ^ fd_ulong_mask_lsb( (int)obj_cnt );
     837       76578 :   while( rem_objs ) {
     838       44409 :     ulong _idx = (ulong)fd_ulong_find_lsb( rem_objs );
     839             : 
     840       44409 :     ulong child_obj_off = superblock_off + FD_GROOVE_BLOCK_FOOTPRINT + _idx*obj_footprint;
     841             : 
     842       44409 :     fd_groove_data_hdr_t obj_hdr = *(fd_groove_data_hdr_t const *)((ulong)_volume0 + child_obj_off);
     843             : 
     844       44409 :     ulong obj_type = fd_groove_data_hdr_type( obj_hdr );
     845       44409 :     ulong obj_idx  = fd_groove_data_hdr_idx ( obj_hdr );
     846       44409 :     ulong obj_szc  = fd_groove_data_hdr_szc ( obj_hdr );
     847             : 
     848       44409 :     TEST( (obj_type==FD_GROOVE_DATA_HDR_TYPE_ALLOC) | (obj_type==FD_GROOVE_DATA_HDR_TYPE_SUPERBLOCK) );
     849       44409 :     TEST( obj_idx==_idx                   );
     850       44409 :     TEST( obj_szc< FD_GROOVE_DATA_SZC_CNT );
     851             : 
     852       44409 :     TEST( fd_ulong_if( obj_type==FD_GROOVE_DATA_HDR_TYPE_ALLOC,
     853       44409 :                        obj_szc, (ulong)fd_groove_data_szc_cfg[ obj_szc ].parent_szc )==szc );
     854             : 
     855       44409 :     ulong req_align = fd_groove_data_hdr_align( obj_hdr );
     856       44409 :     ulong req_sz    = fd_groove_data_hdr_sz   ( obj_hdr );
     857             :   //ulong req_info  = fd_groove_data_hdr_info ( obj_hdr );
     858             : 
     859       44409 :     ulong req_footprint =
     860       44409 :       fd_ulong_align_up( fd_ulong_align_up( FD_GROOVE_DATA_HDR_FOOTPRINT, req_align ) + req_sz, FD_GROOVE_BLOCK_ALIGN );
     861             : 
     862       44409 :     TEST( fd_ulong_is_pow2( req_align )        );
     863       44409 :     TEST( req_align    <=FD_GROOVE_BLOCK_ALIGN );
     864       44409 :     TEST( req_footprint<=obj_footprint         );
     865             : 
     866             :     /* Note that recursion depth is bounded due to the finite number of
     867             :        sizeclasses and the validation above the children are respecting
     868             :        the szc hierarchy above. */
     869             : 
     870       44409 :     if( (!!verify_descendents) & (obj_type==FD_GROOVE_DATA_HDR_TYPE_SUPERBLOCK) ) /* verify superblock descendents */
     871       23910 :       TEST( !fd_groove_data_private_verify_superblock( child_obj_off, obj_szc, 0 /* don't know if in circulation */,
     872       44409 :                                                        verify_descendents, _volume0, _volume1 ) );
     873             : 
     874       44409 :     rem_objs = fd_ulong_pop_lsb( rem_objs );
     875       44409 :   }
     876             : 
     877       32169 :   return FD_GROOVE_SUCCESS;
     878       32169 : }
     879             : 
     880             : int
     881          15 : fd_groove_data_verify( fd_groove_data_t const * data ) {
     882             : 
     883             :   /* Verify join */
     884             : 
     885          15 :   TEST( data );
     886          15 :   TEST( fd_ulong_is_aligned( (ulong)data, alignof(fd_groove_data_t) ) );
     887             : 
     888          15 :   fd_groove_volume_pool_t const * pool           = data->volume_pool;
     889          15 :   ulong                   const * active_slot    = data->active_slot;
     890          15 :   ulong                   const * inactive_stack = data->inactive_stack;
     891             :   /* cgroup_hint is arbitrary */
     892             : 
     893             :   /* Verify volume pool */
     894             : 
     895          15 :   TEST( !fd_groove_volume_pool_verify( pool ) );
     896             : 
     897          15 :   fd_groove_volume_pool_shmem_t const * shpool = (fd_groove_volume_pool_shmem_t const *)fd_groove_volume_pool_shpool_const( pool );
     898             : 
     899          15 :   fd_groove_volume_t const * _volume0   = (fd_groove_volume_t const *)fd_groove_volume_pool_shele_const ( pool );
     900          15 :   ulong                      volume_max =                             fd_groove_volume_pool_ele_max     ( pool );
     901          15 :   fd_groove_volume_t const * _volume1   = _volume0 + volume_max;
     902             : 
     903          15 :   TEST( (!!_volume0) | (!volume_max) );
     904          15 :   TEST( _volume0<=_volume1 );
     905          15 :   TEST( fd_ulong_is_aligned( (ulong)_volume0, FD_GROOVE_VOLUME_ALIGN ) );
     906             : 
     907          15 :   ulong volume_idx = fd_groove_volume_pool_private_vidx_idx( shpool->ver_top );
     908          15 :   while( volume_idx<volume_max ) { /* note: cyclic check already done by volume_pool_verify above */
     909           0 :     TEST( _volume0[ volume_idx ].magic==~FD_GROOVE_VOLUME_MAGIC );
     910           0 :     TEST( _volume0[ volume_idx ].idx  ==volume_idx              );
     911           0 :     volume_idx = fd_groove_volume_pool_private_idx( _volume0[ volume_idx ].next );
     912           0 :   }
     913             : 
     914             :   /* Verify data shmem */
     915             : 
     916          15 :   fd_groove_data_shmem_t const * shdata = (fd_groove_data_shmem_t const *)fd_groove_data_shdata_const( data );
     917             : 
     918          15 :   TEST( fd_ulong_is_aligned( (ulong)shdata, fd_groove_data_align() ) );
     919             : 
     920          15 :   TEST( shdata->magic         ==FD_GROOVE_DATA_MAGIC );
     921          15 :   TEST( shdata->volume_pool   ==shpool               );
     922          15 :   TEST( shdata->active_slot   ==active_slot          );
     923          15 :   TEST( shdata->inactive_stack==inactive_stack       );
     924             : 
     925             :   /* Verify sizeclass configuration */
     926             : 
     927         495 :   for( ulong szc_idx=0UL; szc_idx<FD_GROOVE_DATA_SZC_CNT; szc_idx++ ) {
     928         480 :     ulong obj_cnt       = (ulong)fd_groove_data_szc_cfg[ szc_idx ].obj_cnt;
     929         480 :     ulong obj_footprint = (ulong)fd_groove_data_szc_cfg[ szc_idx ].obj_footprint;
     930         480 :     ulong cgroup_mask   = (ulong)fd_groove_data_szc_cfg[ szc_idx ].cgroup_mask;
     931         480 :     ulong parent_szc    = (ulong)fd_groove_data_szc_cfg[ szc_idx ].parent_szc;
     932             : 
     933         480 :     ulong cgroup_cnt = cgroup_mask + 1UL;
     934             : 
     935         480 :     ulong superblock_footprint = FD_GROOVE_BLOCK_FOOTPRINT + obj_cnt*obj_footprint;
     936             : 
     937         480 :     ulong parent_obj_footprint = (parent_szc<FD_GROOVE_DATA_SZC_CNT) ?
     938         465 :       (ulong)fd_groove_data_szc_cfg[ parent_szc ].obj_footprint : (FD_GROOVE_VOLUME_DATA_MAX - FD_GROOVE_BLOCK_FOOTPRINT);
     939             : 
     940         480 :     TEST( (1UL<=obj_cnt) & (obj_cnt<=64UL)                                );
     941         480 :     TEST( fd_ulong_is_aligned( obj_footprint, FD_GROOVE_BLOCK_FOOTPRINT ) );
     942         480 :     TEST( fd_ulong_is_pow2( cgroup_cnt )                                  );
     943         480 :     TEST( parent_szc<=FD_GROOVE_DATA_SZC_CNT                              );
     944         480 :     TEST( superblock_footprint <= parent_obj_footprint                    );
     945         480 :   }
     946             : 
     947             :   /* Verify all active superblocks */
     948             : 
     949         495 :   for( ulong szc_idx=0UL; szc_idx<FD_GROOVE_DATA_SZC_CNT; szc_idx++ ) {
     950         480 :     ulong cgroup_cnt = (ulong)fd_groove_data_szc_cfg[ szc_idx ].cgroup_mask + 1UL;
     951       12165 :     for( ulong cgroup_idx=0UL; cgroup_idx<cgroup_cnt; cgroup_idx++ ) {
     952       11685 :       ulong superblock_off = active_slot[ szc_idx + FD_GROOVE_DATA_SZC_CNT*cgroup_idx ];
     953       11685 :       if( !superblock_off ) continue;
     954             : 
     955         159 :       TEST( !fd_groove_data_private_verify_superblock( superblock_off, szc_idx, 1 /* is in circulation */,
     956         159 :                                                        0 /* don't verify children */, _volume0, _volume1 ) );
     957         159 :       fd_groove_data_hdr_t hdr = *(fd_groove_data_hdr_t const *)(((ulong)_volume0) + superblock_off);
     958         159 :       TEST( fd_groove_data_hdr_szc( hdr )==szc_idx );
     959         159 :     }
     960         480 :   }
     961             : 
     962             :   /* Verify all inactive superblocks for sizeclass szc_idx */
     963             : 
     964         495 :   for( ulong szc_idx=0UL; szc_idx<FD_GROOVE_DATA_SZC_CNT; szc_idx++ ) {
     965         480 :     ulong superblock_off = inactive_stack[ szc_idx ] & ~(FD_GROOVE_BLOCK_FOOTPRINT-1UL);
     966         480 :     ulong rem            = volume_max*FD_GROOVE_VOLUME_FOOTPRINT / (2UL*FD_GROOVE_BLOCK_FOOTPRINT); /* FIXME: tighter bound? */
     967        8574 :     while( superblock_off ) {
     968        8094 :       FD_TEST( rem ); rem--; /* avoid cycles */
     969             : 
     970        8094 :       TEST( !fd_groove_data_private_verify_superblock( superblock_off, szc_idx, 1 /* is in circulation */,
     971        8094 :                                                        0 /* don't verify children */, _volume0, _volume1 ) );
     972        8094 :       fd_groove_data_hdr_t hdr = *(fd_groove_data_hdr_t const *)(((ulong)_volume0) + superblock_off);
     973        8094 :       TEST( fd_groove_data_hdr_szc( hdr )==szc_idx );
     974             : 
     975        8094 :       superblock_off = fd_groove_data_hdr_info( hdr );
     976        8094 :     }
     977         480 :   }
     978             : 
     979          15 :   return FD_GROOVE_SUCCESS;
     980          15 : }
     981             : 
     982             : int
     983             : fd_groove_data_volume_verify( fd_groove_data_t   const * data,
     984           6 :                               fd_groove_volume_t const * _volume ) {
     985             : 
     986           6 :   TEST( data );
     987             : 
     988           6 :   fd_groove_volume_t const * _volume0 = (fd_groove_volume_t const *)fd_groove_data_volume0_const( data );
     989           6 :   fd_groove_volume_t const * _volume1 = (fd_groove_volume_t const *)fd_groove_data_volume1_const( data );
     990             : 
     991           6 :   ulong volume_off = (ulong)_volume - (ulong)_volume0;
     992             : 
     993           6 :   TEST( (_volume0<=_volume) & (_volume<_volume1)                      );
     994           6 :   TEST( fd_ulong_is_aligned( volume_off, FD_GROOVE_VOLUME_FOOTPRINT ) );
     995             : 
     996           6 :   ulong magic   = _volume->magic;
     997           6 :   ulong idx     = _volume->idx;
     998           6 :   ulong info_sz = _volume->info_sz;
     999             : 
    1000           6 :   TEST( (magic==FD_GROOVE_VOLUME_MAGIC) | (magic==~FD_GROOVE_VOLUME_MAGIC) );
    1001           6 :   TEST( idx*FD_GROOVE_VOLUME_FOOTPRINT==volume_off                         );
    1002           6 :   TEST( info_sz                       <=FD_GROOVE_VOLUME_INFO_MAX          );
    1003             : 
    1004           6 :   if( magic==FD_GROOVE_VOLUME_MAGIC ) {
    1005           6 :     ulong superblock_off = volume_off + FD_GROOVE_BLOCK_FOOTPRINT;
    1006           6 :     ulong superblock_szc = FD_GROOVE_DATA_SZC_CNT-1UL;
    1007           6 :     TEST( !fd_groove_data_private_verify_superblock( superblock_off, superblock_szc, 0 /* don't know if in circulation */,
    1008           6 :                                                      1 /* verify children */, _volume0, _volume1 ) );
    1009           6 :   }
    1010             : 
    1011           6 :   return FD_GROOVE_SUCCESS;
    1012           6 : }
    1013             : 
    1014             : #undef TEST

Generated by: LCOV version 1.14