LCOV - code coverage report
Current view: top level - groove - fd_groove_data.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 475 566 83.9 %
Date: 2026-06-29 05:51:35 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 :         fd_groove_volume_t * _volume = fd_groove_volume_pool_acquire( data->volume_pool );
     327           6 :         if( FD_UNLIKELY( !_volume ) ) return FD_GROOVE_ERR_FULL;
     328             : 
     329           3 : #       if FD_GROOVE_PARANOID
     330           3 :         ulong volume_off = (ulong)_volume - (ulong)_volume0;
     331             : 
     332           3 :         if( FD_UNLIKELY( !( (_volume0<=_volume) & (_volume<_volume1) &
     333           3 :                             fd_ulong_is_aligned( volume_off, FD_GROOVE_VOLUME_FOOTPRINT ) ) ) ) {
     334           0 :           FD_LOG_WARNING(( "volume not at a valid groove data local address" ));
     335           0 :           return FD_GROOVE_ERR_CORRUPT;
     336           0 :         }
     337             : 
     338           3 :         if( FD_UNLIKELY( !( (_volume->magic                         ==~FD_GROOVE_VOLUME_MAGIC  ) &
     339           3 :                             (_volume->idx*FD_GROOVE_VOLUME_FOOTPRINT==volume_off               ) &
     340           3 :                             (_volume->info_sz                       <=FD_GROOVE_VOLUME_INFO_MAX) ) ) ) {
     341           0 :           FD_LOG_WARNING(( "unexpected volume header" ));
     342           0 :           return FD_GROOVE_ERR_CORRUPT;
     343           0 :         }
     344           3 : #       endif
     345             : 
     346           3 :         FD_COMPILER_MFENCE();
     347           3 :         _volume->magic = FD_GROOVE_VOLUME_MAGIC; /* mark volume as potentially containing groove data allocations */
     348           3 :         FD_COMPILER_MFENCE();
     349             : 
     350             :       //parent_idx     = 0UL; /* See note above about initialization */
     351           3 :         superblock_off = (ulong)_volume->data - (ulong)_volume0;
     352             : 
     353       14370 :       } else { /* Acquire a parent_szc object to use for the new superblock */
     354             : 
     355       14370 :         int err = fd_groove_data_private_alloc_obj( data, parent_szc, &superblock_off, &parent_idx ); /* logs details */
     356       14370 :         if( FD_UNLIKELY( err ) ) return err;
     357             : 
     358       14370 :       }
     359             : 
     360       14340 :       ulong superblock_align = FD_GROOVE_DATA_HDR_ALIGN;
     361       14340 :       ulong superblock_sz    = FD_GROOVE_BLOCK_FOOTPRINT - FD_GROOVE_DATA_HDR_FOOTPRINT + obj_footprint*obj_cnt;
     362             : 
     363       14340 :       fd_groove_data_hdr_t * _superblock_hdr = (fd_groove_data_hdr_t *)(((ulong)_volume0) + superblock_off);
     364             : 
     365       14340 : #     if FD_GROOVE_PARANOID
     366       14340 :       if( FD_UNLIKELY( !( ((ulong)_volume0<(ulong)_superblock_hdr) & ((ulong)_superblock_hdr<(ulong)_volume1) &
     367       14340 :                           fd_ulong_is_aligned( (ulong)_superblock_hdr, FD_GROOVE_BLOCK_ALIGN ) ) ) ) {
     368           0 :         FD_LOG_WARNING(( "superblock not at a valid groove data local address" ));
     369           0 :         return FD_GROOVE_ERR_CORRUPT;
     370           0 :       }
     371       14340 : #     endif
     372             : 
     373       14340 :       *_superblock_hdr = fd_groove_data_hdr( FD_GROOVE_DATA_HDR_TYPE_SUPERBLOCK, parent_idx, obj_szc,
     374       14340 :                                              superblock_align, fd_ulong_min( superblock_sz, (1UL<<25)-1UL ), 0UL /* no next */ );
     375       14340 :       *(ulong *)(_superblock_hdr+1) = (1UL<<obj_cnt)-1UL; /* mark all objects in superblock as free */
     376       14340 :     }
     377      996180 :   }
     378             : 
     379             :   /* At this point, we have exclusive access to the superblock, there is
     380             :      at least one free block in it and only we can allocate blocks from
     381             :      it.  (Other threads could free blocks to it concurrently though.)
     382             :      Allocate a free block.  If there were more free blocks, put the
     383             :      superblock back into circulation as the active superblock for our
     384             :      cgroup.  Otherwise, free will put in back into circulation when the
     385             :      application frees a block in it.  See fd_alloc.c for details. */
     386             : 
     387     2121639 :   fd_groove_data_hdr_t * _superblock_hdr = (fd_groove_data_hdr_t *)(((ulong)_volume0) + superblock_off);
     388             : 
     389     2121639 :   ulong volatile * _free_objs = (ulong volatile *)(_superblock_hdr+1);
     390             : 
     391     2121639 : # if FD_GROOVE_PARANOID
     392     2121639 :   if( FD_UNLIKELY( !( ((ulong)_volume0<(ulong)_superblock_hdr) & ((ulong)_superblock_hdr<(ulong)_volume1) &
     393     2121639 :                       fd_ulong_is_aligned( (ulong)_superblock_hdr, FD_GROOVE_BLOCK_ALIGN ) ) ) ) {
     394           0 :     FD_LOG_WARNING(( "superblock not at a valid groove data local address" ));
     395           0 :     return FD_GROOVE_ERR_CORRUPT;
     396           0 :   }
     397             : 
     398     2121639 :   fd_groove_data_hdr_t superblock_hdr = *_superblock_hdr;
     399             : 
     400     2121639 :   ulong superblock_type = fd_groove_data_hdr_type( superblock_hdr );
     401     2121639 :   ulong superblock_szc  = fd_groove_data_hdr_szc ( superblock_hdr );
     402             : 
     403     2121639 :   if( FD_UNLIKELY( !((superblock_type==FD_GROOVE_DATA_HDR_TYPE_SUPERBLOCK) & (superblock_szc==obj_szc)) ) ) {
     404           0 :     FD_LOG_WARNING(( "unexpected superblock header" ));
     405           0 :     return FD_GROOVE_ERR_CORRUPT;
     406           0 :   }
     407     2121639 : # endif
     408             : 
     409     2121639 :   FD_COMPILER_MFENCE();
     410     2121639 :   ulong free_objs = *_free_objs;
     411     2121639 :   FD_COMPILER_MFENCE();
     412             : 
     413     2121639 : # if FD_GROOVE_PARANOID
     414     2121639 :   if( FD_UNLIKELY( (!free_objs) | (!!fd_ulong_shift_right( free_objs, (int)obj_cnt )) ) ) {
     415           0 :     FD_LOG_WARNING(( "%s", (!free_objs) ? "full superblock in circulation" : "invalid free_objs bit field" ));
     416           0 :     return FD_GROOVE_ERR_CORRUPT;
     417           0 :   }
     418     2121639 : # endif
     419             : 
     420     2121639 :   ulong obj = fd_ulong_lsb( free_objs );
     421             : 
     422     2121639 :   FD_COMPILER_MFENCE();
     423     2121639 : # if FD_HAS_ATOMIC
     424     2121639 :   free_objs = FD_ATOMIC_FETCH_AND_SUB( _free_objs, obj ); /* Marginally better asm than FETCH_AND_AND */
     425             : # else
     426             :   free_objs   = *_free_objs;
     427             :   *_free_objs = free_objs & ~obj;
     428             : # endif
     429     2121639 :   FD_COMPILER_MFENCE();
     430             : 
     431     2121639 : # if FD_GROOVE_PARANOID
     432     2121639 :   if( FD_UNLIKELY( (!free_objs) | (!!fd_ulong_shift_right( free_objs, (int)obj_cnt )) ) ) {
     433           0 :     FD_LOG_WARNING(( "%s", (!free_objs) ? "full superblock in circulation" : "invalid free_objs bit field" ));
     434           0 :     return FD_GROOVE_ERR_CORRUPT;
     435           0 :   }
     436     2121639 : # endif
     437             : 
     438     2121639 :   if( FD_LIKELY( free_objs!=obj ) ) {
     439      218265 :     ulong displaced_superblock_off = fd_groove_data_private_active_displace( _active_slot, _volume0, superblock_off );
     440      218265 :     if( FD_UNLIKELY( displaced_superblock_off ) )
     441           0 :       fd_groove_data_private_inactive_push( _inactive_stack, _volume0, displaced_superblock_off );
     442      218265 :   }
     443             : 
     444             :   /* At this point, we've allocated the object */
     445             : 
     446     2121639 :   ulong obj_idx = (ulong)fd_ulong_find_lsb( obj );
     447             : 
     448     2121639 :   *_obj_off = superblock_off + FD_GROOVE_BLOCK_FOOTPRINT + obj_idx*obj_footprint;
     449     2121639 :   *_obj_idx = obj_idx;
     450     2121639 :   return FD_GROOVE_SUCCESS;
     451     2121639 : }
     452             : 
     453             : void *
     454             : fd_groove_data_alloc( fd_groove_data_t * data,
     455             :                       ulong              align,
     456             :                       ulong              sz,
     457             :                       ulong              tag,
     458     2107329 :                       int *              _err ) {
     459             : 
     460     2107329 :   int stack_err[1];
     461     2107329 :   if( !_err ) _err = stack_err;
     462             : 
     463             :   /* Check input args */
     464             : 
     465     2107329 :   if( FD_UNLIKELY( !data ) ) {
     466           6 :     FD_LOG_WARNING(( "NULL data" ));
     467           6 :     *_err = FD_GROOVE_ERR_INVAL;
     468           6 :     return NULL;
     469           6 :   }
     470             : 
     471     2107323 :   align = fd_ulong_if( !!align, align, FD_GROOVE_DATA_ALLOC_ALIGN_DEFAULT );
     472     2107323 :   if( FD_UNLIKELY( !(fd_ulong_is_pow2( align ) & (align<=FD_GROOVE_DATA_ALLOC_ALIGN_MAX)) ) ) {
     473          12 :     FD_LOG_WARNING(( "bad align" ));
     474          12 :     *_err = FD_GROOVE_ERR_INVAL;
     475          12 :     return NULL;
     476          12 :   }
     477             : 
     478     2107311 :   ulong off_obj   = fd_ulong_align_up( FD_GROOVE_DATA_HDR_FOOTPRINT, align );
     479     2107311 :   ulong footprint = fd_ulong_align_up( off_obj+sz, FD_GROOVE_BLOCK_ALIGN );
     480             : 
     481     2107311 :   if( FD_UNLIKELY( !((sz<footprint) & (footprint<=FD_GROOVE_DATA_ALLOC_FOOTPRINT_MAX)) ) ) {
     482           6 :     FD_LOG_WARNING(( "bad sz/align" ));
     483           6 :     *_err = FD_GROOVE_ERR_INVAL;
     484           6 :     return NULL;
     485           6 :   }
     486             : 
     487             :   /* Acquire an object from the tightest suitable sizeclass */
     488             : 
     489     2107305 :   ulong obj_szc = fd_groove_data_szc( footprint );
     490             : 
     491     2107305 :   ulong obj_off;
     492     2107305 :   ulong obj_idx;
     493     2107305 :   int   err = fd_groove_data_private_alloc_obj( data, obj_szc, &obj_off, &obj_idx ); /* logs details */
     494     2107305 :   if( FD_UNLIKELY( err ) ) {
     495           3 :     *_err = err;
     496           3 :     return NULL;
     497           3 :   }
     498             : 
     499             :   /* Carve an allocation into it. */
     500             : 
     501     2107302 :   fd_groove_data_hdr_t * obj_hdr = (fd_groove_data_hdr_t *)((ulong)fd_groove_volume_pool_shele( data->volume_pool ) + obj_off);
     502             : 
     503     2107302 :   *obj_hdr = fd_groove_data_hdr( FD_GROOVE_DATA_HDR_TYPE_ALLOC, obj_idx, obj_szc, align, sz, tag );
     504             : 
     505     2107302 :   *_err = FD_GROOVE_SUCCESS;
     506     2107302 :   return (void *)((ulong)obj_hdr + off_obj);
     507     2107305 : }
     508             : 
     509             : int
     510             : fd_groove_data_private_free( fd_groove_data_t * data,
     511             :                              void *             _obj,
     512     2109834 :                              ulong              exp_type ) {
     513             : 
     514             : # if !FD_GROOVE_PARANOID
     515             :   (void)exp_type; /* Suppress unused warning if running without paranoia */
     516             : # endif
     517             : 
     518     2109834 :   if( FD_UNLIKELY( !data ) ) {
     519           3 :     FD_LOG_WARNING(( "NULL data" ));
     520           3 :     return FD_GROOVE_ERR_INVAL;
     521           3 :   }
     522             : 
     523     2109831 :   if( FD_UNLIKELY( !_obj ) ) return FD_GROOVE_ERR_INVAL;
     524             : 
     525     2109828 :   fd_groove_data_hdr_t * _obj_hdr = fd_groove_data_object_hdr( _obj );
     526             : 
     527     2109828 :   fd_groove_volume_t * _volume0 = (fd_groove_volume_t *)fd_groove_data_volume0( data );
     528             : 
     529     2109828 : # if FD_GROOVE_PARANOID
     530     2109828 :   fd_groove_volume_t * _volume1 = (fd_groove_volume_t *)fd_groove_data_volume1( data );
     531     2109828 :   if( FD_UNLIKELY( !( ((ulong)_volume0<=(ulong)_obj_hdr                             ) &
     532     2109828 :                       ((ulong)_obj_hdr< (ulong)_volume1                             ) &
     533     2109828 :                       (fd_ulong_is_aligned( (ulong)_obj_hdr, FD_GROOVE_BLOCK_ALIGN )) ) ) ) {
     534           0 :     FD_LOG_WARNING(( "object not at a valid groove data local address" ));
     535           0 :     return FD_GROOVE_ERR_INVAL;
     536           0 :   }
     537     2109828 : # endif
     538             : 
     539     2109828 :   fd_groove_data_hdr_t obj_hdr = *_obj_hdr;
     540             : 
     541     2109828 :   ulong obj_type = fd_groove_data_hdr_type( obj_hdr );
     542     2109828 :   ulong obj_idx  = fd_groove_data_hdr_idx ( obj_hdr );
     543     2109828 :   ulong obj_szc  = fd_groove_data_hdr_szc ( obj_hdr );
     544             : 
     545     2109828 : #if FD_GROOVE_PARANOID
     546     2109828 :   if( FD_UNLIKELY( !((obj_type==exp_type) & (obj_szc<FD_GROOVE_DATA_SZC_CNT)) ) ) {
     547           0 :     FD_LOG_WARNING(( "object does not appear to be a groove data %s",
     548           0 :                      exp_type==FD_GROOVE_DATA_HDR_TYPE_ALLOC ? "alloc" : "superblock" ));
     549           0 :     return FD_GROOVE_ERR_INVAL;
     550           0 :   }
     551     2109828 : # endif
     552             : 
     553     2109828 :   obj_szc = fd_ulong_if( obj_type==FD_GROOVE_DATA_HDR_TYPE_ALLOC, obj_szc,
     554     2109828 :                         (ulong)fd_groove_data_szc_cfg[ obj_szc ].parent_szc );
     555     2109828 :   ulong obj_cnt       = (ulong)fd_groove_data_szc_cfg[ obj_szc ].obj_cnt;
     556     2109828 : # if FD_GROOVE_PARANOID
     557     2109828 :   ulong obj_footprint = (ulong)fd_groove_data_szc_cfg[ obj_szc ].obj_footprint;
     558             : 
     559     2109828 :   ulong req_align    = fd_groove_data_hdr_align( obj_hdr );
     560     2109828 :   ulong req_sz       = fd_groove_data_hdr_sz   ( obj_hdr );
     561     2109828 :   ulong req_footprint =
     562     2109828 :     fd_ulong_align_up( fd_ulong_align_up( FD_GROOVE_DATA_HDR_FOOTPRINT, req_align ) + req_sz, FD_GROOVE_BLOCK_ALIGN );
     563             : 
     564     2109828 :   if( FD_UNLIKELY( !( (obj_idx<obj_cnt                 ) &
     565     2109828 :                       (fd_ulong_is_pow2( req_align )   ) &
     566     2109828 :                       (req_align<=FD_GROOVE_BLOCK_ALIGN) &
     567     2109828 :                       (req_footprint<=obj_footprint    ) ) ) ) {
     568           0 :     FD_LOG_WARNING(( "object does not appear to be a groove data %s",
     569           0 :                      exp_type==FD_GROOVE_DATA_HDR_TYPE_ALLOC ? "alloc" : "superblock" ));
     570           0 :     return FD_GROOVE_ERR_INVAL;
     571           0 :   }
     572     2109828 : # endif
     573             : 
     574             :   /* At this point, we appear to have a valid allocated object.  Mark
     575             :      the object as not valid and then free it.
     576             : 
     577             :      Note: marking the object as dead is optional.  It is mostly a hint
     578             :      for diagnostics and for handholding as groove data users shouldn't
     579             :      be calling free on it again and alloc doesn't care about the state
     580             :      unallocated object memory.  Most useful, marking the object as dead
     581             :      can detect double free scenarios.
     582             : 
     583             :      The below implementation is not robust against _concurrent_ double
     584             :      frees.  Would probably have to use something like ATOMIC_CAS
     585             :      semantics on the object header bits to insure nobody freed the
     586             :      object behind our back.  And since the free bit field update isn't
     587             :      atomic with marking the object as dead, would probably need further
     588             :      to do something like mark the object as freeing then update bit
     589             :      field and then mark object as dead, etc. */
     590             : 
     591     2109828 :   fd_groove_data_hdr_t * _superblock_hdr = fd_groove_data_superblock_hdr( _obj, obj_szc, obj_idx );
     592             : 
     593     2109828 :   ulong volatile * _free_objs = (ulong volatile *)(_superblock_hdr+1);
     594             : 
     595     2109828 :   ulong free_objs;
     596             : 
     597     2109828 :   ulong obj = 1UL << obj_idx;
     598             : 
     599     2109828 : # if FD_GROOVE_PARANOID
     600     2109828 :   if( FD_UNLIKELY( !( ((ulong)_volume0<(ulong)_superblock_hdr) & ((ulong)_superblock_hdr<(ulong)_obj_hdr) &
     601     2109828 :                       fd_ulong_is_aligned( (ulong)_superblock_hdr, FD_GROOVE_BLOCK_ALIGN ) ) ) ) {
     602           0 :     FD_LOG_WARNING(( "superblock not at a valid groove data local address" ));
     603           0 :     return FD_GROOVE_ERR_INVAL;
     604           0 :   }
     605             : 
     606     2109828 :   fd_groove_data_hdr_t superblock_hdr = *_superblock_hdr;
     607             : 
     608     2109828 :   ulong superblock_type = fd_groove_data_hdr_type( superblock_hdr );
     609     2109828 :   ulong superblock_szc  = fd_groove_data_hdr_szc ( superblock_hdr );
     610             : 
     611     2109828 :   if( FD_UNLIKELY( !((superblock_type==FD_GROOVE_DATA_HDR_TYPE_SUPERBLOCK) & (superblock_szc==obj_szc)) ) ) {
     612           0 :     FD_LOG_WARNING(( "unexpected superblock header" ));
     613           0 :     return FD_GROOVE_ERR_INVAL;
     614           0 :   }
     615             : 
     616     2109828 :   FD_COMPILER_MFENCE();
     617     2109828 :   free_objs = *_free_objs;
     618     2109828 :   FD_COMPILER_MFENCE();
     619             : 
     620     2109828 :   if( FD_UNLIKELY( (free_objs & obj) | fd_ulong_shift_right( free_objs, (int)obj_cnt ) ) ) {
     621           0 :     FD_LOG_WARNING(( "%s", (free_objs & obj) ? "possible concurrent double free" : "invalid free_objs bit field" ));
     622           0 :     return FD_GROOVE_ERR_INVAL;
     623           0 :   }
     624             : 
     625     2109828 :   FD_COMPILER_MFENCE();
     626     2109828 :   _obj_hdr->bits = 0UL; /* sets object type to an invalid value */
     627     2109828 :   FD_COMPILER_MFENCE();
     628     2109828 : # endif
     629             : 
     630     2109828 :   FD_COMPILER_MFENCE();
     631     2109828 : # if FD_HAS_ATOMIC
     632     2109828 :   free_objs = FD_ATOMIC_FETCH_AND_ADD( _free_objs, obj ); /* Marginally better asm than FETCH_AND_OR */
     633             : # else
     634             :   free_objs   = *_free_objs;
     635             :   *_free_objs = free_objs | obj;
     636             : # endif
     637     2109828 :   FD_COMPILER_MFENCE();
     638             : 
     639     2109828 : # if FD_GROOVE_PARANOID
     640     2109828 :   if( FD_UNLIKELY( (free_objs & obj) | fd_ulong_shift_right( free_objs, (int)obj_cnt ) ) ) {
     641           0 :     FD_LOG_WARNING(( "%s", (free_objs & obj) ? "possible concurrent double free" : "invalid free_objs bit field" ));
     642           0 :     return FD_GROOVE_ERR_CORRUPT;
     643           0 :   }
     644     2109828 : # endif
     645             : 
     646             :   /* At this point, we've freed the object.  We might need to get the
     647             :      object's superblock back into circulation and/or free up excess
     648             :      empty superblocks for this sizeclass. */
     649             : 
     650     2109828 :   ulong free_cnt = (ulong)fd_ulong_popcnt( free_objs );
     651             : 
     652     2109828 :   if( FD_UNLIKELY( !free_cnt ) ) {
     653             : 
     654             :     /* At this point, the superblock was full before we freed it and
     655             :        thus not in circulation for use by fd_alloc.  We need to get the
     656             :        superblock back into circulation.  There are options for this
     657             :        with various subtle tradeoffs.  See fd_alloc.c for details.
     658             :        (FIXME: amongst then, consider using allocation cgroup instead of
     659             :        the groove data instance's cgroup?) */
     660             : 
     661     1898571 :     ulong cgroup = fd_groove_data_cgroup_hint( data ) & (ulong)fd_groove_data_szc_cfg[ obj_szc ].cgroup_mask;
     662             : 
     663     1898571 :     ulong displaced_superblock_off =
     664     1898571 :       fd_groove_data_private_active_displace( data->active_slot + obj_szc + FD_GROOVE_DATA_SZC_CNT*cgroup,
     665     1898571 :                                               _volume0, (ulong)_superblock_hdr - (ulong)_volume0 );
     666             : 
     667     1898571 :     if( FD_UNLIKELY( displaced_superblock_off ) )
     668      991227 :       fd_groove_data_private_inactive_push( data->inactive_stack + obj_szc, _volume0, displaced_superblock_off );
     669             : 
     670     1898571 :   } else if( FD_UNLIKELY( (free_cnt+1UL)==obj_cnt ) ) {
     671             : 
     672             :     /* At this point, the superblock was completely empty after we freed
     673             :        from it, hence it is still in circulation.  If there is also a
     674             :        completely empty superblock on top of the inactive stack, we free
     675             :        that one for general reuse.  This is more subtle than it looks,
     676             :        see fd_alloc.c for details. */
     677             : 
     678       32361 :     ulong volatile * _inactive_stack = data->inactive_stack + obj_szc;
     679             : 
     680       32361 :     ulong superblock_off = fd_groove_data_private_inactive_pop( _inactive_stack, _volume0 );
     681             : 
     682       32361 :     if( FD_LIKELY( superblock_off ) ) {
     683             : 
     684       31788 :       _superblock_hdr = (fd_groove_data_hdr_t *)((ulong)_volume0 + superblock_off);
     685             : 
     686       31788 :       _free_objs = (ulong volatile *)(_superblock_hdr+1);
     687             : 
     688       31788 : #     if FD_GROOVE_PARANOID
     689       31788 :       if( FD_UNLIKELY( !( ((ulong)_volume0<(ulong)_superblock_hdr) & ((ulong)_superblock_hdr<(ulong)_volume1) &
     690       31788 :                           fd_ulong_is_aligned( (ulong)_superblock_hdr, FD_GROOVE_BLOCK_ALIGN ) ) ) ) {
     691           0 :         FD_LOG_WARNING(( "superblock not at a valid groove data local address" ));
     692           0 :         return FD_GROOVE_ERR_CORRUPT;
     693           0 :       }
     694             : 
     695       31788 :       superblock_type = fd_groove_data_hdr_type( superblock_hdr );
     696       31788 :       superblock_szc  = fd_groove_data_hdr_szc ( superblock_hdr );
     697             : 
     698       31788 :       if( FD_UNLIKELY( !((superblock_type==FD_GROOVE_DATA_HDR_TYPE_SUPERBLOCK) & (superblock_szc==obj_szc)) ) ) {
     699           0 :         FD_LOG_WARNING(( "unexpected superblock header" ));
     700           0 :         return FD_GROOVE_ERR_CORRUPT;
     701           0 :       }
     702       31788 : #     endif
     703             : 
     704       31788 :       FD_COMPILER_MFENCE();
     705       31788 :       free_objs = *_free_objs;
     706       31788 :       FD_COMPILER_MFENCE();
     707             : 
     708       31788 : #     if FD_GROOVE_PARANOID
     709       31788 :       if( FD_UNLIKELY( fd_ulong_shift_right( free_objs, (int)obj_cnt ) ) ) {
     710           0 :         FD_LOG_WARNING(( "invalid free_objs bit field" ));
     711           0 :         return FD_GROOVE_ERR_CORRUPT;
     712           0 :       }
     713       31788 : #     endif
     714             : 
     715       31788 :       free_cnt = (ulong)fd_ulong_popcnt( free_objs );
     716             : 
     717       31788 :       if( FD_LIKELY( free_cnt<obj_cnt ) ) { /* inactive top was not completely empty, return to circulation */
     718             : 
     719       29262 :         fd_groove_data_private_inactive_push( _inactive_stack, _volume0, superblock_off );
     720             : 
     721       29262 :       } else if( FD_LIKELY( obj_szc<(FD_GROOVE_DATA_SZC_CNT-1UL) ) ) { /* completely empty and should free from parent sb */
     722             : 
     723        2526 :         int err = fd_groove_data_private_free( data, _superblock_hdr+1, FD_GROOVE_DATA_HDR_TYPE_SUPERBLOCK ); /* offset the hdr */
     724        2526 :         if( FD_UNLIKELY( err ) ) {
     725           0 :           FD_LOG_WARNING(( "superblock free failed (%i-%s)", err, fd_groove_strerror( err ) ));
     726           0 :           return FD_GROOVE_ERR_CORRUPT;
     727           0 :         }
     728             : 
     729        2526 :       } else { /* completely empty and should free parent volume */
     730             : 
     731           0 :         ulong volume_off = superblock_off - FD_GROOVE_BLOCK_FOOTPRINT;
     732             : 
     733           0 :         fd_groove_volume_t * _volume = (fd_groove_volume_t *)((ulong)_volume0 + volume_off);
     734             : 
     735           0 : #       if FD_GROOVE_PARANOID
     736           0 :         if( FD_UNLIKELY( !( (_volume0<=_volume) & (_volume<_volume1) &
     737           0 :                             fd_ulong_is_aligned( volume_off, FD_GROOVE_VOLUME_FOOTPRINT ) ) ) ) {
     738           0 :           FD_LOG_WARNING(( "volume not at a valid groove data local address" ));
     739           0 :           return FD_GROOVE_ERR_CORRUPT;
     740           0 :         }
     741             : 
     742           0 :         if( FD_UNLIKELY( !( (_volume->magic                         ==FD_GROOVE_VOLUME_MAGIC   ) &
     743           0 :                             (_volume->idx*FD_GROOVE_VOLUME_FOOTPRINT==volume_off               ) &
     744           0 :                             (_volume->info_sz                       <=FD_GROOVE_VOLUME_INFO_MAX) ) ) ) {
     745           0 :           FD_LOG_WARNING(( "unexpected volume header" ));
     746           0 :           return FD_GROOVE_ERR_CORRUPT;
     747           0 :         }
     748           0 : #       endif
     749             : 
     750           0 :         FD_COMPILER_MFENCE();
     751           0 :         _volume->magic = ~FD_GROOVE_VOLUME_MAGIC; /* mark volume as containing no groove data allocations */
     752           0 :         FD_COMPILER_MFENCE();
     753             : 
     754           0 :         fd_groove_volume_pool_release( data->volume_pool, _volume );
     755             : 
     756           0 :       }
     757       31788 :     }
     758       32361 :   }
     759             : 
     760     2109828 :   return FD_GROOVE_SUCCESS;
     761     2109828 : }
     762             : 
     763      619491 : #define TEST(c) do {                                                                                \
     764      619491 :     if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return FD_GROOVE_ERR_CORRUPT; } \
     765      619491 :   } while(0)
     766             : 
     767             : /* fd_groove_data_private_verify_superblock verifies the location
     768             :    superblock_off (relative to _volume0) seems to contain a valid
     769             :    superblock.  groove data is located in the caller's address space at
     770             :    [_volume0,_volume1).  exp_szc gives the expected sizeclass for the
     771             :    superblock.  If in_circulation is non-zero, the superblock is known
     772             :    to be in circulation (i.e. contains at least one free object / is
     773             :    either any active or an inactive superblock / is available to alloc
     774             :    for allocation).  Assumes _volume0, _volume1, exp_szc and the
     775             :    sizeclass configuration are valid. */
     776             : 
     777             : static int
     778             : fd_groove_data_private_verify_superblock( ulong                      superblock_off,
     779             :                                           ulong                      exp_szc,
     780             :                                           int                        in_circulation,
     781             :                                           int                        verify_descendents,
     782             :                                           fd_groove_volume_t const * _volume0,
     783       32169 :                                           fd_groove_volume_t const * _volume1 ) {
     784             : 
     785             :   /* Verify superblock_off */
     786             : 
     787       32169 :   fd_groove_data_hdr_t const * _superblock_hdr = (fd_groove_data_hdr_t const *)(((ulong)_volume0) + superblock_off);
     788             : 
     789       32169 :   TEST( ((ulong)_volume0<(ulong)_superblock_hdr) & ((ulong)_superblock_hdr<(ulong)_volume1) );
     790       32169 :   TEST( fd_ulong_is_aligned( (ulong)_superblock_hdr, FD_GROOVE_BLOCK_ALIGN ) );
     791             : 
     792             :   /* Verify superblock header */
     793             : 
     794       32169 :   fd_groove_data_hdr_t hdr = *_superblock_hdr;
     795             : 
     796       32169 :   TEST( fd_groove_data_hdr_type( hdr )==FD_GROOVE_DATA_HDR_TYPE_SUPERBLOCK );
     797             : 
     798       32169 :   ulong szc = fd_groove_data_hdr_szc( hdr );
     799             : 
     800       32169 :   TEST( szc==exp_szc );
     801             : 
     802       32169 :   ulong obj_cnt       = (ulong)fd_groove_data_szc_cfg[ szc ].obj_cnt;
     803       32169 :   ulong obj_footprint = (ulong)fd_groove_data_szc_cfg[ szc ].obj_footprint;
     804       32169 :   ulong parent_szc    = (ulong)fd_groove_data_szc_cfg[ szc ].parent_szc;
     805             : 
     806       32169 :   ulong parent_obj_idx       = fd_groove_data_hdr_idx( hdr );
     807       32169 :   ulong parent_obj_cnt       = (parent_szc<FD_GROOVE_DATA_SZC_CNT) ? (ulong)fd_groove_data_szc_cfg[ parent_szc ].obj_cnt : 1UL;
     808             : //ulong parent_obj_footprint = (parent_szc<FD_GROOVE_DATA_SZC_CNT) ? (ulong)fd_groove_data_szc_cfg[ parent_szc ].obj_footprint :
     809             : //                             (FD_GROOVE_VOLUME_DATA_MAX - FD_GROOVE_BLOCK_FOOTPRINT);
     810             : 
     811       32169 :   TEST( parent_obj_idx < parent_obj_cnt );
     812             : 
     813       32169 :   TEST( fd_groove_data_hdr_align( hdr )==FD_GROOVE_DATA_HDR_ALIGN );
     814       32169 :   TEST( fd_groove_data_hdr_sz   ( hdr )==
     815       32169 :         fd_ulong_min( FD_GROOVE_BLOCK_FOOTPRINT - FD_GROOVE_DATA_HDR_FOOTPRINT + obj_cnt*obj_footprint, (1UL<<25)-1UL ) );
     816             : 
     817       32169 :   ulong free_objs = *(ulong const *)(_superblock_hdr+1);
     818             : 
     819       32169 :   TEST( !fd_ulong_shift_right( free_objs, (int)obj_cnt ) ); /* valid free obj bit field */
     820       32169 :   if( in_circulation ) TEST( !!free_objs ); /* at least 1 free obj for superblocks in circulation */
     821             : 
     822             :   /* Verify superblock object headers */
     823             : 
     824       32169 :   ulong rem_objs = free_objs ^ fd_ulong_mask_lsb( (int)obj_cnt );
     825       76578 :   while( rem_objs ) {
     826       44409 :     ulong _idx = (ulong)fd_ulong_find_lsb( rem_objs );
     827             : 
     828       44409 :     ulong child_obj_off = superblock_off + FD_GROOVE_BLOCK_FOOTPRINT + _idx*obj_footprint;
     829             : 
     830       44409 :     fd_groove_data_hdr_t obj_hdr = *(fd_groove_data_hdr_t const *)((ulong)_volume0 + child_obj_off);
     831             : 
     832       44409 :     ulong obj_type = fd_groove_data_hdr_type( obj_hdr );
     833       44409 :     ulong obj_idx  = fd_groove_data_hdr_idx ( obj_hdr );
     834       44409 :     ulong obj_szc  = fd_groove_data_hdr_szc ( obj_hdr );
     835             : 
     836       44409 :     TEST( (obj_type==FD_GROOVE_DATA_HDR_TYPE_ALLOC) | (obj_type==FD_GROOVE_DATA_HDR_TYPE_SUPERBLOCK) );
     837       44409 :     TEST( obj_idx==_idx                   );
     838       44409 :     TEST( obj_szc< FD_GROOVE_DATA_SZC_CNT );
     839             : 
     840       44409 :     TEST( fd_ulong_if( obj_type==FD_GROOVE_DATA_HDR_TYPE_ALLOC,
     841       44409 :                        obj_szc, (ulong)fd_groove_data_szc_cfg[ obj_szc ].parent_szc )==szc );
     842             : 
     843       44409 :     ulong req_align = fd_groove_data_hdr_align( obj_hdr );
     844       44409 :     ulong req_sz    = fd_groove_data_hdr_sz   ( obj_hdr );
     845             :   //ulong req_info  = fd_groove_data_hdr_info ( obj_hdr );
     846             : 
     847       44409 :     ulong req_footprint =
     848       44409 :       fd_ulong_align_up( fd_ulong_align_up( FD_GROOVE_DATA_HDR_FOOTPRINT, req_align ) + req_sz, FD_GROOVE_BLOCK_ALIGN );
     849             : 
     850       44409 :     TEST( fd_ulong_is_pow2( req_align )        );
     851       44409 :     TEST( req_align    <=FD_GROOVE_BLOCK_ALIGN );
     852       44409 :     TEST( req_footprint<=obj_footprint         );
     853             : 
     854             :     /* Note that recursion depth is bounded due to the finite number of
     855             :        sizeclasses and the validation above the children are respecting
     856             :        the szc hierarchy above. */
     857             : 
     858       44409 :     if( (!!verify_descendents) & (obj_type==FD_GROOVE_DATA_HDR_TYPE_SUPERBLOCK) ) /* verify superblock descendents */
     859       23910 :       TEST( !fd_groove_data_private_verify_superblock( child_obj_off, obj_szc, 0 /* don't know if in circulation */,
     860       44409 :                                                        verify_descendents, _volume0, _volume1 ) );
     861             : 
     862       44409 :     rem_objs = fd_ulong_pop_lsb( rem_objs );
     863       44409 :   }
     864             : 
     865       32169 :   return FD_GROOVE_SUCCESS;
     866       32169 : }
     867             : 
     868             : int
     869          15 : fd_groove_data_verify( fd_groove_data_t const * data ) {
     870             : 
     871             :   /* Verify join */
     872             : 
     873          15 :   TEST( data );
     874          15 :   TEST( fd_ulong_is_aligned( (ulong)data, alignof(fd_groove_data_t) ) );
     875             : 
     876          15 :   fd_groove_volume_pool_t const * pool           = data->volume_pool;
     877          15 :   ulong                   const * active_slot    = data->active_slot;
     878          15 :   ulong                   const * inactive_stack = data->inactive_stack;
     879             :   /* cgroup_hint is arbitrary */
     880             : 
     881             :   /* Verify volume pool */
     882             : 
     883          15 :   TEST( !fd_groove_volume_pool_verify( pool ) );
     884             : 
     885          15 :   fd_groove_volume_pool_shmem_t const * shpool = (fd_groove_volume_pool_shmem_t const *)fd_groove_volume_pool_shpool_const( pool );
     886             : 
     887          15 :   fd_groove_volume_t const * _volume0   = (fd_groove_volume_t const *)fd_groove_volume_pool_shele_const ( pool );
     888          15 :   ulong                      volume_max =                             fd_groove_volume_pool_ele_max     ( pool );
     889          15 :   fd_groove_volume_t const * _volume1   = _volume0 + volume_max;
     890             : 
     891          15 :   TEST( (!!_volume0) | (!volume_max) );
     892          15 :   TEST( _volume0<=_volume1 );
     893          15 :   TEST( fd_ulong_is_aligned( (ulong)_volume0, FD_GROOVE_VOLUME_ALIGN ) );
     894             : 
     895          15 :   ulong volume_idx = fd_groove_volume_pool_private_vidx_idx( shpool->ver_top );
     896          15 :   while( volume_idx<volume_max ) { /* note: cyclic check already done by volume_pool_verify above */
     897           0 :     TEST( _volume0[ volume_idx ].magic==~FD_GROOVE_VOLUME_MAGIC );
     898           0 :     TEST( _volume0[ volume_idx ].idx  ==volume_idx              );
     899           0 :     volume_idx = fd_groove_volume_pool_private_idx( _volume0[ volume_idx ].next );
     900           0 :   }
     901             : 
     902             :   /* Verify data shmem */
     903             : 
     904          15 :   fd_groove_data_shmem_t const * shdata = (fd_groove_data_shmem_t const *)fd_groove_data_shdata_const( data );
     905             : 
     906          15 :   TEST( fd_ulong_is_aligned( (ulong)shdata, fd_groove_data_align() ) );
     907             : 
     908          15 :   TEST( shdata->magic         ==FD_GROOVE_DATA_MAGIC );
     909          15 :   TEST( shdata->volume_pool   ==shpool               );
     910          15 :   TEST( shdata->active_slot   ==active_slot          );
     911          15 :   TEST( shdata->inactive_stack==inactive_stack       );
     912             : 
     913             :   /* Verify sizeclass configuration */
     914             : 
     915         495 :   for( ulong szc_idx=0UL; szc_idx<FD_GROOVE_DATA_SZC_CNT; szc_idx++ ) {
     916         480 :     ulong obj_cnt       = (ulong)fd_groove_data_szc_cfg[ szc_idx ].obj_cnt;
     917         480 :     ulong obj_footprint = (ulong)fd_groove_data_szc_cfg[ szc_idx ].obj_footprint;
     918         480 :     ulong cgroup_mask   = (ulong)fd_groove_data_szc_cfg[ szc_idx ].cgroup_mask;
     919         480 :     ulong parent_szc    = (ulong)fd_groove_data_szc_cfg[ szc_idx ].parent_szc;
     920             : 
     921         480 :     ulong cgroup_cnt = cgroup_mask + 1UL;
     922             : 
     923         480 :     ulong superblock_footprint = FD_GROOVE_BLOCK_FOOTPRINT + obj_cnt*obj_footprint;
     924             : 
     925         480 :     ulong parent_obj_footprint = (parent_szc<FD_GROOVE_DATA_SZC_CNT) ?
     926         465 :       (ulong)fd_groove_data_szc_cfg[ parent_szc ].obj_footprint : (FD_GROOVE_VOLUME_DATA_MAX - FD_GROOVE_BLOCK_FOOTPRINT);
     927             : 
     928         480 :     TEST( (1UL<=obj_cnt) & (obj_cnt<=64UL)                                );
     929         480 :     TEST( fd_ulong_is_aligned( obj_footprint, FD_GROOVE_BLOCK_FOOTPRINT ) );
     930         480 :     TEST( fd_ulong_is_pow2( cgroup_cnt )                                  );
     931         480 :     TEST( parent_szc<=FD_GROOVE_DATA_SZC_CNT                              );
     932         480 :     TEST( superblock_footprint <= parent_obj_footprint                    );
     933         480 :   }
     934             : 
     935             :   /* Verify all active superblocks */
     936             : 
     937         495 :   for( ulong szc_idx=0UL; szc_idx<FD_GROOVE_DATA_SZC_CNT; szc_idx++ ) {
     938         480 :     ulong cgroup_cnt = (ulong)fd_groove_data_szc_cfg[ szc_idx ].cgroup_mask + 1UL;
     939       12165 :     for( ulong cgroup_idx=0UL; cgroup_idx<cgroup_cnt; cgroup_idx++ ) {
     940       11685 :       ulong superblock_off = active_slot[ szc_idx + FD_GROOVE_DATA_SZC_CNT*cgroup_idx ];
     941       11685 :       if( !superblock_off ) continue;
     942             : 
     943         159 :       TEST( !fd_groove_data_private_verify_superblock( superblock_off, szc_idx, 1 /* is in circulation */,
     944         159 :                                                        0 /* don't verify children */, _volume0, _volume1 ) );
     945         159 :       fd_groove_data_hdr_t hdr = *(fd_groove_data_hdr_t const *)(((ulong)_volume0) + superblock_off);
     946         159 :       TEST( fd_groove_data_hdr_szc( hdr )==szc_idx );
     947         159 :     }
     948         480 :   }
     949             : 
     950             :   /* Verify all inactive superblocks for sizeclass szc_idx */
     951             : 
     952         495 :   for( ulong szc_idx=0UL; szc_idx<FD_GROOVE_DATA_SZC_CNT; szc_idx++ ) {
     953         480 :     ulong superblock_off = inactive_stack[ szc_idx ] & ~(FD_GROOVE_BLOCK_FOOTPRINT-1UL);
     954         480 :     ulong rem            = volume_max*FD_GROOVE_VOLUME_FOOTPRINT / (2UL*FD_GROOVE_BLOCK_FOOTPRINT); /* FIXME: tighter bound? */
     955        8574 :     while( superblock_off ) {
     956        8094 :       FD_TEST( rem ); rem--; /* avoid cycles */
     957             : 
     958        8094 :       TEST( !fd_groove_data_private_verify_superblock( superblock_off, szc_idx, 1 /* is in circulation */,
     959        8094 :                                                        0 /* don't verify children */, _volume0, _volume1 ) );
     960        8094 :       fd_groove_data_hdr_t hdr = *(fd_groove_data_hdr_t const *)(((ulong)_volume0) + superblock_off);
     961        8094 :       TEST( fd_groove_data_hdr_szc( hdr )==szc_idx );
     962             : 
     963        8094 :       superblock_off = fd_groove_data_hdr_info( hdr );
     964        8094 :     }
     965         480 :   }
     966             : 
     967          15 :   return FD_GROOVE_SUCCESS;
     968          15 : }
     969             : 
     970             : int
     971             : fd_groove_data_volume_verify( fd_groove_data_t   const * data,
     972           6 :                               fd_groove_volume_t const * _volume ) {
     973             : 
     974           6 :   TEST( data );
     975             : 
     976           6 :   fd_groove_volume_t const * _volume0 = (fd_groove_volume_t const *)fd_groove_data_volume0_const( data );
     977           6 :   fd_groove_volume_t const * _volume1 = (fd_groove_volume_t const *)fd_groove_data_volume1_const( data );
     978             : 
     979           6 :   ulong volume_off = (ulong)_volume - (ulong)_volume0;
     980             : 
     981           6 :   TEST( (_volume0<=_volume) & (_volume<_volume1)                      );
     982           6 :   TEST( fd_ulong_is_aligned( volume_off, FD_GROOVE_VOLUME_FOOTPRINT ) );
     983             : 
     984           6 :   ulong magic   = _volume->magic;
     985           6 :   ulong idx     = _volume->idx;
     986           6 :   ulong info_sz = _volume->info_sz;
     987             : 
     988           6 :   TEST( (magic==FD_GROOVE_VOLUME_MAGIC) | (magic==~FD_GROOVE_VOLUME_MAGIC) );
     989           6 :   TEST( idx*FD_GROOVE_VOLUME_FOOTPRINT==volume_off                         );
     990           6 :   TEST( info_sz                       <=FD_GROOVE_VOLUME_INFO_MAX          );
     991             : 
     992           6 :   if( magic==FD_GROOVE_VOLUME_MAGIC ) {
     993           6 :     ulong superblock_off = volume_off + FD_GROOVE_BLOCK_FOOTPRINT;
     994           6 :     ulong superblock_szc = FD_GROOVE_DATA_SZC_CNT-1UL;
     995           6 :     TEST( !fd_groove_data_private_verify_superblock( superblock_off, superblock_szc, 0 /* don't know if in circulation */,
     996           6 :                                                      1 /* verify children */, _volume0, _volume1 ) );
     997           6 :   }
     998             : 
     999           6 :   return FD_GROOVE_SUCCESS;
    1000           6 : }
    1001             : 
    1002             : #undef TEST

Generated by: LCOV version 1.14