LCOV - code coverage report
Current view: top level - discof/backup - fd_backup_cache.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 216 0.0 %
Date: 2026-08-14 04:54:57 Functions: 0 6 0.0 %

          Line data    Source code
       1             : #include "fd_backup_cache.h"
       2             : #include "fd_backup.h"
       3             : 
       4             : fd_backup_cache_t *
       5             : fd_backup_cache_init( fd_backup_cache_t *        backup,
       6             :                       uchar const * const        cache    [ FD_ACCDB_CACHE_CLASS_CNT ],
       7             :                       ulong const                cache_max[ FD_ACCDB_CACHE_CLASS_CNT ],
       8           0 :                       fd_backup_accidx_t const * idx ) {
       9           0 :   FD_TEST( idx->epoch_slot );
      10           0 :   FD_TEST( idx->epoch      );
      11           0 :   *backup = (fd_backup_cache_t) {
      12           0 :     .idx         = *idx,
      13           0 :     .cache_class = 0UL,
      14           0 :     .cache_idx   = 0UL
      15           0 :   };
      16           0 :   backup->idx.root_generation = 0U;
      17           0 :   FD_VOLATILE( *idx->epoch_slot ) = ULONG_MAX; /* idle until first section */
      18           0 :   for( ulong i=0UL; i<FD_ACCDB_CACHE_CLASS_CNT; i++ ) {
      19           0 :     backup->cache    [ i ] = cache    [ i ];
      20           0 :     backup->cache_max[ i ] = cache_max[ i ];
      21           0 :   }
      22           0 :   return backup;
      23           0 : }
      24             : 
      25             : fd_backup_cache_t *
      26             : fd_backup_cache_join( fd_backup_cache_t * backup,
      27             :                       fd_accdb_shmem_t *  accdb,
      28           0 :                       ulong *             epoch_fseq ) {
      29           0 :   ulong max_live_slots = accdb->max_live_slots;
      30           0 :   ulong max_accounts   = accdb->max_accounts;
      31             : 
      32           0 :   ulong chain_cnt = fd_ulong_pow2_up( (max_accounts>>1) + (max_accounts&1UL) );
      33             : 
      34           0 :   FD_SCRATCH_ALLOC_INIT( l, accdb );
      35           0 :   /*                       */FD_SCRATCH_ALLOC_APPEND( l, FD_ACCDB_SHMEM_ALIGN,           sizeof(fd_accdb_shmem_t)                                );
      36           0 :   /*                       */FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_accdb_fork_shmem_t), max_live_slots*sizeof(fd_accdb_fork_shmem_t)            );
      37           0 :   /*                       */FD_SCRATCH_ALLOC_APPEND( l, descends_set_align(),           max_live_slots*descends_set_footprint( max_live_slots ) );
      38           0 :   void * _acc_map          = FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),                  chain_cnt*sizeof(uint)                                  );
      39           0 :   void * _acc_pool_ele     = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_accdb_accmeta_t),    max_accounts*sizeof(fd_accdb_accmeta_t)             );
      40             : 
      41           0 :   uchar const * cache[ FD_ACCDB_CACHE_CLASS_CNT ];
      42           0 :   for( ulong c=0UL; c<FD_ACCDB_CACHE_CLASS_CNT; c++ ) {
      43           0 :     cache[ c ] = (uchar const *)accdb + accdb->cache_region_off[ c ];
      44           0 :   }
      45             : 
      46           0 :   fd_backup_accidx_t idx = {
      47           0 :     .acc_map      = _acc_map,
      48           0 :     .acc_pool     = _acc_pool_ele,
      49           0 :     .max_accounts = max_accounts,
      50           0 :     .seed         = accdb->seed,
      51           0 :     .chain_mask   = (uint)( chain_cnt-1UL ),
      52           0 :     .epoch_slot   = epoch_fseq,
      53           0 :     .epoch        = &accdb->epoch
      54           0 :   };
      55             : 
      56           0 :   return fd_backup_cache_init( backup, cache, accdb->cache_class_max, &idx );
      57           0 : }
      58             : 
      59             : static inline fd_accdb_cache_line_t *
      60             : cache_line( fd_backup_cache_t * backup,
      61             :             ulong               cls,
      62           0 :             ulong               idx ) {
      63           0 :   return (fd_accdb_cache_line_t *)( backup->cache[ cls ] + idx * fd_accdb_cache_slot_sz[ cls ] );
      64           0 : }
      65             : 
      66             : 
      67             : /* filter_batch is called with arbitrary acc_idx found in cache.
      68             :    Filter out acc_idx that:
      69             :    - are not rooted (generation too new)
      70             :    - have been freed since then (not visible in map) -- these are
      71             :      guaranteed to have not been rooted, since rooted acc_idx are stable
      72             :      while compaction and advance_root is disabled */
      73             : 
      74             : #define SET_NAME found_set
      75             : #define SET_MAX  FD_BACKUP_CACHE_PARA
      76             : #include "../../util/tmpl/fd_set.c"
      77             : 
      78             : static void
      79             : filter_batch( fd_backup_cache_t * backup,
      80           0 :               fd_backup_cache_msg_t * frag ) {
      81           0 :   fd_backup_accidx_t const * idx      = &backup->idx;
      82           0 :   fd_accdb_accmeta_t const * acc_pool = idx->acc_pool;
      83             : 
      84             :   /* filter out non-rooted accounts and tombstones */
      85           0 :   static fd_accdb_accmeta_t const dead_meta = { .key = { .generation = UINT_MAX } };
      86           0 :   for( ulong i=0UL; i<FD_BACKUP_CACHE_PARA; i++ ) {
      87           0 :     uint acc_idx = frag->acc_idx[ i ];
      88           0 :     fd_accdb_accmeta_t const * m = acc_idx!=UINT_MAX ? &acc_pool[ acc_idx ] : &dead_meta;
      89           0 :     int keep = fd_backup_accidx_rooted( idx,
      90           0 :                                         FD_VOLATILE_CONST( m->key.generation ),
      91           0 :                                         FD_VOLATILE_CONST( m->lamports       ) );
      92           0 :     fd_uint_store_if( !keep, &frag->acc_idx[ i ], UINT_MAX );
      93           0 :   }
      94             : 
      95             :   /* filter out invisible accounts */
      96           0 :   found_set_t found[ found_set_word_cnt ];
      97           0 :   found_set_new( found );
      98             : 
      99           0 :   uint head[ FD_BACKUP_CACHE_PARA ];
     100           0 :   for( ulong i=0UL; i<FD_BACKUP_CACHE_PARA; i++ ) {
     101           0 :     head[ i ] = frag->acc_idx[ i ]!=UINT_MAX ? idx->acc_map[ backup->chain_idx[ i ] ] : UINT_MAX;
     102           0 :   }
     103             : 
     104             :   /* sentinel to assist with branchless code */
     105           0 :   static fd_accdb_accmeta_t const dead = {
     106           0 :     .map = { .next = UINT_MAX }
     107           0 :   };
     108             : 
     109             :   /* parallel walk map chains */
     110           0 :   for(;;) {
     111             : 
     112             :     /* check for matches */
     113           0 :     for( ulong i=0UL; i<FD_BACKUP_CACHE_PARA; i++ ) {
     114           0 :       found_set_insert_if( found, frag->acc_idx[ i ]==head[ i ], i );
     115           0 :     }
     116             : 
     117             :     /* convert acc_idx to pointers */
     118           0 :     fd_accdb_accmeta_t const * gather[ FD_BACKUP_CACHE_PARA ];
     119           0 :     for( ulong i=0UL; i<FD_BACKUP_CACHE_PARA; i++ ) {
     120           0 :       uint acc_idx = head[ i ];
     121           0 :       FD_DCHECK_CRIT( fd_backup_accidx_valid( idx, acc_idx ) || acc_idx==UINT_MAX, "acc_idx out of bounds" );
     122           0 :       gather[ i ] = acc_idx!=UINT_MAX ? &acc_pool[ acc_idx ] : &dead;
     123           0 :     }
     124             : 
     125             :     /* wide gather */
     126           0 :     fd_accdb_accmeta_t * meta = backup->meta;
     127           0 :     for( ulong i=0UL; i<FD_BACKUP_CACHE_PARA; i++ ) {
     128           0 :       meta[ i ] = *gather[ i ];
     129           0 :     }
     130             : 
     131             :     /* next */
     132           0 :     for( ulong i=0UL; i<FD_BACKUP_CACHE_PARA; i++ ) {
     133           0 :       head[ i ] = meta[ i ].map.next;
     134           0 :     }
     135             : 
     136             :     /* done? */
     137           0 :     int done = 1;
     138           0 :     for( ulong i=0UL; i<FD_BACKUP_CACHE_PARA; i++ ) {
     139           0 :       if( head[ i ]!=UINT_MAX ) done = 0;
     140           0 :     }
     141           0 :     if( done ) break;
     142             : 
     143           0 :   }
     144             : 
     145             :   /* filter out dead elements */
     146           0 :   for( ulong i=0UL; i<FD_BACKUP_CACHE_PARA; i++ ) {
     147           0 :     if( FD_UNLIKELY( !found_set_test( found, i ) ) ) {
     148           0 :       frag->acc_idx[ i ] = UINT_MAX;
     149           0 :       memset( frag->pubkey[ i ].uc, 0, sizeof(fd_pubkey_t) );
     150           0 :     }
     151           0 :   }
     152             : 
     153           0 : }
     154             : 
     155             : fd_backup_cache_msg_t *
     156             : fd_backup_cache_scan( fd_backup_cache_t * backup,
     157           0 :                       fd_backup_cache_msg_t * frag ) {
     158           0 :   fd_backup_accidx_t * accidx = &backup->idx;
     159             : 
     160           0 :   ulong cls = backup->cache_class;
     161           0 :   if( FD_UNLIKELY( cls >= FD_ACCDB_CACHE_CLASS_CNT ) ) {
     162           0 :     return NULL;
     163           0 :   }
     164             : 
     165           0 :   FD_COMPILER_MFENCE();
     166           0 :   FD_VOLATILE( *accidx->epoch_slot ) = FD_VOLATILE_CONST( *accidx->epoch );
     167           0 :   FD_HW_MFENCE();
     168             : 
     169             :   /* Scan through cache lines (sequentially)
     170             :      This discovers any cached account (rooted or not), therefore may
     171             :      produce account indices that become invalid.  These are filtered
     172             :      out below. */
     173             : 
     174           0 :   long rem = (long)backup->cache_max[ cls ] - (long)backup->cache_idx;
     175           0 :   ulong idx = backup->cache_idx;
     176           0 :   if( FD_LIKELY( rem >= (long)FD_BACKUP_CACHE_PARA ) ) {
     177             :     /* fast path */
     178           0 :     for( ulong i=0UL; i<FD_BACKUP_CACHE_PARA; i++, idx++ ) {
     179           0 :       fd_accdb_cache_line_t const * line = cache_line( backup, cls, idx );
     180           0 :       frag->acc_idx[ i ] = line->acc_idx;
     181           0 :       fd_memcpy( frag->pubkey[ i ].uc, line->key.pubkey, sizeof(fd_pubkey_t) );
     182           0 :       backup->chain_idx[ i ] = fd_backup_accidx_chain( accidx, line->key.pubkey );
     183           0 :     }
     184           0 :   } else {
     185             :     /* slow path */
     186           0 :     for( ulong i=0UL; i<FD_BACKUP_CACHE_PARA; i++ ) {
     187           0 :       frag->acc_idx[ i ] = UINT_MAX;
     188           0 :       memset( frag->pubkey[ i ].uc, 0, sizeof(fd_pubkey_t) );
     189           0 :       backup->chain_idx[ i ] = UINT_MAX; /* deliberately not ULONG_MAX */
     190           0 :     }
     191           0 :     for( ulong i=0UL; rem--; i++, idx++ ) {
     192           0 :       fd_accdb_cache_line_t const * line = cache_line( backup, cls, idx );
     193           0 :       frag->acc_idx[ i ] = line->acc_idx;
     194           0 :       fd_memcpy( frag->pubkey[ i ].uc, line->key.pubkey, sizeof(fd_pubkey_t) );
     195           0 :       backup->chain_idx[ i ] = fd_backup_accidx_chain( accidx, line->key.pubkey );
     196           0 :     }
     197           0 :     if( FD_UNLIKELY( idx >= backup->cache_max[ cls ] ) ) {
     198           0 :       backup->cache_class++;
     199           0 :       idx = 0UL;
     200           0 :     }
     201           0 :   }
     202           0 :   backup->cache_idx = idx;
     203             : 
     204             :   /* Filter out account indices that cannot index acc_pool */
     205             : 
     206           0 :   for( ulong i=0UL; i<FD_BACKUP_CACHE_PARA; i++ ) {
     207           0 :     if( !fd_backup_accidx_valid( accidx, frag->acc_idx[ i ] ) ) {
     208           0 :       frag->acc_idx[ i ] = UINT_MAX;
     209           0 :       memset( frag->pubkey[ i ].uc, 0, sizeof(fd_pubkey_t) );
     210           0 :       backup->chain_idx[ i ] = UINT_MAX;
     211           0 :     }
     212           0 :   }
     213             : 
     214             :   /* Filter out freed/invisible and non-rooted accounts */
     215             : 
     216           0 :   filter_batch( backup, frag );
     217             : 
     218           0 :   FD_COMPILER_MFENCE();
     219           0 :   FD_VOLATILE( *accidx->epoch_slot ) = ULONG_MAX;
     220           0 :   return frag;
     221           0 : }
     222             : 
     223             : int
     224             : fd_backup_cache_read( fd_backup_cache_t * ctx,
     225             :                       fd_pubkey_t const * pubkey,
     226             :                       uint                acc_idx,
     227             :                       uchar *             out,
     228             :                       ulong *             out_sz,
     229           0 :                       ulong               out_max ) {
     230           0 :   FD_TEST( pubkey );
     231             : 
     232           0 :   fd_backup_accidx_t const * accidx = &ctx->idx;
     233             : 
     234           0 :   FD_DCHECK_CRIT( FD_VOLATILE_CONST( *accidx->epoch_slot )!=ULONG_MAX, "caller must publish epoch" );
     235             : 
     236           0 :   if( FD_UNLIKELY( !fd_backup_accidx_valid( accidx, acc_idx ) ) ) {
     237           0 :     return FD_BACKUP_CACHE_ERR_MISS;
     238           0 :   }
     239             : 
     240           0 :   if( FD_UNLIKELY( *out_sz + sizeof(snap_acc_hdr_t) > out_max ) ) {
     241           0 :     return FD_BACKUP_CACHE_ERR_SPACE;
     242           0 :   }
     243             : 
     244             :   /* This is a partial copy of read_one_nocopy */
     245           0 :   fd_accdb_accmeta_t const * accmeta = &accidx->acc_pool[ acc_idx ];
     246           0 :   if( FD_UNLIKELY( memcmp( accmeta->key.pubkey, pubkey->uc, sizeof(fd_pubkey_t) ) ) ) {
     247           0 :     return FD_BACKUP_CACHE_ERR_MISS;
     248           0 :   }
     249             : 
     250             :   /// STEP 1.
     251             :   ///   Walk the hash chain at acc_map[hash(pubkey)] using the same
     252             :   ///   visibility test as fd_accdb_acquire_inner.  See that function
     253             :   ///   for the detailed safety argument under concurrent prepend.
     254           0 :   ulong chain_idx = fd_backup_accidx_chain( accidx, pubkey->uc );
     255           0 :   uint acc_idx2 = FD_VOLATILE_CONST( accidx->acc_map[ chain_idx ] );
     256           0 :   _Bool found = 0;
     257           0 :   while( acc_idx2!=UINT_MAX ) {
     258           0 :     FD_DCHECK_CRIT( fd_backup_accidx_valid( accidx, acc_idx2 ), "acc_idx out of bounds" );
     259           0 :     fd_accdb_accmeta_t const * candidate = &accidx->acc_pool[ acc_idx2 ];
     260           0 :     found |= acc_idx==acc_idx2;
     261           0 :     acc_idx2 = FD_VOLATILE_CONST( candidate->map.next );
     262           0 :   }
     263           0 :   if( !found ) return FD_BACKUP_CACHE_ERR_MISS;
     264             : 
     265             :   /// STEP 2.
     266             :   ///   Snapshot acc fields.  The acc element's metadata is effectively
     267             :   ///   immutable from the perspective of cross-fork readers (see the
     268             :   ///   comment block in fd_accdb.h about cross-fork reads). */
     269           0 :   uint  snap_es       = FD_VOLATILE_CONST( accmeta->executable_size );
     270           0 :   uint  snap_gen      = accmeta->key.generation;
     271           0 :   ulong snap_lamports = accmeta->lamports;
     272           0 :   uint  snap_cidx     = FD_VOLATILE_CONST( accmeta->cache_idx );
     273           0 :   ulong data_len      = (ulong)FD_ACCDB_SIZE_DATA( snap_es );
     274           0 :   int   executable    = FD_ACCDB_SIZE_EXEC( snap_es );
     275           0 :   ulong rec_sz        = sizeof(snap_acc_hdr_t) + fd_ulong_align_up( data_len, 8UL );
     276           0 :   ulong data_pad      = fd_ulong_align_up( data_len, 8UL ) - data_len;
     277           0 :   if( FD_UNLIKELY( *out_sz + rec_sz > out_max ) ) {
     278           0 :     return FD_BACKUP_CACHE_ERR_SPACE;
     279           0 :   }
     280             : 
     281             :   /// STEP 3.
     282             :   ///    Cache hit fast path with try-read-test (ABA) loop.  Same
     283             :   ///    primitives as cache_try_pin: re-check key.generation + pubkey
     284             :   ///    before and after the bulk copy, and bail to the disk path if the
     285             :   ///    line was claimed for eviction (refcnt ==
     286             :   ///    FD_ACCDB_EVICT_SENTINEL).  No CAS on refcnt, we never pin the
     287             :   ///    line.
     288           0 :   if( !FD_ACCDB_SIZE_CACHE_VALID( snap_es ) ) {
     289           0 :     return FD_BACKUP_CACHE_ERR_MISS;
     290           0 :   }
     291           0 :   if( snap_cidx==FD_ACCDB_ACC_CIDX_INVAL ) {
     292           0 :     return FD_BACKUP_CACHE_ERR_MISS;
     293           0 :   }
     294             : 
     295           0 :   ulong cls = FD_ACCDB_ACC_CIDX_CLASS( snap_cidx );
     296           0 :   ulong idx = FD_ACCDB_ACC_CIDX_IDX  ( snap_cidx );
     297           0 :   fd_accdb_cache_line_t * line = cache_line( ctx, cls, idx );
     298             : 
     299           0 :   snap_acc_hdr_t * hdr = (snap_acc_hdr_t *)( out + *out_sz );
     300           0 :   memset( hdr, 0, sizeof(snap_acc_hdr_t) );
     301           0 :   memcpy( hdr->pubkey.uc, pubkey->uc, sizeof(fd_pubkey_t) );
     302             : 
     303           0 :   uint gen0 = FD_VOLATILE_CONST( line->key.generation );
     304           0 :   uint rc0  = FD_VOLATILE_CONST( line->refcnt );
     305           0 :   uint ai0  = FD_VOLATILE_CONST( line->acc_idx );
     306           0 :   if( FD_UNLIKELY( rc0==FD_ACCDB_EVICT_SENTINEL ) ) return FD_BACKUP_CACHE_ERR_MISS;
     307           0 :   if( FD_UNLIKELY( gen0!=snap_gen ) ) return FD_BACKUP_CACHE_ERR_MISS;
     308           0 :   if( FD_UNLIKELY( memcmp( line->key.pubkey, pubkey->uc, sizeof(fd_pubkey_t) ) ) ) return FD_BACKUP_CACHE_ERR_MISS;
     309             :   /* acc_idx==UINT_MAX is the "loading" sentinel set by cold_load_acc
     310             :      before the preadv2 fills the line.  CACHE_VALID can be observed
     311             :      set while the bytes are still stale, so fall to the disk path
     312             :      (which spins on offset_fork and reads from the file) rather
     313             :      than copying garbage. */
     314           0 :   if( FD_UNLIKELY( ai0==UINT_MAX ) ) return FD_BACKUP_CACHE_ERR_MISS;
     315             : 
     316           0 :   FD_COMPILER_MFENCE();
     317           0 :   memcpy( hdr->owner.uc, line->owner, 32UL );
     318           0 :   uchar * d = (uchar *)hdr + sizeof(snap_acc_hdr_t);
     319           0 :   memcpy( d, (uchar const *)(line+1UL), data_len );
     320           0 :   if( data_pad ) memset( d + data_len, 0, data_pad );
     321           0 :   FD_COMPILER_MFENCE();
     322             : 
     323           0 :   uint gen1 = FD_VOLATILE_CONST( line->key.generation );
     324           0 :   uint rc1  = FD_VOLATILE_CONST( line->refcnt );
     325           0 :   uint ai1  = FD_VOLATILE_CONST( line->acc_idx );
     326           0 :   if( FD_UNLIKELY( rc1==FD_ACCDB_EVICT_SENTINEL ) ) return FD_BACKUP_CACHE_ERR_MISS;
     327           0 :   if( FD_UNLIKELY( gen1!=snap_gen ) ) return FD_BACKUP_CACHE_ERR_MISS;
     328           0 :   if( FD_UNLIKELY( memcmp( line->key.pubkey, pubkey->uc, sizeof(fd_pubkey_t) ) ) ) return FD_BACKUP_CACHE_ERR_MISS;
     329           0 :   if( FD_UNLIKELY( ai1==UINT_MAX ) ) return FD_BACKUP_CACHE_ERR_MISS;
     330             : 
     331           0 :   hdr->lamports   = snap_lamports;
     332           0 :   hdr->executable = !!executable;
     333           0 :   hdr->data_len   = data_len;
     334           0 :   *out_sz += rec_sz;
     335           0 :   return FD_BACKUP_CACHE_SUCCESS;
     336           0 : }

Generated by: LCOV version 1.14