LCOV - code coverage report
Current view: top level - disco/store - fd_store.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 973 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 90 0.0 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : #include "fd_store.h"
       3             : 
       4             : #include <errno.h>
       5             : #include <fcntl.h>
       6             : #include <unistd.h>
       7             : 
       8           0 : #define FD_STORE_FEC_DATA_VIEW_SPILL (1U)
       9             : 
      10             : enum {
      11             :   FD_STORE_SPILL_RETRY    = -1,
      12             :   FD_STORE_SPILL_NONE     =  0,
      13             :   FD_STORE_SPILL_COMPLETE =  1
      14             : };
      15             : 
      16             : static int
      17             : store_pwrite_all( int          fd,
      18             :                   void const * buf,
      19             :                   ulong        sz,
      20           0 :                   off_t        off ) {
      21           0 :   ulong written = 0UL;
      22           0 :   while( written<sz ) {
      23           0 :     long res = pwrite( fd, (uchar const *)buf + written, sz-written, off+(off_t)written );
      24           0 :     if( FD_LIKELY( res>0L ) ) { written += (ulong)res; continue; }
      25           0 :     if( FD_UNLIKELY( res<0L && errno==EINTR ) ) continue;
      26           0 :     if( FD_UNLIKELY( !res ) ) errno = EIO;
      27           0 :     return -1;
      28           0 :   }
      29           0 :   return 0;
      30           0 : }
      31             : 
      32             : static int
      33             : store_pread_all( int    fd,
      34             :                  void * buf,
      35             :                  ulong  sz,
      36           0 :                  off_t  off ) {
      37           0 :   ulong read_sz = 0UL;
      38           0 :   while( read_sz<sz ) {
      39           0 :     long res = pread( fd, (uchar *)buf + read_sz, sz-read_sz, off+(off_t)read_sz );
      40           0 :     if( FD_LIKELY( res>0L ) ) { read_sz += (ulong)res; continue; }
      41           0 :     if( FD_UNLIKELY( res<0L && errno==EINTR ) ) continue;
      42           0 :     if( FD_UNLIKELY( !res ) ) errno = EIO;
      43           0 :     return -1;
      44           0 :   }
      45           0 :   return 0;
      46           0 : }
      47             : 
      48             : static inline fd_shredb_shred_entry_t *
      49           0 : disk_shred_pool_laddr( fd_store_t const * store ) {
      50           0 :   return fd_wksp_laddr_fast( fd_store_wksp( store ), store->shred_pool_gaddr );
      51           0 : }
      52             : 
      53             : static inline atomic_ulong *
      54           0 : disk_shred_tag_laddr( fd_store_t const * store ) {
      55           0 :   return fd_wksp_laddr_fast( fd_store_wksp( store ), store->shred_tag_gaddr );
      56           0 : }
      57             : 
      58             : static inline fd_shredb_shred_map_t *
      59             : disk_shred_map_ljoin( fd_store_t const * store,
      60           0 :                       fd_shredb_shred_map_t * join ) {
      61           0 :   fd_wksp_t * wksp = fd_store_wksp( store );
      62           0 :   return fd_shredb_shred_map_join( join,
      63           0 :                                    fd_wksp_laddr_fast( wksp, store->shred_map_gaddr ),
      64           0 :                                    disk_shred_pool_laddr( store ),
      65           0 :                                    store->disk_max_shreds );
      66           0 : }
      67             : 
      68             : static inline fd_shredb_root_entry_t *
      69           0 : disk_root_pool_laddr( fd_store_t const * store ) {
      70           0 :   return fd_wksp_laddr_fast( fd_store_wksp( store ), store->root_pool_gaddr );
      71           0 : }
      72             : 
      73             : static inline fd_shredb_root_map_t *
      74             : disk_root_map_ljoin( fd_store_t const * store,
      75           0 :                      fd_shredb_root_map_t * join ) {
      76           0 :   fd_wksp_t * wksp = fd_store_wksp( store );
      77           0 :   return fd_shredb_root_map_join( join,
      78           0 :                                   fd_wksp_laddr_fast( wksp, store->root_map_gaddr ),
      79           0 :                                   disk_root_pool_laddr( store ),
      80           0 :                                   store->disk_max_shreds );
      81           0 : }
      82             : 
      83             : static inline atomic_ulong *
      84           0 : disk_slot_hint_laddr( fd_store_t const * store ) {
      85           0 :   return fd_wksp_laddr_fast( fd_store_wksp( store ), store->slot_hint_gaddr );
      86           0 : }
      87             : 
      88             : static inline uchar *
      89           0 : cache_data_laddr( fd_store_t const * store ) {
      90           0 :   return fd_wksp_laddr_fast( fd_store_wksp( store ), store->cache_data_gaddr );
      91           0 : }
      92             : 
      93             : static inline ulong *
      94           0 : cache_free_laddr( fd_store_t const * store ) {
      95           0 :   return fd_wksp_laddr_fast( fd_store_wksp( store ), store->cache_free_gaddr );
      96           0 : }
      97             : 
      98             : static inline uint *
      99           0 : spill_free_laddr( fd_store_t const * store ) {
     100           0 :   return fd_wksp_laddr_fast( fd_store_wksp( store ), store->spill_free_gaddr );
     101           0 : }
     102             : 
     103             : static inline uchar *
     104           0 : spill_read_data_laddr( fd_store_t const * store ) {
     105           0 :   return fd_wksp_laddr_fast( fd_store_wksp( store ), store->spill_read_data_gaddr );
     106           0 : }
     107             : 
     108             : static inline fd_store_fec_t *
     109           0 : pool_ele_laddr( fd_store_t const * store ) {
     110           0 :   return fd_wksp_laddr_fast( fd_store_wksp( store ), store->pool_ele_gaddr );
     111           0 : }
     112             : 
     113             : static inline fd_store_slot_to_fecs_t *
     114             : slot_to_fecs_laddr( fd_store_t const * store,
     115           0 :                     ulong              part ) {
     116           0 :   return fd_wksp_laddr_fast( fd_store_wksp( store ), store->slot_to_fecs_gaddr + part*store->slot_to_fecs_sz );
     117           0 : }
     118             : 
     119             : static inline fd_store_pool_t
     120           0 : pool_ljoin( fd_store_t const * store ) {
     121           0 :   return (fd_store_pool_t){
     122           0 :     .pool    = fd_wksp_laddr_fast( fd_store_wksp( store ), store->pool_mem_gaddr ),
     123           0 :     .ele     = fd_wksp_laddr_fast( fd_store_wksp( store ), store->pool_ele_gaddr ),
     124           0 :     .ele_max = store->fec_max
     125           0 :   };
     126           0 : }
     127             : 
     128             : static fd_store_fec_t *
     129           0 : fd_store_fec_acquire( fd_store_t * store ) {
     130           0 :   fd_store_pool_t pool = pool_ljoin( store );
     131           0 :   fd_store_fec_t * fec = fd_store_pool_acquire( &pool );
     132           0 :   if( FD_LIKELY( fec ) ) {
     133           0 :     fec->data_sz              = 0UL;
     134           0 :     fec->data_idx             = 0U;
     135           0 :     fec->cache_prev           = UINT_MAX;
     136           0 :     fec->cache_next           = UINT_MAX;
     137           0 :     fec->data_pin_cnt         = 0U;
     138           0 :     fec->data_state           = FD_STORE_FEC_DATA_EMPTY;
     139           0 :     fec->data_consume_pending = 0U;
     140           0 :   }
     141           0 :   return fec;
     142           0 : }
     143             : 
     144             : static void
     145             : cache_slot_release_locked( fd_store_t * store,
     146           0 :                            ulong        cache_slot ) {
     147           0 :   FD_TEST( cache_slot<store->cache_slot_cnt );
     148           0 :   FD_TEST( store->cache_free_cnt<store->cache_slot_cnt );
     149           0 :   cache_free_laddr( store )[ store->cache_free_cnt++ ] = cache_slot;
     150           0 : }
     151             : 
     152             : static inline uint
     153             : cache_fec_idx( fd_store_t const *     store,
     154           0 :                fd_store_fec_t const * fec ) {
     155           0 :   ulong off = (ulong)fec - (ulong)pool_ele_laddr( store );
     156           0 :   FD_TEST( !(off % sizeof(fd_store_fec_t)) );
     157           0 :   ulong idx = off / sizeof(fd_store_fec_t);
     158           0 :   FD_TEST( idx<store->fec_max );
     159           0 :   return (uint)idx;
     160           0 : }
     161             : 
     162             : static void
     163             : cache_lru_remove_locked( fd_store_t *     store,
     164           0 :                          fd_store_fec_t * fec ) {
     165           0 :   uint idx = cache_fec_idx( store, fec );
     166           0 :   uint prev = fec->cache_prev;
     167           0 :   uint next = fec->cache_next;
     168             : 
     169           0 :   if( prev==UINT_MAX ) { FD_TEST( store->cache_lru_head==idx ); store->cache_lru_head = next; }
     170           0 :   else                 pool_ele_laddr( store )[ prev ].cache_next = next;
     171           0 :   if( next==UINT_MAX ) { FD_TEST( store->cache_lru_tail==idx ); store->cache_lru_tail = prev; }
     172           0 :   else                 pool_ele_laddr( store )[ next ].cache_prev = prev;
     173             : 
     174           0 :   fec->cache_prev = UINT_MAX;
     175           0 :   fec->cache_next = UINT_MAX;
     176           0 : }
     177             : 
     178             : static void
     179             : cache_lru_push_head_locked( fd_store_t *     store,
     180           0 :                             fd_store_fec_t * fec ) {
     181           0 :   uint idx = cache_fec_idx( store, fec );
     182           0 :   FD_TEST( fec->cache_prev==UINT_MAX && fec->cache_next==UINT_MAX );
     183             : 
     184           0 :   fec->cache_next = store->cache_lru_head;
     185           0 :   if( store->cache_lru_head==UINT_MAX ) store->cache_lru_tail = idx;
     186           0 :   else pool_ele_laddr( store )[ store->cache_lru_head ].cache_prev = idx;
     187           0 :   store->cache_lru_head = idx;
     188           0 : }
     189             : 
     190             : static void
     191             : cache_lru_push_tail_locked( fd_store_t *     store,
     192           0 :                             fd_store_fec_t * fec ) {
     193           0 :   uint idx = cache_fec_idx( store, fec );
     194           0 :   FD_TEST( fec->cache_prev==UINT_MAX && fec->cache_next==UINT_MAX );
     195             : 
     196           0 :   fec->cache_prev = store->cache_lru_tail;
     197           0 :   if( store->cache_lru_tail==UINT_MAX ) store->cache_lru_head = idx;
     198           0 :   else pool_ele_laddr( store )[ store->cache_lru_tail ].cache_next = idx;
     199           0 :   store->cache_lru_tail = idx;
     200           0 : }
     201             : 
     202             : static void
     203             : spill_free_push_locked( fd_store_t * store,
     204           0 :                         uint         spill_slot ) {
     205           0 :   FD_TEST( spill_slot<store->spill_slot_cnt && store->spill_free_cnt<store->spill_slot_cnt );
     206           0 :   spill_free_laddr( store )[ store->spill_free_cnt++ ] = spill_slot;
     207           0 : }
     208             : 
     209             : static void
     210             : cache_consume_locked( fd_store_t *     store,
     211           0 :                       fd_store_fec_t * fec ) {
     212           0 :   if( FD_LIKELY( !fec->data_consume_pending ) ) return;
     213           0 :   if( FD_UNLIKELY( fec->data_pin_cnt || fec->data_state==FD_STORE_FEC_DATA_SPILLING ) ) return;
     214             : 
     215           0 :   if( FD_LIKELY( fec->data_state==FD_STORE_FEC_DATA_RAM_WRITING ||
     216           0 :                  fec->data_state==FD_STORE_FEC_DATA_RAM_READY ) ) {
     217           0 :     if( fec->data_state==FD_STORE_FEC_DATA_RAM_READY ) cache_lru_remove_locked( store, fec );
     218           0 :     cache_slot_release_locked( store, fec->data_idx );
     219           0 :   } else if( fec->data_state==FD_STORE_FEC_DATA_DISK ) {
     220           0 :     ulong spill_slot = fec->data_idx;
     221           0 :     FD_TEST( spill_slot<store->spill_slot_cnt && atomic_load_explicit( &store->spill_live_cnt, memory_order_relaxed ) );
     222           0 :     atomic_fetch_sub_explicit( &store->spill_live_cnt, 1UL, memory_order_relaxed );
     223           0 :     spill_free_push_locked( store, (uint)spill_slot );
     224           0 :   }
     225             : 
     226           0 :   fec->data_idx             = 0U;
     227           0 :   fec->cache_prev           = UINT_MAX;
     228           0 :   fec->cache_next           = UINT_MAX;
     229           0 :   fec->data_state           = FD_STORE_FEC_DATA_CONSUMED;
     230           0 :   fec->data_consume_pending = 0U;
     231           0 : }
     232             : 
     233             : static void
     234             : fd_store_fec_release( fd_store_t * store,
     235           0 :                       fd_store_fec_t * fec ) {
     236           0 :   (void)cache_fec_idx( store, fec );
     237           0 :   for(;;) {
     238           0 :     fd_rwlock_write( &store->cache_lock );
     239           0 :     fec->data_consume_pending = 1U;
     240           0 :     cache_consume_locked( store, fec );
     241           0 :     int consumed = fec->data_state==FD_STORE_FEC_DATA_CONSUMED;
     242           0 :     fd_rwlock_unwrite( &store->cache_lock );
     243           0 :     if( FD_LIKELY( consumed ) ) break;
     244           0 :     FD_SPIN_PAUSE();
     245           0 :   }
     246           0 :   fd_store_pool_t pool = pool_ljoin( store );
     247           0 :   fd_store_pool_release( &pool, fec );
     248           0 : }
     249             : 
     250             : static int
     251             : spill_one_locked( fd_store_t                 * store,
     252             :                   int                          disk_fd,
     253           0 :                   fd_store_fec_spill_stats_t * spill ) {
     254           0 :   if( FD_UNLIKELY( disk_fd<0 ) ) return FD_STORE_SPILL_NONE;
     255             : 
     256           0 :   fd_store_fec_t * fec0 = pool_ele_laddr( store );
     257           0 :   uint victim_idx = store->cache_lru_head;
     258           0 :   if( FD_UNLIKELY( victim_idx==UINT_MAX ) ) return FD_STORE_SPILL_NONE;
     259           0 :   fd_store_fec_t * victim = fec0 + victim_idx;
     260           0 :   FD_TEST( victim->data_state==FD_STORE_FEC_DATA_RAM_READY && !victim->data_pin_cnt );
     261           0 :   FD_TEST( victim->data_sz<=store->fec_data_max );
     262             : 
     263           0 :   uint spill_slot;
     264           0 :   if( FD_LIKELY( store->spill_free_cnt ) ) {
     265           0 :     spill_slot = spill_free_laddr( store )[ --store->spill_free_cnt ];
     266           0 :   } else {
     267           0 :     if( FD_UNLIKELY( store->spill_slot_cnt>=store->fec_max ) ) return FD_STORE_SPILL_NONE;
     268           0 :     spill_slot = (uint)store->spill_slot_cnt++;
     269           0 :   }
     270             : 
     271           0 :   ulong ram_slot  = victim->data_idx;
     272           0 :   ulong ram_off   = ram_slot * store->payload_slot_sz;
     273           0 :   ulong spill_off = (ulong)spill_slot * store->payload_slot_sz;
     274           0 :   ulong data_sz   = victim->data_sz;
     275             : 
     276           0 :   cache_lru_remove_locked( store, victim );
     277           0 :   victim->data_state = FD_STORE_FEC_DATA_SPILLING;
     278           0 :   long evict_ticks = -fd_tickcount();
     279           0 :   fd_rwlock_unwrite( &store->cache_lock );
     280             : 
     281           0 :   if( FD_UNLIKELY( store_pwrite_all( disk_fd, cache_data_laddr( store ) + ram_off, data_sz, (off_t)spill_off ) ) )
     282           0 :     FD_LOG_ERR(( "error spilling FEC payload to disk: (%d-%s)", errno, fd_io_strerror( errno ) ));
     283             : 
     284           0 :   fd_rwlock_write( &store->cache_lock );
     285           0 :   FD_TEST( victim->data_state==FD_STORE_FEC_DATA_SPILLING );
     286           0 :   int result;
     287           0 :   if( FD_UNLIKELY( victim->data_pin_cnt || victim->data_consume_pending ) ) {
     288           0 :     int try_next = !!victim->data_pin_cnt;
     289           0 :     spill_free_push_locked( store, spill_slot );
     290           0 :     victim->data_state = victim->data_consume_pending && !victim->data_pin_cnt
     291           0 :                        ? FD_STORE_FEC_DATA_RAM_WRITING
     292           0 :                        : FD_STORE_FEC_DATA_RAM_READY;
     293           0 :     if( FD_LIKELY( !victim->data_pin_cnt && !victim->data_consume_pending ) )
     294           0 :       cache_lru_push_tail_locked( store, victim );
     295           0 :     cache_consume_locked( store, victim );
     296           0 :     result = try_next ? FD_STORE_SPILL_RETRY :
     297           0 :              store->cache_free_cnt ? FD_STORE_SPILL_COMPLETE : FD_STORE_SPILL_NONE;
     298           0 :   } else {
     299           0 :     victim->data_idx   = spill_slot;
     300           0 :     victim->data_state = FD_STORE_FEC_DATA_DISK;
     301           0 :     atomic_fetch_add_explicit( &store->spill_live_cnt, 1UL, memory_order_relaxed );
     302           0 :     atomic_fetch_add_explicit( &store->fec_spill_cnt, 1UL, memory_order_relaxed );
     303           0 :     atomic_fetch_add_explicit( &store->fec_spill_bytes, data_sz, memory_order_relaxed );
     304           0 :     cache_slot_release_locked( store, ram_slot );
     305           0 :     result = FD_STORE_SPILL_COMPLETE;
     306           0 :   }
     307             : 
     308           0 :   evict_ticks += fd_tickcount();
     309           0 :   if( FD_LIKELY( spill ) ) {
     310           0 :     spill->write_cnt++;
     311           0 :     spill->write_bytes += data_sz;
     312           0 :     spill->write_ticks += (ulong)evict_ticks;
     313           0 :   }
     314           0 :   return result;
     315           0 : }
     316             : 
     317             : 
     318             : void *
     319             : fd_store_new( void       * shmem,
     320             :               ulong        fec_max,
     321             :               ulong        fec_data_max,
     322             :               ulong        shred_storage_gib,
     323             :               ulong        shred_cache_bytes,
     324             :               ulong        fec_set_cnt,
     325             :               ulong        max_shreds_per_block,
     326             :               ulong        seed,
     327             :               int          alpenglow,
     328           0 :               ulong        shred_tile_cnt ) {
     329             : 
     330           0 :   if( FD_UNLIKELY( !shmem ) ) { FD_LOG_WARNING(( "NULL shmem" )); return NULL; }
     331           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_store_align() ) ) ) { FD_LOG_WARNING(( "misaligned shmem" )); return NULL; }
     332           0 :   if( FD_UNLIKELY( !fec_max ) ) { FD_LOG_WARNING(( "fec_max must be non-zero" )); return NULL; }
     333           0 :   if( FD_UNLIKELY( fec_max>UINT_MAX ) ) { FD_LOG_WARNING(( "fec_max must fit in uint" )); return NULL; }
     334           0 :   if( FD_UNLIKELY( !fec_data_max ) ) { FD_LOG_WARNING(( "fec_data_max must be non-zero" )); return NULL; }
     335           0 :   if( FD_UNLIKELY( fec_data_max>UINT_MAX ) ) { FD_LOG_WARNING(( "fec_data_max must fit in uint" )); return NULL; }
     336           0 :   if( FD_UNLIKELY( !max_shreds_per_block || max_shreds_per_block>FD_SHREDB_HINT_VALID ) ) { FD_LOG_WARNING(( "bad max_shreds_per_block (%lu)", max_shreds_per_block )); return NULL; }
     337           0 :   if( FD_UNLIKELY( alpenglow && !shred_tile_cnt ) ) { FD_LOG_WARNING(( "alpenglow needs a slot_to_fecs partition per shred tile" )); return NULL; }
     338           0 :   if( FD_UNLIKELY( shred_storage_gib>FD_SHREDB_MAX_SIZE_GIB ) ) {
     339           0 :     FD_LOG_ERR(( "shred database size limit is %lu GiB, but the maximum supported size is %lu GiB",
     340           0 :                  shred_storage_gib, FD_SHREDB_MAX_SIZE_GIB ));
     341           0 :   }
     342             : 
     343           0 :   ulong footprint = fd_store_footprint( fec_max, fec_data_max, shred_storage_gib, shred_cache_bytes, fec_set_cnt, alpenglow, shred_tile_cnt );
     344           0 :   if( FD_UNLIKELY( !footprint ) ) { FD_LOG_WARNING(( "invalid or overflowing store footprint" )); return NULL; }
     345             : 
     346           0 :   fd_wksp_t * wksp = fd_wksp_containing( shmem );
     347           0 :   if( FD_UNLIKELY( !wksp ) ) { FD_LOG_WARNING(( "shmem must be part of a workspace" )); return NULL; }
     348             : 
     349           0 :   ulong chain_cnt       = fd_store_map_chain_cnt_est( fec_max );
     350           0 :   ulong part_cnt        = alpenglow ? shred_tile_cnt : 0UL;
     351           0 :   ulong slot_chain_cnt  = fd_store_slot_to_fecs_chain_cnt_est( fec_max );
     352           0 :   ulong slot_to_fecs_sz = fd_ulong_align_up( fd_store_slot_to_fecs_footprint( slot_chain_cnt ), fd_store_slot_to_fecs_align() );
     353           0 :   ulong payload_slot_sz = fd_store_payload_slot_sz( fec_data_max );
     354           0 :   ulong cache_slot_cnt = shred_cache_bytes
     355           0 :                        ? fd_ulong_min( fec_max, fd_ulong_max( 1UL, shred_cache_bytes / payload_slot_sz ) )
     356           0 :                        : fec_max;
     357             : 
     358           0 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
     359           0 :   fd_store_t *   store          = FD_SCRATCH_ALLOC_APPEND( l, fd_store_align(),         sizeof(fd_store_t)                  );
     360           0 :   void *         map            = FD_SCRATCH_ALLOC_APPEND( l, fd_store_map_align(),     fd_store_map_footprint( chain_cnt ) );
     361           0 :   void *         shpool         = FD_SCRATCH_ALLOC_APPEND( l, fd_store_pool_align(),    fd_store_pool_footprint()           );
     362           0 :   void *         shele          = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_store_fec_t),  sizeof(fd_store_fec_t)*fec_max      );
     363           0 :   uchar *        slot_to_fecs   = FD_SCRATCH_ALLOC_APPEND( l, fd_store_slot_to_fecs_align(), slot_to_fecs_sz*part_cnt );
     364           0 :   uchar *        cache_mem      = FD_SCRATCH_ALLOC_APPEND( l, FD_STORE_PAYLOAD_PAGE_SZ, payload_slot_sz*cache_slot_cnt      );
     365           0 :   ulong *        cache_free     = FD_SCRATCH_ALLOC_APPEND( l, alignof(ulong),           sizeof(ulong)*cache_slot_cnt        );
     366           0 :   uint *         spill_free     = FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),            sizeof(uint)*fec_max                );
     367           0 :   uchar *        spill_read_mem = FD_SCRATCH_ALLOC_APPEND( l, FD_STORE_PAYLOAD_PAGE_SZ, payload_slot_sz                      );
     368           0 :   fd_fec_set_t * fec_sets       = fec_set_cnt
     369           0 :                                 ? FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_fec_set_t), sizeof(fd_fec_set_t)*fec_set_cnt )
     370           0 :                                 : NULL;
     371             : 
     372           0 :   void *         shred_map_mem  = NULL;
     373           0 :   void *         shred_pool_mem = NULL;
     374           0 :   void *         root_map_mem   = NULL;
     375           0 :   void *         root_pool_mem  = NULL;
     376           0 :   atomic_ulong * shred_tag_mem  = NULL;
     377           0 :   atomic_ulong * slot_hint_mem  = NULL;
     378           0 :   ulong          max_shreds     = 0UL;
     379           0 :   ulong          max_slots      = 0UL;
     380           0 :   ulong          disk_chain_cnt = 0UL;
     381           0 :   ulong          root_chain_cnt = 0UL;
     382             : 
     383           0 :   if( shred_storage_gib ) {
     384           0 :     max_shreds     = fd_shredb_max_shreds( shred_storage_gib );
     385           0 :     max_slots      = fd_shredb_max_slots( shred_storage_gib );
     386           0 :     disk_chain_cnt = fd_shredb_shred_map_chain_cnt_est( max_shreds );
     387             : 
     388           0 :     shred_map_mem  = FD_SCRATCH_ALLOC_APPEND( l, fd_shredb_shred_map_align(),      fd_shredb_shred_map_footprint( disk_chain_cnt ) );
     389           0 :     shred_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_shredb_shred_entry_t), max_shreds * sizeof(fd_shredb_shred_entry_t)    );
     390           0 :     shred_tag_mem  = FD_SCRATCH_ALLOC_APPEND( l, alignof(atomic_ulong),            max_shreds * sizeof(atomic_ulong)               );
     391           0 :     slot_hint_mem  = FD_SCRATCH_ALLOC_APPEND( l, alignof(atomic_ulong),            max_slots * sizeof(atomic_ulong)                );
     392           0 :     if( alpenglow ) {
     393           0 :       root_chain_cnt = fd_shredb_root_map_chain_cnt_est( max_shreds );
     394           0 :       root_map_mem   = FD_SCRATCH_ALLOC_APPEND( l, fd_shredb_root_map_align(),      fd_shredb_root_map_footprint( root_chain_cnt ) );
     395           0 :       root_pool_mem  = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_shredb_root_entry_t), max_shreds * sizeof(fd_shredb_root_entry_t)    );
     396           0 :     }
     397           0 :   }
     398             : 
     399           0 :   FD_TEST( FD_SCRATCH_ALLOC_FINI( l, fd_store_align() )==(ulong)shmem + footprint );
     400             : 
     401           0 :   ulong payload_sz;
     402           0 :   ulong wire_sz;
     403           0 :   ulong file_sz;
     404           0 :   if( FD_UNLIKELY( __builtin_umull_overflow( payload_slot_sz, fec_max, &payload_sz ) ||
     405           0 :                    __builtin_umull_overflow( max_shreds, sizeof(fd_shredb_entry_t), &wire_sz ) ||
     406           0 :                    __builtin_uaddl_overflow( payload_sz, wire_sz, &file_sz ) ||
     407           0 :                    file_sz>(ulong)LONG_MAX ) ) {
     408           0 :     FD_LOG_WARNING(( "store backing file size overflows off_t" ));
     409           0 :     return NULL;
     410           0 :   }
     411             : 
     412           0 :   fd_memset( store, 0, sizeof(fd_store_t) );
     413           0 :   store->fec_max               = fec_max;
     414           0 :   store->fec_data_max          = fec_data_max;
     415           0 :   store->store_gaddr           = fd_wksp_gaddr_fast( wksp, store );
     416           0 :   store->pool_mem_gaddr        = fd_wksp_gaddr_fast( wksp, shpool );
     417           0 :   store->pool_ele_gaddr        = fd_wksp_gaddr_fast( wksp, shele  );
     418           0 :   store->alpenglow             = !!alpenglow;
     419           0 :   store->shred_tile_cnt        = shred_tile_cnt;
     420           0 :   store->slot_to_fecs_gaddr    = fd_wksp_gaddr_fast( wksp, slot_to_fecs );
     421           0 :   store->slot_to_fecs_sz       = slot_to_fecs_sz;
     422           0 :   atomic_init( &store->slot_lo, ULONG_MAX );
     423           0 :   store->payload_slot_sz       = payload_slot_sz;
     424           0 :   store->payload_sz            = payload_sz;
     425           0 :   store->wire_off              = payload_sz; /* wire region begins after the payload region (page-aligned) */
     426           0 :   store->cache_slot_cnt        = cache_slot_cnt;
     427           0 :   store->cache_data_gaddr      = fd_wksp_gaddr_fast( wksp, cache_mem );
     428           0 :   store->cache_free_gaddr      = fd_wksp_gaddr_fast( wksp, cache_free );
     429           0 :   store->cache_free_cnt        = cache_slot_cnt;
     430           0 :   ulong free_cap               = fd_ulong_min( 8192UL, (64UL<<20) / payload_slot_sz );
     431           0 :   ulong burst_floor            = fd_ulong_min( 512UL, cache_slot_cnt/2UL );
     432           0 :   ulong free_target            = fd_ulong_max( cache_slot_cnt/10UL, burst_floor );
     433           0 :   store->cache_free_target     = fd_ulong_max( 1UL, fd_ulong_min( free_cap, free_target ) );
     434           0 :   store->cache_free_low_water  = fd_ulong_max( 1UL, (store->cache_free_target*3UL)/4UL );
     435           0 :   store->cache_lru_head        = UINT_MAX;
     436           0 :   store->cache_lru_tail        = UINT_MAX;
     437           0 :   store->spill_free_gaddr      = fd_wksp_gaddr_fast( wksp, spill_free );
     438           0 :   store->spill_read_data_gaddr = fd_wksp_gaddr_fast( wksp, spill_read_mem );
     439           0 :   store->fec_set_cnt           = fec_set_cnt;
     440           0 :   store->fec_sets_gaddr        = fec_set_cnt ? fd_wksp_gaddr_fast( wksp, fec_sets ) : 0UL;
     441           0 :   store->max_shreds_per_block  = max_shreds_per_block;
     442           0 :   fd_rwlock_new( &store->cache_lock );
     443           0 :   fd_rwlock_new( &store->spill_read_lock );
     444           0 :   fd_rwlock_new( &store->fec_lock );
     445           0 :   atomic_init( &store->fec_spill_cnt, 0UL );
     446           0 :   atomic_init( &store->spill_live_cnt, 0UL );
     447           0 :   atomic_init( &store->fec_spill_bytes, 0UL );
     448           0 :   atomic_init( &store->fec_spill_read_cnt, 0UL );
     449           0 :   atomic_init( &store->fec_spill_read_bytes, 0UL );
     450             : 
     451           0 :   void * shmap = fd_store_map_new( map, chain_cnt, seed );
     452           0 :   if( FD_UNLIKELY( !shmap ) ) { FD_LOG_WARNING(( "fd_store_map_new failed" )); return NULL; }
     453           0 :   store->map_gaddr = fd_wksp_gaddr_fast( wksp, shmap );
     454             : 
     455           0 :   if( FD_UNLIKELY( !fd_store_pool_new( shpool ) ) ) { FD_LOG_WARNING(( "fd_store_pool_new failed" )); return NULL; }
     456           0 :   fd_store_pool_t pool_ljoin;
     457           0 :   fd_store_pool_reset( fd_store_pool_join( &pool_ljoin, shpool, shele, fec_max ) );
     458             : 
     459           0 :   for( ulong i=0UL; i<cache_slot_cnt; i++ ) cache_free[ i ] = cache_slot_cnt - 1UL - i;
     460             : 
     461             :   /* FEC metadata starts without a payload location. */
     462           0 :   fd_store_fec_t * fec0 = (fd_store_fec_t *)shele;
     463           0 :   for( ulong i=0UL; i<fec_max; i++ ) {
     464           0 :     fec0[ i ].data_idx     = 0U;
     465           0 :     fec0[ i ].cache_prev   = UINT_MAX;
     466           0 :     fec0[ i ].cache_next   = UINT_MAX;
     467           0 :     fec0[ i ].data_pin_cnt = 0U;
     468           0 :     fec0[ i ].data_state   = FD_STORE_FEC_DATA_EMPTY;
     469           0 :     fec0[ i ].data_consume_pending = 0U;
     470           0 :   }
     471             : 
     472           0 :   for( ulong i=0UL; i<part_cnt; i++ ) FD_TEST( fd_store_slot_to_fecs_new( slot_to_fecs + i*slot_to_fecs_sz, slot_chain_cnt, seed ) );
     473             : 
     474           0 :   if( shred_storage_gib ) {
     475           0 :     void * shred_shmap = fd_shredb_shred_map_new( shred_map_mem, disk_chain_cnt, seed );
     476           0 :     FD_TEST( shred_shmap );
     477           0 :     store->shred_map_gaddr     = fd_wksp_gaddr_fast( wksp, shred_shmap );
     478           0 :     store->shred_pool_gaddr    = fd_wksp_gaddr_fast( wksp, shred_pool_mem );
     479           0 :     store->shred_tag_gaddr     = fd_wksp_gaddr_fast( wksp, shred_tag_mem );
     480           0 :     store->slot_hint_gaddr     = fd_wksp_gaddr_fast( wksp, slot_hint_mem );
     481           0 :     store->disk_max_shreds     = max_shreds;
     482           0 :     store->disk_max_slots      = max_slots;
     483             : 
     484           0 :     fd_shredb_shred_entry_t * cell = (fd_shredb_shred_entry_t *)shred_pool_mem;
     485           0 :     for( ulong i=0UL; i<max_shreds; i++ ) {
     486           0 :       cell[ i ].key  = 0UL;
     487           0 :       cell[ i ].next = UINT_MAX;
     488           0 :       atomic_init( shred_tag_mem+i, 0UL );
     489           0 :     }
     490             : 
     491           0 :     store->has_root_map = !!alpenglow;
     492           0 :     if( alpenglow ) {
     493           0 :       void * root_shmap = fd_shredb_root_map_new( root_map_mem, root_chain_cnt, seed );
     494           0 :       FD_TEST( root_shmap );
     495           0 :       store->root_map_gaddr  = fd_wksp_gaddr_fast( wksp, root_shmap );
     496           0 :       store->root_pool_gaddr = fd_wksp_gaddr_fast( wksp, root_pool_mem );
     497             : 
     498           0 :       fd_shredb_root_entry_t * root_cell = (fd_shredb_root_entry_t *)root_pool_mem;
     499           0 :       for( ulong i=0UL; i<max_shreds; i++ ) {
     500           0 :         fd_memset( root_cell+i, 0, sizeof(fd_shredb_root_entry_t) );
     501           0 :         root_cell[ i ].next = UINT_MAX;
     502           0 :       }
     503           0 :     }
     504           0 :     for( ulong i=0UL; i<max_slots; i++ ) {
     505           0 :       atomic_init( slot_hint_mem+i, 0UL );
     506           0 :     }
     507             : 
     508           0 :     atomic_init( &store->disk_reservation_head, 0UL );
     509           0 :     atomic_init( &store->disk_cnt,              0UL );
     510           0 :     atomic_init( &store->disk_insert_cnt,       0UL );
     511           0 :     atomic_init( &store->disk_write_bytes,      0UL );
     512           0 :   }
     513             : 
     514           0 :   FD_COMPILER_MFENCE();
     515           0 :   FD_VOLATILE( store->magic ) = FD_STORE_MAGIC;
     516           0 :   FD_COMPILER_MFENCE();
     517             : 
     518           0 :   return shmem;
     519           0 : }
     520             : 
     521             : fd_store_t *
     522           0 : fd_store_join( void * shstore ) {
     523           0 :   if( FD_UNLIKELY( !shstore ) ) { FD_LOG_WARNING(( "NULL store" )); return NULL; }
     524           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shstore, fd_store_align() ) ) ) { FD_LOG_WARNING(( "misaligned store" )); return NULL; }
     525             : 
     526           0 :   fd_wksp_t * wksp = fd_wksp_containing( shstore );
     527           0 :   if( FD_UNLIKELY( !wksp ) ) { FD_LOG_WARNING(( "store must be part of a workspace" )); return NULL; }
     528             : 
     529           0 :   fd_store_t * store = (fd_store_t *)shstore;
     530           0 :   if( FD_UNLIKELY( store->magic!=FD_STORE_MAGIC ) ) { FD_LOG_WARNING(( "bad magic" )); return NULL; }
     531             : 
     532           0 :   return store;
     533           0 : }
     534             : 
     535             : int
     536             : fd_store_file_create( char const * path,
     537             :                       ulong        wire_off,
     538           0 :                       ulong        disk_max_shreds ) {
     539           0 :   if( FD_UNLIKELY( !path ) ) {
     540           0 :     errno = EINVAL;
     541           0 :     return -1;
     542           0 :   }
     543             : 
     544           0 :   ulong wire_sz;
     545           0 :   ulong file_sz;
     546           0 :   if( FD_UNLIKELY( __builtin_umull_overflow( disk_max_shreds, sizeof(fd_shredb_entry_t), &wire_sz ) ||
     547           0 :                    __builtin_uaddl_overflow( wire_off, wire_sz, &file_sz ) ||
     548           0 :                    file_sz>(ulong)LONG_MAX ) ) {
     549           0 :     errno = EOVERFLOW;
     550           0 :     return -1;
     551           0 :   }
     552             : 
     553             :   /* No O_TRUNC it frees a previous run's extents and page cache
     554             :      synchronously. */
     555           0 :   int fd = open( path, O_RDWR|O_CREAT, (mode_t)0600 );
     556           0 :   if( FD_UNLIKELY( fd<0 ) ) return -1;
     557             : 
     558           0 :   int err = 0;
     559           0 :   if( FD_UNLIKELY( ftruncate( fd, (off_t)file_sz ) ) ) err = errno;
     560           0 :   if( FD_UNLIKELY( err ) ) {
     561           0 :     if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_WARNING(( "close(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     562           0 :     errno = err;
     563           0 :     return -1;
     564           0 :   }
     565           0 :   return fd;
     566           0 : }
     567             : 
     568             : void *
     569           0 : fd_store_leave( fd_store_t const * store ) {
     570           0 :   if( FD_UNLIKELY( !store ) ) { FD_LOG_WARNING(( "NULL store" )); return NULL; }
     571           0 :   return (void *)store;
     572           0 : }
     573             : 
     574             : void *
     575           0 : fd_store_delete( void * shstore ) {
     576           0 :   if( FD_UNLIKELY( !shstore ) ) { FD_LOG_WARNING(( "NULL store" )); return NULL; }
     577           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shstore, fd_store_align() ) ) ) { FD_LOG_WARNING(( "misaligned store" )); return NULL; }
     578             : 
     579           0 :   fd_store_t * store = (fd_store_t *)shstore;
     580           0 :   if( FD_UNLIKELY( store->magic!=FD_STORE_MAGIC ) ) { FD_LOG_WARNING(( "bad magic" )); return NULL; }
     581             : 
     582           0 :   FD_COMPILER_MFENCE();
     583           0 :   FD_VOLATILE( store->magic ) = 0UL;
     584           0 :   FD_COMPILER_MFENCE();
     585             : 
     586           0 :   return shstore;
     587           0 : }
     588             : 
     589             : 
     590             : uchar *
     591             : fd_store_fec_data_reserve( fd_store_t                 * store,
     592             :                            int                          disk_fd,
     593             :                            uint                       * data_idx,
     594           0 :                            fd_store_fec_spill_stats_t * spill ) {
     595           0 :   if( FD_LIKELY( spill ) ) *spill = (fd_store_fec_spill_stats_t){0};
     596           0 :   if( FD_UNLIKELY( !store || !data_idx ) ) return NULL;
     597             : 
     598             :   /* Pops a free RAM cache slot, spilling LRU payloads as needed. */
     599           0 :   fd_rwlock_write( &store->cache_lock );
     600             : 
     601           0 :   while( FD_UNLIKELY( !store->cache_free_cnt ) ) {
     602           0 :     if( FD_UNLIKELY( store->cache_lru_head==UINT_MAX ) ) {
     603           0 :       fd_rwlock_unwrite( &store->cache_lock );
     604           0 :       FD_SPIN_PAUSE();
     605           0 :       fd_rwlock_write( &store->cache_lock );
     606           0 :       continue;
     607           0 :     }
     608           0 :     int spill_result = spill_one_locked( store, disk_fd, spill );
     609           0 :     if( FD_UNLIKELY( spill_result==FD_STORE_SPILL_RETRY ) ) {
     610           0 :       fd_rwlock_unwrite( &store->cache_lock );
     611           0 :       FD_SPIN_PAUSE();
     612           0 :       fd_rwlock_write( &store->cache_lock );
     613           0 :       continue;
     614           0 :     }
     615           0 :     if( FD_UNLIKELY( !spill_result ) ) {
     616           0 :       fd_rwlock_unwrite( &store->cache_lock );
     617           0 :       return NULL;
     618           0 :     }
     619           0 :   }
     620             : 
     621           0 :   ulong slot = cache_free_laddr( store )[ --store->cache_free_cnt ];
     622           0 :   fd_rwlock_unwrite( &store->cache_lock );
     623             : 
     624           0 :   *data_idx = (uint)slot;
     625           0 :   return cache_data_laddr( store ) + slot*store->payload_slot_sz;
     626           0 : }
     627             : 
     628             : int
     629             : fd_store_fec_data_preevict( fd_store_t                  * store,
     630             :                             int                           disk_fd,
     631           0 :                             fd_store_fec_spill_stats_t * spill ) {
     632           0 :   fd_store_fec_spill_stats_t local_spill[1];
     633           0 :   if( FD_UNLIKELY( !spill ) ) spill = local_spill;
     634           0 :   *spill = (fd_store_fec_spill_stats_t){0};
     635           0 :   if( FD_UNLIKELY( !store || disk_fd<0 ) ) return 0;
     636             : 
     637             :   /* called every pass: don't take the write lock for nothing */
     638           0 :   if( FD_LIKELY( !FD_VOLATILE_CONST( store->cache_preevict_active ) &&
     639           0 :                  FD_VOLATILE_CONST( store->cache_free_cnt )>=store->cache_free_low_water ) ) return 0;
     640             : 
     641           0 :   fd_rwlock_write( &store->cache_lock );
     642           0 :   if( FD_UNLIKELY( !store->cache_preevict_active && store->cache_free_cnt<store->cache_free_low_water ) )
     643           0 :     store->cache_preevict_active = 1U;
     644           0 :   if( FD_UNLIKELY( store->cache_preevict_active && store->cache_free_cnt>=store->cache_free_target ) )
     645           0 :     store->cache_preevict_active = 0U;
     646             : 
     647           0 :   if( FD_LIKELY( !store->cache_preevict_active ) ) {
     648           0 :     fd_rwlock_unwrite( &store->cache_lock );
     649           0 :     return 0;
     650           0 :   }
     651             : 
     652           0 :   spill_one_locked( store, disk_fd, spill );
     653           0 :   if( FD_UNLIKELY( store->cache_free_cnt>=store->cache_free_target ) )
     654           0 :     store->cache_preevict_active = 0U;
     655           0 :   fd_rwlock_unwrite( &store->cache_lock );
     656           0 :   return !!spill->write_cnt;
     657           0 : }
     658             : 
     659             : void
     660             : fd_store_fec_cache_stats_query( fd_store_t                 * store,
     661           0 :                                 fd_store_fec_cache_stats_t * stats ) {
     662           0 :   if( FD_UNLIKELY( !store || !stats ) ) return;
     663           0 :   fd_rwlock_read( &store->cache_lock );
     664           0 :   stats->free_cnt = store->cache_free_cnt;
     665           0 :   stats->max       = store->cache_slot_cnt;
     666           0 :   stats->target    = store->cache_free_target;
     667           0 :   stats->low_water = store->cache_free_low_water;
     668           0 :   fd_rwlock_unread( &store->cache_lock );
     669           0 : }
     670             : 
     671             : static void
     672             : cache_pin_locked( fd_store_t *     store,
     673           0 :                   fd_store_fec_t * fec ) {
     674           0 :   if( FD_UNLIKELY( !fec->data_pin_cnt && fec->data_state==FD_STORE_FEC_DATA_RAM_READY ) )
     675           0 :     cache_lru_remove_locked( store, fec );
     676           0 :   FD_TEST( fec->data_pin_cnt<USHORT_MAX );
     677           0 :   fec->data_pin_cnt++;
     678           0 :   store->cache_pinned_cnt++;
     679           0 : }
     680             : 
     681             : static void
     682             : cache_unpin_locked( fd_store_t *     store,
     683           0 :                     fd_store_fec_t * fec ) {
     684           0 :   FD_TEST( fec->data_pin_cnt && store->cache_pinned_cnt );
     685           0 :   fec->data_pin_cnt--;
     686           0 :   store->cache_pinned_cnt--;
     687             :   /* Prefer spilling payloads that a reader has already copied while
     688             :      retaining their disk copy for replay backfill. */
     689           0 :   if( FD_UNLIKELY( !fec->data_pin_cnt && fec->data_state==FD_STORE_FEC_DATA_RAM_READY ) )
     690           0 :     cache_lru_push_head_locked( store, fec );
     691           0 : }
     692             : 
     693             : int
     694             : fd_store_fec_data_view( fd_store_t *               store,
     695             :                         int                        disk_fd,
     696             :                         fd_store_fec_t *           fec,
     697           0 :                         fd_store_fec_data_view_t * view ) {
     698           0 :   if( FD_UNLIKELY( !view ) ) return -1;
     699           0 :   view->data  = NULL;
     700           0 :   view->fec   = NULL;
     701           0 :   view->flags = 0U;
     702           0 :   if( FD_UNLIKELY( !store || !fec ) ) return -1;
     703           0 :   fd_store_fec_t * fec0 = pool_ele_laddr( store );
     704           0 :   if( FD_UNLIKELY( fec<fec0 || fec>=fec0+store->fec_max ) ) return -1;
     705             : 
     706           0 :   fd_rwlock_write( &store->cache_lock );
     707             : 
     708           0 :   if( FD_UNLIKELY( fec->data_sz>store->fec_data_max ) ) {
     709           0 :     fd_rwlock_unwrite( &store->cache_lock );
     710           0 :     return -1;
     711           0 :   }
     712             : 
     713           0 :   if( FD_LIKELY( fec->data_state==FD_STORE_FEC_DATA_RAM_READY ||
     714           0 :                  fec->data_state==FD_STORE_FEC_DATA_SPILLING ) ) {
     715           0 :     cache_pin_locked( store, fec );
     716           0 :     view->data = cache_data_laddr( store ) + (ulong)fec->data_idx*store->payload_slot_sz;
     717           0 :     view->fec  = fec;
     718           0 :     fd_rwlock_unwrite( &store->cache_lock );
     719           0 :     return 0;
     720           0 :   }
     721             : 
     722           0 :   if( FD_LIKELY( fec->data_state==FD_STORE_FEC_DATA_DISK ) ) {
     723           0 :     if( FD_UNLIKELY( disk_fd<0 ) ) {
     724           0 :       fd_rwlock_unwrite( &store->cache_lock );
     725           0 :       return -1;
     726           0 :     }
     727           0 :     if( FD_UNLIKELY( !fd_rwlock_trywrite( &store->spill_read_lock ) ) ) {
     728           0 :       fd_rwlock_unwrite( &store->cache_lock );
     729           0 :       return -1;
     730           0 :     }
     731           0 :     ulong data_sz  = fec->data_sz;
     732           0 :     ulong data_off = (ulong)fec->data_idx*store->payload_slot_sz;
     733           0 :     uchar * spill_read_data = spill_read_data_laddr( store );
     734           0 :     cache_pin_locked( store, fec );
     735           0 :     fd_rwlock_unwrite( &store->cache_lock );
     736             : 
     737           0 :     if( FD_UNLIKELY( store_pread_all( disk_fd, spill_read_data, data_sz, (off_t)data_off ) ) )
     738           0 :       FD_LOG_ERR(( "error reading spilled FEC payload: (%d-%s)", errno, fd_io_strerror( errno ) ));
     739             : 
     740           0 :     atomic_fetch_add_explicit( &store->fec_spill_read_cnt, 1UL, memory_order_relaxed );
     741           0 :     atomic_fetch_add_explicit( &store->fec_spill_read_bytes, data_sz, memory_order_relaxed );
     742             : 
     743           0 :     view->data  = spill_read_data;
     744           0 :     view->fec   = fec;
     745           0 :     view->flags = FD_STORE_FEC_DATA_VIEW_SPILL;
     746           0 :     return 0;
     747           0 :   }
     748             : 
     749           0 :   fd_rwlock_unwrite( &store->cache_lock );
     750           0 :   return -1;
     751           0 : }
     752             : 
     753             : void
     754             : fd_store_fec_data_view_release( fd_store_t *               store,
     755           0 :                                 fd_store_fec_data_view_t * view ) {
     756           0 :   if( FD_UNLIKELY( !store || !view || !view->fec ) ) return;
     757           0 :   if( view->flags & FD_STORE_FEC_DATA_VIEW_SPILL ) fd_rwlock_unwrite( &store->spill_read_lock );
     758             : 
     759           0 :   fd_rwlock_write( &store->cache_lock );
     760           0 :   cache_unpin_locked( store, view->fec );
     761           0 :   cache_consume_locked( store, view->fec );
     762           0 :   fd_rwlock_unwrite( &store->cache_lock );
     763             : 
     764           0 :   view->data  = NULL;
     765           0 :   view->fec   = NULL;
     766           0 :   view->flags = 0U;
     767           0 : }
     768             : 
     769             : fd_store_fec_t *
     770             : fd_store_query( fd_store_map_t *  map,
     771           0 :                 fd_hash_t const * merkle_root ) {
     772           0 :   for(;;) {
     773           0 :     fd_store_map_query_t query[1];
     774           0 :     int err = fd_store_map_query_try( map, merkle_root, NULL, query, 0 );
     775           0 :     if( FD_UNLIKELY( err==FD_MAP_ERR_AGAIN ) ) continue;
     776           0 :     if( FD_UNLIKELY( err ) ) return NULL;
     777             : 
     778           0 :     fd_store_fec_t * fec = fd_store_map_query_ele( query );
     779           0 :     err = fd_store_map_query_test( query );
     780           0 :     if( FD_LIKELY( !err ) ) return fec;
     781           0 :     if( FD_UNLIKELY( err!=FD_MAP_ERR_AGAIN ) ) return NULL;
     782           0 :   }
     783           0 : }
     784             : 
     785             : int
     786             : fd_store_insert( fd_store_t *       store,
     787             :                  fd_store_map_t *   map,
     788             :                  fd_hash_t const *  merkle_root,
     789             :                  ulong              slot,
     790             :                  ulong              part,
     791             :                  uint               data_idx,
     792             :                  uint               data_sz,
     793             :                  ushort const       shred_sz[ FD_FEC_SHRED_CNT ],
     794           0 :                  fd_store_fec_t **  fec ) {
     795           0 :   FD_TEST( store && map && merkle_root && shred_sz && fec );
     796           0 :   FD_TEST( data_idx<store->cache_slot_cnt && data_sz<=store->fec_data_max );
     797           0 :   *fec = NULL;
     798           0 :   fd_rwlock_read( &store->fec_lock );
     799             : 
     800           0 :   struct {
     801           0 :     fd_store_map_txn_t              txn [1];
     802           0 :     fd_store_map_txn_private_info_t info[1];
     803           0 :   } map_txn_mem;
     804           0 :   fd_store_map_txn_t * txn = fd_store_map_txn_init( map_txn_mem.txn, map, 1UL );
     805           0 :   FD_TEST( !fd_store_map_txn_add( txn, merkle_root, 1 ) );
     806           0 :   FD_TEST( !fd_store_map_txn_try( txn, FD_MAP_FLAG_BLOCKING ) );
     807             : 
     808           0 :   fd_store_map_query_t query[1];
     809           0 :   int err = fd_store_map_txn_query( map, merkle_root, NULL, query, 0 );
     810           0 :   if( FD_LIKELY( err==FD_MAP_ERR_KEY ) ) {
     811           0 :     fd_store_fec_t * new_fec = fd_store_fec_acquire( store );
     812           0 :     FD_TEST( new_fec );
     813           0 :     new_fec->key     = *merkle_root;
     814           0 :     new_fec->data_sz = data_sz;
     815           0 :     memcpy( new_fec->shred_sz, shred_sz, sizeof(new_fec->shred_sz) );
     816             : 
     817             :     /* RAM_READY before it is linked: fd_store_publish (exclusive
     818             :        fec_lock) never sees it mid-write. */
     819           0 :     fd_rwlock_write( &store->cache_lock );
     820           0 :     new_fec->data_idx   = data_idx;
     821           0 :     new_fec->data_state = FD_STORE_FEC_DATA_RAM_READY;
     822           0 :     cache_lru_push_tail_locked( store, new_fec );
     823           0 :     fd_rwlock_unwrite( &store->cache_lock );
     824             : 
     825           0 :     if( FD_LIKELY( store->alpenglow ) ) {
     826           0 :       FD_TEST( part<store->shred_tile_cnt );
     827           0 :       new_fec->slot = (uint)fd_ulong_max( slot, store->root );
     828           0 :       ulong lo = atomic_load_explicit( &store->slot_lo, memory_order_relaxed );
     829           0 :       while( new_fec->slot<lo && !atomic_compare_exchange_weak_explicit( &store->slot_lo, &lo, new_fec->slot, memory_order_relaxed, memory_order_relaxed ) );
     830           0 :       fd_store_slot_to_fecs_ele_insert( fd_store_slot_to_fecs_join( slot_to_fecs_laddr( store, part ) ), new_fec, pool_ele_laddr( store ) );
     831           0 :     }
     832           0 :     FD_TEST( !fd_store_map_txn_insert( map, new_fec ) );
     833           0 :     *fec = new_fec;
     834           0 :     err  = FD_MAP_SUCCESS;
     835           0 :   } else {
     836           0 :     FD_TEST( err==FD_MAP_SUCCESS );
     837           0 :     err = FD_MAP_ERR_KEY;
     838           0 :   }
     839             : 
     840           0 :   FD_TEST( !fd_store_map_txn_test( txn ) );
     841           0 :   fd_store_map_txn_fini( txn );
     842           0 :   fd_rwlock_unread( &store->fec_lock );
     843             : 
     844           0 :   if( FD_UNLIKELY( err==FD_MAP_ERR_KEY ) ) {
     845           0 :     fd_rwlock_write( &store->cache_lock );
     846           0 :     cache_slot_release_locked( store, data_idx );
     847           0 :     fd_rwlock_unwrite( &store->cache_lock );
     848           0 :   }
     849           0 :   return err;
     850           0 : }
     851             : 
     852             : int
     853             : fd_store_remove( fd_store_t *      store,
     854             :                  fd_store_map_t *  map,
     855           0 :                  fd_hash_t const * merkle_root ) {
     856           0 :   fd_rwlock_write( &store->fec_lock );
     857           0 :   fd_store_map_query_t query[1];
     858           0 :   int err = fd_store_map_remove( map, merkle_root, NULL, query, FD_MAP_FLAG_BLOCKING );
     859           0 :   if( FD_LIKELY( !err ) ) fd_store_fec_release( store, fd_store_map_query_ele( query ) );
     860           0 :   else FD_TEST( err==FD_MAP_ERR_KEY );
     861           0 :   fd_rwlock_unwrite( &store->fec_lock );
     862           0 :   return !err;
     863           0 : }
     864             : 
     865             : ulong
     866             : fd_store_publish( fd_store_t *     store,
     867             :                   fd_store_map_t * map,
     868           0 :                   ulong            root ) {
     869           0 :   fd_store_fec_t * fec0 = pool_ele_laddr( store );
     870           0 :   ulong            cnt  = 0UL;
     871           0 :   fd_rwlock_write( &store->fec_lock );
     872           0 :   ulong            lo   = atomic_load_explicit( &store->slot_lo, memory_order_relaxed );
     873           0 :   for( ulong part=0UL; store->alpenglow && part<store->shred_tile_cnt; part++ ) {
     874           0 :     fd_store_slot_to_fecs_t * slot_to_fecs = fd_store_slot_to_fecs_join( slot_to_fecs_laddr( store, part ) );
     875           0 :     for( ulong slot=lo; slot<root; slot++ ) {
     876           0 :       uint key = (uint)slot;
     877           0 :       for( ulong idx = fd_store_slot_to_fecs_idx_remove( slot_to_fecs, &key, ULONG_MAX, fec0 );
     878           0 :                  idx!=ULONG_MAX;
     879           0 :                  idx = fd_store_slot_to_fecs_idx_remove( slot_to_fecs, &key, ULONG_MAX, fec0 ) ) {
     880           0 :         fd_store_map_query_t query[1];
     881           0 :         FD_TEST( !fd_store_map_remove( map, &fec0[ idx ].key, NULL, query, FD_MAP_FLAG_BLOCKING ) );
     882           0 :         fd_store_fec_release( store, fd_store_map_query_ele( query ) );
     883           0 :         cnt++;
     884           0 :       }
     885           0 :     }
     886           0 :   }
     887           0 :   if( FD_LIKELY( lo<root ) ) atomic_store_explicit( &store->slot_lo, root, memory_order_relaxed );
     888           0 :   store->root = fd_ulong_max( store->root, root );
     889           0 :   fd_rwlock_unwrite( &store->fec_lock );
     890           0 :   return cnt;
     891           0 : }
     892             : 
     893             : 
     894             : static inline ulong
     895             : disk_cell_tag( ulong ticket,
     896           0 :                ulong state ) {
     897           0 :   return (ticket<<2) | state;
     898           0 : }
     899             : 
     900             : static inline ulong
     901           0 : disk_cell_state( ulong tag ) {
     902           0 :   return tag & FD_SHREDB_CELL_STATE_MASK;
     903           0 : }
     904             : 
     905             : static inline ulong
     906           0 : disk_cell_ticket( ulong tag ) {
     907           0 :   return tag >> 2;
     908           0 : }
     909             : 
     910             : enum {
     911             :   DISK_PUBLISH_ERR     = -1,
     912             :   DISK_PUBLISH_NOOP    =  0,
     913             :   DISK_PUBLISH_SUCCESS =  1
     914             : };
     915             : 
     916             : static void
     917             : disk_slot_hint_publish( fd_store_t * store,
     918             :                         ulong        slot,
     919           0 :                         uint         shred_idx ) {
     920           0 :   atomic_ulong * hint = disk_slot_hint_laddr( store ) + (slot % store->disk_max_slots);
     921           0 :   ulong desired = fd_shredb_key_pack( slot, shred_idx ) | FD_SHREDB_HINT_VALID;
     922           0 :   ulong current = atomic_load_explicit( hint, memory_order_acquire );
     923           0 :   for(;;) {
     924           0 :     if( FD_LIKELY( current & FD_SHREDB_HINT_VALID ) ) {
     925           0 :       uint current_idx = fd_shredb_key_shred_idx( current & ~FD_SHREDB_HINT_VALID );
     926           0 :       if( FD_LIKELY( fd_shredb_key_slot( current )==slot ) && current_idx>=shred_idx ) return;
     927           0 :     }
     928           0 :     if( atomic_compare_exchange_strong_explicit( hint, &current, desired,
     929           0 :                                                  memory_order_release, memory_order_acquire ) ) return;
     930           0 :   }
     931           0 : }
     932             : 
     933             : /* disk_exact_publish relinks ring cell ring_idx from its old keys to
     934             :    (key,root_key), one map txn per map.  In each map the old key is
     935             :    unlinked only if it still points at the cell, and a new key held by
     936             :    an INVALID cell is stale and removed.
     937             : 
     938             :    The root map decides the result: SUCCESS if (root,idx) was linked,
     939             :    NOOP if a READY cell holds it, ERR if a WRITING cell holds it.  On
     940             :    SUCCESS the slot map is linked only if (slot,idx) is free, and
     941             :    *slot_linked says whether it was.  Both old keys are always unlinked.
     942             : 
     943             :    Without a root map (no alpenglow) the slot map decides the result
     944             :    the same way, so a second version of a (slot,idx) is a NOOP. */
     945             : 
     946             : static int
     947             : disk_exact_publish( fd_store_t *                 store,
     948             :                     ulong                        ring_idx,
     949             :                     ulong                        old_key,
     950             :                     fd_shredb_root_key_t const * old_root_key,
     951             :                     ulong                        key,
     952             :                     fd_shredb_root_key_t const * root_key,
     953           0 :                     int *                        slot_linked ) {
     954           0 :   atomic_ulong const * tags = disk_shred_tag_laddr( store );
     955           0 :   int result = DISK_PUBLISH_SUCCESS;
     956             : 
     957             :   /* Root map */
     958           0 :   if( store->has_root_map ) {
     959           0 :     fd_shredb_root_map_t map[1];
     960           0 :     FD_TEST( disk_root_map_ljoin( store, map ) );
     961           0 :     fd_shredb_root_entry_t * pool = disk_root_pool_laddr( store );
     962           0 :     fd_shredb_root_entry_t * cell = pool + ring_idx;
     963           0 :     struct {
     964           0 :       fd_shredb_root_map_txn_t              txn [1];
     965           0 :       fd_shredb_root_map_txn_private_info_t info[2];
     966           0 :     } txn_mem;
     967           0 :     fd_shredb_root_map_txn_t * txn = fd_shredb_root_map_txn_init( txn_mem.txn, map, 2UL );
     968           0 :     FD_TEST( !fd_shredb_root_map_txn_add( txn, old_root_key, 1 ) );
     969           0 :     FD_TEST( !fd_shredb_root_map_txn_add( txn, root_key,     1 ) );
     970           0 :     FD_TEST( !fd_shredb_root_map_txn_try( txn, FD_MAP_FLAG_BLOCKING ) );
     971             : 
     972           0 :     fd_shredb_root_map_query_t old_query[1];
     973           0 :     fd_shredb_root_map_query_t new_query[1];
     974           0 :     int old_err = fd_shredb_root_map_txn_query( map, old_root_key, NULL, old_query, 0 );
     975           0 :     int new_err = fd_shredb_root_map_txn_query( map, root_key,     NULL, new_query, 0 );
     976           0 :     FD_TEST( old_err==FD_MAP_SUCCESS || old_err==FD_MAP_ERR_KEY );
     977           0 :     FD_TEST( new_err==FD_MAP_SUCCESS || new_err==FD_MAP_ERR_KEY );
     978           0 :     fd_shredb_root_entry_t * old_ele = old_err ? NULL : fd_shredb_root_map_query_ele( old_query );
     979             : 
     980           0 :     if( FD_UNLIKELY( !new_err ) ) {
     981           0 :       fd_shredb_root_entry_t * new_ele = fd_shredb_root_map_query_ele( new_query );
     982           0 :       if( new_ele!=cell ) {
     983           0 :         ulong state = disk_cell_state( atomic_load_explicit( tags+(ulong)(new_ele-pool), memory_order_acquire ) );
     984           0 :         if(      FD_LIKELY  ( state==FD_SHREDB_CELL_READY   ) ) result = DISK_PUBLISH_NOOP;
     985           0 :         else if( FD_UNLIKELY( state==FD_SHREDB_CELL_WRITING ) ) result = DISK_PUBLISH_ERR;
     986           0 :         else FD_TEST( !fd_shredb_root_map_txn_remove( map, root_key, NULL, new_query, 0 ) );
     987           0 :       }
     988           0 :     }
     989             : 
     990           0 :     if( old_ele==cell ) {
     991           0 :       fd_shredb_root_map_query_t remove_query[1];
     992           0 :       FD_TEST( !fd_shredb_root_map_txn_remove( map, old_root_key, NULL, remove_query, 0 ) );
     993           0 :     }
     994             : 
     995           0 :     if( FD_LIKELY( result==DISK_PUBLISH_SUCCESS ) ) {
     996           0 :       cell->key = *root_key;
     997           0 :       FD_TEST( !fd_shredb_root_map_txn_insert( map, cell ) );
     998           0 :     }
     999           0 :     FD_TEST( !fd_shredb_root_map_txn_test( txn ) );
    1000           0 :     fd_shredb_root_map_txn_fini( txn );
    1001           0 :   }
    1002             : 
    1003             :   /* Slot map.  Only a newly stored shred can claim (slot,idx). */
    1004           0 :   *slot_linked = 0;
    1005           0 :   {
    1006           0 :     fd_shredb_shred_map_t map[1];
    1007           0 :     FD_TEST( disk_shred_map_ljoin( store, map ) );
    1008           0 :     fd_shredb_shred_entry_t * pool = disk_shred_pool_laddr( store );
    1009           0 :     fd_shredb_shred_entry_t * cell = pool + ring_idx;
    1010           0 :     fd_shredb_map_key_t old_map_key = old_key;
    1011           0 :     fd_shredb_map_key_t new_map_key = key;
    1012           0 :     struct {
    1013           0 :       fd_shredb_shred_map_txn_t              txn [1];
    1014           0 :       fd_shredb_shred_map_txn_private_info_t info[2];
    1015           0 :     } txn_mem;
    1016           0 :     fd_shredb_shred_map_txn_t * txn = fd_shredb_shred_map_txn_init( txn_mem.txn, map, 2UL );
    1017           0 :     FD_TEST( !fd_shredb_shred_map_txn_add( txn, &old_map_key, 1 ) );
    1018           0 :     FD_TEST( !fd_shredb_shred_map_txn_add( txn, &new_map_key, 1 ) );
    1019           0 :     FD_TEST( !fd_shredb_shred_map_txn_try( txn, FD_MAP_FLAG_BLOCKING ) );
    1020             : 
    1021           0 :     fd_shredb_shred_map_query_t old_query[1];
    1022           0 :     fd_shredb_shred_map_query_t new_query[1];
    1023           0 :     int old_err = fd_shredb_shred_map_txn_query( map, &old_map_key, NULL, old_query, 0 );
    1024           0 :     int new_err = fd_shredb_shred_map_txn_query( map, &new_map_key, NULL, new_query, 0 );
    1025           0 :     FD_TEST( old_err==FD_MAP_SUCCESS || old_err==FD_MAP_ERR_KEY );
    1026           0 :     FD_TEST( new_err==FD_MAP_SUCCESS || new_err==FD_MAP_ERR_KEY );
    1027           0 :     fd_shredb_shred_entry_t * old_ele = old_err ? NULL : fd_shredb_shred_map_query_ele( old_query );
    1028             : 
    1029           0 :     int link = result==DISK_PUBLISH_SUCCESS;
    1030           0 :     if( FD_UNLIKELY( link && !new_err ) ) {
    1031           0 :       fd_shredb_shred_entry_t * new_ele = fd_shredb_shred_map_query_ele( new_query );
    1032           0 :       if( new_ele!=cell ) {
    1033           0 :         ulong state = disk_cell_state( atomic_load_explicit( tags+(ulong)(new_ele-pool), memory_order_acquire ) );
    1034           0 :         if( FD_LIKELY( state==FD_SHREDB_CELL_READY || state==FD_SHREDB_CELL_WRITING ) ) {
    1035           0 :           link = 0;
    1036           0 :           if( !store->has_root_map ) result = state==FD_SHREDB_CELL_READY ? DISK_PUBLISH_NOOP : DISK_PUBLISH_ERR;
    1037           0 :         }
    1038           0 :         else FD_TEST( !fd_shredb_shred_map_txn_remove( map, &new_map_key, NULL, new_query, 0 ) );
    1039           0 :       }
    1040           0 :     }
    1041             : 
    1042           0 :     if( old_ele==cell ) {
    1043           0 :       fd_shredb_shred_map_query_t remove_query[1];
    1044           0 :       FD_TEST( !fd_shredb_shred_map_txn_remove( map, &old_map_key, NULL, remove_query, 0 ) );
    1045           0 :     }
    1046             : 
    1047           0 :     if( FD_LIKELY( link ) ) {
    1048           0 :       cell->key = new_map_key;
    1049           0 :       FD_TEST( !fd_shredb_shred_map_txn_insert( map, cell ) );
    1050           0 :       *slot_linked = 1;
    1051           0 :     }
    1052           0 :     FD_TEST( !fd_shredb_shred_map_txn_test( txn ) );
    1053           0 :     fd_shredb_shred_map_txn_fini( txn );
    1054           0 :   }
    1055             : 
    1056           0 :   return result;
    1057           0 : }
    1058             : 
    1059             : FD_STATIC_ASSERT( __builtin_offsetof( fd_shredb_entry_t, shred )==__builtin_offsetof( fd_shredb_entry_t, shred_sz )+sizeof(ushort), shredb_entry_hdr_packed );
    1060             : 
    1061             : /* disk_root_present returns 1 if (root,idx) is already stored in a READY
    1062             :    cell.  Lets duplicate inserts skip taking a ring cell. */
    1063             : 
    1064             : static int
    1065             : disk_root_present( fd_store_t const *           store,
    1066           0 :                    fd_shredb_root_key_t const * root_key ) {
    1067           0 :   fd_shredb_root_map_t map[1];
    1068           0 :   FD_TEST( disk_root_map_ljoin( store, map ) );
    1069           0 :   fd_shredb_root_map_query_t query[1];
    1070           0 :   if( FD_UNLIKELY( fd_shredb_root_map_query_try( map, root_key, NULL, query, 0 ) ) ) return 0;
    1071           0 :   ulong ring_idx = (ulong)(fd_shredb_root_map_query_ele_const( query )-disk_root_pool_laddr( store ));
    1072           0 :   ulong state = disk_cell_state( atomic_load_explicit( disk_shred_tag_laddr( store )+ring_idx, memory_order_acquire ) );
    1073           0 :   return !fd_shredb_root_map_query_test( query ) && state==FD_SHREDB_CELL_READY;
    1074           0 : }
    1075             : 
    1076             : int
    1077             : fd_store_disk_insert( fd_store_t       * store,
    1078             :                       int                disk_fd,
    1079             :                       fd_shred_t const * shred,
    1080           0 :                       fd_hash_t const  * merkle_root ) {
    1081           0 :   if( FD_UNLIKELY( !store || disk_fd<0 || !shred || !merkle_root || !fd_store_has_disk( store ) ) )
    1082           0 :     return FD_STORE_DISK_INSERT_ERR;
    1083             : 
    1084           0 :   ulong slot      = shred->slot;
    1085           0 :   uint  shred_idx = shred->idx;
    1086           0 :   if( FD_UNLIKELY( slot>=FD_SHREDB_KEY_SLOT_MAX || shred_idx>=store->max_shreds_per_block ) ) return FD_STORE_DISK_INSERT_ERR;
    1087           0 :   ulong                key      = fd_shredb_key_pack( slot, shred_idx );
    1088           0 :   fd_shredb_root_key_t root_key = fd_shredb_root_key( merkle_root->uc, shred_idx );
    1089             : 
    1090             :   /* Skip exact duplicates before taking a cell, which would otherwise
    1091             :      evict an older shred.  disk_exact_publish still catches races. */
    1092           0 :   if( FD_UNLIKELY( store->has_root_map && disk_root_present( store, &root_key ) ) ) return FD_STORE_DISK_INSERT_DUP;
    1093             : 
    1094           0 :   ulong ticket = atomic_fetch_add_explicit( &store->disk_reservation_head, 1UL, memory_order_relaxed ) + 1UL;
    1095           0 :   if( FD_UNLIKELY( !ticket || ticket>(ULONG_MAX>>2) ) ) return FD_STORE_DISK_INSERT_ERR;
    1096           0 :   ulong ring_idx = (ticket-1UL) % store->disk_max_shreds;
    1097             : 
    1098           0 :   fd_shredb_shred_entry_t * cell = disk_shred_pool_laddr( store ) + ring_idx;
    1099           0 :   atomic_ulong * cell_tag = disk_shred_tag_laddr( store ) + ring_idx;
    1100           0 :   ulong old_tag = atomic_load_explicit( cell_tag, memory_order_acquire );
    1101           0 :   ulong old_state = disk_cell_state( old_tag );
    1102           0 :   if( FD_UNLIKELY( old_state==FD_SHREDB_CELL_WRITING || disk_cell_ticket( old_tag )>=ticket ) )
    1103           0 :     return FD_STORE_DISK_INSERT_ERR;
    1104           0 :   ulong writing_tag = disk_cell_tag( ticket, FD_SHREDB_CELL_WRITING );
    1105           0 :   if( FD_UNLIKELY( !atomic_compare_exchange_strong_explicit( cell_tag, &old_tag, writing_tag,
    1106           0 :                                                               memory_order_acq_rel, memory_order_acquire ) ) )
    1107           0 :     return FD_STORE_DISK_INSERT_ERR;
    1108           0 :   old_state = disk_cell_state( old_tag );
    1109           0 :   ulong                old_key      = cell->key;
    1110           0 :   fd_shredb_root_key_t old_root_key = store->has_root_map ? disk_root_pool_laddr( store )[ ring_idx ].key : (fd_shredb_root_key_t){0};
    1111             : 
    1112           0 :   fd_shredb_entry_t wr_entry[1];
    1113           0 :   ulong shred_sz = fd_ulong_min( fd_shred_sz( shred ), FD_SHRED_MAX_SZ );
    1114           0 :   ulong tail_off = __builtin_offsetof( fd_shredb_entry_t, shred )+shred_sz;
    1115           0 :   wr_entry->tag      = disk_cell_tag( ticket, FD_SHREDB_CELL_READY );
    1116           0 :   wr_entry->key      = key;
    1117           0 :   wr_entry->shred_sz = (ushort)shred_sz;
    1118           0 :   memcpy( wr_entry->root, merkle_root->uc, FD_SHRED_MERKLE_NODE_SZ );
    1119           0 :   fd_memcpy( wr_entry->shred, shred, shred_sz );
    1120           0 :   fd_memset( (uchar *)wr_entry+tail_off, 0, sizeof(wr_entry)-tail_off );
    1121             : 
    1122           0 :   off_t off = (off_t)(store->wire_off + ring_idx*sizeof(fd_shredb_entry_t));
    1123           0 :   if( FD_UNLIKELY( store_pwrite_all( disk_fd, wr_entry, sizeof(wr_entry), off ) ) )
    1124           0 :     FD_LOG_ERR(( "error writing to disk store: (%d-%s)", errno, fd_io_strerror( errno ) ));
    1125           0 :   atomic_fetch_add_explicit( &store->disk_write_bytes, sizeof(wr_entry), memory_order_relaxed );
    1126             : 
    1127           0 :   int slot_linked;
    1128           0 :   int publish_result = disk_exact_publish( store, ring_idx, old_key, &old_root_key, key, &root_key, &slot_linked );
    1129           0 :   if( FD_LIKELY( publish_result==DISK_PUBLISH_SUCCESS ) ) {
    1130             :     /* The hint serves slot map reads, so only the first version sets it */
    1131           0 :     if( FD_LIKELY( slot_linked ) ) disk_slot_hint_publish( store, slot, shred_idx );
    1132           0 :     atomic_store_explicit( cell_tag, wr_entry->tag, memory_order_release );
    1133           0 :     if( old_state!=FD_SHREDB_CELL_READY )
    1134           0 :       atomic_fetch_add_explicit( &store->disk_cnt, 1UL, memory_order_relaxed );
    1135           0 :     atomic_fetch_add_explicit( &store->disk_insert_cnt, 1UL, memory_order_relaxed );
    1136           0 :     return FD_STORE_DISK_INSERT_SUCCESS;
    1137           0 :   }
    1138             : 
    1139           0 :   atomic_store_explicit( cell_tag, disk_cell_tag( ticket, FD_SHREDB_CELL_INVALID ), memory_order_release );
    1140           0 :   if( old_state==FD_SHREDB_CELL_READY )
    1141           0 :     atomic_fetch_sub_explicit( &store->disk_cnt, 1UL, memory_order_relaxed );
    1142           0 :   if( publish_result==DISK_PUBLISH_NOOP ) return FD_STORE_DISK_INSERT_DUP;
    1143           0 :   return FD_STORE_DISK_INSERT_ERR;
    1144           0 : }
    1145             : 
    1146           0 : #define FD_STORE_DISK_READ_RETRY_CNT (8UL)
    1147             : 
    1148             : /* disk_read_key_once reads the cell holding slot map key into rd_entry,
    1149             :    or returns MISS or BUSY if the cell changed during the read. */
    1150             : 
    1151             : static int
    1152             : disk_read_key_once( fd_store_t const *  store,
    1153             :                     int                 disk_fd,
    1154             :                     ulong               key,
    1155           0 :                     fd_shredb_entry_t * rd_entry ) {
    1156           0 :   fd_shredb_map_key_t map_key = key;
    1157           0 :   fd_shredb_shred_map_t map[1];
    1158           0 :   FD_TEST( disk_shred_map_ljoin( store, map ) );
    1159           0 :   fd_shredb_shred_map_query_t query[1];
    1160           0 :   int err = fd_shredb_shred_map_query_try( map, &map_key, NULL, query, 0 );
    1161           0 :   if( FD_UNLIKELY( err==FD_MAP_ERR_AGAIN || err==FD_MAP_ERR_CORRUPT ) ) return FD_STORE_DISK_QUERY_BUSY;
    1162           0 :   if( FD_UNLIKELY( err==FD_MAP_ERR_KEY ) ) return FD_STORE_DISK_QUERY_MISS;
    1163           0 :   FD_TEST( !err );
    1164             : 
    1165           0 :   fd_shredb_shred_entry_t const * cell = fd_shredb_shred_map_query_ele_const( query );
    1166           0 :   ulong ring_idx = (ulong)(cell-disk_shred_pool_laddr( store ));
    1167           0 :   atomic_ulong * cell_tag = disk_shred_tag_laddr( store ) + ring_idx;
    1168           0 :   ulong tag = atomic_load_explicit( cell_tag, memory_order_acquire );
    1169           0 :   if( FD_UNLIKELY( disk_cell_state( tag )!=FD_SHREDB_CELL_READY ) ) {
    1170           0 :     int query_err = fd_shredb_shred_map_query_test( query );
    1171           0 :     if( FD_UNLIKELY( query_err || disk_cell_state( tag )==FD_SHREDB_CELL_WRITING ) ) return FD_STORE_DISK_QUERY_BUSY;
    1172           0 :     return FD_STORE_DISK_QUERY_MISS;
    1173           0 :   }
    1174             : 
    1175           0 :   off_t off = (off_t)(store->wire_off + ring_idx*sizeof(fd_shredb_entry_t));
    1176           0 :   if( FD_UNLIKELY( store_pread_all( disk_fd, rd_entry, sizeof(fd_shredb_entry_t), off ) ) )
    1177           0 :     FD_LOG_ERR(( "error reading disk store: (%d-%s)", errno, fd_io_strerror( errno ) ));
    1178           0 :   ulong tag_after = atomic_load_explicit( cell_tag, memory_order_acquire );
    1179           0 :   int query_err = fd_shredb_shred_map_query_test( query );
    1180           0 :   if( FD_UNLIKELY( query_err || tag_after!=tag ) ) return FD_STORE_DISK_QUERY_BUSY;
    1181           0 :   if( FD_UNLIKELY( rd_entry->tag!=tag || rd_entry->key!=key ) ) return FD_STORE_DISK_QUERY_BUSY;
    1182           0 :   return (int)fd_ulong_min( rd_entry->shred_sz, FD_SHRED_MAX_SZ );
    1183           0 : }
    1184             : 
    1185             : /* disk_read_root_once is disk_read_key_once for the root map key of
    1186             :    (root,shred_idx).  The key is exact, so a stable cell holding another
    1187             :    root has changed (BUSY).  The key only holds the offset in the FEC
    1188             :    set, so a cell with the same offset but another shred_idx is a MISS. */
    1189             : 
    1190             : static int
    1191             : disk_read_root_once( fd_store_t const *           store,
    1192             :                      int                          disk_fd,
    1193             :                      fd_shredb_root_key_t const * root_key,
    1194             :                      uint                         shred_idx,
    1195           0 :                      fd_shredb_entry_t *          rd_entry ) {
    1196           0 :   fd_shredb_root_map_t map[1];
    1197           0 :   FD_TEST( disk_root_map_ljoin( store, map ) );
    1198           0 :   fd_shredb_root_map_query_t query[1];
    1199           0 :   int err = fd_shredb_root_map_query_try( map, root_key, NULL, query, 0 );
    1200           0 :   if( FD_UNLIKELY( err==FD_MAP_ERR_AGAIN || err==FD_MAP_ERR_CORRUPT ) ) return FD_STORE_DISK_QUERY_BUSY;
    1201           0 :   if( FD_UNLIKELY( err==FD_MAP_ERR_KEY ) ) return FD_STORE_DISK_QUERY_MISS;
    1202           0 :   FD_TEST( !err );
    1203             : 
    1204           0 :   fd_shredb_root_entry_t const * cell = fd_shredb_root_map_query_ele_const( query );
    1205           0 :   ulong ring_idx = (ulong)(cell-disk_root_pool_laddr( store ));
    1206           0 :   atomic_ulong * cell_tag = disk_shred_tag_laddr( store ) + ring_idx;
    1207           0 :   ulong tag = atomic_load_explicit( cell_tag, memory_order_acquire );
    1208           0 :   if( FD_UNLIKELY( disk_cell_state( tag )!=FD_SHREDB_CELL_READY ) ) {
    1209           0 :     int query_err = fd_shredb_root_map_query_test( query );
    1210           0 :     if( FD_UNLIKELY( query_err || disk_cell_state( tag )==FD_SHREDB_CELL_WRITING ) ) return FD_STORE_DISK_QUERY_BUSY;
    1211           0 :     return FD_STORE_DISK_QUERY_MISS;
    1212           0 :   }
    1213             : 
    1214           0 :   off_t off = (off_t)(store->wire_off + ring_idx*sizeof(fd_shredb_entry_t));
    1215           0 :   if( FD_UNLIKELY( store_pread_all( disk_fd, rd_entry, sizeof(fd_shredb_entry_t), off ) ) )
    1216           0 :     FD_LOG_ERR(( "error reading disk store: (%d-%s)", errno, fd_io_strerror( errno ) ));
    1217           0 :   ulong tag_after = atomic_load_explicit( cell_tag, memory_order_acquire );
    1218           0 :   int query_err = fd_shredb_root_map_query_test( query );
    1219           0 :   if( FD_UNLIKELY( query_err || tag_after!=tag ) ) return FD_STORE_DISK_QUERY_BUSY;
    1220           0 :   if( FD_UNLIKELY( rd_entry->tag!=tag ||
    1221           0 :                    memcmp( rd_entry->root, root_key->root, FD_SHRED_MERKLE_NODE_SZ ) ||
    1222           0 :                    fd_shredb_key_shred_idx( rd_entry->key )%FD_FEC_SHRED_CNT!=root_key->off ) ) return FD_STORE_DISK_QUERY_BUSY;
    1223           0 :   if( FD_UNLIKELY( fd_shredb_key_shred_idx( rd_entry->key )!=shred_idx ) ) return FD_STORE_DISK_QUERY_MISS;
    1224           0 :   return (int)fd_ulong_min( rd_entry->shred_sz, FD_SHRED_MAX_SZ );
    1225           0 : }
    1226             : 
    1227             : int
    1228             : fd_store_disk_query( fd_store_t const * store,
    1229             :                      int                disk_fd,
    1230             :                      ulong              slot,
    1231             :                      uint               shred_idx,
    1232           0 :                      uchar              out[ FD_SHRED_MAX_SZ ] ) {
    1233           0 :   if( FD_UNLIKELY( !store || disk_fd<0 || !out || !fd_store_has_disk( store ) ||
    1234           0 :                    slot>=FD_SHREDB_KEY_SLOT_MAX || shred_idx>=store->max_shreds_per_block ) ) return FD_STORE_DISK_QUERY_MISS;
    1235           0 :   ulong key = fd_shredb_key_pack( slot, shred_idx );
    1236           0 :   for( ulong retry=0UL; retry<FD_STORE_DISK_READ_RETRY_CNT; retry++ ) {
    1237           0 :     fd_shredb_entry_t rd_entry[1];
    1238           0 :     int result = disk_read_key_once( store, disk_fd, key, rd_entry );
    1239           0 :     if( FD_UNLIKELY( result==FD_STORE_DISK_QUERY_BUSY ) ) { FD_SPIN_PAUSE(); continue; }
    1240           0 :     if( FD_UNLIKELY( result<0 ) ) return result;
    1241           0 :     fd_memcpy( out, rd_entry->shred, (ulong)result );
    1242           0 :     return result;
    1243           0 :   }
    1244           0 :   return FD_STORE_DISK_QUERY_BUSY;
    1245           0 : }
    1246             : 
    1247             : int
    1248             : fd_store_disk_query_root( fd_store_t const * store,
    1249             :                           int                disk_fd,
    1250             :                           uchar const *      merkle_root,
    1251             :                           uint               shred_idx,
    1252           0 :                           uchar              out[ FD_SHRED_MAX_SZ ] ) {
    1253           0 :   if( FD_UNLIKELY( !store || disk_fd<0 || !merkle_root || !out || !fd_store_has_disk( store ) || !store->has_root_map ||
    1254           0 :                    shred_idx>=store->max_shreds_per_block ) ) return FD_STORE_DISK_QUERY_MISS;
    1255           0 :   fd_shredb_root_key_t root_key = fd_shredb_root_key( merkle_root, shred_idx );
    1256           0 :   for( ulong retry=0UL; retry<FD_STORE_DISK_READ_RETRY_CNT; retry++ ) {
    1257           0 :     fd_shredb_entry_t rd_entry[1];
    1258           0 :     int result = disk_read_root_once( store, disk_fd, &root_key, shred_idx, rd_entry );
    1259           0 :     if( FD_UNLIKELY( result==FD_STORE_DISK_QUERY_BUSY ) ) { FD_SPIN_PAUSE(); continue; }
    1260           0 :     if( FD_UNLIKELY( result<0 ) ) return result;
    1261           0 :     fd_memcpy( out, rd_entry->shred, (ulong)result );
    1262           0 :     return result;
    1263           0 :   }
    1264           0 :   return FD_STORE_DISK_QUERY_BUSY;
    1265           0 : }
    1266             : 
    1267             : int
    1268             : fd_store_disk_probe( fd_store_t const * store,
    1269             :                      ulong              slot,
    1270           0 :                      uint               shred_idx ) {
    1271           0 :   if( FD_UNLIKELY( !store || !fd_store_has_disk( store ) || slot>=FD_SHREDB_KEY_SLOT_MAX ) ) return 0;
    1272           0 :   if( FD_UNLIKELY( shred_idx==UINT_MAX ) ) {
    1273           0 :     return !!(atomic_load_explicit( disk_slot_hint_laddr( store ) + (slot % store->disk_max_slots), memory_order_acquire ) & FD_SHREDB_HINT_VALID);
    1274           0 :   }
    1275           0 :   if( FD_UNLIKELY( shred_idx>=store->max_shreds_per_block ) ) return 0;
    1276           0 :   fd_shredb_map_key_t map_key = fd_shredb_key_pack( slot, shred_idx );
    1277           0 :   fd_shredb_shred_map_t map[1];
    1278           0 :   FD_TEST( disk_shred_map_ljoin( store, map ) );
    1279           0 :   fd_shredb_shred_map_query_t query[1];
    1280           0 :   return fd_shredb_shred_map_query_try( map, &map_key, NULL, query, 0 )!=FD_MAP_ERR_KEY;
    1281           0 : }
    1282             : 
    1283             : int
    1284             : fd_store_disk_query_highest( fd_store_t const * store,
    1285             :                              int                disk_fd,
    1286             :                              ulong              slot,
    1287             :                              uint               min_shred_idx,
    1288           0 :                              uchar              out[ FD_SHRED_MAX_SZ ] ) {
    1289           0 :   if( FD_UNLIKELY( !store || disk_fd<0 || !out || !fd_store_has_disk( store ) || slot>=FD_SHREDB_KEY_SLOT_MAX ) )
    1290           0 :     return FD_STORE_DISK_QUERY_MISS;
    1291           0 :   for( ulong retry=0UL; retry<FD_STORE_DISK_READ_RETRY_CNT; retry++ ) {
    1292           0 :     atomic_ulong const * hint_ptr = disk_slot_hint_laddr( store ) + (slot % store->disk_max_slots);
    1293           0 :     ulong hint = atomic_load_explicit( hint_ptr, memory_order_acquire );
    1294           0 :     if( FD_UNLIKELY( !(hint & FD_SHREDB_HINT_VALID) ) ) return FD_STORE_DISK_QUERY_MISS;
    1295           0 :     if( FD_UNLIKELY( fd_shredb_key_slot( hint )!=slot ) ) return FD_STORE_DISK_QUERY_BUSY;
    1296           0 :     uint idx = fd_shredb_key_shred_idx( hint & ~FD_SHREDB_HINT_VALID );
    1297           0 :     if( FD_UNLIKELY( idx>=store->max_shreds_per_block ) ) return FD_STORE_DISK_QUERY_MISS;
    1298             : 
    1299           0 :     fd_shredb_entry_t rd_entry[1];
    1300           0 :     int result = disk_read_key_once( store, disk_fd, fd_shredb_key_pack( slot, idx ), rd_entry );
    1301           0 :     ulong hint_after = atomic_load_explicit( hint_ptr, memory_order_acquire );
    1302           0 :     if( FD_UNLIKELY( hint_after!=hint || result==FD_STORE_DISK_QUERY_BUSY ) ) {
    1303           0 :       FD_SPIN_PAUSE();
    1304           0 :       continue;
    1305           0 :     }
    1306           0 :     if( FD_UNLIKELY( result==FD_STORE_DISK_QUERY_MISS ) ) return FD_STORE_DISK_QUERY_SCAN_LIMIT;
    1307           0 :     if( FD_UNLIKELY( result<0 ) ) return result;
    1308             : 
    1309           0 :     fd_shred_t const * shred = (fd_shred_t const *)fd_type_pun_const( rd_entry->shred );
    1310           0 :     if( FD_UNLIKELY( idx < min_shred_idx && !(shred->data.flags & FD_SHRED_DATA_FLAG_SLOT_COMPLETE) ) )
    1311           0 :       return FD_STORE_DISK_QUERY_MISS;
    1312           0 :     fd_memcpy( out, rd_entry->shred, (ulong)result );
    1313           0 :     return result;
    1314           0 :   }
    1315           0 :   return FD_STORE_DISK_QUERY_BUSY;
    1316           0 : }
    1317             : 
    1318             : int
    1319             : fd_store_disk_stats_query( fd_store_t const *      store,
    1320           0 :                            fd_store_disk_stats_t * stats ) {
    1321           0 :   if( FD_UNLIKELY( !store || !stats || !fd_store_has_disk( store ) ) ) return FD_STORE_DISK_QUERY_MISS;
    1322           0 :   stats->shred_cnt       = atomic_load_explicit( &store->disk_cnt, memory_order_relaxed );
    1323           0 :   stats->current_bytes   = stats->shred_cnt*sizeof(fd_shredb_entry_t)
    1324           0 :                          + atomic_load_explicit( &store->spill_live_cnt, memory_order_relaxed )*store->payload_slot_sz;
    1325           0 :   stats->allocated_bytes = fd_ulong_min( atomic_load_explicit( &store->disk_reservation_head, memory_order_relaxed ),
    1326           0 :                                         store->disk_max_shreds )*sizeof(fd_shredb_entry_t)
    1327           0 :                          + FD_VOLATILE_CONST( store->spill_slot_cnt )*store->payload_slot_sz;
    1328           0 :   stats->insert_cnt      = atomic_load_explicit( &store->disk_insert_cnt, memory_order_relaxed );
    1329           0 :   stats->write_bytes     = atomic_load_explicit( &store->disk_write_bytes, memory_order_relaxed );
    1330           0 :   return 0;
    1331           0 : }

Generated by: LCOV version 1.14