LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_txncache_shmem.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 156 0.0 %
Date: 2026-11-11 12:40:43 Functions: 0 6 0.0 %

          Line data    Source code
       1             : #include "fd_txncache_shmem.h"
       2             : #include "fd_txncache_private.h"
       3             : 
       4             : #define POOL_NAME       blockcache_pool
       5           0 : #define POOL_T          fd_txncache_blockcache_shmem_t
       6             : #define POOL_IDX_T      ulong
       7           0 : #define POOL_NEXT       pool.next
       8             : #define POOL_IMPL_STYLE 2
       9             : #include "../../util/tmpl/fd_pool.c"
      10             : 
      11             : #define MAP_NAME               blockhash_map
      12           0 : #define MAP_KEY                blockhash
      13           0 : #define MAP_ELE_T              fd_txncache_blockcache_shmem_t
      14             : #define MAP_KEY_T              fd_hash_t
      15           0 : #define MAP_PREV               blockhash_map.prev
      16           0 : #define MAP_NEXT               blockhash_map.next
      17             : #define MAP_KEY_EQ(k0,k1)      fd_hash_eq( k0, k1 )
      18             : #define MAP_KEY_HASH(key,seed) (__extension__({ (void)(seed); fd_ulong_load_8_fast( (key)->uc ); }))
      19             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
      20             : #define MAP_MULTI              1
      21             : #define MAP_IMPL_STYLE         2
      22             : #include "../../util/tmpl/fd_map_chain.c"
      23             : 
      24             : #define SLIST_NAME       root_slist
      25             : #define SLIST_ELE_T      fd_txncache_blockcache_shmem_t
      26             : #define SLIST_IDX_T      ulong
      27           0 : #define SLIST_NEXT       slist.next
      28             : #define SLIST_IMPL_STYLE 2
      29             : #include "../../util/tmpl/fd_slist.c"
      30             : 
      31             : FD_FN_CONST ulong
      32             : fd_txncache_max_txnpages( ulong max_active_slots,
      33           0 :                           ulong max_txn_per_slot ) {
      34             :   /* The pool must hold the worst case set of simultaneously live
      35             :      transactions.  Entries stay live until their inserting fork's
      36             :      blockcache is removed, and rooted forks are retained for
      37             :      FD_TXNCACHE_MAX_BLOCKHASH_DISTANCE further root advances, so up to
      38             :      max_active_slots forks' insertions are live at once.  The worst
      39             :      case is the boot transient: the snapshot root (holding the full
      40             :      staged snapshot load) plus every other pool fork full of live
      41             :      inserts.
      42             : 
      43             :      The snapshot transient retains at most 151 slot deltas at
      44             :      max_txn_per_slot entries each.  max_txn_per_slot is 2x the actual
      45             :      per slot transaction limit, because Agave snapshots contain each
      46             :      transaction twice (once keyed by signature, once by message hash,
      47             :      indistinguishably; see FD_PACK_MAX_TXNCACHE_TXN_PER_SLOT).  Live
      48             :      inserts are 1x, so each non-snapshot fork holds at most
      49             :      max_txn_per_slot/2 entries.
      50             : 
      51             :      We count pages, not transactions, so pages might be wasted.  The
      52             :      maximum page wastage occurs when every blockcache has a partially
      53             :      full tail page, adding max_active_slots-1 pages beyond the global
      54             :      transaction count bound (one blockcache's tail is already counted
      55             :      by the ceiling division). */
      56             : 
      57           0 :   ulong snapshot_budget = FD_TXNCACHE_MAX_SLOT_DELTAS*max_txn_per_slot;
      58           0 :   ulong live_budget     = (max_active_slots-1UL)*((max_txn_per_slot+1UL)/2UL);
      59           0 :   return max_active_slots-1UL
      60           0 :        + 1UL+(snapshot_budget+live_budget-1UL)/FD_TXNCACHE_TXNS_PER_PAGE;
      61           0 : }
      62             : 
      63             : FD_FN_CONST ulong
      64             : fd_txncache_max_txnpages_per_blockhash( ulong max_active_slots,
      65           0 :                                         ulong max_txn_per_slot ) {
      66             :   /* The maximum number of transaction pages we might need to store all
      67             :      the transactions that could be seen in a blockhash.
      68             : 
      69             :      In the worst case, every transaction in every live bank refers to
      70             :      the same blockhash, but a blockhash can never hold more pages than
      71             :      exist in the pool. */
      72             : 
      73           0 :   ulong max_txnpages = fd_txncache_max_txnpages( max_active_slots, max_txn_per_slot );
      74           0 :   ulong result = 1UL+(max_txn_per_slot*max_active_slots)/FD_TXNCACHE_TXNS_PER_PAGE;
      75           0 :   return fd_ulong_min( result, max_txnpages );
      76           0 : }
      77             : 
      78             : FD_FN_CONST ulong
      79           0 : fd_txncache_shmem_align( void ) {
      80           0 :   return FD_TXNCACHE_SHMEM_ALIGN;
      81           0 : }
      82             : 
      83             : ulong
      84             : fd_txncache_shmem_footprint( ulong max_live_slots,
      85             :                              ulong max_txn_per_slot,
      86           0 :                              ulong cache_footprint ) {
      87           0 :   if( FD_UNLIKELY( !cache_footprint ) ) return 0UL;
      88           0 :   if( FD_UNLIKELY( max_live_slots<1UL ) ) return 0UL;
      89           0 :   if( FD_UNLIKELY( max_txn_per_slot<1UL ) ) return 0UL;
      90             : 
      91           0 :   ulong max_active_slots = FD_TXNCACHE_MAX_BLOCKHASH_DISTANCE+max_live_slots;
      92           0 :   ulong blockhash_map_chains = fd_ulong_pow2_up( 2UL*max_active_slots );
      93           0 :   ulong bucket_cnt = fd_txncache_bucket_cnt( max_txn_per_slot );
      94             : 
      95           0 :   ulong _txnpages_max = fd_txncache_max_txnpages( max_active_slots, max_txn_per_slot );
      96           0 :   if( FD_UNLIKELY( _txnpages_max>FD_TXNCACHE_MAX_TXNPAGES ) )
      97           0 :     FD_LOG_ERR(( "txncache needs %lu txnpages but at most %lu are addressable (max_live_slots=%lu max_txn_per_slot=%lu)",
      98           0 :                  _txnpages_max, FD_TXNCACHE_MAX_TXNPAGES, max_live_slots, max_txn_per_slot ));
      99           0 :   ulong frames_max = fd_ulong_min( 1UL+(cache_footprint-1UL)/sizeof(fd_txncache_txnpage_t), _txnpages_max );
     100             : 
     101           0 :   ulong _descends_footprint = descends_set_footprint( max_active_slots );
     102           0 :   if( FD_UNLIKELY( !_descends_footprint ) ) return 0UL;
     103             : 
     104           0 :   ulong l;
     105           0 :   l = FD_LAYOUT_INIT;
     106           0 :   l = FD_LAYOUT_APPEND( l, FD_TXNCACHE_SHMEM_ALIGN,          sizeof(fd_txncache_shmem_t)                                  );
     107           0 :   l = FD_LAYOUT_APPEND( l, blockhash_map_align(),            blockhash_map_footprint( blockhash_map_chains )              );
     108           0 :   l = FD_LAYOUT_APPEND( l, blockcache_pool_align(),          blockcache_pool_footprint( max_active_slots )                );
     109           0 :   l = FD_LAYOUT_APPEND( l, alignof(uint),                    max_active_slots*bucket_cnt*sizeof(uint)                     ); /* blockcache->heads */
     110           0 :   l = FD_LAYOUT_APPEND( l, descends_set_align(),             max_active_slots*_descends_footprint                         ); /* blockcache->descends */
     111           0 :   l = FD_LAYOUT_APPEND( l, alignof(uint),                    _txnpages_max*sizeof(uint)                                   ); /* txnpages_free */
     112           0 :   l = FD_LAYOUT_APPEND( l, alignof(fd_txncache_page_meta_t), _txnpages_max*sizeof(fd_txncache_page_meta_t)                ); /* page_meta */
     113           0 :   l = FD_LAYOUT_APPEND( l, alignof(uint),                    frames_max*sizeof(uint)                                      ); /* frame_page */
     114           0 :   l = FD_LAYOUT_APPEND( l, alignof(uint),                    frames_max*sizeof(uint)                                      ); /* frames_free */
     115           0 :   l = FD_LAYOUT_APPEND( l, alignof(fd_txncache_txnpage_t),   frames_max*sizeof(fd_txncache_txnpage_t)                     ); /* txnpages */
     116           0 :   l = FD_LAYOUT_APPEND( l, alignof(uint),                    bucket_cnt*sizeof(uint)                                      ); /* scratchpad heads for purge stale */
     117           0 :   l = FD_LAYOUT_APPEND( l, alignof(fd_txncache_txnpage_t),   sizeof(fd_txncache_txnpage_t)                                ); /* scratchpad txnpage for purge stale */
     118           0 :   l = FD_LAYOUT_APPEND( l, alignof(fd_txncache_txnpage_t),   sizeof(fd_txncache_txnpage_t)                                ); /* compaction input */
     119           0 :   return FD_LAYOUT_FINI( l, FD_TXNCACHE_SHMEM_ALIGN );
     120           0 : }
     121             : 
     122             : void *
     123             : fd_txncache_shmem_new( void * shmem,
     124             :                        ulong  max_live_slots,
     125             :                        ulong  max_txn_per_slot,
     126             :                        ulong  cache_footprint,
     127           0 :                        ulong  seed ) {
     128           0 :   if( FD_UNLIKELY( !shmem ) ) {
     129           0 :     FD_LOG_WARNING(( "NULL shmem" ));
     130           0 :     return NULL;
     131           0 :   }
     132             : 
     133           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_txncache_shmem_align() ) ) ) {
     134           0 :     FD_LOG_WARNING(( "misaligned shmem" ));
     135           0 :     return NULL;
     136           0 :   }
     137             : 
     138           0 :   if( FD_UNLIKELY( !cache_footprint ) ) return NULL;
     139           0 :   if( FD_UNLIKELY( !max_live_slots ) ) return NULL;
     140           0 :   if( FD_UNLIKELY( !max_txn_per_slot ) ) return NULL;
     141             : 
     142           0 :   ulong max_active_slots = FD_TXNCACHE_MAX_BLOCKHASH_DISTANCE+max_live_slots;
     143           0 :   ulong blockhash_map_chains = fd_ulong_pow2_up( 2UL*max_active_slots );
     144           0 :   ulong bucket_cnt = fd_txncache_bucket_cnt( max_txn_per_slot );
     145             : 
     146           0 :   ulong _txnpages_max               = fd_txncache_max_txnpages              ( max_active_slots, max_txn_per_slot );
     147           0 :   ulong _txnpages_per_blockhash_max = fd_txncache_max_txnpages_per_blockhash( max_active_slots, max_txn_per_slot );
     148           0 :   if( FD_UNLIKELY( _txnpages_max>FD_TXNCACHE_MAX_TXNPAGES ) )
     149           0 :     FD_LOG_ERR(( "txncache needs %lu txnpages but at most %lu are addressable (max_live_slots=%lu max_txn_per_slot=%lu)",
     150           0 :                  _txnpages_max, FD_TXNCACHE_MAX_TXNPAGES, max_live_slots, max_txn_per_slot ));
     151           0 :   ulong frames_max = fd_ulong_min( 1UL+(cache_footprint-1UL)/sizeof(fd_txncache_txnpage_t), _txnpages_max );
     152             : 
     153           0 :   ulong _descends_footprint = descends_set_footprint( max_active_slots );
     154           0 :   if( FD_UNLIKELY( !_descends_footprint ) ) return NULL;
     155             : 
     156           0 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
     157           0 :   fd_txncache_shmem_t * tc    = FD_SCRATCH_ALLOC_APPEND( l, FD_TXNCACHE_SHMEM_ALIGN,          sizeof(fd_txncache_shmem_t)                                  );
     158           0 :   void * _blockhash_map       = FD_SCRATCH_ALLOC_APPEND( l, blockhash_map_align(),            blockhash_map_footprint( blockhash_map_chains )              );
     159           0 :   void * _blockcache_pool     = FD_SCRATCH_ALLOC_APPEND( l, blockcache_pool_align(),          blockcache_pool_footprint( max_active_slots )                );
     160           0 :                                 FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),                    max_active_slots*bucket_cnt*sizeof(uint)                     );
     161           0 :   void * _blockcache_descends = FD_SCRATCH_ALLOC_APPEND( l, descends_set_align(),             max_active_slots*_descends_footprint                         );
     162           0 :   uint * txnpages_free        = FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),                    _txnpages_max*sizeof(uint)                                   );
     163           0 :   void * _page_meta           = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_txncache_page_meta_t), _txnpages_max*sizeof(fd_txncache_page_meta_t)                );
     164           0 :   uint * frame_page           = FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),                    frames_max*sizeof(uint)                                      );
     165           0 :   uint * frames_free          = FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),                    frames_max*sizeof(uint)                                      );
     166           0 :                                 FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_txncache_txnpage_t),   frames_max*sizeof(fd_txncache_txnpage_t)                     );
     167           0 :                                 FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),                    bucket_cnt*sizeof(uint)                                      );
     168           0 :                                 FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_txncache_txnpage_t),   sizeof(fd_txncache_txnpage_t)                                );
     169           0 :                                 FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_txncache_txnpage_t),   sizeof(fd_txncache_txnpage_t)                                );
     170           0 :   tc->frames_max = frames_max;
     171           0 :   fd_txncache_page_meta_t * page_meta = _page_meta;
     172             : 
     173           0 :   fd_txncache_blockcache_shmem_t * blockcache_pool = blockcache_pool_join( blockcache_pool_new( _blockcache_pool, max_active_slots ) );
     174           0 :   FD_TEST( blockcache_pool );
     175           0 :   for( ulong i=0UL; i<max_active_slots; i++ ) blockcache_pool[ i ].frozen = -1;
     176             : 
     177           0 :   blockhash_map_t * blockhash_map = blockhash_map_join( blockhash_map_new( _blockhash_map, blockhash_map_chains, 0UL /* seed not used */ ) );
     178           0 :   FD_TEST( blockhash_map );
     179             : 
     180           0 :   for( ulong i=0UL; i<max_active_slots; i++ ) {
     181           0 :     descends_set_t * descends_set = descends_set_join( descends_set_new( (uchar *)_blockcache_descends + i*_descends_footprint, max_active_slots ) );
     182           0 :     FD_TEST( descends_set );
     183           0 :   }
     184             : 
     185           0 :   tc->root_cnt = 0UL;
     186           0 :   FD_TEST( root_slist_join( root_slist_new( tc->root_ll ) ) );
     187             : 
     188           0 :   tc->lock->value = 0;
     189           0 :   fd_rwlock_new( tc->alloc_lock );
     190             : 
     191           0 :   tc->txn_per_slot_max           = max_txn_per_slot;
     192           0 :   tc->active_slots_max           = max_active_slots;
     193           0 :   tc->bucket_cnt                 = bucket_cnt;
     194           0 :   tc->txnpages_per_blockhash_max = _txnpages_per_blockhash_max;
     195           0 :   tc->txnpages_max               = _txnpages_max;
     196             : 
     197           0 :   tc->blockcache_generation = 0U;
     198           0 :   tc->txnpages_free_cnt     = _txnpages_max;
     199           0 :   tc->frames_free_cnt       = frames_max;
     200           0 :   tc->evict_next            = 0UL;
     201           0 :   tc->release_seq           = 0UL;
     202             : 
     203           0 :   tc->page_miss_cnt         = 0UL;
     204           0 :   tc->page_spilled_cnt      = 0UL;
     205           0 :   tc->page_spill_read_cnt   = 0UL;
     206           0 :   tc->frame_evicted_cnt     = 0UL;
     207           0 :   tc->spill_ticks           = 0UL;
     208           0 :   tc->lock_read_wait_ticks  = 0UL;
     209           0 :   tc->lock_write_wait_ticks = 0UL;
     210           0 :   for( ulong i=0UL; i<_txnpages_max; i++ ) {
     211           0 :     txnpages_free[ i ] = (uint)i;
     212           0 :     page_meta[ i ].frame      = UINT_MAX;
     213           0 :     page_meta[ i ].dirty      = 0U;
     214           0 :     page_meta[ i ].disk_valid = 0;
     215           0 :     page_meta[ i ].next       = UINT_MAX;
     216           0 :     page_meta[ i ].ref        = 0U;
     217           0 :     page_meta[ i ].io         = 0U;
     218           0 :   }
     219           0 :   for( ulong i=0UL; i<frames_max; i++ ) {
     220           0 :     frame_page[ i ]  = UINT_MAX;
     221           0 :     frames_free[ i ] = (uint)i;
     222           0 :   }
     223             : 
     224           0 :   tc->seed = seed;
     225             : 
     226           0 :   FD_COMPILER_MFENCE();
     227           0 :   FD_VOLATILE( tc->magic ) = FD_TXNCACHE_SHMEM_MAGIC;
     228           0 :   FD_COMPILER_MFENCE();
     229             : 
     230           0 :   return (void *)tc;
     231           0 : }
     232             : 
     233             : fd_txncache_shmem_t *
     234           0 : fd_txncache_shmem_join( void * shtc ) {
     235           0 :   if( FD_UNLIKELY( !shtc ) ) {
     236           0 :     FD_LOG_WARNING(( "NULL shtc" ));
     237           0 :     return NULL;
     238           0 :   }
     239             : 
     240           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shtc, fd_txncache_shmem_align() ) ) ) {
     241           0 :     FD_LOG_WARNING(( "misaligned shtc" ));
     242           0 :     return NULL;
     243           0 :   }
     244             : 
     245           0 :   fd_txncache_shmem_t * tc = (fd_txncache_shmem_t *)shtc;
     246             : 
     247           0 :   if( FD_UNLIKELY( tc->magic!=FD_TXNCACHE_SHMEM_MAGIC ) ) {
     248           0 :     FD_LOG_WARNING(( "bad magic" ));
     249           0 :     return NULL;
     250           0 :   }
     251             : 
     252           0 :   return tc;
     253           0 : }

Generated by: LCOV version 1.14